crfVisit.js 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683
  1. const config=new Object();
  2. function clear(){
  3. let el=config.document.getElementById(config.debugId);
  4. if (el===null) {
  5. //alert("Debug section not initialized");
  6. return;
  7. }
  8. config.document.getElementById(config.debugId).value="";
  9. }
  10. function print(msg){
  11. let el=config.document.getElementById(config.debugId);
  12. if (el===null) {
  13. //alert("Debug section not initialized. Message: "+msg);
  14. return;
  15. }
  16. el.value+="\n"+msg;
  17. }
  18. function getCRFrefFirst(){
  19. //crfRef is part of html call and gets stored in the page
  20. return config.document.getElementById(config.crfRefId).innerHTML;
  21. }
  22. function getCRFref(){
  23. //'crfRefId'
  24. return config.formConfig.crfEntry['entryId'];
  25. }
  26. function onFailure(errorInfo, options, responseObj){
  27. if (errorInfo && errorInfo.exception)
  28. alert("Failure: " + errorInfo.exception);
  29. else
  30. alert("Failure: " + responseObj.statusText);
  31. }
  32. function generateDebugSection(){
  33. //let debug=true;
  34. //if (debug) print("generateDebugSection "+sectionName);
  35. let formName=config.debugDiv;
  36. let sectionName="debugSection";
  37. let sectionTitle="Debug Messages";
  38. let tb=config.document.createElement('table');
  39. tb.className='t2';
  40. let row=tb.insertRow();
  41. let cell=config.document.createElement('th');
  42. row.appendChild(cell);
  43. cell.setAttribute("colspan","4");
  44. cell.style.fontSize="20px";
  45. cell.style.textAlign="center";
  46. let cellData=config.document.createTextNode(sectionTitle);
  47. cell.appendChild(cellData);
  48. cell=row.insertCell();
  49. let input=config.document.createElement("input");
  50. input.type="button";
  51. input.value="Show";
  52. input.id="toggle"+sectionName+"VisbilityButton";
  53. input.onclick=function(){toggleVisibility(sectionName,input.id)};
  54. cell.appendChild(input);
  55. config.document.getElementById(formName).appendChild(tb);
  56. let div=config.document.createElement('div');
  57. div.id=sectionName;
  58. div.style.display="none";
  59. config.document.getElementById(formName).appendChild(div);
  60. let debugArea=config.document.createElement('textarea');
  61. debugArea.rows=10;
  62. debugArea.cols=95;
  63. debugArea.id=config.debugId;
  64. div.appendChild(debugArea);
  65. }
  66. function setAdditionalData(additionalData,entry){
  67. let debug=true;
  68. if (debug) print("setAdditionalData");
  69. if (entry["showFlag"]==="NONE") {
  70. if (debug) print("Returning empty additionalData");
  71. return additionalData;
  72. }
  73. if (entry["showFlag"]==="REVIEW") {
  74. //abuse additionalData to signal different segment
  75. if (debug) print("Returning additionalData that signal review form");
  76. additionalData.isReview=true;
  77. return additionalData;
  78. }
  79. additionalData.showFlag=entry["showFlag"];
  80. additionalData.showFlagValue=entry["showFlagValue"];
  81. additionalData.queryName=entry["showQuery"];
  82. additionalData.filters=new Object();
  83. additionalData.filters['crfRef']=getCRFref();
  84. return additionalData;
  85. }
  86. function fullAccessSetup(listName){
  87. //generate setup object whcih should contain fields:
  88. //readonlyFlag - whether the dataset is writeable
  89. //filters - selection fields that allow creation of LABKEY.Filter.create()
  90. //getInputId - formating of unique ids for html elements
  91. //addApply - whether a submit/Save button is generated
  92. //unique - whether entries in list are unique
  93. let debug=true;
  94. if (debug) print("fullAccessSetup");
  95. let setup=new Object();
  96. setup.readonlyFlag=function(vName){return false};
  97. setup.filters=new Object();
  98. setup.filters['crfRef']=getCRFref();
  99. setup.getInputId=function(vName){return listName+"_"+vName;}
  100. setup.addApply="Save";
  101. return setup;
  102. }
  103. function readonlySetup(){
  104. //see definition of setup object above
  105. let debug=true;
  106. if (debug) print("readonlySetup");
  107. let setup=new Object();
  108. setup.readonlyFlag=function(vName){return true};
  109. setup.filters=new Object();
  110. setup.filters['crfRef']=getCRFref();
  111. setup.getInputId=function(vName){return vName;}
  112. return setup;
  113. }
  114. //afterFormConfig replaced by afterFormSetup
  115. //afterFormSetupLookup replaced by afterFormDatasets
  116. function generateSection(sectionName, sectionTitle, listName, additionalData){
  117. let formName=config.masterForm;//this is HTML designator of area on page
  118. let debug=true;
  119. if (debug) print("generateSection "+sectionName);
  120. let tb=config.document.createElement('table');
  121. tb.className='t2';
  122. let row=tb.insertRow();
  123. let cell=config.document.createElement('th');
  124. row.appendChild(cell);
  125. cell.setAttribute("colspan","4");
  126. cell.style.fontSize="20px";
  127. cell.style.textAlign="center";
  128. let cellData=config.document.createTextNode(sectionTitle);
  129. cell.appendChild(cellData);
  130. cell=row.insertCell();
  131. let input=config.document.createElement("input");
  132. input.type="button";
  133. input.value="Show";
  134. input.id="toggle"+sectionName+"VisbilityButton";
  135. input.onclick=function(){toggleVisibility(sectionName,input.id)};
  136. cell.appendChild(input);
  137. config.document.getElementById(formName).appendChild(tb);
  138. let div=config.document.createElement('div');
  139. div.id=sectionName;
  140. div.style.display="none";
  141. config.document.getElementById(formName).appendChild(div);
  142. let divTable=config.document.createElement('div');
  143. divTable.id=sectionName+"Table";
  144. div.appendChild(divTable);
  145. if ("showFlag" in additionalData) {
  146. additionalData.divName=sectionName+"SubDiv";
  147. additionalData.divQueryName=sectionName+"SubDivList";
  148. let div1=config.document.createElement('div');
  149. div1.id=additionalData.divName;
  150. div1.style.display="none";
  151. div.appendChild(div1);
  152. let div2=config.document.createElement('div');
  153. div2.id=additionalData.divQueryName;
  154. div1.appendChild(div2);
  155. }
  156. if (debug) print("generate master table");
  157. let setup=fullAccessSetup(listName);
  158. let formStatus=config.formConfig.formStatus;
  159. if (formStatus=="Submitted")
  160. setup=readonlySetup();
  161. if (formStatus=="Approved")
  162. setup=readonlySetup();
  163. if ("isReview" in additionalData){
  164. generateReviewSection(listName,div.id,generateReviewSectionCB);
  165. return;
  166. }
  167. //master table is unique per visit
  168. setup.unique=true;
  169. generateTable(listName,divTable.id,true,additionalData,setup);
  170. if (debug) print("generate master table: done");
  171. let generateSubTable=true;
  172. if (formStatus=="Submitted")
  173. generateSubTable=false;
  174. if (formStatus=="Approved")
  175. generateSubTable=false;
  176. if (! ("showFlag" in additionalData) ) generateSubTable=false;
  177. if (generateSubTable){
  178. let qName=additionalData.queryName;
  179. let dName=additionalData.divName;
  180. let setup=fullAccessSetup(qName);
  181. //if (readonly) setup=readonlySetup(config);
  182. generateTable(qName,dName,false,additionalData,setup);
  183. }
  184. print("generate review");
  185. let divReviewList=config.document.createElement('div');
  186. divReviewList.id=sectionName+"ReviewList";
  187. div.appendChild(divReviewList);
  188. let divReview=config.document.createElement('div');
  189. divReview.id=sectionName+"Review";
  190. div.appendChild(divReview);
  191. //assume we already have listId (content of config.setupQueryName is listId)
  192. //we need listName also
  193. //qconfig.queryName=config.setupQueryName;
  194. generateReview(divReview.id,divReviewList.id,listName);
  195. }
  196. //>>>>reviewSection associated routines
  197. function parseResponseXML(){
  198. //print(config.config,'Status:' +this.status);
  199. print('Status:'+this.status);
  200. if (this.status!=200) return;
  201. config.loadFileConfig.json=JSON.parse(this.responseText);
  202. config.loadFileConfig.cb();
  203. }
  204. function loadFile(){
  205. print('YY: '+config.loadFileConfig.url);
  206. let connRequest=new XMLHttpRequest();
  207. connRequest.addEventListener("loadend",parseResponseXML);
  208. //function(e){parseResponseXML(e,config);});
  209. connRequest.open("GET", config.loadFileConfig.url);
  210. connRequest.send();
  211. }
  212. function getBasePath(){
  213. let server=LABKEY.ActionURL.getBaseURL();
  214. let basePath=server+"_webdav";
  215. basePath+=LABKEY.ActionURL.getContainer();
  216. return basePath;
  217. }
  218. function generateReviewSection(listName,id,callback){
  219. //callback should be generateReviewSectionCB and it takes no arguments
  220. print("generateReviewSection");
  221. //need base path
  222. config.loadFileConfig=new Object();
  223. config.loadFileConfig.cb=callback;
  224. config.loadFileConfig.id=id;
  225. config.loadFileConfig.url=getBasePath()+'/@files/reportSetup/'+listName+'.json';
  226. loadFile();
  227. //load file and continue in the next function
  228. }
  229. function generateErrorMessage(id,listName,msg){
  230. print('generateErrorMessage:');
  231. let eid=listName+"_errorMsg";
  232. let el=config.document.getElementById(eid);
  233. if (el===null){
  234. el=config.document.createElement("p");
  235. config.document.getElementById(id).appendChild(el);
  236. }
  237. el.innerHTML=msg;
  238. }
  239. function clearErrorMessage(listName){
  240. let eid=listName+"_errorMsg";
  241. let el=config.document.getElementById(eid);
  242. if (el===null) return;
  243. el.remove();
  244. }
  245. function getParticipantCode(pid){
  246. let selectRows=new Object();
  247. selectRows.schemaName='lists';
  248. selectRows.queryName='PET';
  249. pid.afterId=setParticipantCode;
  250. selectRows.success=function(data){afterRegistration(pid,data);}
  251. selectRows.filterArray=[LABKEY.Filter.create("crfRef",getCRFref())];
  252. LABKEY.Query.selectRows(selectRows);
  253. }
  254. function visitCodeFromVisitId(visitId){
  255. if (visitId<0) return "NONE";
  256. let project=LABKEY.ActionURL.getContainer();
  257. print('visitCodeFromVisitId: '+project.search('retro'));
  258. if (project.search('retro')>-1)
  259. visitId-=1;
  260. return 'VISIT_'+visitId.toString();
  261. }
  262. function replaceSlash(x){
  263. return x.replace(/\//,'_');
  264. }
  265. function setParticipantCode(pid){
  266. let rows=pid.registration.rows;
  267. if (rows.length==1){
  268. print('setParticipantCode: '+rows[0].participantCode+'/'+rows[0].visitId);
  269. let visitCode=visitCodeFromVisitId(rows[0].visitId);
  270. print('setParticipantCode: '+pid.participantId+'/'+visitCode);
  271. pid.participantCode=replaceSlash(pid.participantId);
  272. pid.visitCode=visitCode;
  273. }
  274. generateReviewSection2(pid);
  275. }
  276. function generateReviewSectionCB(){
  277. let listName=config.loadFileConfig.listName;
  278. let id=config.loadFileConfig.id;
  279. clearErrorMessage(listName);
  280. let pid=new Object();
  281. pid.participantCode="NONE";
  282. pid.visitCode="NONE";
  283. getParticipantCode(pid);
  284. print('Get participant code sent');
  285. //involves database search, continue after callback
  286. }
  287. function getValueFromElement(id,defaultValue){
  288. let e=config.document.getElementById(id);
  289. if (e!=null){
  290. defaultValue=e.innerHTML;
  291. }
  292. return defaultValue;
  293. }
  294. function pickParticipantCodeFromPage(){
  295. let pid=new Object();
  296. pid.participantCode=getValueFromElement("participantCode","NIX-LJU-D2002-IRAE-A000");
  297. pid.visitCode=getValueFromElement("visitCode","VISIT_1");
  298. generateReviewSection2(pid);
  299. }
  300. function patternReplace(src,replacements,values){
  301. for (rep in replacements){
  302. let txt1=src.replace(new RegExp(rep),values[replacements[rep]]);
  303. src=txt1;
  304. }
  305. return src;
  306. }
  307. function plotImage(row,rowVariable,obj,pid){
  308. let nim=obj.values.length;
  309. let baseDir=patternReplace(obj.imageDir,obj.replacements,pid);
  310. print('Base dir: '+pid.basePath);
  311. for (let im=0;im<nim;im++){
  312. pid[obj.variable]=obj.values[im];
  313. let img=config.document.createElement('img');
  314. let imgSrc=patternReplace(obj.file,obj.replacements,pid);
  315. print('Image: '+imgSrc);
  316. let imagePath=pid.basePath+'/'+baseDir+'/'+imgSrc;
  317. img.src=imagePath;
  318. img.width="300";
  319. let cell=row.insertCell();
  320. cell.id=pid[obj.variable]+"_"+rowVariable+pid[rowVariable];
  321. cell.appendChild(img);
  322. }
  323. }
  324. function showReport(row,rowVariable,obj,pid){
  325. let nr=obj.values.length;
  326. for (let ir=0;ir<nr;ir++){
  327. let cell=row.insertCell();
  328. cell.id=obj.values[ir]+"_"+rowVariable+pid[rowVariable];
  329. cell.width="300px";
  330. let reportConfig=new Object();
  331. reportConfig.partName="Report";
  332. reportConfig.renderTo=cell.id;
  333. //reportConfig.showFrame=false;
  334. //reportConfig.width="300";
  335. reportConfig.frame="none";
  336. reportConfig.partConfig=new Object();
  337. reportConfig.partConfig.width="300";
  338. reportConfig.partConfig.title="R Report";
  339. reportConfig.partConfig.reportName=obj.values[ir];
  340. for (f in obj.parameters){
  341. reportConfig.partConfig[f]=pid[f];
  342. }
  343. reportConfig.partConfig.showSection="myscatterplot";
  344. let reportWebPartRenderer = new LABKEY.WebPart(reportConfig);
  345. print('Render to: '+reportConfig.renderTo);
  346. reportWebPartRenderer.render();
  347. }
  348. }
  349. function showProbability(row,rowSetup,j,obj,pid){
  350. print('showProbability: '+rowSetup);
  351. let np=obj.values.length;
  352. let probDensity=new Object();
  353. probDensity.mean=rowSetup.mean[j];
  354. probDensity.sigma=rowSetup.sigma[j];
  355. print('showProbability: mean '+probDensity.mean+' sigma '+probDensity.sigma);
  356. for (let ip=0;ip<np;ip++){
  357. let cell=row.insertCell();
  358. probDensity.func=obj.values[ip];
  359. probDensity.organCode=pid.organCode;
  360. pid[obj.variable]=rowSetup[obj.variable][j];
  361. probDensity.percentile=pid.percentile;
  362. let selectRows=new Object();
  363. selectRows.queryName=obj.queryName;
  364. selectRows.schemaName="study";
  365. selectRows.filterArray=[];
  366. for (let f in obj.filters){
  367. selectRows.filterArray.push(LABKEY.Filter.create(f,pid[obj.filters[f]]));
  368. print('Filter ['+f+']: '+pid[obj.filters[f]]);
  369. }
  370. selectRows.success=function(data){drawProbability(data,cell,obj,pid,probDensity);}
  371. LABKEY.Query.selectRows(selectRows);
  372. }
  373. }
  374. function erf(x){
  375. let fx=[0,0.02,0.04,0.06,0.08,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,
  376. 1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,
  377. 2.1,2.2,2.3,2.4,2.5,3,3.5];
  378. let fy=[0,0.222702589,0.328626759,0.428392355,0.520499878,
  379. 0.603856091,0.677801194,0.742100965,0.796908212,
  380. 0.842700793,0.880205070,0.910313978,0.934007945,
  381. 0.952285120,0.966105146,0.976348383,
  382. 0.983790459,0.989090502,0.992790429,0.995322265,
  383. 0.997020533,0.998137154,0.998856823,0.999311486,
  384. 0.999593048,0.999977910,0.999999257];
  385. let n=32;
  386. let i0=n-1;
  387. for (let i=1;i<n;i++){
  388. if (Math.abs(x)>fx[i]) continue;
  389. i0=i-1;
  390. break;
  391. }
  392. let fval=1;
  393. if (i0<n-1){
  394. //interpolate
  395. let y1=fy[i0+1];
  396. let y0=fy[i0];
  397. let x1=fx[i0+1];
  398. let x0=fx[i0];
  399. fval=y0+(y1-y0)/(x1-x0)*(Math.abs(x)-x0);
  400. }
  401. print('Erf: '+fval);
  402. if (x<0) return -fval;
  403. return fval;
  404. }
  405. function drawProbability(data,cell,obj,pid,probDensity){
  406. print('drawProbability');
  407. if (data.rows.length!=1){
  408. print("drawProbability row length mismatch: "+data.rows.length);
  409. return;
  410. }
  411. //possible mismatch; I assume the dataset will have a field called value
  412. let val=data.rows[0].value;
  413. let prob=0;
  414. let fz=-100;
  415. if (probDensity.func=="gaus"){
  416. fz=(val-probDensity.mean)/probDensity.sigma/Math.sqrt(2);
  417. prob=0.5+0.5*erf(fz);
  418. }
  419. let color="red";
  420. let fzx=fz*Math.sqrt(2);
  421. print('drawProbability '+fzx);
  422. for (let i=1;i<obj.intervals.n;i++){
  423. if (fzx>obj.intervals.zlimits[i]) continue;
  424. color=obj.intervals.colors[i-1];
  425. break;
  426. }
  427. let fbox=config.document.createElement("div");
  428. fbox.style.backgroundColor=color;
  429. fbox.style.width="180px";
  430. fbox.style.height="180px";
  431. print('organCode '+probDensity.organCode);
  432. let organName="Lung";
  433. if (probDensity.organCode==4){
  434. organName="Thyroid";
  435. }
  436. if (probDensity.organCode==5){
  437. organName="Bowel";
  438. }
  439. let fp4=document.createElement("p");
  440. fp4.classList.add("center");
  441. fp4.style.textAlign="center";
  442. fp4.innerHTML=organName;
  443. fbox.appendChild(fp4);
  444. let fp=document.createElement("p");
  445. //fp.innerHTML=prob.toPrecision(3);
  446. fp.innerHTML=val.toPrecision(3);
  447. fp.style.fontSize="25px";
  448. fp.style.textAlign="center";
  449. fbox.appendChild(fp);
  450. let fp1=document.createElement("p");
  451. fp1.classList.add("center");
  452. fp1.style.textAlign="center";
  453. //fp1.innerHTML="IRAEMM-probability";
  454. fp1.innerHTML="SUV("+probDensity.percentile+"%)";
  455. fbox.appendChild(fp1);
  456. let fp2=document.createElement("p");
  457. fp2.innerHTML=fzx.toPrecision(3);
  458. fp2.style.fontSize="25px";
  459. fp2.style.textAlign="center";
  460. fbox.appendChild(fp2);
  461. let fp3=document.createElement("p");
  462. fp3.classList.add("center");
  463. fp3.style.textAlign="center";
  464. fp3.innerHTML="z-value";
  465. fbox.appendChild(fp3);
  466. cell.appendChild(fbox);
  467. }
  468. function generateReviewSection2(pid){
  469. let listName=config.loadFileConfig.listName;
  470. let id=config.loadFileConfig.id;
  471. print('generateReviewSection2: '+pid.participantCode+'/'+
  472. pid.visitCode);
  473. if (pid.participantCode=="NONE" || pid.visitCode=="NONE"){
  474. generateErrorMessage(id,listName,
  475. "ParticipantId/visitId not set");
  476. return;
  477. }
  478. print('JSON: '+config.loadFileConfig.json);
  479. let json=config.loadFileConfig.json;
  480. let nrows=json.rows.values.length;
  481. let ncol=json.columns.length;
  482. pid.basePath=getBasePath()+"/@files";
  483. let el=config.document.getElementById(id);
  484. let table=config.document.createElement('table');
  485. table.style.tableLayout="fixed";
  486. table.style.columnWidth="300px";
  487. el.appendChild(table);
  488. for (let i=0;i<nrows;i++){
  489. pid[json.rows.variable]=json.rows.values[i];
  490. //let organ=organs[i];
  491. let row=table.insertRow();
  492. for (let j=0;j<ncol;j++){
  493. let obj=json.columns[j];
  494. if (obj.display=="image")
  495. plotImage(row,json.rows.variable,obj,pid);
  496. if (obj.display=="report")
  497. showReport(row,json.rows.variable,obj,pid);
  498. if (obj.display=="probability"){
  499. showProbability(row,json.rows,i,obj,pid);
  500. }
  501. }
  502. }
  503. }
  504. ///>>>>>>>>>>>>>>end of reviewSection(REPORT)
  505. function generateReview(divReviewId,divReviewListId, listName){
  506. let listId=config.formConfig.fields[listName].queryId;
  507. let debug=true;
  508. if (debug) print("Generate review for: "+listId);
  509. let reviewSetup=new Object();
  510. reviewSetup.readonlyFlag=function(vName){
  511. if (vName=="queryName") return true;
  512. if (vName=="queryname") return true;
  513. if (vName=="ModifiedBy") return true;
  514. return false;};
  515. reviewSetup.addApply="Add Review";
  516. let generateTableFlag=true;
  517. let formStatus=config.formConfig.formStatus;
  518. if (formStatus == "Approved" ){
  519. delete reviewSetup.addApply;
  520. reviewSetup.readonlyFlag=function(vName){return false;}
  521. generateTableFlag=false;
  522. }
  523. reviewSetup.filters=new Object();
  524. reviewSetup.filters["crfRef"]=getCRFref();
  525. reviewSetup.filters["queryName"]=listId;//entry in reviewComments list is queryname, all in small caps
  526. //needs listName, in argument
  527. reviewSetup.getInputId=function(vName){return listName+"_add"+vName};
  528. reviewSetup.divReviewListId=divReviewListId;
  529. if (debug) {
  530. let msg="Review: divId: "+divReviewId;
  531. msg+=" inputId: "+reviewSetup.getInputId;
  532. print(msg);
  533. }
  534. updateListDisplay(divReviewListId,"reviewComments",reviewSetup.filters,true);
  535. if (! generateTableFlag) return;
  536. generateTable("reviewComments",divReviewId,false,new Object(),reviewSetup);
  537. }
  538. //>>>>>>>>>>trigger visibility of additional lists
  539. function setListVisibility(setup,additionalData,readonlyFlag){
  540. let debug=true;
  541. if (debug){
  542. print("Set list visibility ");
  543. for (f in additionalData){
  544. print("AdditionalData["+f+"]: "+additionalData[f]);
  545. }
  546. }
  547. let x = config.document.getElementById(additionalData.divName);
  548. if (debug) print("\n Div: "+x);
  549. x.style.display="none";
  550. let eId=setup.getInputId(additionalData.showFlag);
  551. let e = config.document.getElementById(eId);
  552. let sText;
  553. if (readonlyFlag) sText=e.innerHTML;
  554. else sText=e.options[e.selectedIndex].text;
  555. if (debug) print("\n Selected option text: "+sText);
  556. if (sText == additionalData.showFlagValue){
  557. let filters=new Object();
  558. if ("filters" in additionalData) filters=additionalData.filters;
  559. x.style.display = "block";
  560. updateListDisplay(additionalData.divQueryName,
  561. additionalData.queryName,filters,readonlyFlag);
  562. }
  563. }
  564. //>>have list refresh when data is added (not optimal yet)
  565. function updateListDisplay(divName,queryName,filters,readonlyFlag){
  566. let debug=true;
  567. if (debug)
  568. print("UpdateListDisplay: Query - "+queryName
  569. +" div - "+divName);
  570. if (divName=="NONE") return;
  571. let crfRef=getCRFref();
  572. let div=config.document.getElementById(divName);
  573. if (debug)
  574. print("Enabling display to queryName: "+queryName);
  575. var qconfig=new Object();
  576. qconfig.renderTo=divName;
  577. qconfig.containerPath=config.containerPath;
  578. qconfig.schemaName='lists';
  579. qconfig.queryName=queryName;
  580. qconfig.buttonBarPosition='top';
  581. qconfig.filters=[];
  582. for (f in filters){
  583. qconfig.filters.push(LABKEY.Filter.create(f, filters[f]));
  584. }
  585. qconfig.success=updateSuccess;
  586. qconfig.failure=updateFailure;
  587. //show only print button
  588. if (readonlyFlag){
  589. qconfig.buttonBar=new Object();
  590. qconfig.buttonBar.items=["print"];
  591. }
  592. LABKEY.QueryWebPart(qconfig);
  593. }
  594. function updateSuccess(data){
  595. print("Update success");
  596. }
  597. function updateFailure(data){
  598. print("Update failed");
  599. }
  600. //TODO: this should trigger a data refresh on section, ie populateData(field)
  601. function toggleVisibility(divName,buttonName){
  602. let x = config.document.getElementById(divName);
  603. if (x.style.display === "none") {
  604. x.style.display = "block";
  605. config.document.getElementById(buttonName).value="Hide";
  606. } else {
  607. x.style.display = "none";
  608. config.document.getElementById(buttonName).value="Show";
  609. }
  610. }
  611. function generateButtonBU(divName,title,buttonName,callback,
  612. callbackParameters){
  613. let debug=true;
  614. if (debug) print("generateButtonBU");
  615. let tb=config.document.createElement('table');
  616. tb.className="t2";
  617. let r1=tb.insertRow();
  618. th=config.document.createElement('th');
  619. r1.appendChild(th);
  620. th.innerHTML=title;
  621. //*!*
  622. let c2=r1.insertCell();
  623. let i1=config.document.createElement("input");
  624. i1.type="button";
  625. i1.value=buttonName;
  626. i1.style.fontSize="20px";
  627. i1.onclick=function(){callback(callbackParameters);}
  628. c2.appendChild(i1);
  629. let c1=r1.insertCell();
  630. c1.setAttribute("colspan","1");
  631. c1.id=callbackParameters.submitReportId;
  632. let el=config.document.getElementById(divName);
  633. if (debug) print("generateButton: element["+divName+"]: "+el);
  634. el.appendChild(tb);
  635. }
  636. function generateButton(divName,title,buttonName,callback){
  637. let debug=true;
  638. if (debug) print("generateButtonX");
  639. let tb=config.document.createElement('table');
  640. tb.className="t2";
  641. let r1=tb.insertRow();
  642. th=config.document.createElement('th');
  643. r1.appendChild(th);
  644. th.innerHTML=title;
  645. //*!*
  646. let c2=r1.insertCell();
  647. let i1=config.document.createElement("input");
  648. i1.type="button";
  649. i1.value=buttonName;
  650. i1.style.fontSize="20px";
  651. i1.onclick=callback;
  652. c2.appendChild(i1);
  653. let c1=r1.insertCell();
  654. c1.setAttribute("colspan","1");
  655. c1.id=config.submitReportId;
  656. let el=config.document.getElementById(divName);
  657. if (debug) print("generateButton: element["+divName+"]: "+el);
  658. el.appendChild(tb);
  659. }
  660. //>>populate fields
  661. //
  662. function addFieldRow(tb,entry,field,setup,additionalData){
  663. let vName=field.name;
  664. let vType=field.type;
  665. let isLookup=("lookup" in field);
  666. print("addFieldRow ["+vName+"/"+vType+'/'+isLookup+"]");
  667. let row=tb.insertRow();
  668. let cell=config.document.createElement('th');
  669. row.appendChild(cell);
  670. let text = config.document.createTextNode(field.shortCaption);
  671. cell.appendChild(text);
  672. cell=row.insertCell();
  673. let varValue="UNDEF";
  674. if (vName in setup.filters) varValue=setup.filters[vName];
  675. if (vName in entry) varValue=entry[vName];
  676. //date
  677. if (vType=="date"){
  678. if (varValue==="UNDEF") varValue=new Date();
  679. else varValue=new Date(varValue);
  680. }
  681. let input=null;
  682. cell.colSpan="3";
  683. let cellClass="input";
  684. let cellType="text";
  685. let readonlyFlag=setup.readonlyFlag(vName);
  686. if (isLookup){
  687. let lookup=field["lookup"];
  688. //get all values from config.formConfig.lookup[X]
  689. let lObject=config.formConfig.lookup[lookup.queryName];
  690. varValue=lObject.LUT[varValue];
  691. }
  692. //set the html input object
  693. while (1){
  694. if (readonlyFlag){
  695. input=config.document.createTextNode(varValue);
  696. break;
  697. }
  698. //lookup
  699. if (isLookup){
  700. input = config.document.createElement("select");
  701. break;
  702. }
  703. //date
  704. if (vType=="date"){
  705. input = config.document.createElement("input");
  706. input.type="date";
  707. break;
  708. }
  709. //string
  710. if (vType=="string"){
  711. //we have to make sure UNDEF is carried to below
  712. //since we are adapting file to either show
  713. //current file or allow user to select a file
  714. //
  715. //TODO change this so one can always select file
  716. //but also show the selected file
  717. if (varValue==null) varValue="UNDEF";
  718. else{
  719. if (varValue.length==0) varValue="UNDEF";
  720. }
  721. if(vName.search("reviewComment")>-1){
  722. input = config.document.createElement("textarea");
  723. input.cols="65";
  724. input.rows="5";
  725. break;
  726. }
  727. input=config.document.createElement('input');
  728. input.type="text";
  729. if (vName.search('_file_')<0) break;
  730. print('varValue: '+varValue);
  731. if (varValue!="UNDEF") break;
  732. input.type="file";
  733. break;
  734. }
  735. if (vType=="float"){
  736. input = config.document.createElement("input");
  737. input.type="text";
  738. break;
  739. }
  740. if (vType=="boolean"){
  741. input = config.document.createElement("input");
  742. input.type="checkbox";
  743. print("Creating checkbox");
  744. break;
  745. }
  746. break;
  747. }
  748. input.id=setup.getInputId(vName);
  749. cell.appendChild(input);
  750. if (varValue != "UNDEF"){
  751. if (vType=="string")
  752. input.value=varValue;
  753. if (vType=="float")
  754. input.value=varValue;
  755. if (vType=="date")
  756. input.valueAsDate=varValue;
  757. if (vType=="boolean")
  758. input.checked=varValue;
  759. }
  760. if (readonlyFlag) return;
  761. if (!isLookup) return;
  762. let lookup=field["lookup"];
  763. //get all values from config.formConfig.lookup[X]
  764. let lObject=config.formConfig.lookup[lookup.queryName];
  765. //debug
  766. print("Query: "+lookup.queryName);
  767. print("ElementId: "+input.id);
  768. print("No of options: " +lObject.LUT.length);
  769. print("Element: "+input);
  770. //set the lut value (input is text label) for readonly
  771. //clear existing fields from input
  772. for(let i = input.options.length; i >= 0; i--) {
  773. input.remove(i);
  774. }
  775. //create option -1
  776. let opt = config.document.createElement("option");
  777. opt.text = "<Select>";
  778. opt.value = -1;
  779. input.options[0] = opt;
  780. print( "Adding <Select>");
  781. //set value
  782. let fv=entry[vName];//can be undefined
  783. if (vName in setup.filters) fv=setup.filters[vName];
  784. input.selectedIndex=0;
  785. //add other, label them with LUT
  786. for (let v in lObject.LUT) {
  787. print('populating '+v+': '+lObject.LUT[v]);
  788. let opt = config.document.createElement("option");
  789. opt.text = lObject.LUT[v];
  790. opt.value = v;
  791. input.options[input.options.length] = opt;
  792. if (fv==undefined) continue;
  793. if (fv==v) input.selectedIndex=input.options.length-1;
  794. }
  795. //add watcher for showFlag field of additionalData
  796. if (!("showFlag" in additionalData))
  797. return;
  798. print("\n Parsing additional data ");
  799. let expId=setup.getInputId(additionalData.showFlag);
  800. if (expId!=input.id) {
  801. print("Skipping fields not flagged as showFlag:"+expId+"/"+input.id);
  802. return;
  803. }
  804. print("Setting onChange to "+input.id);
  805. input.onchange=function(){setListVisibility(setup,additionalData,readonlyFlag)};
  806. setListVisibility(setup,additionalData,readonlyFlag);
  807. }
  808. function generateTable(listName,divName,unique,additionalData,setup){
  809. let debug=true;
  810. if (debug) print("generateTable: "+listName);
  811. //assume data is set in config.formConfig.dataQueries[data.queryName].rows;
  812. let entry=new Object();
  813. //data snapshot
  814. let fQuery=config.formConfig.dataQueries[listName];
  815. //here I assume that listName was parsed during setDataLayout and setData
  816. //so that rows was set (even if they are empty)
  817. print("Nrows "+fQuery.rows.length);
  818. if (fQuery.rows.length>0)
  819. entry=fQuery.rows[0];
  820. let tb=config.document.createElement('table');
  821. tb.className="t2";
  822. config.document.getElementById(divName).appendChild(tb);
  823. //this are the fields (probably constant)
  824. let fields=fQuery.fields;
  825. for (f in fields){
  826. let field=fields[f];
  827. //each field is a new row
  828. print("Adding field: "+f+'/'+field.name);
  829. if (field.hidden) continue;
  830. if (field.name=="crfRef") continue;
  831. addFieldRow(tb,entry,field,setup,additionalData);
  832. }
  833. //add comment field
  834. if (!("addApply" in setup)) {
  835. print("populateTable: done");
  836. return;
  837. }
  838. let row=tb.insertRow();
  839. let th=config.document.createElement('th');
  840. row.appendChild(th);
  841. th.innerHTML=setup.addApply;
  842. let cell=row.insertCell();
  843. //cell.setAttribute("colspan","2");
  844. let input=config.document.createElement("input");
  845. input.type="button";
  846. input.value=setup.addApply;
  847. cell.appendChild(input);
  848. let cell1=row.insertCell();
  849. cell1.setAttribute("colspan","2");
  850. cell1.id=setup.getInputId("rerviewLastSave");
  851. cell1.innerHTML="No recent update";
  852. //saveReview is a generic name for saving content of the html page to a list entry
  853. input.onclick=function(){saveReview(data.queryName,cell1.id,setup)};
  854. }
  855. function checkVariable(el,varValue,displayText, parameters,varName,readonlyFlag){
  856. //set value with a little bit of QA
  857. //parameters should contain varName or this will return false
  858. //compares current varValue to value in parameters[varName], if they are equal,
  859. //sets the selectedIndex to last one
  860. let debug=true;
  861. if (!varName in parameters) return false;
  862. print("Comparing: " + varValue + "/" + parameters[varName]);
  863. if (varValue==parameters[varName]){
  864. if (readonlyFlag) el.innerHTML=displayText;
  865. else {
  866. //last one added?
  867. el.selectedIndex=el.options.length-1;
  868. print("Equal; "+el.selectedIndex);
  869. }
  870. return true;
  871. }
  872. return false;
  873. }
  874. function saveReview(queryName,elementId,setup){
  875. let debug=true;
  876. if (debug) print("saveReview: elementId "+elementId+" queryName "+queryName);
  877. let qconfig=new Object();
  878. qconfig.containerPath=config.containerPath;
  879. qconfig.schemaName='lists';//could be made more generic
  880. qconfig.queryName=queryName;
  881. if ("unique" in setup){
  882. qconfig.filterArray=[LABKEY.Filter.create("crfRef",getCRFref())];
  883. }
  884. qconfig.success=function(data){saveReviewToList(data,elementId,setup);}
  885. //to guess the fields, one must do selectRows first?
  886. LABKEY.Query.selectRows(qconfig);
  887. }
  888. function saveReviewToList(data,elementId,setup){
  889. let debug=true;
  890. if (debug) {
  891. let msg="saveReviewToList: "+" elementId "+elementId;
  892. msg+=" nrows "+data.rows.length;
  893. print(msg);
  894. }
  895. let useInsert=false;
  896. if (!("unique" in setup)) useInsert=true;
  897. if (data.rows.length==0) useInsert=true;
  898. let entry=new Object();
  899. if (!useInsert){
  900. entry=data.rows[0];
  901. }
  902. entry.crfRef=getCRFref();
  903. if (debug) print("Set crfRef="+entry.crfRef);
  904. if ("queryName" in setup.filters) {
  905. entry.queryName=setup.filters["queryName"];
  906. if (debug) print("Setting queryName: "+entry.queryName);
  907. }
  908. if ("queryname" in setup.filters) {
  909. entry.queryname=setup.filters["queryname"];
  910. if (debug) print("Setting queryname: "+entry.queryname);
  911. }
  912. let fields=data.metaData.fields;
  913. for (f in fields){
  914. let field=fields[f];
  915. if (debug) print("saveReview field: "+field.name);
  916. if (field.hidden) continue;
  917. let vName=fields[f].name;
  918. if (vName=="crfRef") continue;
  919. if (vName=="queryName") continue;
  920. if (vName=="queryname") continue;
  921. let eId=setup.getInputId(vName);
  922. let el=config.document.getElementById(eId);
  923. if (!el) {
  924. if (debug) print("saveReview element: "+eId+" not found");
  925. continue;
  926. }
  927. if (debug) print("saveReview element: "+eId);
  928. let vType=fields[f].type;
  929. if (debug) print("vType: "+vType);
  930. if ("lookup" in fields[f]){
  931. if (el.nodeName==="SELECT"){
  932. entry[vName]=el.options[el.selectedIndex].value;
  933. }
  934. if (el.nodeName==="TD"){
  935. entry[vName]=el.innerHTML;
  936. }
  937. print("Setting lookup to "+entry[vName]);
  938. continue;
  939. }
  940. if (vType=="date"){
  941. var date=el.valueAsDate;
  942. if (date==="null") continue;
  943. date.setUTCHours(12);
  944. entry[vName]=date.toString();
  945. print("Setting date to "+entry[vName]);
  946. }
  947. if (vType=="string"){
  948. entry[vName]=el.value;
  949. let idx=field.name.search('_file_');
  950. let lg=el.value.length;
  951. if (idx>-1 && lg>0){
  952. print('Attachment field: '+el.value);
  953. //entry[vName]=el.files[0].stream();
  954. let ctx=new Object();
  955. ctx['dirName']='consent';
  956. ctx['ID']=entry['crfRef'];
  957. ctx['project']=config.containerPath;
  958. //need ID->crf!
  959. //assume crfRef will get set before this
  960. //element is encountered
  961. uploadFile(el,ctx);
  962. let suf=entry[vName].split('.').pop();
  963. entry[vName]=entry['crfRef']+'.'+suf;
  964. }
  965. }
  966. if (vType=="float"){
  967. entry[vName]=el.value;
  968. }
  969. if (vType=="boolean"){
  970. entry[vName]=el.checked;
  971. }
  972. }
  973. let qconfig=new Object();
  974. qconfig.rows=[entry];
  975. qconfig.containerPath=config.containerPath;
  976. qconfig.schemaName=data.schemaName;
  977. qconfig.queryName=data.queryName;
  978. //only update comments
  979. print("modifyRows: useInsert "+useInsert);
  980. qconfig.success=function(data){updateLastSavedFlag(data,setup,elementId)};
  981. if (!useInsert){
  982. LABKEY.Query.updateRows(qconfig);
  983. }
  984. else{
  985. LABKEY.Query.insertRows(qconfig);
  986. }
  987. }
  988. function updateLastSavedFlag(data,setup,elementId){
  989. let debug=true;
  990. if (debug) print("Update last saved flag to "+elementId);
  991. let el=config.document.getElementById(elementId);
  992. let dt=new Date();
  993. el.innerHTML="Last saved "+dt.toString();
  994. if (data.queryName=="reviewComments"){
  995. updateListDisplay(setup.divReviewListId,"reviewComments",setup.filters,true);
  996. }
  997. }
  998. //******************************************upload to database *********************
  999. function onDatabaseUpload(){
  1000. print("Database upload");
  1001. configUpload=new Object();
  1002. //figure out the participantId
  1003. let qconfig=new Object();
  1004. qconfig.schemaName="lists";
  1005. qconfig.queryName="Forms";
  1006. qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)];
  1007. //qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)]
  1008. qconfig.success=function(data){afterForms(configUpload,data)};
  1009. LABKEY.Query.selectRows(qconfig);
  1010. }
  1011. function afterForms(configUpload,data){
  1012. let formEntry=data.rows[0];
  1013. configUpload.registrationQueryId=formEntry["masterQuery"];
  1014. configUpload.afterId=afterParticipantId;
  1015. let qconfig=new Object();
  1016. qconfig.queryName=config.queryMap[configUpload.registrationQueryId];
  1017. //queryMap holds mapping for queries in visit;
  1018. //masterQuery should be one of them, so this is safe.
  1019. qconfig.schemaName='lists';
  1020. qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref())];
  1021. qconfig.success=function(data){afterRegistration(configUpload,data);}
  1022. LABKEY.Query.selectRows(qconfig);
  1023. //waitForCompleteUpload(config);//
  1024. }
  1025. function afterRegistration(configUpload,data){
  1026. print("afterRegistration: rows:"+data.rows.length);
  1027. configUpload.registration=data;
  1028. let registrationData=configUpload.registration;
  1029. clearErr();
  1030. if (registrationData.rows.length!=1){
  1031. let msg="ERROR: Found "+registrationData.rows.length;
  1032. msg+=" registration entries for crfrefid "+getCRFref();
  1033. print(msg);
  1034. configUpload.afterId(configUpload);
  1035. return;
  1036. }
  1037. print('registration participant field: '+config.registrationParticipantIdField);
  1038. configUpload.participantId=
  1039. registrationData.rows[0][config.registrationParticipantIdField];
  1040. //could be a lookup field
  1041. let fields=registrationData.metaData.fields;
  1042. let field="NONE";
  1043. for (f in fields){
  1044. if (fields[f]["name"]==config.registrationParticipantIdField)
  1045. field=fields[f];
  1046. }
  1047. if ("lookup" in field){
  1048. let pid=configUpload.participantId;
  1049. print("Using lookup for participantId: "+pid);
  1050. let lookup=field["lookup"];
  1051. print("Lookup: ["+lookup.schemaName+','+lookup.queryName+']');
  1052. let qconfig=new Object();
  1053. qconfig.containerPath=config.containerPath;
  1054. qconfig.schemaName=lookup.schemaName;
  1055. qconfig.queryName=lookup.queryName;
  1056. qconfig.filterArray=
  1057. [LABKEY.Filter.create(lookup.keyColumn,pid)];
  1058. qconfig.success=function(data){
  1059. afterRegistrationLookup(configUpload,data,lookup.displayColumn)};
  1060. LABKEY.Query.selectRows(qconfig);
  1061. }
  1062. else{
  1063. //afterParticipantId(configUpload);
  1064. configUpload.afterId(configUpload);
  1065. }
  1066. }
  1067. function afterRegistrationLookup(configUpload,data,displayColumn){
  1068. print("afterRegistrationLookup");
  1069. let entry=data.rows[0];
  1070. configUpload.participantId=entry[displayColumn];
  1071. print('Setting to '+configUpload.participantId);
  1072. configUpload.afterId(configUpload);
  1073. //afterParticipantId(configUpload);
  1074. }
  1075. function afterParticipantId(configUpload){
  1076. print("Setting participantId to "+configUpload.participantId);
  1077. //another select rows to update all queries from setup
  1078. //just use registration for test
  1079. let qconfig=new Object();
  1080. qconfig.schemaName='lists';
  1081. //qconfig.queryName=config.setupQueryName;
  1082. qconfig.queryName="FormSetup";
  1083. qconfig.filterArray=[LABKEY.Filter.create("formName",config.formId)];
  1084. qconfig.success=function(data){afterSetup(configUpload,data);}
  1085. LABKEY.Query.selectRows(qconfig);
  1086. }
  1087. function afterSetup(configUpload,data){
  1088. configUpload.queries=new Array();
  1089. for (let i=0;i<data.rows.length;i++){
  1090. let entry=data.rows[i];
  1091. //skip reviews
  1092. if (entry.showFlag=="REVIEW") continue;
  1093. //use lookup table to convert from id to name
  1094. let queryName=config.queryMap[entry.queryName];
  1095. configUpload.queries.push({queryName:queryName,queryStatus:"QUEUED"});
  1096. if (entry.showQuery=="NONE")
  1097. continue
  1098. configUpload.queries.push({queryName:entry.showQuery,queryStatus:"QUEUED"});
  1099. }
  1100. //add reviews
  1101. configUpload.queries.push({queryName:"reviewComments",queryStatus:"QUEUED"});
  1102. configUpload.queryId=0;
  1103. copyToDataset(configUpload);
  1104. //config.upload["start"]=true;
  1105. //for (u in config.upload){
  1106. // if (u=="start") continue;
  1107. // if (u=="count") continue;
  1108. // copyToDataset(config,u,participantId);
  1109. //}
  1110. //
  1111. //print("Database updated");
  1112. }
  1113. function copyToDataset(configUpload){
  1114. if (configUpload.queryId==configUpload.queries.length) {
  1115. updateFlag(3);//Approved
  1116. return;
  1117. }
  1118. let queryName=configUpload.queries[configUpload.queryId].queryName;
  1119. print("copyToDataset["+configUpload.queryId+"/"+configUpload.queries.length+"]: "+queryName);
  1120. let qconfig=new Object();
  1121. qconfig.queryName=queryName;
  1122. qconfig.schemaName="lists";
  1123. qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref())];
  1124. qconfig.success=function(data){afterListData(configUpload,data)};
  1125. LABKEY.Query.selectRows(qconfig);
  1126. }
  1127. function afterListData(configUpload,data){
  1128. let queryName=configUpload.queries[configUpload.queryId].queryName;
  1129. let msg="["+queryName+"/list]: "+data.rows.length+" entries";
  1130. print(msg);
  1131. configUpload.queries[configUpload.queryId].listData=data;
  1132. let qconfig=new Object();
  1133. qconfig.queryName=queryName;
  1134. qconfig.schemaName="study";
  1135. qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref())];
  1136. qconfig.filterArray.push(LABKEY.Filter.create('ParticipantId',configUpload.participantId));
  1137. qconfig.success=function(data){afterStudyData(configUpload,data)};
  1138. LABKEY.Query.selectRows(qconfig);
  1139. }
  1140. function afterStudyData(configUpload,data){
  1141. configUpload.queries[configUpload.queryId].studyData=data;
  1142. let queryName=configUpload.queries[configUpload.queryId].queryName;
  1143. let msg="["+queryName+"/study]: "+data.rows.length+" entries";
  1144. print(msg);
  1145. let listRows=configUpload.queries[configUpload.queryId].listData.rows;
  1146. //skip uploading an empty set
  1147. if (listRows.length==0){
  1148. printErr("List "+queryName+" empty.");
  1149. configUpload.queries[configUpload.queryId].queryStatus="DONE";
  1150. configUpload.queryId+=1;
  1151. copyToDataset(configUpload);
  1152. return;
  1153. }
  1154. let studyRows=configUpload.queries[configUpload.queryId].studyData.rows;
  1155. for (let i=0;i<studyRows.length;i++){
  1156. let entry=studyRows[i];
  1157. if (! (i<listRows.length) ) continue;
  1158. let entryList=listRows[i];
  1159. //keeps study only variables (ParticipantId, SequenceNum)
  1160. for (let f in entryList) {
  1161. entry[f]=entryList[f];
  1162. print("Copying ["+f+"]: "+entry[f]+"/"+entryList[f]);
  1163. }
  1164. }
  1165. if (studyRows.length>0) {
  1166. let qconfig=new Object();
  1167. qconfig.queryName=queryName;
  1168. qconfig.schemaName="study";
  1169. qconfig.rows=studyRows;
  1170. qconfig.success=function(data){afterStudyUpload(configUpload,data);}
  1171. LABKEY.Query.updateRows(qconfig)
  1172. }
  1173. else{
  1174. let data=new Object();
  1175. data.rows=new Array();
  1176. afterStudyUpload(configUpload,data);
  1177. }
  1178. }
  1179. function afterStudyUpload(configUpload,data){
  1180. //let participantField=config.participantField;
  1181. let participantField=config.formConfig.studyData["SubjectColumnName"];
  1182. let dataObject=configUpload.queries[configUpload.queryId];
  1183. let queryName=dataObject.queryName;
  1184. printErr("Updated "+data.rows.length+" rows to "+queryName);
  1185. let studyRows=dataObject.studyData.rows;
  1186. let listRows=dataObject.listData.rows;
  1187. let rows=new Array();
  1188. //also updating existing rows, if they exist
  1189. for (let i=studyRows.length;i<listRows.length;i++){
  1190. let entry=listRows[i];
  1191. //make sure you have the participantField right
  1192. //
  1193. entry[participantField]=configUpload.participantId;
  1194. entry.crfRef=getCRFref();
  1195. entry.SequenceNum=getCRFref();
  1196. entry.SequenceNum=entry.SequenceNum % 1000000000;
  1197. if (listRows.length>1){
  1198. entry.SequenceNum+=i/100;
  1199. }
  1200. print( "Adding sequence number "+entry.SequenceNum);
  1201. rows.push(entry);
  1202. }
  1203. if (rows.length>0){
  1204. let qconfig=new Object();
  1205. qconfig.queryName=queryName;
  1206. qconfig.schemaName="study";
  1207. qconfig.success=function(data){
  1208. afterListUpload(configUpload,data)};
  1209. qconfig.rows=rows;
  1210. LABKEY.Query.insertRows(qconfig);
  1211. }
  1212. else{
  1213. let data=new Object();
  1214. data.rows=rows;
  1215. afterListUpload(configUpload,data);
  1216. }
  1217. }
  1218. function afterListUpload(configUpload,data){
  1219. let queryName=configUpload.queries[configUpload.queryId].queryName;
  1220. printErr("Inserted "+data.rows.length+" rows to "+queryName);
  1221. configUpload.queries[configUpload.queryId].queryStatus="DONE";
  1222. configUpload.queryId+=1;
  1223. copyToDataset(configUpload);
  1224. }
  1225. //*************************update for further review *************************
  1226. function onUpdateForReview(){
  1227. updateFlag(4);//Pending review
  1228. }
  1229. function updateFlag(flag){
  1230. if (flag==4)
  1231. print("Sent to further review");
  1232. let qconfig=new Object();
  1233. qconfig.schemaName='lists';
  1234. qconfig.queryName='crfEntry';
  1235. qconfig.success=function(data){setFlag(data,flag);}
  1236. qconfig.filterArray=[LABKEY.Filter.create("entryId",getCRFref())];
  1237. LABKEY.Query.selectRows(qconfig);
  1238. }
  1239. function setFlag(data,flag){
  1240. let debug=true;
  1241. if (flag==4) print("setFlagToReview");
  1242. if (data.rows.length!=1){
  1243. let msg="ERROR: Found "+data.rows.length;
  1244. msg+=" entries for crfrefid "+getCRFref();
  1245. print(msg);
  1246. return;
  1247. }
  1248. let entry=data.rows[0];
  1249. entry.FormStatus=flag;//Pending Review
  1250. if (debug)
  1251. print("Set form status to "+entry.FormStatus);
  1252. let qconfig=new Object();
  1253. qconfig.schemaName='lists';
  1254. qconfig.queryName='crfEntry';
  1255. qconfig.rows=[entry];
  1256. qconfig.success=function(data){completeWithFlag(data,flag);}
  1257. LABKEY.Query.updateRows(qconfig);
  1258. }
  1259. function completeWithFlag(data,flag){
  1260. let debug=true;
  1261. if (debug){
  1262. if (flag==4) print("complete with review");
  1263. }
  1264. redirect();
  1265. }
  1266. //************************************************ submit *******************************************
  1267. function onSubmit(){
  1268. let debug=true;
  1269. hideErr();
  1270. clearErr();
  1271. printErr("onSubmit");
  1272. //the idea of check form is to provide a methodology for form validation
  1273. //it consists of two parts - a routine that initiates the check, and a
  1274. //polling watchdog that waits for its execution.
  1275. //Non-linear approach follows the natural javascript pathway.
  1276. checkForm();
  1277. //watchdog with callback that gets executed when checkForm is done
  1278. let cb=new Object();
  1279. cb.success=finalValidation;
  1280. cb.failure=function(config){printErr("waitForCheckForm failed")};
  1281. waitForCheckForm(cb);
  1282. }
  1283. function finalValidation(){
  1284. let debug=true;
  1285. if (debug) print("validate");
  1286. let completed=true;
  1287. for (f in config.fields){
  1288. let field=config.fields[f];
  1289. if (field.status!="DONE") {
  1290. printErr("Missing entry for "+field.title);
  1291. completed=false;
  1292. }
  1293. }
  1294. if (debug) print("valid: "+completed);
  1295. if (completed){
  1296. modifyCRFEntry();
  1297. }
  1298. else{
  1299. let el=document.getElementById(config.submitReportId);
  1300. el.innerHTML="Form invalid";
  1301. }
  1302. }
  1303. function modifyCRFEntry(){
  1304. printErr("Form valid");
  1305. let c1=new Object();
  1306. c1.schemaName='lists';
  1307. c1.queryName='crfEntry';
  1308. c1.filterArray=[LABKEY.Filter.create('entryId',getCRFref())];
  1309. c1.success=changeCRFStatus;
  1310. LABKEY.Query.selectRows(c1);
  1311. }
  1312. function changeCRFStatus(data){
  1313. let entry=data.rows[0];
  1314. entry.formStatus=2;//Submitted
  1315. let el=document.getElementById(config.submitReportId);
  1316. el.innerHTML="Submitting form";
  1317. let c1=new Object();
  1318. c1.schemaName=data.schemaName;
  1319. c1.queryName=data.queryName;
  1320. c1.containerPath=config.containerPath;
  1321. c1.rows=[entry];
  1322. //close window upon success
  1323. c1.success=sendEmail;
  1324. LABKEY.Query.updateRows(c1);
  1325. }
  1326. function sendEmail(data){
  1327. print('send email'+data.rows.length);
  1328. let crf=data.rows[0]['entryId'];
  1329. let formId=data.rows[0]['Form'];
  1330. let link=LABKEY.ActionURL.getBaseURL();
  1331. link+=LABKEY.ActionURL.getContainer();
  1332. link+='/crf-visit.view?';
  1333. link+='entryId='+crf;
  1334. link+='&formId='+formId;
  1335. //debug
  1336. let recipients=new Array();
  1337. let typeTo=LABKEY.Message.recipientType.to;
  1338. //from crfManagers list
  1339. let as=LABKEY.Message.createRecipient(typeTo,'andrej.studen@ijs.si');
  1340. recipients.push(as);
  1341. let typeHtml=LABKEY.Message.msgType.html;
  1342. let typePlain=LABKEY.Message.msgType.plain;
  1343. let msg=LABKEY.Message.createMsgContent(typeHtml,'<h2>Test</h2>');
  1344. let msg1=LABKEY.Message.createMsgContent(typePlain,link);
  1345. LABKEY.Message.sendMessage({
  1346. msgFrom:'labkey@fmf.uni-lj.si',
  1347. msgSubject:'Form submitted',
  1348. msgRecipients:recipients,
  1349. msgContent:[msg1],
  1350. success: redirect
  1351. });
  1352. }
  1353. function hideErr(){
  1354. let el=config.document.getElementById("errorDiv");
  1355. el.style.display="none";
  1356. }
  1357. function clearErr(){
  1358. let el=config.document.getElementById("errorTxt");
  1359. el.value="";
  1360. }
  1361. function showErr(){
  1362. let el=config.document.getElementById("errorDiv");
  1363. el.style.display="block";
  1364. }
  1365. function printErr(msg){
  1366. showErr();
  1367. el=config.document.getElementById("errorTxt");
  1368. el.style.color="red";
  1369. el.value+="\n"+msg;
  1370. }
  1371. //validation worker
  1372. function checkForm(){
  1373. let debug=true;
  1374. let crfRef=getCRFref()
  1375. config.status="UNKNOWN";
  1376. //fields are queries that are part of the form
  1377. //this allows validation to be performed on all
  1378. //queries that are part of the form
  1379. for (f in config.formConfig.fields){
  1380. let field=config.formConfig.fields[f];
  1381. field.status="UNKNOWN";
  1382. if (debug)
  1383. print("Setting status for "+f+" to "+ field.status);
  1384. let selectRows=new Object();
  1385. selectRows.containerPath=config.containerPath;
  1386. selectRows.schemaName="lists";
  1387. selectRows.queryName=f;
  1388. //select only entry related to present form
  1389. selectRows.filterArray=[LABKEY.Filter.create('crfRef',crfRef)];
  1390. selectRows.success=checkData;
  1391. selectRows.failure=function(errorObj){print("checkData failed.")};
  1392. LABKEY.Query.selectRows(selectRows);
  1393. }
  1394. }
  1395. function waitForCheckForm(cb){
  1396. //watchdog - a timeout function
  1397. //if control reaches the end of the function, it gets re-executed after the timeout
  1398. //the body provides two alternative routes that end with return,
  1399. //one for failure (if timeout gets executed too often)
  1400. //one for success, which looks at the config object, which is in parallel
  1401. //modified by the checkForm and as a daughter process, checkData functions
  1402. let debug=true;
  1403. //initalize counter
  1404. if (!("i" in config)) config.i=0;
  1405. //report execution depth
  1406. if (debug) print("["+config.i+"] checkForm status "+config.status);
  1407. //failure route
  1408. if (config.i>100) {
  1409. if (debug) print("executing failure");
  1410. cb.failure(config);
  1411. return;
  1412. }
  1413. //success route; count on other functions to modify config.status
  1414. //this is very non-obviuos for linear programing, but
  1415. //checkData and waitForCheckForm share the same memory space
  1416. if (config.status=="DONE") {
  1417. if (debug) print("executing success");
  1418. cb.success(config);
  1419. if (debug) print("success executed");
  1420. return;
  1421. }
  1422. //this is the repeat route, neither pathway was indicated, stay in loop
  1423. config.i+=1;
  1424. setTimeout(function(){waitForCheckForm(cb);},1000);
  1425. }
  1426. function checkData(data){
  1427. //insulated worker. Data corresponds to entry of a particular query
  1428. let debug=true;
  1429. if (debug) print("checkData ");
  1430. let field=config.formConfig.fields[data.queryName];
  1431. field.status="NONE";
  1432. if (debug) print("Setting status for "+data.queryName+" to "+
  1433. field.status);
  1434. //this is the only test implemented - we should have at least one row in each of the queries
  1435. //we could also have a more targeted functions, but their form escapes me at the moment
  1436. if (data.rows.length>0)
  1437. field.status="DONE";
  1438. if (debug)
  1439. print("checkData set status for "+data.queryName+" to "+field.status);
  1440. //here we generate the flag/bell for the watchdog
  1441. for (f in config.fields){
  1442. let subField=config.fields[f];
  1443. if (debug)
  1444. print("checkData status["+f+"]: "+subField.status);
  1445. //UNKNOW means we haven't visited it yet
  1446. if (subField.status=="UNKNOWN") {
  1447. if (debug) print("\t Status for "+f+" not set ["+ subField.status+"]");
  1448. //we opt out - clearly, unchecked fields are still present
  1449. return;
  1450. }
  1451. }
  1452. //since we didn't exit for any field, we are done, and can set watchdog flag
  1453. config.status="DONE";
  1454. }
  1455. //**************************************************
  1456. //
  1457. function onRemoveCRF(){
  1458. let debug=true;
  1459. if (debug){
  1460. print("Removing CRF");
  1461. }
  1462. let selectRows=new Object();
  1463. selectRows.containerPath=config.containerPath;
  1464. selectRows.schemaName="lists";
  1465. selectRows.queryName="inputLists";
  1466. selectRows.success=afterInputLists;
  1467. LABKEY.Query.selectRows(selectRows);
  1468. }
  1469. function afterInputLists(data){
  1470. let debug=true;
  1471. if (debug)
  1472. print("After input lists");
  1473. config.inputLists=data;
  1474. config.inputListsIterator=0;
  1475. removeCRFLoop();
  1476. }
  1477. function removeCRFLoop(){
  1478. let debug=true;
  1479. let i=config.inputListsIterator;
  1480. if (debug)
  1481. print("removeCRFLoop ["+i+"/"+config.inputLists.rows.length+"]");
  1482. if (i>config.inputLists.rows.length){
  1483. if (0) return;
  1484. redirect();
  1485. }
  1486. let queryName="crfEntry";
  1487. let idVar="entryId";
  1488. if (i<config.inputLists.rows.length){
  1489. queryName=config.inputLists.rows[i].queryName;
  1490. idVar="crfRef";
  1491. }
  1492. if (debug)
  1493. print("["+i+"/"+config.inputLists.rows.length+"] "+queryName+"/"+idVar);
  1494. let selectRows=new Object();
  1495. selectRows.containerPath=config.containerPath;
  1496. selectRows.schemaName="lists";
  1497. selectRows.queryName=queryName;
  1498. selectRows.filterArray=[LABKEY.Filter.create(idVar,getCRFref())];
  1499. selectRows.success=removeListCRF;
  1500. selectRows.failure=skipListCRF;
  1501. LABKEY.Query.selectRows(selectRows);
  1502. }
  1503. function removeListCRF(data){
  1504. let debug=true;
  1505. if (debug)
  1506. print(data.queryName+": "+data.rows.length);
  1507. config.inputListsIterator+=1;
  1508. if (data.rows.length==0){
  1509. removeCRFLoop();
  1510. return;
  1511. }
  1512. let deleteRows=new Object();
  1513. deleteRows.containerPath=config.containerPath;
  1514. deleteRows.schemaName=data.schemaName;
  1515. deleteRows.queryName=data.queryName;
  1516. deleteRows.success=function(data){removeCRFLoop()};
  1517. deleteRows.rows=data.rows;
  1518. LABKEY.Query.deleteRows(deleteRows);
  1519. }
  1520. function skipListCRF(errorInfo){
  1521. let debug=true;
  1522. if (debug)
  1523. print("Error in removeCRF: "+errorInfo.exception);
  1524. config.inputListsIterator+=1;
  1525. removeCRFLoop();
  1526. }
  1527. function redirect(){
  1528. let formUrl="begin";
  1529. let params=new Object();
  1530. params.name=formUrl;
  1531. params.pageId="CRF";
  1532. let containerPath= config.containerPath;
  1533. // This changes the page after building the URL.
  1534. //Note that the wiki page destination name is set in params.
  1535. var homeURL = LABKEY.ActionURL.buildURL("project", formUrl , containerPath, params);
  1536. print("Redirecting to "+homeURL);
  1537. window.location = homeURL;
  1538. }
  1539. function checkBlob(){
  1540. print("checkBlob: "+config.blob);
  1541. if (config.blob) {
  1542. clearInterval(config.blobInterval);
  1543. config.a.href = config.window.URL.createObjectURL(config.blob);
  1544. print("HREF: "+config.a.href);
  1545. config.a.download = 'test.pdf';
  1546. config.a.click();
  1547. config.window.URL.revokeObjectURL(config.a.href);
  1548. }
  1549. config.count=config.count+1;
  1550. print("Eval: "+config.count);
  1551. if (config.count>100){
  1552. clearInterval(config.blobInterval);
  1553. }
  1554. }
  1555. function printForm(){
  1556. config.doc=new PDFDocument();
  1557. //config.doc.end();
  1558. let stream = config.doc.pipe(blobStream()).on("finish",function(){
  1559. config.blob=stream.toBlob("application/pdf");});
  1560. print("BLob: "+config.blob);
  1561. config.a = config.document.createElement("a");
  1562. config.document.body.appendChild(config.a);
  1563. config.a.innerHTML="Download PDF";
  1564. config.a.style = "display: none";
  1565. config.count=0;
  1566. //run until blob is set
  1567. config.blobInterval=setInterval(checkBlob,1000);
  1568. //pick data from crfForm list
  1569. print("Printing form");
  1570. printHeader();
  1571. setData(formatPrintData);
  1572. }
  1573. function printHeader(){
  1574. config.doc.fontSize(25).text(config.formConfig.form['formName']);
  1575. config.doc.moveDown();
  1576. let crfEntry=config.formConfig.crfEntry;
  1577. let site=config.formConfig.site;
  1578. let val=new Object();
  1579. let user=config.formConfig.user;
  1580. val['A']={o:crfEntry,f:'EudraCTNumber',t:'Eudra CT Number'};
  1581. val['B']={o:crfEntry,f:'StudyCoordinator',t:'Study Coordinator'};
  1582. val['C']={o:crfEntry,f:'StudySponsor',t:'Study Sponsor'};
  1583. val['D']={o:site,f:'siteName',t:'Site'};
  1584. val['E']={o:site,f:'sitePhone',t:'Phone'};
  1585. val['F']={o:user,f:'DisplayName',t:'Investigator'};
  1586. for (let f in val){
  1587. print('Printing for '+f);
  1588. let e=val[f];
  1589. let entry=new Object();
  1590. entry[f]=e.o[e.f];
  1591. printPDF(entry,
  1592. {name:f,caption:e.t,type:'string'},null);
  1593. }
  1594. config.doc.moveDown();
  1595. }
  1596. function formatPrintData(){
  1597. qS=config.formConfig.dataQueries;
  1598. for (let q in qS){
  1599. print('Setting up '+q);
  1600. let qData=qS[q];
  1601. print('Number of rows: '+qData.rows.length);
  1602. if (qData.rows.length>0){
  1603. config.doc.fontSize(20).text(qData.title);
  1604. }
  1605. for (let i=0;i<qData.rows.length;i++){
  1606. let entry=qData.rows[i];
  1607. for (let f in qData.fields){
  1608. let field=qData.fields[f];
  1609. let lookup=null;
  1610. if (field.lookup){
  1611. lookup=config.formConfig.lookup[field.lookup.queryName];
  1612. }
  1613. if (field.hidden) continue;
  1614. printPDF(entry,field,lookup);
  1615. }
  1616. }
  1617. config.doc.moveDown();
  1618. }
  1619. print("All done");
  1620. config.doc.end();
  1621. }
  1622. function printPDF(entry,field,lookup){
  1623. //object field should have a name, type, caption
  1624. //entry should have field.name
  1625. //lookup is null or has a lookup table LUT
  1626. //for value v of entry[field.name]
  1627. //
  1628. //the total width of a A4 page is 598 px,
  1629. //left margin is 72. With a right margin of 50,
  1630. //the total available with is 476 px.
  1631. let w=476;
  1632. let spacing=25;
  1633. let w1=(w-spacing)*0.5;
  1634. let fontSize=14;
  1635. print('printPDF: entry['+field.name+']='+entry[field.name]);
  1636. let v=entry[field.name];
  1637. if (lookup!=null){
  1638. v=lookup.LUT[v];
  1639. }
  1640. print('printPDF: field type:'+field.type);
  1641. if (field.type=="date"){
  1642. let d=new Date(v);
  1643. v=d.getDate()+'/'+(d.getMonth()+1)+'/'+d.getFullYear();
  1644. }
  1645. if (v===null) v=' / ';
  1646. if (v===undefined) v=' / ';
  1647. //measure text
  1648. let label=field.caption;
  1649. let opt={width:w1};
  1650. config.doc.fontSize(fontSize);
  1651. //for more eloquent display the height of the text
  1652. //can be measured prior to output
  1653. //use currentLineHeight to scale height
  1654. //let lineH=config.doc.currentLineHeight(1);
  1655. //let h=config.doc.heightOfString(label,opt)/lineH;
  1656. //print label
  1657. config.doc.font('Courier').text(label,opt);
  1658. //align last row of description w/ first row of value
  1659. config.doc.moveUp();
  1660. //store x value for later use
  1661. let tx=config.doc.x;
  1662. let ty=config.doc.y;
  1663. //shift for value output
  1664. config.doc.x+=w1+spacing;
  1665. config.doc.font('Courier-Bold').text(v,opt);
  1666. //restore x value
  1667. config.doc.x=tx;
  1668. }
  1669. function generateMasterForm(){
  1670. generateDebugSection();
  1671. setFormConfig();
  1672. }
  1673. function populateBasicData(){
  1674. config.document.getElementById('eudraCTNumber').innerHTML=
  1675. config.formConfig.crfEntry.EudraCTNumber;
  1676. config.document.getElementById('studyCoordinator').innerHTML=
  1677. config.formConfig.crfEntry.StudyCoordinator;
  1678. config.document.getElementById('studySponsor').innerHTML=
  1679. config.formConfig.crfEntry.StudySponsor;
  1680. config.document.getElementById('siteName').innerHTML=
  1681. config.formConfig.site['siteName'];
  1682. config.document.getElementById('sitePhone').innerHTML=
  1683. config.formConfig.site['sitePhone'];
  1684. config.document.getElementById('investigatorName').innerHTML=
  1685. config.formConfig.user['DisplayName'];
  1686. }
  1687. function afterConfig(){
  1688. let debug=true;
  1689. if (debug)
  1690. print("afterConfig");
  1691. //schedule basic data for later
  1692. populateBasicData();
  1693. let rows=config.formConfig.formStatusData.rows;
  1694. for (let i=0; i<rows.length; i++){
  1695. let key=rows[i].Key;
  1696. if (config.formConfig.crfEntry["FormStatus"]!=key)
  1697. continue;
  1698. config.formConfig.formStatus=rows[i].formStatus;
  1699. break;
  1700. }
  1701. let formStatus=config.formConfig.formStatus;
  1702. print("Generating buttons for formStatus \""+ formStatus+"\"");
  1703. let done="FALSE";
  1704. if (formStatus=="Submitted"){
  1705. generateButton("submitDiv","Complete submission",
  1706. "Upload to database",onDatabaseUpload);
  1707. generateButton("submitDiv","Review submission",
  1708. "Ask for further review",onUpdateForReview);
  1709. generateButton("submitDiv","Remove submission",
  1710. "Remove CRF form",onRemoveCRF);
  1711. done="TRUE";
  1712. }
  1713. if (formStatus=="Approved"){
  1714. generateButton("submitDiv","Review submission",
  1715. "Restore form for further review",onUpdateForReview);
  1716. generateButton("submitDiv","Print form",
  1717. "Generate PDF",printForm);
  1718. done="TRUE";
  1719. }
  1720. if (done=="FALSE"){
  1721. //all other states, particularly Review and In progress
  1722. generateButton("submitDiv","Complete submission",
  1723. "Submit",onSubmit);
  1724. generateButton("submitDiv","Remove submission",
  1725. "Remove CRF form",onRemoveCRF);
  1726. }
  1727. generateButton("submitDiv","Done","Exit",redirect);
  1728. print('Here');
  1729. //here we should get data. For now, just initialize objects that will hold data
  1730. //config.formConfig.dataFields=new Object();
  1731. //config.formConfig.entries=new Object();
  1732. setDataLayout(afterDataLayout);//callback is afterDataLayout
  1733. }
  1734. function afterDataLayout(){
  1735. setData(afterData);//callback is afterData
  1736. }
  1737. function afterData(){
  1738. let rowsSetup=config.formConfig.formSetup.rows;
  1739. for (let i=0;i<rowsSetup.length;i++){
  1740. let entry=rowsSetup[i];
  1741. print("entry[showFlag]: "+entry["showFlag"]);
  1742. let queryName=config.formConfig.queryMap[entry['queryName']];
  1743. let additionalData=new Object();
  1744. setAdditionalData(additionalData,entry);
  1745. //section fits one dataset/list
  1746. generateSection(queryName,entry["title"],queryName, additionalData);
  1747. }
  1748. }
  1749. //entry point from generateMasterForm
  1750. function setFormConfig(){
  1751. //add object to store form related data
  1752. config.formConfig=new Object();
  1753. let debug=true;
  1754. if (debug)
  1755. print("generateMasterForm1");
  1756. let selectRows=new Object();
  1757. selectRows.containerPath=config.containerPath;
  1758. selectRows.schemaName='lists';
  1759. selectRows.queryName='crfEntry';
  1760. //use first-> we must first establish link to the rigth crf entry
  1761. selectRows.filterArray=[LABKEY.Filter.create('entryId',getCRFrefFirst())];
  1762. //store form related data to this object
  1763. selectRows.success=afterCRFEntry;
  1764. LABKEY.Query.selectRows(selectRows);
  1765. }
  1766. function afterCRFEntry(data){
  1767. config.formConfig.crfEntry=data.rows[0];
  1768. print("Setting crfEntry (x) to "+config.formConfig.crfEntry["entryId"]);
  1769. let selectRows=new Object();
  1770. selectRows.containerPath=config.containerPath;
  1771. selectRows.schemaName='lists';
  1772. selectRows.queryName='site';
  1773. selectRows.filterArray= [
  1774. LABKEY.Filter.create('siteNumber',config.formConfig.crfEntry.Site)];
  1775. selectRows.success=afterSite;
  1776. LABKEY.Query.selectRows(selectRows);
  1777. }
  1778. function afterSite(data){
  1779. print("afterSite");
  1780. config.formConfig.site=data.rows[0];
  1781. print("Setting site name to "+
  1782. config.formConfig.site["siteName"]
  1783. +" phone: "+config.formConfig.site["sitePhone"]);
  1784. let selectRows=new Object();
  1785. selectRows.containerPath=config.containerPath;
  1786. selectRows.schemaName='core';
  1787. selectRows.queryName='Users';
  1788. selectRows.filterArray=[
  1789. LABKEY.Filter.create('siteNumber',
  1790. config.formConfig.crfEntry.UserId)];
  1791. selectRows.success=afterUser;
  1792. LABKEY.Query.selectRows(selectRows);
  1793. }
  1794. function afterUser(data){
  1795. config.formConfig.user=data.rows[0];
  1796. print("Setting user to "+config.formConfig.user["DisplayName"]);
  1797. let selectRows=new Object();
  1798. selectRows.containerPath=config.containerPath;
  1799. selectRows.schemaName='study';
  1800. selectRows.queryName='Study';
  1801. selectRows.columns="SubjectColumnName";
  1802. selectRows.success=afterStudy;
  1803. LABKEY.Query.selectRows(selectRows);
  1804. }
  1805. function afterStudy(data){
  1806. config.formConfig.studyData=data.rows[0];
  1807. print("XSetting participantField to "+config.formConfig.studyData["SubjectColumnName"]);
  1808. let selectRows=new Object();
  1809. selectRows.containerPath=config.containerPath;
  1810. selectRows.schemaName='lists';
  1811. selectRows.queryName='FormStatus';
  1812. selectRows.filterArray=[];
  1813. selectRows.success=afterFormStatus;
  1814. LABKEY.Query.selectRows(selectRows);
  1815. }
  1816. function afterFormStatus(data){
  1817. print("afterFormStatus: ");
  1818. config.formConfig.formStatusData=data;
  1819. let qconfig=new Object();
  1820. qconfig.schemaName="lists";
  1821. qconfig.queryName="Forms";
  1822. qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)];
  1823. //qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)]
  1824. qconfig.success=afterForms1;
  1825. LABKEY.Query.selectRows(qconfig);
  1826. }
  1827. function afterForms1(data){
  1828. print("afterForms1: ");
  1829. config.formConfig.form=data.rows[0];
  1830. let selectRows=new Object();
  1831. selectRows.containerPath=config.containerPath;
  1832. selectRows.schemaName='lists';
  1833. selectRows.queryName='FormSetup';
  1834. selectRows.filterArray=[LABKEY.Filter.create('formName',config.formId)];
  1835. selectRows.success=afterFormSetup;
  1836. LABKEY.Query.selectRows(selectRows);
  1837. }
  1838. function afterFormSetup(data){
  1839. config.formConfig.formSetup=data;
  1840. print("Number of datasets for form ["+config.formId+"]: "+
  1841. config.formConfig.formSetup.rows.length);
  1842. let fields=config.formConfig.formSetup.metaData.fields;
  1843. //get the lookup for queryName column
  1844. let formQueryName='queryName';
  1845. let field="NONE";
  1846. for (f in fields){
  1847. if (fields[f]['name']!=formQueryName) continue;
  1848. field=fields[f];
  1849. break;
  1850. }
  1851. let lookup=field.lookup;
  1852. print("Getting dataset names from "+lookup.queryName);
  1853. let selectRows=new Object();
  1854. selectRows.containerPath=config.containerPath;
  1855. selectRows.schemaName=lookup.schemaName;
  1856. selectRows.queryName=lookup.queryName;
  1857. selectRows.success=afterFormDatasets;
  1858. LABKEY.Query.selectRows(selectRows);
  1859. }
  1860. function afterFormDatasets(data){
  1861. print('afterFormDatasets: '+data.rows.length);
  1862. config.formConfig.formDatasets=data;//inputLists
  1863. config.formConfig.fields=new Object();
  1864. config.formConfig.queryMap=new Object();
  1865. let rows=config.formConfig.formSetup.rows;
  1866. //should skip report only rows
  1867. for (let i=0;i<rows.length;i++){
  1868. let entry=rows[i];
  1869. let reviewField=(entry['showFlag']=='REVIEW');
  1870. let queryId=entry['queryName'];
  1871. let lookupRows=config.formConfig.formDatasets.rows;
  1872. print('QueryID['+i+']='+queryId);
  1873. let dentry;
  1874. for (let j=0;j<lookupRows.length;j++){
  1875. if (queryId!=lookupRows[j]['Key']) continue;
  1876. dentry=lookupRows[j];
  1877. break;
  1878. }
  1879. let qName=dentry['queryName'];
  1880. //update list of dataset formConfig is observing (fields/queryMap)
  1881. while (1){
  1882. //review contains no data
  1883. if (reviewField) break;
  1884. //already in fields
  1885. if (qName in config.formConfig.fields) break;
  1886. config.formConfig.fields[qName]=new Object();
  1887. break;
  1888. }
  1889. while(1){
  1890. //already done
  1891. if (queryId in config.formConfig.queryMap) break;
  1892. config.formConfig.queryMap[queryId]=qName;
  1893. break;
  1894. }
  1895. if (reviewField) continue;
  1896. //only do this for real lists
  1897. let field=config.formConfig.fields[qName];
  1898. field.title=entry['title'];
  1899. field.queryId=queryId;
  1900. }
  1901. print("List of datasets in form : ");
  1902. for (f in config.formConfig.fields){
  1903. let field=config.formConfig.fields[f];
  1904. print("\t"+f+" ID: "+field.queryId+' title '+field.title);
  1905. }
  1906. afterConfig();
  1907. }
  1908. //>>>>>>>>>>>>>>>>>new>>>>>>>>>>>>
  1909. function setDataLayout(cb){
  1910. let rowsSetup=config.formConfig.formSetup.rows;
  1911. config.formConfig.dataQueries=new Object();
  1912. let dS=config.formConfig.dataQueries;//reference only
  1913. let qMap=config.formConfig.queryMap;
  1914. config.formConfig.lookup=new Object();
  1915. for (let i=0;i<rowsSetup.length;i++){
  1916. let entry=rowsSetup[i];
  1917. //skip review rows
  1918. if (entry['showFlag']=='REVIEW')
  1919. continue;
  1920. let queryId=entry['queryName'];
  1921. let queryName=qMap[entry['queryName']];
  1922. dS[queryName]=new Object();
  1923. dS[queryName].title=entry['title'];
  1924. if (entry['showQuery']!="NONE"){
  1925. let sqName=entry['showQuery'];
  1926. dS[sqName]=new Object();
  1927. dS[sqName].title=findTitle(sqName);
  1928. }
  1929. }
  1930. //always add reviews
  1931. //
  1932. config.formConfig.dataQueries['reviewComments']=new Object();
  1933. //perhaps we will need queryId, but this is stuff for later
  1934. for (q in config.formConfig.dataQueries){
  1935. //callback will be a watchdog and will complete only
  1936. //when all data will be gathered
  1937. let dq=config.formConfig.dataQueries[q];
  1938. dq.collectingLayout="INITIALIZED";
  1939. let selectRows=new Object();
  1940. selectRows.queryName=q;
  1941. selectRows.schemaName='lists';
  1942. //we are only interested in metadata
  1943. selectRows.success=function(data){afterDatasets(data,cb);}
  1944. LABKEY.Query.selectRows(selectRows);
  1945. }
  1946. }
  1947. function findTitle(queryName){
  1948. //find by name from formDatasets
  1949. //and set associated title as title
  1950. let rows=config.formConfig.formDatasets.rows;
  1951. for (let i=0;i<rows.length;i++){
  1952. let entry=rows[i];
  1953. if (entry['queryName']!=queryName) continue;
  1954. return entry['title'];
  1955. }
  1956. return "NONE";
  1957. }
  1958. //this happens after the for loop, so all dataQueries objects are set
  1959. function afterDatasets(data,cb){
  1960. let qobject=config.formConfig.dataQueries[data.queryName];
  1961. print("Inspecting layout for "+data.queryName+" "+qobject);
  1962. qobject.fields=data.metaData.fields;
  1963. qobject.collectingLayout="STARTED";
  1964. //qobject.started="TRUE";
  1965. qobject.lookup=new Object();//keep track of objects
  1966. let i=0;
  1967. for (let f in qobject.fields){
  1968. //anything else is simple but lookup
  1969. let field=qobject.fields[f];
  1970. if (!("lookup" in field)) continue;
  1971. qobject.lookup[f]="PENDING";
  1972. print("Adding pending lookup for field "+f);
  1973. if (field.lookup.queryName in config.formConfig.lookup){
  1974. qobject.lookup[f]="DONE";
  1975. continue;
  1976. }
  1977. print("Setting up query for field "+f);
  1978. config.formConfig.lookup[field.lookup.queryName]=new Object();
  1979. let lObject=config.formConfig.lookup[field.lookup.queryName];
  1980. lObject.keyColumn=field.lookup.keyColumn;
  1981. lObject.displayColumn=field.lookup.displayColumn;
  1982. let selectRows=new Object();
  1983. selectRows.schemaName=field.lookup.schemaName;
  1984. selectRows.queryName=field.lookup.queryName;
  1985. selectRows.columns=field.lookup.keyColumn+","+field.lookup.displayColumn;
  1986. //wait for all lookups to return
  1987. selectRows.success=function(data){addLookup(data,qobject,f,cb);};
  1988. print("Sending query: ["+field.lookup.queryName+"] "+field.lookup.keyColumn+'/'+field.lookup.displayColumn);
  1989. LABKEY.Query.selectRows(selectRows);
  1990. i+=1;
  1991. }
  1992. if (i==0){
  1993. print("No lookups for "+data.queryName);
  1994. qobject.collectingLayout="FINISHED";
  1995. }
  1996. //check if done (both here and in lookup
  1997. //this only passes if no lookups are anywhere in the dataset assembly
  1998. if (!dataLayoutSet()) return;
  1999. cb();
  2000. }
  2001. function addLookup(data,qobject,f,cb){
  2002. print("Adding lookup "+data.queryName+' '+qobject+' '+f);
  2003. let lObject=config.formConfig.lookup[data.queryName];
  2004. lObject.LUT=new Array();//key to value
  2005. lObject.ValToKey=new Array();//value to key
  2006. let key=lObject.keyColumn;
  2007. let val=lObject.displayColumn;
  2008. for (let i=0;i<data.rows.length;i++){
  2009. lObject.LUT[data.rows[i][key]]=data.rows[i][val];
  2010. lObject.ValToKey[data.rows[i][val]]=data.rows[i][key];
  2011. }
  2012. qobject.lookup[f]="DONE";
  2013. if (!dataLayoutSet()) return;
  2014. cb();
  2015. }
  2016. function dataLayoutSet(){
  2017. print("Checking layout completeness");
  2018. let dq=config.formConfig.dataQueries;
  2019. for (f in dq){
  2020. print("["+f+"]: "+dq[f].collectingLayout);
  2021. if (dq[f].collectingLayout=="INITIALIZED")
  2022. return 0;
  2023. if (dq[f].collectingLayout=="FINISHED")
  2024. continue; //OK
  2025. let qobject=dq[f];
  2026. for (q in qobject.lookup){
  2027. if (qobject.lookup[q]=="DONE") {
  2028. print("["+f+"/"+q+"]: DONE");
  2029. continue;//OK
  2030. }
  2031. print("["+f+"/"+q+"]: "+qobject.lookup[q]);
  2032. return 0;
  2033. }
  2034. dq[f].collectingLayout="FINISHED";
  2035. }
  2036. print("Success");
  2037. return 1;
  2038. }
  2039. function setData(cb){
  2040. //collect data and execute callback cb for queries in cb.queryList
  2041. for (q in config.formConfig.dataQueries){
  2042. let fQuery=config.formConfig.dataQueries[q];
  2043. fQuery.collectingData="STARTED";
  2044. let selectRows=new Object();
  2045. selectRows.queryName=q;
  2046. selectRows.schemaName='lists';
  2047. selectRows.filterArray=[
  2048. LABKEY.Filter.create("crfRef",getCRFref())
  2049. ];
  2050. selectRows.success=function(data){assembleData(data,cb);};
  2051. LABKEY.Query.selectRows(selectRows);
  2052. }
  2053. }
  2054. function assembleData(data,cb){
  2055. let fQuery=config.formConfig.dataQueries[data.queryName];
  2056. fQuery.rows=data.rows;
  2057. fQuery.collectingData="FINISHED";
  2058. for (q in config.formConfig.dataQueries){
  2059. let dq=config.formConfig.dataQueries[q];
  2060. //print("assembleData ["+q+"]: "+dq.collectingData);
  2061. if (dq.collectingData=="STARTED") return;
  2062. }
  2063. cb();
  2064. }
  2065. function uploadFile(inputElement,context){
  2066. //context should have ID and dirName attributes;
  2067. //path will be dirName/ID/fieldName_ID.suf
  2068. //where suf is identical to localPath content picked from
  2069. //inputElement
  2070. print('uploadFile: '+inputElement.value+'/');
  2071. if (inputElement.type=="text") return;
  2072. print('uploadFile: '+inputElement.files+'/');
  2073. print('uploadFile: '+inputElement.files.length+'/');
  2074. if (inputElement.files.length>0){
  2075. let file=inputElement.files[0];
  2076. print('uploadFile: '+inputElement.value+'/'+file.size);
  2077. }
  2078. let url=LABKEY.ActionURL.getBaseURL();
  2079. url+='_webdav';
  2080. url+=LABKEY.ActionURL.getContainer();
  2081. url+='/@files';
  2082. url+='/'+context['dirName'];
  2083. print('uploadFile url: '+url);
  2084. let uploadConfig=new Object();
  2085. uploadConfig.inputElement=inputElement;
  2086. uploadConfig.context=context;
  2087. uploadConfig.url=url;
  2088. uploadConfig.success=afterBaseDir;
  2089. uploadConfig.failure=tryMakeDir;
  2090. webdavCheck(uploadConfig);
  2091. }
  2092. function afterBaseDir(cfg){
  2093. print('afterBaseDir');
  2094. cfg.url+='/'+cfg.context['ID'];
  2095. cfg.success=afterIDDir;
  2096. cfg.failure=tryMakeDir;
  2097. webdavCheck(cfg);
  2098. }
  2099. function afterIDDir(cfg){
  2100. print('afterIDDir');
  2101. if (cfg.inputElement.files.length==0){
  2102. print('No files found');
  2103. return;
  2104. }
  2105. let file=cfg.inputElement.files[0];
  2106. print('Uploading '+file.name);
  2107. let suf=file.name.split('.').pop();
  2108. cfg.url+='/'+cfg.context['ID']+'.'+suf;
  2109. cfg.success=afterUpload;
  2110. cfg.failure=onFailure;
  2111. cfg.data=file;
  2112. webdavPut(cfg);
  2113. }
  2114. function afterUpload(cfg){
  2115. print('afterUpload');
  2116. }
  2117. function tryMakeDir(cfg){
  2118. print('tryMakeDir '+cfg.url);
  2119. cfg.failure=onFailure;
  2120. webdavMakeDir(cfg);
  2121. }
  2122. function request(cfg,verb,data){
  2123. print('request['+verb+'] '+cfg.url);
  2124. let connRequest=new XMLHttpRequest();
  2125. connRequest.addEventListener("loadend",
  2126. function(){checkResponse(connRequest,cfg);});
  2127. connRequest.open(verb, cfg.url);
  2128. connRequest.send(data);
  2129. //print('request['+verb+'] sent');
  2130. }
  2131. function checkResponse(xrq,cfg){
  2132. //print('checkResponse: readyState '+xrq.readyState);
  2133. //print('checkResponse: status '+xrq.status);
  2134. if (xrq.status<400) {
  2135. //client errors 400-499
  2136. //server errors 500-599
  2137. cfg.success(cfg);
  2138. return;
  2139. }
  2140. cfg.status=xrq.status;
  2141. cfg.failure(cfg);
  2142. }
  2143. function webdavMakeDir(cfg){ request(cfg,'MKCOL',null);}
  2144. function webdavCheck(cfg) { request(cfg,'GET',null);}
  2145. function webdavPut(cfg) { request(cfg,'PUT',cfg.data);}
  2146. function onFailure(cfg){
  2147. print('request failed with status='+cfg.status);
  2148. }