imageBrowser.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. import os
  2. import unittest
  3. from __main__ import vtk, qt, ctk, slicer
  4. from slicer.ScriptedLoadableModule import *
  5. import json
  6. import datetime
  7. import sys
  8. #
  9. # labkeySlicerPythonExtension
  10. #
  11. class imageBrowser(ScriptedLoadableModule):
  12. """Uses ScriptedLoadableModule base class, available at:
  13. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  14. """
  15. def __init__(self, parent):
  16. ScriptedLoadableModule.__init__(self, parent)
  17. self.parent.title = "irAEMM Browser" # TODO make this more human readable by adding spaces
  18. self.parent.categories = ["LabKey"]
  19. self.parent.dependencies = []
  20. self.parent.contributors = ["Andrej Studen (UL/FMF)"] # replace with "Firstname Lastname (Organization)"
  21. self.parent.helpText = """
  22. Interface to irAEMM files in LabKey
  23. """
  24. self.parent.acknowledgementText = """
  25. Developed within the medical physics research programme of the Slovenian research agency.
  26. """ # replace with organization, grant and thanks.
  27. #
  28. # labkeySlicerPythonExtensionWidget
  29. #
  30. class imageBrowserWidget(ScriptedLoadableModuleWidget):
  31. """Uses ScriptedLoadableModuleWidget base class, available at:
  32. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  33. """
  34. def setup(self):
  35. print("Setting up imageBrowserWidget")
  36. ScriptedLoadableModuleWidget.setup(self)
  37. # Instantiate and connect widgets ...
  38. fhome=os.path.expanduser('~')
  39. fsetup=os.path.join(fhome,'.labkey','setup.json')
  40. try:
  41. with open(fsetup) as f:
  42. self.setup=json.load(f)
  43. except FileNotFoundError:
  44. self.setup={}
  45. try:
  46. pt=self.setup['paths']
  47. except KeyError:
  48. self.setup['paths']={}
  49. lName='labkeyInterface'
  50. try:
  51. sys.path.append(self.setup['paths'][lName])
  52. except KeyError:
  53. self.setup['paths'][lName]=loadLibrary(lName)
  54. with open(fsetup,'w') as f:
  55. json.dump(self.setup,f,indent='\t')
  56. import labkeyInterface
  57. import labkeyDatabaseBrowser
  58. import labkeyFileBrowser
  59. self.network=labkeyInterface.labkeyInterface()
  60. setupCollapsibleButton = ctk.ctkCollapsibleButton()
  61. setupCollapsibleButton.text = "Setup"
  62. self.layout.addWidget(setupCollapsibleButton)
  63. #Form layout (maybe one can think of more intuitive layouts)
  64. setupFormLayout = qt.QFormLayout(setupCollapsibleButton)
  65. self.serverList=qt.QComboBox()
  66. self.serverList.addItem("ONKO-NIX")
  67. self.serverList.currentIndexChanged.connect(self.onServerListChanged)
  68. setupFormLayout.addRow("Database:",self.serverList)
  69. self.setupList=qt.QComboBox()
  70. self.setupList.addItem("limfomiPET_iBrowser.json")
  71. self.setupList.currentIndexChanged.connect(self.onSetupListChanged)
  72. setupFormLayout.addRow("Setup:",self.setupList)
  73. def onSetupListChanged(self,i):
  74. fileName=os.path.join(os.path.expanduser('~'),'.labkey',fName)
  75. with open(fileName,'r') as f:
  76. self.isetup=json.load(f)
  77. self.project=self.isetup['project']"iPNUMMretro/Study"
  78. self.schema='study'
  79. self.dataset=self.isetup['query']
  80. view=self.isetup['view']
  81. try:
  82. ds=self.db.selectRows(self.project,self.schema,self.dataset,
  83. [],self.isetup['view'])
  84. except KeyError:
  85. ds=self.db.selectRows(self.project,self.schema,self.dataset,[])
  86. ids=[row['PatientId'] for row in ds['rows']]
  87. ids=list(set(ids))
  88. #
  89. # Setup Area
  90. #
  91. #this comes up with the database
  92. self.participantField=qt.QLabel("PatientId")
  93. setupFormLayout.addRow("Participant field:",self.participantField)
  94. self.ctField=qt.QLabel("ctResampled")
  95. setupFormLayout.addRow("Data field (CT):",self.ctField)
  96. self.petField=qt.QLabel("petResampled")
  97. setupFormLayout.addRow("Data field (PET):",self.petField)
  98. self.segmentationField=qt.QLabel("Segmentation")
  99. setupFormLayout.addRow("Data field (Segmentation):",self.segmentationField)
  100. self.idField=qt.QLabel(self.network.getUserId()['displayName'])
  101. setupFormLayout.addRow("ID",self.idField)
  102. self.logic=imageBrowserLogic(self)
  103. #
  104. # Patients Area
  105. #
  106. patientsCollapsibleButton = ctk.ctkCollapsibleButton()
  107. patientsCollapsibleButton.text = "Patients"
  108. #don't add it yet
  109. #self.layout.addWidget(patientsCollapsibleButton)
  110. patientsFormLayout = qt.QFormLayout(patientsCollapsibleButton)
  111. self.patientList=qt.QComboBox()
  112. for id in ids:
  113. self.patientList.addItem(id)
  114. self.patientList.currentIndexChanged.connect(self.onPatientListChanged)
  115. patientsFormLayout.addRow("Patient:",self.patientList)
  116. self.visitList=qt.QComboBox()
  117. self.visitList.currentIndexChanged.connect(self.onVisitListChanged)
  118. patientsFormLayout.addRow("Visit:",self.visitList)
  119. self.ctCode=qt.QLabel("ctCode")
  120. patientsFormLayout.addRow("CT:",self.ctCode)
  121. self.petCode=qt.QLabel("petCode")
  122. patientsFormLayout.addRow("PET:",self.petCode)
  123. self.segmentationCode=qt.QLabel("segmentationCode")
  124. patientsFormLayout.addRow("Segmentation",self.segmentationCode)
  125. self.patientLoad=qt.QPushButton("Load")
  126. self.patientLoad.clicked.connect(self.onPatientLoadButtonClicked)
  127. patientsFormLayout.addRow("Load patient",self.patientLoad)
  128. self.patientClear=qt.QPushButton("Clear")
  129. self.patientClear.clicked.connect(self.onPatientClearButtonClicked)
  130. patientsFormLayout.addRow("Clear patient",self.patientClear)
  131. self.keepCached=qt.QCheckBox("keep Cached")
  132. self.keepCached.setChecked(1)
  133. patientsFormLayout.addRow("Keep cached",self.keepCached)
  134. #set to a defined state
  135. self.onPatientListChanged(0)
  136. #
  137. # Review Area
  138. #
  139. reviewCollapsibleButton = ctk.ctkCollapsibleButton()
  140. reviewCollapsibleButton.text = "Review"
  141. self.layout.addWidget(reviewCollapsibleButton)
  142. self.reviewBoxLayout = qt.QVBoxLayout(reviewCollapsibleButton)
  143. self.reviewFormLayout = qt.QFormLayout()
  144. self.reviewSegment=qt.QComboBox()
  145. self.reviewSegment.currentIndexChanged.connect(self.onReviewSegmentChanged)
  146. self.reviewFormLayout.addRow("Selected region:",self.reviewSegment)
  147. self.reviewResult=qt.QComboBox()
  148. self.reviewFormLayout.addRow("What do you think about the segmentation:",\
  149. self.reviewResult)
  150. reviewOptions=['Select','Excellent','Minor deficiencies',\
  151. 'Major deficiencies','Unusable']
  152. for opt in reviewOptions:
  153. self.reviewResult.addItem(opt)
  154. self.aeResult=qt.QComboBox()
  155. self.reviewFormLayout.addRow("Is organ suffering from adverse effect?",\
  156. self.aeResult)
  157. aeOptions=['Select','Yes','No']
  158. for opt in aeOptions:
  159. self.aeResult.addItem(opt)
  160. #self.aeResult.setCurrentIndex(0)
  161. self.updateReview=qt.QPushButton("Save")
  162. self.reviewFormLayout.\
  163. addRow("Save segmentation and AE decision for current segment",\
  164. self.updateReview)
  165. self.updateReview.clicked.connect(self.onUpdateReviewButtonClicked)
  166. self.reviewBoxLayout.addLayout(self.reviewFormLayout)
  167. submitFrame=qt.QGroupBox("Submit data")
  168. self.submitFormLayout=qt.QFormLayout()
  169. self.reviewComment=qt.QTextEdit("this is a test")
  170. self.submitFormLayout.addRow("Comments (optional)",\
  171. self.reviewComment)
  172. self.submitReviewButton=qt.QPushButton("Submit")
  173. self.submitFormLayout.addRow("Submit to database",\
  174. self.submitReviewButton)
  175. self.submitReviewButton.clicked.connect(self.onSubmitReviewButtonClicked)
  176. submitFrame.setLayout(self.submitFormLayout)
  177. submitFrame.setFlat(1)
  178. #submitFrame.setFrameShape(qt.QFrame.StyledPanel)
  179. #submitFrame.setFrameShadow(qt.QFrame.Sunken)
  180. submitFrame.setStyleSheet("background-color:rgba(220,215,180,45)")
  181. self.reviewBoxLayout.addWidget(submitFrame)
  182. def onServerListChanged(self,i):
  183. serverName=self.serverList.currentText
  184. fileName="NONE"
  185. if serverName=="ONKO-NIX":
  186. fileName="onko-nix.json"
  187. if fileName=="NONE":
  188. print("No path was associated with server {}".format(serverName))
  189. self.serverList.setStyleSheet('background-color: violet')
  190. return
  191. fconfig=os.path.join(os.path.expanduser('~'),'.labkey',fileName)
  192. self.network.init(fconfig)
  193. remoteId=self.network.getUserId()
  194. if remoteId==None:
  195. self.serverList.setStyleSheet('background-color: red')
  196. return
  197. self.serverList.setStyleSheet('background-color: green')
  198. self.db=labkeyDatabaseBrowser.labkeyDB(self.network)
  199. self.fb=labkeyFileBrowser.labkeyFileBrowser(self.network)
  200. def onPatientListChanged(self,i):
  201. idFilter={'variable':'PatientId','value':self.patientList.currentText,'oper':'eq'}
  202. ds=self.db.selectRows(self.project,self.schema,self.dataset, [idFilter],"segmentationReview")
  203. seq=[int(row['SequenceNum']) for row in ds['rows']]
  204. self.visitList.clear()
  205. for s in seq:
  206. self.visitList.addItem("Visit "+str(s))
  207. self.onVisitListChanged(0)
  208. def onVisitListChanged(self,i):
  209. try:
  210. s=self.visitList.currentText.split(' ')[1]
  211. except IndexError:
  212. return
  213. print("Visit: Selected item: {}->{}".format(i,s))
  214. idFilter={'variable':'PatientId',\
  215. 'value':self.patientList.currentText,'oper':'eq'}
  216. sFilter={'variable':'SequenceNum','value':s,'oper':'eq'}
  217. ds=self.db.selectRows(self.project,self.schema,self.dataset,[idFilter,sFilter],"segmentationReview")
  218. if not len(ds['rows'])==1:
  219. print("Found incorrect number {} of matches for [{}]/[{}]".\
  220. format(len(ds['rows']),\
  221. self.patientList.currentText,s))
  222. row=ds['rows'][0]
  223. #copy row properties for data access
  224. self.currentRow=row
  225. self.petCode.setText(row[self.petField.text])
  226. self.ctCode.setText(row[self.ctField.text])
  227. self.segmentationCode.setText(row[self.segmentationField.text])
  228. def onPatientLoadButtonClicked(self):
  229. print("Load")
  230. #delegate loading to logic
  231. #try:
  232. self.logic.loadImage(self.currentRow,self.keepCached.isChecked())
  233. segmentList=self.logic.compileSegmentation()
  234. #also bladder,vertebraL1, stomach, heart
  235. for seg in segmentList:
  236. if not seg in self.segmentList:
  237. continue
  238. #filter to most important ones
  239. self.reviewSegment.addItem(seg)
  240. self.logic.loadReview(self.currentRow)
  241. self.logic.loadAE(self.currentRow)
  242. for segment in self.segmentList:
  243. rIdx=self.logic.getReviewResult(segment)
  244. aIdx=self.logic.getAEResult(segment)
  245. print("Segment {}: {}/{}".format(segment,rIdx,aIdx))
  246. try:
  247. if (rIdx+aIdx)>0:
  248. self.updateResult(segment,rIdx,aIdx)
  249. except TypeError:
  250. continue
  251. try:
  252. self.reviewComment.setPlainText(self.logic.reviewComment)
  253. except AttributeError:
  254. pass
  255. self.onReviewSegmentChanged()
  256. #except AttributeError:
  257. # print("Missing current row")
  258. # return
  259. def onReviewSegmentChanged(self):
  260. segment=self.reviewSegment.currentText
  261. self.logic.hideSegments()
  262. self.logic.showSegment(segment)
  263. #set reviewFlag to stored value
  264. self.reviewResult.setCurrentIndex(self.logic.getReviewResult(segment))
  265. self.aeResult.setCurrentIndex(self.logic.getAEResult(segment))
  266. def onSubmitReviewButtonClicked(self):
  267. print("Submit")
  268. print("Selected review:{}/{}".format(self.reviewResult.currentIndex,
  269. self.reviewResult.currentText))
  270. print("Comment:{}".format(self.reviewComment))
  271. self.logic.submitReview(self.currentRow,\
  272. self.reviewComment.plainText)
  273. self.logic.submitAE(self.currentRow)
  274. def onUpdateReviewButtonClicked(self):
  275. print("Save")
  276. segment=self.reviewSegment.currentText
  277. self.logic.updateReview(segment,\
  278. self.reviewResult.currentIndex)
  279. self.logic.updateAE(segment,\
  280. self.aeResult.currentIndex)
  281. self.updateResult(segment,self.reviewResult.currentIndex,\
  282. self.aeResult.currentIndex)
  283. def updateResult(self,segment,reviewResult,aeResult):
  284. reviewText=self.reviewResult.itemText(reviewResult)
  285. aeText=self.aeResult.itemText(aeResult)
  286. idx=self.findCompletedSegment(segment)
  287. if idx<0:
  288. qReview=qt.QLabel(reviewText)
  289. self.submitFormLayout.insertRow(0,segment,qReview)
  290. qAE=qt.QLabel(aeText)
  291. self.submitFormLayout.insertRow(1,segment+'AE',qAE)
  292. try:
  293. self.segmentsCompleted.append(segment)
  294. self.segmentsCompleted.append(segment+'AE')
  295. except AttributeError:
  296. self.segmentsCompleted=[]
  297. self.segmentsCompleted.append(segment)
  298. self.segmentsCompleted.append(segment+'AE')
  299. else:
  300. qReview=self.submitFormLayout.itemAt(idx,1).widget()
  301. qReview.setText(reviewText)
  302. qAE=self.submitFormLayout.itemAt(idx+1,1).widget()
  303. qAE.setText(aeText)
  304. reviewColors=['pink','green','yellow','orange','red']
  305. qReview.setStyleSheet("background-color: "+reviewColors[reviewResult])
  306. aeColors=['pink','red','green']
  307. qAE.setStyleSheet("background-color: "+aeColors[aeResult])
  308. def findCompletedSegment(self,segment):
  309. for i in range(self.submitFormLayout.rowCount()):
  310. if self.submitFormLayout.itemAt(i,0).widget().text==segment:
  311. return i
  312. return -1
  313. def removeCompletedSegments(self):
  314. try:
  315. segments=self.segmentsCompleted
  316. except AttributeError:
  317. return
  318. for seg in segments:
  319. idx=self.findCompletedSegment(seg)
  320. if idx>-1:
  321. self.submitFormLayout.removeRow(idx)
  322. self.segmentsCompleted=[]
  323. def onPatientClearButtonClicked(self):
  324. self.logic.clearVolumesAndSegmentations()
  325. self.reviewSegment.clear()
  326. self.removeCompletedSegments()
  327. self.reviewComment.clear()
  328. def cleanup(self):
  329. pass
  330. #
  331. # dbBrowserLogic
  332. #
  333. class imageBrowserLogic(ScriptedLoadableModuleLogic):
  334. """This class should implement all the actual
  335. computation done by your module. The interface
  336. should be such that other python code can import
  337. this class and make use of the functionality without
  338. requiring an instance of the Widget.
  339. Uses ScriptedLoadableModuleLogic base class, available at:
  340. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  341. """
  342. def __init__(self,parent=None):
  343. ScriptedLoadableModuleLogic.__init__(self, parent)
  344. if not parent==None:
  345. #assume parent has the network set up
  346. self.parent=parent
  347. self.net=parent.network
  348. self.db=parent.db
  349. self.fb=parent.fb
  350. self.project=parent.project
  351. self.schema=parent.schema
  352. self.participantField=parent.participantField.text
  353. self.segmentList=parent.segmentList
  354. self.segLabel={'1':'liver','2':'spleen','3':'lung','4':'thyroid',\
  355. '5':'kidney','6':'pancreas','7':'gallbladder','8':'bladder',\
  356. '9':'aorta','10':'trachea','11':'sternum','12':'vertebraL1',\
  357. '13':'adrenal','14':'psoasMajor','15':'rectus',\
  358. '16':'bowel','17':'stomach','18':'heart'}
  359. def setLabkeyInterface(self,net):
  360. #additional way of setting the labkey network interface
  361. #if no parent was provided in logic initialization (stand-alone mode)
  362. self.net=net
  363. def setLabkeyProject(self,project):
  364. self.project=project
  365. def loadImage(self,row,keepCached):
  366. tempDir=os.path.join(os.path.expanduser('~'),'temp')
  367. if not os.path.isdir(tempDir):
  368. os.mkdir(tempDir)
  369. #fields={'ctResampled':True,'petResampled':False}
  370. fields={"CT":self.parent.ctField.text,\
  371. "PET":self.parent.petField.text,\
  372. "Segmentation":self.parent.segmentationField.text}
  373. relativePaths={x:['preprocessedImages',row['patientCode'],row['visitCode'],row[y]]\
  374. for (x,y) in fields.items()}
  375. self.volumeNode={}
  376. for f in relativePaths:
  377. p=relativePaths[f]
  378. localPath=os.path.join(tempDir,p[-1])
  379. if not os.path.isfile(localPath):
  380. #download from server
  381. remotePath=self.fb.formatPathURL(self.project,'/'.join(p))
  382. if not self.fb.entryExists(remotePath):
  383. print("Failed to get {}".format(remotePath))
  384. continue
  385. self.fb.readFileToFile(remotePath,localPath)
  386. properties={}
  387. #make sure segmentation gets loaded as a labelmap
  388. if f=="Segmentation":
  389. properties["labelmap"]=1
  390. self.volumeNode[f]=slicer.util.loadNodeFromFile(localPath,filetype='VolumeFile',properties=properties)
  391. if not keepCached:
  392. os.remove(localPath)
  393. #mimic abdominalCT standardized window setting
  394. self.volumeNode['CT'].GetScalarVolumeDisplayNode().\
  395. SetWindowLevel(1400, -500)
  396. #set colormap for PET to PET-Heat (this is a verbatim setting from
  397. #the Volumes->Display->Lookup Table colormap identifier)
  398. self.volumeNode['PET'].GetScalarVolumeDisplayNode().\
  399. SetAndObserveColorNodeID(\
  400. slicer.util.getNode('PET-Heat').GetID())
  401. slicer.util.setSliceViewerLayers(background=self.volumeNode['CT'],\
  402. foreground=self.volumeNode['PET'],foregroundOpacity=0.1,fit=True)
  403. #segmentations
  404. def compileSegmentation(self):
  405. try:
  406. labelmapVolumeNode = self.volumeNode['Segmentation']
  407. except KeyError:
  408. print("No segmentaion volumeNode available")
  409. return
  410. self.segmentationNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode')
  411. slicer.modules.segmentations.logic().\
  412. ImportLabelmapToSegmentationNode(labelmapVolumeNode, self.segmentationNode)
  413. segmentList=[]
  414. seg=self.segmentationNode.GetSegmentation()
  415. for i in range(seg.GetNumberOfSegments()):
  416. segment=seg.GetNthSegment(i)
  417. segment.SetName(self.segLabel[segment.GetName()])
  418. segmentList.append(segment.GetName())
  419. #seg.CreateClosedSurfaceRepresentation()
  420. slicer.mrmlScene.RemoveNode(labelmapVolumeNode)
  421. self.volumeNode.pop('Segmentation',None)
  422. #return list of segment names
  423. return segmentList
  424. def hideSegments(self):
  425. try:
  426. displayNode=self.segmentationNode.GetDisplayNode()
  427. except AttributeError:
  428. return
  429. seg=self.segmentationNode.GetSegmentation()
  430. for i in range(seg.GetNumberOfSegments()):
  431. #segment=self.segmentationNode.GetSegmentation().GetNthSegment(i)
  432. segmentID=seg.GetNthSegmentID(i)
  433. displayNode.SetSegmentVisibility(segmentID, False)
  434. #print("Done")
  435. def showSegment(self,name):
  436. try:
  437. displayNode=self.segmentationNode.GetDisplayNode()
  438. except AttributeError:
  439. return
  440. seg=self.segmentationNode.GetSegmentation()
  441. for i in range(seg.GetNumberOfSegments()):
  442. segment=seg.GetNthSegment(i)
  443. if not segment.GetName()==name:
  444. continue
  445. segmentID=seg.GetNthSegmentID(i)
  446. displayNode.SetSegmentVisibility(segmentID, True)
  447. break
  448. #print("Done")
  449. #clear
  450. def clearVolumesAndSegmentations(self):
  451. nodes=slicer.util.getNodesByClass("vtkMRMLVolumeNode")
  452. nodes.extend(slicer.util.getNodesByClass("vtkMRMLSegmentationNode"))
  453. res=[slicer.mrmlScene.RemoveNode(f) for f in nodes]
  454. self.segmentationNode=None
  455. self.reviewResult={}
  456. self.aeList={}
  457. #reviews by segment
  458. def updateReview(self,segment,value):
  459. try:
  460. self.reviewResult[segment]=value
  461. except AttributeError:
  462. self.reviewResult={}
  463. self.updateReview(segment,value)
  464. def getReviewResult(self,segment):
  465. try:
  466. return self.reviewResult[segment]
  467. except AttributeError:
  468. #review result not initialized
  469. return 0
  470. except KeyError:
  471. #segment not done yet
  472. return 0
  473. #load review from labkey
  474. def getUniqueRows(self, project, dataset, fields, inputRow):
  475. filters=[]
  476. for f in fields:
  477. filters.append({'variable':f,'value':str(inputRow[f]),'oper':'eq'})
  478. ds=self.db.selectRows(project,self.schema,dataset,filters)
  479. return ds['rows']
  480. def loadReview(self,currentRow):
  481. #see if we have already done a review
  482. currentRow['ModifiedBy']=self.net.getUserId()['id']
  483. rows=self.getUniqueRows(self.parent.project,self.parent.reviewDataset,\
  484. [self.participantField,'visitCode','Segmentation','ModifiedBy'],currentRow)
  485. if len(rows)==0:
  486. return
  487. row=rows[0]
  488. for label in self.segmentList:
  489. #name=self.segLabel[label]+'Review'
  490. name=label+'Review'
  491. try:
  492. self.updateReview(label,row[name])
  493. except KeyError:
  494. continue
  495. self.reviewComment=row['reviewComment']
  496. #submit review to labkey
  497. def submitReview(self,currentRow,comment):
  498. currentRow['ModifiedBy']=self.net.getUserId()['id']
  499. fields=[self.participantField,'visitCode','Segmentation','ModifiedBy']
  500. rows=self.getUniqueRows(self.parent.project,self.parent.reviewDataset,\
  501. fields,currentRow)
  502. mode='update'
  503. if len(rows)==0:
  504. mode='insert'
  505. row={}
  506. for f in fields:
  507. row[f]=currentRow[f]
  508. frows=self.getUniqueRows(self.parent.project,self.parent.reviewDataset,\
  509. [self.participantField,'visitCode'],currentRow)
  510. row['SequenceNum']=currentRow['SequenceNum']+0.01*len(frows)
  511. else:
  512. row=rows[0]
  513. seg=self.segmentationNode.GetSegmentation()
  514. for i in range(seg.GetNumberOfSegments()):
  515. segment=seg.GetNthSegment(i)
  516. fieldName=segment.GetName()+'Review'
  517. value=self.getReviewResult(segment.GetName())
  518. row[fieldName]=value
  519. row['reviewComment']=comment
  520. row['Date']=datetime.datetime.now().ctime()
  521. self.db.modifyRows(mode,self.parent.project,self.parent.schema,\
  522. self.parent.reviewDataset,[row])
  523. print("review submitted")
  524. #AE management
  525. def updateAE(self,segment,value):
  526. try:
  527. self.aeList[segment]=value
  528. except AttributeError:
  529. self.aeList={}
  530. self.updateAE(segment,value)
  531. def getAEResult(self,segment):
  532. try:
  533. return self.aeList[segment]
  534. except AttributeError:
  535. #review result not initialized (unknown)
  536. return 0
  537. except KeyError:
  538. #segment not done yet (unknown)
  539. return 0
  540. def loadAE(self,currentRow):
  541. currentRow['ModifiedBy']=self.net.getUserId()['id']
  542. fields=[self.participantField,'petResampled','ModifiedBy']
  543. rows=self.getUniqueRows(self.parent.project,self.parent.aeDataset,\
  544. fields,currentRow)
  545. if len(rows)==0:
  546. return
  547. print("Found {} rows".format(len(rows)))
  548. row=rows[0]
  549. for seg in self.segmentList:
  550. name=seg+'AE'
  551. try:
  552. self.updateAE(seg,row[name])
  553. except AttributeError:
  554. continue
  555. except KeyError:
  556. continue
  557. def submitAE(self,currentRow):
  558. currentRow['ModifiedBy']=self.net.getUserId()['id']
  559. fields=[self.participantField,'petResampled','ModifiedBy']
  560. rows=self.getUniqueRows(self.parent.project,self.parent.aeDataset,\
  561. fields,currentRow)
  562. if len(rows)==0:
  563. mode='insert'
  564. row={}
  565. for f in fields:
  566. row[f]=currentRow[f]
  567. frows=self.getUniqueRows(self.parent.project,self.parent.aeDataset,\
  568. [self.participantField,'visitCode'],currentRow)
  569. row['SequenceNum']=currentRow['SequenceNum']+0.01*len(frows)
  570. else:
  571. mode='update'
  572. row=rows[0]
  573. for seg in self.segmentList:
  574. row[seg+'AE']=self.getAEResult(seg)
  575. row['Date']=datetime.datetime.now().ctime()
  576. resp=self.db.modifyRows(mode,self.parent.project,self.parent.schema,\
  577. self.parent.aeDataset,[row])
  578. print("Response {}".format(resp))
  579. print("AE submitted")
  580. class irAEMMBrowserTest(ScriptedLoadableModuleTest):
  581. """
  582. This is the test case for your scripted module.
  583. Uses ScriptedLoadableModuleTest base class, available at:
  584. https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  585. """
  586. def setUp(self):
  587. """ Do whatever is needed to reset the state - typically a scene clear will be enough.
  588. """
  589. slicer.mrmlScene.Clear(0)
  590. def runTest(self):
  591. """Run as few or as many tests as needed here.
  592. """
  593. self.setUp()
  594. self.test_irAEMMBrowser()
  595. def test_irAEMMBrowser(self):
  596. """ Ideally you should have several levels of tests. At the lowest level
  597. tests sould exercise the functionality of the logic with different inputs
  598. (both valid and invalid). At higher levels your tests should emulate the
  599. way the user would interact with your code and confirm that it still works
  600. the way you intended.
  601. One of the most important features of the tests is that it should alert other
  602. developers when their changes will have an impact on the behavior of your
  603. module. For example, if a developer removes a feature that you depend on,
  604. your test should break so they know that the feature is needed.
  605. """
  606. self.delayDisplay("Starting the test")
  607. #
  608. # first, get some data
  609. #