瀏覽代碼

Merge to head

klanecek 2 月之前
父節點
當前提交
e0823d0c6d

+ 5 - 0
addingUser.md

@@ -0,0 +1,5 @@
+### Adding user
+
+- Add user to labkey site (Settings -> Site -> Users -> Add User).
+- Add user to project group (Settings -> Site -> Site Groups)
+- Add user to crfEditors/crfMonitors/crfSponsors.

+ 3 - 3
config/module.xml

@@ -5,11 +5,11 @@
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
     <bean id="moduleBean" class="org.labkey.api.module.SimpleModule">
         <property name="name" value="CRF"/>
-        <property name="version" value="0.1"/>
+	    <!--<property name="version" value="0.1"/>-->
         <property name="requiredServerVersion" value="0.0"/>
         <property name="moduleDependencies" value=""/>
-        <property name="svnRevision" value="0"/>
-        <property name="svnUrl" value="Not built from a source control working copy"/>
+	    <!--<property name="svnRevision" value="0"/>-->
+	    <!--<property name="svnUrl" value="Not built from a source control working copy"/>-->
         <property name="buildUser" value="astuden"/>
         <property name="buildTime" value="March 5 2020, 11:43 AM"/>
         <property name="buildOS" value="Ubuntu"/>

二進制
setup/crfEntryTemplate_listArchive.zip


二進制
setup/crfLayoutTemplate_listArchive.zip


二進制
setup/crfSettingsTemplate_listArchive.zip


二進制
setup/reviewCommentsTemplate_listArchive.zip


+ 9 - 3
views/approvedPortal.html

@@ -20,6 +20,11 @@ height:120px;
 }
 </style>
 
+<table cellspacing="2" cellpadding="5" border="0">
+<tr><td>Version: </td><td><strong id="version">0.0</strong></td></tr>
+</table>
+
+
 <div id="formDiv">
 </div>
 
@@ -32,12 +37,13 @@ height:120px;
 <script type "text/javascript">
 window.onload=init;
 function init(){
-	let config=new Object();
 	config.document=document;
 	config.reviewMode="APPROVED";
+	config.role="crfSponsor";
 	config.div="formDiv";
 	config.debugArea="formStatus";
-	print(config,"Starting");
-	generateFormArray(config);
+	clear();
+	print("XStarting");
+	generateFormArray();
 }
 </script>

+ 1 - 1
views/approvedPortal.webpart.xml

@@ -1,4 +1,4 @@
 <webpart xlmns="http://labkey.org/data/xml/webpart"
-	title="CRF Manager Portal">
+	title="CRF Approved Portal">
 	<view name="approvedPortal"/>
 </webpart>

+ 59 - 0
views/crfManager.html

@@ -0,0 +1,59 @@
+<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>
+
+<table cellspacing="2" cellpadding="5" border="0">
+<tr><td>Version: </td><td><strong id="version">0.0</strong></td></tr>
+</table>
+
+<div id="descDiv">
+</div>
+
+<div id="formDiv">
+</div>
+
+<div id="debugDiv" style="display:block">
+	<h3>Debug notes</h3>
+	<textarea cols="95" rows="5" id="debugArea">
+	</textarea>
+</div>
+
+
+<script type "text/javascript">
+window.onload=init;
+function init(){
+	config.document=document;
+	config.window=window;
+	config.div="formDiv";
+	config.debugArea="debugArea";
+	config.role="crfManager";
+	clear();
+	print("StartingX");
+	//add a button with callback
+	config.button = config.document.createElement("button");
+   config.button.innerHTML = "Generate description";
+	let el=config.document.getElementById('descDiv');
+	el.appendChild(config.button);
+	config.button.onclick=generateDescription;
+   generateFormArray();
+
+}
+</script>

+ 11 - 0
views/crfManager.view.xml

@@ -0,0 +1,11 @@
+<view xmlns="http://labkey.org/data/xml/view" title="CRF Management" frame="portal">
+	<dependencies>
+		<dependency path="crf/crfManager.js"/>
+      <dependency path="crf/formPortal.js"/>
+		<dependency path="crf/pdfkit.standalone.js" />
+		<dependency path="crf/blob-stream.js"/>
+	</dependencies>
+	<permissions>
+		<permission name="login"/>
+	</permissions>
+</view>

+ 4 - 0
views/crfManager.webpart.xml

@@ -0,0 +1,4 @@
+<webpart xlmns="http://labkey.org/data/xml/webpart"
+	title="CRF Manager">
+	<view name="crfManager"/>
+</webpart>

+ 8 - 3
views/formPortal.html

@@ -20,6 +20,10 @@ height:120px;
 }
 </style>
 
+<table cellspacing="2" cellpadding="5" border="0">
+<tr><td>Version: </td><td><strong id="version">0.0</strong></td></tr>
+</table>
+
 <div id="formDiv">
 </div>
 
@@ -33,12 +37,13 @@ height:120px;
 <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");
+	config.role="crfEditor";
+	clear();
+	print("Starting");
 	config.participantField="PatientId";
-	generateFormArray(config);
+	generateFormArray();
 }
 </script>

+ 9 - 3
views/reviewPortal.html

@@ -20,6 +20,11 @@ height:120px;
 }
 </style>
 
+<table cellspacing="2" cellpadding="5" border="0">
+<tr><td>Version: </td><td><strong id="version">0.0</strong></td></tr>
+</table>
+
+
 <div id="formDiv">
 </div>
 
@@ -32,12 +37,13 @@ height:120px;
 <script type "text/javascript">
 window.onload=init;
 function init(){
-	let config=new Object();
 	config.document=document;
 	config.reviewMode="REVIEW";
+	config.role="crfMonitor";
 	config.div="formDiv";
 	config.debugArea="formStatus";
-	print(config,"Starting");
-	generateFormArray(config);
+	clear();
+	print("Starting");
+	generateFormArray();
 }
 </script>

+ 7 - 18
views/visit.html

@@ -11,15 +11,11 @@ div.d1 {text-align:center; width=400px; background-color:#e0e0e0;
         font-size:      20px; margin-bottom:20px}
 </style>
 
-<table cellspacing="2" cellpadding="5" border="0">
-<tr><td>Version: </td><td><strong id="version">0.0</strong></td></tr>
-<tr><td>CRF ID: </td><td><strong id="crfRefId">1583163135258</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>
-<tr><td>Site: </td><td><strong id="siteName">Loading</strong></td></tr>
-<tr><td>Telephone(site): </td><td><strong id="sitePhone">Loading</strong></td></tr>
-<tr><td>Investigator: </td><td><strong id="investigatorName">Loading</strong></td></tr>
+<div id="staticDataDiv"/>
+<!--set static values -->
+
+<table cellspacing="2" cellpadding="5" border="0" id="staticTable">
+<tr><td width="200">CRF ID: </td><td><strong id="crfRefId">1583163135258</strong></td></tr>
 </table>
 
 <form name="visitForm" id="visitForm">
@@ -53,7 +49,6 @@ function init(){
 	//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;
 
 
@@ -61,14 +56,9 @@ function init(){
 	//config is part of crfVisit.js
 	//will this change if we are in views?
 	//config.review=true;
-	config.reviewMode=searchParams.get("reviewMode");
-	if (config.reviewMode=="EDIT") delete config.reviewMode;
 
 	//where to get ParticipantId
-	config.registrationQueryId=registrationQueryId;
-	config.registrationParticipantIdField="participantCode";
 	//pick this from study properties
-	config.participantField="PatientId";
 	config.masterForm="visitForm";
 	
 	config.document=document;
@@ -76,12 +66,11 @@ function init(){
 	config.debugDiv="debugDiv";
 	config.debugId="formStatus";
 	config.crfRefId="crfRefId";
+	config.registrationParticipantIdField="participantCode";
 	config.containerPath= LABKEY.ActionURL.getContainer();
 	//config.setupQueryName=formSetupQuery;
-	config.formName=searchParams.get("formName");
 	config.formId=searchParams.get("formId");
-	config.submitReportId="submitReport";
-	
+	config.role=searchParams.get('role');	
 	
 	clear();
 

+ 3 - 1
views/visit.view.xml

@@ -1,6 +1,8 @@
 <view xmlns="http://labkey.org/data/xml/view" title="CRF Form">
 	<dependencies>
-		<dependency path="crf/crfVisit.js"/>
+      <dependency path="crf/runQuery.js"/>
+      <dependency path="crf/participantIdManager.js"/>
+      <dependency path="crf/crfVisit.js"/>
 		<dependency path="crf/crfReview.js"/>
 		<!--local copy of pdfkit, version 0.10.0-->
 		<!--https://github.com/devongovett/pdfkit/releases/download/v0.10.0/pdfkit.standalone.js-->

+ 342 - 0
web/crf/crfManager.js

@@ -0,0 +1,342 @@
+function generateDescription(){
+	//loop over all forms
+	//read the setup
+	let debug=true;
+   let fName='[generateDescription]';
+   if (debug)
+      print(fName);
+
+	//add object to store form related data
+	config.formConfig=new Object();
+
+	config.formConfig.softwareVersion='0.0.1';
+
+	
+	//set containers for data and configuration
+	setContainer('data',LABKEY.ActionURL.getContainer());
+	setContainer('config',LABKEY.ActionURL.getContainer());
+	setContainer('CRF',LABKEY.ActionURL.getContainer());
+
+	let selectRows=new Object();
+	//this is local data
+	selectRows.containerPath=getContainer('CRF');
+	selectRows.schemaName='lists';
+	selectRows.queryName='crfSettings';
+	//store form related data to this object
+	selectRows.success=function(data){afterSettings(data,collectLayout);};
+	LABKEY.Query.selectRows(selectRows);
+
+}
+
+function collectLayout(){
+
+	let queryArray=new Array();
+	//users
+	queryArray.push(makeQuery('CRF','users','userData',[]));
+	queryArray[queryArray.length-1].schemaName='core';
+	
+	//Forms	
+	queryArray.push(makeQuery('config','Forms','formData',[]));
+	//FormSetup	
+	queryArray.push(makeQuery('config','FormSetup','formSetup',[]));
+	//inputLists
+	queryArray.push(makeQuery('config','inputLists','inputLists',[]));
+	//
+
+	print('running getDataFromQueries');
+	getDataFromQueries(queryArray,loadForms);
+}
+
+function findName(listId){
+	let frows=config.formConfig.inputLists.rows;
+	for (let i=0;i<frows.length;i++){
+		if (frows[i]['Key']!=listId) continue;
+		return frows[i]['queryName'];
+	}
+}
+
+function loadForms(){
+	print('loadedData');
+	let queryArray=new Array();
+	
+	let frows=config.formConfig.formSetup.rows;
+	for (let i=0;i<frows.length;i++){
+		let listId=frows[i]['queryName'];
+		//skip forms only
+		let showFlag=frows[i]['showFlag'];
+		let showQuery=frows[i]['showQuery'];
+		if (showFlag=='REVIEW') continue;
+		let listName=findName(listId);
+		print(listName);
+		queryArray.push(makeQuery('data',listName,listName,[]));
+		if (showFlag=='NONE') continue;
+		queryArray.push(makeQuery('data',showQuery,showQuery,[]));
+	}
+
+	getDataFromQueries(queryArray,printLayout);
+
+}
+
+function getList(formId){
+	let fList=new Array();
+	let frows=config.formConfig.formSetup.rows;
+	for (let i=0;i<frows.length;i++){
+		if (frows[i]['formName']!=formId) continue;
+		let listId=frows[i]['queryName'];
+		let showFlag=frows[i]['showFlag'];
+		let showQuery=frows[i]['showQuery'];
+		if (showFlag=='REVIEW') continue;
+		let fObj=new Object();
+		fObj['queryName']=findName(listId);
+		fObj['title']=frows[i]['title'];
+		fList.push(fObj);
+		if (showFlag=='NONE') continue;
+		let fObj1=new Object();
+		fObj1['queryName']=showQuery;
+		fObj1['title']=frows[i]['title']+' (details)';
+		fList.push(fObj1);
+	}
+	return fList;
+}
+
+function printField(field){
+	let name=field['name'];
+	if (name=='Key') return;
+	if (name=='crfRef') return;
+	let type=field['type'];
+	let qName='';
+	if ('lookup' in field){
+		qName=field.lookup.queryName;
+	}
+	print(name+' '+type+'/'+qName);
+	printPDF(field);
+}
+
+function addLookup(lookupList,field){
+	if ('lookup' in field){
+		lookupList.add(field.lookup.queryName);
+	}
+}
+
+function printFields(lookupList,listName){
+	let fields=config.formConfig[listName].metaData.fields;
+	//print('getFields '+listName+': '+fields.length);
+	for (f in fields){
+		printField(fields[f]);
+		addLookup(lookupList,fields[f]);		
+		//printPDF(fields[f]);
+	}
+}
+
+function printData(){
+	let frows=config.formConfig.formData.rows;
+	let lookupList=new Set();
+	for (let i=0;i<frows.length;i++){
+		let formId=frows[i]['Key'];
+		print(frows[i]['formName']);
+		printTitlePDF(20,frows[i]['formName']);
+		let fList=getList(formId);
+		for (let j=0;j<fList.length;j++){
+			print(fList[j]['queryName']);
+			printTitlePDF(16,fList[j]['title']);
+			printFields(lookupList,fList[j]['queryName']);
+		}
+	}
+	print('all done');
+	let queryArray=new Array();
+	for (let item of lookupList){
+		queryArray.push(makeQuery('data',item,item,[]));
+	}
+	let cb=function(){printLookup(lookupList);};
+	getDataFromQueries(queryArray,cb);
+}
+
+function printQuery(queryName){
+	printTitlePDF(16,queryName);
+	print(queryName);
+	let frows=config.formConfig[queryName].rows;
+	print('rows: '+frows);
+	let fields=config.formConfig[queryName].metaData.fields;
+	let field=undefined;
+	for (f in fields){
+		if (fields[f].name=='Key') continue;
+		field=fields[f];
+		break;
+	}
+	for (let i=0;i<frows.length;i++){
+		printPDFEntry(field,frows[i]);
+	}
+}
+
+function printLookup(lookupList){
+
+	printTitlePDF(20,'Enumerators');
+	for (let item of lookupList){
+		printQuery(item);
+	}
+	config.doc.end();
+
+}
+
+function checkBlob(){
+	print("checkBlob: "+config.blob);
+	if (config.blob) {
+		clearInterval(config.blobInterval);
+		config.a.href = config.window.URL.createObjectURL(config.blob);
+		print("HREF: "+config.a.href);
+		config.a.download = 'test.pdf';
+		config.a.click();
+		config.window.URL.revokeObjectURL(config.a.href);
+	}
+	config.count=config.count+1;
+	print("Eval: "+config.count);
+	if (config.count>100){
+		clearInterval(config.blobInterval);
+	}
+
+}
+
+
+function printLayout(){
+
+	config.doc=new PDFDocument({margins: {top:20,left:50,right:100,bottom:20},paragraphGap:10});
+	//config.doc.end();
+	let stream = config.doc.pipe(blobStream()).on("finish",function(){
+			config.blob=stream.toBlob("application/pdf");});
+	
+	print("BLob: "+config.blob);
+	config.a = config.document.createElement("a");
+	config.document.body.appendChild(config.a);
+	config.a.innerHTML="Download PDF";
+	config.a.style = "display: none";
+	config.count=0;
+	//run until blob is set
+	config.blobInterval=setInterval(checkBlob,1000);
+
+	//pick data from crfForm list
+        print("Printing form");
+	printData();
+}
+
+function printTitlePDF(fontSize,title){
+	config.doc.y+=10;
+   //x margin
+   //config.doc.x=50;
+	config.doc.font('Courier-Bold').fontSize(fontSize).text(title);
+}
+
+function printPDF(field){
+	//object field should have a name, type, caption
+	//entry should have field.name
+	//lookup is null or has a lookup table LUT 
+	//for value v of entry[field.name]
+	//
+	//the total width of a A4 page is 598 px, 
+	//left margin is 72. With a right margin of 50,
+	//the total available with is 476 px.
+
+   let fName='[printPDF]';
+	
+	let w=676;
+	let spacing=25;
+	let w1=(w-spacing)*0.5;
+	let fontSize=14;	
+   let margin=50;
+   let topMargin=20;
+	
+	print(fName+' entry['+field.name);
+	print('printPDF: field type:'+field.type);
+
+   config.doc.y+=10;
+   let ty1=config.doc.y;
+
+
+	//measure text
+	let label=field.caption;
+	let opt={width:w1};
+	config.doc.fontSize(fontSize);
+	
+	//for more eloquent display the height of the text
+	//can be measured prior to output
+	//use currentLineHeight to scale height
+	//let lineH=config.doc.currentLineHeight(1);
+	//let h=config.doc.heightOfString(label,opt)/lineH;
+
+
+	//print label
+	config.doc.font('Courier').text(label,opt);
+	
+   //store x value for later use
+	let tx=config.doc.x;
+	let ty=config.doc.y;
+   
+   let height=config.doc.heightOfString(label,opt);
+   let lWidth=config.doc.widthOfString(label,opt);
+   
+   let ypos=ty1;
+   if (ty<ty1) ypos=topMargin;
+   let xpos=lWidth+spacing+margin;
+   if (height>2*fontSize){
+      //multiline text
+      ypos=0.5*(ty+ty1);
+      if (ty<ty1){
+         //page break
+         ypos=0.5*ty;
+      }
+      xpos=w1+spacing+margin;
+	}
+
+	//shift for value output
+	config.doc.x=xpos;
+   config.doc.y=ypos;
+	let v='['+field.type;
+	if ('lookup' in field){
+		v+='/'+field.lookup.queryName;
+	}
+   v+=']';
+	print('v: '+v);
+	config.doc.font('Courier-Bold').text(v,opt);
+
+	//restore x value
+	config.doc.x=tx;
+   //set y to maximum of current and position of the label
+   if (config.doc.y<ty) config.doc.y=ty;
+	
+}
+
+function printPDFEntry(field,entry){
+	//object field should have a name, type, caption
+	//entry should have field.name
+	//lookup is null or has a lookup table LUT 
+	//for value v of entry[field.name]
+	//
+	//the total width of a A4 page is 598 px, 
+	//left margin is 72. With a right margin of 50,
+	//the total available with is 476 px.
+	
+	let w=476;
+	let spacing=25;
+	let w1=(w-spacing)*0.5;
+	let fontSize=14;	
+	
+	print('printPDF: entry['+field.name);
+	print('printPDF: field type:'+field.type);
+
+	//measure text
+	let label=entry[field.name];
+	let opt={width:w1};
+	config.doc.fontSize(fontSize);
+	
+	//for more eloquent display the height of the text
+	//can be measured prior to output
+	//use currentLineHeight to scale height
+	//let lineH=config.doc.currentLineHeight(1);
+	//let h=config.doc.heightOfString(label,opt)/lineH;
+
+
+	//print label
+	config.doc.font('Courier').text(label,opt);
+	
+	
+}
+

File diff suppressed because it is too large
+ 431 - 233
web/crf/crfVisit.js


+ 335 - 286
web/crf/formPortal.js

@@ -1,288 +1,367 @@
-function print(config, msg){
+//global config variable
+const config=new Object();
+
+function print(msg){
 	config.document.getElementById(config.debugArea).value+="\n"+msg;
 }
 
-function clear(config){
+function clear(){
 	config.document.getElementById(config.debugArea).value="";
 }
 
-function getMode(config){
-	if ("reviewMode" in config){
-		return config.reviewMode;
+function getMode(){
+	if ("role" in config){
+		return config.role;
 	}
-	return "EDIT";
+	return "crfEditor";
 }
 
-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 doNothing(){
+	print('doNothing called');
+}
+
+function makeQuery(containerName,queryName,fieldName,filterArray){
+	//queryArray should contain elements with
+	//- fieldName to set the data variable
+	//- containerName to select container (data,config,CRF)
+	//- queryName to select query
+	//- filterArray to perform filtering, empty array works
+	//- callback cb to be called with no arguments
+	
+	let e=new Object();
+	e.containerName=containerName;
+	e.queryName=queryName;
+	e.fieldName=fieldName;
+	e.filterArray=filterArray;
+	return e;
 }
 
-function generateFormArray(config){
-	print(config,"generateFormArray "+getMode(config));
+function getDataFromQueries(queryArray,cb){
+	afterQuery(new Object(),-1,queryArray,cb);
+}
 
-	let formConfig=new Object();
+function afterQuery(data,id,queryArray,cb){
+
+	print('afterQuery['+id+']: ');
+
+	if (id>-1){
+		let fieldName=queryArray[id].fieldName;
+		print('afterQuery['+fieldName+']: '+data.rows.length);
+		//uses config.formConfig
+		config.formConfig[fieldName]=data;
+	}
+	id+=1;
+	if (id==queryArray.length) {
+		cb();
+		return;
+	}
 
-	let qconfig=new Object();
 
+	let e=queryArray[id];
+	let qconfig=new Object();
+	qconfig.containerPath=getContainer(e.containerName);
 	qconfig.schemaName="lists";
-	qconfig.queryName="Forms";
+	if ("schemaName" in e){
+		print('afterQuery: schemaName='+e.schemaName);
+		qconfig.schemaName=e.schemaName;
+	}
 
+	if ("columns" in e){
+		print('afterQuery: columns='+e.columns);
+		qconfig.columns=e.columns;
+	}
+	qconfig.queryName=e.queryName;
+	//this should point to configuration container
+	//don't filter -> so we can pick up other forms (say registration) later on
+	//qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)];
+	if ("filterArray" in e)
+		qconfig.filterArray=e.filterArray;
+	
 	//qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)]
-	qconfig.success=function(data){afterPopulatingForms(config,formConfig,data)};
+	qconfig.success=function(data){afterQuery(data,id,queryArray,cb);};
+	qconfig.failure=doNothing;
 	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 printMessage(msg){
+	let txt=config.document.createElement("p");
+	config.document.getElementById(config.div).appendChild(txt);
+	txt.innerText=msg;
 }
 
-function afterPopulatingUsers(config,formConfig,data){
-	
-	formConfig.users=data;
-	print(config,"afterPopulatingUsers");
-	print(config,"Number of users: "+formConfig.users.rows.length);
-
+function userName(id){
+	let formConfig=config.formConfig;
+	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";
+}
 
-	let qconfig=new Object();
+function setContainer(label,container){
+	if (!(config.formConfig.hasOwnProperty('container'))){
+		config.formConfig.container=new Array();
+	}
+	config.formConfig.container[label]=container;
+}
 
-	qconfig.schemaName="lists";
-	qconfig.queryName="inputLists";
-	qconfig.success=function(data){afterPopulatingLists(config,formConfig,data)};
-	LABKEY.Query.selectRows(qconfig);
+function getContainer(label){
+	return config.formConfig.container[label];
 }
 
-function afterPopulatingLists(config,formConfig,data){
 
-	formConfig.inputLists=data;
-	print(config,"afterPopulatingLists");
-	print(config,"Number of lists: "+formConfig.inputLists.rows.length);
+function generateFormArray(){
+	print("generateFormArray "+getMode());
 	
-
-	let qconfig=new Object();
-
-	qconfig.schemaName="study";
-	qconfig.queryName="StudyProperties";
-	qconfig.columns="StudySponsor,StudyCoordinator,EudraCTNumber,RegulatoryNumber,SubjectColumnName"
-	//make sure SubjectColumnName is part of the view
-	qconfig.success=function(data){afterPopulatingStudyData(config,formConfig,data)};
-	LABKEY.Query.selectRows(qconfig);
-}
+	config.formConfig=new Object();
+	config.formConfig.softwareVersion='0.1.9';
+	//report software version
+	config.document.getElementById('version').innerText=config.formConfig.softwareVersion;	
 	
-function afterPopulatingStudyData(config,formConfig,data){
+	setContainer('data',LABKEY.ActionURL.getContainer());
+	setContainer('config',LABKEY.ActionURL.getContainer());
+	setContainer('CRF',LABKEY.ActionURL.getContainer());
+
+	let selectRows=new Object();
+	//this is local data
+	selectRows.containerPath=getContainer('CRF');
+	selectRows.schemaName='lists';
+	selectRows.queryName='crfSettings';
+	//store form related data to this object
+	selectRows.success=function(data){afterSettings(data,collectData);};
+	LABKEY.Query.selectRows(selectRows);
 
-	formConfig.studyData=data;
-	print(config,"afterPopulatingStudyData");
-	print(config,"Number of study data entries: "+formConfig.studyData.rows.length);
-	print(config,"ParticipantId: "+formConfig.studyData.rows[0].SubjectColumnName);
+}
 
-	
+//also used by crfManager
+function afterSettings(data,cb){
 
-	let qconfig=new Object();
+	config.formConfig.settings=new Array();
+	for (let i=0;i<data.rows.length;i++){
+		let n=data.rows[i]['name'];
+		let v=data.rows[i]['value'];
+		config.formConfig.settings[n]=v;
+	}
 
-	qconfig.schemaName="study";
-	let demographicDataId=formConfig.dataForms.rows[0].masterQuery;
-	let demographicDataQuery="NONE";
-	for (let i=0;i<formConfig.inputLists.rows.length;i++){
-		let entry=formConfig.inputLists.rows[i];
-		print(config,"inputList ["+i+"] ["+entry.Key+"] "+entry.queryName);
-		if (entry.Key==demographicDataId){
-			demographicDataQuery=entry.queryName;
-			break;
-		}
+	let st=config.formConfig.settings;
+	print('afterSettings');
+	for (let k in st){
+		print('\t'+k+'='+st[k]);
 	}
-	print(config,'Setting demographic query to '+demographicDataQuery);
 
-	qconfig.queryName=demographicDataQuery;
-	//qconfig.queryName="demographicData";
-	
-	qconfig.success=function(data){afterPopulatingDemographicData(config,formConfig,data)};
-	LABKEY.Query.selectRows(qconfig);
+	//if ('dataContainer' in st){
+	//	setContainer('data',st['dataContainer']);
+	//}
+	let vname='configContainer';
+	if (vname in st){
+		setContainer('config',st[vname]);
+	}
+	print('Config: '+getContainer('config'));
+	print('Data: '+getContainer('data'));
+   //collectData();
+   cb();
 }
 
-function afterPopulatingDemographicData(config,formConfig,data){	
+function collectData(){
+	//setup queryArray
+	let queryArray=new Array();
+
+   //static variables
+	queryArray.push(makeQuery('data','crfStaticVariables','crfStaticVariables',[]));
+	//Forms
+	queryArray.push(makeQuery('config','Forms','dataForms',[]));
+	//users
+	queryArray.push(makeQuery('data','users','users',[]));
+	queryArray[queryArray.length-1].schemaName='core';
+	//inputLists
+	queryArray.push(makeQuery('config','inputLists','inputLists',[]));
+	//crfEditors
+	queryArray.push(makeQuery('config','crfEditors','crfEditors',[]));
+	//crfMonitors
+	queryArray.push(makeQuery('config','crfMonitors','crfMonitors',[]));
+	//crfSponsors
+	queryArray.push(makeQuery('config','crfSponsors','crfSponsors',[]));
+	//crfManagers
+	queryArray.push(makeQuery('config','crfManagers','crfManagers',[]));
+	//FormStatus
+	queryArray.push(makeQuery('config','FormStatus','formStatusg',[]));
+	//site
+	queryArray.push(makeQuery('config','site','siteData',[]));
+	//crfEntry
+	queryArray.push(makeQuery('data','crfEntry','crfEntries',[]));
+
+	getDataFromQueries(queryArray,addStudyData);
+	//getDataFromQueries(queryArray,fcontinue);
+}
 
-	formConfig.demographicData=data;
-	print(config,"afterPopulatingDemographic");
-	print(config,"Number of patients: "+formConfig.demographicData.rows.length);
+function addStudyData(){
+	//setup queryArray
+	let queryArray=new Array();
 	
+	queryArray.push(makeQuery('data','StudyProperties','studyData',[]));
+	let e=queryArray[queryArray.length-1];
+	e.schemaName='study';
+   let columnModel="";
+	let varRows=config.formConfig['crfStaticVariables'].rows;
+	for (let i=0;i<varRows.length;i++){
+      if (i>0) columnModel+=',';
+      columnModel+=varRows[i]['staticVariable'];
+   }
+	e.columns=columnModel;
+   getDataFromQueries(queryArray,generateWidgets);
 
-	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);
-	
+function generateWidgets(){
+   let fName='[generateWidgets]';
+	let formConfig=config.formConfig;
 
-	let qconfig=new Object();
+	print("Number of study data entries: "+formConfig.studyData.rows.length);
+	print("ParticipantId: "+formConfig.studyData.rows[0].SubjectColumnName);
 
-	qconfig.schemaName="lists";
-	qconfig.queryName="crfReviewers";
-	qconfig.success=function(data){afterPopulatingCrfReviewers(config,formConfig,data)};
-	LABKEY.Query.selectRows(qconfig);
-}
+	let dataForms=formConfig.dataForms.rows;
 
-function afterPopulatingCrfReviewers(config,formConfig,data){
-	formConfig.crfReviewers=data;
-	print(config,"afterPopulatingCrfReviewers");
-	print(config,"Number of CRF reviewerrs: "+formConfig.crfReviewers.rows.length);
-	let qconfig=new Object();
+	formConfig.table=config.document.createElement("table");
+	config.document.getElementById(config.div).appendChild(formConfig.table);
 
-	qconfig.schemaName="lists";
-	qconfig.queryName="crfManagers";
-	qconfig.success=function(data){afterPopulatingCrfManagers(config,formConfig,data)};
-	LABKEY.Query.selectRows(qconfig);
-}
+	let accessModeColumn=getMode()+'Status';
+	print('accessModeColumn '+accessModeColumn);
+	//cutting down on number of fields
+   //let creatorModeColumn=getMode()+'Creator';
 
-function afterPopulatingCrfManagers(config,formConfig,data){
-	formConfig.crfManagers=data;
-	print(config,"afterPopulatingCrfManagers");
-	print(config,"Number of CRF managers: "+formConfig.crfManagers.rows.length);
-	let qconfig=new Object();
 
-	qconfig.schemaName="lists";
-	qconfig.queryName="FormStatus";
-	qconfig.success=function(data){afterPopulatingFormStatus(config,formConfig,data)};
-	LABKEY.Query.selectRows(qconfig);
-}
+	//switch from status based to form based access
+	print("Forms: "+dataForms.length);
+	print("Entries: "+formConfig.crfEntries.rows.length);
+	let fEntries=formConfig.crfEntries.rows;
+	let users=formConfig.users.rows;
+	let currentUserId=LABKEY.Security.currentUser.id;
+	let currentUser=undefined;
 
-function afterPopulatingFormStatus(config,formConfig,data){
-	formConfig.formStatus=data;
-	print(config,"afterPopulatingFormStatus");
-	print(config,"Number of states in FormStatus: "+formConfig.formStatus.rows.length);
-	
-	formConfig.table=config.document.createElement("table");
-	config.document.getElementById(config.div).appendChild(formConfig.table);
+	for (let i=0;i<users.length;i++){
+		if (users[i].UserId!=currentUserId) continue;
+		currentUser=users[i];
+	}
 
-	let qconfig=new Object();
-	qconfig.schemaName="lists";
-	qconfig.queryName="crfEntry";
-
-	let visibleLevel="crfEditor";
-	if ("reviewMode" in config) {
-		if (config.reviewMode=="REVIEW")
-			visibleLevel="crfReviewer";
-		else
-			visibleLevel="crfManager";
+   //determine the role filter
+	let fList=config.role+'s';
+   //check for users that fit the role, 
+   //fRows lists all users for role
+	let fRows=config.formConfig[fList].rows;
+   print(fName+' candidates: '+fRows.length)
+	//current user must be in the list
+	
+	let currentUserRoles=new Array();
+	//the same user can act for multiple sites
+	for (let i=0;i<fRows.length;i++){
+		if (fRows[i].User!=currentUser.UserId) continue;
+		currentUserRoles.push(fRows[i]);
 	}
 
+   //cludge for public sites where all users can act as anything
+	let sts=config.formConfig.settings;
+	let vName='allowAllForSite';
+	if (vName in sts){
+		let tempUserRole=new Object();
+		tempUserRole.User=currentUser.UserId;
+		tempUserRole.Site=parseInt(sts[vName]);
+		currentUserRoles.push(tempUserRole);
+	}
 
-	let formStatusValue="";
-	for (let i=0;i<formConfig.formStatus.rows.length;i++){
-		if (formConfig.formStatus.rows[i].visibleLevel==visibleLevel){
-			if (formStatusValue.length>0) formStatusValue+=";";
-			formStatusValue+=String(formConfig.formStatus.rows[i].Key);
-		}
+   //currentUser was not matched in fRows
+	if (currentUserRoles.length==0){
+		printMessage('User '+currentUser.DisplayName+" can't act as "+config.role);
+		return;
 	}
-	
-	//if ("review" in config) formStatusValue="2";//Submitted
-	qconfig.filterArray=[LABKEY.Filter.create('formStatus',formStatusValue,LABKEY.Filter.Types.IN)];
-	let currentUser=LABKEY.Security.currentUser.id;
-	if ("reviewMode" in config){
-		let userList=formConfig.crfReviewers;
-		if (config.reviewMode=="APPROVED")
-			userList=formConfig.crfManagers;
-
-		let reviewer=0;
-		for (let i=0;i<userList.rows.length;i++){
-			if (userList.rows[i].User!=currentUser)
-				continue;
-			reviewer=1;
-			break;
+
+   //currentUser should be also attached to the site of the document
+	let currentSites=new Array();
+	let siteRows=config.formConfig.siteData.rows;
+	for (let i=0;i<siteRows.length;i++){
+		for (let j=0;j<currentUserRoles.length;j++){
+			if (siteRows[i].siteNumber!=currentUserRoles[j].Site) continue;
+			currentSites.push(siteRows[i]);
 		}
-		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));
-		
 
+	config.formConfig.currentSites=currentSites;
+	let msg='User '+currentUser.DisplayName+' acting as '+config.role+' for (';
+	for (let i=0;i<currentSites.length;i++){
+		if (i>0) msg+=', ';
+		msg+=currentSites[i].siteName;
 	}
-	qconfig.success=function(data){afterPopulatingEntries(config,formConfig,data)};
-	LABKEY.Query.selectRows(qconfig);
-}
+	msg+=')';
+	printMessage(msg);
 
+   //browse through forms
+	for (let i=0;i<dataForms.length;i++){
 
-function afterPopulatingEntries(config,formConfig,data){
-	
-	formConfig.formData=data;
-	let dataForms=formConfig.dataForms;
-	let table=formConfig.table;
-	print(config,"afterPopulatingEntries");
+      //dataForms is Forms
+		let qForm=dataForms[i];
+		let formKey=qForm.Key;
+		
+		//add row for each form
+		let row=formConfig.table.insertRow(i);
+		let formName=qForm.formName;
+		print("["+i+"/"+formKey+']: '+formName);
 
-	print(config,"Forms: "+dataForms.rows.length);
-	print(config,"InProgress: "+data.rows.length);
+      //column counter
+		let k=0;
 
+      //get the target status
+		let formStatus=qForm[accessModeColumn];
+		print('target formStatus '+formStatus);
 
-	
-	for (let i=0;i<dataForms.rows.length; i++){
-	
-		let formKey=dataForms.rows[i].Key;
-		print(config,"Key["+i+"]: "+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)
+		for (let j=0;j<fEntries.length;j++){
+			let entry=fEntries[j];
+			let formId=entry.Form;
+			
+         if (formId!=formKey)
 				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)
+         //only select forms where status matches the target status
+			if (entry.FormStatus!=formStatus){
+				continue;
+			}
+
+			print('Candidate '+entry.entryId);
+			//TODO: smart filter on user (now we get to see all)
+			//
+			//for editors
+			if (config.role=='crfEditor' && entry.UserId!=currentUser.UserId){
+				print('Skipping identity mismatch: '+entry.UserId+'/'+currentUser.UserId);  
+				continue;
+			}
+         
+         //for others
+			let matchingSite=-1;
+			let potentialSiteNumbers="[";
+			for (let k=0;k<currentSites.length;k++){
+				if (k>0) potentialSiteNumbers+=',';
+				potentialSiteNumbers+=currentSites[k].siteNumber;
+				if (entry.Site!=currentSites[k].siteNumber) continue;
+				matchingSite=currentSites[k].siteNumber;
+				break;
+			}
+			potentialSiteNumbers+=']';
+			if (matchingSite==-1){
+				print('Skipping wrong site: '+entry.Site+'/'+potentialSiteNumbers);
 				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;
+			let id=entry.entryId;
 			fp.innerHTML=id;
 			//it would be great if this were patientId if available
 			//fp.classList.add("large","center");
@@ -291,10 +370,10 @@ function afterPopulatingEntries(config,formConfig,data){
 			
 			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)
+			for (let ii=0;ii<users.length;ii++){
+				if (users[ii].UserId!=entry.UserId)
 					continue;
-				user=formConfig.users.rows[ii].DisplayName;
+				user=users[ii].DisplayName;
 				break;
 			}
 			fp1.innerHTML=user;
@@ -309,16 +388,14 @@ function afterPopulatingEntries(config,formConfig,data){
 			
 			let fp3=config.document.createElement("p");
 			fp3.id="pid"+id;
-			fp3.innerHTML="NONE";
-			print(config,'Setting participant id from query: '+masterQuery);
-			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);
-			}
+         let pid=entry['participantStudyId'];
+         let loc=entry['participantLocalId'];
+         let label='';
+         if (pid) label+=pid+' ';
+         if (loc) label+='(Local: '+loc+')';
+         if (label.length==0) label="NONE";
+			fp3.innerHTML=label;
+
 			fp3.classList.add("center");
 			fbox.appendChild(fp3);
 			
@@ -327,16 +404,24 @@ function afterPopulatingEntries(config,formConfig,data){
 			
 			let cell=row.insertCell(k);
 			cell.classList.add("stretch");
-			cell.id="box"+data.rows[j].crfRef;
+			cell.id="box"+entry.crfRef;
 
 			let button=config.document.createElement("button");
 			button.appendChild(fbox);
-			button.onclick=function(){openForm(config,formConfig,id,undefined)};
+			button.onclick=function(){openForm(entry)};
 
 			cell.appendChild(button);
 			k++;
 		}
-		if ("reviewMode" in config) continue;
+		print('finished checking existing forms');
+
+		//only those that are allowed to create forms
+		//print('Status: '+qForm[creatorModeColumn]);
+		
+      let creator=qForm['creator'];
+      if (!creator) continue;
+      if (creator!=getMode()) continue;
+      //if (qForm[creatorModeColumn]!='TRUE') continue;
 
 		let fbox=config.document.createElement("div");
 		fbox.classList.add("box","red");
@@ -357,7 +442,7 @@ function afterPopulatingEntries(config,formConfig,data){
 
 		let button=config.document.createElement("button");
 		button.appendChild(fbox);
-		button.onclick=function(){createForm(config,formConfig,formKey)};
+		button.onclick=function(){createForm(formKey)};
 
 		cell.appendChild(button);
 
@@ -366,46 +451,16 @@ function afterPopulatingEntries(config,formConfig,data){
 
 }
 
-function setPatientId(config,formConfig,data,id){
-	if (data.rows.length==0) return;
-
-	let participantCode=data.rows[0].participantCode;
-	let participantField=formConfig.studyData.rows[0].SubjectColumnName;
-
-
-	print(config,'participantCode: '+participantCode);
-	print(config,'participantField: '+participantCode);
-	for (let i=0;i<formConfig.demographicData.rows.length;i++){
-		let entry=formConfig.demographicData.rows[i];
-		let key=entry.lsid;
-		print(config,'Comparing to: '+key);
-		if (key!=participantCode) continue;
-		let participantId=entry[participantField];
-		config.document.getElementById(id).innerHTML=participantId;
-		break;
-	}
+function openForm(crfEntry){
+	let formConfig=config.formConfig;
+	let crfRef=crfEntry.entryId;
 	
-
-}
-
-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;
+	print("Clicked for "+crfRef);
 
 	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);
+		print("Setting form "+formConfig.dataForms.rows[i].formName);
 		formEntry=formConfig.dataForms.rows[i];
 		break;
 	}
@@ -414,7 +469,7 @@ function openForm(config,formConfig,crfRef, crfEntry){
 	//select between review and view
 	//let formUrl=formEntry["formUrl"];
 	//if ("reviewMode" in config) formUrl=formEntry["reviewFormUrl"];
-	//print(config,"Setting url "+formUrl);
+	//print("Setting url "+formUrl);
 
 	//direct all to the same html
 	let formUrl="visit";
@@ -424,27 +479,26 @@ function openForm(config,formConfig,crfRef, crfEntry){
 	let params = {
 		"name": formUrl, 
 		// The destination wiki page. The name of this parameter is not arbitrary.
-		"userid": crfEntry.UserId, 
 		"entryId": crfRef,
-		"registrationQueryId":"NOT USED",
-		"reviewMode":reviewMode,
 		"formId":formId,
-		"formName":"NOT USED" 
+		"role" : config.role
 	};
 
 	//"formSetupQuery":formEntry["setupQuery"],
 	let containerPath= LABKEY.ActionURL.getContainer();
-        // This changes the page after building the URL. 
+   // 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);
+   var wikiURL = LABKEY.ActionURL.buildURL("crf", formUrl , containerPath, params);
+   print("Redirecting to "+wikiURL);
 
 		 
 	window.location = wikiURL;
 }
 
-function createForm(config,formConfig,formId){
-	print(config,"Create form w/id "+formId);
+function createForm(formId){
+	let formConfig=config.formConfig;
+
+	print("Create form w/id "+formId);
 	
 	let crfEntry=new Object();
 	crfEntry.entryId=Date.now();
@@ -454,27 +508,22 @@ function createForm(config,formConfig,formId){
 	//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;
+   let varRows=formConfig['crfStaticVariables'].rows;
+   for (let i=0;i<varRows.length;i++){
+      let varName=varRows[i].staticVariable;
+	   crfEntry[varName]=studyData[varName];
+   }
 	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;
-	}	
+	crfEntry.Site=config.formConfig.currentSites[0].siteNumber;
+	print("Setting site to id="+crfEntry.Site);
 	//from argument list
 	crfEntry.Form=formId;
 
 	let qconfig=new Object();
+	qconfig.containerPath=getContainer('data');
 	qconfig.schemaName='lists';
 	qconfig.queryName='crfEntry';
-	qconfig.success=function(data){openForm(config,formConfig,crfEntry.entryId,crfEntry)};
+	qconfig.success=function(data){openForm(crfEntry)};
 	qconfig.rows=[crfEntry];
 	LABKEY.Query.insertRows(qconfig);
 }

+ 404 - 0
web/crf/participantIdManager.js

@@ -0,0 +1,404 @@
+//all functions are based off of participantManager (print, config, etc.)
+
+function addSelectOptions(){
+   let pM=this;
+   if (pM.mode=="LOCAL") return;
+   
+   let input=pM.getInputElement();
+   let config=pM.config;
+
+   let fName='addParticipantSelectOptions';
+   pM.print(fName+' input '+input);
+   let opt=config.document.createElement("option");
+   opt.value=-1;
+
+   opt.text="<Select>";
+   input.options[input.options.length]=opt;
+
+   let demoRows=config.formConfig['registrationData'].rows;
+   pM.print(fName+" demoRows: "+demoRows.length);
+   for (let i=0;i<demoRows.length;i++){
+      let opt2=config.document.createElement("option");
+      opt2.value=i+1;
+      let id=demoRows[i][pM.getCrfEntryFieldName()];
+      let loc=demoRows[i][pM.getCrfEntryFieldName('LOCAL')];
+      opt2.text=id+' (Local: '+loc+')';
+      input.options[input.options.length]=opt2;
+      pM.print(fName+' '+pM.participantField+' '+demoRows[i][pM.participantField]);
+   }
+
+   input.selectedIndex=0;
+}
+
+function updateElements(){
+   let pM=this;
+   let fName='[updateElements]';
+   //reset all values (some might be different depending on the call timing)
+
+   //selector is with study
+   pM.cellSelector=config.document.getElementById(pM.cellSelectorId);
+   pM.inputSelector=config.document.getElementById(pM.inputSelectorId);
+   pM.textStudy=config.document.getElementById(pM.textStudyId);
+  
+   pM.print(fName+' selector '+pM.inputSelector+' id '+pM.inputSelectorId);
+   //value is with local
+   pM.cellValue=config.document.getElementById(pM.cellValueId);
+   pM.inputValue=config.document.getElementById(pM.inputValueId);
+   pM.textLocal=config.document.getElementById(pM.textLocalId);
+   
+   //pM.inputManageLocal=config.document.getElementById(pM.inputManageLocalId);
+   //pM.inputManageStudy=config.document.getElementById(pM.inputManageStudyId);
+}
+
+function generateEntryField(){
+   let pM=this;
+   let fName='[generateParticipantEntryField]:';
+   pM.print(fName);
+
+   //this is HTML designator of area on page
+   let config=pM.config;
+	let formName=config.masterForm;
+
+    
+   pM.tb=config.document.createElement('table');
+   let tb=pM.tb;
+	tb.className='t2';
+	let row=tb.insertRow();
+	
+   //label for local ID
+   let cell=config.document.createElement('th');
+	row.appendChild(cell);	
+	cell.setAttribute("colspan","1");
+	cell.style.fontSize="20px";
+	cell.style.textAlign="left";
+   //Use study coding for participant field
+   cell.innerText='Local ID';
+	//cell.innerText=pM.participantField;
+
+
+   //value
+   let cellValue=row.insertCell();
+   cellValue.id=pM.cellValueId;
+
+   pM.cellManageLocal=row.insertCell();
+   
+   //second row for study id
+   let rowStudy=tb.insertRow();
+
+   //label for study ID
+   let cellStudy=config.document.createElement('th');
+	rowStudy.appendChild(cellStudy);	
+	cellStudy.setAttribute("colspan","1");
+	cellStudy.style.fontSize="20px";
+	cellStudy.style.textAlign="left";
+   //Use study coding for participant field
+   cellStudy.innerText='Study ID';
+
+	//selector for study id
+   let cellSelector=rowStudy.insertCell();
+   cellSelector.id=pM.cellSelectorId;
+
+   //manage
+   pM.cellManageStudy=rowStudy.insertCell();
+   config.document.getElementById(formName).appendChild(tb);
+   pM.print(fName+' done');
+
+}
+
+function getMode(mode="NONE"){
+   let pM=this;
+   if (mode=="NONE") return pM.mode;
+   return mode;
+}
+
+//reslovers which operate depending on mode
+function getInputId(mode="NONE"){
+   let pM=this;
+   let fName='[getInputId]';
+   pM.print(fName);
+   if (pM.getMode(mode)=="LOCAL") return pM.inputValueId;
+   return pM.inputSelectorId;
+}
+
+
+function getInputCell(mode="NONE"){
+   let pM=this;
+   let fName='[getInputCell]';
+   pM.print(fName+' mode '+mode+' getMode '+pM.getMode(mode));
+   if (pM.getMode(mode)=="LOCAL") return pM.cellValue;
+   return pM.cellSelector;
+}
+
+function getInputElement(mode="NONE"){
+   let pM=this;
+   let fName='[getInputElement]';
+   pM.print(fName);
+   let elementType=pM.getInputElementType(mode);
+   let id=pM.getInputId(mode);
+   let cell=pM.getInputCell(mode);
+   let el=pM.config.document.getElementById(id);
+   pM.print(fName+' mode '+pM.getMode(mode)+' type '+elementType+' id '+id+' cell '+cell+' el '+el);
+   if (el) return el;
+
+   el=pM.config.document.createElement(elementType);
+   print(fName+' input '+el);
+   el.id=id;
+
+   cell.replaceChildren(el);
+   pM.addSelectOptions();
+ 
+   return el;
+}
+
+function getInputElementType(mode="NONE"){
+   let pM=this;
+   let fName='[getInputElementType]';
+   pM.print(fName);
+   if (pM.getMode(mode)=="LOCAL") return "input";
+   return "select";
+}
+
+
+function getTextFieldId(mode="NONE"){
+   let pM=this;
+   let fName='[getTextFieldId]';
+   pM.print(fName);
+   if (pM.getMode(mode)=="LOCAL") return pM.textLocalId;
+   return pM.textStudyId;
+}
+
+  
+function getTextElement(mode="NONE"){
+   let pM=this;
+   let fName='[getTextElement]';
+   pM.print(fName+' mode '+mode);
+   let id=pM.getTextFieldId(mode);
+   pM.print(fName+' id '+id);
+   let el=pM.config.document.getElementById(id);
+   pM.print(fName+' el '+el);
+   if (el) return el;
+   el=config.document.createElement("p");
+   el.id=id;
+   let cell=pM.getInputCell(mode);
+   //let oldEl=pM.getInputElement(mode);
+   cell.replaceChildren(el);
+   return el;
+}
+
+
+//get the button, create if not there yet
+function getInputManage(mode="NONE"){
+   let pM=this;
+   let fName='[getInputManage]';
+   //pM.print(fName);
+   let config=pM.config;
+   //this prevents from having two inputs; it is either local or global from the outset
+   if ("inputManage" in pM) return pM.inputManage;
+
+   pM.inputManage=config.document.createElement("input");
+   let inputManage=pM.inputManage;
+   inputManage.type="button";
+   inputManage.onclick=function(){pM.manageId();};
+   //inputManageLocal.id=pM.inputManageLocalId;
+   let cell=pM.cellManageStudy;
+   if (pM.getMode(mode)=="LOCAL") cell=pM.cellManageLocal;
+   //pM.print(fName+' inputManage '+pM.inputManage+' cell '+cell+' mode '+pM.mode);
+   cell.appendChild(inputManage);
+   return inputManage;
+}
+
+//callback that splits to edit or set/label mode
+function manageId(){
+   let pM=this;
+   let fName='[manageId]';
+   pM.print(fName);
+   //this can happen after object was created, so make sure current
+   //elements are used
+   pM.updateElements();
+   let x=pM.getInputManage();
+
+   if (x.value=="Set"){
+      pM.setId();
+      return;
+   }
+   if (x.value=="Edit"){
+      pM.editId();
+      return;
+   }
+}
+
+//set mode
+function setId(){
+   let pM=this;
+   let fName='[setId]';
+   pM.print(fName);
+   let el=pM.getInputElement();
+
+   pM.print(fName+" value: "+el.value);
+   let pId=el.value;
+   let label=pId;
+   if (pM.mode!="LOCAL"){
+      if (el.value<0) return;
+      let opt=el.options[el.selectedIndex];
+      label=opt.text;
+      pId=label.replace(/\(.*\)/,'');
+      label=label.replace(/ \(Local: /,':');
+      label=label.replace(/\)/,'');
+   }
+   pM.setParticipantIdToCrfEntry(pId);//no argument (should come from mode)
+   pM.print(fName+" new value "+pId);
+   pM.setLabelMode(label);
+   pM.updateCrfEntry();
+}
+
+function setLabelMode(pId){
+   let fName='[setLabelMode1]';
+   let pM=this;
+   let config=pM.config;
+
+   pM.print(fName+' id '+pId);
+   ids=pId.split(':');
+
+   let textValue=pM.getTextElement();
+   pM.print(fName+' textElement '+textValue);
+   textValue.innerText=ids[0];
+
+   if (pM.mode=="STUDY"){
+      let loc=ids[1];
+      //pM.getParticipantIdFromCrfEntry('LOCAL');
+      pM.print(fName+' setting local id '+loc);
+      let tValLocal=pM.getTextElement('LOCAL');
+      tValLocal.innerText=loc;
+      pM.setParticipantIdToCrfEntry(loc,'LOCAL');
+   }
+
+   let x=pM.getInputManage();//getInputManage
+   if ("readOnly" in pM){
+      x.style.display="none";
+   }
+   x.value="Edit";
+
+   
+}
+
+//edit mode
+function editId(){
+   let pM=this;
+   pM.setEditMode();
+}
+
+function setEditMode(){
+   let pM=this;
+   let config=pM.config;
+
+   let fName='[setEditMode1]';
+   pM.print(fName+' pM '+pM+' mode '+pM.mode);
+   //input
+   let el=pM.getInputElement();
+
+   let x=pM.getInputManage();
+   x.value="Set";
+
+}
+
+//manage interaction to storage/CRF and study/LabKey
+function getParticipantField(config){
+   return config.formConfig['studyDataAll'].rows[0]['SubjectColumnName'];
+}
+
+function getCrfEntryFieldName(mode="NONE"){
+   let pM=this;
+   let variable="Study";
+   if (mode=="NONE") mode=pM.mode;
+   if (mode=="LOCAL") variable="Local";
+   return 'participant'+variable+'Id';
+}
+
+function setParticipantIdToCrfEntry(pId,mode="NONE"){
+   let pM=this;
+   let config=pM.config;
+   config.formConfig.crfEntry[pM.getCrfEntryFieldName(mode)]=pId;
+}
+
+function getParticipantIdFromCrfEntry(mode="NONE"){
+   let pM=this;
+   let config=pM.config;
+   return config.formConfig.crfEntry[pM.getCrfEntryFieldName(mode)];
+}
+
+//main interface. Use this to generate object and to refer to it later on
+function getParticipantManagerObject(config){
+
+   let fName='[getParticipantManagerObject]';
+   config.print(fName);
+   if ("participantManager" in config) {
+      let pM=config.participantManager;
+      pM.updateElements();
+      return pM;
+   }
+
+   let pM=new Object();
+   //circular reference to traverse pM up and down
+   config.participantManager=pM;
+   pM.config=config;
+   //config should have a print routine
+   pM.print=config.print;
+
+   //this never change
+   pM.participantField=getParticipantField(config);
+
+
+   pM.cellSelectorId=pM.participantField+"_cellSelect";
+   pM.inputSelectorId=pM.participantField+"_Select";
+   pM.textStudyId=pM.participantField+"_textStudy";
+
+   pM.cellValueId=pM.participantField+"_cellValue";
+   pM.inputValueId=pM.participantField+"_Value";
+   pM.textLocalId=pM.participantField+"_textLocal";
+
+   pM.inputManageLocalId=pM.participantField+"_ManageLocal";
+   pM.inputManageStudyId=pM.participantField+"_ManageStudy";
+
+   pM.mode="LOCAL";//or "STUDY"
+
+   //add methods
+   pM.getMode=getMode;
+
+   //global methods that are not subject to mode modifier
+   pM.updateElements=updateElements;
+   pM.addSelectOptions=addSelectOptions;
+   pM.generateEntryField=generateEntryField;
+
+   //getters subject to mode
+   pM.getInputId=getInputId;
+   pM.getInputCell=getInputCell;
+   pM.getInputElement=getInputElement;
+   pM.getInputElementType=getInputElementType;
+
+   pM.getTextFieldId=getTextFieldId;
+   pM.getTextElement=getTextElement;
+
+   pM.getInputManage=getInputManage;
+
+   //callback
+   pM.manageId=manageId;
+   
+   //set/label mode
+   pM.setId=setId;
+   pM.setLabelMode=setLabelMode;
+
+   //edit mode
+   pM.editId=editId;
+   pM.setEditMode=setEditMode;
+   
+   //interact with storage/CRF and study/LabKey
+   pM.setParticipantIdToCrfEntry=setParticipantIdToCrfEntry;
+   pM.getParticipantIdFromCrfEntry=getParticipantIdFromCrfEntry;
+   pM.getCrfEntryFieldName=getCrfEntryFieldName;
+
+
+   //init
+   pM.generateEntryField();
+   pM.updateElements();
+   return pM;
+}

+ 84 - 0
web/crf/runQuery.js

@@ -0,0 +1,84 @@
+//external dependencies:
+//LABKEY.Query
+//print -> configObject.print
+
+function makeQuery(targetObject,containerName,queryName,fieldName,filterArray){
+   //call with makeQuery(config.formConfig,getContainer(name),...
+	let e=new Object();
+	e.containerName=containerName;
+	e.queryName=queryName;
+	e.fieldName=fieldName;
+	e.filterArray=filterArray;
+   e.targetObject=targetObject;
+	return e;
+}
+
+function getDataFromQueries(queryArray,cb){
+	//queryArray should contain elements with
+	//- fieldName to set the data variable
+	//- containerName to select container (data,config,CRF)
+	//- queryName to select query
+	//- filterArray to perform filtering, empty array works
+	//- callback cb to be called with no arguments
+	//
+	afterQuery(new Object(),-1,queryArray,cb);
+}
+
+function afterQuery(data,id,queryArray,cb){
+	//queryArray should contain elements with
+	//- fieldName to set the data variable
+	//- containerName to select container (data,config,CRF)
+	//- queryName to select query
+	//- filterArray to perform filtering, empty array works
+	//- callback cb to be called with no arguments
+	//
+	//it should be called with id -1.
+	//
+	//targetObject.print('afterQuery1['+id+'/'+queryArray.length+']: ');
+
+	if (id>-1){
+	   let e1=queryArray[id];
+		let fieldName=e1.fieldName;
+		e1.targetObject.print('afterQuery['+fieldName+']: '+data.rows.length);
+		e1.targetObject[fieldName]=data;
+	}
+	id+=1;
+	if (id==queryArray.length) {
+		cb();
+		return;
+	}
+
+
+	let e=queryArray[id];
+	let qconfig=new Object();
+	qconfig.containerPath=e.targetObject.getContainer(e.containerName);
+	qconfig.schemaName="lists";
+	if ("schemaName" in e){
+		e.targetObject.print('afterQuery: schemaName='+e.schemaName);
+		qconfig.schemaName=e.schemaName;
+	}
+
+	if ("columns" in e){
+		e.targetObject.print('afterQuery: columns='+e.columns);
+		qconfig.columns=e.columns;
+	}
+	qconfig.queryName=e.queryName;
+	//this should point to configuration container
+	//don't filter -> so we can pick up other forms (say registration) later on
+	//qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)];
+	if ("filterArray" in e)
+		qconfig.filterArray=e.filterArray;
+	
+	//qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)]
+	qconfig.success=function(data){afterQuery(data,id,queryArray,cb);};
+	qconfig.failure=function(errorInfo,responseObj){onFailure(e.targetObject,errorInfo,responseObj);};
+	LABKEY.Query.selectRows(qconfig);
+
+}
+
+function onFailure(targetObj, errorInfo, responseObj){
+   //don't have configObject to rely to
+   targetObj.print('[afterQuery]: Failure: '+errorInfo.exception);
+
+}
+

Some files were not shown because too many files changed in this diff