import labkeyInterface import labkeyInterface import labkeyDatabaseBrowser # interact with the database (connect, get handle, getRows, uploadRows) def connectDB(server): net=labkeyInterface.labkeyInterface() fconfig=os.path.join(os.path.expanduser('~'),'.labkey',f'{server}.json') net.init(fconfig) return net,labkeyDatabaseBrowser.labkeyDB(net) def getDB(pars): try: return pars['db'] except KeyError: pass pars['net'],pars['db']=connectDB(pars['server']) return pars['db'] def getRows(pars): db=getDB(pars) qFilter=pars.get('qFilter',[]) return db.selectRows(pars['project'],pars['schema'], pars['query'],qFilter)['rows'] def modifyRows(pars,mode,rows): db=getDB(pars) return db.modifyRows(mode,pars['project'],pars['schema'],pars['query'],rows) # helper function #very generic variable splitting routine def parseVariables(q): if q==None: return [] arr=q.split(';') v=[x.split('=') for x in arr] return [{'varName':x[0],'varValue':x[1]} for x in v] # get snapshot of the data layout #this should really look at FormStatus on layout and do the conversion def toStatus(x): if x=='Submitted': return 2 if x=='InProgress': return 1 if x=='Approved': return 5 #get dataset names def getInputLists(pars): try: return pars['inputLists'] except KeyError: pass parServerLayout={'db':pars['db'],'project':'crfLayout/hypoAfrica'} parInputLists={'schema':'lists','query':'inputLists'} rows=getRows(parServerLayout|parInputLists) pars['inputLists']={r['Key']:r['queryName'] for r in rows} return pars['inputLists'] #get datasets for form def getDatasets(pars,form): try: return pars['datasets'][form] except KeyError: pass parServerLayout={'db':pars['db'],'project':'crfLayout/hypoAfrica'} # set datasets for forms formFilter={'variable':'formName','value':f'{form}','oper':'eq'} parFormSetup={'schema':'lists','query':'FormSetup'} parFilter={'qFilter':[formFilter]} rows=getRows(parServerLayout|parFormSetup|parFilter) #ignore show query since 15 has all set to NONE #keep variable definition inputLists=getInputLists(pars) datasets={r['Key']:r|{'query':inputLists[r['queryName']]} for r in rows} try: pars['datasets'][form]=datasets except KeyError: pars['datasets']={form:datasets} return datasets #manager CRF master entries (get, modify, upload) def getCrfEntry(pars,crf): crfFilter={'variable':'entryId','value':crf,'oper':'eq'} parFilter={'qFilter':[crfFilter]} parCrf={'schema':'lists','query':'crfEntry'} rows=getRows(pars|parCrf|parFilter) if len(rows)!=1: print('Fail crfEntry') return None return rows[0] def changeFormStatus(pars,crfEntry,targetStatus): crfEntry['FormStatus']=toStatus(targetStatus) uploadCrfEntry(pars,crfEntry) def uploadCrfEntry(pars,crfEntry): parCrf={'schema':'lists','query':'crfEntry'} outcome=modifyRows(pars|parCrf,'update',[crfEntry]) try: print(outcome['exception']) except KeyError: pass #manage DATA (set, verify, upload) def setData(pars,crfEntry,schema): datasets=getDatasets(pars,crfEntry['Form']) for k in datasets: d=datasets[k] crf=crfEntry['entryId'] #print('Setting [{}:{}/{}]'.format(schema,d['query'],d['variableDefinition'])) crfFilter={'variable':'crfRef','value':crf,'oper':'eq'} qFilter=[crfFilter] qvar=parseVariables(d['variableDefinition']) qvarFilter=[{'variable':x['varName'],'value':x['varValue'],'oper':'eq'} for x in qvar] qFilter.extend(qvarFilter) parD={'schema':schema,'query':d['query'],'qFilter':qFilter} rows=getRows(pars|parD) try: pars['data'][schema][k]=rows except KeyError: try: pars['data'][schema]={k:rows} except KeyError: pars['data']={schema:{k:rows}} def verifyData(pars,crfEntry,schema): ok=True datasets=getDatasets(pars,crfEntry['Form']) for k in datasets: d=datasets[k] rows=pars['data'][schema][k] testFailed={'lists':len(rows)!=1,'study':len(rows)>1} if testFailed[schema]: print('[{} {}/{}]: {}'.format(schema,d['query'], d['variableDefinition'],len(rows))) #at least one fail ok=False continue return ok def uploadData(pars,crfEntry): status=True datasets=getDatasets(pars,crfEntry['Form']) for k in datasets: listRow=pars['data']['lists'][k][0] studyRows=pars['data']['study'][k] try: outRow=studyRows[0] outRow.update(listRow) mode='update' except IndexError: outRow={x:listRow[x] for x in listRow} mode='insert' outRow['ParticipantId']=crfEntry['participantStudyId'] outRow['SequenceNum']=(int(crfEntry['entryId']) % 1000000000 ) d=datasets[k] parD={'schema':'study','query':d['query']} outcome=modifyRows(pars|parD,mode,[outRow]) try: print(outcome['exception']) status=False except KeyError: pass return status ## mimic CRF actions def onSubmitedToInProgress(pars,crf): crfEntry=getCrfEntry(pars,crf) statusIn=crfEntry['FormStatus'] if statusIn!=toStatus('Submitted'): print(f'[{crf}]: Inappropriate status({statusIn})') return changeFormStatus(pars,crfEntry,'InProgress') def onApprovedToSubmited(pars,crf): crfEntry=getCrfEntry(pars,crf) statusIn=crfEntry['FormStatus'] if statusIn!=toStatus('Approved'): print(f'[{crf}]: Inappropriate status({statusIn})') return changeFormStatus(pars,crfEntry,'Submitted') def onSubmit(pars,crf): crfEntry=getCrfEntry(pars,crf) statusIn=crfEntry['FormStatus'] if statusIn!=toStatus('InProgress'): print(f'[{crf}]: Inappropriate status({statusIn})') return form=crfEntry['Form'] setData(pars,crfEntry,'lists') status=verifyData(pars,crfEntry,'lists') crf=crfEntry['entryId'] if not status: print(f'[{crf}/{form}] verification failed') return changeFormStatus(pars,crfEntry,'Submitted') def onDatabaseUpload(pars,crf): crfEntry=getCrfEntry(pars,crf) statusIn=crfEntry['FormStatus'] if statusIn!=toStatus('Submitted'): print(f'[{crf}]: Inappropriate status({statusIn})') return form=crfEntry['Form'] setData(pars,crfEntry,'lists') setData(pars,crfEntry,'study') dataOK=verifyData(pars,crfEntry,'study') if not dataOK: print(f'[{crf}]: verification failed') return print(f'[{crf}]: verification OK') uploadOK=uploadData(pars,crfEntry) if not uploadOK: print(f'[{crf}]: upload failed') return print(f'[{crf}]: upload OK') changeFormStatus(pars,crfEntry,'Approved')