//global config variable const config=new Object(); function print(msg){ config.document.getElementById(config.debugArea).value+="\n"+msg; } function clear(){ config.document.getElementById(config.debugArea).value=""; } function getMode(){ if ("role" in config){ return config.role; } return "crfEditor"; } 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 getDataFromQueries(queryArray,cb){ afterQuery(new Object(),-1,queryArray,cb); } 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 e=queryArray[id]; let qconfig=new Object(); qconfig.containerPath=getContainer(e.containerName); qconfig.schemaName="lists"; 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){afterQuery(data,id,queryArray,cb);}; qconfig.failure=doNothing; LABKEY.Query.selectRows(qconfig); } function printMessage(msg){ let txt=config.document.createElement("p"); config.document.getElementById(config.div).appendChild(txt); txt.innerText=msg; } 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"; } function setContainer(label,container){ if (!(config.formConfig.hasOwnProperty('container'))){ config.formConfig.container=new Array(); } config.formConfig.container[label]=container; } function getContainer(label){ return config.formConfig.container[label]; } function generateFormArray(){ print("generateFormArray "+getMode()); config.formConfig=new Object(); config.formConfig.softwareVersion='T.1.11'; //report software version config.document.getElementById('version').innerText=config.formConfig.softwareVersion; 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=afterSettings; LABKEY.Query.selectRows(selectRows); } function afterSettings(data){ 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; } let st=config.formConfig.settings; print('afterSettings'); for (let k in st){ print('\t'+k+'='+st[k]); } //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')); //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); } 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,fcontinue); } function filterEntry(entry,filter,settings){ if (entry.Form!=filter.form) return false; //only select forms where status matches the target status if (entry.FormStatus!=filter.formStatus){ return false; } print('Candidate '+entry.entryId); //TODO: smart filter on user (now we get to see all) // //for editors if ("filterUser" in settings && filter.role=='crfEditor' && entry.UserId!=filter.userId){ print('Skipping identity mismatch: '+entry.UserId+'/'+filter.userId); return false; } //for others let matchingSite=-1; let potentialSiteNumbers="["; for (let k=0;k<filter.sites.length;k++){ if (k>0) potentialSiteNumbers+=','; potentialSiteNumbers+=filter.sites[k].siteNumber; //skip mismatching sites if (entry.Site!=filter.sites[k].siteNumber) continue; matchingSite=filter.sites[k].siteNumber; break; } potentialSiteNumbers+=']'; if (matchingSite==-1){ print('Skipping wrong site: '+entry.Site+'/'+potentialSiteNumbers); return false; } return true; } function fcontinue(){ let fName='[fcontinue]'; let formConfig=config.formConfig; print("Number of study data entries: "+formConfig.studyData.rows.length); print("ParticipantId: "+formConfig.studyData.rows[0].SubjectColumnName); let dataForms=formConfig.dataForms.rows; formConfig.table=config.document.createElement("table"); config.document.getElementById(config.div).appendChild(formConfig.table); let accessModeColumn=getMode()+'Status'; print('accessModeColumn '+accessModeColumn); //cutting down on number of fields //let creatorModeColumn=getMode()+'Creator'; //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; for (let i=0;i<users.length;i++){ if (users[i].UserId!=currentUserId) continue; currentUser=users[i]; } //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); } //currentUser was not matched in fRows if (currentUserRoles.length==0){ printMessage('User '+currentUser.DisplayName+" can't act as "+config.role); return; } //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]); } } 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; } msg+=')'; printMessage(msg); let filter=new Object(); filter.role=config.role; filter.userId=currentUser.UserId; filter.sites=currentSites; //browse through forms for (let i=0;i<dataForms.length;i++){ //dataForms is Forms let qForm=dataForms[i]; let formKey=qForm.Key; filter.form=qForm.Key; //add row for each form let row=formConfig.table.insertRow(i); let formName=qForm.formName; print("["+i+"/"+formKey+']: '+formName); //column counter let k=0; //get the target status let formStatus=qForm[accessModeColumn]; filter.formStatus=qForm[accessModeColumn]; print('target formStatus '+formStatus); for (let j=0;j<fEntries.length;j++){ let entry=fEntries[j]; if (!filterEntry(entry,filter,config.formConfig.settings)) continue; //insert form // let fbox=config.document.createElement("div"); fbox.classList.add("box","gold"); let fp=config.document.createElement("p"); let id=entry.entryId; fp.innerHTML=id; //it would be great if this were patientId if available //fp.classList.add("large","center"); fp.classList.add("center"); fbox.appendChild(fp); let fp1=config.document.createElement("p"); let user="NONE"; for (let ii=0;ii<users.length;ii++){ if (users[ii].UserId!=entry.UserId) continue; user=users[ii].DisplayName; break; } fp1.innerHTML=user; fp1.classList.add("center"); fbox.appendChild(fp1); let fp2=config.document.createElement("p"); fp2.innerHTML=formName; fp2.classList.add("center"); fbox.appendChild(fp2); let fp3=config.document.createElement("p"); fp3.id="pid"+id; 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); let cell=row.insertCell(k); cell.classList.add("stretch"); cell.id="box"+entry.crfRef; let button=config.document.createElement("button"); button.appendChild(fbox); button.onclick=function(){openForm(entry)}; cell.appendChild(button); k++; } 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"); let fp=config.document.createElement("p"); fp.innerHTML="Create new"; fbox.appendChild(fp); let fp2=config.document.createElement("p"); fp2.innerHTML=formName; fp2.classList.add("center"); fbox.appendChild(fp2); let cell=row.insertCell(k); cell.classList.add("stretch"); let button=config.document.createElement("button"); button.appendChild(fbox); button.onclick=function(){createForm(formKey)}; cell.appendChild(button); } } function openForm(crfEntry){ let formConfig=config.formConfig; let crfRef=crfEntry.entryId; 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("Setting form "+formConfig.dataForms.rows[i].formName); formEntry=formConfig.dataForms.rows[i]; break; } if (formEntry==undefined) return; //select between review and view //let formUrl=formEntry["formUrl"]; //if ("reviewMode" in config) formUrl=formEntry["reviewFormUrl"]; //print("Setting url "+formUrl); //direct all to the same html let formUrl="visit"; reviewMode="EDIT"; if ("reviewMode" in config) reviewMode=config.reviewMode; let params = { "name": formUrl, // The destination wiki page. The name of this parameter is not arbitrary. "entryId": crfRef, "formId":formId, "role" : config.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_tecant", formUrl , containerPath, params); print("Redirecting to "+wikiURL); window.location = wikiURL; } function createForm(formId){ let formConfig=config.formConfig; print("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=formConfig.studyData.rows[0]; 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; 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(crfEntry)}; qconfig.rows=[crfEntry]; LABKEY.Query.insertRows(qconfig); }