Eager Beaver 5 anni fa
commit
fbad6ad2e6
6 ha cambiato i file con 295 aggiunte e 0 eliminazioni
  1. 14 0
      Readme.md
  2. 75 0
      getFile.py
  3. 33 0
      labkeyDatabaseBrowser.py
  4. 57 0
      labkeyFileBrowser.py
  5. 101 0
      labkeyInterface.py
  6. 15 0
      network-config-sample.json

+ 14 - 0
Readme.md

@@ -0,0 +1,14 @@
+## Labkey Python interface ##
+A Python3 urllib3 based interface for communication with the LabKey server. Update over official LabKey version is the use of client certificates to access potentially sensitive sites.
+
+### Setup ###
+Update `network-config-sample.json` with personal information needed to access the site (certificates, keys, username and password)
+When using labkeyInterface, use the updated file as input to labkeyInterface constructor, ie
+```python
+ifc=labkeyInterface.labkeyInterface('/path/to/network-config.json')
+```
+
+### Use ###
+`getFile.py` illustrates use to connect to site, collect image and perform image analysis.
+
+

+ 75 - 0
getFile.py

@@ -0,0 +1,75 @@
+import labkeyInterface
+import labkeyFileBrowser
+import io
+import pydicom
+import numpy
+import itk
+
+net=labkeyInterface.labkeyInterface()
+net.init('/home/studen/.labkey/merlin.json')
+b=labkeyFileBrowser.labkeyFileBrowser(net)
+
+url=b.formatPathURL('DORA/Radiomics',"")
+ok,dirs=b.listRemoteDir(url)
+url=dirs[10]
+
+ok,dirs=b.listRemoteDir(url)
+url=dirs[0]
+
+ok,dirs=b.listRemoteDir(url)
+fileUrl=dirs[0]
+
+resp=net.get(fileUrl)
+fileObj=io.BytesIO(resp.data)
+
+#get dcm representation of the file
+dcm=pydicom.dcmread(fileObj)
+
+#matrix dimension
+ConstPixelDims = (int(dcm.Rows), int(dcm.Columns))
+npArray = numpy.zeros(ConstPixelDims, dtype=dcm.pixel_array.dtype)
+
+npArray[:, :] = dcm.pixel_array
+
+#get it to itk
+itkImg=itk.GetImageFromArray(npArray)
+
+
+#works for DORA dicoms, but it would be great to be able to get that from itkImg
+InputPixelType = itk.SS
+#required by Canny
+OutputPixelType = itk.F
+
+Dimension = 2
+
+InputImageType = itk.Image[InputPixelType, Dimension]
+OutputImageType = itk.Image[OutputPixelType, Dimension]
+
+#recast to F
+castImageFilter = itk.CastImageFilter[InputImageType, OutputImageType].New()
+castImageFilter.SetInput(itkImg)
+
+#for debugging
+
+castImageFilter.Update()
+itkImg1=castImageFilter.GetOutput()
+
+variance=2
+upperThreshold=5
+lowerThreshold=0.4*upperThreshold
+
+cannyFilter = itk.CannyEdgeDetectionImageFilter[
+    OutputImageType,
+    OutputImageType].New()
+cannyFilter.SetInput(castImageFilter.GetOutput())
+cannyFilter.SetVariance(variance)
+cannyFilter.SetLowerThreshold(lowerThreshold)
+cannyFilter.SetUpperThreshold(upperThreshold)
+
+cannyFilter.Update()
+
+canny=cannyFilter.GetOutput()
+
+npCanny=itk.GetArrayFromImage(canny)
+
+

+ 33 - 0
labkeyDatabaseBrowser.py

@@ -0,0 +1,33 @@
+import labkeyInterface
+import json
+
+class labkeyDB:
+    def __init__(self,net):
+        self.net=net
+
+    def selectRows(self,project,schemaName, queryName,qfilter):
+
+        debug=False
+        url=self.net.GetLabkeyUrl()+'/'+project
+        url+='/query-selectRows.api?schemaName='+schemaName+\
+                '&query.queryName='+queryName
+        for f in qfilter:
+            url+="&query."+f['variable']+"~"+f['oper']+"="+f['value']
+        if debug:
+            print("Sending {}").format(url)
+        response=self.net.get(url)
+        return json.loads(response.data)
+
+    def modifyRows(self,mode, project,schemaName, queryName, rows):
+        #mode can be insert/update/delete
+        debug=True
+        data={}
+        data['schemaName']=schemaName
+        data['queryName']=queryName
+        data['rows']=rows
+        url=self.net.GetLabkeyUrl()+'/'+project
+        url+='/query-'+mode+'Rows.api?'
+        response=self.net.post(url,json.dumps(data))
+        return response.data
+
+

+ 57 - 0
labkeyFileBrowser.py

@@ -0,0 +1,57 @@
+import urllib3
+import xml.etree.ElementTree as ET
+
+
+class labkeyFileBrowser:
+
+    def __init__(self,net):
+        self.net=net
+
+    def GetRootUrl(self):
+        return self.net.GetLabkeyUrl()+"/_webdav"
+    
+    def listRemoteDir(self,url):
+        #input is remoteDir, result are remoteDirs
+
+        #r=MethodRequest(dirUrl,method="PROPFIND")
+        PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
+                    <propfind xmlns="DAV:">\n
+                    <prop>\n
+                    <getetag/>\n
+                    </prop>\n
+                    </propfind>"""
+        headers=urllib3.util.make_headers(basic_auth=self.net.getBasicAuth())
+        headers["Content-Type"]='text/xml; charset="utf-8"'
+        headers['content-length']=str(len(PROPFIND))
+        headers['Depth']='1'
+        #add csrf
+        headers["X-LABKEY-CSRF"]=self.net.getCSRF()
+
+        try:
+            f=self.net.http.request('PROPFIND',url,headers=headers,body=PROPFIND)
+        #f contains json as a return value
+        except urllib3.exceptions.HTTPError as e:
+            print(e)
+            return False,dirs
+        
+        tree=ET.XML(f.data)
+        rps=tree.findall('{DAV:}response')
+        dirs=[]
+        for r in rps:
+            hr=r.find('{DAV:}href')
+            dirent=hr.text
+            #dirent=re.sub('/labkey/_webdav/','',dirent)
+            dirent=self.net.connectionConfig['host']+dirent
+            dirs.append(dirent)
+        del dirs[0]
+        return True,dirs
+
+    def readFileToBuffer(self, url):
+        return self.net.get(url)
+    
+    def formatPathURL(self,project,path):
+        url=self.GetRootUrl()+'/'+project+'/@files'
+        if len(path)==0:
+            return url
+        return url+'/'+path
+        

+ 101 - 0
labkeyInterface.py

@@ -0,0 +1,101 @@
+import urllib3
+#requests doesnt work as it lacks key password checking
+import json
+
+
+class labkeyInterface:
+
+    def init(self,fname):
+        #fname is a file with configuration
+        try:
+            f=open(fname)
+        except OSError as e:
+            print("Confgiuration error: OS error({0}): {1}").format(e.errno, e.strerror)
+            raise
+
+        self.connectionConfig=json.load(f)
+        
+        self.http=urllib3.PoolManager()
+
+        if 'SSL' in self.connectionConfig:
+            self.http = urllib3.PoolManager(\
+                cert_file=self.connectionConfig['SSL']['user'],\
+                cert_reqs='CERT_REQUIRED',\
+                key_file=self.connectionConfig['SSL']['key'],\
+                ca_certs=self.connectionConfig['SSL']['ca'])
+
+    #password=self.connectionConfig['SSL']['keyPwd'],\ doesnt work until 1.25
+    def GetLabkeyUrl(self):
+        return self.connectionConfig['host']+"/labkey"
+
+    def GetLabkeyWebdavUrl(self):
+        return self.GetLabkeyUrl()+"/_webdav"
+
+    def getBasicAuth(self):
+        user=self.connectionConfig['labkey']['user']
+        pwd=self.connectionConfig['labkey']['password']
+        return user+":"+pwd
+        
+        
+
+
+    
+    def get(self,url):
+
+        debug=False
+        if debug:
+            print("GET: {0}").format(url)
+            print("as {0}").format(user)
+        headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
+        try:
+            return self.http.request('GET',url,headers=headers)
+        #f contains json as a return value
+        except urllib3.exceptions.HTTPError as e:
+            print(e)
+
+    def post(self,url,data):
+
+        debug=False
+        headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
+        headers["Content-Type"]="application/json"
+        #add csrf;also sets self.cookie
+        headers["X-LABKEY-CSRF"]=self.getCSRF()
+        headers["Cookie"]=self.cookie
+
+        try:
+            return self.http.request('POST',url,headers=headers,body=data)
+        #f contains json as a return value
+        except urllib3.exceptions.HTTPError as e:
+            print(e)
+
+    def put(self,url,data):
+
+        debug=False
+
+        if debug:
+            print("PUT: {}").format(url)
+
+        headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
+        headers["Content-Type"]="application/octet-stream"
+        #add csrf
+        headers["X-LABKEY-CSRF"]=self.getCSRF()
+        headers["Cookie"]=self.cookie
+
+        try:
+            return self.http.request('PUT',url,headers=headers,body=data)
+        #f contains json as a return value
+        except urllib3.exceptions.HTTPError as e:
+            print(e)
+      
+
+            
+    def getCSRF(self):
+        url=self.GetLabkeyUrl()+'/login/whoAmI.view'
+        response=self.get(url)
+        self.cookie=response.getheader('Set-Cookie')
+        jsonData=json.loads(response.data)
+        return jsonData["CSRF"]
+
+
+
+

+ 15 - 0
network-config-sample.json

@@ -0,0 +1,15 @@
+{ "SSL":
+  {"user":  "/home/studen/temp/crt/astuden.crt",
+    "key": "/home/studen/temp/crt/astuden1.key",
+    "keyComment":"the key file should be created without password",
+    "ca": "/home/studen/temp/crt/NIX_Ljubljana_CA_chain.crt",
+    "keyPwd" : "IGNORED"
+  },
+  "host" : "https://merlin.fmf.uni-lj.si",
+  "context" : "labkey",
+  "labkey" :
+  { "user" : "andrej.studen@ijs.si",
+    "password" : "labkeyPassword"
+  }
+}
+