populateImagingFromTransferList.py 7.3 KB

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