scanOrthancQuick.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #labkey/orthanc interface
  2. #scans the orthanc internal database and fills labkey table
  3. #"Orthanc" section expected in the configuration file, with
  4. #"queryName": query to be filled for each image
  5. #"demographicsQuery": query to be filled for every patient
  6. #"schemaName": the schema both queries are part of
  7. #"project": name of the project under labkey that collects Orthanc data,
  8. # typically named Orthanc or similar
  9. #"participantField": sorting participant field label in labkey study
  10. import os
  11. import json
  12. import re
  13. import sys
  14. def main(parameterFile):
  15. fhome=os.path.expanduser('~')
  16. fsetup=os.path.join(fhome,'.labkey','setup.json')
  17. with open(fsetup,'r') as f:
  18. setup=json.load(f)
  19. with open(parameterFile) as f:
  20. pars=json.load(f)
  21. sys.path.insert(0,setup['paths']['nixWrapper'])
  22. import nixWrapper
  23. nixWrapper.loadLibrary("labkeyInterface")
  24. import labkeyInterface
  25. import labkeyDatabaseBrowser
  26. nixWrapper.loadLibrary('orthancInterface')
  27. import orthancInterface
  28. import orthancDatabaseBrowser
  29. try:
  30. networkSetup=pars['networkSetup']
  31. except KeyError:
  32. networkSetup='network.json'
  33. fconfig=os.path.join(fhome,'.labkey',networkSetup)
  34. if not os.path.isfile(fconfig):
  35. print('Failed to find network configuration {}'.format(fconfig))
  36. return
  37. net=labkeyInterface.labkeyInterface()
  38. net.init(fconfig)
  39. db=labkeyDatabaseBrowser.labkeyDB(net)
  40. onet=orthancInterface.orthancInterface()
  41. onet.init(fconfig)
  42. odb=orthancDatabaseBrowser.orthancDB(onet)
  43. opars=pars['Orthanc']
  44. project=opars['project']
  45. participantField=opars['participantField']
  46. patients=odb.getPatients()
  47. #equivalent for labkey side?
  48. dsDemo=db.selectRows(project,opars['schemaName'],\
  49. opars['demographicsQuery'],[])
  50. dsPatients=[row['OrthancId'] for row in dsDemo['rows']]
  51. pMissing=[p for p in patients if p not in dsPatients]
  52. print('Missing : {}'.format(len(pMissing)))
  53. #we need details for all patients (some might have just a study uploaded
  54. pdata={p:odb.getPatientData(p) for p in patients}
  55. #update patient data for missing patients
  56. prows=[]
  57. for p in pMissing:
  58. dicom=pdata[p]['MainDicomTags']
  59. row={}
  60. row[participantField]=dicom['PatientID']
  61. row['birthDate']=dicom['PatientBirthDate']
  62. row['PatientName']=dicom['PatientName']
  63. row['OrthancId']=p
  64. prows.append(row)
  65. db.modifyRows('insert',project,opars['schemaName'],\
  66. opars['demographicsQuery'],prows)
  67. n=len(patients)
  68. i=0
  69. #download the full images dataset (~1000 rows)
  70. ds=db.selectRows(project,opars['schemaName'],opars['queryName'],[])
  71. for p in patients:
  72. dicom=pdata[p]['MainDicomTags']
  73. patientId=dicom['PatientID']
  74. #get all studies for pateint
  75. #qfilter={'variable':participantField,'value':patientId,'oper':'eq'}
  76. dsStudies=[row['orthancStudy'] for row in ds['rows'] if row[participantField]==patientId]
  77. #number of entries for patient (if new must be inserted)
  78. seqNum=len(dsStudies)
  79. #unique
  80. dsStudies=list(set(dsStudies))
  81. studies=pdata[p]['Studies']
  82. pHex=p.split('-')
  83. print("[{:>3d}/{:>3d}] ID: {:>10s} [{}] Studies: {:>2}/{:>2} Entries in DB: {:>4d}".format(i,n,patientId,pHex[-1],len(dsStudies),len(studies),seqNum))
  84. missingStudies=[s for s in studies if s not in dsStudies]
  85. #print('Missing studies: {}'.format(len(missingStudies)))
  86. srows=[]
  87. for s in missingStudies:
  88. sdata=odb.getStudyData(s)
  89. sdicom=sdata['MainDicomTags']
  90. sid=sdicom['StudyInstanceUID']
  91. #print('Data: {}'.format(sdata))
  92. #this is try/except protetcted in previous version...
  93. sdate="19700101"
  94. try:
  95. sdate=sdicom['StudyDate']
  96. except KeyError:
  97. pass
  98. #continue
  99. print('\tStudy[{}]: {}/{}'.format(sdate,s,sid))
  100. for se in sdata['Series']:
  101. sedata=odb.getSeriesData(se)
  102. sedicom=sedata['MainDicomTags']
  103. seid=sedicom['SeriesInstanceUID']
  104. #print('Data: {}'.format(sedata))
  105. seDesc="NONE"
  106. try:
  107. seDesc=sedicom['SeriesDescription']
  108. except KeyError:
  109. pass
  110. #replace letters that might trip the database
  111. spanishOAcute=''.join([chr(3619),chr(3603)])
  112. spanishAAcute=''.join([chr(3619),chr(3585)])
  113. seDesc=re.sub(spanishOAcute,'o',seDesc)
  114. seDesc=re.sub(spanishAAcute,'a',seDesc)
  115. print('\t\tSeries[{}]: {}/{}'.format(seDesc,se,seid))
  116. #qfilter={'variable':'orthancSeries','value':se,'oper':'eq'}
  117. #ds=db.selectRows(project,opars['schemaName'],\
  118. # opars['queryName'],[qfilter])
  119. #count existing entries for patient
  120. #qfilter={'variable':participantField,'value':patientId,'oper':'eq'}
  121. #ds=db.selectRows(project,opars['schemaName'],\
  122. #opars['queryName'],[qfilter])
  123. # seqNum=len(ds['rows'])
  124. #create new row
  125. row={}
  126. row[participantField]=patientId
  127. row['sequenceNum']=seqNum
  128. row['orthancSeries']=se
  129. row['dicomStudy']=sid
  130. row['orthancStudy']=s
  131. row['dicomSeries']=seid
  132. row['studyDate']=sdate
  133. row['seriesDescription']=seDesc
  134. srows.append(row)
  135. seqNum+=1
  136. #upload updates
  137. if len(srows)>0:
  138. db.modifyRows('insert',project,opars['schemaName'],\
  139. opars['queryName'],srows)
  140. i+=1
  141. print("Done")
  142. if __name__=="__main__":
  143. main(sys.argv[1])