populateImagingFromTransferList.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #date sorts studies from orthanc dataset into target study dataset
  2. #takes transferQuery as the list of images that should be available on orthanc
  3. import os
  4. import json
  5. import re
  6. import sys
  7. import datetime
  8. import re
  9. def main(parameterFile):
  10. fhome=os.path.expanduser('~')
  11. fsetup=os.path.join(fhome,'.labkey','setup.json')
  12. with open(fsetup,'r') as f:
  13. setup=json.load(f)
  14. sys.path.insert(0,setup['paths']['nixWrapper'])
  15. import nixWrapper
  16. nixWrapper.loadLibrary("labkeyInterface")
  17. import labkeyInterface
  18. import labkeyDatabaseBrowser
  19. import labkeyFileBrowser
  20. fconfig=os.path.join(fhome,'.labkey','network.json')
  21. net=labkeyInterface.labkeyInterface()
  22. net.init(fconfig)
  23. db=labkeyDatabaseBrowser.labkeyDB(net)
  24. fb=labkeyFileBrowser.labkeyFileBrowser(net)
  25. with open(parameterFile,'r') as f:
  26. pars=json.load(f)
  27. i=0
  28. #from orthancDatabase/Imaging dataset
  29. projectOrthanc=pars['Orthanc']['project']
  30. inputQuery=pars['Orthanc']['queryName']
  31. inputSchema=pars['Orthanc']['schemaName']
  32. inputParticipantField=pars['Orthanc']['participantField']
  33. #to target project dataset
  34. projectStudy=pars['Database']['project']
  35. #'iPNUMMretro/Study'
  36. #for prospective, set
  37. #projectStudy='IPNUMMprospektiva/Study'
  38. outputQuery=pars['Database']['queryName']
  39. outputSchema=pars['Database']['schemaName']
  40. #select patientId that are contained in the demographics dataset
  41. transferQuery=pars['Database']['transferQuery']
  42. dbParticipantField=pars['Database']['participantField']
  43. missingSchema=pars['Database']['missingImagesSchema']
  44. missingQuery=pars['Database']['missingImagesQuery']
  45. #make a list of images from transferQuery
  46. dsImage=db.selectRows(projectStudy,outputSchema,transferQuery,[])
  47. for im in dsImage['rows']:
  48. #for orthanc
  49. inputIdFilter={'variable':inputParticipantField,\
  50. 'value':im[dbParticipantField],\
  51. 'oper':'eq'}
  52. #for database
  53. idFilter={'variable':dbParticipantField,\
  54. 'value':im[dbParticipantField],\
  55. 'oper':'eq'}
  56. seqNum=im['imagingVisitId']
  57. seqFilter={'variable':'SequenceNum','value':str(seqNum),'oper':'eq'}
  58. #for speedup one should check if a match was already done in Database/queryName
  59. #ds1 are the matching outputs in target dataset
  60. ds1=db.selectRows(projectStudy,outputSchema,outputQuery,\
  61. [idFilter,seqFilter])
  62. if len(ds1['rows'])>1:
  63. #never happens (idFilter and seqFilter)
  64. print('ERROR: too many matches in {} for {}/{}'.\
  65. format(outputQuery,im[dbParticipantField],seqNum))
  66. continue
  67. if len(ds1['rows'])>0:
  68. #just the one match, fine
  69. print('Entry for {}/{} already resolved'.\
  70. format(im[dbParticipantField],seqNum))
  71. entry=ds1['rows'][0]
  72. try:
  73. if len(entry['PETWB_orthancId'])>0 and len(entry['CT_orthancId'])>0:
  74. continue
  75. except:
  76. pass
  77. print('Debug mode for missing cases')
  78. #have to convert from datetime to %Y%m%d format
  79. #dateFilter={'variable':'imageDate','value':im['imageDate'],'oper':'eq'}
  80. dsOrthanc=db.selectRows(projectOrthanc,inputSchema,inputQuery,[inputIdFilter])
  81. #what if dsOrthanc['rows'] is empty?
  82. #this is part of QA and is reported in missingImages Schema/Query
  83. #convert dates
  84. sDates={r['orthancSeries']:datetime.datetime.strptime(r['studyDate'],'%Y/%m/%d %H:%M:%S')
  85. for r in dsOrthanc['rows']}
  86. sDates={x:sDates[x].strftime('%Y%m%d') for x in sDates}
  87. #unique dates (for date mismatch)
  88. dates=list(set(sDates.values()))
  89. #select dates
  90. sDatesMatch={x:sDates[x] for x in sDates if sDates[x]==im['imageDate']}
  91. #select
  92. rowsMatch=[r for r in dsOrthanc['rows'] if r['orthancSeries'] in list(sDatesMatch.keys())]
  93. #select CT matches
  94. rowsCT=[r for r in rowsMatch if r['seriesDescription'].find('CT WB')==0]
  95. #should not include fov
  96. rowsCT=[r for r in rowsCT if r['seriesDescription'].find('fov')<0]
  97. print('entry[{}/{}] rowsCT : {}'.format(im[dbParticipantField],seqNum,rowsCT))
  98. #should be one and one only
  99. rowsPET=[r for r in rowsMatch if r['seriesDescription']=='PET WB']
  100. print('entry[{}/{}] rowsPET: {}'.format(im[dbParticipantField],seqNum,rowsPET))
  101. dataRow={}
  102. if len(rowsCT)==1:
  103. dataRow=rowsCT[0]
  104. if len(dataRow)==0:
  105. if len(rowsPET)==1:
  106. dataRows=rowsPET[0]
  107. #deal with erroneous outcomes (ie- no CT, more then 1 CT, no PET, more than 1 PET)
  108. if len(rowsPET)!=1 or len(rowsCT)!=1:
  109. #standard spiel - find if already present; if so, skip, if not, add
  110. imFilter={'variable':'imagingVisitId',
  111. 'value':'{}'.format(im['imagingVisitId']),
  112. 'oper':'eq'}
  113. dsMissing=db.selectRows(projectStudy,missingSchema,\
  114. missingQuery,[idFilter,imFilter])
  115. #already recorded
  116. vals=[dbParticipantField,'imagingVisitId','imageDate']
  117. mode='insert'
  118. if len(dsMissing['rows'])>0:
  119. mode='update'
  120. orow=dsMissing['rows'][0]
  121. else:
  122. orow={v:im[v] for v in vals}
  123. orow['imageDateMismatch']=','.join(dates)
  124. failDesc=''
  125. if len(rowsPET)==0:
  126. failDesc+='[MISSSING PET]'
  127. if len(rowsPET)>1:
  128. failDesc+='[MULTIPLE PET]'
  129. if len(rowsCT)==0:
  130. failDesc+='[MISSSING CT]'
  131. if len(rowsCT)>1:
  132. failDesc+='[MULTIPLE CT]'
  133. orow['failureDescription']=failDesc
  134. db.modifyRows(mode,projectStudy,missingSchema,\
  135. missingQuery,[orow])
  136. #don't break, but fill only for singular outcomes
  137. #figure out which row in output study to update
  138. filters=[]
  139. print('Participant {} Sequence number {}'.\
  140. format(im[dbParticipantField],str(seqNum)))
  141. #refresh the matching study list (after CT we have found PET)
  142. ds1=db.selectRows(projectStudy,outputSchema,outputQuery,\
  143. [idFilter,seqFilter])
  144. mode='update'
  145. outRow={}
  146. if len(ds1['rows'])==0:
  147. mode='insert'
  148. outRow[dbParticipantField]=im[dbParticipantField]
  149. outRow['SequenceNum']=seqNum
  150. if len(dataRow)==1:
  151. outRow['dicomStudy']=dataRow['dicomStudy']
  152. else:
  153. #never happens if we check for sd1 before matches are found
  154. outRow=ds1['rows'][0]
  155. if len(rowsPET)==1:
  156. outRow['PET_orthancId']=rowsPET[0]['orthancSeries']
  157. if len(rowsCT)==1:
  158. outRow['CT_orthancId']=rowsCT[0]['orthancSeries']
  159. if len(dataRow)==1:
  160. outRow['studyDate']=dataRow['studyDate']
  161. outRow['imagingVisitId']=im['imagingVisitId']
  162. outRow['visitCode']='VISIT_'+str(im['imagingVisitId'])
  163. modifyStatus=db.modifyRows(mode,projectStudy,outputSchema,\
  164. outputQuery,[outRow])
  165. print('{}'.format(modifyStatus))
  166. print("Done")
  167. if __name__=='__main__':
  168. main(sys.argv[1])