123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- import qt
- import urllib2
- import ssl
- import cookielib
- import xml.etree.ElementTree as ET
- import re
- import StringIO
- import slicer
- import shutil
- import distutils
- import os
- import base64
- import json
- #see https://gist.github.com/logic/2715756, allow requests to do Put
- class MethodRequest(urllib2.Request):
- def __init__(self, *args, **kwargs):
- if 'method' in kwargs:
- self._method = kwargs['method']
- del kwargs['method']
- else:
- self._method = None
- return urllib2.Request.__init__(self, *args, **kwargs)
- def get_method(self, *args, **kwargs):
- if self._method is not None:
- return self._method
- return urllib2.Request.get_method(self, *args, **kwargs)
- class slicerNetwork(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
- def __init__(self, parent):
- slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self,parent)
- self.parent.title="slicerNetwork"
- pass
- class labkeyURIHandler(slicer.vtkURIHandler):
- def __init__(self):
- slicer.vtkURIHandler.__init__(self)
- self.className="labkeyURIHandler"
- slicer.mrmlScene.AddURIHandler(self)
- try:
- self.localCacheDirectory=os.path.join(os.environ["HOME"],"labkeyCache")
- except:
- #in windows, the variable is called HOMEPATH
- fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
- self.localCacheDirectory=os.path.join(fhome,"labkeyCache")
- self.mode="http"
- def CanHandleURI(self,uri):
- print "labkeyURIHandler::CanHandleURI({0})".format(uri)
- if uri.find('labkey://')==0:
- return 1
- return 0
- def GetClassName(self):
- return self.className
- def GetHostName(self):
- return self.hostname
- def SetHostName(self,host):
- self.hostname=host
- def SetLocalCahceDirectory(self,dir):
- self.localCacheDirectory=dir
- def GetLocalCacheDirectory(self):
- return self.localCacheDirectory
- def GetLabkeyUrl(self):
- return self.hostname+"/labkey"
- def GetLabkeyWebdavUrl(self):
- return self.GetLabkeyUrl()+"/_webdav"
- def GetLocalPath(self,source):
- debug=False
- relativePath=re.sub('labkey://','',source)
- sp=os.sep.encode('string-escape')
- if debug:
- print "Substituting / with {0} in {1}".format(sp,relativePath)
- relativePath=re.sub('/',sp,relativePath)
- return os.path.join(self.localCacheDirectory,relativePath)
- def GetRemotePath(self,source):
- return self.GetLabkeyWebdavUrl()+"/"+GetLabkeyPathFromLocalPath(source)
- def GetLabkeyPathFromLocalPath(self,f):
- #report it with URL separator, forward-slash
- if f.find(self.localCacheDirectory)<-1:
- print "Localpath misformation. Exiting"
- return "NULL"
- dest=re.sub(self.localCacheDirectory,'',f)
- #leaves /ContextPath/%40files/subdirectory_list/files
- #remove first separator
- sp=os.sep.encode('string-escape')
- if dest[0]==sp:
- dest=dest[1:len(dest)]
- return re.sub(sp,'/',dest)
- def GetLabkeyPathFromRemotePath(self,f):
- #used to query labkey stuff, so URL separator is used
- f=re.sub('labkey://','',f)
- f=re.sub(self.GetLabkeyWebdavUrl(),'',f)
- if f[0]=='/':
- f=f[1:len(f)]
- return f;
- def GetFile(self,source):
- # check for file in cache. If available, use, if not, download
- localPath=self.GetLocalPath(source)
- if os.path.isfile(localPath):
- return localPath
- self.StageFileRead(source,localPath)
- return localPath
- def StageFileRead(self,source,dest):
- debug=False
- if debug:
- print "labkeyURIHandler::StageFileRead({0},{1})".format(source,dest)
- labkeyPath=re.sub('labkey://','',source)
- remote=self.readFile(self.hostname,labkeyPath)
- #make all necessary directories
- path=os.path.dirname(dest)
- if not os.path.isdir(path):
- os.makedirs(path)
- local=open(dest,'wb')
- #make sure we are at the begining of the file
- #check file size
- if debug:
- remote.seek(0,2)
- sz=remote.tell()
- print "Remote size: {0}".format(sz)
- remote.seek(0)
- shutil.copyfileobj(remote,local)
- if debug:
- print "Local size: {0}".format(local.tell())
- local.close()
- def StageFileWrite(self,source,dest):
- print "labkeyURIHandler::StageFileWrite({0},{1}) not implemented yet".format(
- source,dest)
- def fileTypesAvailable(self):
- return ('VolumeFile','SegmentationFile','TransformFile')
- #mimic slicer.util.loadNodeFromFile
- # def loadNodeFromFile(self, filename, filetype, properties={}, returnNode=False):
- # #this is the only relevant part - file must be downloaded to cache
- # localPath=self.GetFile(filename)
- # slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode)
- # #remove retrieved file
- # try:
- # if not(properties['keepCachedFile']) :
- # os.remove(localPath)
- # except:
- # pass
- # def loadVolume(self,filename, properties={}, returnNode=False):
- # filetype = 'VolumeFile'
- # #redirect to self.loadNodeFromFile first to get the cached file
- # return self.loadNodeFromFile(filename,filetype, properties,returnNode)
- #
- # def loadSegmentation(self,filename,properties={},returnNode=False):
- # filetype='SegmentationFile'
- # #redirect to self.loadNodeFromFile first to get the cached file
- # return self.loadNodeFromFile(filename,filetype, properties,returnNode)
- # #add others if needed
- ## setup & initialization routines
- def configureSSL(self,cert,key,pwd,cacert):
- #do this first
- try:
- self.ctx=ssl.SSLContext(ssl.PROTOCOL_SSLv23)
- self.ctx.load_cert_chain(cert,key,pwd)
- self.ctx.verify_mode=ssl.CERT_REQUIRED
- self.ctx.load_verify_locations(cacert)
- except ssl.SSLError as err:
- print " Failed to configure SSL: {0}".format(str(err))
- self.mode="https"
- def initRemote(self):
- if self.mode=="https":
- http_handler=urllib2.HTTPSHandler(context=self.ctx)
- if self.mode=="http":
- http_handler=urllib2.HTTPHandler()
- #cookie part
- cj=cookielib.CookieJar()
- cookie_handler=urllib2.HTTPCookieProcessor(cj)
- self.opener=urllib2.build_opener(http_handler,cookie_handler)
- def parseConfig(self,fname):
- try:
- f=open(fname)
- except OSError as e:
- print "Confgiuration error: OS error({0}): {1}".format(e.errno, e.strerror)
- return
- dt=json.load(f)
- if dt.has_key('SSL'):
- self.configureSSL(
- dt['SSL']['user'],
- dt['SSL']['key'],
- dt['SSL']['keyPwd'],
- dt['SSL']['ca']
- )
- self.hostname=dt['host']
- self.auth_name=dt['labkey']['user']
- self.auth_pass=dt['labkey']['password']
- #cj in opener contains the cookies
- def get(self,url):
- debug=False
- if debug:
- print "GET: {0}".format(url)
- print "as {0}".format(self.auth_name)
- r=urllib2.Request(url)
- base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
- r.add_header("Authorization", "Basic %s" % base64string)
- return self.opener.open(r)
- #f contains json as a return value
- def post(self,url,data):
- r=urllib2.Request(url)
- #makes it a post
- r.add_data(data)
- r.add_header("Content-Type","application/json")
- #add csrf
- csrf=self.getCSRF()
- r.add_header("X-LABKEY-CSRF",csrf)
- base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
- r.add_header("Authorization", "Basic %s" % base64string)
- print "{}: {}".format(r.get_method(),r.get_full_url())
- print "data: {}".format(r.get_data())
- print "Content-Type: {}".format(r.get_header('Content-Type'))
- return self.opener.open(r)
- #f contains json as a return value
- def put(self,url,data):
- print "PUT: {0}".format(url)
- r=MethodRequest(url.encode('utf-8'),method="PUT")
- #makes it a post
- r.add_data(data)
- r.add_header("content-type","application/octet-stream")
- base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
- r.add_header("Authorization", "Basic %s" % base64string)
- return self.opener.open(r)
- #f contains json as a return value
- def getCSRF(self):
- url=self.GetLabkeyUrl()+'/login/whoAmI.view'
- jsonData=json.load(self.get(url))
- return jsonData["CSRF"]
- def remoteDirExists(self,url):
- status,dirs=self.listRemoteDir(url);
- return status
- def mkdir(self,remoteDir):
- if self.remoteDirExists(remoteDir):
- return False
- r=MethodRequest(remoteDir,method="MKCOL")
- base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
- r.add_header("Authorization", "Basic %s" % base64string)
- try:
- f=self.opener.open(r)
- except:
- print "Error: Failed MKCOL {}".format(remoteDir)
- return False
- return True
- def mkdirs(self,remoteDir):
- labkeyPath=self.GetLabkeyPathFromRemotePath(remoteDir)
- s=0
- while True:
- s1=labkeyPath.find('/',s)
- if s1<0:
- break
- path=labkeyPath[0:s1]
- remotePath=self.GetLabkeyWebdavUrl()+'/'+path
- dirExists=self.remoteDirExists(remotePath)
- if not dirExists:
- if not self.mkdir(remotePath):
- return False
- s=s1+1
- return self.mkdir(remoteDir)
- def rmdir(self,remoteDir):
- if not self.remoteDirExists(remoteDir):
- return True
- r=MethodRequest(remoteDir,method="DELETE")
- base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
- r.add_header("Authorization", "Basic %s" % base64string)
- try:
- f=self.opener.open(r)
- except:
- print "Error: Failed DELETE {}".format(remoteDir)
- return False
- return True
- def listDir(self,dir):
- print "Listing for {0}".format(dir)
- dirUrl=self.GetLabkeyWebdavUrl()+"/"+dir
- status,dirs=self.listRemoteDir(dirUrl)
- return dirs
- def isDir(self, remotePath):
- #print "isDir: {}".format(remotePath)
- r=MethodRequest(remotePath,method="PROPFIND")
- PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
- <a:propfind xmlns:a="DAV:">\n
- <a:prop>\n
- <a:resourcetype/>\n
- </a:prop>\n
- </a:propfind>"""
- r.add_header('content-type','text/xml; charset="utf-8"')
- r.add_header('content-length',str(len(PROPFIND)))
- r.add_header('Depth','0')
- r.add_data(PROPFIND)
- base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
- r.add_header("Authorization", "Basic %s" % base64string)
- print "PROPFIND: {0}".format(remotePath)
- try:
- f=self.opener.open(r)
- except:
- return False
- tree=ET.XML(f.read())
- try:
- rps=tree.find('{DAV:}response').find('{DAV:}propstat')
- rps=rps.find('{DAV:}prop')
- rps=rps.find('{DAV:}resourcetype').find('{DAV:}collection')
- if rps != None:
- return True
- else:
- return False
- except:
- return False
- def listRemoteDir(self,dirUrl):
- 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>"""
- r.add_header('content-type','text/xml; charset="utf-8"')
- r.add_header('content-length',str(len(PROPFIND)))
- r.add_header('Depth','1')
- r.add_data(PROPFIND)
- base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
- r.add_header("Authorization", "Basic %s" % base64string)
- print "PROPFIND: {0}".format(dirUrl)
- dirs=[]
- try:
- f=self.opener.open(r)
- except:
- return False,dirs
- tree=ET.XML(f.read())
- rps=tree.findall('{DAV:}response')
- for r in rps:
- hr=r.find('{DAV:}href')
- dirent=hr.text
- dirent=re.sub('/labkey/_webdav/','',dirent)
- dirs.append(dirent)
- del dirs[0]
- return True,dirs
- def toRelativePath(self,dirs):
- flist1=[]
- for d in dirs:
- if d[-1]=='/':
- d=d[:-1]
- if d.rfind('/')>-1:
- d=d[d.rfind('/')+1:]
- flist1.append(d)
- return flist1
- def readFile(self, serverUrl, path):
- dirUrl=serverUrl+"/labkey/_webdav"+"/"+path
- f=self.get(dirUrl)
- return StringIO.StringIO(f.read())
- def uploadFile(self,localPath):
- #get upstream directories sorted out
- labkeyPath=self.GetLabkeyPathFromLocalPath(localPath)
- if labkeyPath=="NULL":
- errorCode="Failed to upload {}. Potential incorrect location"
- errorCode+=". Should be in labkeyCache!"
- print errorCode.format(localPath)
- return False
- labkeyDir=labkeyPath[0:labkeyPath.rfind('/')]
- remoteDir=self.GetLabkeyWebdavUrl()+'/'+labkeyDir
- if not self.remoteDirExists(remoteDir):
- if not self.mkdirs(remoteDir):
- errorCode="UploadFile: Could not create directory {}"
- print errorCode.format(remoteDir)
- return False
- #make an URL request
- with open(localPath, 'r') as f:
- data=f.read()
- remotePath=self.GetLabkeyWebdavUrl()+'/'+labkeyPath
- self.put(remotePath,data)
- def copyFileToRemote(self,localPath, remotePath):
- #get upstream directories sorted out
- labkeyDir=os.path.dirname(remotePath)
- if not self.remoteDirExists(labkeyDir):
- if not self.mkdirs(labkeyDir):
- errorCode="UploadFile: Could not create directory {}"
- print errorCode.format(labkeyDir)
- return False
- #make an URL request
- if (self.isDir(remotePath)):
- remotePath=remotePath+'/'+os.path.basename(localPath)
- with open(localPath, 'r') as f:
- data=f.read()
- self.put(remotePath,data)
- def loadDir(self, path):
- #dirURL=serverUrl+"/labkey/_webdav/"+path
- files=self.listDir(path)
- fdir="NONE"
- for f in files:
- #returns local path
- try:
- fdir=os.path.dirname(self.GetFile(f))
- except:
- #fails if there is a subdirectory; go recursively
- print "self.readDir(f) not implemented"
- return fdir
- def loadDataset(self,project,dataset):
- url=self.GetLabkeyUrl()+'/'+project
- url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
- return json.load(self.get(url))
- def filterDataset(self,project,dataset,variable,value,oper="eq"):
- debug=True
- url=self.GetLabkeyUrl()+'/'+project
- url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
- url+="&query."+variable+"~"+oper+"="+value
- if debug:
- print "Sending {}".format(url)
- return json.load(self.get(url))
- def modifyDataset(self,method,project,dataset,rows):
- #method can be insert or update
- data={}
- data['schemaName']='study'
- data['queryName']=dataset
- data['rows']=rows
- url=self.GetLabkeyUrl()+'/'+project
- url+='/query-'+method+'Rows.api?'
- return self.post(url,json.dumps(data))
|