iraemmBrowser.py 24 KB

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