#labkey/orthanc interface #scans the orthanc internal database and fills labkey table #"Orthanc" section expected in the configuration file, with #"queryName": query to be filled for each image #"demographicsQuery": query to be filled for every patient #"schemaName": the schema both queries are part of #"project": name of the project under labkey that collects Orthanc data, # typically named Orthanc or similar #"participantField": sorting participant field label in labkey study import os import json import re import sys import datetime def main(parameterFile): fhome=os.path.expanduser('~') fsetup=os.path.join(fhome,'.labkey','setup.json') with open(fsetup,'r') as f: setup=json.load(f) sys.path.insert(0,setup['paths']['nixWrapper']) sys.path.insert(0,setup['paths']['orthancInterface']) sys.path.insert(0,setup['paths']['labkeyInterface']) import nixWrapper #nixWrapper.loadLibrary("labkeyInterface") import labkeyInterface import labkeyDatabaseBrowser #nixWrapper.loadLibrary('orthancInterface') import orthancInterface import orthancDatabaseBrowser fconfig=os.path.join(fhome,'.labkey','network.json') net=labkeyInterface.labkeyInterface() net.init(fconfig) db=labkeyDatabaseBrowser.labkeyDB(net) onet=orthancInterface.orthancInterface() onet.init(fconfig) odb=orthancDatabaseBrowser.orthancDB(onet) with open(parameterFile) as f: pars=json.load(f) opars=pars['Orthanc'] project=opars['project'] participantField=opars['participantField'] patients=odb.getPatients() #equivalent for labkey side? dsDemo=db.selectRows(project,opars['schemaName'],\ opars['demographicsQuery'],[]) dsPatients=[row['OrthancId'] for row in dsDemo['rows']] pMissing=[p for p in patients if p not in dsPatients] print('Missing : {}'.format(len(pMissing))) #we need details for all patients (some might have just a study uploaded pdata={p:odb.getPatientData(p) for p in patients} #update patient data for missing patients prows=[] for p in pMissing: dicom=pdata[p]['MainDicomTags'] row={} pid=dicom['PatientID'] if len(pid)>32: pid=pid[:32] row[participantField]=pid try: row['birthDate']=datetime.datetime.strptime(dicom['PatientBirthDate'],'%Y-%m-%d %H:%M') except (KeyError,ValueError): pass row['PatientName']=dicom['PatientName'] row['OrthancId']=p prows.append(row) if len(prows)>0: resp=db.modifyRows('insert',project,opars['schemaName'],\ opars['demographicsQuery'],prows) print(resp) n=len(patients) i=0 #download the full images dataset (~1000 rows) ds=db.selectRows(project,opars['schemaName'],opars['queryName'],[]) for p in patients: dicom=pdata[p]['MainDicomTags'] patientId=dicom['PatientID'] if len(patientId)>32: patientId=patientId[:32] #get all studies for pateint #qfilter={'variable':participantField,'value':patientId,'oper':'eq'} dsStudies=[row['orthancStudy'] for row in ds['rows'] if row[participantField]==patientId] #number of entries for patient (if new must be inserted) seqNum=len(dsStudies) #unique dsStudies=list(set(dsStudies)) studies=pdata[p]['Studies'] pHex=p.split('-') print("[{:>3d}/{:>3d}] ID: {:>10s} [{}] Studies: {:>2}/{:>2} Entries in DB: {:>4d}".format(i,n,patientId,pHex[-1],len(dsStudies),len(studies),seqNum)) missingStudies=[s for s in studies if s not in dsStudies] #print('Missing studies: {}'.format(len(missingStudies))) srows=[] for s in missingStudies: sdata=odb.getStudyData(s) sdicom=sdata['MainDicomTags'] sid=sdicom['StudyInstanceUID'] #print('Data: {}'.format(sdata)) #this is try/except protetcted in previous version... sdate="19700101" try: sdate=datetime.datetime.strptime(sdicom['StudyDate'],'%Y-%m-%d %H:%M') except (KeyError,ValueError): pass #continue print('\tStudy[{}]: {}/{}'.format(sdate,s,sid)) for se in sdata['Series']: sedata=odb.getSeriesData(se) sedicom=sedata['MainDicomTags'] seid=sedicom['SeriesInstanceUID'] #print('Data: {}'.format(sedata)) seDesc="NONE" try: seDesc=sedicom['SeriesDescription'] except KeyError: pass #replace letters that might trip the database spanishOAcute=''.join([chr(3619),chr(3603)]) spanishAAcute=''.join([chr(3619),chr(3585)]) seDesc=re.sub(spanishOAcute,'o',seDesc) seDesc=re.sub(spanishAAcute,'a',seDesc) #this is a weird O that appears sometimes seDesc=seDesc.replace(chr(212),'O') print('\t\tSeries[{}]: {}/{}'.format(seDesc,se,seid)) #qfilter={'variable':'orthancSeries','value':se,'oper':'eq'} #ds=db.selectRows(project,opars['schemaName'],\ # opars['queryName'],[qfilter]) #count existing entries for patient #qfilter={'variable':participantField,'value':patientId,'oper':'eq'} #ds=db.selectRows(project,opars['schemaName'],\ #opars['queryName'],[qfilter]) # seqNum=len(ds['rows']) #create new row row={} row[participantField]=patientId row['sequenceNum']=seqNum row['orthancSeries']=se row['dicomStudy']=sid row['orthancStudy']=s row['dicomSeries']=seid row['studyDate']=sdate row['seriesDescription']=seDesc srows.append(row) seqNum+=1 #upload updates if len(srows)>0: db.modifyRows('insert',project,opars['schemaName'],\ opars['queryName'],srows) i+=1 print("Done") if __name__=="__main__": main(sys.argv[1])