浏览代码

Version that can store, align and export aligned CTs and segmentations. Alignment is still manual, MR/CT for head-neck settings of elastix used.

Andrej Studen @ Win7 5 年之前
父节点
当前提交
1634c1adc1
共有 6 个文件被更改,包括 1010 次插入188 次删除
  1. 233 0
      dataExplorer/CTRegistration.py
  2. 34 177
      dataExplorer/dataExplorer.py
  3. 353 0
      dataExplorer/exportDicom.py
  4. 270 0
      dataExplorer/importDicom.py
  5. 112 11
      dataExplorer/loadPatient.py
  6. 8 0
      onko-nix.json

+ 233 - 0
dataExplorer/CTRegistration.py

@@ -0,0 +1,233 @@
+from slicer.ScriptedLoadableModule import *
+import slicerNetwork
+import qt,vtk,ctk,slicer
+import os
+import resample
+import exportDicom
+import loadPatient
+
+
+class CTRegistration(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
+  def __init__(self,parent):
+        slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self, parent)
+        self.className="CTRegistration"
+        self.parent.title="CTRegistration"
+        self.parent.categories = ["Examples"]
+        self.parent.dependencies = []
+        self.parent.contributors = ["Andrej Studen (University of Ljubljana)"] # replace with "Firstname Lastname (Organization)"
+        self.parent.helpText = """
+        This is an example of scripted loadable module bundled in an extension.
+        It performs registration of CT (EBRT) and MRI (BRT)
+        """
+        self.parent.helpText += self.getDefaultModuleDocumentationLink()
+        self.parent.acknowledgementText = """
+        This extension developed within Medical Physics research programe of ARRS
+        """ # replace with organization, grant and thanks.
+
+#
+# dataExplorerWidget
+
+
+
+class CTRegistrationWidget(ScriptedLoadableModuleWidget):
+  """Uses ScriptedLoadableModuleWidget base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+
+  def setup(self):
+      ScriptedLoadableModuleWidget.setup(self)
+      self.logic=CTRegistrationLogic(self)
+
+      try:
+           fhome=os.environ["HOME"]
+      except:
+              #in windows, the variable is called HOMEPATH
+           fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
+
+      cfgPath=os.path.join(fhome,".labkey")
+      cfgPath=os.path.join(cfgPath,"onko-nix.json")
+
+      self.onkoNet=slicerNetwork.labkeyURIHandler()
+      self.onkoNet.parseConfig(cfgPath)
+      self.onkoNet.initRemote()
+
+      self.project='EMBRACE/Studija'
+
+      datasetCollapsibleButton = ctk.ctkCollapsibleButton()
+      datasetCollapsibleButton.text = "Node data"
+      self.layout.addWidget(datasetCollapsibleButton)
+      # Layout within the dummy collapsible button
+      datasetFormLayout = qt.QFormLayout(datasetCollapsibleButton)
+
+      self.patientId=qt.QLineEdit("LJU004")
+      datasetFormLayout.addRow("EMBRACE ID:",self.patientId)
+
+      loadDataButton=qt.QPushButton("Load")
+      loadDataButton.clicked.connect(self.onLoadDataButtonClicked)
+      datasetFormLayout.addRow("Data:",loadDataButton)
+
+      self.exportButton=qt.QPushButton("Export")
+      self.exportButton.clicked.connect(self.onExportButtonClicked)
+      datasetFormLayout.addRow("Export:",self.exportButton)
+
+
+
+  def onLoadDataButtonClicked(self):
+      self.logic.loadData(self.onkoNet,self.patientId.text)
+
+  def onExportButtonClicked(self):
+      self.logic.export(self.onkoNet,self.project,self.patientId.text)
+
+
+
+class CTRegistrationLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
+  def __init__(self,parent):
+       slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
+       self.exporter=exportDicom.exportDicomLogic(self)
+       self.importer=loadPatient.loadPatientLogic(self)
+       self.resampler=resample.resampleLogic(self)
+
+       try:
+           fhome=os.environ["HOME"]
+       except:
+              #in windows, the variable is called HOMEPATH
+           fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
+
+
+  def getLocalRegistrationPath(self,net,project,patientId):
+      path=os.path.join(net.GetLocalCacheDirectory(),project)
+      path=os.path.join(path,"%40files")
+      path=os.path.join(path,patientId)
+      path=os.path.join(path,"Registration")
+      if not os.path.isdir(path):
+          os.mkdir(path)
+
+      relDir=net.GetRelativePathFromLocalPath(path)
+      remoteDir=net.GetLabkeyPathFromRelativePath(relDir)
+
+      if not net.isRemoteDir(remoteDir):
+          net.mkdir(remoteDir)
+      return path
+
+
+  def loadData(self,net,patientId):
+      self.importer.setURIHandler(net)
+      self.ct=self.importer.loadCT(patientId)
+      if len(self.ct)<1:
+          print("No CT found for patient {}").format(patientId)
+          return
+      if len(self.ct)>1:
+          print("Too many CT volumes found for patient {}").format(patientId)
+          return
+      volumeNode=self.ct[0]['node']
+      volumeNode.SetName(patientId+"_CT")
+      self.ctrs=self.importer.loadCTRS(patientId)
+      if len(self.ctrs)<1:
+          print("No CT-segmentation found for patient {}").format(patientId)
+          return
+      if len(self.ctrs)>1:
+          print("Multiple CT-segmentations found for patient {}").format(patientId)
+          return
+      segNode=self.ctrs[0]['node']
+      segNode.SetName(patientId+"_CTRS")
+
+      self.dmr=self.importer.loadDMR(patientId)
+      if len(self.dmr)<1:
+          print("No DMR found for patient {}").format(patientId)
+          return
+      if len(self.dmr)>1:
+          print("Multiple DMR found for patient {}").format(patientId)
+          return
+      dmrMetadata=self.dmr[0]['metadata']
+      if len(dmrMetadata['frameOfReferenceInstanceUid'])<1:
+          refId=self.exporter.generateFrameOfReferenceUUID('volume')
+          dmrMetadata['frameOfReferenceInstanceUid']=refId
+      self.dmr[0]['node'].SetName(patientId+"_DMR")
+
+  def exportFile(self,net,path):
+      print("localPath: {}").format(path)
+      relativePath=net.GetRelativePathFromLocalPath(path)
+      print("relativePath: {}").format(relativePath)
+      remotePath=net.GetLabkeyPathFromRelativePath(relativePath)
+      print("remotePath: {}").format(relativePath)
+      net.copyLocalFileToRemote(path,remotePath)
+
+
+
+  def exportTransformation(self,net,project,patientId):
+     tNodeName=patientId+"_T2_DF"
+     tNode=slicer.util.getFirstNodeByName(tNodeName)
+     path=self.getLocalRegistrationPath(net,project,patientId)
+     fname=tNodeName+".h5"
+     path=os.path.join(path,fname)
+     slicer.util.saveNode(tNode,path)
+     self.exportFile(net,path)
+     return tNode
+
+
+  def exportSegmentation(self,net,project,patientId,tNode):
+
+      #DMR
+      dmrNode=self.dmr[0]['node']
+      dmrMetadata=self.dmr[0]['metadata']
+
+      #segmentations
+      segNode=self.ctrs[0]['node']
+      segMetadata=self.ctrs[0]['metadata']
+      segMetadata['frameOfReferenceInstanceUid']=dmrMetadata['frameOfReferenceInstanceUid']
+      nSeg=segNode.GetSegmentation().GetNumberOfSegments()
+
+      for i in range(0,nSeg):
+         segId=segNode.GetSegmentation().GetNthSegmentID(i)
+         segment=segNode.GetSegmentation().GetSegment(segId)
+         nodeName=segNode.GetName()+'_'+segId
+         node=slicer.util.getFirstNodeByName(nodeName)
+
+         if node==None:
+            segNode.SetAndObserveTransformNodeID(tNode.GetID())
+            binaryRep={'node':segNode,
+                      'mask':segNode.GetBinaryLabelmapRepresentation(segID)}
+            self.resampler.rebinSegment(dmrNode,binaryRep)
+            node=slicer.util.getFirstNodeByName(nodeName)
+
+         segMetadata['seriesInstanceUid']=self.exporter.generateSeriesUUID('segmentation')
+         segMetadata['seriesNumber']=i
+         segMetadata['patientId']=patientId
+
+         self.exporter.exportNode(net,project,node,segMetadata)
+
+  def exportCT(self,net,project,patientId,tNode):
+             #DMR
+         dmrNode=self.dmr[0]['node']
+         dmrMetadata=self.dmr[0]['metadata']
+
+         #CT (rebinned)
+         ctNode=self.ct[0]['node']
+         ctMetadata=self.ct[0]['metadata']
+         ctMetadata['frameOfReferenceInstanceUid']=dmrMetadata['frameOfReferenceInstanceUid']
+         ctMetadata['seriesInstanceUid']=self.exporter.generateSeriesUUID('segmentation')
+         ctMetadata['patientId']=patientId
+
+         ctNode=self.ct[0]['node']
+         ctName=ctNode.GetName()
+         ctRBName=ctNode.GetName()+"_TF"
+         ctRBNode=slicer.util.getFirstNodeByName(ctRBName)
+         if ctRBNode==None:
+             ctNode.SetAndObserveTransformNodeID(tNode.GetID())
+             ctRBNode=self.resampler.rebinNode(ctNode,dmrNode)
+             ctRBNode.SetName(ctRBName)
+             ctNode.SetName(ctName)
+         self.exporter.exportNode(net,project,ctRBNode,ctMetadata)
+
+
+
+
+  def export(self,net,project,patientId):
+      #transformation
+      tNode=self.exportTransformation(net,project,patientId)
+
+      self.exportSegmentation(net,project,patientId,tNode)
+      #tNodeName=patientId+"_T2_DF"
+      #tNode=slicer.util.getFirstNodeByName(tNodeName)
+
+      self.exportCT(net,project,patientId,tNode)

+ 34 - 177
dataExplorer/dataExplorer.py

@@ -48,74 +48,16 @@ class dataExplorerWidget(ScriptedLoadableModuleWidget):
     #
     # Parameters Area
     #
-    try:
-      self.startDir=os.path.join(os.environ['HOME'],"temp/crt")
-    except:
-      fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
-      self.startDir=os.path.join(fhome,"temp")
-
+    basePath='D:\\Sw\\src\\EMBRACE'
+    netConfig=os.path.join(basePath,'onko-nix.json')
     self.sNet=slicerNetwork.labkeyURIHandler()
-    self.loadPatientLogic.setURIHandler(self.sNet)
-
-    configCollapsibleButton = ctk.ctkCollapsibleButton()
-    configCollapsibleButton.text = "Configuration"
-    self.layout.addWidget(configCollapsibleButton)
-
-    # Layout within the dummy collapsible button
-    configFormLayout = qt.QFormLayout(configCollapsibleButton)
-
-    self.configDir='/afs/f9.ijs.si/home/studen/software/src/embraceParse'
-    self.loadConfigButton=qt.QPushButton("Load Config")
-    self.loadConfigButton.connect('clicked(bool)',self.onLoadConfigButtonClicked)
-    configFormLayout.addRow("Configuration:",self.loadConfigButton)
-
-
-    ## connection sub-area
-
-    connectionCollapsibleButton = ctk.ctkCollapsibleButton()
-    connectionCollapsibleButton.text = "Connection"
-    self.layout.addWidget(connectionCollapsibleButton)
-
-    # Layout within the dummy collapsible button
-    connectionFormLayout = qt.QFormLayout(connectionCollapsibleButton)
-
-    #
-    # input volume selector
-    #
-    self.server=qt.QLineEdit("https://merlin.fmf.uni-lj.si")
-    connectionFormLayout.addRow("Server: ", self.server)
-
-    self.userCertButton=qt.QPushButton("Load")
-    self.userCertButton.toolTip="Load user certificate (crt)"
-    self.userCertButton.connect('clicked(bool)',self.onUserCertButtonClicked)
-    connectionFormLayout.addRow("User certificate:",self.userCertButton)
+    self.sNet.parseConfig(netConfig)
+    self.sNet.initRemote()
 
-    self.userKeyButton=qt.QPushButton("Load")
-    self.userKeyButton.toolTip="Load user key (key)"
-    self.userKeyButton.connect('clicked(bool)',self.onUserKeyButtonClicked)
-    connectionFormLayout.addRow("User key:",self.userKeyButton)
-
-    self.userCAButton=qt.QPushButton("Load")
-    self.userCAButton.toolTip="Load CA certificate (crt)"
-    self.userCAButton.connect('clicked(bool)',self.onUserCAButtonClicked)
-    connectionFormLayout.addRow("User certificate:",self.userCAButton)
-
-    self.authName=qt.QLineEdit("email")
-    #self.authName.textChanged.connect(self.updateAuthName)
-    connectionFormLayout.addRow("Labkey username: ", self.authName)
-
-    self.authPass=qt.QLineEdit("email")
-    self.authPass.setEchoMode(qt.QLineEdit.Password)
-    #self.authPass.textChanged.connect(self.updateAuthPass)
-    connectionFormLayout.addRow("Labkey password: ", self.authPass)
-
-    self.initButton=qt.QPushButton("Init")
-    self.initButton.toolTip="Init connection"
-    self.initButton.connect('clicked(bool)',self.onInitButtonClicked)
-    connectionFormLayout.addRow("Connection:",self.initButton)
+    self.loadPatientLogic.setURIHandler(self.sNet)
+    self.project="EMBRACE/Studija"
+    self.dataset="ImagingVisitsManaged"
 
-    # Add vertical spacer
-    self.layout.addStretch(1)
 
     datasetCollapsibleButton = ctk.ctkCollapsibleButton()
     datasetCollapsibleButton.text = "Dataset"
@@ -123,15 +65,24 @@ class dataExplorerWidget(ScriptedLoadableModuleWidget):
     # Layout within the dummy collapsible button
     datasetFormLayout = qt.QFormLayout(datasetCollapsibleButton)
 
-    self.datasetName=qt.QLineEdit("datasetName")
-    datasetFormLayout.addRow("Dataset:",self.datasetName)
-
-    self.datasetProject=qt.QLineEdit("datasetProject")
-    datasetFormLayout.addRow("Project:",self.datasetProject)
-
     self.datasetButton=qt.QPushButton("Load")
     self.datasetButton.connect('clicked(bool)',self.onDatasetLoadButtonClicked)
-    datasetFormLayout.addRow("Project:",self.datasetButton)
+    datasetFormLayout.addRow("Data:",self.datasetButton)
+
+    self.patientId=qt.QLineEdit("LJU004")
+    datasetFormLayout.addRow("Patient ID:",self.patientId)
+
+    loadCTButton=qt.QPushButton("CT")
+    loadCTButton.clicked.connect(self.onLoadCTButtonClicked)
+    datasetFormLayout.addRow("Load:",loadCTButton)
+
+    loadCTRSButton=qt.QPushButton("CT-RS")
+    loadCTRSButton.clicked.connect(self.onLoadCTRSButtonClicked)
+    datasetFormLayout.addRow("Load:",loadCTRSButton)
+
+    loadDMRButton=qt.QPushButton("DMR")
+    loadDMRButton.clicked.connect(self.onLoadDMRButtonClicked)
+    datasetFormLayout.addRow("Load:",loadDMRButton)
 
 
 
@@ -156,114 +107,11 @@ class dataExplorerWidget(ScriptedLoadableModuleWidget):
   def cleanup(self):
     pass
 
-  def onLoadConfigButtonClicked(self):
-      filename=qt.QFileDialog.getOpenFileName(None,'Open configuration file (JSON)',
-           self.configDir, '*.json')
-      with open(filename,'r') as f:
-          dt=json.load(f)
-      if dt.has_key('host'):
-          self.server.setText(dt['host'])
-      if dt.has_key('dataset'):
-          self.datasetName.setText(dt['dataset'])
-      if dt.has_key('project'):
-          self.datasetProject.setText(dt['project'])
-      if dt.has_key('SSL'):
-          if dt['SSL'].has_key('user'):
-              self.userCertButton.setText(dt['SSL']['user'])
-          if dt['SSL'].has_key('key'):
-              self.userKeyButton.setText(dt['SSL']['key'])
-          if dt['SSL'].has_key('keyPwd'):
-              self.keyPwd=dt['SSL']['keyPwd']
-          if dt['SSL'].has_key('ca'):
-              self.userCAButton.setText(dt['SSL']['ca'])
-      if dt.has_key('labkey'):
-          if dt['labkey'].has_key('user'):
-              self.authName.setText(dt['labkey']['user'])
-          if dt['labkey'].has_key('password'):
-              self.authPass.setText(dt['labkey']['password'])
-      self.loadConfigButton.setText(os.path.basename(filename))
-
-  def onUserCertButtonClicked(self):
-     filename=qt.QFileDialog.getOpenFileName(None,'Open user certificate',
-           self.startDir, '*.crt')
-     #pwd=qt.QInputDialog.getText(None,'Certificate password',
-     # 'Enter certificate password',qt.QLineEdit.Password)
-     if not(filename) :
-         print "No file selected"
-         return
-
-     f=qt.QFile(filename)
-     if not (f.open(qt.QIODevice.ReadOnly)) :
-         print "Could not open file"
-         return
-
-     certList=qt.QSslCertificate.fromPath(filename)
-     if len(certList) < 1:
-         print "Troubles parsing {0}".format(filename)
-         return
-
-     cert=qt.QSslCertificate(f)
-     print "cert.isNull()={0}".format(cert.isNull())
-     self.userCertButton.setText(filename)
-     self.authName.setText(cert.subjectInfo("emailAddress"))
-
-  def onUserKeyButtonClicked(self):
-     filename=qt.QFileDialog.getOpenFileName(None,'Open private key',
-           self.startDir, '*.key')
-     if not (filename) :
-         print "No file selected"
-         return
-
-     f=qt.QFile(filename)
-     if not (f.open(qt.QIODevice.ReadOnly)) :
-         print "Could not open file"
-         return
-     self.keyPwd=qt.QInputDialog.getText(None,'Private key password',
-       'Enter key password',qt.QLineEdit.Password)
-
-     key=qt.QSslKey(f,qt.QSsl.Rsa,qt.QSsl.Pem,qt.QSsl.PrivateKey,
-        str(self.keyPwd))
-     self.userKeyButton.setText(filename)
-
-  def onUserCAButtonClicked(self):
-    filename=qt.QFileDialog.getOpenFileName(None,'Open authority certificate',
-             self.startDir, '*.crt')
-    if not(filename) :
-       print "No file selected"
-       return
-
-    f=qt.QFile(filename)
-
-    if not (f.open(qt.QIODevice.ReadOnly)) :
-        print "Could not open file"
-        return
-
-    certList=qt.QSslCertificate.fromPath(filename)
-
-    if len(certList) < 1:
-        print "Troubles parsing {0}".format(filename)
-        return
-
-    #self.logic.caCert=qt.QSslCertificate(f)#certList[0]
-    self.userCAButton.setText(filename)
-
-  def onInitButtonClicked(self):
-      self.sNet.configureSSL(
-        self.userCertButton.text,
-        self.userKeyButton.text,
-        self.keyPwd,
-        self.userCAButton.text
-      )
-
-      self.sNet.hostname=self.server.text
-      self.sNet.auth_name=self.authName.text
-      self.sNet.auth_pass=self.authPass.text
-      self.sNet.initRemote()
-      self.initButton.setText("Active")
+
 
   def onDatasetLoadButtonClicked(self):
 
-      ds=self.sNet.loadDataset(self.datasetProject.text,self.datasetName.text)
+      ds=self.sNet.loadDataset(self.project,self.dataset)
       #loaded a JSON object -> convert to suitable display
       columns=ds['columnModel']
       self.data.setColumnCount(len(columns))
@@ -289,6 +137,7 @@ class dataExplorerWidget(ScriptedLoadableModuleWidget):
 
       #populate patient list
       patientList=[f['EMBRACE_ID'] for f in ds['rows']]
+      #remove duplicates
       patientSet=set(patientList)
       for p in patientSet:
            patientButton=qt.QPushButton(p)
@@ -301,6 +150,14 @@ class dataExplorerWidget(ScriptedLoadableModuleWidget):
       print "onPatientButtonClicked() for {}".format(label)
       self.loadPatientLogic.load(label)
 
+  def onLoadCTButtonClicked(self):
+      self.CT=self.loadPatientLogic.loadCT(self.patientId.text)
+
+  def onLoadCTRSButtonClicked(self):
+      self.CTRS=self.loadPatientLogic.loadCTRS(self.patientId.text)
+
+  def onLoadDMRButtonClicked(self):
+      self.DMR=self.loadPatientLogic.loadDMR(self.patientId.text)
 
 #
 # dataExplorerLogic

+ 353 - 0
dataExplorer/exportDicom.py

@@ -0,0 +1,353 @@
+import DICOMLib
+from slicer.ScriptedLoadableModule import *
+import slicerNetwork
+import qt,vtk,ctk,slicer
+import datetime
+import re
+import os
+import slicer.cli
+
+class exportDicom(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
+  def __init__(self,parent):
+        slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self, parent)
+        self.className="exportDicom"
+        self.parent.title="exportDicom"
+        self.parent.categories = ["Examples"]
+        self.parent.dependencies = []
+        self.parent.contributors = ["Andrej Studen (University of Ljubljana)"] # replace with "Firstname Lastname (Organization)"
+        self.parent.helpText = """
+        This is an example of scripted loadable module bundled in an extension.
+        It adds hierarchy datat for reliable dicom export of a node
+        """
+        self.parent.helpText += self.getDefaultModuleDocumentationLink()
+        self.parent.acknowledgementText = """
+        This extension developed within Medical Physics research programe of ARRS
+        """ # replace with organization, grant and thanks.
+
+#
+# dataExplorerWidget
+
+
+
+class exportDicomWidget(ScriptedLoadableModuleWidget):
+  """Uses ScriptedLoadableModuleWidget base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+
+  def setup(self):
+      ScriptedLoadableModuleWidget.setup(self)
+      self.logic=exportDicomLogic(self)
+
+      try:
+           fhome=os.environ["HOME"]
+      except:
+              #in windows, the variable is called HOMEPATH
+           fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
+
+      cfgPath=os.path.join(fhome,".labkey")
+      cfgPath=os.path.join(cfgPath,"onko-nix.json")
+
+      self.onkoNet=slicerNetwork.labkeyURIHandler()
+      self.onkoNet.parseConfig(cfgPath)
+      self.onkoNet.initRemote()
+
+      self.project='EMBRACE/Studija'
+
+      datasetCollapsibleButton = ctk.ctkCollapsibleButton()
+      datasetCollapsibleButton.text = "Node data"
+      self.layout.addWidget(datasetCollapsibleButton)
+      # Layout within the dummy collapsible button
+      datasetFormLayout = qt.QFormLayout(datasetCollapsibleButton)
+
+      self.volumeNode=qt.QLineEdit("Shifted_1_PTV N")
+      datasetFormLayout.addRow("volumeNode:",self.volumeNode)
+      self.patientId=qt.QLineEdit("LJU004")
+      datasetFormLayout.addRow("PatientId:",self.patientId)
+      self.studyInstanceUid=qt.QLineEdit("1.2.840.113704.1.1762661776.922.1250707615.8")
+      datasetFormLayout.addRow("StudyInstanceUid:",self.studyInstanceUid)
+      self.studyDescription=qt.QLineEdit("Segmentation:PTV N")
+      datasetFormLayout.addRow("StudyDescription:",self.studyDescription)
+
+      self.addHierarchyButton=qt.QPushButton("Add hierarchy")
+      self.addHierarchyButton.clicked.connect(self.onAddHierarchyButtonClicked)
+      datasetFormLayout.addRow("Volume:",self.addHierarchyButton)
+
+      self.exportButton=qt.QPushButton("Export")
+      self.exportButton.clicked.connect(self.onExportButtonClicked)
+      datasetFormLayout.addRow("Export:",self.exportButton)
+
+
+
+  def onAddHierarchyButtonClicked(self):
+      metadata={'patientId':self.patientId.text,
+                'studyDescription':self.studyDescription.text,
+                'studyInstanceUid':self.studyInstanceUid.text}
+      node=slicer.util.getFirstNodeByName(self.volumeNode.text)
+      if node==None:
+          return
+      self.logic.addHierarchy(node,metadata)
+
+  def onExportButtonClicked(self):
+      metadata={'patientId':self.patientId.text,
+                'studyDescription':self.studyDescription.text,
+                'studyInstanceUid':self.studyInstanceUid.text,
+                'seriesInstanceUid':self.logic.generateSeriesUUID('volume'),
+                'frameOfReferenceInstanceUid':self.logic.generateFrameOfReferenceUUID('volume')}
+      node=slicer.util.getFirstNodeByName(self.volumeNode.text)
+      if node==None:
+          return
+      self.logic.exportNode(self.onkoNet,self.project,node,metadata)
+
+
+
+class exportDicomLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
+  def __init__(self,parent):
+       slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
+       self.baseUUID='1.2.826.0.1.36800.43.10.248'
+       self.labelUUID={'series':'1','study':'2','instance':'3','frameOfReference':4}
+       self.dataUUID={'volume':'1','segmentation':'2','transformation':'3'}
+
+       try:
+           fhome=os.environ["HOME"]
+       except:
+              #in windows, the variable is called HOMEPATH
+           fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
+
+
+       self.basePath=os.path.join(fhome,'.dicom')
+
+       if not os.path.isdir(self.basePath):
+           os.mkdir(self.basePath)
+
+       self.itemLabel={
+            'patientName': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientNameAttributeName(),
+            'modality': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMSeriesModalityAttributeName(),
+            'patientId': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientIDAttributeName(),
+            'patientSex': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientSexAttributeName(),
+            'patientBirthDate': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientBirthDateAttributeName(),
+            'patientComments': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMPatientCommentsAttributeName(),
+            'studyDescription': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyDescriptionAttributeName(),
+            'studyInstanceUid': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyInstanceUIDTagName(),
+            'studyDate': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyDateAttributeName(),
+            'studyId': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyIDTagName(),
+            'studyTime': slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyTimeAttributeName()}
+
+
+
+
+
+
+
+  def generateStudyUUID(self,type):
+
+        x=datetime.datetime.now()
+        date=x.strftime("%Y%m%d")
+        studyFile=os.path.join(self.basePath,'studyCount'+date+'.txt')
+
+        try:
+            f=open(studyFile,"r")
+            id=int(f.readline())
+            id=id+1
+            f.close()
+        except:
+            id=0
+
+        studyId="{}.{}.{}.{}.{}".format(self.baseUUID,self.labelUUID['study'],
+                        self.dataUUID[type],date,id)
+
+        f=open(studyFile,"w")
+        f.write("{}".format(id))
+        f.close()
+        return studyId
+
+
+
+  def generateFrameOfReferenceUUID(self,type):
+
+        x=datetime.datetime.now()
+        date=x.strftime("%Y%m%d")
+        forFile=os.path.join(self.basePath,'frameCount'+date+'.txt')
+
+        try:
+            f=open(studyFile,"r")
+            id=int(f.readline())
+            id=id+1
+            f.close()
+        except:
+            id=0
+
+        forId="{}.{}.{}.{}.{}".format(self.baseUUID,self.labelUUID['frameOfReference'],
+                        self.dataUUID[type],date,id)
+
+        f=open(forFile,"w")
+        f.write("{}".format(id))
+        f.close()
+        return forId
+
+  def generateSeriesUUID(self,type):
+
+        x=datetime.datetime.now()
+        hour=x.strftime("%H")
+        hour=re.sub('^0','',hour)
+        ft=hour+x.strftime("%M%S")
+        seriesInstanceUid=self.baseUUID+'.'+self.labelUUID['series']+'.'
+        seriesInstanceUid+=self.dataUUID[type]+'.'+x.strftime("%Y%m%d")+'.'+ft
+        return seriesInstanceUid
+
+  def setAttribute(self,shn,itemId,label,metadata):
+        try:
+            shn.SetItemAttribute(itemId,self.itemLabel['modality'],metadata['modality'])
+        except KeyError:
+            pass
+
+  def addHierarchy(self,dataNode,metadata):
+        #convert dataNode to fully fledged DICOM object in hierarchy
+
+        #variation of addSeriesInSubjectHierarchy
+        shn=slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
+        sceneItemId=shn.GetSceneItemID()
+
+        #add the series for the data node
+        seriesItemId=shn.CreateItem(sceneItemId,dataNode)
+
+        x=datetime.datetime.now()
+        hour=x.strftime("%H")
+        hour=re.sub('^0','',hour)
+        ft=hour+x.strftime("%M%S")
+        seriesInstanceUid=self.baseUUID+'.'+self.labelUUID['series']+'.'
+        seriesInstanceUid+=self.dataUUID['volume']+'.'+x.strftime("%Y%m%d")+'.'+ft
+        shn.SetItemUID(seriesItemId,slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(),
+            seriesInstanceUid)
+
+        self.setAttribute(shn,seriesItemId,'modality',metadata)
+        self.setAttribute(shn,seriesItemId,'seriesNumber',metadata)
+
+        #add PatientId
+        try:
+            patientId=metadata['patientId']
+        except KeyError:
+            patientId='Unknown'
+
+        try:
+            studyInstanceUid=metadata['studyInstanceUid']
+        except KeyError:
+            studyInstanceUid=self.generateStudyUUID('volume')
+
+        slicer.vtkSlicerSubjectHierarchyModuleLogic.InsertDicomSeriesInHierarchy(shn,patientId,
+            studyInstanceUid,seriesInstanceUid)
+
+        patientItemId=shn.GetItemByUID(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(),patientId)
+
+        self.setAttribute(shn,patientItemId,'patientName',metadata)
+        self.setAttribute(shn,patientItemId,'patientId',metadata)
+        self.setAttribute(shn,patientItemId,'patientSex',metadata)
+        self.setAttribute(shn,patientItemId,'patientBirthDate',metadata)
+        self.setAttribute(shn,patientItemId,'patientComments',metadata)
+
+        patientItemName=patientId
+        shn.SetItemName(patientItemId,patientItemName)
+
+        studyItemId=shn.GetItemByUID(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(),studyInstanceUid)
+
+        self.setAttribute(shn,studyItemId,'studyDescription',metadata)
+        self.setAttribute(shn,studyItemId,'studyInstanceUid',metadata)
+        self.setAttribute(shn,studyItemId,'studyId',metadata)
+        self.setAttribute(shn,studyItemId,'studyDate',metadata)
+        self.setAttribute(shn,studyItemId,'studyTime',metadata)
+
+        studyItemName=studyInstanceUid
+        shn.SetItemName(studyItemId,studyItemName)
+
+  def setTagValue(self,cliparameters,metadata,field):
+       try:
+           cliparameters[field]=metadata[field]
+       except KeyError:
+           pass
+
+  def setDirectoryValue(self,cliparameters,cliKey,metadata,key):
+       try:
+           cliparameters[cliKey]=metadata[key]
+       except KeyError:
+           pass
+
+
+
+  def exportNode(self,net, project,volumeNode,metadata):
+
+       cliparameters={}
+       tagList=['patientName','patientComments','studyDate','studyTime','studyDescription',
+            'modality','manufacturer','model','seriesDescription',
+            'seriesNumber','seriesDate','seriesTime','contentDate','contentTime']
+
+       for tag in tagList:
+           self.setTagValue(cliparameters,metadata,tag)
+
+       valuePairs={'patientID':'patientId',
+                    'studyID':'studyId',
+                    'seriesInstanceUID':'seriesInstanceUid',
+                    'studyInstanceUID':'studyInstanceUid',
+                    'frameOfReferenceInstanceUID':'frameOfReferenceInstanceUid'}
+
+       for key in valuePairs:
+           self.setDirectoryValue(cliparameters,key,metadata,valuePairs[key])
+
+
+       #buffed up exportable is now ready to be stored
+       fdir=net.GetLocalCacheDirectory()
+       project=project.replace('/','\\')
+       fdir=os.path.join(fdir,project)
+       fdir=os.path.join(fdir,'%40files')
+       fdir=os.path.join(fdir,metadata['patientId'])
+       fdir=os.path.join(fdir,'Registration')
+
+       if not os.path.isdir(fdir):
+           os.mkdir(fdir)
+
+       relDir=net.GetRelativePathFromLocalPath(fdir)
+       remoteDir=net.GetLabkeyPathFromRelativePath(relDir)
+
+       if not net.isRemoteDir(remoteDir):
+            net.mkdir(remoteDir)
+
+
+       dirName=volumeNode.GetName();
+       dirName=dirName.replace(" ","_")
+       fdir=os.path.join(fdir,dirName)
+
+       if not os.path.isdir(fdir):
+           os.mkdir(fdir)
+
+       cliparameters['dicomDirectory']=fdir
+       cliparameters['dicomPrefix']='IMG'
+       cliparameters['inputVolume']=volumeNode.GetID()
+
+       print("[CLI]SeriesInstanceUID: {}").format(cliparameters['seriesInstanceUID'])
+       print("[MeD]SeriesInstanceUID: {}").format(metadata['seriesInstanceUid'])
+
+       try:
+           dicomWrite=slicer.modules.createdicomseries
+       except AttributeError:
+           print("Missing dicom exporter")
+           return
+
+       cliNode=slicer.cli.run(dicomWrite,None,cliparameters,wait_for_completion=True)
+       status=cliNode.GetStatusString()
+       print("Status: {}").format(status)
+       if status.find("error")>-1:
+           print("Error: {}").format(cliNode.GetErrorText())
+           return
+
+       relDir=net.GetRelativePathFromLocalPath(fdir)
+       remoteDir=net.GetLabkeyPathFromRelativePath(relDir)
+
+       if not net.isRemoteDir(remoteDir):
+           net.mkdir(remoteDir)
+
+       for f in os.listdir(fdir):
+           localPath=os.path.join(fdir,f)
+           print("localPath: {}").format(localPath)
+           relativePath=net.GetRelativePathFromLocalPath(localPath)
+           print("relativePath: {}").format(relativePath)
+           remotePath=net.GetLabkeyPathFromRelativePath(relativePath)
+           print("remotePath: {}").format(relativePath)
+           net.copyLocalFileToRemote(localPath,remotePath)

+ 270 - 0
dataExplorer/importDicom.py

@@ -0,0 +1,270 @@
+import dicom
+import vtkInterface as vi
+import os
+import vtk, qt, ctk, slicer
+from slicer.ScriptedLoadableModule import *
+import slicerNetwork
+import json
+import loadDicom
+import DICOMLib
+
+class importDicom(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
+    def __init__(self,parent):
+        slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self, parent)
+        self.className="loadPatient"
+        self.parent.title="loadPatient"
+
+#equivalent of loadable + labkey interface
+class dicomSeries():
+    def __init__(self):
+        self.data = []
+        self.idx = []
+        self.z = []
+        self.pixel_size = [0,0,0]
+        self.lpsOrigin = [0,0,0]
+        self.lpsOrigin[2]=1e30
+        self.lpsOrientation=[0,0,0,0,0,0]
+
+
+    def getfile(self,net,relativePath):
+        return net.readFileToBuffer(relativePath)
+
+    def addFile(self,f):
+        try:
+            self.files.append(f)
+        except:
+            self.files=[f]
+
+    def setLabel(self,label):
+        self.label=label
+
+    def getLabel(self):
+        try:
+            return self.label
+        except:
+            return None
+
+    def setMetadata(self,key,value):
+        try:
+            self.metadata[key]=value
+        except:
+            self.metadata={key:value}
+
+    def getMetadata(self):
+        try:
+            return self.metadata
+        except:
+            return {}
+
+    def load(self,net):
+
+        for f in self.files:
+            print '{}:'.format(f)
+            fileBuffer=self.getfile(net,f)
+            self.loadFile(fileBuffer)
+
+        nz=len(self.idx)
+        sh=self.data[-1].shape
+        sh_list=list(sh)
+        sh_list.append(nz)
+        data_array=np.zeros(sh_list)
+
+        for k in range(0,nz):
+            kp=int(np.round((self.z[k]-self.center[2])/self.pixel_size[2]))
+            data_array[:,:,kp]=np.transpose(self.data[k])
+
+        try:
+            nodeName='Series'+self.label
+        except:
+            print('Could not set series label')
+            nodeName='UnknownSeries'
+
+        newNode=slicer.vtkMRMLScalarVolumeNode()
+        newNode.SetName(nodeName)
+
+        ijkToRAS = vtk.vtkMatrix4x4()
+        #think how to do this with image orientation
+
+        rasOrientation=[-self.lpsOrientation[i] if (i%3 < 2) else self.lpsOrientation[i]
+          for i in range(0,len(self.lpsOrientation))]
+        rasOrigin=[-self.lpsOrigin[i] if (i%3<2) else self.lpsOrigin[i] for i in range(0,len(self.lpsOrigin))]
+
+        for i in range(0,3):
+             for j in range(0,3):
+                 ijkToRAS.SetElement(i,j,self.pixel_size[i]*rasOrientation[3*j+i])
+
+             ijkToRAS.SetElement(i,3,rasOrigin[i])
+
+        newNode.SetIJKToRASMatrix(ijkToRAS)
+
+        v=vtk.vtkImageData()
+        v.GetPointData().SetScalars(
+            vtk.util.numpy_support.numpy_to_vtk(
+                np.ravel(self.data,order='F'),deep=True, array_type=vtk.VTK_FLOAT))
+        v.SetOrigin(0,0,0)
+        v.SetSpacing(1,1,1)
+        v.SetDimensions(self.data.shape)
+
+        newNode.SetAndObserveImageData(v)
+        slicer.mrmlScene.AddNode(newNode)
+        volume={'node':newNode,'metadata':self.metadata}
+        return volume
+
+    def loadFile(self,fileBuffer):
+
+        plan=dicom.read_file(fileBuffer)
+
+        self.data.append(plan.pixel_array)
+        self.idx.append(plan.InstanceNumber)
+        self.z.append(plan.ImagePositionPatient[2])
+
+
+        #pixelSize
+        pixel_size=[plan.PixelSpacing[0],plan.PixelSpacing[1],
+                    plan.SliceThickness]
+
+        for i in range(0,3):
+            if self.pixel_size[i] == 0:
+                self.pixel_size[i] = float(pixel_size[i])
+            if abs(self.pixel_size[i]-pixel_size[i]) > 1e-3:
+                print 'Pixel size mismatch {.2f}/{.2f}'.format(self.pixel_size[i],
+                        pixel_size[i])
+
+        #origin
+        for i in range(0,2):
+            if self.lpsOrigin[i] == 0:
+                self.lpsOrigin[i] = float(plan.ImagePositionPatient[i])
+            if abs(self.lpsOrigin[i]-plan.ImagePositionPatient[i]) > 1e-3:
+                print 'Image center mismatch {.2f}/{.2f}'.format(self.lpsOrigin[i],
+                    plan.ImagePositionPatient[i])
+        #not average, but minimum (!) why??
+
+        if plan.ImagePositionPatient[2]<self.lpsOrigin[2]:
+            self.lpsOrigin[2]=plan.ImagePositionPatient[2]
+
+
+        #orientation
+        for i in range(0,6):
+            if self.lpsOrientation[i] == 0:
+                self.lpsOrientation[i] = float(plan.ImageOrientationPatient[i])
+            if abs(self.lpsOrientation[i]-plan.ImageOrientationPatient[i]) > 1e-3:
+                print 'Image orientation mismatch {0:.2f}/{1:.2f}'.format(self.lpsOrientation[i],
+                        plan.ImageOrientationPatient[i])
+
+        return True
+
+class importDicomLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic):
+    def __init__(self,parent):
+       slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic.__init__(self, parent)
+       self.tag={
+        'studyInstanceUid':0x0020000d,
+        'seriesInstanceUid':0x0020000e,
+        'patientId':0x00100020,
+        'patientName':0x00100010,
+        'sequenceName':0x00180024,
+        'seriesNumber':0x00200011,
+        'percentPhaseFieldOfView':0x00180094,
+        'modality': 0x00080060,
+        'patientSex': 0x00100040,
+        'patientBirthDate': 0x00100030,
+        'patientComments': 0x00104000,
+        'studyDescription': 0x00081030,
+        'studyDate': 0x00080020,
+        'studyId': 0x00200010,
+        'studyTime': 0x00080030,
+        'frameOfReferenceInstanceUid':0x00200052}
+
+    def loadVolumes(self,net,directory,filter):
+        #mimic examineForImport
+        seriesList=self.examineForImport(net,directory,filter)
+        print("Got {} series").format(len(seriesList))
+        volumes=[]
+
+        for s in seriesList:
+            try:
+                volumes.append(s.load(net))
+                #often fails, e.g. JPEGLossles
+            except:
+                loadable=DICOMLib.DICOMLoadable()
+                loadable.name='Series'+str(s.getLabel())
+                print("Loading for {} number of files (pre-load) {}").format(loadable.name,len(s.files))
+                loadable.files=[net.DownloadFileToCache(f) for f in s.files]
+                print("Loading for {} number of files (pre-sort) {}").format(loadable.name,len(loadable.files))
+
+                loadable.files,distances,loadable.warning=DICOMLib.DICOMUtils.getSortedImageFiles(loadable.files,1e-3)
+
+                print("Loading for {} number of files {}").format(loadable.name,len(loadable.files))
+                try:
+                    volumeNode=self.volumePlugin.load(loadable)
+                except:
+                    self.volumePlugin=slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']()
+                    volumeNode=self.volumePlugin.load(loadable)
+                volume={'node':volumeNode,'metadata':s.getMetadata()}
+                volumes.append(volume)
+
+
+        return volumes
+
+
+    def listdir(self,net,relativeDirectory):
+        return net.listRelativeDir(relativeDirectory)
+
+    def getfile(self,net,relativePath):
+        return net.readFileToBuffer(relativePath)
+
+    def examineForImport(self,net,directory,filter):
+        #split by series
+        files=self.listdir(net,directory)
+        seriesList=[]
+
+        seriesList.append(dicomSeries())
+        series=seriesList[0]
+
+        for f in files:
+            fileBuffer=self.getfile(net,f)
+            #validate
+            try:
+                plan = dicom.read_file(fileBuffer)
+            except:
+                print ("{}: Not a dicom file")
+                continue
+
+            #determine validity first
+            fileValid=True
+            seriesKey=None
+            for key in filter:
+                if filter[key]==None:
+                    continue
+                if filter[key]=='SeriesLabel':
+                    seriesTag=self.tag[key]
+                    continue
+
+                v=plan[self.tag[key]].value
+                if not v==filter[key]:
+                    print('Filter mismatch {}{:x}: {}/{}').format(key,self.tag[key],v,filter[key])
+                    fileValid=False
+
+            if not fileValid:
+                continue
+
+            #determine serieslabel second
+            seriesLabel=plan[seriesTag].value
+            if series.getLabel()==seriesLabel:
+                series.addFile(f)
+                continue
+
+            #add new series
+            if not series.getLabel()==None:
+                seriesList.append(dicomSeries())
+                series=seriesList[-1]
+
+            #set series parameters
+            series.addFile(f)
+            series.setLabel(seriesLabel)
+            for key in filter:
+                if not filter[key]==None:
+                    continue
+                v=plan[self.tag[key]].value
+                series.setMetadata(key,v)
+
+        return seriesList

+ 112 - 11
dataExplorer/loadPatient.py

@@ -4,6 +4,7 @@ import re
 import slicerNetwork
 import os
 import subprocess
+import importDicom
 
 fValue=loadDicom.dicomValue
 dicomModify="/afs/f9.ijs.si/home/studen/software/install/"
@@ -22,6 +23,14 @@ class loadPatientLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic
        #self.plugin=slicer.modules.dicomPlugins['DicomRtImportExportPlugin']()
        #self.volumePlugin=slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']()
        self.dicomLoader=loadDicom.loadDicomLogic(self)
+       self.dicomImporter=importDicom.importDicomLogic(self)
+       self.project="EMBRACE/Studija"
+       self.imagingDataset="ImagingVisitsManaged"
+       self.idDataset="PopisneStevilke"
+       self.dicomProject="Test/Transfer"
+       self.dicomDataset="Imaging"
+       self.dicomPath=self.dicomProject+'/%40files/dicom'
+
 
        #self.rtReader=slicer.vtkSlicerDicomRtReader()
 
@@ -30,28 +39,46 @@ class loadPatientLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic
     def setURIHandler(self, net):
         self.sNet=net
 
+    def getOICode(self, label):
+        filter=[]
+        filterID={"variable":"EMBRACE_ID",
+            "oper":"eq",
+            "value":label}
+        filter.append(filterID)
+        fSet=self.sNet.filterDataset(self.project,self.idDataset,filter)
+        try:
+            row=fSet["rows"][0]
+        except:
+            print("OI code for {} not found").format(label)
+            return None
+        return row["OICode"]
+
+
     def load(self,label):
         #clear previous data
         debug=True
         loadDicom.clearNodes()
-        
-        containerPath='Matej/studija'
-        subFolder=''
-        dirUrl=containerPath+"/@files"+subFolder
-        dir=os.path.join(dirUrl,label)
+
+        dirUrl=self.project+"/@files"
+        dir=dirUrl+'/'+label
         #load segmentations
         rs=dir+"/RS/DICOM";
         self.dicomLoader.load(self.sNet,rs)
 
 
         rsMatch='None'
-        dataset="ImagingVisitsManaged"
-        fSet=self.sNet.filterDataset(containerPath,dataset,'EMBRACE_ID',label)
+        filter=[]
+        filterID={"variable":"EMBRACE_ID",
+            "oper":"eq",
+            "value":label}
+        filter.append(filterID)
+        filterType={"variable":"Type",
+                "oper":"eq",
+                "value":"RS"}
+        filter.append(filterType)
+        fSet=self.sNet.filterDataset(self.project,self.dataset,filter)
         fData=fSet['rows']
-        for r in fData:
-            if r['type']!='RS':
-                continue
-            row=r
+        row=fData[0] #first match
 
         if row['rsMatch']==None:
             return
@@ -63,3 +90,77 @@ class loadPatientLogic(slicer.ScriptedLoadableModule.ScriptedLoadableModuleLogic
         if debug:
             print "Loading {}".format(dicomDir)
         self.dicomLoader.load(self.sNet,dicomDir)
+
+    def findEmbrace(self,embraceId,imageType):
+        filter=[]
+        filterID={"variable":"EMBRACE_ID",
+            "oper":"eq",
+            "value":embraceId}
+        filter.append(filterID)
+        filterType={"variable":"Type",
+                "oper":"eq",
+                "value":imageType}
+        filter.append(filterType)
+        fSet=self.sNet.filterDataset(self.project,self.imagingDataset,filter)
+        try:
+            seqNum=fSet["rows"][0]["pseudoTransverseID"]
+        except:
+            print("CT data for patient {} not found").format(label)
+            return None
+
+        OIcode=self.getOICode(embraceId)
+        dicomFilter=[]
+        filterID={"variable":"PatientId",
+            "oper":"eq",
+            "value":OIcode}
+        dicomFilter.append(filterID)
+        filterSeq={"variable":"SequenceNum",
+            "oper":"eq",
+            "value":str(seqNum)}
+        dicomFilter.append(filterSeq)
+        fSet=self.sNet.filterDataset(self.dicomProject,self.dicomDataset,dicomFilter)
+        try:
+            row=fSet['rows'][0]
+        except:
+            print("Dicom for patient {}/{} not found").format(label,OIcode)
+            return None
+        return {'Study':row['Study'],'Series':row['Series']}
+
+    def loadCT(self,label):
+        studySeries=self.findEmbrace(label,"CT")
+        if studySeries==None:
+            return
+        relativePath=self.dicomPath+'/'+studySeries['Study']+'/'+studySeries['Series']
+        dicomReadFilter={'seriesNumber':"SeriesLabel",
+                        'studyInstanceUid':None,
+                        'frameOfReferenceInstanceUid':None}
+        return self.dicomImporter.loadVolumes(self.sNet,relativePath,dicomReadFilter)
+
+    def loadCTRS(self,label):
+        studySeries=self.findEmbrace(label,"CT-RS")
+        if studySeries==None:
+            return
+        relativePath=self.dicomPath+'/'+studySeries['Study']+'/'+studySeries['Series']
+        dicomReadFilter={'seriesNumber':"SeriesLabel",
+                        'studyInstanceUid':None,
+                        'frameOfReferenceInstanceUid':None}
+        print("Loading segmentation: {}").format(relativePath)
+        return self.dicomLoader.loadSegmentations(self.sNet,relativePath,dicomReadFilter)
+
+
+    def loadDMR(self,label):
+        #clear previous data
+        debug=True
+
+        dirUrl=self.project+"/@files"
+        dir=dirUrl+'/'+label
+        #load segmentations
+        rs=dir+"/DMR/DICOM";
+
+        dicomReadFilter={'seriesNumber':"SeriesLabel",
+                        'sequenceName':"*tse2d1_3",
+                        'percentPhaseFieldOfView':90,
+                        'studyInstanceUid':None,
+                        'frameOfReferenceInstanceUid':None}
+
+        return self.dicomImporter.loadVolumes(self.sNet,rs,dicomReadFilter)

+ 8 - 0
onko-nix.json

@@ -0,0 +1,8 @@
+{ "host" : "http://onko-nix.onko-i.si:8080",
+  "context" : "labkey",
+  "labkey": 
+	{"user":"andrej.studen@ijs.si",
+	"password" : "cesen18"},
+  "project": "Test/Transfer",
+  "dataset": "Imaging"
+}