ソースを参照

Adding participantPortal, which in many cases uses new modular infrastructure. Would be good to use the same (crfSetup, crfData) in formPortal.js and crfVisit.js

Andrej Studen 2 年 前
コミット
fbc1ffb1b3

+ 3 - 24
views/participantPortal.html

@@ -1,25 +1,3 @@
-<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>
@@ -34,7 +12,7 @@ height:120px;
 </div>
 
 <script type "text/javascript">
-window.onload=init;
+window.onload=loadScripts;
 
 function loadScripts(){
   LABKEY.requiresScript(["crfTecant/participantPortal.js"],init);
@@ -42,6 +20,7 @@ function loadScripts(){
 
 function init(){
    console.log('Here participantPortal');
-   //formPortal.scriptsLoaded();
+   let action=function(){ participantPortal.generateFormArray();};
+   participantPortal.init(action);
 }
 </script>

+ 255 - 0
web/crfTecant/crfData.js

@@ -0,0 +1,255 @@
+//not tested yet.
+//to use, add crfTecant/crfData.js to requiresScript and in the call-back, run init
+//will work with crfSetup as setup object
+
+var crfData={};
+
+crfData.init=
+function(cb=null){
+   this.print('[crfData:init]');
+   let that=this;
+   let action=function(){that.afterScripts(cb);};
+   LABKEY.requiresScript(["crfTecant/runQuery.js","crfTecant/variableList.js"],action);
+}
+
+crfData.afterScripts=
+function(cb=null){
+   if (cb) cb();
+}
+
+crfData.setSetup=
+function(setup){
+   this.setup=setup;
+}
+
+crfData.getContainer=
+function(label){
+   return this.setup.getContainer(label);
+}
+
+crfData.print=
+function(msg){
+   console.log(msg);
+}
+
+//getters
+crfData.getSnapshotObject=
+function(){
+   if (!("dataQueriesSnapshot" in this))
+      this.dataQueriesSnapshot=new Object();
+   return this.dataQueriesSnapshot;
+}
+
+
+crfData.getQuerySnapshot=
+function(queryName){
+   //check whether queryName is in snapshotObject?
+   return this.getSnapshotObject()[queryName];
+}
+
+crfData.getLayoutObject=
+function(){
+   if (!("dataQueriesLayout" in this))
+      this.dataQueriesLayout=new Object();
+   return this.dataQueriesLayout;
+}
+
+crfData.getQueryLayout=
+function(queryName){
+   //check whether queryName is in snapshotObject?
+   return this.getLayoutObject()[queryName];
+}
+
+crfData.getLookupObject=
+function(){
+   if (!("lookup" in this))
+      this.lookup=new Object();
+   return this.lookup;
+}
+
+crfData.getLookup=
+function(queryName){
+   let x=this.getLookupObject();
+   if (queryName in x) return x[queryName];
+   return null;
+}
+
+crfData.getQueryList=
+function(){
+   if (!("queryList" in this))
+      this.queryList=new Object();
+   return this.queryList;
+}
+
+crfData.getRegistration=
+function(){
+   let regQueryPars=variableList.parseVariables(this.setup.getSettings('registrationQuery'));
+   let query=regQueryPars['query'];
+   return this.getQuerySnapshot(query).rows;
+}
+
+crfData.getRegistrationMap=
+function(value=null){
+   let rows=this.getRegistration();
+   let qMap=new Object();
+   let key='Key';
+   if (!value) value='participantStudyId';
+   for (let i=0;i<rows.length;i++){
+      qMap[rows[i][key]]=rows[i][value];
+   }
+   return qMap;
+}
+
+crfData.setDataLayout=
+function(formId,cb){
+   let fName='[setDataLayout]';
+   this.print(fName);
+	let rowsSetup=this.setup.selectFormSetupRows(formId);
+   let queryArray=new Array();
+	let dS=this.getLayoutObject();//reference only
+   let qList=this.getQueryList();
+	let qMap=this.setup.getMap('inputLists');
+	//config.formConfig.lookup=new Object();
+	for (let i=0;i<rowsSetup.length;i++){
+		let entry=rowsSetup[i];
+		//skip review rows
+		if (entry['showFlag']=='REVIEW')
+			continue;
+		let queryId=entry['queryName'];
+		let q=qMap[queryId];
+		queryArray.push(runQuery.makeQuery(dS,'data',q,q,[]));
+      qList[q]=0;
+      this.print(fName+' adding '+q);
+		if (entry['showQuery']!="NONE"){
+			let sq=entry['showQuery'];
+		   queryArray.push(runQuery.makeQuery(dS,'data',sq,sq,[]));
+         qList[sq]=0;
+         this.print(fName+' adding '+sq);
+			
+		}
+	}
+
+	//always add reviews
+   let q='reviewComments';
+   queryArray.push(runQuery.makeQuery(dS,'data',q,q,[]));
+   qList[q]=0;
+   let that=this;
+   let action=function(){that.processLayout(cb);};
+   runQuery.getDataFromQueries(this,queryArray,action);
+}
+
+//this happens after the for loop, so all dataQueries objects are set
+crfData.processLayout=
+function(cb=null){
+   let fName='[processLayout]';
+   let qList=this.getQueryList();
+   //for layouts
+   let queryArray=new Array();
+   let targetObject=this.getLookupObject();
+   let lookupSet=new Object();
+   for (let q in qList){
+      let qobject=this.getQueryLayout(q);
+	   this.print(fName+" inspecting layout for "+q+" "+qobject);
+	   qobject.fields=qobject.metaData.fields;
+	   qobject.title=this.findTitle(q);
+
+      //check for lookups
+	   for (let f in qobject.fields){
+		   //anything else is simple but lookup
+		   let field=qobject.fields[f];
+		   if (!("lookup" in field)) continue;
+         let lookup=field.lookup;
+         let qObject=this.getLookup(lookup.queryName);
+         if (qObject) continue;
+         //add to list
+         let qName=lookup.queryName;
+         let qCode=qName+':'+lookup.keyColumn+':'+lookup.displayColumn;
+         let e=runQuery.makeQuery(targetObject,'data',qName,qCode,[]);
+         //adjust minor settings
+         if (lookup.containerPath) e.containerPath=lookup.containerPath;
+         e.schemaName=lookup.schemaName;
+         e.columns=lookup.keyColumn+','+lookup.displayColumn;
+         lookupSet[qCode]=e;
+         this.print(fName+' inserting '+qCode);
+      }
+   }
+   for (let x in lookupSet){
+      queryArray.push(lookupSet[x]);
+      this.print(fName+' adding '+x);
+      for (let v in lookupSet[x]){
+         this.print(fName+' value ['+v+'] '+lookupSet[x][v]);
+      }
+   }
+   //this.print(fName+' print '+targetObject.print);
+   let that=this;
+   let action=function(){that.processLookup(cb);};
+   this.print(fName+' getDataFromQueries');
+   runQuery.getDataFromQueries(this,queryArray,action);
+   this.print(fName+' getDataFromQueries done');
+}
+
+crfData.processLookup=
+function(cb=null){
+   let fName="[processLookup]";
+
+   let obj=this.getLookupObject();
+   for (let q in obj){
+	   this.print(fName+" "+q);
+      let a=q.split(':');
+      if (a.length<3) continue;
+      let lookupName=a[0];
+      let key=a[1];
+      let val=a[2];
+      obj[lookupName]=new Object();
+      this.print(fName+' adding ['+lookupName+'] '+key+'/'+val);
+      let lObject=obj[lookupName];
+
+	   lObject.LUT=new Array();//key to value
+	   lObject.ValToKey=new Array();//value to key
+	   lObject.keyColumn=key
+	   lObject.displayColumn=val;
+      
+      let qRows=obj[q].rows;
+	   for (let i=0;i<qRows.length;i++){
+         let r=qRows[i];
+         this.print(fName+' LUT ['+r[key]+'] '+r[val]);
+		   lObject.LUT[r[key]]=r[val];
+		   lObject.ValToKey[r[val]]=r[key];
+	   }
+   }
+	if (cb) cb();
+}
+
+crfData.setData=
+function(crfRef,cb=null){
+	fName='[setData]';
+	//let crfMatch=this.getCRFref();
+	//let parentCrf=config.formConfig.crfEntry['parentCrf'];
+	//if (parentCrf!=undefined) crfMatch=parentCrf;
+
+	this.print(fName+' form crf ['+crfRef+'] ');
+
+   let queryArray=new Array();
+   let targetObject=this.getSnapshotObject();
+	//collect data and execute callback cb for queries in cb.queryList
+   let qList=this.setup.getQueryList();
+	for (q in qList){
+
+		let filters=[LABKEY.Filter.create("crfRef",crfRef)];
+      queryArray.push(runQuery.makeQuery(targetObject,'data',q,q,filters));
+
+	}
+   runQuery.getDataFromQueries(this,queryArray,cb);
+}
+
+crfData.setRegistration=
+function(cb=null){
+   let regQueryPars=variableList.parseVariables(this.setup.getSettings('registrationQuery'));
+   let q=regQueryPars['query'];
+   let queryArray=new Array();   
+   let targetObject=this.getSnapshotObject();
+   queryArray.push(runQuery.makeQuery(targetObject,'data',q,q,[]));
+   runQuery.getDataFromQueries(this,queryArray,cb);
+}
+
+

+ 34 - 0
web/crfTecant/crfHTML.css

@@ -0,0 +1,34 @@
+.box{
+width:120px;
+height:120px;
+}
+
+.gold{ background-color: gold; }
+.red{ background-color: darkred; }
+.green {background-color: green;}
+.orange {background-color: orange;}
+.blue {background-color: steelblue;}
+.brown {background-color: tan;}
+.empty { background-color: #dddddd;
+      border-style: dashed;
+}
+
+.large{
+	font-size: 30px;
+}
+
+.medium{
+   font-size: 24px;
+}
+
+.center{
+	text-align: center;
+}
+
+.stretch{
+	padding: 50px;
+}
+
+.stretch {
+   padding: 50px;
+}

+ 81 - 0
web/crfTecant/crfHTML.js

@@ -0,0 +1,81 @@
+var crfHTML={};
+
+crfHTML.print=
+function(msg){
+   console.log(msg);
+}
+
+crfHTML.init=
+function(cb=null){
+   LABKEY.requiresCss("crfTecant/crfHTML.css");
+   this.print('CSS loaded');
+   if (cb) cb();
+}
+
+crfHTML.makeSelect=
+function(qMap,id=null,el=null){
+   let fName='[makeSelect]';
+   let input=document.createElement('select');
+   let opt = document.createElement("option");
+	opt.text = "<Select>";
+	opt.value = -1;
+	input.options[0] = opt;
+	this.print(fName+": Adding <Select>");
+	
+
+	//add other, label them with LUT
+	for (let v in qMap) {
+		this.print(fName+': populating '+v+': '+qMap[v]);
+
+		let opt = document.createElement("option");
+		opt.text = qMap[v];
+		opt.value = v;
+		input.options[input.options.length] = opt;
+		
+	}
+	input.selectedIndex=0;	
+   this.append(input,id,el);
+   return input;
+}
+
+crfHTML.append=
+function(element,id=null,el=null){
+   if (id) document.getElementById(id).appendChild(element);
+   if (el) el.appendChild(element);
+}
+
+crfHTML.clear=
+function(el){
+   while (el.hasChildNodes()){
+      el.removeChild(el.lastChild);
+   }
+}
+crfHTML.createTable=
+function(id=null,el=null,style=null){
+   let table=document.createElement('table');
+   this.append(table,id,el);
+   if (style) this.addStyle(style);
+   return table;
+}
+
+crfHTML.addStyle=
+function(el,style){
+   el.classList.add(style);
+}
+   
+
+crfHTML.createBox=
+function(id=null,el=null){
+   let fbox=document.createElement('div');
+   fbox.classList.add("box");
+   this.append(fbox,id,el);
+   return fbox;
+}
+
+crfHTML.createParagraph=
+function(text,id=null,el=null){
+   let fp=document.createElement("p");
+   fp.innerHTML=text;
+   fp.classList.add("center");
+   this.append(fp,id,el);
+}

+ 252 - 0
web/crfTecant/crfSetup.js

@@ -0,0 +1,252 @@
+var crfSetup={};
+
+crfSetup.print=
+function(msg){
+   console.log(msg);
+}
+
+crfSetup.init=
+function(cb=null){
+   let fName="[crfSetup:init]";
+   this.print(fName);
+   let that=this;
+   let action=function(){that.afterScripts(cb);}
+   LABKEY.requiresScript(["crfTecant/runQuery.js"],action);
+}
+
+crfSetup.afterScripts=
+function(cb=null){
+   if (cb) cb();
+}
+
+crfSetup.setContainer=
+function(label,container){
+	if (!(this.hasOwnProperty('container'))){
+		this.container=new Array();
+	}
+	this.container[label]=container;
+}
+
+crfSetup.getContainer=
+function(label){
+	return this.container[label];
+}
+
+crfSetup.getSettings=
+function(variable){
+   if (variable in this.settings){
+      return this.settings[variable];
+   }
+   return null;
+}
+
+crfSetup.getRows=
+function(objectName){
+   if (objectName in this)
+      return this[objectName].rows;
+   return new Array();
+}
+
+crfSetup.getMaps=
+function(){
+   if (!("maps" in this))
+      this.maps=new Object();
+   return this.maps;
+}
+
+crfSetup.getEntryMaps=
+function(){
+   if (!("entryMaps" in this))
+      this.entryMaps=new Object();
+   return this.entryMaps;
+}
+
+crfSetup.getMap=
+function(queryName){
+   let maps=this.getMaps();
+   if (!(queryName in maps))
+      this.parseMap(queryName);
+   return maps[queryName];
+
+}
+
+crfSetup.getEntryMap=
+function(queryName){
+   let entryMaps=this.getEntryMaps();
+   if (!(queryName in entryMaps))
+      this.parseEntryMap(queryName);
+   return entryMaps[queryName];
+}
+
+crfSetup.setContainers=
+function(cb=null){
+
+   this.setContainer('data',LABKEY.ActionURL.getContainer());
+	this.setContainer('config',LABKEY.ActionURL.getContainer());
+	this.setContainer('CRF',LABKEY.ActionURL.getContainer());
+   let selectRows=new Object();
+	//this is local data
+	selectRows.containerPath=this.getContainer('CRF');
+	selectRows.schemaName='lists';
+	selectRows.queryName='crfSettings';
+	//store form related data to this object
+   let that=this;
+	selectRows.success=function(data){that.parseSettings(data,cb);};
+	LABKEY.Query.selectRows(selectRows);
+}
+
+crfSetup.parseSettings=
+function(data,cb){
+   let fName="[parseSettings]";
+	this.settings=new Array();
+	for (let i=0;i<data.rows.length;i++){
+		let n=data.rows[i]['name'];
+		let v=data.rows[i]['value'];
+		this.settings[n]=v;
+	}
+
+	this.print(fName);
+	for (let k in this.settings){
+		this.print(fName+'\t'+k+'='+this.settings[k]);
+	}
+
+	//if ('dataContainer' in st){
+	//	setContainer('data',st['dataContainer']);
+	//}
+	let vname='configContainer';
+	if (vname in this.settings){
+		this.setContainer('config',this.settings[vname]);
+	}
+	this.print(fName+' config: '+this.getContainer('config'));
+	this.print(fName+' data: '+this.getContainer('data'));
+
+   if (cb) cb();
+
+}
+
+crfSetup.parseSetup=
+function(cb=null){
+	//setup queryArray
+	let queryArray=new Array();
+
+   //targetObject
+   let targetObject=this;
+
+   //static variables
+	queryArray.push(runQuery.makeQuery(targetObject,'data','crfStaticVariables','crfStaticVariables',[]));
+	//Forms
+	queryArray.push(runQuery.makeQuery(targetObject,'config','Forms','dataForms',[]));
+	//users
+	queryArray.push(runQuery.makeQuery(targetObject,'data','users','users',[]));
+	queryArray[queryArray.length-1].schemaName='core';
+	//inputLists
+	queryArray.push(runQuery.makeQuery(targetObject,'config','inputLists','inputLists',[]));
+	//crfEditors
+	queryArray.push(runQuery.makeQuery(targetObject,'config','crfEditors','crfEditors',[]));
+	//crfMonitors
+	queryArray.push(runQuery.makeQuery(targetObject,'config','crfMonitors','crfMonitors',[]));
+	//crfSponsors
+	queryArray.push(runQuery.makeQuery(targetObject,'config','crfSponsors','crfSponsors',[]));
+	//crfManagers
+	queryArray.push(runQuery.makeQuery(targetObject,'config','crfManagers','crfManagers',[]));
+	//FormStatus
+	queryArray.push(runQuery.makeQuery(targetObject,'config','FormStatus','formStatus',[]));
+	//site
+	queryArray.push(runQuery.makeQuery(targetObject,'config','site','siteData',[]));
+	//crfEntry
+	queryArray.push(runQuery.makeQuery(targetObject,'data','crfEntry','crfEntries',[]));
+   
+   queryArray.push(
+		runQuery.makeQuery(targetObject,'config','generateConfig','generateConfigData',[]));	
+   let that=this;
+   let action=function(){that.addStudyProperties(cb);};
+	runQuery.getDataFromQueries(this,queryArray,action);
+}
+
+crfSetup.addStudyProperties=
+function(cb){
+   //setup queryArray
+	let queryArray=new Array();
+   let targetObject=this;
+	
+	queryArray.push(runQuery.makeQuery(targetObject,'data','StudyProperties','studyData',[]));
+	let e=queryArray[queryArray.length-1];
+	e.schemaName='study';
+   let columnModel="";
+	let varRows=this.getRows('crfStaticVariables');
+	for (let i=0;i<varRows.length;i++){
+      if (i>0) columnModel+=',';
+      columnModel+=varRows[i]['staticVariable'];
+   }
+	e.columns=columnModel;
+   let that=this;
+   //let action=function(){that.fcontinue();};
+   //let action=function(){that.parseQueryMap(cb);};
+   let action=cb;
+   runQuery.getDataFromQueries(this,queryArray,action);
+
+}
+
+crfSetup.selectFormSetupRows=
+function(formId){
+	let formSetupRows=new Array();
+   let config=this.config;
+	let allRows=this.getRows("formSetup");
+	for (let i=0;i<allRows.length;i++){
+		let formEntry=allRows[i];
+		if (formEntry.formName==formId)
+			formSetupRows.push(formEntry);
+	}
+	return formSetupRows;
+}
+
+crfSetup.parseMap=
+function(queryName){
+   let fName='[parseMap/'+queryName+']';
+   let key="Key";
+   let value="value";
+   if (queryName=="inputLists")
+      value="queryName";
+   if (queryName=="users"){
+      key="UserId";
+      value="DisplayName";
+   }
+   if (queryName=='dataForms')
+      value='formName';
+   if (queryName=='formStatus')
+      value='formStatus';
+   this.print(fName);
+   let rows=this.getRows(queryName);
+   this.maps[queryName]=new Object();
+   let qMap=this.maps[queryName];
+   for (let i=0;i<rows.length;i++){
+      let r=rows[i];
+      qMap[r[key]]=r[value];
+      //this.print(fName+' ['+r[key]+'] '+r[value]);
+   }
+   
+}
+
+crfSetup.parseEntryMap=
+function(queryName){
+   let rows=this.getRows(queryName);
+   this.entryMaps[queryName]=new Object();
+   let qMap=this.entryMaps[queryName];
+   let key='Key';
+   if (queryName=='Users') key='UserId';
+   for (let i=0;i<rows.length;i++){
+      let r=rows[i];
+      qMap[r[key]]=r;
+      //this.print(fName+' ['+r[key]+'] '+r[value]);
+   }
+}
+ 
+crfSetup.printMap=
+function(queryName){
+   let fName='[printMap]';
+   let qMap=this.getMap(queryName);
+   for (let x in qMap){
+      this.print(fName+' ['+x+'] '+qMap[x]);
+   }
+}
+

+ 175 - 4
web/crfTecant/participantPortal.js

@@ -4,23 +4,194 @@ participantPortal.print=function(msg){
    console.log(msg);
 }
 
+participantPortal.idField='participantStudyId';
+
 participantPortal.init=
 function(cb=null){
    let that=this;
    let action=function(){that.scriptsLoaded(cb);};
-   LABKEY.Utils.requiresScript(["crfTecant/runQuery.js"],action);
+   LABKEY.Utils.requiresScript(["crfTecant/crfSetup.js","crfTecant/crfData.js","crfTecant/crfHTML.js"],action);
 }
 
 participantPortal.scriptsLoaded=
 function(cb=null){
-   if (cb) cb();
+   //if other script need init, just stack the init scripts
+   //let action=function(){runQuery.init(cb);}
+   crfData.setSetup(crfSetup);
+   crfHTML.init();
+   let action=function(){crfData.init(cb);};
+   crfSetup.init(action);
 }
 
+participantPortal.getParticipantMap=
+function(){
+   if (!("participantMap" in this)){
+      this.participantMap=new Object();
+      this.sortByParticipantId();
+   }
+   return this.participantMap;
+}
+   
+
+participantPortal.getParticipantArray=
+function(id,formId){
+   let fName='[getParticipantArray/'+id+','+formId+']';
+   //this.print(fName);
+   let pMap=this.getParticipantMap();
+   if (!(id in pMap))
+      pMap[id]=new Object();
+   if (!(formId in pMap[id]))
+      pMap[id][formId]=new Array();
+   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]';
+   this.print(fName);
+   //gang callbacks (last to first)
+   let that=this;
+   let makePortal=function(){that.makePortal();};
+   let setRegistration=function(){crfData.setRegistration(makePortal);};
+   let action=function(){crfSetup.parseSetup(setRegistration);}
+   crfSetup.setContainers(action);
+}
+
+participantPortal.sortByParticipantId=
+function(){
+   let fName='[sortByParticipantId]';
+   //this.print(fName);
+   //let pMap=this.getParticipantMap();
+   let rows=crfSetup.getRows('crfEntries');
+   for (let i=0;i<rows.length;i++){
+      let entry=rows[i];
+      let id=entry[this.idField];
+      if (!id) id="NONE";
+      let formId=entry['Form'];
+      let pArray=this.getParticipantArray(id,formId);
+      pArray.push(entry);
+      this.print(fName+' pushing '+id+','+formId);
+   }
+}
+
+participantPortal.printParticipantArray=
+function(){
+   let fName='[printParticipantMap]';
+   this.print(fName);
+   let pMap=this.getParticipantMap();
+   for (let q in pMap){
+      for (let x in pMap[q])
+         this.print(fName+' ['+q+','+x+'] '+pMap[q][x].length);
+   }
+}
+
+
+participantPortal.makePortal=
+function(){
+   let idMap=crfData.getRegistrationMap(this.idField);
+   let updatedMap=new Object();
+   for (q in idMap){
+      if (!idMap[q]) continue;
+      updatedMap[q]=idMap[q];
+   }
+   updatedMap[1000]='NONE';
+   this.participantSelect=crfHTML.makeSelect(updatedMap,'formDiv');
+   this.displayTable=crfHTML.createTable('formDiv');
    let that=this;
-   let action=function(){that.fcontinue0();};
-   this.init(action);
+   this.participantSelect.onchange=function(){that.displayEntries();}
+}
+
+participantPortal.displayEntries=
+function(){
+   let fName='[displayEntries]';
+   this.print(fName);
+   let formRows=crfSetup.getRows('dataForms');
+   
+
+   //let idRows=crfData.getRegistration();
+   let selectId=this.participantSelect.options[this.participantSelect.selectedIndex].text;
+   //let formId=formIds[Object.keys(formIds)[0]];
+   //this.printParticipantArray();
+   this.print(fName+' rows '+this.displayTable.rows.length);
+
+   for (let i=0;i<formRows.length;i++){
+      let formId=formRows[i]['Key'];
+      let row=this.displayTable.rows[i];
+      if (!row) row=this.displayTable.insertRow(i);
+      let labelCell=row.cells[0];
+      let formName=crfSetup.getMap('dataForms')[formId];
+      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);
+      let forms=this.getParticipantArray(selectId,formId);
+      this.displayForms(cell,forms);
+      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;
+   
+}
+
+
+
 

+ 2 - 1
web/crfTecant/runQuery.js

@@ -3,6 +3,7 @@ var runQuery={};
 //external dependencies:
 //LABKEY.Query
 //print -> configObject.print
+//getContainter -> parentClass.getContainer
 
 runQuery.makeQuery=
 function(targetObject,containerName,queryName,fieldName,filterArray){
@@ -50,7 +51,7 @@ function(data,id,parentClass,queryArray,cb){
 	}
 	id+=1;
 	if (id==queryArray.length) {
-		cb();
+		if (cb) cb();
 		return;
 	}