Browse Source

Adding integrated form and participant portal

Andrej Studen 2 years ago
parent
commit
1b54736de1

+ 11 - 7
views/formPortal.view.xml

@@ -1,8 +1,12 @@
-<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 xmlns="http://labkey.org/data/xml/view" title="Form Portal">
+   <dependencies>
+
+      <dependency path="crf/formPortal.js"/>
+		<!--local copy of pdfkit, version 0.10.0-->
+		<!--https://github.com/devongovett/pdfkit/releases/download/v0.10.0/pdfkit.standalone.js-->
+		<!--local copy of blob-stream, version 0.1.3-->
+		<!--https://github.com/devongovett/blob-stream/releases/download/v0.1.3/blob-stream.js-->
+
+ </dependencies>
 </view>
+<!-- need to restart labkey to add new files -->

+ 25 - 0
views/formPortalNew.html

@@ -0,0 +1,25 @@
+
+<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>
+
+
+<script type "text/javascript">
+window.onload=loadScripts;
+
+function loadScripts(){
+   LABKEY.requiresScript(["crf/formPortalNew.js"],init);
+}
+
+function init(){
+   formPortal.init(afterInit);
+}
+
+function afterInit(){
+   formPortal.print("Starting");      
+   formPortal.generateFormArray();
+}
+</script>

+ 4 - 0
views/formPortalNew.webpart.xml

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

+ 1 - 1
views/participantPortal.html

@@ -15,7 +15,7 @@
 window.onload=loadScripts;
 
 function loadScripts(){
-  LABKEY.requiresScript(["crfTecant/participantPortal.js"],init);
+  LABKEY.requiresScript(["crf/participantPortal.js"],init);
 }
 
 function init(){

+ 4 - 0
views/participantPortal.webpart.xml

@@ -0,0 +1,4 @@
+<webpart xlmns="http://labkey.org/data/xml/webpart"
+	title="Form Portal (New)">
+	<view name="formPortalNew"/>
+</webpart>

+ 14 - 0
web/crf/crfData.js

@@ -112,6 +112,20 @@ function(value=null){
    return qMap;
 }
 
+crfData.getRegistrationEntryMap=
+function(key=null){
+   let rows=this.getRegistration();
+   let qMap=new Object();
+   if (!key) key='Key';
+   for (let i=0;i<rows.length;i++){
+      qMap[rows[i][key]]=rows[i];
+   }
+   return qMap;
+}
+
+
+
+
 crfData.setDataLayout=
 function(formId,role,cb){
    let fName='[setDataLayout]';

+ 9 - 3
web/crf/crfHTML.css

@@ -6,8 +6,8 @@ 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}
 
-div.d1 {text-align:center; width=400px; background-color:#e0e0e0;
-        font-size:      20px; margin-bottom:20px}
+div.d1 {text-align:center; width:400px; background-color:#e0e0e0;
+        font-size:20px; margin-bottom:20px}
 
 .box{
 width:120px;
@@ -20,7 +20,13 @@ height:120px;
 .orange {background-color: orange;}
 .blue {background-color: steelblue;}
 .brown {background-color: tan;}
-.empty { background-color: #dddddd;
+.yellow {background-color: #ffffcc;}
+.salmon {background-color: #ffcccc;}
+.teal {background-color: #cccc99;}
+.mauve {background-color: #ccccff;}
+.fuchsia {background-color: #ffccff;}
+.brick {background-color: #cc6666;}
+.empty { background-color: #cccccc;
       border-style: dashed;
 }
 

+ 6 - 0
web/crf/crfHTML.js

@@ -84,6 +84,11 @@ function(text,id=null,el=null){
    return tNode;
 }
 
+crfHTML.setTextNode=
+function(el,text){
+   el.nodeValue=text;
+}
+
 crfHTML.createDiv=
 function(divId=null,id=null,el=null){
    let div=document.createElement('div');
@@ -153,6 +158,7 @@ function(input){
 
 crfHTML.addSelectOptions=
 function(input,qMap){
+   let fName='[addSelectOptions]';
    this.clearOptions(input);
    let opt = document.createElement("option");
 	opt.text = "<Select>";

+ 72 - 0
web/crf/crfRoleSelector.js

@@ -0,0 +1,72 @@
+var crfRoleSelector={};
+
+crfRoleSelector.init=
+function(cb=null){
+   let that=this;
+   let action=function(){that.afterScripts(cb);};
+   LABKEY.requiresScript(["crf/crfHTML.js"],action);
+}
+
+crfRoleSelector.afterScripts=
+function(cb=null){
+   if (cb) cb();
+}
+
+crfRoleSelector.set=
+function(setup){
+   this.setup=setup;
+}
+
+crfRoleSelector.print=
+function(msg){
+   console.log(msg);
+}
+
+
+crfRoleSelector.makePortal=
+function(cb=null){
+   //cb is a callback function that gets executed on select change
+   let fName='[crfRoleSelector::makePortal]';
+   let userId=LABKEY.Security.currentUser.id;
+   let userRoles=['crfEditor','crfMonitor','crfSponsor','crfManager'];
+   //let siteRows=this.setup.getRows('siteData');
+   //find possible matches between user and sites
+   let options=new Object();
+   let siteMap=this.setup.getEntryMap('siteData:siteNumber');
+   let userMap=this.setup.getEntryMap('users:UserId');
+   for (let i=0;i<userRoles.length;i++){
+      let listName=userRoles[i]+'s';
+      let listRows=this.setup.getRows(listName);
+      for (let j=0;j<listRows.length;j++){
+         if (listRows[j]['User']!=userId) continue;
+         let siteNumber=listRows[j]['Site'];
+         let siteRow=siteMap[siteNumber];
+         options[userRoles[i]+':'+siteNumber]=userRoles[i]+' for '+siteRow['siteName'];
+      }
+   }
+   for (let opt in options){
+      this.print(fName+' ['+opt+'] '+options[opt]);
+   }
+   let table=crfHTML.createTable('formDiv');
+   let row=table.insertRow();
+   let cell=row.insertCell();
+   this.roleSelect=crfHTML.createSelect(options,null,cell);
+   row=table.insertRow();
+   cell=row.insertCell();
+   this.roleLabel=crfHTML.createTextNode('Please select option',null,cell);
+   let that=this;
+   this.roleSelect.onchange=function(){that.onChange(cb);}
+}
+
+crfRoleSelector.onChange=
+function(cb=null){
+   crfHTML.setTextNode(this.roleLabel,this.roleSelect.value);
+   if (cb) cb();
+}
+
+crfRoleSelector.getRoleAndSite=
+function(){
+   if (this.roleSelect.selectedIndex==0)
+      return null;
+   return this.roleSelect.value;
+}

+ 37 - 0
web/crf/crfSetup.js

@@ -128,6 +128,40 @@ function(qMap){
    return qInverseMap;
 }
 
+crfSetup.getStudyIdLabel
+=function(){
+   return 'participantStudyId';
+}
+
+crfSetup.getLocalIdLabel
+=function(){
+   return 'participantLocalId';
+}
+
+crfSetup.getParticipantLabel=
+function(entry){
+   let pid=entry[this.getStudyIdLabel()];
+   let loc=entry[this.getLocalIdLabel()];
+   let label='';
+   if (pid) label+=pid+' ';
+   if (loc) label+='(Local: '+loc+')';
+   if (label.length==0) label="NONE";
+   return label;
+ 
+}
+
+crfSetup.getStudyId=
+function(label){
+   return label.replace(/ \([^\)]*\)/,"");
+}
+
+crfSetup.getLocalId=
+function(label){
+   return label.replace(/^.*\(Local: ([^\)]*)\)$/,"$1");
+}
+
+
+
 crfSetup.setContainers=
 function(cb=null){
 
@@ -205,6 +239,9 @@ function(cb=null){
 	//crfManagers
 	queryArray.push(runQuery.makeQuery(targetObject,'config','crfManagers','crfManagers',[]));
 	//FormStatus
+   //
+
+	queryArray.push(runQuery.makeQuery(targetObject,'config','FormStatus','formStatusAll',[]));
    let statusFilter=[];
    if ("formStatus" in this)
       statusFilter.push(LABKEY.Filter.create('Key',this.formStatus));

+ 23 - 8
web/crf/crfVisitNew.js

@@ -1524,11 +1524,10 @@ function(){
 
 crfVisit.redirect=
 function(){
-	let debug=false;
-	let formUrl="begin";
+	let formUrl="participantPortal";
 	let params=new Object();
 	params.name=formUrl;
-	params.pageId="CRF";
+	//params.pageId="CRF";
 
 	//points to crf container
 	let containerPath=crfSetup.getContainer('CRF');
@@ -1536,10 +1535,9 @@ function(){
 	// This changes the page after building the URL. 
 	//Note that the wiki page destination name is set in params.
         
-	var homeURL = LABKEY.ActionURL.buildURL(
-			"project", formUrl , containerPath, params);
-        this.print("Redirecting to "+homeURL);
-	if (debug) return;	 
+	//let homeURL = LABKEY.ActionURL.buildURL("project", formUrl , containerPath, params);
+	let homeURL = LABKEY.ActionURL.buildURL("crf", formUrl , containerPath, params);
+   this.print("Redirecting to "+homeURL);
 	window.location = homeURL;
 
 }
@@ -2185,7 +2183,7 @@ crfVisit.parseSetup=
 function(){
 
    //debug
-   let fName='[fcontinue]';
+   let fName='[parseSetup]';
    let varRows=crfSetup.getRows('crfStaticVariables');
    let studyVars=crfSetup.getRows('studyData')[0];
    for (let i=0;i<varRows.length;i++){
@@ -2204,11 +2202,28 @@ function(){
 	this.print("Setting user to "+this.userEntry["DisplayName"]);
 
 	this.print('Setting operator to: '+this.role);
+
+
 	
    //point formId to point to form set in crfEntry
    let formId=crfData.getCrfEntry()['Form'];
 	this.formEntry=crfSetup.getEntryMap('dataForms')[formId];
 
+	let accessModeColumn=this.role+'Status';
+   let targetStatus=this.formEntry[accessModeColumn];
+   let formStatus=crfData.getCrfEntry()['FormStatus'];
+
+   this.print(fName+' comparing status '+formStatus+'/'+targetStatus);
+
+   if (targetStatus!=formStatus){
+      let statusMap=crfSetup.getEntryMap('formStatusAll');
+      let targetEntry=statusMap[targetStatus];
+      let actualEntry=statusMap[formStatus];
+      alert('Form status ['+actualEntry.formStatus+'] not suitable to be operated by '+this.role+' targeting status ['+targetEntry.formStatus+']');
+      this.redirect();
+   }
+
+
    crfSetup.setAdditionalData(this.crfRef,formId);
 	
 	this.afterConfig();

+ 102 - 150
web/crf/formGenerator.js

@@ -1,22 +1,69 @@
 //namespace
 var formGenerator={};
 
-formGenerator.set=
-function(parentClass){
-   this.parent=parentClass;
+formGenerator.init=
+function(cb=null){
+   let that=this;
+   let action=function(){that.afterScripts(cb);};
+   let dependencies=new Array();
+   dependencies.push("crf/crfHTML.js");
+   dependencies.push("crf/crfSetup.js");
+   dependencies.push("crf/crfData.js");
+   dependencies.push("crf/runQuery.js");
+
+
+   LABKEY.requiresScript(dependencies,action);
+}
+
+formGenerator.afterScripts=
+function(cb=null){
+
+   crfData.setSetup(crfSetup);
+   let initData=function(){crfData.init(cb);};
+   crfSetup.init(initData);
+}
+
+formGenerator.setRoleAndSite=
+function(roleAndSite){
+   this.roleAndSite=roleAndSite;
+}
+
+formGenerator.showFormGenerator=
+function(){
+   if ("table" in this){
+      this.table.display='block';
+      return;
+   }
+   addFormGenerator();
+}
+
+formGenerator.hideFormGenerator=
+function(){
+   if (this.table){
+      this.table.display='none';
+   }
+}
+
+formGenerator.getCrfSelectRow=
+function(crfRef){
+   let rows=this.crfSelectRows;
+   for (let i=0;i<rows.length;i++){
+      if (rows[i]['crfRef']==crfRef)
+         return rows[i];
+
+   }
+   return new Object();
 }
 
 formGenerator.addFormGenerator=
 function(){
    //parentClass should provide config and print and getContainer
-   let config=this.parent.config;
       
    let fName='[addFormGenerator]';
-   this.parent.print(fName);
+   this.print(fName);
    //layout
-   let table=config.document.createElement("table");
+   this.table=crfHTML.createTable('formDiv');
    table.className="t2";
-   config.document.getElementById(config.div).appendChild(table);
    //this is a form manipulator
    let fgForm=new Object();
 
@@ -25,17 +72,19 @@ function(){
    fgForm.comment=this.addInputRow(table,'Enter comment','text');
    fgForm.details=this.addInputRow(table,'Details','label');
    fgForm.warnings=this.addInputRow(table,'Warnings','label');
-   fgForm.warnings.innerHTML='formGenerator version 2.1.0';
-   this.addOption(fgForm.formSelect,'<Select>',-1);
-   let formRows=config.formConfig.generateConfigData.rows;
+   fgForm.warnings.innerHTML='formGenerator version 3.1.0';
+   let formRows=crfSetup.getRows('generateConfigData');
+   let options=new Object();
    for (let i=0;i<formRows.length;i++){
       let formId=formRows[i]["formId"];
       let formName=this.getFormName(formId);
-      this.parent.print(fName+' '+formRows[i]["formId"]+'/'+formName);
-      this.addOption(fgForm.formSelect,formName,formId);
+      this.print(fName+' '+formRows[i]["formId"]+'/'+formName);
+      options[formId]=formName;
+      //this.addOption(fgForm.formSelect,formName,formId);
    }
    //callbacks should be called on copy of this
    let that=this;
+   crfHTML.addSelectOptions(fgForm.formSelect,options);
    fgForm.formSelect.onchange=function(){that.updateIdList(fgForm);};
    fgForm.crfSelect.onchange=function(){that.updateLabel(fgForm);};
    fgForm.generateButton=this.addInputRow(table,'Generate Form','button');
@@ -44,134 +93,50 @@ function(){
       
 }
 
-formGenerator.insertRow=
-function(schemaName,queryName,row,cb=null,containerPath=null){
-   let fName='[fgInsertRow]';
-   this.parent.print(fName);
-   //cb=function(data){....}
-	let qconfig=new Object();
-	if (containerPath)
-      qconfig.containerPath=containerPath;
-	qconfig.schemaName=schemaName;
-	qconfig.queryName=queryName;
-	qconfig.success=function(data){;};
-   if (cb) qconfig.success=cb;
-   qconfig.rows=[row];
-   this.parent.print(fName+' qconfig '+qconfig);
-	LABKEY.Query.insertRows(qconfig);
-}
-
 formGenerator.addInputRow=
 function(table,header,type){
-   let config=this.parent.config;
    let fName='[addInputRow]';
-   this.parent.print(fName);
+   this.print(fName);
    let row=table.insertRow();
-   let cell=config.document.createElement('th');
-   let text=config.document.createTextNode(header);
-   cell.appendChild(text);
-   row.appendChild(cell);
+   let cell=crfHTML.createTblHeader(null,row);
+   crfHTML.createTextNode(header,null,cell);
+
+   cell=row.insertCell();
    let input=null;
       
    if (type=="select")
-      input=config.document.createElement(type);
+      input=crfHTML.createSelect(new Object(),null,cell);
+
+   if (type=="button")
+      input=crfHTML.createButton(null,cell);
 
-   if (type=="button"){
-      input=config.document.createElement("input");
-      input.type="button";
-   }
    if (type=="text"){
-      input=config.document.createElement('textarea');
+      input=crfHTML.createTextArea(null,cell);
       input.cols="65";
       input.rows="5";
    }
    if (type=="label")
-      input=config.document.createElement(type);
+      input=crfHTML.createLabel('Loading',null,cell);
 
-   let cell1=row.insertCell();
-   cell1.appendChild(input);
    return input;
 }
 
-formGenerator.getFormName=
-function(formId){
-   let config=this.parent.config;
-   let rows=config.formConfig.dataForms.rows;
-   for (let i=0;i<rows.length;i++){
-      if (rows[i]['Key']==formId){
-         return rows[i]['formName'];
-      }
-   }
-   return "NONE";
-}
-
-formGenerator.getQueryName=
-function(queryId){
-   let config=this.parent.config;
-   let rows=config.formConfig.inputLists.rows;
-   for (let i=0;i<rows.length;i++){
-      if (rows[i]['Key']==queryId){
-         return rows[i]['queryName'];
-      }
-   }
-   return "NONE";
-}
-
-formGenerator.getGCRow=
-function(formId){
-   let config=this.parent.config;
-   let formRows=config.formConfig.generateConfigData.rows;
-   for (let i=0;i<formRows.length;i++){
-      if (formRows[i]['formId']==formId){
-         return formRows[i];
-      }
-   }
-   return Object();
-}
-
-formGenerator.getCrfSelectRow=
-function(crfRef){
-   let config=this.parent.config;
-   let rows=config.formConfig.crfSelectRows;
-   for (let i=0;i<rows.length;i++){
-      if (rows[i]['crfRef']==crfRef)
-         return rows[i];
-
-   }
-   return Object();
-}
-
-
-formGenerator.addOption=
-function(input,name,value){
-   let config=this.parent.config;
-   let opt=config.document.createElement("option");
-   opt.text=name;
-   opt.value=value;
-   input.options[input.options.length]=opt;
-}
-
-formGenerator.clearOptions=
-function(input){
-   for(let i = input.options.length; i >= 0; i--) {
-		input.remove(i);
-   }
-}
 
 formGenerator.createFormWithId=
 function(fgForm){
    //get form id and entry id from select and create form as above
    let fName='[createFormWithId]';
+   let ar=roleAndSite.split(':');
+   let role=ar[0];
+   let siteNumber=ar[1];
 
-   this.parent.print(fName);
-   let config=this.parent.config;
+   this.print(fName);
    let formId=fgForm.formSelect.options[fgForm.formSelect.selectedIndex].value;
    let crfRef=fgForm.crfSelect.options[fgForm.crfSelect.selectedIndex].text;
-   let configRow=this.getGCRow(formId);
+   let configRow=crfSetup.getEntryMap('generateConfigData:formId')[formId];
    let crfSelectRow=this.getCrfSelectRow(crfRef);
-	let formConfig=config.formConfig;
 
-	this.parent.print("Create form w/id "+formId);
+	this.print("Create form w/id "+formId);
 	
 	let crfEntry=new Object();
 	crfEntry.entryId=Date.now();
@@ -184,15 +149,15 @@ function(fgForm){
 	crfEntry.formStatus=configRow['formStatus'];//In progress
 	//set other variables
 	//requires studyData as part of formConfig
-	let studyData=formConfig.studyData.rows[0];
-   let varRows=formConfig['crfStaticVariables'].rows;
+	let studyData=crfSetup.getRows('studyDataAll')[0];
+   let varRows=crfSetup.getRows('crfStaticVariables');
    for (let i=0;i<varRows.length;i++){
       let varName=varRows[i].staticVariable;
 	   crfEntry[varName]=studyData[varName];
    }
 	crfEntry.UserId=LABKEY.Security.currentUser.id;
-	crfEntry.Site=config.formConfig.currentSites[0].siteNumber;
-	this.parent.print("Setting site to id="+crfEntry.Site);
+	crfEntry.Site=siteNumber;
+	this.print("Setting site to id="+crfEntry.Site);
 	//from argument list
 	crfEntry.Form=formId;
    crfEntry.parentCrf=crfRef;
@@ -204,7 +169,7 @@ function(fgForm){
    reviewComment['crfRef']=crfRef;
    //comment length
    let x=fgForm.comment.value;
-   this.parent.print(fName+' comment length '+x.length);
+   this.print(fName+' comment length '+x.length);
    if (x.length==0){
       fgForm.warnings.innerHTML='Supply a comment';
       return;
@@ -212,21 +177,16 @@ function(fgForm){
    reviewComment['reviewComment']=fgForm.comment.value;
    reviewComment['queryName']=configRow['queryId'];
 
-   let crfStatus=new Object();
-   crfStatus.entryId=crfEntry.entryId;
-   crfStatus.submissionDate=new Date();
-   crfStatus.FormStatus=crfEntry.formStatus;
-   crfStatus.User=crfEntry.UserId;
-   crfStatus.Form=crfEntry.Form;
-   crfStatus.operator=config.role;
+   let crfStatus=crfData.createCrfStatus(crfEntry);
+   crfStatus.operator=role;
    crfStatus.action='createFormWithId';
 
    let that=this;
-   let containerPath=this.parent.getContainer('data');
+   let containerPath=crfSetup.getContainer('data');
    let rd=function(data){that.redirect();};
-   let pass1=function(data){that.insertRow('lists','crfStatus',crfStatus,rd,containerPath);};
-   let pass=function(data){that.insertRow('lists','reviewComments',reviewComment,pass1,containerPath);};
-   this.insertRow('lists','crfEntry',crfEntry,pass,this.parent.getContainer('data'));
+   let pass1=function(data){runQuery.insertRows('lists','crfStatus',[crfStatus],rd,containerPath);};
+   let pass=function(data){runQuery.insertRows('lists','reviewComments',[reviewComment],pass1,containerPath);};
+   runQuery.insertRows('lists','crfEntry',crfEntry,pass,containerPath);
 }
 
 
@@ -234,34 +194,28 @@ formGenerator.updateIdList=
 function(fgForm){
    let fName='[updateIdList]';
    let formId=fgForm.formSelect.options[fgForm.formSelect.selectedIndex].value;
-   this.parent.print(fName+' id '+formId);
-   //remove old options
-   this.clearOptions(fgForm.crfSelect);
-   this.parent.print(fName+' options cleared');
+   this.print(fName+' id '+formId);
    //get query associated with form
-   let configRow=this.getGCRow(formId);
+   let configRow=crfSetup.getEntryMap('generateConfigData:formId')[formId];
    let queryId=configRow['queryId'];
-   this.parent.print(fName+' queryId '+queryId);
+   this.print(fName+' queryId '+queryId);
    if (!queryId || queryId<0)
       return;
-
-   let qselect=new Object();
-   qselect.containerPath=this.parent.getContainer('data');
-   qselect.schemaName='lists';
-   qselect.queryName=this.getQueryName(queryId);
-   let that=this;
-   qselect.success=function(data){that.updateIdListWithData(fgForm,data);};
-   LABKEY.Query.selectRows(qselect);
+   let qMap=crfSetup.getMap('inputLists');
+   let containerPath=crfSetup.getContainer('data');
+   let success=function(data){that.updateIdListWithData(fgForm,data);};
+   runQuery.selectRows('lists',qMap[queryId],[],success,containerPath);
 }
 
 formGenerator.updateIdListWithData=
 function(fgForm,data){
-   let config=this.parent.config;
    let rows=data.rows;
-   config.formConfig.crfSelectRows=data.rows;
+   this.crfSelectRows=data.rows;
+   let options=new Object();
    for (let i=0;i<rows.length;i++){
-      this.addOption(fgForm.crfSelect,rows[i]['crfRef'],i);
+      options[i]=rows[i]['crfRef'];
    }
+   crfHTML.addSelectOptions(fgForm.crfSelect,options);
    let event=new Event('change');
    fgForm.crfSelect.dispatchEvent(event);
 }
@@ -276,22 +230,20 @@ function(fgForm){
 formGenerator.redirect=
 function(){
 
-	let debug=false;
 	let formUrl="begin";
 	let params=new Object();
 	params.name=formUrl;
 	params.pageId="CRF";
 
 	//points to crf container
-	let containerPath=this.parent.getContainer('data');
+	let containerPath=crfSetup.getContainer('data');
         
 	// This changes the page after building the URL. 
 	//Note that the wiki page destination name is set in params.
         
 	var homeURL = LABKEY.ActionURL.buildURL(
 			"project", formUrl , containerPath, params);
-   this.parent.print("Redirecting to "+homeURL);
-	if (debug) return;	 
+   this.print("Redirecting to "+homeURL);
 	window.location = homeURL;
 
 	

+ 331 - 0
web/crf/formPortalNew.js

@@ -0,0 +1,331 @@
+var formPortal={};
+
+formPortal.setDebug=
+function(debug=null){
+   if (debug){
+      this.print=function(msg){debug.print(msg);};
+      this.clear=function(){debug.clear();}
+      return;
+   }
+   //provide default functions if not debug object is available
+   this.print=function(msg){console.log(msg);}
+   this.clear=function(){;}
+}
+
+
+formPortal.setDebug();
+
+formPortal.doNothing=
+function(){
+	this.print('doNothing called');
+}
+
+//load runQuery.js
+
+formPortal.printMessage=
+function(msg){
+	crfHTML.createParagraph(msg,'formDiv')
+}
+
+
+formPortal.init=
+function(cb=null){
+   let that=this;
+   let action=function(){that.scriptsLoaded(cb);};
+   LABKEY.Utils.requiresScript(["crf/runQuery.js","crf/formGenerator.js","crf/crfRoleSelector.js","crf/crfSetup.js","crf/crfData.js","crf/crfHTML.js"],action);
+}
+
+formPortal.scriptsLoaded=
+function(cb=null){
+  crfHTML.init();
+  crfRoleSelector.set(crfSetup);
+  crfData.setSetup(crfSetup);
+  let initGenerator=function(){formGenerator.init(cb);};
+  let initData=function(){crfData.init(initGenerator);};
+  crfSetup.init(initData);
+}
+
+formPortal.generateFormArray=
+function(){
+   let fName='[generateFormArray]';
+   this.print(fName);
+	this.print(fName);
+   let that=this;
+   let action=function(){that.afterSetup();};
+   let parseSetup=function(){crfSetup.parseSetup(action);};
+   crfSetup.setContainers(parseSetup);
+	
+}
+
+formPortal.filterEntry=
+function(entry,filter){
+   let fName="[filterEntry]";
+
+   //this.print(fName+' candidate '+entry.entryId);
+
+   if (entry.Form!=filter.form)
+      return false;
+
+   //only select forms where status matches the target status
+   if (entry.FormStatus!=filter.formStatus){
+      return false;
+   }
+
+   
+   if (crfSetup.getSettings("filterUser") && filter.role=='crfEditor' && entry.UserId!=filter.userId){
+      //this.print(fName+' skipping identity mismatch: '+entry.UserId+'/'+filter.userId);  
+      return false;
+   }
+
+   if (crfSetup.getSettings("filterSite") && entry.Site!=filter.siteNumber){
+      //this.print(fName+' skipping site mismatch: '+entry.Site+'/'+filter.siteNumber);  
+      return false;
+
+   }
+   return true;
+}
+
+formPortal.afterSetup=
+function(){
+   let fName='[afterSetup]';
+   this.print(fName);
+   let that=this;
+   let cb=function(){that.updateForms();};
+   crfRoleSelector.makePortal(cb);
+	this.table=crfHTML.createTable('formDiv');
+}
+
+formPortal.updateForms=
+function(){
+//this is normallz callback on role selector
+   roleAndSite=crfRoleSelector.getRoleAndSite();
+   this.drawForms(roleAndSite);
+}
+
+formPortal.drawForms=
+function(roleAndSite=null){
+   let fName="[drawForms]"; 
+
+   if (!roleAndSite){
+      alert("Please set role and site");
+      return false;
+   }
+
+   //set filter
+   let ar=roleAndSite.split(":");
+   let role=ar[0];
+   let filter=new Object();
+
+   filter.role=role;
+   filter.siteNumber=ar[1];
+   filter.userId=LABKEY.Security.currentUser.id;
+
+   let accessModeColumn=filter.role+'Status';
+
+   
+   let formRows=crfSetup.getRows('dataForms');
+   //browse through forms
+   let fEntries=crfSetup.getRows('crfEntries');
+
+   this.print(fName+' all entries '+fEntries.length);
+	for (let i=0;i<formRows.length;i++){
+
+      //dataForms is Forms
+		let formEntry=formRows[i];
+		let formId=formEntry['Key'];
+      
+      //set the status for form		
+      filter.formStatus=formEntry[accessModeColumn];
+      this.print(fName+' target formStatus ['+formEntry['formName']+'] '+filter.formStatus);
+
+		//add row for each form
+		let row=this.table.rows[i];
+      if (!row) row=this.table.insertRow(i);
+
+      let labelCell=row.cells[0];
+      let formName=formEntry['formName'];
+      if (!labelCell){
+         labelCell=row.insertCell();
+         labelCell.innerText=formName;
+         crfHTML.addStyle(labelCell,'medium');
+         crfHTML.addStyle(labelCell,'center');
+
+         //crfHTML.createParagraph(formName,null,labelCell);
+      }
+      let cell=row.cells[1];
+      if (!cell) {
+         cell=row.insertCell();
+         crfHTML.addStyle(cell,'stretch');
+      }
+      crfHTML.clear(cell);
+
+      //update the filter with form idr
+      filter.form=formId;
+ 
+      let forms=new Array();
+
+		for (let j=0;j<fEntries.length;j++){
+         let entry=fEntries[j];
+			if (this.filterEntry(entry,filter))
+            forms.push(entry);
+		}
+      this.displayForms(cell,forms,roleAndSite,formId);
+		this.print(fName+' finished checking existing forms');
+
+		//only those that are allowed to create forms
+		//print('Status: '+qForm[creatorModeColumn]);
+		
+	}		  
+   if (filter.role=='crfManager')
+      //need formGenerator.js
+      formGenerator.showFormGenerator(this);
+   else
+      formGenerator.hideFormGenerator();
+
+}
+
+formPortal.displayForms=
+function(el,formList,roleAndSite,formId=null,idLabel=null){
+   let fName='[displayForms]';
+   //formList is a list of crfEntry entries
+   
+   let table=crfHTML.createTable(null,el);
+   let row=table.insertRow();
+   let n=formList.length;
+   let that=this;
+
+   for (let i=0;i<n;i++){
+      let entry=formList[i];
+      let cell=row.insertCell(i);
+      crfHTML.addStyle(cell,'stretch');
+
+      let fbox=crfHTML.createBox(null,cell);
+      //colormap of formStatus to colors
+      let stat=entry['FormStatus'];
+
+      let style=crfSetup.getEntryMap('formStatus')[stat]['color'];
+      if (!style) style='gold';
+      crfHTML.addStyle(fbox,style);
+      this.print(fName+' setting style ['+i+'] '+style);
+
+      let user=crfSetup.getMap('users')[entry['UserId']];
+      let idLabel=crfSetup.getParticipantLabel(entry);
+      let formStatus=crfSetup.getMap('formStatus')[stat];
+      let text=[entry['entryId'],user,idLabel,formStatus];
+      fbox.onclick=function(){that.openForm(entry,roleAndSite);};
+
+      for (let j=0;j<text.length;j++){
+         crfHTML.createParagraph(text[j],null,fbox);
+      }
+   }
+   if (n>0) return table;
+   if (!formId) return table;
+
+   //always add empty form
+
+   //should not be allowed to create new forms
+   let regFormId=crfSetup.getSettings('registrationFormId');
+   if (regFormId==formId) return table;
+   let cell=row.insertCell(n);
+   crfHTML.addStyle(cell,'stretch');
+   let fbox=crfHTML.createBox(null,cell);
+   crfHTML.addStyle(fbox,'empty');
+   fbox.onclick=function(){that.createForm(formId,roleAndSite,idLabel);};
+   return table;
+   
+}
+
+formPortal.openForm=
+function(crfEntry,roleAndSite){
+   let fName="[openForm]";
+	let crfRef=crfEntry.entryId;
+	
+	this.print(fName+" clicked for "+crfRef);
+   let ar=roleAndSite.split(':');
+   let role=ar[0];
+   let siteNumber=ar[1];
+
+
+	//select between review and view
+	//let formUrl=formEntry["formUrl"];
+	//if ("reviewMode" in config) formUrl=formEntry["reviewFormUrl"];
+	//print("Setting url "+formUrl);
+
+	//direct all to the same html
+	let formUrl="visitNew";
+
+	reviewMode="EDIT";
+	let params = {
+		"name": formUrl, 
+		// The destination wiki page. The name of this parameter is not arbitrary.
+		"entryId": crfRef,
+		"role" : role
+	};
+
+	//"formSetupQuery":formEntry["setupQuery"],
+	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);
+   this.print(fName+" redirecting to "+wikiURL);
+
+		 
+	window.location = wikiURL;
+}
+
+formPortal.createForm=
+function(formId,roleAndSite,idLabel=null){
+   let fName="[createForm]";
+   
+   let formRow=crfSetup.getEntryMap('dataForms')[formId];
+   let creator=formRow['creator'];
+   if (!creator) {
+      alert("Creator for form "+formRow['formName']+' not set');
+      return;
+   }
+   let ar=roleAndSite.split(':');
+   let role=ar[0];
+   let siteNumber=ar[1];
+
+   if (creator!=role){
+      alert("Can't create form as "+role+' only allowed for '+creator);
+      return;
+   }
+
+	this.print(fName+" create form w/id "+formId);
+	
+	let crfEntry=new Object();
+	crfEntry.entryId=Date.now();
+	crfEntry["Date"]=new Date();
+	crfEntry["View"]="[VIEW]";
+	crfEntry.formStatus=1;//In progress
+	//set other variables
+	//requires studyData as part of formConfig
+	let studyData=crfSetup.getRows('studyData')[0];
+   let varRows=crfSetup.getRows('crfStaticVariables');
+   for (let i=0;i<varRows.length;i++){
+      let varName=varRows[i].staticVariable;
+	   crfEntry[varName]=studyData[varName];
+   }
+	crfEntry.UserId=LABKEY.Security.currentUser.id;
+	crfEntry.Site=siteNumber;
+	this.print(fName+" setting site to id="+crfEntry.Site);
+	//from argument list
+	crfEntry.Form=formId;
+   if (idLabel){
+      crfEntry[crfSetup.getLocalIdLabel()]=crfSetup.getLocalId(idLabel);
+      crfEntry[crfSetup.getStudyIdLabel()]=crfSetup.getStudyId(idLabel);
+   }
+
+
+
+   let crfStatus=crfData.createCrfStatus(crfEntry);
+   crfStatus.operator=role;
+   crfStatus.action='createForm';
+   let that=this;
+   let cb=function(data){that.openForm(crfEntry,roleAndSite);};
+   let containerPath=crfSetup.getContainer('data');
+   let pass=function(data){runQuery.insertRows('lists','crfStatus',[crfStatus],cb,containerPath);};
+   runQuery.insertRows('lists','crfEntry',[crfEntry],pass,containerPath);
+}
+

+ 5 - 4
web/crf/participantIdManager.js

@@ -15,6 +15,7 @@ function(cb=null){
 
 participantIdManager.afterScripts=
 function(cb=null){
+   crfHTML.init();
    if (cb) cb();
 }
 
@@ -270,7 +271,7 @@ function(pM,pId){
    let fName='[setLabelMode1]';
 
    this.print(fName+' id '+pId);
-   ids=pId.split(':');
+   let ids=pId.split(':');
 
    let textValue=this.getTextElement(pM);
    this.print(fName+' textElement '+textValue);
@@ -282,7 +283,7 @@ function(pM,pId){
       this.print(fName+' setting local id '+loc);
       let tValLocal=this.getTextElement(pM,'LOCAL');
       tValLocal.innerText=loc;
-      this.setParticipantIdToCrfEntry(pM,loc,'LOCAL');
+      //this.setParticipantIdToCrfEntry(pM,loc,'LOCAL');
    }
 
    let x=this.getInputManage(pM);//getInputManage
@@ -324,8 +325,8 @@ participantIdManager.getCrfEntryFieldName=
 function(pM,mode="NONE"){
    let variable="Study";
    if (mode=="NONE") mode=pM.mode;
-   if (mode=="LOCAL") variable="Local";
-   return 'participant'+variable+'Id';
+   if (mode=="LOCAL") return crfSetup.getLocalIdLabel();
+   return crfSetup.getStudyIdLabel();
 }
 
 participantIdManager.setParticipantIdToCrfEntry=

+ 48 - 69
web/crf/participantPortal.js

@@ -5,12 +5,13 @@ participantPortal.print=function(msg){
 }
 
 participantPortal.idField='participantStudyId';
+participantPortal.localIdField='participantLocalId';
 
 participantPortal.init=
 function(cb=null){
    let that=this;
    let action=function(){that.scriptsLoaded(cb);};
-   LABKEY.Utils.requiresScript(["crf/crfSetup.js","crf/crfData.js","crf/crfHTML.js"],action);
+   LABKEY.Utils.requiresScript(["crf/crfSetup.js","crf/crfData.js","crf/crfHTML.js","crf/crfRoleSelector.js","crf/formPortalNew.js"],action);
 }
 
 participantPortal.scriptsLoaded=
@@ -18,9 +19,11 @@ function(cb=null){
    //if other script need init, just stack the init scripts
    //let action=function(){runQuery.init(cb);}
    crfData.setSetup(crfSetup);
+   crfRoleSelector.set(crfSetup);
    crfHTML.init();
-   let action=function(){crfData.init(cb);};
-   crfSetup.init(action);
+   let initFormPortal=function(){formPortal.init(cb);};
+   let initData=function(){crfData.init(initFormPortal);};
+   crfSetup.init(initData);
 }
 
 participantPortal.getParticipantMap=
@@ -45,19 +48,6 @@ function(id,formId){
    return pMap[id][formId];
 }
 
-participantPortal.getParticipantLabel=
-function(entry){
-   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";
-   return label;
- 
-}
-
-
 participantPortal.generateFormArray=
 function(){
    let fName='[generateFormArray]';
@@ -101,28 +91,60 @@ function(){
 
 participantPortal.makePortal=
 function(){
-   let idMap=crfData.getRegistrationMap(this.idField);
+   let that=this;
+   let updateParticipants=function(){that.updateParticipants();};
+   crfRoleSelector.makePortal(updateParticipants);
+   this.participantSelect=crfHTML.createSelect(new Object(),'formDiv');
+   this.displayTable=crfHTML.createTable('formDiv');
+   this.participantSelect.onchange=function(){that.displayEntries();}
+}
+
+participantPortal.updateParticipants=
+function(){
+   let fName='[participantPortal.updateParticipants]';
+   let roleAndSite=crfRoleSelector.getRoleAndSite();
+   let ar=roleAndSite.split(':');
+   let site=ar[1];
+   this.print(fName+' site '+site);
+   let regMap=crfData.getRegistrationEntryMap();
+   this.print(fName+' registration '+Object.keys(regMap).length);
+   //let idMap=crfData.getRegistrationMap(this.idField);
+   //let idLocalMap=crfData.getRegistrationMap(this.localIdField);
    let updatedMap=new Object();
-   for (q in idMap){
-      if (!idMap[q]) continue;
-      updatedMap[q]=idMap[q];
+   for (q in regMap){
+      let label=crfSetup.getParticipantLabel(regMap[q]);
+      this.print(fName+' label '+label+' site '+site);
+      if (regMap[q]['site']!=site) continue;
+      updatedMap[q]=label;
+            //idMap[q]+':Local '+idLocalMap[q];
    }
    updatedMap[1000]='NONE';
-   this.participantSelect=crfHTML.makeSelect(updatedMap,'formDiv');
-   this.displayTable=crfHTML.createTable('formDiv');
-   let that=this;
-   this.participantSelect.onchange=function(){that.displayEntries();}
+   updatedMap[1001]='Add new user';
+   crfHTML.addSelectOptions(this.participantSelect,updatedMap);
 }
 
 participantPortal.displayEntries=
 function(){
    let fName='[displayEntries]';
+   let roleAndSite=crfRoleSelector.getRoleAndSite();
    this.print(fName);
    let formRows=crfSetup.getRows('dataForms');
    
 
    //let idRows=crfData.getRegistration();
-   let selectId=this.participantSelect.options[this.participantSelect.selectedIndex].text;
+   let selectIdText=this.participantSelect.options[this.participantSelect.selectedIndex].text;
+   if (selectIdText=='Add new user'){
+      let formId=crfSetup.getSettings('registrationFormId');
+      if (!formId){
+         alert('registrationFormId not set in settings!');
+         return;
+      }
+      formPortal.createForm(formId,roleAndSite);
+      return;
+   }
+
+
+   let selectId=crfSetup.getStudyId(selectIdText);
    //let formId=formIds[Object.keys(formIds)[0]];
    //this.printParticipantArray();
    this.print(fName+' rows '+this.displayTable.rows.length);
@@ -148,50 +170,7 @@ function(){
       }
       crfHTML.clear(cell);
       let forms=this.getParticipantArray(selectId,formId);
-      this.displayForms(cell,forms);
+      formPortal.displayForms(cell,forms,roleAndSite,formId,selectIdText);
       this.print(fName+' ['+selectId+'/'+formId+'] forms '+forms.length);
    }
 }
-
-participantPortal.displayForms=
-function(el,formList){
-   //formList is a list of crfEntry entries
-   
-   let table=crfHTML.createTable(null,el);
-   let row=table.insertRow();
-   let n=formList.length;
-
-   let fn=1;
-   if (n>fn) fn=n;
-
-   for (let i=0;i<fn;i++){
-      let entry=formList[i];
-      let cell=row.insertCell(i);
-      crfHTML.addStyle(cell,'stretch');
-
-      let fbox=crfHTML.createBox(null,cell);
-      if (n==0){
-         crfHTML.addStyle(fbox,'empty');
-         break;
-      }
-      //colormap of formStatus to colors
-      let stat=entry['FormStatus'];
-      let style=crfSetup.getEntryMap('formStatus')[stat]['color'];
-      if (!style) style='gold';
-      crfHTML.addStyle(fbox,style);
-      let user=crfSetup.getMap('users')[entry['UserId']];
-      let idLabel=this.getParticipantLabel(entry);
-      let formStatus=crfSetup.getMap('formStatus')[stat];
-      let text=[entry['entryId'],user,idLabel,formStatus];
-
-      for (let j=0;j<text.length;j++){
-         crfHTML.createParagraph(text[j],null,fbox);
-      }
-   }
-   return table;
-   
-}
-
-
-
-