crfVisit.js 103 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834
  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 makeQuery(containerName,queryName,fieldName,filterArray){
  19. //generates an instruction entry that looks up queryName from container and stores
  20. //the resulting table to fieldName potentially using filterArray
  21. let e=new Object();
  22. e.containerName=containerName;
  23. e.queryName=queryName;
  24. e.fieldName=fieldName;
  25. e.filterArray=filterArray;
  26. return e;
  27. }
  28. function getDataFromQueries(queryArray,cb){
  29. //queryArray should contain elements with
  30. //- fieldName to set the data variable
  31. //- containerName to select container (data,config,CRF)
  32. //- queryName to select query
  33. //- filterArray to perform filtering, empty array works
  34. //- callback cb to be called with no arguments
  35. //
  36. afterQuery(new Object(),-1,queryArray,cb);
  37. }
  38. function afterQuery(data,id,queryArray,cb){
  39. //queryArray should contain elements with
  40. //- fieldName to set the data variable
  41. //- containerName to select container (data,config,CRF)
  42. //- queryName to select query
  43. //- filterArray to perform filtering, empty array works
  44. //- callback cb to be called with no arguments
  45. //
  46. //it should be called with id -1.
  47. //
  48. print('afterQuery['+id+']: ');
  49. if (id>-1){
  50. let fieldName=queryArray[id].fieldName;
  51. print('afterQuery['+fieldName+']: '+data.rows.length);
  52. config.formConfig[fieldName]=data;
  53. }
  54. id+=1;
  55. if (id==queryArray.length) {
  56. cb();
  57. return;
  58. }
  59. let e=queryArray[id];
  60. let qconfig=new Object();
  61. qconfig.containerPath=getContainer(e.containerName);
  62. qconfig.schemaName="lists";
  63. if ("schemaName" in e){
  64. print('afterQuery: schemaName='+e.schemaName);
  65. qconfig.schemaName=e.schemaName;
  66. }
  67. if ("columns" in e){
  68. print('afterQuery: columns='+e.columns);
  69. qconfig.columns=e.columns;
  70. }
  71. qconfig.queryName=e.queryName;
  72. //this should point to configuration container
  73. //don't filter -> so we can pick up other forms (say registration) later on
  74. //qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)];
  75. if ("filterArray" in e)
  76. qconfig.filterArray=e.filterArray;
  77. //qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)]
  78. qconfig.success=function(data){afterQuery(data,id,queryArray,cb);};
  79. qconfig.failure=doNothing;
  80. LABKEY.Query.selectRows(qconfig);
  81. }
  82. function getCRFrefFirst(){
  83. //crfRef is part of html call and gets stored in the page
  84. return config.document.getElementById(config.crfRefId).innerHTML;
  85. }
  86. function getCRFref(){
  87. //'crfRefId'
  88. return config.formConfig.crfEntry['entryId'];
  89. }
  90. function getCRFrefData(){
  91. let parentCrf=config.formConfig.crfEntry['parentCrf'];
  92. if (parentCrf!=undefined) return parentCrf;
  93. return getCRFref();
  94. }
  95. function onFailure(errorInfo, options, responseObj){
  96. if (errorInfo && errorInfo.exception)
  97. alert("Failure: " + errorInfo.exception);
  98. else
  99. alert("Failure: " + responseObj.statusText);
  100. }
  101. function doNothing(){
  102. print('doNothing called');
  103. }
  104. function generateDebugSection(){
  105. //let debug=true;
  106. //if (debug) print("generateDebugSection "+sectionName);
  107. let formName=config.debugDiv;
  108. let sectionName="debugSection";
  109. let sectionTitle="Debug Messages";
  110. let tb=config.document.createElement('table');
  111. tb.className='t2';
  112. let row=tb.insertRow();
  113. let cell=config.document.createElement('th');
  114. row.appendChild(cell);
  115. cell.setAttribute("colspan","4");
  116. cell.style.fontSize="20px";
  117. cell.style.textAlign="center";
  118. let cellData=config.document.createTextNode(sectionTitle);
  119. cell.appendChild(cellData);
  120. cell=row.insertCell();
  121. let input=config.document.createElement("input");
  122. input.type="button";
  123. input.id="toggle"+sectionName+"VisbilityButton";
  124. input.onclick=function(){toggleVisibility(sectionName,input.id)};
  125. cell.appendChild(input);
  126. config.document.getElementById(formName).appendChild(tb);
  127. let div=config.document.createElement('div');
  128. div.id=sectionName;
  129. config.document.getElementById(formName).appendChild(div);
  130. //start open (for debug)
  131. //input.value="Hide";
  132. //div.style.display="block";
  133. //start hidden (for production)
  134. input.value="Show";
  135. div.style.display="none";
  136. let debugArea=config.document.createElement('textarea');
  137. debugArea.rows=10;
  138. debugArea.cols=95;
  139. debugArea.id=config.debugId;
  140. div.appendChild(debugArea);
  141. //print('ver: 0.10');
  142. }
  143. function getAdditionalData(formSetupEntry){
  144. let queryName=config.formConfig.queryMap[formSetupEntry['queryName']];
  145. let fName='[getAdditionalData/'+queryName+']';
  146. print(fName);
  147. if (queryName in config.formConfig.additionalData){
  148. print(fName+': Returning preset value');
  149. return config.formConfig.additionalData[queryName];
  150. }
  151. print(fName+': generating');
  152. config.formConfig.additionalData[queryName]=new Object();
  153. //takes address, so further changes will be to the newly created object
  154. let ad=config.formConfig.additionalData[queryName];
  155. if (formSetupEntry["showFlag"]==="NONE") {
  156. print(fName+": empty");
  157. return ad;
  158. }
  159. if (formSetupEntry["showFlag"]==="REVIEW") {
  160. //abuse additionalData to signal different segment
  161. print(fName+": generateReport");
  162. ad.isReview=true;
  163. return ad;
  164. }
  165. print(fName+': setting values');
  166. ad.showFlag=formSetupEntry["showFlag"];
  167. ad.showFlagValue=formSetupEntry["showFlagValue"];
  168. //variables associated with target query
  169. let qVars=formSetupEntry["showQuery"].split(';');
  170. ad.queryName=qVars[0];
  171. //remove first element
  172. qVars.shift();
  173. //join back to string for parsing
  174. let code=qVars.join(";");
  175. //if empty string, set to undefined for parseCode
  176. if (code.length==0){
  177. code=undefined;
  178. }
  179. //parse var=value pairs
  180. ad.variableDefinition=parseCode(code);
  181. //print for debugging
  182. for (let f in ad.variableDefinition){
  183. let v=ad.variableDefinition[f];
  184. print(fName+': adding ['+f+']='+v);
  185. }
  186. ad.filters=new Object();
  187. ad.filters['crfRef']=getCRFref();
  188. let msg=fName+": flag "+ad.showFlag;
  189. msg+=" value "+ad.showFlagValue;
  190. msg+=" query "+ad.queryName;
  191. print(msg);
  192. return ad;
  193. }
  194. function generateSetup(){
  195. let setup=new Object();
  196. //setVariables contains special variables that have a
  197. //preset value in a specified table:
  198. //A.) these values won't appear in the form
  199. //B.) will be submitted with value specified to the database
  200. //make sure setVariables object is present in an object
  201. setup.setVariables=new Object();
  202. return setup;
  203. }
  204. function fullAccessSetup(sectionId,listName){
  205. //generate setup object whcih should contain fields:
  206. //readonlyFlag - whether the dataset is writeable
  207. //filters - selection fields that allow creation of LABKEY.Filter.create()
  208. //getInputId - formating of unique ids for html elements
  209. //addApply - whether a submit/Save button is generated
  210. //unique - whether entries in list are unique
  211. let debug=true;
  212. if (debug) print("fullAccessSetup");
  213. let setup=generateSetup();
  214. setup.queryName=listName;
  215. setup.readonlyFlag=function(vName){return false};
  216. setup.filters=new Object();
  217. setup.filters['crfRef']=getCRFref();
  218. setup.getInputId=function(vName){return sectionId+"_"+vName;}
  219. setup.addApply="Save";
  220. setup.isReview=false;
  221. setup.sectionId=sectionId;
  222. return setup;
  223. }
  224. function readonlySetup(sectionId,listName){
  225. //see definition of setup object above
  226. let debug=true;
  227. if (debug) print("readonlySetup");
  228. let setup=generateSetup();
  229. setup.queryName=listName;
  230. setup.readonlyFlag=function(vName){return true};
  231. setup.filters=new Object();
  232. setup.filters['crfRef']=getCRFref();
  233. setup.getInputId=function(vName){return sectionId+'_'+vName;}
  234. setup.isReview=false;
  235. setup.sectionId=sectionId;
  236. return setup;
  237. }
  238. function getSetup(sectionId,listName,writeAccess=true){
  239. //change to section granulated permission of type EDIT, COMMENT, READ
  240. //let formStatus=config.formConfig.formStatus;
  241. //equivalent to READ
  242. if (!writeAccess)
  243. //if (formStatus=="Submitted")
  244. return readonlySetup(sectionId,listName);
  245. //if (formStatus=="Approved")
  246. // return readonlySetup(listName);
  247. return fullAccessSetup(sectionId,listName);
  248. }
  249. function parseVariableDefinition(formSetupEntry){
  250. let fName='parseVariableDefinition['+formSetupEntry['title']+']';
  251. let code=formSetupEntry['variableDefinition'];
  252. print(fName+' '+code);
  253. return parseCode(code);
  254. }
  255. function parseCode(code){
  256. //helper function to decode content of variableDefinition
  257. //to a JS object
  258. //
  259. //should contain semicolon split var=value pairs
  260. let vars=new Object();
  261. if (code==undefined)
  262. //variableDefinition field is empty, return empyt object
  263. return vars;
  264. let ar=code.split(';');
  265. for (let i=0;i<ar.length;i++){
  266. q=ar[i].split('=');
  267. vars[q[0]]=q[1];
  268. }
  269. return vars;
  270. }
  271. function setHelp(setup,formSetupEntry){
  272. let helpRows=config.formConfig.help.rows;
  273. for (let i=0;i<helpRows.length;i++){
  274. let eh=helpRows[i];
  275. if (eh['query']==formSetupEntry['queryName']){
  276. if (!("help" in setup)){
  277. setup["help"]=new Object();
  278. setup["help"].rows=new Array();
  279. }
  280. setup["help"].rows.push(eh);
  281. print('Adding help to '+config.formConfig.queryMap[eh['query']]);
  282. }
  283. }
  284. //this updates setup
  285. }
  286. function generateSection(formSetupEntry){
  287. //generates a (hideable) section according to setup in formEntrySetup
  288. let listName=config.formConfig.queryMap[formSetupEntry['queryName']];
  289. let sectionId='section'+formSetupEntry['Key'];
  290. let fName='[generateSection/'+listName+'/'+sectionId+']';
  291. let sectionTitle=formSetupEntry['title'];
  292. let accessModeColumn=config.formConfig.operator+'Mode';
  293. let accessMode=formSetupEntry[accessModeColumn];
  294. //this will fix it for later use as well
  295. let additionalData=getAdditionalData(formSetupEntry);
  296. print(fName);
  297. let formName=config.masterForm;//this is HTML designator of area on page
  298. let debug=true;
  299. let tb=config.document.createElement('table');
  300. tb.className='t2';
  301. let row=tb.insertRow();
  302. let cell=config.document.createElement('th');
  303. row.appendChild(cell);
  304. cell.setAttribute("colspan","4");
  305. cell.style.fontSize="20px";
  306. cell.style.textAlign="center";
  307. let cellData=config.document.createTextNode(sectionTitle);
  308. cell.appendChild(cellData);
  309. cell=row.insertCell();
  310. let input=config.document.createElement("input");
  311. input.type="button";
  312. input.value="Show";
  313. input.id="toggle"+sectionId+"VisbilityButton";
  314. input.onclick=function(){toggleVisibility(sectionId,input.id)};
  315. //toggleVisibilityArgumentList changed!
  316. cell.appendChild(input);
  317. config.document.getElementById(formName).appendChild(tb);
  318. let div=config.document.createElement('div');
  319. //relabel this to allow multiple entries for the same list
  320. div.id=sectionId;
  321. div.style.display="none";
  322. config.document.getElementById(formName).appendChild(div);
  323. let divTable=config.document.createElement('div');
  324. divTable.id=sectionId+"Table";
  325. div.appendChild(divTable);
  326. if ("showFlag" in additionalData) {
  327. additionalData.divName=sectionId+"SubDiv";
  328. additionalData.divQueryName=sectionId+"SubDivList";
  329. let div1=config.document.createElement('div');
  330. div1.id=additionalData.divName;
  331. div1.style.display="none";
  332. div.appendChild(div1);
  333. let div2=config.document.createElement('div');
  334. div2.id=additionalData.divQueryName;
  335. div1.appendChild(div2);
  336. }
  337. if (debug) print(fName+" master table");
  338. let writeMode=accessMode=="EDIT";
  339. //setup stores some common values that get passed to generateTable
  340. //most significantly, whether fields are changeable or not
  341. if ("isReview" in additionalData){
  342. generateReviewSection(listName,div.id,generateReviewSectionCB);
  343. return;
  344. }
  345. //master table is unique per visit
  346. let setup=getSetup(sectionId,listName,writeMode);
  347. setup.unique=true;
  348. setHelp(setup,formSetupEntry);
  349. //set variables from formSetup variableDefinition field
  350. //to setup.setVariables
  351. setup.setVariables=parseVariableDefinition(formSetupEntry);
  352. for (x in setup.setVariables){
  353. print(fName+' setVariables '+x+':'+setup.setVariables[x]);
  354. }
  355. generateTable(listName,divTable.id,additionalData,setup);
  356. if (debug) print(fName+" master table: done");
  357. let generateSubTable=true;
  358. //generateSubTable equivalent to read/write access to section
  359. if (accessMode != "EDIT")
  360. generateSubTable=false;
  361. if (! ("showFlag" in additionalData) ) generateSubTable=false;
  362. if (generateSubTable){
  363. let qName=additionalData.queryName;
  364. let dName=additionalData.divName;
  365. let subsectionId='sub'+sectionId;
  366. let subSetup=fullAccessSetup(subsectionId,qName);
  367. //only set master query for additionalData
  368. subSetup.masterQuery=listName;
  369. //if (readonly) setup=readonlySetup(config);
  370. generateTable(qName,dName,additionalData,subSetup);
  371. //generateTable(formSetupEntry,qName,dName,additionalData,setup);
  372. }
  373. print(fName+" generate review");
  374. let divReviewList=config.document.createElement('div');
  375. divReviewList.id=sectionId+"ReviewList";
  376. div.appendChild(divReviewList);
  377. let divReview=config.document.createElement('div');
  378. divReview.id=sectionId+"Review";
  379. div.appendChild(divReview);
  380. //assume we already have listId (content of config.setupQueryName is listId)
  381. //we need listName also
  382. //qconfig.queryName=config.setupQueryName;
  383. generateReview(divReview.id,divReviewList.id,listName,accessMode);
  384. //generateReview depends on listName
  385. print(fName+" generate review complete");
  386. if (accessMode!='GENERATE') return;
  387. print('Adding generate button');
  388. //add generateButton
  389. let divGenerateButton=config.document.createElement('div');
  390. divGenerateButton.id=sectionId+'GenerateButton';
  391. div.appendChild(divGenerateButton);
  392. print('Adding generate button completed to here');
  393. let cb=function(){onGenerateQuery(listName);};
  394. generateButton(divGenerateButton.id,'Generate','Generate '+listName,'XX',cb);
  395. print('Adding generate button completed');
  396. }
  397. //>>>>reviewSection associated routines
  398. function parseResponseXML(){
  399. //print(config.config,'Status:' +this.status);
  400. print('Status:'+this.status);
  401. if (this.status!=200) return;
  402. config.loadFileConfig.json=JSON.parse(this.responseText);
  403. config.loadFileConfig.cb();
  404. }
  405. function loadFile(){
  406. print('YY: '+config.loadFileConfig.url);
  407. let connRequest=new XMLHttpRequest();
  408. connRequest.addEventListener("loadend",parseResponseXML);
  409. //function(e){parseResponseXML(e,config);});
  410. connRequest.open("GET", config.loadFileConfig.url);
  411. connRequest.send();
  412. }
  413. function getBasePath(){
  414. let server=LABKEY.ActionURL.getBaseURL();
  415. let basePath=server+"_webdav";
  416. basePath+=LABKEY.ActionURL.getContainer();
  417. return basePath;
  418. }
  419. function generateReviewSection(listName,id,callback){
  420. //callback should be generateReviewSectionCB and it takes no arguments
  421. print("generateReviewSection");
  422. //need base path
  423. config.loadFileConfig=new Object();
  424. config.loadFileConfig.cb=callback;
  425. config.loadFileConfig.id=id;
  426. config.loadFileConfig.url=getBasePath()+'/@files/reportSetup/'+listName+'.json';
  427. loadFile();
  428. //load file and continue in the next function
  429. }
  430. function generateErrorMessage(id,listName,msg){
  431. print('generateErrorMessage:');
  432. let eid=listName+"_errorMsg";
  433. let el=config.document.getElementById(eid);
  434. if (el===null){
  435. el=config.document.createElement("p");
  436. config.document.getElementById(id).appendChild(el);
  437. }
  438. el.innerHTML=msg;
  439. }
  440. function clearErrorMessage(listName){
  441. let eid=listName+"_errorMsg";
  442. let el=config.document.getElementById(eid);
  443. if (el===null) return;
  444. el.remove();
  445. }
  446. function getParticipantCode(pid){
  447. let selectRows=new Object();
  448. selectRows.schemaName='lists';
  449. //we should now which form we are in
  450. let mfId=config.formConfig.form['masterQuery'];
  451. selectRows.queryName=config.formConfig.queryMap[mfId];
  452. //point to data container
  453. selectRows.containerPath=getContainer('data');
  454. //selectRows.queryName='PET';
  455. pid.afterId=setParticipantCode;
  456. pid.participantField=config.formConfig.studyData["SubjectColumnName"];
  457. selectRows.success=function(data){afterRegistration(pid,data);}
  458. selectRows.filterArray=[LABKEY.Filter.create("crfRef",getCRFref())];
  459. LABKEY.Query.selectRows(selectRows);
  460. }
  461. function visitCodeFromVisitId(visitId){
  462. if (visitId<0) return "NONE";
  463. let project=getContainer('data');
  464. print('visitCodeFromVisitId: '+project.search('retro'));
  465. if (project.search('retro')>-1)
  466. visitId-=1;
  467. return 'VISIT_'+visitId.toString();
  468. }
  469. function replaceSlash(x){
  470. return x.replace(/\//,'_');
  471. }
  472. function setParticipantCode(pid){
  473. let fName='[setParticipantCode]';
  474. let rows=pid.registration.rows;
  475. //pick from study
  476. let participantField=config.formConfig.studyData["SubjectColumnName"];
  477. if (rows.length==1){
  478. print(fName+': '+rows[0][participantField]+'/'+rows[0].visitId);
  479. let visitCode=visitCodeFromVisitId(rows[0].visitId);
  480. print('setParticipantCode: '+pid.participantId+'/'+visitCode);
  481. pid.participantCode=replaceSlash(pid.participantId);
  482. pid.visitCode=visitCode;
  483. }
  484. generateReviewSection2(pid);
  485. }
  486. function generateReviewSectionCB(){
  487. let listName=config.loadFileConfig.listName;
  488. let id=config.loadFileConfig.id;
  489. clearErrorMessage(listName);
  490. let pid=new Object();
  491. pid.participantCode="NONE";
  492. pid.visitCode="NONE";
  493. getParticipantCode(pid);
  494. print('Get participant code sent');
  495. //involves database search, continue after callback
  496. }
  497. function getValueFromElement(id,defaultValue){
  498. let e=config.document.getElementById(id);
  499. if (e!=null){
  500. defaultValue=e.innerHTML;
  501. }
  502. return defaultValue;
  503. }
  504. function pickParticipantCodeFromPage(){
  505. let pid=new Object();
  506. pid.participantCode=getValueFromElement("participantCode","NIX-LJU-D2002-IRAE-A000");
  507. pid.visitCode=getValueFromElement("visitCode","VISIT_1");
  508. generateReviewSection2(pid);
  509. }
  510. function patternReplace(src,replacements,values){
  511. for (rep in replacements){
  512. let txt1=src.replace(new RegExp(rep),values[replacements[rep]]);
  513. src=txt1;
  514. }
  515. return src;
  516. }
  517. function plotImage(cell,k,row,rowVariable,obj,pid){
  518. let baseDir=patternReplace(obj.imageDir,obj.replacements,pid);
  519. print('Base dir: '+pid.basePath);
  520. pid[obj.variable]=obj.values[k];
  521. cell.id=pid[obj.variable]+"_"+rowVariable+pid[rowVariable];
  522. let img=null;
  523. let imgId=cell.id+'_img_';
  524. img=config.document.getElementById(imgId);
  525. if (img===null){
  526. img=config.document.createElement('img');
  527. img.id=imgId;
  528. cell.appendChild(img);
  529. }
  530. let imgSrc=patternReplace(obj.file,obj.replacements,pid);
  531. print('Image: '+imgSrc);
  532. let imagePath=pid.basePath+'/'+baseDir+'/'+imgSrc;
  533. img.src=imagePath;
  534. img.width="300";
  535. }
  536. function showReport(cell,k,row,rowVariable,obj,pid){
  537. cell.width="300px";
  538. cell.id='report_'+obj.values[k]+"_"+rowVariable+pid[rowVariable];
  539. let reportConfig=new Object();
  540. reportConfig.partName="Report";
  541. reportConfig.renderTo=cell.id;
  542. //reportConfig.showFrame=false;
  543. //reportConfig.width="300";
  544. reportConfig.frame="none";
  545. reportConfig.partConfig=new Object();
  546. reportConfig.partConfig.width="300";
  547. reportConfig.partConfig.title="R Report";
  548. reportConfig.partConfig.reportName=obj.values[k];
  549. for (f in obj.parameters){
  550. reportConfig.partConfig[f]=pid[f];
  551. }
  552. reportConfig.partConfig.showSection="myscatterplot";
  553. let reportWebPartRenderer = new LABKEY.WebPart(reportConfig);
  554. print('Render to: '+reportConfig.renderTo);
  555. reportWebPartRenderer.render();
  556. }
  557. function showProbability(cell,k,row,rowSetup,j,obj,pid){
  558. print('showProbability: '+rowSetup);
  559. let rowVariable=rowSetup.variable;
  560. cell.id='prob_'+obj.values[k]+"_"+rowVariable+pid[rowVariable];
  561. let probDensity=new Object();
  562. probDensity.mean=rowSetup.mean[j];
  563. probDensity.sigma=rowSetup.sigma[j];
  564. print('showProbability: mean '+probDensity.mean+' sigma '+probDensity.sigma);
  565. probDensity.func=obj.values[k];
  566. probDensity.organCode=pid.organCode;
  567. pid[obj.variable]=rowSetup[obj.variable][j];
  568. probDensity.percentile=pid.percentile;
  569. let selectRows=new Object();
  570. selectRows.queryName=obj.queryName;
  571. selectRows.schemaName="study";
  572. selectRows.filterArray=[];
  573. selectRows.containerPath=getContainer('data');
  574. for (let f in obj.filters){
  575. selectRows.filterArray.push(
  576. LABKEY.Filter.create(f,pid[obj.filters[f]]));
  577. print('Filter ['+f+']: '+pid[obj.filters[f]]);
  578. }
  579. selectRows.success=function(data){
  580. drawProbability(data,cell,obj,pid,probDensity);}
  581. LABKEY.Query.selectRows(selectRows);
  582. }
  583. function erf(x){
  584. 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,
  585. 1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,
  586. 2.1,2.2,2.3,2.4,2.5,3,3.5];
  587. let fy=[0,0.222702589,0.328626759,0.428392355,0.520499878,
  588. 0.603856091,0.677801194,0.742100965,0.796908212,
  589. 0.842700793,0.880205070,0.910313978,0.934007945,
  590. 0.952285120,0.966105146,0.976348383,
  591. 0.983790459,0.989090502,0.992790429,0.995322265,
  592. 0.997020533,0.998137154,0.998856823,0.999311486,
  593. 0.999593048,0.999977910,0.999999257];
  594. let n=32;
  595. let i0=n-1;
  596. for (let i=1;i<n;i++){
  597. if (Math.abs(x)>fx[i]) continue;
  598. i0=i-1;
  599. break;
  600. }
  601. let fval=1;
  602. if (i0<n-1){
  603. //interpolate
  604. let y1=fy[i0+1];
  605. let y0=fy[i0];
  606. let x1=fx[i0+1];
  607. let x0=fx[i0];
  608. fval=y0+(y1-y0)/(x1-x0)*(Math.abs(x)-x0);
  609. }
  610. print('Erf: '+fval);
  611. if (x<0) return -fval;
  612. return fval;
  613. }
  614. function setLine(fbox,name,value,fontSize){
  615. let fpId=fbox.id+name;
  616. let fp=config.document.getElementById(fpId);
  617. if (fp===null){
  618. fp=config.document.createElement("p");
  619. fp.id=fpId;
  620. fbox.appendChild(fp);
  621. }
  622. fp.classList.add("center");
  623. fp.style.textAlign="center";
  624. fp.style.fontSize=fontSize;
  625. fp.innerText=value;
  626. }
  627. function drawProbability(data,cell,obj,pid,probDensity){
  628. print('drawProbability');
  629. if (data.rows.length!=1){
  630. print("drawProbability row length mismatch: "+data.rows.length);
  631. return;
  632. }
  633. //possible mismatch; I assume the dataset will have a field called value
  634. let val=data.rows[0].value;
  635. let prob=0;
  636. let fz=-100;
  637. if (probDensity.func=="gaus"){
  638. fz=(val-probDensity.mean)/probDensity.sigma/Math.sqrt(2);
  639. prob=0.5+0.5*erf(fz);
  640. }
  641. let color="red";
  642. let fzx=fz*Math.sqrt(2);
  643. print('drawProbability '+fzx);
  644. for (let i=1;i<obj.intervals.n;i++){
  645. if (fzx>obj.intervals.zlimits[i]) continue;
  646. color=obj.intervals.colors[i-1];
  647. break;
  648. }
  649. let fboxId=cell.id+'_fbox_';
  650. let fbox=config.document.getElementById(fboxId);
  651. if (fbox===null){
  652. fbox=config.document.createElement("div");
  653. fbox.id=fboxId;
  654. cell.appendChild(fbox);
  655. }
  656. fbox.style.backgroundColor=color;
  657. fbox.style.width="180px";
  658. fbox.style.height="180px";
  659. print('organCode '+probDensity.organCode);
  660. let organName="Lung";
  661. if (probDensity.organCode==4){
  662. organName="Thyroid";
  663. }
  664. if (probDensity.organCode==5){
  665. organName="Bowel";
  666. }
  667. setLine(fbox,'_fp4_',organName,"16px");
  668. setLine(fbox,'_fp_',val.toPrecision(3),"25px");
  669. setLine(fbox,'_fp1_',"SUV("+probDensity.percentile+"%)","16px");
  670. setLine(fbox,'_fp2_',fzx.toPrecision(3),"25px");
  671. setLine(fbox,'_fp3_',"z-value","16px");
  672. }
  673. function generateReviewSection2(pid){
  674. let listName=config.loadFileConfig.listName;
  675. let id=config.loadFileConfig.id;
  676. print('generateReviewSection2: '+pid.participantCode+'/'+
  677. pid.visitCode);
  678. if (pid.participantCode=="NONE" || pid.visitCode=="NONE"){
  679. generateErrorMessage(id,listName,
  680. "ParticipantId/visitId not set");
  681. return;
  682. }
  683. print('JSON: '+config.loadFileConfig.json);
  684. let json=config.loadFileConfig.json;
  685. let nrows=json.rows.values.length;
  686. let ncol=json.columns.length;
  687. pid.basePath=getBasePath()+"/@files";
  688. let el=config.document.getElementById(id);
  689. let tableId=id+'_Table';
  690. let table=config.document.getElementById(tableId);
  691. if (table==null){
  692. table=config.document.createElement('table');
  693. table.id=tableId;
  694. el.appendChild(table);
  695. }
  696. table.style.tableLayout="fixed";
  697. table.style.columnWidth="300px";
  698. for (let i=0;i<nrows;i++){
  699. pid[json.rows.variable]=json.rows.values[i];
  700. //let organ=organs[i];
  701. let row=null;
  702. if (i<table.rows.length)
  703. row=table.rows[i];
  704. else
  705. row=table.insertRow();
  706. let ic=0;
  707. for (let j=0;j<ncol;j++){
  708. let obj=json.columns[j];
  709. let nv=obj.values.length;
  710. for (let k=0;k<nv;k++){
  711. let cell=null;
  712. if (ic<row.cells.length)
  713. cell=row.cells[ic];
  714. else
  715. cell=row.insertCell();
  716. if (obj.display=="image")
  717. plotImage(cell,k,row,json.rows.variable,obj,pid);
  718. if (obj.display=="report")
  719. showReport(cell,k,row,json.rows.variable,obj,pid);
  720. if (obj.display=="probability"){
  721. showProbability(cell,k,row,json.rows,i,obj,pid);
  722. }
  723. ic++;
  724. }
  725. }
  726. }
  727. }
  728. ///>>>>>>>>>>>>>>end of reviewSection(REPORT)
  729. function generateReview(divReviewId,divReviewListId, listName, accessMode){
  730. let listId=config.formConfig.fields[listName].queryId;
  731. //listId is a number->should it be queryName?
  732. let debug=true;
  733. if (debug) print("Generate review for: "+listId+'/'+listName);
  734. let reviewSetup=generateSetup();
  735. reviewSetup.readonlyFlag=function(vName){
  736. if (vName=="queryName") return true;
  737. if (vName=="queryname") return true;
  738. if (vName=="ModifiedBy") return true;
  739. return false;};
  740. reviewSetup.addApply="Add Review";
  741. let generateTableFlag=true;
  742. let formStatus=config.formConfig.formStatus;
  743. //COMMENTS allowed or not
  744. //three levels of access: EDIT, COMMENT, READ
  745. if (accessMode == "READ"){
  746. //if (formStatus == "Approved" ){
  747. delete reviewSetup.addApply;
  748. reviewSetup.readonlyFlag=function(vName){return false;}
  749. generateTableFlag=false;
  750. }
  751. reviewSetup.filters=new Object();
  752. reviewSetup.filters["crfRef"]=getCRFref();
  753. reviewSetup.filters["queryName"]=listId;//entry in reviewComments list is queryname, all in small caps
  754. //needs listName, in argument
  755. reviewSetup.getInputId=function(vName){return divReviewId+"_add"+vName};
  756. reviewSetup.divReviewListId=divReviewListId;
  757. reviewSetup.isReview=true;
  758. if (debug) {
  759. let msg="Review: divId: "+divReviewId;
  760. //msg+=" inputId: "+reviewSetup.getInputId;
  761. print(msg);
  762. }
  763. updateListDisplay(divReviewListId,"reviewComments",reviewSetup.filters,true);
  764. if (! generateTableFlag) return;
  765. generateTable("reviewComments",divReviewId,new Object(),reviewSetup);
  766. }
  767. //>>>>>>>>>>trigger visibility of additional lists
  768. function setListVisibility(input,setup,readonlyFlag){
  769. let debug=true;
  770. let fName="[setListVisibility/"+setup.queryName+"]";
  771. print(fName);
  772. let additionalData=config.formConfig.additionalData[setup.queryName];
  773. let x = config.document.getElementById(additionalData.divName);
  774. if (debug) print(fName+": Div: "+x);
  775. x.style.display="none";
  776. let sText;
  777. if ("setVariable" in input){
  778. sText=input.setVariable;
  779. }
  780. else{
  781. if (readonlyFlag) sText=input.innerText;
  782. else sText=input.options[input.selectedIndex].text;
  783. }
  784. if (debug) print(fName+": Selected option text: "+sText);
  785. if (sText == additionalData.showFlagValue){
  786. let filters=new Object();
  787. if ("filters" in additionalData) filters=additionalData.filters;
  788. x.style.display = "block";
  789. updateListDisplay(additionalData.divQueryName,
  790. additionalData.queryName,filters,readonlyFlag);
  791. }
  792. }
  793. //>>have list refresh when data is added (not optimal yet)
  794. function updateListDisplay(divName,queryName,filters,readonlyFlag){
  795. //use Labkey.QueryWebPart to show list
  796. let debug=true;
  797. let fName="[updateListDisplay]";
  798. if (debug)
  799. print(fName+": Query - "+queryName
  800. +" div - "+divName);
  801. if (divName=="NONE") return;
  802. let crfRef=getCRFref();
  803. let div=config.document.getElementById(divName);
  804. print(fName+": setting border");
  805. div.style.border="thin solid black";
  806. div.style.width="800px";
  807. if (debug)
  808. print(fName+": generating WebPart: "+queryName);
  809. var qconfig=new Object();
  810. qconfig.renderTo=divName;
  811. //point to data container
  812. qconfig.containerPath=getContainer('data');
  813. qconfig.schemaName='lists';
  814. qconfig.queryName=queryName;
  815. qconfig.buttonBarPosition='top';
  816. qconfig.filters=[];
  817. for (f in filters){
  818. qconfig.filters.push(LABKEY.Filter.create(f, filters[f]));
  819. }
  820. qconfig.success=updateSuccess;
  821. qconfig.failure=updateFailure;
  822. //show only print button
  823. if (readonlyFlag){
  824. qconfig.buttonBar=new Object();
  825. qconfig.buttonBar.items=["print"];
  826. }
  827. LABKEY.QueryWebPart(qconfig);
  828. }
  829. function updateSuccess(data){
  830. print("Update success");
  831. }
  832. function updateFailure(data){
  833. print("Update failed");
  834. }
  835. function toggleVisibility(sectionId,buttonName){
  836. let fName='[toggleVisibility/'+sectionId+']';
  837. print(fName);
  838. let x = config.document.getElementById(sectionId);
  839. if (x.style.display === "none") {
  840. //exclude non data sections (like debug)...
  841. print(fName+': issuing setData(populateSection)');
  842. x.style.display = "block";
  843. config.document.getElementById(buttonName).value="Hide";
  844. let cb=function(){populateSection(sectionId);};
  845. setData(cb);
  846. } else {
  847. x.style.display = "none";
  848. config.document.getElementById(buttonName).value="Show";
  849. }
  850. }
  851. function generateButtonBU(divName,title,buttonName,callback,
  852. callbackParameters){
  853. let debug=true;
  854. if (debug) print("generateButtonBU");
  855. let tb=config.document.createElement('table');
  856. tb.className="t2";
  857. let r1=tb.insertRow();
  858. th=config.document.createElement('th');
  859. r1.appendChild(th);
  860. th.innerHTML=title;
  861. //*!*
  862. let c2=r1.insertCell();
  863. let i1=config.document.createElement("input");
  864. i1.type="button";
  865. i1.value=buttonName;
  866. i1.style.fontSize="20px";
  867. i1.onclick=function(){callback(callbackParameters);}
  868. c2.appendChild(i1);
  869. let c1=r1.insertCell();
  870. c1.setAttribute("colspan","1");
  871. c1.id=callbackParameters.submitReportId;
  872. let el=config.document.getElementById(divName);
  873. if (debug) print("generateButton: element["+divName+"]: "+el);
  874. el.appendChild(tb);
  875. }
  876. function generateButton(divName,caption,label,callbackLabel,callback){
  877. let debug=true;
  878. if (debug) print("generateButtonX");
  879. let tb=config.document.createElement('table');
  880. tb.className="t2";
  881. let r1=tb.insertRow();
  882. th=config.document.createElement('th');
  883. r1.appendChild(th);
  884. th.innerHTML=caption;
  885. //*!*
  886. let c2=r1.insertCell();
  887. let i1=config.document.createElement("input");
  888. i1.type="button";
  889. i1.value=label;
  890. i1.style.fontSize="20px";
  891. i1.onclick=callback;
  892. i1.id='button_'+callbackLabel;
  893. c2.appendChild(i1);
  894. let c1=r1.insertCell();
  895. c1.setAttribute("colspan","1");
  896. //this is only for saveReview?
  897. c1.id=divName+'_reportField';
  898. //c1.id=config.submitReportId;
  899. let el=config.document.getElementById(divName);
  900. if (debug) print("generateButton: element["+divName+"]: "+el);
  901. el.appendChild(tb);
  902. }
  903. function generateSubQuery(input, setup, readonlyFlag){
  904. let fName="[generateSubQuery]";
  905. if (setup.isReview) return;
  906. if (!(setup.queryName in config.formConfig.additionalData)){
  907. print(fName+': no additionalData entry (probably a subquery)');
  908. return;
  909. }
  910. let additionalData=config.formConfig.additionalData[setup.queryName];
  911. if (!("showFlag" in additionalData))
  912. return;
  913. print(fName);
  914. let expId=setup.getInputId(additionalData.showFlag);
  915. if (expId!=input.id) {
  916. print(fName+": ignoring field "+input.id+"/"+expId);
  917. return;
  918. }
  919. print(fName+": Setting onChange to "+input.id);
  920. if (!readonlyFlag)
  921. input.onchange=function(){setListVisibility(input,setup,readonlyFlag)};
  922. }
  923. //>>populate fields
  924. //
  925. //
  926. //split to field generation and field population
  927. //
  928. function addFieldRow(tb,field,setup,additionalData){
  929. let fName="[addFieldRow/"+setup.queryName+':'+field.name+']';
  930. let vName=field.name;
  931. let vType=field.type;
  932. let isLookup=("lookup" in field);
  933. print(fName+": ["+vName+"/"+vType+'/'+isLookup+"]");
  934. let row=tb.insertRow();
  935. let cell=config.document.createElement('th');
  936. row.appendChild(cell);
  937. let text = config.document.createTextNode(field.shortCaption);
  938. cell.appendChild(text);
  939. let cell1=row.insertCell();
  940. let input=null;
  941. cell1.colSpan="3";
  942. let readonlyFlag=setup.readonlyFlag(vName);
  943. print(fName+' inputType '+field.inputType);
  944. //set the html input object
  945. while (1){
  946. if (readonlyFlag){
  947. input=config.document.createElement('label');
  948. input.innerText='Loading';
  949. break;
  950. }
  951. //lookup
  952. if (isLookup){
  953. input = config.document.createElement("select");
  954. break;
  955. }
  956. //date
  957. if (vType=="date"){
  958. input = config.document.createElement("input");
  959. input.type="date";
  960. break;
  961. }
  962. //string
  963. if (vType=="string"){
  964. //we have to make sure UNDEF is carried to below
  965. //since we are adapting file to either show
  966. //current file or allow user to select a file
  967. //
  968. //TODO change this so one can always select file
  969. //but also show the selected file
  970. if(vName.search("reviewComment")>-1){
  971. input = config.document.createElement("textarea");
  972. input.cols="65";
  973. input.rows="5";
  974. break;
  975. }
  976. if (field.inputType=="textarea"){
  977. input = config.document.createElement("textarea");
  978. input.cols="65";
  979. input.rows="5";
  980. break;
  981. }
  982. input=config.document.createElement('input');
  983. input.type="text";
  984. if (vName.search('_file_')<0) break;
  985. cell1.setAttribute('colspan',"1");
  986. let cell2=row.insertCell();
  987. cell2.setAttribute('colspan',"2");
  988. let input1=config.document.createElement('input');
  989. input1.type="file";
  990. input1.id=setup.getInputId(vName)+'_file_';
  991. cell2.appendChild(input1);
  992. break;
  993. }
  994. if (vType=="float"){
  995. input = config.document.createElement("input");
  996. input.type="text";
  997. break;
  998. }
  999. if (vType=="boolean"){
  1000. input = config.document.createElement("input");
  1001. input.type="checkbox";
  1002. print("Creating checkbox");
  1003. break;
  1004. }
  1005. break;
  1006. }
  1007. input.id=setup.getInputId(vName);
  1008. cell1.appendChild(input);
  1009. print(fName+': adding element '+input.id);
  1010. print(fName+': listing element '+config.document.getElementById(input.id));
  1011. //connect associated list
  1012. generateSubQuery(input,setup,readonlyFlag);
  1013. if (readonlyFlag) {
  1014. print(fName+': exiting(readonlyFlag)');
  1015. return;
  1016. }
  1017. if (!isLookup) {
  1018. print(fName+': exiting (not lookup)');
  1019. return;
  1020. }
  1021. let lookup=field["lookup"];
  1022. //get all values from config.formConfig.lookup[X]
  1023. let lObject=config.formConfig.lookup[lookup.queryName];
  1024. //debug
  1025. print(fName+": query: "+lookup.queryName);
  1026. print(fName+": ElementId: "+input.id);
  1027. print(fName+": No of options: " +lObject.LUT.length);
  1028. print(fName+": Element: "+input);
  1029. //set the lut value (input is text label) for readonly
  1030. input.style.textAlign="center";
  1031. //input.style.textAlignLast="center";
  1032. //clear existing fields from input
  1033. for(let i = input.options.length; i >= 0; i--) {
  1034. input.remove(i);
  1035. }
  1036. //create option -1
  1037. let opt = config.document.createElement("option");
  1038. opt.text = "<Select>";
  1039. opt.value = -1;
  1040. input.options[0] = opt;
  1041. print(fName+": Adding <Select>");
  1042. //add other, label them with LUT
  1043. for (let v in lObject.LUT) {
  1044. print(fName+': populating '+v+': '+lObject.LUT[v]);
  1045. let opt = config.document.createElement("option");
  1046. opt.text = lObject.LUT[v];
  1047. opt.value = v;
  1048. input.options[input.options.length] = opt;
  1049. }
  1050. input.selectedIndex=0;
  1051. }
  1052. function selectEntry(entryRows,setVariables){
  1053. let fName='[selectEntry]';
  1054. for (let i=0;i<entryRows.length;i++){
  1055. let tE=entryRows[i];
  1056. //this could be empty, end will be reached
  1057. let doContinue=false;
  1058. for (v in setVariables){
  1059. print(fName+' '+v+' '+tE[v]+'/'+setVariables[v]);
  1060. if (tE[v]!=setVariables[v]){
  1061. doContinue=true;
  1062. break;
  1063. }
  1064. }
  1065. if (doContinue) continue;
  1066. return tE;
  1067. }
  1068. let tE=Object();
  1069. tE['notValid']=true;
  1070. return tE;
  1071. }
  1072. function populateFieldRow(entry,field,setup){
  1073. if (!(field.name in setup.setVariables))
  1074. populateField(entry,field,setup);
  1075. populateSubQuery(entry,field,setup);
  1076. }
  1077. function populateSubQuery(entry,field,setup){
  1078. let fName='[populateSubQuery/'+setup.queryName+':'+field.name+']';
  1079. if (setup.isReview) return;
  1080. if (!(setup.queryName in config.formConfig.additionalData)){
  1081. let msg=fName+': no additionalData entry for '+setup.queryName;
  1082. msg+=' (probably a subquery)';
  1083. print(msg);
  1084. return;
  1085. }
  1086. //find if field is connected to a sub array
  1087. //find queryName
  1088. //
  1089. let additionalData=config.formConfig.additionalData[setup.queryName];
  1090. print(fName);
  1091. //let flag=additionalData.showFlag;
  1092. if (!("showFlag" in additionalData)) return;
  1093. let eId=setup.getInputId(additionalData.showFlag);
  1094. let id=setup.getInputId(field.name);
  1095. if (eId!=id) {
  1096. print(fName+": X ignoring field "+id+"/"+eId);
  1097. return;
  1098. }
  1099. print(fName+': id '+id);
  1100. //hard to estimate readonlyFlag
  1101. //
  1102. let readonlyFlag=setup.readonlyFlag(field.name);
  1103. let input=config.document.getElementById(id);
  1104. if (input){
  1105. let eType=input.nodeName.toLowerCase();
  1106. readonlyFlag=eType!="select";
  1107. }
  1108. else{
  1109. input=new Object();
  1110. input.setVariable=getLookupLabel(field,setup.setVariables[field.name])
  1111. }
  1112. setListVisibility(input,setup,readonlyFlag);
  1113. }
  1114. function getLookupLabel(field,value){
  1115. let lookup=field["lookup"];
  1116. //get all values from config.formConfig.lookup[X]
  1117. let lObject=config.formConfig.lookup[lookup.queryName];
  1118. return lObject.LUT[value];
  1119. }
  1120. function populateField(entry,field,setup){
  1121. let vName=field.name;
  1122. let fName='[populateFieldName/'+vName+']';
  1123. let varValue="UNDEF";
  1124. //if (vName in setup.filters) varValue=setup.filters[vName];
  1125. if (vName in entry) varValue=entry[vName];
  1126. //if part of the filter, set it to value
  1127. if (vName in setup.filters) varValue=setup.filters[vName];
  1128. let isLookup=("lookup" in field);
  1129. print(fName+' v='+varValue+'/'+isLookup+' ['+
  1130. setup.getInputId(field.name)+']');
  1131. let vType=field.type;
  1132. let id=setup.getInputId(vName);
  1133. let input=config.document.getElementById(id);
  1134. //date
  1135. if (vType=="date"){
  1136. if (varValue==="UNDEF") varValue=new Date();
  1137. else varValue=new Date(varValue);
  1138. }
  1139. //lookup for readonly
  1140. if (isLookup && varValue!="UNDEF"){
  1141. varValue=getLookupLabel(field,varValue);
  1142. }
  1143. print('Element: '+input);
  1144. //figure out the element type
  1145. let eType=input.nodeName.toLowerCase();
  1146. print('Element type: '+eType);
  1147. print('Value '+varValue);
  1148. //change varValue for printing
  1149. if (varValue=="UNDEF") varValue="";
  1150. //HTMLTextArea, createElement(textArea)
  1151. if (eType==="textarea"){
  1152. input.value=varValue;
  1153. return;
  1154. }
  1155. //Text, createTextNode
  1156. if (eType==="#text"){
  1157. input.nodeValue=varValue;
  1158. return;
  1159. }
  1160. //HTMLLabelElement, createElement('label')
  1161. if (eType==="label"){
  1162. input.innerText=varValue;
  1163. return;
  1164. }
  1165. //HTMLSelectElement, createElement('select')
  1166. if (eType==="select"){
  1167. input.selectedIndex=0;
  1168. for (let i=0;i<input.options.length;i++){
  1169. let v=input.options[i].text;
  1170. if (v!=varValue) continue;
  1171. input.selectedIndex=i;
  1172. break;
  1173. }
  1174. return;
  1175. }
  1176. if (eType!="input"){
  1177. print('Unknown type: '+eType+' encountered, igonring');
  1178. return;
  1179. }
  1180. //HTMLInputElement
  1181. let type=input.type;
  1182. if (type=="date"){
  1183. input.valueAsDate=varValue;
  1184. return;
  1185. }
  1186. //string,float
  1187. if (type=="text"){
  1188. input.value=varValue;
  1189. return;
  1190. }
  1191. //boolean
  1192. if (type=="checkbox"){
  1193. input.checked=varValue;
  1194. return;
  1195. }
  1196. print('Unknown input type: '+type+'. Ignoring.');
  1197. }
  1198. function populateTable(listName,writeMode,setup){
  1199. //function populateTable(formSetupEntry){
  1200. //let listName=config.formConfig.queryMap[formSetupEntry['queryName']];
  1201. //let accessMode=config.formConfig.operator+'Mode';
  1202. //let writeMode=formSetupEntry[accessMode]=='EDIT';
  1203. let fName='[populateTable/'+listName+']';
  1204. print(fName+' setup '+setup);
  1205. //data snapshot
  1206. let fQuery=config.formConfig.dataQueries[listName];
  1207. //here I assume that listName was parsed during setDataLayout and setData
  1208. //so that rows was set (even if they are empty)
  1209. print(fName+"]: nrows "+fQuery.rows.length);
  1210. //this makes sure that matching versus set variables is performed
  1211. //only entry with matching value of setVariable will be selected
  1212. let entry=selectEntry(fQuery.rows,setup.setVariables);
  1213. let fields=fQuery.fields;
  1214. let helpRows=new Array();
  1215. for (f in fields){
  1216. let field=fields[f];
  1217. //each field is a new row
  1218. print(fName+": Adding field: "+f+'/'+field.name+' hidden: '+field.hidden);
  1219. if (field.hidden) continue;
  1220. if (field.name=="crfRef") continue;
  1221. populateFieldRow(entry,field,setup);
  1222. let helpItem=getHelpItem(field,setup);
  1223. if (helpItem) helpRows.push(helpItem);
  1224. }
  1225. populateHelp(listName,helpRows,setup);
  1226. }
  1227. function getHelpVar(queryName,code,setup){
  1228. let fName='[getHelpVar]';
  1229. print(fName+' code '+code);
  1230. let subs=code.match(/_[^_]*_/g);
  1231. if (!subs){
  1232. print(fName+' no match for '+code);
  1233. return code;
  1234. }
  1235. let rpc=new Object();
  1236. for (let i=0;i<subs.length;i++){
  1237. let c=subs[i].replace(/_/g,'');
  1238. print(fName+' ['+i+'] '+c);
  1239. let qf=c.match(/\[[^\]]*\]/g);
  1240. let lField=undefined;
  1241. if (qf){
  1242. qf[0]=qf[0].replace(/[\[\]]/g,'');
  1243. lField=qf[0];
  1244. print(fName+' lField '+lField);
  1245. }
  1246. //drop field
  1247. c=c.replace(/\[[^\]]*\]/,'');
  1248. //field in lookup (if not lookup.keyColumn)
  1249. //query
  1250. let query=queryName;
  1251. let vField=c;
  1252. let qq=c.split(':');
  1253. if (qq.length>1){
  1254. query=qq[0];
  1255. vField=qq[1];
  1256. }
  1257. let value='';
  1258. if (vField in setup.setVariables){
  1259. value=setup.setVariables[vField];
  1260. }
  1261. else{
  1262. //robustify
  1263. if (!(query in config.formConfig.dataQueries)){
  1264. print(fName+' query '+query+' not available, check configuration');
  1265. return "INVALID";
  1266. }
  1267. if (config.formConfig.dataQueries[query].rows.length==0){
  1268. print(fName+' returning INVALID');
  1269. return "INVALID";
  1270. }
  1271. value=config.formConfig.dataQueries[query].rows[0][vField];
  1272. }
  1273. print(fName+' query '+query+' vField '+vField+' value '+value);
  1274. if (lField==undefined){
  1275. rpc[subs[i]]=value;
  1276. continue;
  1277. }
  1278. let fQuery=config.formConfig.dataQueries[query];
  1279. //variable vField must be a lookup
  1280. //let field=fQuery.fields[vField];
  1281. let field=undefined;
  1282. for (let f in fQuery.fields){
  1283. if (fQuery.fields[f].name==vField){
  1284. field=fQuery.fields[f];
  1285. }
  1286. }
  1287. let lookup=config.formConfig.lookup[field.lookup.queryName];
  1288. for (let j=0;j<lookup.rows.length;j++){
  1289. let o=lookup.rows[j];
  1290. if (o[lookup.keyColumn]==value){
  1291. rpc[subs[i]]=o[lField];
  1292. break;
  1293. }
  1294. }
  1295. }
  1296. for (let x in rpc){
  1297. code=code.replace(x,rpc[x]);
  1298. }
  1299. print(fName+' returning '+code);
  1300. return code;
  1301. }
  1302. function getHelpItem(field,setup){
  1303. if (!("help" in setup)) return undefined;
  1304. let fName='[getHelpItem]';
  1305. print(fName);
  1306. for (let i=0;i<setup["help"].rows.length;i++){
  1307. if (setup["help"].rows[i]["variable"]==field.name){
  1308. helpItem=new Object();
  1309. helpItem.setup=setup["help"].rows[i];
  1310. helpItem.field=field;
  1311. print(fName+' adding help for '+field.name);
  1312. return helpItem;
  1313. }
  1314. }
  1315. return undefined;
  1316. }
  1317. function generateHelp(tb,helpRows,setup){
  1318. let fName='[generateHelp]';
  1319. for (let i=0; i<helpRows.length; i++){
  1320. let eh=helpRows[i];
  1321. let row=tb.insertRow();
  1322. let th=config.document.createElement('th');
  1323. row.appendChild(th);
  1324. th.innerHTML=eh.setup['helpTitle'];
  1325. let cell=row.insertCell();
  1326. cell.setAttribute('colspan','3');
  1327. let el=config.document.createElement('textarea');
  1328. //el.id=sectionId+'_help';
  1329. el.cols="70";
  1330. el.rows="10";
  1331. el.value="Loading";
  1332. el.id=setup.sectionId+"_help"+eh.setup['Key'];
  1333. print(fName+' creating '+el.id);
  1334. cell.appendChild(el);
  1335. }
  1336. }
  1337. function populateHelp(listName,helpRows,setup){
  1338. let fName='[populateHelp]';
  1339. print(fName);
  1340. for (let i=0; i<helpRows.length; i++){
  1341. let eh=helpRows[i];
  1342. let id=setup.sectionId+'_help'+eh.setup['Key']
  1343. let el=config.document.getElementById(id);
  1344. let lookup=eh.field['lookup'];
  1345. let qName=lookup.queryName;
  1346. let tLookup=config.formConfig.lookup[qName];
  1347. let varName=getHelpVar(listName,eh.setup['fieldDescriptor'],setup);
  1348. if (varName=="INVALID"){
  1349. el.value="Please select patient/timepoint";
  1350. continue;
  1351. }
  1352. let text="";
  1353. for (let j=0;j<tLookup.rows.length;j++){
  1354. //print(tLookup.rows[j][tLookup.keyColumn]+' '+tLookup.rows[j][tLookup.displayColumn]+
  1355. //' '+tLookup.rows[j][varName]);
  1356. text+=tLookup.rows[j][tLookup.displayColumn]+" - "+tLookup.rows[j][varName]+"\n";
  1357. }
  1358. print(fName+' setting '+id+': '+el);
  1359. el.value=text;
  1360. }
  1361. }
  1362. function generateTable(listName,divName,additionalData,setup){
  1363. let debug=true;
  1364. let fName="[generateTable/"+listName+"]";
  1365. if (debug) print(fName);
  1366. //is listName and setup.queryName a duplicate of the same value
  1367. print(fName+': setup.queryName '+setup.queryName);
  1368. //assume data is set in config.formConfig.dataQueries[data.queryName].rows;
  1369. //data snapshot
  1370. let fQuery=config.formConfig.dataQueries[listName];
  1371. //here I assume that listName was parsed during setDataLayout and setData
  1372. //so that rows was set (even if they are empty)
  1373. print(fName+": Nrows "+fQuery.rows.length);
  1374. let entry=selectEntry(fQuery.rows,setup.setVariables);
  1375. let tb=config.document.createElement('table');
  1376. tb.className="t2";
  1377. config.document.getElementById(divName).appendChild(tb);
  1378. //this are the fields (probably constant)
  1379. let fields=fQuery.fields;
  1380. let helpRows=new Array();
  1381. for (f in fields){
  1382. let field=fields[f];
  1383. //each field is a new row
  1384. print(fName+": Adding field: "+f+'/'+field.name);
  1385. if (field.hidden) continue;
  1386. if (field.name=="crfRef") continue;
  1387. //do not expose fields indicated in setVariables
  1388. if (field.name in setup.setVariables) continue;
  1389. addFieldRow(tb,field,setup,additionalData);
  1390. populateFieldRow(entry,field,setup);
  1391. let helpItem=getHelpItem(field,setup);
  1392. if (helpItem) helpRows.push(helpItem);
  1393. }
  1394. generateHelp(tb,helpRows,setup);
  1395. populateHelp(listName,helpRows,setup);
  1396. //add comment field
  1397. if (!("addApply" in setup)) {
  1398. print(fName+"generateTable: done");
  1399. return;
  1400. }
  1401. let row=tb.insertRow();
  1402. let th=config.document.createElement('th');
  1403. row.appendChild(th);
  1404. th.innerHTML=setup.addApply;
  1405. let cell=row.insertCell();
  1406. //cell.setAttribute("colspan","2");
  1407. let input=config.document.createElement("input");
  1408. input.type="button";
  1409. input.value=setup.addApply;
  1410. cell.appendChild(input);
  1411. let cell1=row.insertCell();
  1412. cell1.setAttribute("colspan","2");
  1413. cell1.id=setup.getInputId("rerviewLastSave");
  1414. cell1.innerHTML="No recent update";
  1415. //saveReview is a generic name for saving content of the html page to a list entry
  1416. input.onclick=function(){saveReview(listName,cell1.id,setup)};
  1417. }
  1418. function saveReview(queryName,elementId,setup){
  1419. //loads any queryName
  1420. let debug=true;
  1421. if (debug) print("saveReview: elementId "+elementId+" queryName "+queryName);
  1422. let useInsert=false;
  1423. if (!("unique" in setup)) useInsert=true;
  1424. let fQuery=config.formConfig.dataQueries[queryName];
  1425. let entry=selectEntry(fQuery.rows,setup.setVariables);
  1426. if ("notValid" in entry) useInsert=true;
  1427. if (useInsert) entry=new Object();
  1428. entry.crfRef=getCRFrefData();
  1429. if (debug) print("Set crfRef="+entry.crfRef);
  1430. //if ("queryName" in setup.filters) {
  1431. // entry.queryName=setup.filters["queryName"];
  1432. // if (debug) print("Setting queryName: "+entry.queryName);
  1433. //}
  1434. let fields=fQuery.fields;
  1435. for (f in fields){
  1436. let field=fields[f];
  1437. if (debug) print("saveReview field: "+field.name);
  1438. if (field.hidden) continue;
  1439. let vName=field.name;
  1440. let vType=field.type;
  1441. if (debug) print("vType: "+vType);
  1442. if (vName=="crfRef") continue;
  1443. //need to save queryName for reviewComments
  1444. let eId=setup.getInputId(vName);
  1445. let el=config.document.getElementById(eId);
  1446. if (!el) {
  1447. if (debug) print("saveReview element: "+eId+" not found");
  1448. continue;
  1449. }
  1450. if (debug) print("saveReview element: "+eId);
  1451. let eType=el.nodeName.toLowerCase();
  1452. if (eType==="select"){
  1453. entry[vName]=el.options[el.selectedIndex].value;
  1454. continue;
  1455. }
  1456. if (eType==="td"){
  1457. entry[vName]=el.innerText;
  1458. continue;
  1459. }
  1460. if (vType=="date"){
  1461. let date=el.valueAsDate;
  1462. if (date==="null") continue;
  1463. date.setUTCHours(12);
  1464. entry[vName]=date.toString();
  1465. print("Setting date to "+entry[vName]);
  1466. continue;
  1467. }
  1468. if (vType=="string"){
  1469. entry[vName]=el.value;
  1470. if (vName.search('_file_')<0)
  1471. continue;
  1472. //upload file
  1473. let id1=eId+'_file_';
  1474. let input1=config.document.getElementById(id1);
  1475. print('Attachment field: '+input1.value);
  1476. //entry[vName]=el.files[0].stream();
  1477. let ctx=new Object();
  1478. ctx['dirName']='consent';
  1479. ctx['ID']=entry['crfRef'];
  1480. //should point to data container
  1481. ctx['project']=getContainer('data');
  1482. //need ID->crf!
  1483. //assume crfRef will get set before this
  1484. //element is encountered
  1485. uploadFile(input1,ctx);
  1486. let fv=el.value;
  1487. let suf=fv.split('.').pop();
  1488. entry[vName]=entry['crfRef']+'.'+suf;
  1489. continue;
  1490. }
  1491. if (vType=="float" || vType=="int"){
  1492. entry[vName]=el.value;
  1493. if (vName=="queryName") {
  1494. print('Parsing queryName: '+el.innerText);
  1495. entry[vName]=config.formConfig.fields[el.innerText].queryId;
  1496. //use queryMap lookup
  1497. }
  1498. continue;
  1499. }
  1500. if (vType=="boolean"){
  1501. entry[vName]=el.checked;
  1502. continue;
  1503. }
  1504. }
  1505. //update values from setup.setVariables
  1506. for (v in setup.setVariables){
  1507. entry[v]=setup.setVariables[v];
  1508. }
  1509. let qconfig=new Object();
  1510. qconfig.rows=[entry];
  1511. //should point to data container
  1512. qconfig.containerPath=getContainer('data');
  1513. qconfig.schemaName='lists';
  1514. qconfig.queryName=queryName;
  1515. //only update comments
  1516. print("modifyRows: useInsert "+useInsert);
  1517. qconfig.success=function(data){updateLastSavedFlag(data,setup,elementId)};
  1518. if (!useInsert){
  1519. LABKEY.Query.updateRows(qconfig);
  1520. }
  1521. else{
  1522. LABKEY.Query.insertRows(qconfig);
  1523. }
  1524. }
  1525. function updateLastSavedFlag(data,setup,elementId){
  1526. let debug=true;
  1527. if (debug) print("Update last saved flag to "+elementId);
  1528. let el=config.document.getElementById(elementId);
  1529. let dt=new Date();
  1530. el.innerHTML="Last saved "+dt.toString();
  1531. if (data.queryName=="reviewComments"){
  1532. updateListDisplay(setup.divReviewListId,"reviewComments",setup.filters,true);
  1533. }
  1534. //refresh stored data!
  1535. let writeMode=!setup.readonlyFlag();
  1536. if ("unique" in setup)
  1537. setData(function (){populateTable(data.queryName,writeMode,setup);});
  1538. if ("masterQuery" in setup){
  1539. let ad=config.formConfig.additionalData[setup.masterQuery];
  1540. print('Updating list display: '+setup.queryName+'/'+ad.queryName);
  1541. updateListDisplay(ad.divQueryName,ad.queryName,ad.filters,false);
  1542. }
  1543. }
  1544. //******************************************upload to database *********************
  1545. function onDatabaseUpload(){
  1546. let fName='[onDatabaseUpload]';
  1547. print(fName);
  1548. config.upload=new Object();
  1549. //figure out the participantId
  1550. let masterQueryId=config.formConfig.form["masterQuery"];
  1551. print(fName+': master query: '+masterQueryId);
  1552. let pidClass=new Object();
  1553. pidClass.afterId=afterParticipantId;
  1554. //use stored name of participantField
  1555. //for migrating data from lists to study
  1556. //this is to avoid conflicts in column assignments
  1557. //since datasets contain all fields of a list plus
  1558. //default fields of which participantId is one
  1559. pidClass.participantField=config.registrationParticipantIdField;
  1560. let qconfig=new Object();
  1561. qconfig.queryName=config.formConfig.queryMap[masterQueryId];
  1562. //queryMap holds mapping for queries in visit;
  1563. //masterQuery should be one of them, so this is safe.
  1564. qconfig.schemaName='lists';
  1565. qconfig.containerPath=getContainer('data');
  1566. qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref())];
  1567. qconfig.success=function(data){afterRegistration(data,pidClass);};
  1568. LABKEY.Query.selectRows(qconfig);
  1569. //waitForCompleteUpload(config);//
  1570. }
  1571. function afterRegistration(data,fc){
  1572. let fName='[afterRegistration/'+data.queryName+']';
  1573. print(fName+": rows:"+data.rows.length);
  1574. fc.registration=data;
  1575. let registrationData=fc.registration;
  1576. clearErr();
  1577. if (registrationData.rows.length!=1){
  1578. let msg=fName+": ERROR: Found "+registrationData.rows.length;
  1579. msg+=" registration entries for crfrefid "+getCRFref();
  1580. print(msg);
  1581. fc.afterId(fc);
  1582. return;
  1583. }
  1584. print(fName+'registration participant field: '+fc.participantField);
  1585. fc.participantId=registrationData.rows[0][fc.participantField];
  1586. //could be a lookup field (particularly for studies)
  1587. print('ID: '+fc.participantId);
  1588. let fields=registrationData.metaData.fields;
  1589. let field="NONE";
  1590. for (f in fields){
  1591. if (fields[f]["name"]==fc.participantField)
  1592. field=fields[f];
  1593. }
  1594. if ("lookup" in field){
  1595. let pid=fc.participantId;
  1596. print("Using lookup for participantId: "+pid);
  1597. let lookup=field["lookup"];
  1598. print("Lookup: ["+lookup.schemaName+','+lookup.queryName+']');
  1599. let qconfig=new Object();
  1600. //should point to data container
  1601. qconfig.containerPath=getContainer('data');
  1602. qconfig.schemaName=lookup.schemaName;
  1603. qconfig.queryName=lookup.queryName;
  1604. qconfig.filterArray=
  1605. [LABKEY.Filter.create(lookup.keyColumn,pid)];
  1606. qconfig.success=function(data){
  1607. afterRegistrationLookup(data,lookup.displayColumn,fc)};
  1608. LABKEY.Query.selectRows(qconfig);
  1609. }
  1610. else{
  1611. //afterParticipantId(configUpload);
  1612. fc.afterId(fc);
  1613. }
  1614. }
  1615. function afterRegistrationLookup(data,displayColumn,fc){
  1616. print("afterRegistrationLookup");
  1617. let entry=data.rows[0];
  1618. fc.participantId=entry[displayColumn];
  1619. print('Setting to '+fc.participantId);
  1620. fc.afterId(fc);
  1621. //afterParticipantId(configUpload);
  1622. }
  1623. function afterParticipantId(fc){
  1624. print("Setting participantId to "+fc.participantId);
  1625. config.upload.participantId=fc.participantId;
  1626. //another select rows to update all queries from setup
  1627. //just use registration for test
  1628. let formSetupRows=config.formConfig.formSetupRows;
  1629. config.upload.queries=new Array();
  1630. print("Form rows: "+formSetupRows.length);
  1631. for (let i=0;i<formSetupRows.length;i++){
  1632. let entry=formSetupRows[i];
  1633. //skip reviews
  1634. if (entry.showFlag=="REVIEW") continue;
  1635. //use lookup table to convert from id to name
  1636. let queryName=config.formConfig.queryMap[entry.queryName];
  1637. config.upload.queries.push({queryName:queryName,queryStatus:"QUEUED"});
  1638. print('form ['+i+']='+queryName+' '+entry.showFlag+'/'+entry.showQuery);
  1639. if (entry.showQuery=="NONE")
  1640. continue;
  1641. config.upload.queries.push({queryName:entry.showQuery,queryStatus:"QUEUED"});
  1642. }
  1643. //add reviews
  1644. config.upload.queries.push({queryName:"reviewComments",queryStatus:"QUEUED"});
  1645. config.upload.queryId=0;
  1646. copyToDataset();
  1647. }
  1648. function copyToDataset(){
  1649. let fName='[copyToDataset]: ';
  1650. print(fName+'['+config.upload.queryId+'/'+config.upload.queries.length+']');
  1651. //watch dog + scheduler
  1652. //
  1653. //watchdog part
  1654. if (config.upload.queryId==config.upload.queries.length) {
  1655. print(fName+'completing');
  1656. let targetStatus=config.formConfig.targetStatus['onDatabaseUpload'];
  1657. let targetRecipient=config.formConfig.targetRecipient['onDatabaseUpload'];
  1658. let action=new Object();
  1659. action.name='onDatabaseUpload';
  1660. action.cb=function(data){sendEmail(data,targetRecipient,redirect,'Form uploaded');}
  1661. updateFlag(targetStatus,action);//Approved
  1662. return;
  1663. }
  1664. //scheduler
  1665. let queryName=config.upload.queries[config.upload.queryId].queryName;
  1666. print("copyToDataset["+config.upload.queryId+"/"+
  1667. config.upload.queries.length+"]: "+queryName);
  1668. let qconfig=new Object();
  1669. qconfig.queryName=queryName;
  1670. qconfig.schemaName="lists";
  1671. qconfig.containerPath=getContainer('data');
  1672. qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref())];
  1673. qconfig.success=afterListData;
  1674. LABKEY.Query.selectRows(qconfig);
  1675. }
  1676. function afterListData(data){
  1677. let fName='[afterListData]: ';
  1678. let queryName=config.upload.queries[config.upload.queryId].queryName;
  1679. print(fName+" ["+queryName+"/list]: "+data.rows.length+" entries");
  1680. config.upload.queries[config.upload.queryId].listData=data;
  1681. let id=config.upload.participantId;
  1682. let qconfig=new Object();
  1683. qconfig.queryName=queryName;
  1684. qconfig.schemaName="study";
  1685. qconfig.containerPath=getContainer('data');
  1686. qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref())];
  1687. qconfig.filterArray.push(LABKEY.Filter.create('ParticipantId',id));
  1688. qconfig.success=afterStudyData;
  1689. LABKEY.Query.selectRows(qconfig);
  1690. }
  1691. function afterStudyData(data){
  1692. let fName='[afterStudyData]: ';
  1693. let queryObj=config.upload.queries[config.upload.queryId];
  1694. queryObj.studyData=data;
  1695. let msg=fName+"["+queryObj.queryName+"/study]: "+data.rows.length+" entries";
  1696. print(msg);
  1697. let listRows=queryObj.listData.rows;
  1698. //skip uploading an empty set
  1699. if (listRows.length==0){
  1700. printErr("List "+queryObj.queryName+" empty.");
  1701. queryObj.queryStatus="DONE";
  1702. config.upload.queryId+=1;
  1703. //back to watchdog
  1704. copyToDataset();
  1705. return;
  1706. }
  1707. let studyRows=queryObj.studyData.rows;
  1708. for (let i=0;i<studyRows.length;i++){
  1709. let entry=studyRows[i];
  1710. //
  1711. if (! (i<listRows.length) ) continue;
  1712. let entryList=listRows[i];
  1713. //keeps study only variables (ParticipantId, SequenceNum)
  1714. for (let f in entryList) {
  1715. entry[f]=entryList[f];
  1716. print(fName+"Copying ["+f+"]: "+entry[f]+"/"+entryList[f]);
  1717. }
  1718. }
  1719. print(fName+' copying completed');
  1720. if (studyRows.length>0) {
  1721. let qconfig=new Object();
  1722. qconfig.queryName=queryObj.queryName;
  1723. qconfig.schemaName="study";
  1724. qconfig.rows=studyRows;
  1725. qconfig.containerPath=getContainer('data');
  1726. qconfig.success=afterStudyUpload;
  1727. LABKEY.Query.updateRows(qconfig);
  1728. print(fName+'updateRows sent');
  1729. }
  1730. else{
  1731. let data=new Object();
  1732. data.rows=new Array();
  1733. afterStudyUpload(data);
  1734. }
  1735. }
  1736. function afterStudyUpload(data){
  1737. let fName='[afterStudyUpload] ';
  1738. print(fName);
  1739. //let participantField=config.participantField;
  1740. let participantField=config.formConfig.studyData["SubjectColumnName"];
  1741. print(fName+' participantField: '+participantField);
  1742. let queryObj=config.upload.queries[config.upload.queryId];
  1743. let queryName=queryObj.queryName;
  1744. printErr("Updated "+data.rows.length+" rows to "+queryName);
  1745. let studyRows=queryObj.studyData.rows;
  1746. let listRows=queryObj.listData.rows;
  1747. let rows=new Array();
  1748. //also updating existing rows, if they exist
  1749. for (let i=studyRows.length;i<listRows.length;i++){
  1750. let entry=listRows[i];
  1751. //make sure you have the participantField right
  1752. //
  1753. entry[participantField]=config.upload.participantId;
  1754. entry.crfRef=getCRFref();
  1755. entry.SequenceNum=getCRFref();
  1756. entry.SequenceNum=entry.SequenceNum % 1000000000;
  1757. if (listRows.length>1){
  1758. entry.SequenceNum+=i/100;
  1759. }
  1760. print( "Adding sequence number "+entry.SequenceNum);
  1761. rows.push(entry);
  1762. }
  1763. if (rows.length>0){
  1764. let qconfig=new Object();
  1765. qconfig.queryName=queryName;
  1766. qconfig.schemaName="study";
  1767. qconfig.containerPath=getContainer('data');
  1768. qconfig.success=afterListUpload;
  1769. qconfig.rows=rows;
  1770. LABKEY.Query.insertRows(qconfig);
  1771. }
  1772. else{
  1773. let data=new Object();
  1774. data.rows=rows;
  1775. afterListUpload(data);
  1776. }
  1777. }
  1778. function afterListUpload(data){
  1779. let queryObj=config.upload.queries[config.upload.queryId];
  1780. let queryName=queryObj.queryName;
  1781. printErr("Inserted "+data.rows.length+" rows to "+queryName);
  1782. queryObj.queryStatus="DONE";
  1783. config.upload.queryId+=1;
  1784. copyToDataset();
  1785. }
  1786. //*************************update for further review *************************
  1787. function onUpdateForReview(){
  1788. let targetStatus=config.formConfig.targetStatus['onUpdateForReview'];
  1789. let targetRecipient=config.formConfig.targetRecipient['onUpdateForReview'];
  1790. let action=new Object();
  1791. action.name='onUpdateForReview';
  1792. action.cb=function(data){sendEmail(data,targetRecipient,redirect,'Form updated for review');};
  1793. updateFlag(targetStatus,action);
  1794. }
  1795. function updateFlag(flag,action){
  1796. let qconfig=new Object();
  1797. qconfig.schemaName='lists';
  1798. qconfig.queryName='crfEntry';
  1799. qconfig.containerPath=getContainer('data');
  1800. qconfig.success=function(data){setFlag(data,flag,action);}
  1801. qconfig.filterArray=[LABKEY.Filter.create("entryId",getCRFref())];
  1802. LABKEY.Query.selectRows(qconfig);
  1803. }
  1804. function setFlag(data,flag,action){
  1805. let fName='[setFlag]';
  1806. let debug=true;
  1807. if (data.rows.length!=1){
  1808. let msg=fName+": ERROR: Found "+data.rows.length;
  1809. msg+=" entries for crfrefid "+getCRFref();
  1810. print(msg);
  1811. return;
  1812. }
  1813. let entry=data.rows[0];
  1814. entry.FormStatus=flag;
  1815. let uId=config.formConfig.currentUser.UserId;
  1816. entry[config.formConfig.operator]=uId;
  1817. print(fName+': Form: '+entry.Form);
  1818. print(fName+": set form status to "+entry.FormStatus);
  1819. let qconfig=new Object();
  1820. qconfig.schemaName='lists';
  1821. qconfig.queryName='crfEntry';
  1822. qconfig.containerPath=getContainer('data');
  1823. qconfig.rows=[entry];
  1824. //qconfig.success=function(data){completeWithFlag(data,flag);}
  1825. qconfig.success=function(data){completeWithFlag(data,action);};
  1826. LABKEY.Query.updateRows(qconfig);
  1827. }
  1828. function completeWithFlag(data,action){
  1829. let fName='[completeWithFlag]';
  1830. print(fName+': nrows '+data.rows.length);
  1831. let fentry=data.rows[0];
  1832. print(fName+': form status '+fentry.FormStatus);
  1833. print(fName+': form '+fentry.Form);
  1834. let entry=new Object();
  1835. entry.entryId=getCRFref();
  1836. entry.submissionDate=new Date();
  1837. entry.FormStatus=fentry.FormStatus;
  1838. entry.User=config.formConfig.currentUser.UserId;
  1839. entry.Form=fentry.Form;
  1840. entry.operator=config.formConfig.operator;
  1841. entry.action=action.name;
  1842. let qconfig=new Object();
  1843. qconfig.schemaName='lists';
  1844. qconfig.queryName='crfStatus';
  1845. qconfig.containerPath=getContainer('data');
  1846. qconfig.rows=[entry];
  1847. //qconfig.success=function(data){completeWithFlag(data,flag);}
  1848. qconfig.success=action.cb;
  1849. LABKEY.Query.insertRows(qconfig);
  1850. }
  1851. //************************************************ submit *******************************************
  1852. function onSubmit(){
  1853. //update list storage and change status
  1854. let debug=true;
  1855. hideErr();
  1856. clearErr();
  1857. printErr("onSubmit");
  1858. setData(verifyData);
  1859. }
  1860. function verifyData(){
  1861. let queries=config.formConfig.dataQueries;
  1862. for (q in queries){
  1863. let qData=queries[q];
  1864. if (q=="reviewComments") continue;
  1865. //if it doesn't have additionalData, it is a sub query
  1866. if (!(q in config.formConfig.additionalData))
  1867. continue;
  1868. if (qData.rows.length<1){
  1869. printErr('Missing entry for query '+q);
  1870. return false;
  1871. }
  1872. }
  1873. let targetStatus=config.formConfig.targetStatus['onSubmit'];
  1874. let targetRecipient=config.formConfig.targetRecipient['onSubmit'];
  1875. print('verifyStatus: targetStatus: '+targetStatus);
  1876. let fName='verifyStatus';
  1877. //useful for debug
  1878. //let finalStep=doNothing;
  1879. //production mode
  1880. let finalStep=redirect;
  1881. let action=new Object();
  1882. action.name='onSubmit';
  1883. action.cb=function(data){sendEmail(data,targetRecipient,finalStep,'Form sumbitted');};
  1884. updateFlag(targetStatus,action);
  1885. }
  1886. function getEmail(recipientCode){
  1887. print('getEmail w/'+recipientCode);
  1888. let recipients=new Array();
  1889. let typeTo=LABKEY.Message.recipientType.to;
  1890. let create=LABKEY.Message.createRecipient;
  1891. let currentUser=config.formConfig.currentUser;
  1892. let formCreator=config.formConfig.formCreator;
  1893. let currentSite=config.formConfig.currentSite;
  1894. let userRows=config.formConfig.userRows;
  1895. let parentUser=undefined;
  1896. if ("parentCrfData" in config.formConfig){
  1897. let parentCrf=config.formConfig.parentCrfData;
  1898. parentUser=getUser(parentCrf.rows[0].UserId,'parentUser');
  1899. }
  1900. let recipientCategories=recipientCode.split(',');
  1901. for (let i=0;i<recipientCategories.length;i++){
  1902. let recipient=recipientCategories[i];
  1903. print('Checking '+recipient);
  1904. if (recipient=='crfEditor'){
  1905. print('Adding :'+formCreator.Email);
  1906. recipients.push(create(typeTo,formCreator.Email));
  1907. if (parentUser==undefined) continue;
  1908. print('Adding :'+parentUser.Email);
  1909. recipients.push(create(typeTo,parentUser.Email));
  1910. continue;
  1911. }
  1912. //Monitor or Sponsor
  1913. let fList=recipient+'s';
  1914. let fRows=config.formConfig[fList];
  1915. for (let i=0;i<fRows.length;i++){
  1916. print('Checking '+fRows[i].User+'/'+fRows[i].Site);
  1917. if (fRows[i].Site!=currentSite.siteNumber) continue;
  1918. for (let j=0;j<userRows.length;j++){
  1919. if (userRows[j].UserId!=fRows[i].User) continue;
  1920. print('Adding :'+userRows[j].Email);
  1921. recipients.push(create(typeTo,userRows[j].Email));
  1922. break;
  1923. }
  1924. }
  1925. }
  1926. return recipients;
  1927. }
  1928. function sendEmail(data,recipient='crfEditor',cb=redirect,subj='Form submitted'){
  1929. print('sendEmail; recipient: '+recipient);
  1930. let st=config.formConfig.settings;
  1931. let cvar='sendEmail';
  1932. if (cvar in st){
  1933. print(cvar+' set to '+st[cvar]);
  1934. if (st[cvar]=='FALSE'){
  1935. print('Skipping sending emails');
  1936. redirect();
  1937. return;
  1938. }
  1939. }
  1940. print('send email '+data.rows.length);
  1941. let crf=data.rows[0]['entryId'];
  1942. let formId=data.rows[0]['Form'];
  1943. let link=LABKEY.ActionURL.getBaseURL();
  1944. link+=LABKEY.ActionURL.getContainer();
  1945. link+='/crf-visit.view?';
  1946. link+='entryId='+crf;
  1947. link+='&formId='+formId;
  1948. link+='&role='+recipient;
  1949. //debug
  1950. let recipients=getEmail(recipient);
  1951. //from crfManagers list
  1952. let typeHtml=LABKEY.Message.msgType.html;
  1953. let typePlain=LABKEY.Message.msgType.plain;
  1954. let msg1=LABKEY.Message.createMsgContent(typePlain,link);
  1955. //let cb=doNothing;
  1956. //let cb=redirect;
  1957. LABKEY.Message.sendMessage({
  1958. msgFrom:'labkey@fmf.uni-lj.si',
  1959. msgSubject:subj,
  1960. msgRecipients:recipients,
  1961. msgContent:[msg1],
  1962. success: cb
  1963. });
  1964. }
  1965. function hideErr(){
  1966. let el=config.document.getElementById("errorDiv");
  1967. el.style.display="none";
  1968. }
  1969. function clearErr(){
  1970. let el=config.document.getElementById("errorTxt");
  1971. el.value="";
  1972. }
  1973. function showErr(){
  1974. let el=config.document.getElementById("errorDiv");
  1975. el.style.display="block";
  1976. }
  1977. function printErr(msg){
  1978. showErr();
  1979. el=config.document.getElementById("errorTxt");
  1980. el.style.color="red";
  1981. el.value+="\n"+msg;
  1982. }
  1983. //**************************************************
  1984. //
  1985. function onRemoveCRF(){
  1986. let debug=true;
  1987. if (debug){
  1988. print("Removing CRF");
  1989. }
  1990. let selectRows=new Object();
  1991. //points to data container
  1992. selectRows.containerPath=getContainer('config');
  1993. selectRows.schemaName="lists";
  1994. selectRows.queryName="inputLists";
  1995. selectRows.success=afterInputLists;
  1996. LABKEY.Query.selectRows(selectRows);
  1997. }
  1998. function afterInputLists(data){
  1999. let debug=true;
  2000. if (debug)
  2001. print("After input lists");
  2002. config.inputLists=data;
  2003. config.inputListsIterator=0;
  2004. removeCRFLoop();
  2005. }
  2006. function removeCRFLoop(){
  2007. let debug=true;
  2008. let i=config.inputListsIterator;
  2009. let iMax=config.inputLists.rows.length;
  2010. //in fact, we are adding two additional passages of the loop, one for
  2011. //crfEntry, the second for the same query, but using parentCrf as the
  2012. //selection variable
  2013. let iTotal=iMax+1;
  2014. //let iTotal=iMax+1;
  2015. if (debug)
  2016. print("removeCRFLoop ["+i+"/"+iMax+"]");
  2017. if (i>iTotal){
  2018. if (0) return;
  2019. redirect();
  2020. }
  2021. let queryName="crfEntry";
  2022. let idVar="entryId";
  2023. let idValue=getCRFref();
  2024. if (i<iMax){
  2025. //in all but crfEntry, variable is called crfRef
  2026. queryName=config.inputLists.rows[i].queryName;
  2027. idVar="crfRef";
  2028. }
  2029. //for i=iMax and i=iMax+1, query is crfEntry.
  2030. //delete also crfEntries where parentCrf is set to crf that we are deleting
  2031. if (i==iTotal){
  2032. idVar='parentCrf';
  2033. }
  2034. if (debug)
  2035. print("["+i+"/"+iMax+"] "+queryName+":"+idVar+'/'+idValue);
  2036. let selectRows=new Object();
  2037. //points to data container
  2038. selectRows.containerPath=getContainer('data');
  2039. selectRows.schemaName="lists";
  2040. selectRows.queryName=queryName;
  2041. selectRows.filterArray=[LABKEY.Filter.create(idVar,idValue)];
  2042. selectRows.success=removeListCRF;
  2043. selectRows.failure=skipListCRF;
  2044. LABKEY.Query.selectRows(selectRows);
  2045. }
  2046. function removeListCRF(data){
  2047. let debug=true;
  2048. if (debug)
  2049. print(data.queryName+": "+data.rows.length);
  2050. config.inputListsIterator+=1;
  2051. if (data.rows.length==0){
  2052. removeCRFLoop();
  2053. return;
  2054. }
  2055. let deleteRows=new Object();
  2056. //points to data container
  2057. deleteRows.containerPath=getContainer('data');
  2058. deleteRows.schemaName=data.schemaName;
  2059. deleteRows.queryName=data.queryName;
  2060. deleteRows.success=function(data){removeCRFLoop()};
  2061. deleteRows.rows=data.rows;
  2062. LABKEY.Query.deleteRows(deleteRows);
  2063. }
  2064. function skipListCRF(errorInfo){
  2065. let debug=true;
  2066. if (debug)
  2067. print("Error in removeCRF: "+errorInfo.exception);
  2068. config.inputListsIterator+=1;
  2069. removeCRFLoop();
  2070. }
  2071. function redirect(){
  2072. let debug=false;
  2073. let formUrl="begin";
  2074. let params=new Object();
  2075. params.name=formUrl;
  2076. params.pageId="CRF";
  2077. //points to crf container
  2078. let containerPath=getContainer('CRF');
  2079. // This changes the page after building the URL.
  2080. //Note that the wiki page destination name is set in params.
  2081. var homeURL = LABKEY.ActionURL.buildURL(
  2082. "project", formUrl , containerPath, params);
  2083. print("Redirecting to "+homeURL);
  2084. if (debug) return;
  2085. window.location = homeURL;
  2086. }
  2087. //printing section
  2088. function checkBlob(){
  2089. print("checkBlob: "+config.blob);
  2090. if (config.blob) {
  2091. clearInterval(config.blobInterval);
  2092. config.a.href = config.window.URL.createObjectURL(config.blob);
  2093. print("HREF: "+config.a.href);
  2094. config.a.download = 'test.pdf';
  2095. config.a.click();
  2096. config.window.URL.revokeObjectURL(config.a.href);
  2097. }
  2098. config.count=config.count+1;
  2099. print("Eval: "+config.count);
  2100. if (config.count>100){
  2101. clearInterval(config.blobInterval);
  2102. }
  2103. }
  2104. function printForm(){
  2105. config.doc=new PDFDocument();
  2106. //config.doc.end();
  2107. let stream = config.doc.pipe(blobStream()).on("finish",function(){
  2108. config.blob=stream.toBlob("application/pdf");});
  2109. print("BLob: "+config.blob);
  2110. config.a = config.document.createElement("a");
  2111. config.document.body.appendChild(config.a);
  2112. config.a.innerHTML="Download PDF";
  2113. config.a.style = "display: none";
  2114. config.count=0;
  2115. //run until blob is set
  2116. config.blobInterval=setInterval(checkBlob,1000);
  2117. //pick data from crfForm list
  2118. print("Printing form");
  2119. printHeader();
  2120. setData(formatPrintData);
  2121. }
  2122. function printHeader(){
  2123. config.doc.fontSize(25).text(config.formConfig.form['formName']);
  2124. config.doc.moveDown();
  2125. let crfEntry=config.formConfig.crfEntry;
  2126. let site=config.formConfig.currentSite;
  2127. let val=new Object();
  2128. let user=config.formConfig.user;
  2129. val['A']={o:crfEntry,f:'EudraCTNumber',t:'Eudra CT Number'};
  2130. val['B']={o:crfEntry,f:'StudyCoordinator',t:'Study Coordinator'};
  2131. val['C']={o:crfEntry,f:'StudySponsor',t:'Study Sponsor'};
  2132. val['D']={o:site,f:'siteName',t:'Site'};
  2133. val['E']={o:site,f:'sitePhone',t:'Phone'};
  2134. val['F']={o:user,f:'DisplayName',t:'Investigator'};
  2135. for (let f in val){
  2136. print('Printing for '+f);
  2137. let e=val[f];
  2138. let entry=new Object();
  2139. entry[f]=e.o[e.f];
  2140. printPDF(entry,
  2141. {name:f,caption:e.t,type:'string'},null);
  2142. }
  2143. config.doc.moveDown();
  2144. }
  2145. function formatPrintData(){
  2146. qS=config.formConfig.dataQueries;
  2147. for (let q in qS){
  2148. print('Setting up '+q);
  2149. let qData=qS[q];
  2150. print('Number of rows: '+qData.rows.length);
  2151. if (qData.rows.length>0){
  2152. config.doc.fontSize(20).text(qData.title);
  2153. }
  2154. for (let i=0;i<qData.rows.length;i++){
  2155. let entry=qData.rows[i];
  2156. for (let f in qData.fields){
  2157. let field=qData.fields[f];
  2158. let lookup=null;
  2159. if (field.lookup){
  2160. lookup=config.formConfig.lookup[field.lookup.queryName];
  2161. }
  2162. if (field.hidden) continue;
  2163. printPDF(entry,field,lookup);
  2164. }
  2165. }
  2166. config.doc.moveDown();
  2167. }
  2168. print("All done");
  2169. config.doc.end();
  2170. }
  2171. function printPDF(entry,field,lookup){
  2172. //object field should have a name, type, caption
  2173. //entry should have field.name
  2174. //lookup is null or has a lookup table LUT
  2175. //for value v of entry[field.name]
  2176. //
  2177. //the total width of a A4 page is 598 px,
  2178. //left margin is 72. With a right margin of 50,
  2179. //the total available with is 476 px.
  2180. let w=476;
  2181. let spacing=25;
  2182. let w1=(w-spacing)*0.5;
  2183. let fontSize=14;
  2184. print('printPDF: entry['+field.name+']='+entry[field.name]);
  2185. let v=entry[field.name];
  2186. if (lookup!=null){
  2187. v=lookup.LUT[v];
  2188. }
  2189. print('printPDF: field type:'+field.type);
  2190. if (field.type=="date"){
  2191. let d=new Date(v);
  2192. v=d.getDate()+'/'+(d.getMonth()+1)+'/'+d.getFullYear();
  2193. }
  2194. if (v===null) v=' / ';
  2195. if (v===undefined) v=' / ';
  2196. //measure text
  2197. let label=field.caption;
  2198. let opt={width:w1};
  2199. config.doc.fontSize(fontSize);
  2200. //for more eloquent display the height of the text
  2201. //can be measured prior to output
  2202. //use currentLineHeight to scale height
  2203. //let lineH=config.doc.currentLineHeight(1);
  2204. //let h=config.doc.heightOfString(label,opt)/lineH;
  2205. //print label
  2206. config.doc.font('Courier').text(label,opt);
  2207. //align last row of description w/ first row of value
  2208. config.doc.moveUp();
  2209. //store x value for later use
  2210. let tx=config.doc.x;
  2211. let ty=config.doc.y;
  2212. //shift for value output
  2213. config.doc.x+=w1+spacing;
  2214. config.doc.font('Courier-Bold').text(v,opt);
  2215. //restore x value
  2216. config.doc.x=tx;
  2217. }
  2218. //master section, entry point from html files
  2219. function generateMasterForm(){
  2220. generateDebugSection();
  2221. //read enviroment from lists disperesed on labkey
  2222. setFormConfig();
  2223. }
  2224. //helper function to set basic parameters on web page
  2225. //(fields defined in html file)
  2226. function populateBasicData(){
  2227. config.document.getElementById('version').innerText=
  2228. config.formConfig.softwareVersion;
  2229. config.document.getElementById('eudraCTNumber').innerText=
  2230. config.formConfig.crfEntry.EudraCTNumber;
  2231. config.document.getElementById('studyCoordinator').innerText=
  2232. config.formConfig.crfEntry.StudyCoordinator;
  2233. config.document.getElementById('studySponsor').innerText=
  2234. config.formConfig.crfEntry.StudySponsor;
  2235. config.document.getElementById('siteName').innerText=
  2236. config.formConfig.currentSite['siteName'];
  2237. config.document.getElementById('sitePhone').innerText=
  2238. config.formConfig.currentSite['sitePhone'];
  2239. config.document.getElementById('investigatorName').innerText=
  2240. config.formConfig.user['DisplayName'];
  2241. let h1=config.formConfig.form['formName'];
  2242. config.document.getElementById('formTitle').innerText=h1;
  2243. print('Setting title to '+h1);
  2244. }
  2245. //come here after the layout is read from labkey page
  2246. //
  2247. function generateErrorMsg(msg){
  2248. let txt=config.document.createElement('p');
  2249. txt.innerText=msg;
  2250. config.document.getElementById(config.masterForm).appendChild(txt);
  2251. generateButton("submitDiv",'Exit','Exit','redirect',redirect);
  2252. }
  2253. function getUser(id,field){
  2254. if (field in config.formConfig) return config.formConfig[field];
  2255. let uRows=config.formConfig.userRows;
  2256. for (let i=0;i<uRows.length;i++){
  2257. let userId=uRows[i].UserId;
  2258. if (userId!=id) continue;
  2259. config.formConfig[field]=uRows[i];
  2260. return config.formConfig[field];
  2261. }
  2262. return null;
  2263. }
  2264. function afterConfig(){
  2265. let debug=true;
  2266. if (debug)
  2267. print("afterConfig");
  2268. populateBasicData();
  2269. //check if user has permission on the form
  2270. let currentUser=getUser(LABKEY.Security.currentUser.id,'currentUser');
  2271. let currentSite=config.formConfig.currentSite;
  2272. let formCreator=getUser(config.formConfig.crfEntry.UserId,'formCreator');
  2273. let formCreatorId=formCreator.UserId;
  2274. //let formSite=config.formConfig.crfEntry.Site;
  2275. let fList=config.formConfig.operator+'s';
  2276. let fRows=config.formConfig[fList];
  2277. //let currentSiteId=-1;
  2278. //depending on operator mode, we should decide what is right
  2279. let operator=config.formConfig.operator;
  2280. if (operator=='crfEditor'){
  2281. //editor can only edit its own forms
  2282. if (currentUser.UserId!=formCreatorId){
  2283. let msg='User '+currentUser.DisplayName;
  2284. msg+=' has no permission on this form';
  2285. generateErrorMsg(msg);
  2286. return;
  2287. }
  2288. }
  2289. if (operator=='crfMonitor' || operator=='crfSponsor'){
  2290. //monitor can look at forms based on his site
  2291. //find monitor line
  2292. let operatorSites=new Array();
  2293. for (let i=0;i<fRows.length;i++){
  2294. if (fRows[i].User!=currentUser.UserId) continue;
  2295. operatorSites.push(fRows[i].Site);
  2296. }
  2297. print('operator Site: '+operatorSites.length);
  2298. if (operatorSites.length==0){
  2299. let msg='User '+currentUser.DisplayName;
  2300. msg+=' is not a '+operator;
  2301. generateErrorMsg(msg);
  2302. return;
  2303. }
  2304. let selectedSite=-1;
  2305. let siteCandidates="[";
  2306. for (let i=0;i<operatorSites.length;i++){
  2307. if (i>0) siteCandidates+=", ";
  2308. siteCandidates+=operatorSites[i];
  2309. if (operatorSites[i]!=currentSite.siteNumber) continue;
  2310. selectedSite=currentSite.siteNumber;
  2311. break;
  2312. }
  2313. siteCandidates+="]";
  2314. if (selectedSite==-1){
  2315. let msg='User '+currentUser.DisplayName;
  2316. msg+=' is not a '+operator+' for site ';
  2317. msg+=currentSite.siteName+'('+currentSite.siteNumber+')';
  2318. msg+='/'+siteCandidates;
  2319. generateErrorMsg(msg);
  2320. return;
  2321. }
  2322. }
  2323. print('User '+currentUser.DisplayName+'/'+
  2324. config.formConfig.currentSite['siteName']+
  2325. ' acting as '+config.formConfig.operator);
  2326. let rows=config.formConfig.crfButtons.rows;
  2327. config.formConfig.targetStatus=new Array();
  2328. config.formConfig.targetRecipient=new Array();
  2329. for (let i=0; i<rows.length; i++){
  2330. let action=rows[i].action;//String
  2331. let tstatus=rows[i].targetFormStatus;
  2332. let trecip=rows[i].targetRecipient;
  2333. config.formConfig.targetStatus[action]=tstatus;
  2334. config.formConfig.targetRecipient[action]=trecip;
  2335. }
  2336. let formStatus=config.formConfig.formStatus;
  2337. //let functionArray=new Array();
  2338. print("Generating buttons for formStatus \""+ formStatus+"\"");
  2339. let buttonRows=config.formConfig.crfButtons.rows;
  2340. for (let i=0;i<buttonRows.length;i++){
  2341. let bt=buttonRows[i];
  2342. if (typeof window[bt.action]==="function"){
  2343. generateButton("submitDiv",bt.caption,bt.label,bt.action,
  2344. window[bt.action]);
  2345. }
  2346. else{
  2347. print('No match for function :'+bt.action+
  2348. ' obj: '+window[bt.action]);
  2349. }
  2350. }
  2351. print('Here');
  2352. //here we should get data. For now, just initialize objects that will hold data
  2353. setDataLayout(afterDataLayout);//callback is afterDataLayout
  2354. }
  2355. function afterDataLayout(){
  2356. setData(afterData);//callback is afterData
  2357. }
  2358. function afterData(){
  2359. let fName='afterData';
  2360. //operatorBasedAccessMode
  2361. let accessMode=config.formConfig.operator+'Mode';
  2362. let rowsSetup=config.formConfig.formSetupRows;
  2363. print(fName+' list of sections');
  2364. for (let i=0;i<rowsSetup.length;i++){
  2365. let entry=rowsSetup[i];
  2366. let queryName=config.formConfig.queryMap[entry['queryName']];
  2367. print(fName+'\t'+queryName);
  2368. }
  2369. for (let i=0;i<rowsSetup.length;i++){
  2370. let entry=rowsSetup[i];
  2371. let queryName=config.formConfig.queryMap[entry['queryName']];
  2372. print(fName+" ["+queryName+"]: showFlag: "+entry["showFlag"]);
  2373. print(fName+" ["+queryName+"]: accessMode: "+entry[accessMode]);
  2374. const nData=config.formConfig.dataQueries[queryName].rows.length;
  2375. print(fName+" ["+queryName+"]: nData: "+nData);
  2376. //skip sections
  2377. //also from fields
  2378. if (entry[accessMode]=="NONE") continue;
  2379. //skip readonly empty records
  2380. //if (entry[accessMode]=="READ" && nData==0) continue;
  2381. //let additionalData=new Object();
  2382. //setAdditionalData(additionalData,entry);
  2383. //section fits one dataset/list
  2384. generateSection(entry);
  2385. //generateSection(queryName,entry["title"],entry[accessMode],
  2386. // additionalData);
  2387. }
  2388. }
  2389. function findSetupRow(sectionId){
  2390. let rowsSetup=config.formConfig.formSetupRows;
  2391. let key=sectionId.replace('section','');
  2392. for (let i=0;i<rowsSetup.length;i++){
  2393. let e=rowsSetup[i];
  2394. //let queryName1=config.formConfig.queryMap[e['queryName']];
  2395. if (e.formName!=config.formId) continue;
  2396. if (e['Key']==key) return e;
  2397. //if (queryName1!=queryName) continue;
  2398. }
  2399. return null;
  2400. }
  2401. function populateSection(sectionId){
  2402. //queryName should be found from setup
  2403. let fName='[populateSection/'+sectionId+']';
  2404. print(fName);
  2405. let entry=findSetupRow(sectionId);
  2406. //ignore names without associated entry in formSetup
  2407. if (entry==undefined){
  2408. print(fName+': no matching FormSetup entry found');
  2409. return;
  2410. }
  2411. let queryName=config.formConfig.queryMap[entry['queryName']];
  2412. //populate comes after generate, we should be pretty safe in taking
  2413. //already generated additionalData
  2414. if (!(queryName in config.formConfig.additionalData)){
  2415. print(fName+': no additionalData generated for '+queryName);
  2416. return;
  2417. }
  2418. let additionalData=config.formConfig.additionalData[queryName];
  2419. print(fName+': using additionalData '+additionalData);
  2420. if ("isReview" in additionalData){
  2421. generateReviewSection(queryName,queryName,generateReviewSectionCB);
  2422. return;
  2423. }
  2424. let accessMode=config.formConfig.operator+'Mode';
  2425. let aM=entry[accessMode];
  2426. print(fName+': accessMode '+aM);
  2427. if (aM!='GENERATE'){
  2428. let writeMode=(aM=='EDIT');
  2429. print(fName+': writeMode '+writeMode);
  2430. let setup=getSetup(sectionId,queryName,writeMode);
  2431. setup.setVariables=parseVariableDefinition(entry);
  2432. setHelp(setup,entry);
  2433. populateTable(queryName,writeMode,setup);
  2434. return;
  2435. }
  2436. //deal with generate
  2437. //
  2438. //already available -> shift to READ mode
  2439. let divTable=sectionId+'Table';
  2440. let divObj=config.document.getElementById(divTable);
  2441. let divRev=config.document.getElementById(sectionId+'Review');
  2442. let divRLi=config.document.getElementById(sectionId+'ReviewList');
  2443. let divGBu=config.document.getElementById(sectionId+'GenerateButton');
  2444. print('div GBU: '+divGBu);
  2445. divObj.style.display="block";
  2446. divRev.style.display="block";
  2447. divRLi.style.display="block";
  2448. if (divGBu!=undefined) divGBu.style.display="none";
  2449. let nData=config.formConfig.dataQueries[queryName].rows.length;
  2450. print('['+queryName+']: nrows '+nData);
  2451. if (nData>0){
  2452. let setup=getSetup(sectionId,queryName,false);
  2453. setup.setVariables=parseVariableDefinition(entry);
  2454. //this fails for setVariables, although they are available via entry['variableDefinition']
  2455. populateTable(queryName,false,setup);
  2456. return;
  2457. }
  2458. //hide table
  2459. divObj.style.display="none";
  2460. divRev.style.display="none";
  2461. divRLi.style.display="none";
  2462. if (divGBu!=undefined) divGBu.style.display="block";
  2463. //add buttons?
  2464. //is button already generated?
  2465. //populateTable(entry);
  2466. }
  2467. //******* generateQuery infrastructure *********************
  2468. function onGenerateQuery(queryName){
  2469. print('onGenerateQuery '+queryName);
  2470. //
  2471. let cfgRows=config.formConfig.generateConfigData.rows;
  2472. // //queryName to queryId?
  2473. let queryId=config.formConfig.fields[queryName].queryId;
  2474. let cfgRow=undefined;
  2475. for (let i=0;i<cfgRows.length;i++){
  2476. if (cfgRows[i].queryId!=queryId) continue;
  2477. cfgRow=cfgRows[i];
  2478. break;
  2479. }
  2480. if (cfgRow==undefined){
  2481. print('generateConfig for queryName['+queryId+']='+queryName+' not found');
  2482. return;
  2483. }
  2484. //add config to the list
  2485. if (!("generateConfig" in config.formConfig)){
  2486. config.formConfig.generateConfig=new Object();
  2487. }
  2488. config.formConfig.generateConfig[queryName]=cfgRow;
  2489. if (!("generateForm" in config.formConfig)){
  2490. config.formConfig.generateForm=new Object();
  2491. }
  2492. //
  2493. let formRows=config.formConfig.formRows;
  2494. let formId=cfgRow.formId;
  2495. for (let i=0;i<formRows.length;i++){
  2496. if (formRows[i].Key==formId) {
  2497. config.formConfig.generateForm[queryName]=formRows[i];
  2498. break;
  2499. }
  2500. }
  2501. //print('XcfgRow '+config.formConfig.generateForm[queryName]);
  2502. //
  2503. // //check if all required datasets were at least saved
  2504. checkGenerationFields(queryName);
  2505. }
  2506. function checkGenerationFields(queryName){
  2507. let genForm=config.formConfig.generateForm[queryName];
  2508. let genCfg=config.formConfig.generateConfig[queryName];
  2509. let mailRecipient=genCfg.emailRecipient;
  2510. //list of queries that are part of Registration form
  2511. print('checkRegistrationFields');
  2512. print('setRecipient: '+mailRecipient);
  2513. let formId=genForm.Key;
  2514. print("Checking form w/id "+formId);
  2515. let selectGenerationRows=selectFormSetupRows(formId);
  2516. //registration rows
  2517. for (let i=0;i<selectGenerationRows.length;i++){
  2518. let row=selectGenerationRows[i];
  2519. let queryId=row.queryName;
  2520. let fQueryName=config.formConfig.queryMap[queryId];
  2521. if (fQueryName==queryName) continue;
  2522. let fQuery=config.formConfig.dataQueries[fQueryName];
  2523. print('Checking '+fQueryName+' nrows: '+fQuery.rows.length);
  2524. if (fQuery.rows.length==0){
  2525. generateError(queryName,fQueryName);
  2526. return;
  2527. }
  2528. }
  2529. generateMessage(queryName,'Vailidation OK');
  2530. print('callback: set recipient: '+mailRecipient);
  2531. let cb=function(){prepareForm(queryName,formId,mailRecipient);};
  2532. generateListEntry(formId,queryName,cb);
  2533. }
  2534. function prepareForm(queryName,formId,mailRecipient){
  2535. print('prepareForm: recipient '+mailRecipient);
  2536. //
  2537. // return;
  2538. //look for existing registration entry
  2539. let selectRows=new Object();
  2540. //inputLists should be in configuration container
  2541. selectRows.containerPath=getContainer('data');
  2542. selectRows.schemaName='lists';
  2543. selectRows.queryName='crfEntry';
  2544. selectRows.success=function(data){generateForm(data,queryName,mailRecipient);};
  2545. let formFilter=LABKEY.Filter.create('Form',formId);
  2546. let parentCrfFilter=LABKEY.Filter.create('parentCrf',getCRFref());
  2547. selectRows.filterArray=[formFilter,parentCrfFilter];
  2548. LABKEY.Query.selectRows(selectRows);
  2549. }
  2550. function generateError(queryName,fQueryName){
  2551. let elName=queryName+'GenerateButton'+'_reportField';
  2552. let el=config.document.getElementById(elName);
  2553. el.innerText='Error: '+fQueryName+' was not set';
  2554. el.style.color='red';
  2555. }
  2556. function generateMessage(queryName,msg){
  2557. let elName=queryName+'GenerateButton'+'_reportField';
  2558. let el=config.document.getElementById(elName);
  2559. el.innerText=msg;
  2560. el.style.color='green';
  2561. }
  2562. function generateForm(data,queryName,mailRecipient){
  2563. print('generateForm, recpioent: '+mailRecipient);
  2564. //
  2565. const nData=data.rows.length;
  2566. print('Registration: '+nData+' rows');
  2567. let formRow=config.formConfig.generateForm[queryName];
  2568. //we have to generate masterQuery with parentCrf and crfRef
  2569. //and crfEntry with new entryId and parentCrf equal to crfRef
  2570. if (nData>0) {
  2571. generateMessage(queryName,'Registration already generated.');
  2572. return;
  2573. }
  2574. let formId=formRow.Key;
  2575. let formName=formRow.formName;
  2576. let crfBase=config.formConfig.crfEntry;
  2577. let crfEntry=new Object();
  2578. //add new reference
  2579. crfEntry.entryId=Date.now();
  2580. crfEntry.parentCrf=getCRFref();
  2581. crfEntry["Date"]=new Date();
  2582. crfEntry["View"]="[VIEW]";
  2583. crfEntry.formStatus=1;//In progress
  2584. // //set other variables
  2585. //requires studyData as part of formConfig
  2586. // let studyData=config.formConfig.studyData;
  2587. print('Adding study: '+crfBase.EudraCTNumber);
  2588. crfEntry.EudraCTNumber=crfBase.EudraCTNumber;
  2589. crfEntry.StudyCoordinator=crfBase.StudyCoordinator;
  2590. crfEntry.StudySponsor=crfBase.StudySponsor;
  2591. crfEntry.RegulatoryNumber=crfBase.RegulatoryNumber;
  2592. //
  2593. // //find sponsor for site
  2594. let site=crfBase.Site;
  2595. let crfSponsors=config.formConfig.crfSponsors;
  2596. let users=config.formConfig.userRows;
  2597. for (let i=0;i<crfSponsors.length;i++){
  2598. //print('Checking for site '+crfSponsors[i].Site);
  2599. if (crfSponsors[i].Site!=site) continue;
  2600. config.formConfig.sponsorId=crfSponsors[i].User;
  2601. //print('Setting id '+config.formConfig.sponsorId);
  2602. //finds first
  2603. break;
  2604. }
  2605. for (let j=0;j<users.length;j++){
  2606. if (config.formConfig.sponsorId!=users[j].UserId) continue;
  2607. config.formConfig.sponsor=users[j];
  2608. //finds first (should be unique)
  2609. break;
  2610. }
  2611. print('Selecting '+config.formConfig.sponsor.DisplayName+' as sponsor');
  2612. //different user than the original form...
  2613. //should be set to the study sponsor
  2614. crfEntry.UserId=config.formConfig.sponsor.UserId;
  2615. crfEntry.Site=site;
  2616. // //set formId to one found through registration search
  2617. crfEntry.Form=formId;
  2618. ////
  2619. let qconfig=new Object();
  2620. qconfig.schemaName='lists';
  2621. qconfig.queryName='crfEntry';
  2622. qconfig.success=function(data){sendEmail(data,mailRecipient,doNothing,formName+' generated');}
  2623. qconfig.rows=[crfEntry];
  2624. LABKEY.Query.insertRows(qconfig);
  2625. }
  2626. //
  2627. function generateListEntry(formId,queryName,cb){
  2628. //check if registration was already generated
  2629. let formRows=config.formConfig.formRows;
  2630. let qForm=undefined;
  2631. for (let i=0;i<formRows.length;i++){
  2632. if (formRows[i].Key!=formId) continue;
  2633. qForm=formRows[i];
  2634. }
  2635. let nData=config.formConfig.dataQueries[queryName].rows.length
  2636. if (nData>0) return;
  2637. //only generate record for parentCrfRef
  2638. //let qArray=new Array();
  2639. //let e1=new Object();
  2640. //e1.crfRef=data.rows[0].entryId;
  2641. //e1.submissionDate=new Date();
  2642. //only for registration
  2643. //e1.registrationStatus=0;
  2644. //qArray.push(e1);
  2645. let e2=new Object();
  2646. e2.crfRef=getCRFref();
  2647. e2.registrationStatus=0;
  2648. e2.submissionDate=new Date();
  2649. print('set values');
  2650. let qconfig=new Object();
  2651. qconfig.containerPath=getContainer('data');
  2652. qconfig.schemaName='lists';
  2653. qconfig.queryName=queryName;
  2654. qconfig.success=cb;
  2655. qconfig.rows=[e2]
  2656. LABKEY.Query.insertRows(qconfig);
  2657. }
  2658. // ******************** end form generator (Registration) ********************
  2659. //jump to populate table/generate review, etc defined at the begining of the file
  2660. function setContainer(label,container){
  2661. if (!(config.formConfig.hasOwnProperty('container'))){
  2662. config.formConfig.container=new Array();
  2663. }
  2664. config.formConfig.container[label]=container;
  2665. }
  2666. function getContainer(label){
  2667. return config.formConfig.container[label];
  2668. }
  2669. //entry point from generateMasterForm
  2670. function setFormConfig(){
  2671. //add object to store form related data
  2672. config.formConfig=new Object();
  2673. config.formConfig.softwareVersion='0.15.14';
  2674. let debug=true;
  2675. if (debug)
  2676. print("generateMasterForm1");
  2677. //set containers for data and configuration
  2678. //TODO: set this from a query
  2679. //
  2680. setContainer('data',LABKEY.ActionURL.getContainer());
  2681. setContainer('config',LABKEY.ActionURL.getContainer());
  2682. setContainer('CRF',LABKEY.ActionURL.getContainer());
  2683. let selectRows=new Object();
  2684. //this is local data
  2685. selectRows.containerPath=getContainer('CRF');
  2686. selectRows.schemaName='lists';
  2687. selectRows.queryName='crfSettings';
  2688. //store form related data to this object
  2689. selectRows.success=afterSettings;
  2690. LABKEY.Query.selectRows(selectRows);
  2691. }
  2692. function afterSettings(data){
  2693. config.formConfig.settings=new Array();
  2694. for (let i=0;i<data.rows.length;i++){
  2695. let n=data.rows[i]['name'];
  2696. let v=data.rows[i]['value'];
  2697. config.formConfig.settings[n]=v;
  2698. }
  2699. let st=config.formConfig.settings;
  2700. print('afterSettings');
  2701. for (let k in st){
  2702. print('\t'+k+'='+st[k]);
  2703. }
  2704. //if ('dataContainer' in st){
  2705. // setContainer('data',st['dataContainer']);
  2706. //}
  2707. let vname='configContainer';
  2708. if (vname in st){
  2709. setContainer('config',st[vname]);
  2710. }
  2711. print('Config: '+getContainer('config'));
  2712. print('Data: '+getContainer('data'));
  2713. let selectRows=new Object();
  2714. //this is local data
  2715. selectRows.containerPath=getContainer('data');
  2716. selectRows.schemaName='lists';
  2717. selectRows.queryName='crfEntry';
  2718. //use first-> we must first establish link to the rigth crf entry
  2719. selectRows.filterArray=[LABKEY.Filter.create('entryId',getCRFrefFirst())];
  2720. //store form related data to this object
  2721. selectRows.success=afterCRFEntry;
  2722. LABKEY.Query.selectRows(selectRows);
  2723. }
  2724. function afterCRFEntry(data){
  2725. config.formConfig.crfEntry=data.rows[0];
  2726. print("Setting crfEntry (x) to "+config.formConfig.crfEntry["entryId"]);
  2727. //for empty records or those with parentCrf not set, parentCrf comes up as null
  2728. //nevertheless, with two equal signs, check against undefined also works
  2729. print('parentCrf set to '+config.formConfig.crfEntry.parentCrf);
  2730. collectData();
  2731. }
  2732. function collectData(){
  2733. let varLabel='sourceFormStatus';
  2734. let formStatus=config.formConfig.crfEntry['FormStatus'];
  2735. let queryArray=new Array();
  2736. //site
  2737. queryArray.push(makeQuery('config','site','siteData',[]));
  2738. //users
  2739. queryArray.push(makeQuery('CRF','users','userData',[]));
  2740. queryArray[queryArray.length-1].schemaName='core';
  2741. //crfEditors
  2742. queryArray.push(makeQuery('config','crfEditors','crfEditorsData',[]));
  2743. //crfMonitors
  2744. queryArray.push(makeQuery('config','crfMonitors','crfMonitorsData',[]));
  2745. //crfSponsors
  2746. queryArray.push(makeQuery('config','crfSponsors','crfSponsorsData',[]));
  2747. //study
  2748. queryArray.push(makeQuery('data','Study','studyDataAll',[]));
  2749. let e=queryArray[queryArray.length-1];
  2750. e.schemaName='study';
  2751. e.columns="SubjectColumnName,EudraCTNumber,StudySponsor";
  2752. e.columns+=",StudyCoordinator,RegulatoryNumber";
  2753. //formStatus
  2754. let formFilter=LABKEY.Filter.create('Key',formStatus);
  2755. queryArray.push(makeQuery('config','FormStatus','formStatusData',[formFilter]));
  2756. //crfButtons
  2757. let statusFilter=LABKEY.Filter.create(varLabel,formStatus);
  2758. queryArray.push(makeQuery('config','crfButtons','crfButtons',[statusFilter]));
  2759. //Forms
  2760. queryArray.push(makeQuery('config','Forms','formData',[]));
  2761. //FormSetup
  2762. queryArray.push(makeQuery('config','FormSetup','formSetup',[]));
  2763. //generateConfig
  2764. queryArray.push(makeQuery('config','generateConfig','generateConfigData',[]));
  2765. //parentCrf
  2766. let parentCrf=config.formConfig.crfEntry['parentCrf'];
  2767. if (parentCrf!=undefined){
  2768. let crfFilter=LABKEY.Filter.create('entryId',parentCrf);
  2769. queryArray.push(makeQuery('data','crfEntry','parentCrfData',[crfFilter]));
  2770. }
  2771. queryArray.push(makeQuery('data','help','help'));
  2772. print('running getDataFromQueries');
  2773. getDataFromQueries(queryArray,fcontinue);
  2774. }
  2775. function selectFormSetupRows(formId){
  2776. let formSetupRows=new Array();
  2777. let allRows=config.formConfig.formSetup.rows;
  2778. for (let i=0;i<allRows.length;i++){
  2779. let formEntry=allRows[i];
  2780. if (formEntry.formName==formId)
  2781. formSetupRows.push(formEntry);
  2782. }
  2783. return formSetupRows;
  2784. }
  2785. function fcontinue(){
  2786. //parse site
  2787. config.formConfig.siteRows=config.formConfig.siteData.rows;
  2788. let sRows=config.formConfig.siteRows;
  2789. for (let i=0;i<sRows.length;i++){
  2790. let siteId=sRows[i].siteNumber;
  2791. print('site '+siteId);
  2792. if (siteId==config.formConfig.crfEntry.Site){
  2793. config.formConfig.currentSite=sRows[i];
  2794. break;
  2795. }
  2796. }
  2797. //config.formConfig.site=data.rows[0];
  2798. print("Setting site name to "+config.formConfig.currentSite.siteName);
  2799. //study
  2800. config.formConfig.studyData=config.formConfig.studyDataAll.rows[0];
  2801. print("XSetting participantField to "+
  2802. config.formConfig.studyData["SubjectColumnName"]);
  2803. config.formConfig.crfEditors=config.formConfig.crfEditorsData.rows;
  2804. config.formConfig.crfMonitors=config.formConfig.crfMonitorsData.rows;
  2805. config.formConfig.crfSponsors=config.formConfig.crfSponsorsData.rows;
  2806. config.formConfig.userRows=config.formConfig.userData.rows;
  2807. let uRows=config.formConfig.userRows;
  2808. for (let i=0;i<uRows.length;i++){
  2809. let userId=uRows[i].UserId;
  2810. if (userId==config.formConfig.crfEntry.UserId){
  2811. config.formConfig.user=uRows[i];
  2812. break;
  2813. }
  2814. }
  2815. //config.formConfig.user=data.rows[0];
  2816. print("Setting user to "+config.formConfig.user["DisplayName"]);
  2817. let fsRows=config.formConfig.formStatusData.rows;
  2818. config.formConfig.formStatus=fsRows[0].formStatus;
  2819. config.formConfig.operator=config.role;
  2820. //config.formConfig.operator=fsRows[0].operator;
  2821. print('Setting operator to: '+config.formConfig.operator);
  2822. config.formConfig.formRows=config.formConfig.formData.rows;
  2823. let formRows=config.formConfig.formRows;
  2824. //filter out the current form
  2825. for (let i=0;i<formRows.length;i++){
  2826. if (formRows[i].Key==config.formId){
  2827. config.formConfig.form=formRows[i];
  2828. break;
  2829. }
  2830. }
  2831. config.formConfig.formSetupRows=selectFormSetupRows(config.formId);
  2832. print("Number of datasets for form ["+config.formId+"]: "+
  2833. config.formConfig.formSetupRows.length);
  2834. let fields=config.formConfig.formSetup.metaData.fields;
  2835. //get the lookup for queryName column
  2836. let formQueryName='queryName';
  2837. let field="NONE";
  2838. for (f in fields){
  2839. if (fields[f]['name']!=formQueryName) continue;
  2840. field=fields[f];
  2841. break;
  2842. }
  2843. let lookup=field.lookup;
  2844. print("Getting dataset names from "+lookup.queryName);
  2845. let selectRows=new Object();
  2846. //inputLists should be in configuration container
  2847. selectRows.containerPath=getContainer('config');
  2848. selectRows.schemaName=lookup.schemaName;
  2849. selectRows.queryName=lookup.queryName;
  2850. selectRows.success=afterFormDatasets;
  2851. LABKEY.Query.selectRows(selectRows);
  2852. }
  2853. function afterFormDatasets(data){
  2854. print('afterFormDatasets: '+data.rows.length);
  2855. config.formConfig.formDatasets=data;//inputLists
  2856. config.formConfig.fields=new Object();
  2857. config.formConfig.queryMap=new Object();
  2858. config.formConfig.additionalData=new Object();
  2859. let rows=config.formConfig.formSetupRows;
  2860. //should skip report only rows
  2861. for (let i=0;i<rows.length;i++){
  2862. let entry=rows[i];
  2863. let reviewField=(entry['showFlag']=='REVIEW');
  2864. //is the operator set yet?
  2865. let accessMode=config.formConfig.operator+'Mode';
  2866. let skipField=(entry[accessMode]=="NONE");
  2867. let queryId=entry['queryName'];
  2868. let lookupRows=config.formConfig.formDatasets.rows;
  2869. print('QueryID['+i+']='+queryId);
  2870. let dentry;
  2871. for (let j=0;j<lookupRows.length;j++){
  2872. if (queryId!=lookupRows[j]['Key']) continue;
  2873. dentry=lookupRows[j];
  2874. break;
  2875. }
  2876. let qName=dentry['queryName'];
  2877. //update list of dataset formConfig is observing (fields/queryMap)
  2878. while (1){
  2879. //review contains no data
  2880. if (reviewField) break;
  2881. if (skipField) break;
  2882. //already in fields
  2883. if (qName in config.formConfig.fields) break;
  2884. config.formConfig.fields[qName]=new Object();
  2885. break;
  2886. }
  2887. while(1){
  2888. //already done
  2889. if (queryId in config.formConfig.queryMap) break;
  2890. config.formConfig.queryMap[queryId]=qName;
  2891. break;
  2892. }
  2893. if (reviewField) continue;
  2894. if (skipField) continue;
  2895. //only do this for real lists
  2896. let field=config.formConfig.fields[qName];
  2897. field.title=entry['title'];
  2898. field.queryId=queryId;
  2899. }
  2900. print("List of datasets in form : ");
  2901. for (f in config.formConfig.fields){
  2902. let field=config.formConfig.fields[f];
  2903. print("\t"+f+" ID: "+field.queryId+' title '+field.title);
  2904. }
  2905. afterConfig();
  2906. }
  2907. //>>>>>>>>>>>>>>>>>new>>>>>>>>>>>>
  2908. function setDataLayout(cb){
  2909. let rowsSetup=config.formConfig.formSetupRows;
  2910. config.formConfig.dataQueries=new Object();
  2911. let dS=config.formConfig.dataQueries;//reference only
  2912. let qMap=config.formConfig.queryMap;
  2913. config.formConfig.lookup=new Object();
  2914. for (let i=0;i<rowsSetup.length;i++){
  2915. let entry=rowsSetup[i];
  2916. //skip review rows
  2917. if (entry['showFlag']=='REVIEW')
  2918. continue;
  2919. let queryId=entry['queryName'];
  2920. let queryName=qMap[entry['queryName']];
  2921. dS[queryName]=new Object();
  2922. dS[queryName].title=entry['title'];
  2923. if (entry['showQuery']!="NONE"){
  2924. let sqName=entry['showQuery'];
  2925. dS[sqName]=new Object();
  2926. dS[sqName].title=findTitle(sqName);
  2927. }
  2928. }
  2929. //always add reviews
  2930. //
  2931. config.formConfig.dataQueries['reviewComments']=new Object();
  2932. //perhaps we will need queryId, but this is stuff for later
  2933. for (q in config.formConfig.dataQueries){
  2934. //callback will be a watchdog and will complete only
  2935. //when all data will be gathered
  2936. let dq=config.formConfig.dataQueries[q];
  2937. dq.collectingLayout="INITIALIZED";
  2938. let selectRows=new Object();
  2939. selectRows.queryName=q;
  2940. selectRows.schemaName='lists';
  2941. selectRows.containerPath=getContainer('data');
  2942. //we are only interested in metadata
  2943. selectRows.success=function(data){afterDatasets(data,cb);}
  2944. LABKEY.Query.selectRows(selectRows);
  2945. }
  2946. }
  2947. function findTitle(queryName){
  2948. //find by name from formDatasets
  2949. //and set associated title as title
  2950. let rows=config.formConfig.formDatasets.rows;
  2951. for (let i=0;i<rows.length;i++){
  2952. let entry=rows[i];
  2953. if (entry['queryName']!=queryName) continue;
  2954. return entry['title'];
  2955. }
  2956. return "NONE";
  2957. }
  2958. //this happens after the for loop, so all dataQueries objects are set
  2959. function afterDatasets(data,cb){
  2960. let qobject=config.formConfig.dataQueries[data.queryName];
  2961. print("Inspecting layout for "+data.queryName+" "+qobject);
  2962. qobject.fields=data.metaData.fields;
  2963. qobject.collectingLayout="STARTED";
  2964. //qobject.started="TRUE";
  2965. qobject.lookup=new Object();//keep track of objects
  2966. let i=0;
  2967. for (let f in qobject.fields){
  2968. //anything else is simple but lookup
  2969. let field=qobject.fields[f];
  2970. if (!("lookup" in field)) continue;
  2971. qobject.lookup[f]="PENDING";
  2972. print("Adding pending lookup for field "+f);
  2973. if (field.lookup.queryName in config.formConfig.lookup){
  2974. qobject.lookup[f]="DONE";
  2975. continue;
  2976. }
  2977. print("Setting up query for field "+f);
  2978. config.formConfig.lookup[field.lookup.queryName]=new Object();
  2979. let lObject=config.formConfig.lookup[field.lookup.queryName];
  2980. lObject.keyColumn=field.lookup.keyColumn;
  2981. lObject.displayColumn=field.lookup.displayColumn;
  2982. let selectRows=new Object();
  2983. selectRows.schemaName=field.lookup.schemaName;
  2984. selectRows.queryName=field.lookup.queryName;
  2985. selectRows.containerPath=getContainer('data');
  2986. if ("containerPath" in field.lookup){
  2987. selectRows.containerPath=field.lookup.containerPath;
  2988. }
  2989. //selectRows.columns=field.lookup.keyColumn+","+field.lookup.displayColumn;
  2990. //get all, descriptions may be hidden in further variables
  2991. //wait for all lookups to return
  2992. selectRows.success=function(data){addLookup(data,qobject,f,cb);};
  2993. print("Sending query: ["+
  2994. field.lookup.queryName+"] "+
  2995. field.lookup.keyColumn+'/'+
  2996. field.lookup.displayColumn);
  2997. LABKEY.Query.selectRows(selectRows);
  2998. i+=1;
  2999. }
  3000. if (i==0){
  3001. print("No lookups for "+data.queryName);
  3002. qobject.collectingLayout="FINISHED";
  3003. }
  3004. //check if done (both here and in lookup
  3005. //this only passes if no lookups are anywhere in the dataset assembly
  3006. if (!dataLayoutSet()) return;
  3007. cb();
  3008. }
  3009. function addLookup(data,qobject,f,cb){
  3010. print("Adding lookup "+data.queryName+' '+qobject+' '+f);
  3011. let lObject=config.formConfig.lookup[data.queryName];
  3012. lObject.LUT=new Array();//key to value
  3013. lObject.ValToKey=new Array();//value to key
  3014. let key=lObject.keyColumn;
  3015. let val=lObject.displayColumn;
  3016. for (let i=0;i<data.rows.length;i++){
  3017. lObject.LUT[data.rows[i][key]]=data.rows[i][val];
  3018. lObject.ValToKey[data.rows[i][val]]=data.rows[i][key];
  3019. }
  3020. lObject.rows=data.rows;
  3021. qobject.lookup[f]="DONE";
  3022. if (!dataLayoutSet()) return;
  3023. cb();
  3024. }
  3025. function dataLayoutSet(){
  3026. print("Checking layout completeness");
  3027. let dq=config.formConfig.dataQueries;
  3028. for (f in dq){
  3029. print("["+f+"]: "+dq[f].collectingLayout);
  3030. if (dq[f].collectingLayout=="INITIALIZED")
  3031. return 0;
  3032. if (dq[f].collectingLayout=="FINISHED")
  3033. continue; //OK
  3034. let qobject=dq[f];
  3035. for (q in qobject.lookup){
  3036. if (qobject.lookup[q]=="DONE") {
  3037. print("["+f+"/"+q+"]: DONE");
  3038. continue;//OK
  3039. }
  3040. print("["+f+"/"+q+"]: "+qobject.lookup[q]);
  3041. return 0;
  3042. }
  3043. dq[f].collectingLayout="FINISHED";
  3044. }
  3045. print("Success");
  3046. return 1;
  3047. }
  3048. function setData(cb){
  3049. fName='[setData]';
  3050. print(fName+': cb '+cb);
  3051. let crfMatch=getCRFref();
  3052. let parentCrf=config.formConfig.crfEntry['parentCrf'];
  3053. if (parentCrf!=undefined) crfMatch=parentCrf;
  3054. print(fName+' form crf ['+getCRFref()+'] matching for crfRef='+crfMatch);
  3055. //collect data and execute callback cb for queries in cb.queryList
  3056. for (q in config.formConfig.dataQueries){
  3057. let fQuery=config.formConfig.dataQueries[q];
  3058. fQuery.collectingData="STARTED";
  3059. let selectRows=new Object();
  3060. selectRows.containerPath=getContainer('data');
  3061. selectRows.queryName=q;
  3062. selectRows.schemaName='lists';
  3063. selectRows.filterArray=[
  3064. LABKEY.Filter.create("crfRef",crfMatch)
  3065. ];
  3066. selectRows.success=function(data){assembleData(data,cb);};
  3067. LABKEY.Query.selectRows(selectRows);
  3068. }
  3069. }
  3070. function assembleData(data,cb){
  3071. let fName='[assembleData/'+data.queryName+']';
  3072. let fQuery=config.formConfig.dataQueries[data.queryName];
  3073. fQuery.rows=data.rows;
  3074. fQuery.collectingData="FINISHED";
  3075. print(fName+': adding data');
  3076. for (q in config.formConfig.dataQueries){
  3077. let dq=config.formConfig.dataQueries[q];
  3078. //print("assembleData ["+q+"]: "+dq.collectingData);
  3079. if (dq.collectingData=="STARTED") return;
  3080. }
  3081. print(fName+': completing');
  3082. cb();
  3083. }
  3084. function uploadFile(inputElement,context){
  3085. //context should have ID and dirName attributes;
  3086. //path will be dirName/ID/fieldName_ID.suf
  3087. //where suf is identical to localPath content picked from
  3088. //inputElement
  3089. print('uploadFile: '+inputElement.value+'/');
  3090. if (inputElement.type=="text") return;
  3091. print('uploadFile: '+inputElement.files+'/');
  3092. print('uploadFile: '+inputElement.files.length+'/');
  3093. if (inputElement.files.length>0){
  3094. let file=inputElement.files[0];
  3095. print('uploadFile: '+inputElement.value+'/'+file.size);
  3096. }
  3097. let url=LABKEY.ActionURL.getBaseURL();
  3098. url+='_webdav';
  3099. url+=LABKEY.ActionURL.getContainer();
  3100. url+='/@files';
  3101. url+='/'+context['dirName'];
  3102. print('uploadFile url: '+url);
  3103. let uploadConfig=new Object();
  3104. uploadConfig.inputElement=inputElement;
  3105. uploadConfig.context=context;
  3106. uploadConfig.url=url;
  3107. uploadConfig.success=afterBaseDir;
  3108. uploadConfig.failure=tryMakeDir;
  3109. webdavCheck(uploadConfig);
  3110. }
  3111. function afterBaseDir(cfg){
  3112. print('afterBaseDir');
  3113. cfg.url+='/'+cfg.context['ID'];
  3114. cfg.success=afterIDDir;
  3115. cfg.failure=tryMakeDir;
  3116. webdavCheck(cfg);
  3117. }
  3118. function afterIDDir(cfg){
  3119. print('afterIDDir');
  3120. if (cfg.inputElement.files.length==0){
  3121. print('No files found');
  3122. return;
  3123. }
  3124. let file=cfg.inputElement.files[0];
  3125. print('Uploading '+file.name);
  3126. let suf=file.name.split('.').pop();
  3127. cfg.url+='/'+cfg.context['ID']+'.'+suf;
  3128. cfg.success=afterUpload;
  3129. cfg.failure=onFailure;
  3130. cfg.data=file;
  3131. webdavPut(cfg);
  3132. }
  3133. function afterUpload(cfg){
  3134. print('afterUpload');
  3135. }
  3136. function tryMakeDir(cfg){
  3137. print('tryMakeDir '+cfg.url);
  3138. cfg.failure=onFailure;
  3139. webdavMakeDir(cfg);
  3140. }
  3141. function request(cfg,verb,data){
  3142. print('request['+verb+'] '+cfg.url);
  3143. let connRequest=new XMLHttpRequest();
  3144. connRequest.addEventListener("loadend",
  3145. function(){checkResponse(connRequest,cfg);});
  3146. connRequest.open(verb, cfg.url);
  3147. connRequest.send(data);
  3148. //print('request['+verb+'] sent');
  3149. }
  3150. function checkResponse(xrq,cfg){
  3151. //print('checkResponse: readyState '+xrq.readyState);
  3152. //print('checkResponse: status '+xrq.status);
  3153. if (xrq.status<400) {
  3154. //client errors 400-499
  3155. //server errors 500-599
  3156. cfg.success(cfg);
  3157. return;
  3158. }
  3159. cfg.status=xrq.status;
  3160. cfg.failure(cfg);
  3161. }
  3162. function webdavMakeDir(cfg){ request(cfg,'MKCOL',null);}
  3163. function webdavCheck(cfg) { request(cfg,'GET',null);}
  3164. function webdavPut(cfg) { request(cfg,'PUT',cfg.data);}
  3165. function onFailure(cfg){
  3166. print('request failed with status='+cfg.status);
  3167. }