Browse Source

Adding form portal and updates

Andrej Studen @ ONKO-NIX 4 years ago
parent
commit
f5d3013754

+ 41 - 0
views/formPortal.html

@@ -0,0 +1,41 @@
+<style>
+.box{
+width:120px;
+height:120px;
+}
+
+.gold{ background-color: gold; }
+.red{ background-color: red; }
+
+.large{
+	font-size: 30px;
+}
+
+.center{
+	text-align: center;
+}
+
+.stretch{
+	padding: 50px;
+}
+</style>
+
+<div id="formDiv"/>
+
+	<div id="debugDiv" style="display:none">
+		<textarea cols="95" rows="5" id="formStatus">
+			Hello
+		</textarea>
+	</div>
+
+<script type "text/javascript">
+window.onload=init;
+function init(){
+	let config=new Object();
+	config.document=document;
+	config.div="formDiv";
+	config.debugArea="formStatus";
+	print(config,"Starting");
+	generateFormArray(config);
+}
+</script>

+ 8 - 0
views/formPortal.view.xml

@@ -0,0 +1,8 @@
+<view xmlns="http://labkey.org/data/xml/view" title="FORM Portal" frame="portal">
+	<dependencies>
+		<dependency path="crf/formPortal.js"/>
+	</dependencies>
+	<permissions>
+		<permission name="login"/>
+	</permissions>
+</view>

+ 30 - 147
views/reviewPortal.html

@@ -1,159 +1,42 @@
 <style>
-p.hidden{
-	display:none;
+.box{
+width:120px;
+height:120px;
 }
-table {margin-bottom:20px;table-layout:fixed; border-collapse:collapse; border-spacing:10px}
-table.t1 {width:400px; border:1px solid black}
-table.t1 th {border:1px solid black;padding:4px;background-color:#e0e0e0}
-table.t1 td {text-align:center}
-table.t2 {width:800px; border:1px solid black;}
-table.t2 th {border:1px solid black;padding:4px;background-color:#e0e0e0}
-table.t2 td {border:1px solid black; text-align:center}
 
-table.headTable {width:800px}
-table.headTable th {font-size:20px,text-align:center,background-color:#e0e0e0}
-table.headTable td {font-size:20px,text-align:center}
+.gold{ background-color: gold; }
+.red{ background-color: red; }
 
-div.d1 {text-align:center; width=400px; background-color:#e0e0e0;
-        font-size:      20px; margin-bottom:20px}
-
-</style>
-
-<div class="d1">CRF Review Portal</div>
-
-<div id="generalDataHead"></div>
-<div id="generalDataDiv" style="display:none">
-<table id="generalDataTable" cellspacing="2" cellpadding="5" border="0">
-<tr><td>Eudra CT Number: </td><td><strong id="eudraCTNumber">Loading</strong></td></tr>
-<tr><td>Study Sponsor: </td><td><strong id="studySponsor">Loading</strong></td></tr>
-<tr><td>Study Coordinator: </td><td><strong id="studyCoordinator">Loading</strong></td></tr>
-<tr><td>Regulatory authority Entry Number:</td><td><strong id="regulatoryNumber">Loading</strong></td></tr>
-</table>
-</div>
-
-<div id="pickFormHead"></div>
-<div id="pickFormDiv" style="display:none">
-	<div id="formDiv"></div>
-	<div id="selectFormDiv" style="display:block"></div>
-	<div id="listDiv"></div>
-</div>
-
-<div id="startDiv"></div>
-
-<div id="debugAreaHead"></div>
-<div id="debugAreaDiv" style="display:block">
-<textarea cols="95" rows="5" name="formStatus" id="formStatus">Entering data</textarea>
-</div>
-
-<script type="text/javascript">
+.large{
+	font-size: 30px;
+}
 
-document.getElementById("formStatus").value+="\nStarting";
-window.onload = init();
+.center{
+	text-align: center;
+}
 
+.stretch{
+	padding: 50px;
+}
+</style>
 
+<div id="formDiv"/>
 
-// Initialize the form by populating the Reagent drop-down list and
-// entering data associated with the current user.
-function init() {
+	<div id="debugDiv" style="display:none">
+		<textarea cols="95" rows="5" id="formStatus">
+			Hello
+		</textarea>
+	</div>
 
-	var config=new Object();
+<script type "text/javascript">
+window.onload=init;
+function init(){
+	let config=new Object();
 	config.document=document;
-	config.debugId='formStatus';
-
-	print(config,"Testing");
-	generateHead(config,"debugAreaHead","debugAreaDiv","Debug info");
-	generateHead(config,"generalDataHead","generalDataDiv","General data");
-	generateHead(config,"pickFormHead","pickFormDiv","Select CRF");
-						
-	let par=new Object();
-	par.masterQuery="crfEntry";
-	par.dataDiv="listDiv";
-	par.masterUserVarName="UserId";
-	par.formStatusName="FormStatus";
-											//Select PendingReview only
-	//*****CHANGE*****
-	par.formStatus=2;//Submitted
-	par.addDiv="selectFormDiv";
-	
-	par.config=config;	
-
-	par.source=new Object();
-	par.source.queryName="StudyProperties";
-	par.source.schemaName="study";
-	par.source.vars=["eudraCTNumber","studyCoordinator","regulatoryNumber","studySponsor"];
-	par.vars=new Object();
-
-	//User
-	let parUser=new Object();
-	parUser.masterSelectVarName="UserId";
-	parUser.callback=function(){};
-	parUser.selectId="UserSelect";
-	parUser.inputType="select";
-	//only display users in crfReviewers list
-	parUser.filter=new Object();
-	parUser.filter.queryName="crfReviewers";
-	parUser.filter.filterVarName="User";
-	parUser.filter.filters=new Object();
-	parUser.filter.filters["User"]=LABKEY.Security.currentUser.id;
-	par.vars["User"]=parUser;
-	//Crf		
-	let parCrf=new Object();
-	parCrf.masterSelectVarName="entryId";
-	parCrf.callback=generateList;
-	parCrf.selectId="crfSelect";
-	parCrf.addSelect=true;
-	//*****CHANGE****
-	//parCrf.addNewFlag=-1;
-	parCrf.inputType="select";
-	par.vars["Crf"]=parCrf;
-	//Site
-	let parSite=new Object();
-	parSite.masterSelectVarName="Site";
-	parSite.callback=generateListAndPopulateDaughterSelect;
-	parSite.selectId="SiteSelect";
-	parSite.daughterSelect="Crf";
-	parSite.inputType="select";
-
-	parSite.filter=new Object();
-	parSite.filter.queryName="crfReviewers";
-	parSite.filter.filterVarName="Site";
-	parSite.filter.filters=new Object();
-	parSite.filter.filters["User"]=LABKEY.Security.currentUser.id;
-	par.vars["Site"]=parSite;
-	
-	//Form
-	par.vars["Form"]=new Object();
-	let parForm=par.vars["Form"];
-	parForm.masterSelectVarName="Form";
-	parForm.callback=function(){};
-	parForm.selectId="FormSelect";
-	parForm.selectAll=true;
-	parForm.inputType="select";
-	//****CHANGE***** 
-	parForm.urlName="reviewFormUrl";
-				
-	//FormStatus;
-	par.vars["FormStatus"]=new Object();
-	let parFormStatus=par.vars["FormStatus"];
-	parFormStatus.masterSelectVarName="FormStatus";
-	parFormStatus.callback=generateListAndPopulateDaughterSelect;
-	parFormStatus.selectId="FormStatusSelect";
-	parFormStatus.daughterSelect="Crf";
-	parFormStatus.inputType="select";
-												
-	parFormStatus.filter=new Object();
-	parFormStatus.filter.queryName="formStatusVisibility";	
-	parFormStatus.filter.filterVarName="formStatus";
-	parFormStatus.filter.filters=new Object();
-	parFormStatus.filter.filters["visibilityLevel"]="crfReviewer"; 
-																				
-	par.vars["eudraCTNumber"]=sourceVar("EudraCTNumber","eudraCTNumber","EudraCTNumber");
-	par.vars["studyCoordinator"]=sourceVar("StudyCoordinator","studyCoordinator","StudyCoordinator");
-	par.vars["studySponsor"]=sourceVar("StudySponsor","studySponsor","StudySponsor");
-	par.vars["regulatoryNumber"]=sourceVar("RegulatoryNumber","regulatoryNumber","RegulatoryNumber");	
-
-	par.filters=["Site","FormStatus"];
-
-	drawForm(par);
+	config.review=1;
+	config.div="formDiv";
+	config.debugArea="formStatus";
+	print(config,"Starting");
+	generateFormArray(config);
 }
 </script>

+ 1 - 1
views/reviewPortal.view.xml

@@ -1,6 +1,6 @@
 <view xmlns="http://labkey.org/data/xml/view" title="Review Portal">
 	<dependencies>
-			<dependency path="crf/crfPortal.js"/>
+			<dependency path="crf/formPortal.js"/>
 		</dependencies>
 	<permissions>
 		<permission name="login"/>

+ 9 - 8
views/reviewVisit0.html

@@ -31,12 +31,13 @@ div.d1 {text-align:center; width=400px; background-color:#e0e0e0;
 <textarea id="errorTxt" cols="95" rows="10"></textarea>
 </div>
 
-<div id="debug" style="display:block">
+<div id="debugDiv"/>
+<!--<div id="debug" style="display:block">
 <textarea cols="95" rows="10" name="formStatus" id="formStatus">
 Loading
 </textarea>
 </div>
-
+-->
 
 
 <script type="text/javascript">
@@ -45,35 +46,35 @@ window.onload = init();
 
 function init(){
 		
-	var searchParams = new URLSearchParams(window.location.search);
+	let searchParams = new URLSearchParams(window.location.search);
 	
 	//update this to pick crfRef from url
 	let crfRef=searchParams.get('entryId');
+	let formSetupQuery=searchParams.get('formSetupQuery');	
+	let registrationQueryId=searchParams.get('registrationQueryId');
 	document.getElementById("crfRefId").innerHTML=crfRef;
-	//let crfRef=document.getElementById("crfRefId").innerHTML;
-	
 
 
 	let config=new Object();
 	//will this change if we are in views?
 	config.review=true;
 	//where to get ParticipantId
-	config.registrationQuery="registration";
+	config.registrationQueryId=registrationQueryId;
 	config.registrationParticipantIdField="participantCode";
 	config.masterForm="visitForm";
 	
 	config.document=document;
+	config.debugDiv="debugDiv";
 	config.debugId="formStatus";
 	config.crfRefId="crfRefId";
 	config.containerPath= LABKEY.ActionURL.getContainer();
-	config.setupQueryName="visitZeroSetup";
+	config.setupQueryName=formSetupQuery;
 	config.submitReportId="submitReport";
 	
 	
 	clear(config);
 	print(config,"Container path"+config.containerPath);
 
-
 	generateMasterForm(config);
 
 

+ 9 - 8
views/visit0.html

@@ -12,7 +12,7 @@ div.d1 {text-align:center; width=400px; background-color:#e0e0e0;
 </style>
 
 <table cellspacing="2" cellpadding="5" border="0">
-<tr><td>CRF ID: </td><td><strong id="crfRefId">1583163135258</strong></td></tr>
+<tr><td>CRF ID: </td><td><strong id="crfRefId">XXXXXXXXXXXXXX</strong></td></tr>
 <tr><td>Eudra CT Number: </td><td><strong id="eudraCTNumber">Loading</strong></td></tr>
 <tr><td>Study Sponsor: </td><td><strong id="studySponsor">Loading</strong></td></tr>
 <tr><td>Study Coordinator: </td><td><strong id="studyCoordinator">Loading</strong></td></tr>
@@ -25,19 +25,20 @@ div.d1 {text-align:center; width=400px; background-color:#e0e0e0;
 </form>
 
 <div id="submitDiv"/>
-
+ 
 
 <div id="errorDiv" style="display:none">
 <textarea id="errorTxt" cols="95" rows="10"></textarea>
 </div>
 
-<div id="debug" style="display:block">
+<div id="debugDiv"/>
+
+<!--<div id="debug" style="display:block">
 <textarea cols="95" rows="10" name="formStatus" id="formStatus">
 Loading
 </textarea>
 </div>
-
-
+-->
 
 <script type="text/javascript">
 
@@ -49,9 +50,8 @@ function init(){
 	
 	//update this to pick crfRef from url
 	let crfRef=searchParams.get('entryId');
+	let formSetupQuery=searchParams.get('formSetupQuery');
 	document.getElementById("crfRefId").innerHTML=crfRef;
-	//let crfRef=document.getElementById("crfRefId").innerHTML;
-	
 
 
 	let config=new Object();
@@ -59,10 +59,11 @@ function init(){
 	config.masterForm="visitForm";
 	
 	config.document=document;
+	config.debugDiv="debugDiv";
 	config.debugId="formStatus";
 	config.crfRefId="crfRefId";
 	config.containerPath= LABKEY.ActionURL.getContainer();
-	config.setupQueryName="visitZeroSetup";
+	config.setupQueryName=formSetupQuery;
 	config.submitReportId="submitReport";
 	
 	clear(config);

+ 1 - 1
views/visit0.view.xml

@@ -1,4 +1,4 @@
-<view xmlns="http://labkey.org/data/xml/view" title="Visit #0">
+<view xmlns="http://labkey.org/data/xml/view" title="Screening Visit">
 	<dependencies>
 			<dependency path="crf/crfVisit.js"/>
 	</dependencies>

+ 3 - 1
web/crf/crfPortal.js

@@ -698,7 +698,9 @@ function finalRedirect(data,entry,par){
 	var params = {
 		"name": formUrl, // The destination wiki page. The name of this parameter is not arbitrary.
 		"userid": entry[par.vars["User"].masterSelectVarName], 
-		"entryId": entry[par.vars["Crf"].masterSelectVarName]
+		"entryId": entry[par.vars["Crf"].masterSelectVarName],
+		"formSetupQuery":formEntry["setupQuery"],
+		"registrationQueryId":formEntry["masterQuery"]
 	};
 
 	let containerPath= LABKEY.ActionURL.getContainer();

+ 339 - 88
web/crf/crfVisit.js

@@ -1,9 +1,19 @@
 function clear(config){
+	let el=config.document.getElementById(config.debugId);
+	if (el===null) {
+		//alert("Debug section not initialized");
+		return;
+	}
 	config.document.getElementById(config.debugId).value="";
 }
 
 function print(config,msg){
-	config.document.getElementById(config.debugId).value+="\n"+msg;
+	let el=config.document.getElementById(config.debugId);
+	if (el===null) {
+		//alert("Debug section not initialized. Message: "+msg);
+		return;
+	}
+	el.value+="\n"+msg;
 }
 
 function getCRFref(config){
@@ -11,6 +21,58 @@ function getCRFref(config){
 	return config.document.getElementById(config.crfRefId).innerHTML;
 }
 
+function onFailure(errorInfo, options, responseObj){
+	
+	if (errorInfo && errorInfo.exception)
+		alert("Failure: " + errorInfo.exception);
+	else
+		alert("Failure: " + responseObj.statusText);
+}
+
+function generateDebugSection(config){
+	//let debug=true;
+	//if (debug) print(config,"generateDebugSection "+sectionName);
+
+	let formName=config.debugDiv;
+	let sectionName="debugSection";
+	let sectionTitle="Debug Messages";
+	let tb=config.document.createElement('table');
+	tb.className='t2';
+	let row=tb.insertRow();
+	let cell=config.document.createElement('th');
+	row.appendChild(cell);	
+	cell.setAttribute("colspan","4");
+	cell.style.fontSize="20px";
+	cell.style.textAlign="center";
+	let cellData=config.document.createTextNode(sectionTitle);
+	cell.appendChild(cellData);
+	cell=row.insertCell();
+	let input=config.document.createElement("input");	
+	input.type="button";
+	input.value="Show";
+	input.id="toggle"+sectionName+"VisbilityButton";
+	input.onclick=function(){toggleVisibility(config,sectionName,input.id)};
+	cell.appendChild(input);
+	config.document.getElementById(formName).appendChild(tb);
+	
+
+	let div=config.document.createElement('div');
+	div.id=sectionName;
+	div.style.display="none";
+	config.document.getElementById(formName).appendChild(div);	
+
+	let debugArea=config.document.createElement('textarea');
+	debugArea.rows=10;
+	debugArea.cols=95;
+	debugArea.id=config.debugId;
+	div.appendChild(debugArea);
+
+
+
+}
+
+
+
 function populateBasicData(config,data){
 	let debug=false;
 	if (debug)
@@ -27,12 +89,19 @@ function populateBasicData(config,data){
 	qconfig.success=function(data){setHTML(config,data,'siteName','siteName')};
 	LABKEY.Query.selectRows(qconfig);
 
+
+	if (debug)
+		print(config,"set user: "+data.rows[0].UserId);
 	let qconfig1=new Object();
 	qconfig1.containerPath=config.containerPath;
 	qconfig1.schemaName='core';
 	qconfig1.queryName='Users';
 	qconfig1.filterArray= [LABKEY.Filter.create('UserId',data.rows[0].UserId)];
 	qconfig1.success= function(data){setHTML(config,data,'investigatorName','DisplayName')};
+	qconfig1.failure=onFailure;
+	if (debug)
+		print(config,"USER select rows");
+
 	LABKEY.Query.selectRows(qconfig1);
 }
 
@@ -74,7 +143,8 @@ function fullAccessSetup(config,listName){
 	setup.filters['crfRef']=getCRFref(config);
 	setup.getInputId=function(vName){return listName+"_"+vName;}
 	setup.addApply="Save";
-	setup.unique=true;
+	//not neccesarily unique
+	//setup.unique=true;
 	//no addApply
 	return setup;
 
@@ -101,24 +171,73 @@ function generateForm(config,data){
 		if (debug) print(config,"generateForm ["+i+"/"+data.rows.length+"]");
 
 		let entry=data.rows[i];
-		let queryName=entry["queryName"];
+		//this is actually a pointer into another list (==lookup)
+		//another selectRows is needed to actually get the name for the queryName
+		//
 		
-		if (debug) print(config,"entry[queryName]: "+entry["queryName"]);
 		
-		if (!(queryName in config.fields))
-			 config.fields[queryName]=new Object();
 		
-		let field=config.fields[queryName];
-		field.title=entry["title"];
+		let fields=data.metaData.fields;
+		if (debug) print(config,"generateForm ["+i+"]: fields: "+fields);
+		let field="NONE";
+		//if (debug) printTableSetup(config,data);
+		for (f in fields){
+			if (fields[f]['name']!='queryName') continue;
+			field=fields[f];
+			break;
+		}
+		if (debug) print(config,"generateForm ["+i+"]: field: "+field);
+		let lookup=field.lookup;
+		if (debug) print(config,"generateForm ["+i+"]: lookup: "+lookup);
+		let qconfig=new Object();
+		qconfig.containerPath=config.containerPath;
+		qconfig.schemaName=lookup.schemaName;
+		qconfig.queryName=lookup.queryName;
+		qconfig.filterArray=[LABKEY.Filter.create(lookup.keyColumn,entry['queryName'])];
+		qconfig.success=function(data){generateFormLookup(config,data,entry)};
+		LABKEY.Query.selectRows(qconfig);
+	}
+}
 
-		if (debug) print(config,"entry[showFlag]: "+entry["showFlag"]);
 
-		let additionalData=new Object();
-		setAdditionalData(config,additionalData,entry);
-		generateSection(config,entry["queryName"],entry["title"],
-				entry["queryName"], additionalData);
+function generateFormLookup(config,data,entry){
+	//id : entry[queryName]
+	//name: dentry[queryName]
+	
+	let debug=true;
+	let queryId=entry['queryName'];
 
+	if (data.rows.length==0){
+		alert("No rows found for listId: "+entry['queryName']);
+		return;
 	}
+	let dentry=data.rows[0];
+	if (debug)
+		for (f in dentry) print(config,"generateFormLookup field: "+f+" value: "+dentry[f]);
+	
+	let queryName=dentry["queryName"];
+
+	if (debug) print(config,"generateFormLookup: ID: "+queryId+" name: "+queryName);
+	
+	//update fields for review
+	if (!(queryName in config.fields))
+		config.fields[queryName]=new Object();
+
+	if (!(queryId in config.queryMap))
+		config.queryMap[queryId]=queryName;
+		
+	let field=config.fields[queryName];
+
+	field.title=entry["title"];
+	field.queryId=queryId;
+
+	if (debug) print(config,"entry[showFlag]: "+entry["showFlag"]);
+
+	let additionalData=new Object();
+	setAdditionalData(config,additionalData,entry);
+	generateSection(config,queryName,entry["title"],queryName, additionalData);
+
+
 }
 
 function generateSection(config, sectionName, sectionTitle, listName, additionalData){
@@ -172,6 +291,8 @@ function generateSection(config, sectionName, sectionTitle, listName, additional
 	if (debug) print(config,"generate master table");
 	
 	let setup=fullAccessSetup(config,listName);
+	//master table is unique per visit
+	setup.unique=true;
 	if (config.review) 
 		setup=readonlySetup(config);
 	generateTable(config,listName,divTable.id,true,additionalData,setup);
@@ -195,37 +316,46 @@ function generateSection(config, sectionName, sectionTitle, listName, additional
 	divReview.id=sectionName+"Review";
 	div.appendChild(divReview);
 
+
+	//assume we already have listId (content of config.setupQueryName is listId)
+	//we need listName also
 	let qconfig=new Object();	
 	qconfig.containerPath=config.containerPath;
 	qconfig.schemaName='lists';
-	qconfig.queryName='visitZeroSetup';
+	qconfig.queryName=config.setupQueryName;
 	qconfig.success=function(data){
 		generateReview(config,data,divReview.id,divReviewList.id,listName)};
 	LABKEY.Query.selectRows(qconfig);
 }
 
 function generateReview(config,data,divReviewId,divReviewListId, listName){
+	let listId=config.fields[listName].queryId;
+
 	let debug=true;
-	if (debug) print(config,"Generate review for: "+listName);
-	let listId=-1;
-	for (let i=0;i<data.rows.length;i++){
-		let entry=data.rows[i];
-		if (entry["queryName"]!=listName) continue;
-		listId=entry["Key"];
-		break;
-	}
-	if (debug) print(config,"Review: setting listId: "+listId);	
+	if (debug) print(config,"Generate review for: "+listId);
+	
+	//don't need this
+	//let listId=-1;
+	//for (let i=0;i<data.rows.length;i++){
+	//	let entry=data.rows[i];
+	//	if (entry["queryName"]!=listName) continue;
+	//	listId=entry["Key"];
+	//	break;
+	//}
+	//if (debug) print(config,"Review: setting listId: "+listId);	
 
 	let reviewSetup=new Object();
 	reviewSetup.readonlyFlag=function(vName){
 		if (vName=="queryName") return true; 
+		if (vName=="queryname") return true; 
 		if (vName=="ModifiedBy") return true;
 		return false;};
 	reviewSetup.addApply="Add Review";
 	reviewSetup.filters=new Object();
 	reviewSetup.filters["crfRef"]=getCRFref(config);
-	reviewSetup.filters["queryName"]=listId;
-	reviewSetup.filters["ModifiedBy"]=LABKEY.Security.currentUser.id;
+	reviewSetup.filters["queryName"]=listId;//entry in reviewComments list is queryname, all in small caps
+	//reviewSetup.filters["ModifiedBy"]=LABKEY.Security.currentUser.id;
+	//needs listName, in argument
 	reviewSetup.getInputId=function(vName){return listName+"_add"+vName};
 	reviewSetup.divReviewListId=divReviewListId;
 		
@@ -375,31 +505,34 @@ function generateTable(config,listName,divName,unique,additionalData,setup){
 	LABKEY.Query.selectRows(qconfig);
 }
 
+function printTableSetup(config,data){
+	let obj=data.metaData.fields;
+	for (f in obj){	
+		print(config,"Data["+f+"]: "+obj[f]);
+		for (g in obj[f]){
+			print(config,"Data.metaData.fields["+f+"]["+g+"]: "+obj[f][g]);
+		}
+		if ("lookup" in obj[f]){
+			for (h in obj[f]["lookup"]){
+				print(config,"Lookup["+h+"]: "+obj[f]["lookup"][h]);
+			}
+		}
+	}
+}
+
+
 function populateTable(config,data,divName,unique,additionalData,setup){
 	//generate and populate table with the first suitable entry
 	let debug=true;
 
+	
 	if (debug){
 		print(config,"populateTable Query: "+data.queryName+" divName: "+divName+" setup: "+setup);
 
 
-		if (0){
-			let obj=data.metaData.fields;
-			for (f in obj){	
-				print(config,"Data["+f+"]: "+obj[f]);
-				for (g in obj[f]){
-					print(config,"Data.metaData.fields["+f+"]["+g+"]: "+obj[f][g]);
-				}
-				if ("lookup" in obj[f]){
-					for (h in obj[f]["lookup"]){
-						print(config,"Lookup["+h+"]: "+obj[f]["lookup"][h]);
-	
-					}
-				}
-
-			}
-		}
+		if (0)  printTableSetup(config,data);
 	}
+	
 	let crfRef=getCRFref(config);
 	let entry=new Object();
 
@@ -456,7 +589,7 @@ function populateTable(config,data,divName,unique,additionalData,setup){
 				input = config.document.createElement("input");
 				input.type="date";
 			}
-			if (vType=="string"){
+			if (vType=="string" && !("lookup" in field)){
 				if(vName.search("reviewComment")>-1){
 					input = config.document.createElement("textarea");
 					input.cols="65";
@@ -471,12 +604,18 @@ function populateTable(config,data,divName,unique,additionalData,setup){
 				input = config.document.createElement("input");
 				input.type="text";
 			}	
-		
+			if (vType=="boolean"){
+				input = config.document.createElement("input");
+				input.type="checkbox";
+				print(config,"Creating checkbox");
+			}
+
 			input.id=setup.getInputId(vName);
-			if (vName in entry){
+			if (varValue != "UNDEF"){
 				if (vType=="string") input.value=varValue;
 				if (vType=="float")  input.value=varValue;
 				if (vType=="date") input.valueAsDate=varValue;
+				if (vType=="boolean") input.checked=varValue;
 			}
 		}
 
@@ -543,6 +682,7 @@ function populateSelect(config,data,parameters,setup,readonlyFlag){
 		print(config,"ElementId: "+elementId);
 		print(config,"keyColumn: "+keyColumn);
 		print(config,"displayColumn: "+displayColumn);
+		print(config,"No of options: " +data.rows.length);
 	
 		if ("selectedKey" in parameters){
 			print(config,"SelectedKey: "+parameters["selectedKey"]);
@@ -563,6 +703,7 @@ function populateSelect(config,data,parameters,setup,readonlyFlag){
 		opt.text = "<Select>";
 		opt.value = -1;
 		el.options[0] = opt;
+		if (debug) print(config, "Adding <Select>");
 
 	}
 
@@ -667,6 +808,10 @@ function saveReviewToList(config,data,elementId,setup){
 		entry.queryName=setup.filters["queryName"];
 		if (debug) print(config,"Setting queryName: "+entry.queryName);
 	}
+	if ("queryname" in setup.filters) {
+		entry.queryname=setup.filters["queryname"];
+		if (debug) print(config,"Setting queryname: "+entry.queryname);
+	}
 	let fields=data.metaData.fields;
 	for (f in fields){
 
@@ -678,6 +823,7 @@ function saveReviewToList(config,data,elementId,setup){
 
 		if (vName=="crfRef") continue;
 		if (vName=="queryName") continue;
+		if (vName=="queryname") continue;
 		
 		let eId=setup.getInputId(vName);
 		
@@ -717,6 +863,9 @@ function saveReviewToList(config,data,elementId,setup){
 		if (vType=="float"){
 			entry[vName]=el.value;
 		}	
+		if (vType=="boolean"){
+			entry[vName]=el.checked;
+		}
 
 
 	}
@@ -754,97 +903,184 @@ function updateLastSavedFlag(config,data,setup,elementId){
 
 function onDatabaseUpload(config){
 	print(config,"Database upload");
+	configUpload=new Object();
 	//figure out the participantId
 	
 	let qconfig=new Object();
-	qconfig.queryName=config.registrationQuery;
+	qconfig.queryName=config.queryMap[config.registrationQueryId];
+	//queryMap holds mapping for queries in visit; 
+	//masterQuery should be one of them, so this is safe.
 	qconfig.schemaName='lists';
 	qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref(config))];
-	qconfig.success=function(data){updateDatabaseWithPatientId(config,data);}
+	qconfig.success=function(data){afterRegistration(config,configUpload,data);}
 	LABKEY.Query.selectRows(qconfig);
+	//waitForCompleteUpload(config);//
 
 }
 
-function updateDatabaseWithPatientId(config,data){
+function afterRegistration(config,configUpload,data){
+	print(config,"afterRegistration: rows:"+data.rows.length);
+	configUpload.registration=data;
+	let registrationData=configUpload.registration;
 	clearErr(config);
-	if (data.rows.length!=1){
-		let msg="ERROR: Found "+data.rows.length;
+	if (registrationData.rows.length!=1){
+		let msg="ERROR: Found "+registrationData.rows.length;
 		msg+=" registration entries for crfrefid "+getCRFref(config);
 		print(config,msg);
 		return;
 	}
-	participantId=data.rows[0][config.registrationParticipantIdField];
-	print(config,"Setting participantId to "+participantId);
+	configUpload.participantId=registrationData.rows[0][config.registrationParticipantIdField];
+	//could be a lookup field
+	let fields=registrationData.metaData.fields;
+	let field="NONE";
+	for (f in fields){
+		if (fields[f]["name"]==config.registrationParticipantIdField)
+			field=fields[f];
+	}
+	if ("lookup" in field){
+		print(config,"Using lookup for participantId: "+configUpload.participantId);
+		let lookup=field["lookup"];
+		print(config,"Lookup: "+lookup);
+		let qconfig=new Object();
+		qconfig.containerPath=config.containerPath;
+		qconfig.schemaName=lookup.schemaName;
+		qconfig.queryName=lookup.queryName;
+		qconfig.filterArray=
+			[LABKEY.Filter.create(lookup.keyColumn,configUpload.participantId)];
+		qconfig.success=function(data){
+			afterRegistrationLookup(config,configUpload,data,lookup.displayColumn)};
+		LABKEY.Query.selectRows(qconfig);
+	}
+	else{
+		afterParticipantId(config,configUpload);
+	}
+}
+
+function afterRegistrationLookup(config,configUpload,data,displayColumn){
+	print(config,"afterRegistrationLookup");
+	let entry=data.rows[0];
+	configUpload.participantId=entry[displayColumn];
+	afterParticipantId(config,configUpload);
+}
+
+
+function afterParticipantId(config,configUpload){
+	print(config,"Setting participantId to "+configUpload.participantId);
 	//another select rows to update all queries from setup
 	//just use registration for test
-	let qconfig=new Array();
+	let qconfig=new Object();
 	qconfig.schemaName='lists';
 	qconfig.queryName=config.setupQueryName;
-	qconfig.success=function(data){copyDatasetsFromSetup(config,data);}
+	qconfig.success=function(data){afterSetup(config,configUpload,data);}
 	LABKEY.Query.selectRows(qconfig);
 }
 
-function copyDatasetsFromSetup(config,data){
+function afterSetup(config,configUpload,data){
+
+	configUpload.queries=new Array();
 
 	for (let i=0;i<data.rows.length;i++){
 		let entry=data.rows[i];
-		copyToDataset(config,entry.queryName,participantId);
-		if (entry.showQuery!="NONE"){
-			copyToDataset(config,entry.showQuery,participantId);
-		}
+		//use lookup table to convert from id to name
+		let queryName=config.queryMap[entry.queryName];
+		configUpload.queries.push({queryName:queryName,queryStatus:"QUEUED"});
+		if (entry.showQuery=="NONE")
+			continue
+		configUpload.queries.push({queryName:entry.showQuery,queryStatus:"QUEUED"});
 	}
-	print(config,"Database updated");
+	//add reviews
+	configUpload.queries.push({queryName:"reviewComments",queryStatus:"QUEUED"});
+	copyToDataset(config,configUpload);
+
+	//config.upload["start"]=true;
+	//for (u in config.upload){
+	//	if (u=="start") continue;
+	//	if (u=="count") continue;
+	//	copyToDataset(config,u,participantId);
+	//}
+	//
+	//print(config,"Database updated");
 }
 
 
 
-function copyToDataset(config,queryName,participantId){
-	print(config,"copyToDataset");
+function copyToDataset(config,configUpload){
+
+	let queryId=-1;
+	for (let i=0;i<configUpload.queries.length;i++){
+		if (configUpload.queries[i].queryStatus=="DONE") 
+			continue;
+		queryId=i;
+		break;
+	}
+	if (queryId<0) {
+		updateFlag(config,3);//Approved
+		return;
+	}
+
+	let queryName=configUpload.queries[queryId].queryName;
+	print(config,"copyToDataset["+queryId+"/"+configUpload.queries.length+"]: "+queryName);
 	let qconfig=new Object();
 	qconfig.queryName=queryName;
 	qconfig.schemaName="lists";
 	qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref(config))];
-	qconfig.success=function(data){copyDataToDataset(config,queryName,participantId,data)};
+	qconfig.success=function(data){afterListData(config,configUpload,queryId,data)};
 	LABKEY.Query.selectRows(qconfig);
 }
 
-function copyDataToDataset(config,queryName,participantId,dataList){
-	print(config,"copyDataToDataset");
+function afterListData(config,configUpload,queryId,data){
+
+	let queryName=configUpload.queries[queryId].queryName;
+	let msg="copyDataToDataset ["+queryId+"/"+configUpload.queries.length+"] : "+queryName;
+	print(config,msg);
+	configUpload.listData=data;
 
 	let qconfig=new Object();
 	qconfig.queryName=queryName;
 	qconfig.schemaName="study";
 	qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref(config))];
-	qconfig.filterArray.push(LABKEY.Filter.create('ParticipantId',participantId));
-	qconfig.success=function(data){copyDataToDatasetChecked(config,queryName,participantId,dataList,data)};
+	qconfig.filterArray.push(LABKEY.Filter.create('ParticipantId',configUpload.participantId));
+	qconfig.success=function(data){afterStudyData(config,configUpload,queryId,data)};
 	LABKEY.Query.selectRows(qconfig);
 }
 
 
-function copyDataToDatasetChecked(config,queryName,participantId,dataList,dataStudy){
+function afterStudyData(config,configUpload,queryId,data){
+	configUpload.studyData=data;
 
-	print(config,"copyDataToDatasetChecked");
-	if (dataStudy.rows.lentgh>0){
+	let queryName=configUpload.queries[queryId].queryName;
+	print(config,"copyDataToDatasetChecked: "+queryName);
+	
+	if (configUpload.listData.rows.lentgh>0){
 		clearErr(config);
-		let msg="Dataset "+queryName+" already filled for participant "+participantId;
+		let msg="Dataset "+queryName+" already filled for participant ";
+		msg+=configUpload.participantId;
 		printErr(config,msg);
+		configUpload.queris[queryId].queryStatus="DONE";
 		//rows with the same crfRef and patientId already inserted - skip
-		return;
+		copyToDataset(config,configUpload);
 	}
-	
+
+	let dataList=configUpload.listData;
+	if (dataList.rows.length==0){
+		printErr(config,"Dataset "+queryName+" empty.");
+		configUpload.queries[queryId].queryStatus="DONE";
+		copyToDataset(config,configUpload);
+	}
+
 	//update non-list elements
 	let seqNumber=getCRFref(config);
 
 	let rows=new Array();
 	for (let i=0;i<dataList.rows.length;i++){
 		let entry=dataList.rows[i];
-		entry.ParticipantId=participantId;
+		entry.ParticipantId=configUpload.participantId;
 		entry.crfRef=getCRFref(config);
 		entry.SequenceNum=getCRFref(config);
 		entry.SequenceNum=entry.SequenceNum % 1000000000;
 		
 		if (dataList.rows.length>1){
-			entry.SequenceNum+=i/10;
+			entry.SequenceNum+=i/100;
 		}
 		print(config, "Adding sequence number "+entry.SequenceNum);
 		rows.push(entry);
@@ -854,13 +1090,17 @@ function copyDataToDatasetChecked(config,queryName,participantId,dataList,dataSt
 	qconfig.queryName=queryName;
 	qconfig.schemaName="study";
 	qconfig.success=function(data){
-		reportCopyDataToDatasetChecked(config,queryName,participantId,data)};
+		copyData(config,configUpload,queryId,data)};
 	qconfig.rows=rows;
 	LABKEY.Query.insertRows(qconfig);
 }
 
-function reportCopyDataToDatasetChecked(config,queryName,participantId,data){
+function copyData(config,configUpload,queryId,data){
+
+	let queryName=configUpload.queries[queryId].queryName;
 	printErr(config,"Inserted "+data.rows.length+" rows to "+queryName);
+	configUpload.queries[queryId].queryStatus="DONE";
+	copyToDataset(config,configUpload);
 
 }
 
@@ -870,18 +1110,23 @@ function reportCopyDataToDatasetChecked(config,queryName,participantId,data){
 //*************************update for further review *************************
 
 function onUpdateForReview(config){
-	print(config,"Sent to further review");
+	updateFlag(config,4);//Pending review
+}
+
+function updateFlag(config,flag){
+	if (flag==4)
+		print(config,"Sent to further review");
 	let qconfig=new Object();
 	qconfig.schemaName='lists';
 	qconfig.queryName='crfEntry';
-	qconfig.success=function(data){setFlagToReview(config,data);}
+	qconfig.success=function(data){setFlag(config,data,flag);}
 	qconfig.filterArray=[LABKEY.Filter.create("entryId",getCRFref(config))];
 	LABKEY.Query.selectRows(qconfig);
 }
 
-function setFlagToReview(config,data){
+function setFlag(config,data,flag){
 	let debug=true;
-	print(config,"setFlagToReview");
+	if (flag==4) print(config,"setFlagToReview");
 	if (data.rows.length!=1){
 		let msg="ERROR: Found "+data.rows.length;
 		msg+=" entries for crfrefid "+getCRFref(config);
@@ -889,7 +1134,7 @@ function setFlagToReview(config,data){
 		return;
 	}
 	let entry=data.rows[0];
-	entry.FormStatus=4;//Pending Review
+	entry.FormStatus=flag;//Pending Review
 	if (debug)
 		print(config,"Set form status to "+entry.FormStatus);
 	
@@ -897,16 +1142,17 @@ function setFlagToReview(config,data){
 	qconfig.schemaName='lists';
 	qconfig.queryName='crfEntry';
 	qconfig.rows=[entry];
-	qconfig.success=function(data){completeWithReview(config,data);}
+	qconfig.success=function(data){completeWithFlag(config,data,flag);}
 	LABKEY.Query.updateRows(qconfig);
 	
 }
 
-function completeWithReview(config,data){
+function completeWithFlag(config,data,flag){
 	let debug=true;
 
-	if (debug)
-		print(config,"complete with review");
+	if (debug){
+		if (flag==4) print(config,"complete with review");
+	}
 	let formUrl="begin";
 	var params = {
 		"name": formUrl, // The destination wiki page. The name of this parameter is not arbitrary.
@@ -966,7 +1212,7 @@ function finalRedirect(config){
 	let c1=new Object();
 	c1.schemaName='lists';
 	c1.queryName='crfEntry';
-	c1.filterArray=[LABKEY.Filter.create('crfRef',getCRFref(config))];
+	c1.filterArray=[LABKEY.Filter.create('entryId',getCRFref(config))];
 	c1.success=function(data){setSubmitStatus(config,data)};
 	LABKEY.Query.selectRows(c1);
 }
@@ -1079,7 +1325,12 @@ function checkData(data,config){
 }
 
 
+
+
 function generateMasterForm(config){
+
+	generateDebugSection(config);
+
 	let debug=true;
 
 	if (debug)
@@ -1094,15 +1345,15 @@ function generateMasterForm(config){
 	//requires populateBasicData
 	LABKEY.Query.selectRows(selectRows);
 
-
 	config.fields=new Object();	
+	config.queryMap=new Object();
 
 	let configSelectRows=new Object();
 	configSelectRows.containerPath=config.containerPath;
 	configSelectRows.schemaName='lists';
 	configSelectRows.queryName=config.setupQueryName;
 	configSelectRows.success=function(data){generateForm(config,data);};
-	configSelectRows.failure=function(errorTxt){print(config,"generateForm fail")};
+	configSelectRows.failure=onFailure;//function(errorTxt){print(config,"generateForm fail" + errorTxt)};
 	LABKEY.Query.selectRows(configSelectRows);
 
 	if (debug)

+ 393 - 0
web/crf/formPortal.js

@@ -0,0 +1,393 @@
+function print(config, msg){
+	config.document.getElementById(config.debugArea).value+="\n"+msg;
+}
+
+function clear(config){
+	config.document.getElementById(config.debugArea).value="";
+}
+
+function userName(formConfig,id){
+	for (let i=0;i<formConfig.users.rows.length;i++){
+		if (formConfig.users.rows[i].UserId!=id)
+			continue;
+		return formConfig.users.rows[i].DisplayName;
+	}
+	return "NONE";
+}
+
+function generateFormArray(config){
+	print(config,"generateFormArray");
+
+	let formConfig=new Object();
+
+	let qconfig=new Object();
+
+	qconfig.schemaName="lists";
+	qconfig.queryName="Forms";
+
+	//qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)]
+	qconfig.success=function(data){afterPopulatingForms(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+
+
+}
+
+function afterPopulatingForms(config,formConfig,data){
+	
+	formConfig.dataForms=data;
+	print(config,"afterPopulatingForms");
+	print(config,"Number of forms: "+formConfig.dataForms.rows.length);
+	
+	let qconfig=new Object();
+
+	qconfig.schemaName="core";
+	qconfig.queryName="users";
+	qconfig.success=function(data){afterPopulatingUsers(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+	
+}
+
+function afterPopulatingUsers(config,formConfig,data){
+	
+	formConfig.users=data;
+	print(config,"afterPopulatingUsers");
+	print(config,"Number of users: "+formConfig.users.rows.length);
+
+
+	let qconfig=new Object();
+
+	qconfig.schemaName="lists";
+	qconfig.queryName="inputLists";
+	qconfig.success=function(data){afterPopulatingLists(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+}
+
+function afterPopulatingLists(config,formConfig,data){
+
+	formConfig.inputLists=data;
+	print(config,"afterPopulatingLists");
+	print(config,"Number of lists: "+formConfig.inputLists.rows.length);
+	
+
+	let qconfig=new Object();
+
+	qconfig.schemaName="study";
+	qconfig.queryName="Study";
+	qconfig.success=function(data){afterPopulatingStudyData(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+}
+	
+function afterPopulatingStudyData(config,formConfig,data){
+
+	formConfig.studyData=data;
+	print(config,"afterPopulatingStudyData");
+	print(config,"Number of study data entries: "+formConfig.studyData.rows.length);
+	
+
+	let qconfig=new Object();
+
+	qconfig.schemaName="study";
+	qconfig.queryName="demographicData";
+	qconfig.success=function(data){afterPopulatingDemographicData(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+}
+
+function afterPopulatingDemographicData(config,formConfig,data){	
+
+	formConfig.demographicData=data;
+	print(config,"afterPopulatingDemographic");
+	print(config,"Number of patients: "+formConfig.demographicData.rows.length);
+	
+
+	let qconfig=new Object();
+
+	qconfig.schemaName="lists";
+	qconfig.queryName="crfEditors";
+	qconfig.success=function(data){afterPopulatingCrfEditors(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+}
+
+function afterPopulatingCrfEditors(config,formConfig,data){	
+
+	formConfig.crfEditors=data;
+	print(config,"afterPopulatingCrfEditors");
+	print(config,"Number of CRF editors: "+formConfig.crfEditors.rows.length);
+	
+
+	let qconfig=new Object();
+
+	qconfig.schemaName="lists";
+	qconfig.queryName="crfReviewers";
+	qconfig.success=function(data){afterPopulatingCrfReviewers(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+}
+
+function afterPopulatingCrfReviewers(config,formConfig,data){
+	formConfig.crfReviewers=data;
+	print(config,"afterPopulatingCrfReviewers");
+	print(config,"Number of CRF reviewerrs: "+formConfig.crfReviewers.rows.length);
+	
+	formConfig.table=config.document.createElement("table");
+	config.document.getElementById(config.div).appendChild(formConfig.table);
+
+	let qconfig=new Object();
+	qconfig.schemaName="lists";
+	qconfig.queryName="crfEntry";
+	let formStatusValue=1;//In Progress
+	if ("review" in config) formStatusValue=2;//Submitted
+	qconfig.filterArray=[LABKEY.Filter.create('formStatus',formStatusValue)];
+	let currentUser=LABKEY.Security.currentUser.id;
+	if ("review" in config){
+		let reviewer=0;
+		for (let i=0;i<formConfig.crfReviewers.rows.length;i++){
+			if (formConfig.crfReviewers.rows[i].User!=currentUser)
+				continue;
+			reviewer=1;
+			break;
+		}
+		print(config,"reviewer "+reviewer);
+		if (reviewer==0) return;
+	}
+	else{
+		//this only allows users to modify forms they have created
+		//qconfig.filterArray.push(LABKEY.Filter.create('UserId',currentUser));
+		//sometimes all people from reviewer list are allowed to complete each other's forms interchangeably
+		let userListX="";
+		for (let i=0;i<formConfig.crfEditors.rows.length;i++){
+			userId=formConfig.crfEditors.rows[i]['User'];
+			if (i>0) userListX+=";";
+			userListX+=String(userId);
+		}
+		qconfig.filterArray.push(
+			LABKEY.Filter.create('UserId',userListX,LABKEY.Filter.Types.IN));
+		
+
+	}
+	qconfig.success=function(data){afterPopulatingEntries(config,formConfig,data)};
+	LABKEY.Query.selectRows(qconfig);
+}
+
+
+function afterPopulatingEntries(config,formConfig,data){
+	
+	formConfig.formData=data;
+	let dataForms=formConfig.dataForms;
+	let table=formConfig.table;
+	print(config,"afterPopulatingEntries");
+
+	print(config,"Forms: "+dataForms.rows.length);
+	print(config,"InProgress: "+data.rows.length);
+
+
+	
+	for (let i=0;i<dataForms.rows.length; i++){
+	
+		let formKey=dataForms.rows[i].Key;
+		//print(config,"Key: "+formKey);
+		
+		//figure out master query name
+		let masterQuery="NONE";
+		let mID=dataForms.rows[i].masterQuery;
+		print(config,"Setting master query: "+mID);
+		for (let i2=0;i2<formConfig.inputLists.rows.length;i2++){
+			//queryName
+			if (formConfig.inputLists.rows[i2].Key!=mID)
+				continue;
+			
+			masterQuery=formConfig.inputLists.rows[i2].queryName;
+			print(config,"Setting master query "+masterQuery);
+			break;
+		}
+		
+		let row=table.insertRow(i);
+		let formName=dataForms.rows[i].formName;
+		let k=0;
+		for (let j=0;j<data.rows.length;j++){
+			let formId=data.rows[j].Form;
+			//print(config,"Row["+j+"] formId: "+formId);
+			if (formId!=formKey)
+				continue;
+			//insert form
+			let fbox=config.document.createElement("div");
+			fbox.classList.add("box","gold");
+
+			let fp=config.document.createElement("p");
+			let id=data.rows[j].entryId;
+			fp.innerHTML=id;
+			//it would be great if this were patientId if available
+			//fp.classList.add("large","center");
+			fp.classList.add("center");
+			fbox.appendChild(fp);
+			
+			let fp1=config.document.createElement("p");
+			let user="NONE";
+			for (let ii=0;ii<formConfig.users.rows.length;ii++){
+				if (formConfig.users.rows[ii].UserId!=data.rows[j].UserId)
+					continue;
+				user=formConfig.users.rows[ii].DisplayName;
+				break;
+			}
+			fp1.innerHTML=user;
+			fp1.classList.add("center");
+			fbox.appendChild(fp1);
+			
+			
+			let fp2=config.document.createElement("p");
+			fp2.innerHTML=formName;
+			fp2.classList.add("center");
+			fbox.appendChild(fp2);
+			
+			let fp3=config.document.createElement("p");
+			fp3.id="pid"+id;
+			fp3.innerHTML="NONE";
+			if (masterQuery!="NONE"){
+				let qconfig=new Object();
+				qconfig.schemaName='lists';
+				qconfig.queryName=masterQuery;
+				qconfig.filterArray=[LABKEY.Filter.create('crfRef',id)];
+				qconfig.success=function(data){setPatientId(config,formConfig,data,fp3.id);}
+				LABKEY.Query.selectRows(qconfig);
+			}
+			fp3.classList.add("center");
+			fbox.appendChild(fp3);
+			
+
+
+			
+			let cell=row.insertCell(k);
+			cell.classList.add("stretch");
+			cell.id="box"+data.rows[j].crfRef;
+
+			let button=config.document.createElement("button");
+			button.appendChild(fbox);
+			button.onclick=function(){openForm(config,formConfig,id,undefined)};
+
+			cell.appendChild(button);
+			k++;
+		}
+		if ("review" in config) continue;
+
+		let fbox=config.document.createElement("div");
+		fbox.classList.add("box","red");
+
+		let fp=config.document.createElement("p");
+		fp.innerHTML="Create new";
+		fbox.appendChild(fp);
+			
+			
+			
+		let fp2=config.document.createElement("p");
+		fp2.innerHTML=formName;
+		fp2.classList.add("center");
+		fbox.appendChild(fp2);
+			
+		let cell=row.insertCell(k);
+		cell.classList.add("stretch");
+
+		let button=config.document.createElement("button");
+		button.appendChild(fbox);
+		button.onclick=function(){createForm(config,formConfig,formKey)};
+
+		cell.appendChild(button);
+
+	}		
+
+
+}
+
+function setPatientId(config,formConfig,data,id){
+	if (data.rows.length==0) return;
+
+	let participantCode=data.rows[0].participantCode;
+	for (let i=0;i<formConfig.demographicData.rows.length;i++){
+		let entry=formConfig.demographicData.rows[i];
+		let key=entry.lsid;
+		if (key!=participantCode) continue;
+		let participantId=entry.ParticipantId;
+		config.document.getElementById(id).innerHTML=participantId;
+		break;
+	}
+	
+
+}
+
+function openForm(config,formConfig,crfRef, crfEntry){
+	print(config,"Clicked for "+crfRef);
+	if (crfEntry==undefined){
+		for (let i=0;i<formConfig.formData.rows.length;i++){
+			if (formConfig.formData.rows[i].entryId!=crfRef) continue;
+			print(config,"Setting "+formConfig.formData.rows[i].entryId);
+			crfEntry=formConfig.formData.rows[i];
+			break;
+		}
+	}
+	if (crfEntry==undefined) return;
+
+	let formEntry=undefined;
+	let formId=crfEntry.Form;
+	for (let i=0;i<formConfig.dataForms.rows.length;i++){
+		if (formConfig.dataForms.rows[i].Key!=formId) continue;
+		print(config,"Setting form "+formConfig.dataForms.rows[i].formName);
+		formEntry=formConfig.dataForms.rows[i];
+		break;
+	}
+	if (formEntry==undefined) return;
+
+	//select between review and view
+	let formUrl=formEntry["formUrl"];
+	if ("review" in config) formUrl=formEntry["reviewFormUrl"];
+	print(config,"Setting url "+formUrl);
+
+	let params = {
+		"name": formUrl, 
+		// The destination wiki page. The name of this parameter is not arbitrary.
+		"userid": crfEntry.UserId, 
+		"entryId": crfRef,
+		"formSetupQuery":formEntry["setupQuery"],
+		"registrationQueryId":formEntry["masterQuery"]
+	};
+
+	let containerPath= LABKEY.ActionURL.getContainer();
+        // This changes the page after building the URL. 
+	//Note that the wiki page destination name is set in params.
+        var wikiURL = LABKEY.ActionURL.buildURL("crf", formUrl , containerPath, params);
+        print(config,"Redirecting to "+wikiURL);
+
+		 
+	window.location = wikiURL;
+}
+
+function createForm(config,formConfig,formId){
+	print(config,"Create form w/id "+formId);
+	
+	let crfEntry=new Object();
+	crfEntry.entryId=Date.now();
+	crfEntry["Date"]=new Date();
+	crfEntry.formStatus=1;//In progress
+	//set other variables
+	//requires studyData as part of formConfig
+	let studyData=formConfig.studyData.rows[0];
+	crfEntry.EudraCTNumber=studyData.EudraCTNumber;
+	crfEntry.StudyCoordinator=studyData.StudyCoordinator;
+	crfEntry.StudySponsor=studyData.StudySponsor;
+	crfEntry.RegulatoryNumber=studyData.RegulatoryNumber;
+	crfEntry.UserId=LABKEY.Security.currentUser.id;
+	//requires crfEditors as part of formConfig
+	for (let i=0;i<formConfig.crfEditors.rows.length;i++){
+		print(config,"Checking user "+formConfig.crfEditors.rows[i].User);
+		if (formConfig.crfEditors.rows[i].User!=crfEntry.UserId) continue;
+		print(config,"Found user");
+		crfEntry.Site=formConfig.crfEditors.rows[i].Site;
+		print(config,"Setting site to id="+crfEntry.Site);
+		break;
+	}	
+	//from argument list
+	crfEntry.Form=formId;
+
+	let qconfig=new Object();
+	qconfig.schemaName='lists';
+	qconfig.queryName='crfEntry';
+	qconfig.success=function(data){openForm(config,formConfig,crfEntry.entryId,crfEntry)};
+	qconfig.rows=[crfEntry];
+	LABKEY.Query.insertRows(qconfig);
+}