slicerNetwork.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import qt
  2. import urllib2
  3. import ssl
  4. import cookielib
  5. import xml.etree.ElementTree as ET
  6. import re
  7. import StringIO
  8. import slicer
  9. import shutil
  10. import distutils
  11. import os
  12. import base64
  13. #see https://gist.github.com/logic/2715756, allow requests to do Put
  14. class MethodRequest(urllib2.Request):
  15. def __init__(self, *args, **kwargs):
  16. if 'method' in kwargs:
  17. self._method = kwargs['method']
  18. del kwargs['method']
  19. else:
  20. self._method = None
  21. return urllib2.Request.__init__(self, *args, **kwargs)
  22. def get_method(self, *args, **kwargs):
  23. if self._method is not None:
  24. return self._method
  25. return urllib2.Request.get_method(self, *args, **kwargs)
  26. class slicerNetwork:
  27. def __init__(self,parent):
  28. parent.title = "slicerNetwork"
  29. self.parent=parent
  30. class labkeyURIHandler(slicer.vtkURIHandler):
  31. def __init__(self):
  32. slicer.vtkURIHandler.__init__(self)
  33. self.className="labkeyURIHandler"
  34. slicer.mrmlScene.AddURIHandler(self)
  35. try:
  36. self.localCacheDirectory=os.path.join(os.environ["HOME"],"labkeyCache")
  37. except:
  38. #in windows, the variable is called HOMEPATH
  39. fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
  40. self.localCacheDirectory=os.path.join(fhome,"labkeyCache")
  41. def CanHandleURI(self,uri):
  42. print "labkeyURIHandler::CanHandleURI({0})".format(uri)
  43. if uri.find('labkey://')==0:
  44. return 1
  45. return 0
  46. def GetClassName(self):
  47. return self.className
  48. def GetHostName(self):
  49. return self.hostname
  50. def SetHostName(self,host):
  51. self.hostname=host
  52. def SetLocalCahceDirectory(self,dir):
  53. self.localCacheDirectory=dir
  54. def GetLocalCacheDirectory(self):
  55. return self.localCacheDirectory
  56. def GetLabkeyUrl(self):
  57. return self.hostname+"/labkey/_webdav"
  58. def GetLocalPath(self,source):
  59. debug=False
  60. relativePath=re.sub('labkey://','',source)
  61. sp=os.sep.encode('string-escape')
  62. if debug:
  63. print "Substituting / with {0} in {1}".format(sp,relativePath)
  64. relativePath=re.sub('/',sp,relativePath)
  65. return os.path.join(self.localCacheDirectory,relativePath)
  66. def GetRemotePath(self,source):
  67. return self.GetLabkeyUrl()+"/"+GetLabkeyPathFromLocalPath(source)
  68. def GetLabkeyPathFromLocalPath(self,f):
  69. #report it with URL separator, forward-slash
  70. if f.find(self.localCacheDirectory)<-1:
  71. print "Localpath misformation. Exiting"
  72. return "NULL"
  73. dest=re.sub(self.localCacheDirectory,'',f)
  74. #leaves /ContextPath/%40files/subdirectory_list/files
  75. #remove first separator
  76. sp=os.sep.encode('string-escape')
  77. if dest[0]==sp:
  78. dest=dest[1:len(dest)]
  79. return re.sub(sp,'/',dest)
  80. def GetLabkeyPathFromRemotePath(self,f):
  81. #used to query labkey stuff, so URL separator is used
  82. f=re.sub('labkey://','',f)
  83. f=re.sub(self.GetLabkeyUrl(),'',f)
  84. if f[0]=='/':
  85. f=f[1:len(f)]
  86. return f;
  87. def GetFile(self,source):
  88. # check for file in cache. If available, use, if not, download
  89. localPath=self.GetLocalPath(source)
  90. if os.path.isfile(localPath):
  91. return localPath
  92. self.StageFileRead(source,localPath)
  93. return localPath
  94. def StageFileRead(self,source,dest):
  95. print "labkeyURIHandler::StageFileRead({0},{1})".format(source,dest)
  96. labkeyPath=re.sub('labkey://','',source)
  97. remote=self.readFile(self.hostname,labkeyPath)
  98. #make all necessary directories
  99. path=os.path.dirname(dest)
  100. try:
  101. os.makedirs(path)
  102. except:
  103. if not os.path.isdir(path):
  104. raise
  105. local=open(dest,'wb')
  106. #make sure we are at the begining of the file
  107. #check file size
  108. remote.seek(0,2)
  109. sz=remote.tell()
  110. print "Remote size: {0}".format(sz)
  111. remote.seek(0)
  112. shutil.copyfileobj(remote,local)
  113. print "Local size: {0}".format(local.tell())
  114. local.close()
  115. def StageFileWrite(self,source,dest):
  116. print "labkeyURIHandler::StageFileWrite({0},{1}) not implemented yet".format(
  117. source,dest)
  118. def fileTypesAvailable(self):
  119. return ('VolumeFile','SegmentationFile','TransformFile')
  120. #mimic slicer.util.loadNodeFromFile
  121. def loadNodeFromFile(self, filename, filetype, properties={}, returnNode=False):
  122. #this is the only relevant part - file must be downloaded to cache
  123. localPath=self.GetFile(filename)
  124. slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode)
  125. #remove retrieved file
  126. try:
  127. if not(properties['keepCachedFile']) :
  128. os.remove(localPath)
  129. except:
  130. pass
  131. def loadVolume(self,filename, properties={}, returnNode=False):
  132. filetype = 'VolumeFile'
  133. #redirect to self.loadNodeFromFile first to get the cached file
  134. return self.loadNodeFromFile(filename,filetype, properties,returnNode)
  135. def loadSegmentation(self,filename,properties={},returnNode=False):
  136. filetype='SegmentationFile'
  137. #redirect to self.loadNodeFromFile first to get the cached file
  138. return self.loadNodeFromFile(filename,filetype, properties,returnNode)
  139. #add others if needed
  140. def configureSSL(self,cert,key,pwd,cacert):
  141. #do this first
  142. try:
  143. self.ctx=ssl.SSLContext(ssl.PROTOCOL_SSLv23)
  144. self.ctx.load_cert_chain(cert,key,pwd)
  145. self.ctx.verify_mode=ssl.CERT_REQUIRED
  146. self.ctx.load_verify_locations(cacert)
  147. except ssl.SSLError as err:
  148. print " Failed to configure SSL: {0}".format(str(err))
  149. def connectRemote(self,serverUrl,uname,pwd):
  150. https_handler=urllib2.HTTPSHandler(context=self.ctx)
  151. self.SetHostName(serverUrl)
  152. #cookie part
  153. cj=cookielib.CookieJar()
  154. cookie_handler=urllib2.HTTPCookieProcessor(cj)
  155. self.opener=urllib2.build_opener(https_handler,cookie_handler)
  156. loginUrl=serverUrl+"/labkey/login/login.post"
  157. r=urllib2.Request(str(loginUrl))
  158. print "Connecting to {0}".format(loginUrl)
  159. r.add_header('ContentType','application/x-www-form-urlencoded')
  160. data="email="+uname+"&password="+pwd
  161. #print "Data: '{0}".format(data)
  162. r.add_data(data)
  163. self.opener.open(r)
  164. def initRemote(self):
  165. https_handler=urllib2.HTTPSHandler(context=self.ctx)
  166. #cookie part
  167. cj=cookielib.CookieJar()
  168. cookie_handler=urllib2.HTTPCookieProcessor(cj)
  169. self.opener=urllib2.build_opener(https_handler,cookie_handler)
  170. #cj in opener contains the cookies
  171. def get(self,url):
  172. print "GET: {0}".format(url)
  173. r=urllib2.Request(url)
  174. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  175. r.add_header("Authorization", "Basic %s" % base64string)
  176. return self.opener.open(r)
  177. #f contains json as a return value
  178. def post(self,url,data):
  179. r=urllib2.Request(url)
  180. #makes it a post
  181. r.add_data(data)
  182. r.add_header("content-type","application/json")
  183. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  184. r.add_header("Authorization", "Basic %s" % base64string)
  185. f=self.opener.open(r)
  186. #f contains json as a return value
  187. def put(self,url,data):
  188. r=MethodRequest(url,method="PUT")
  189. #makes it a post
  190. r.add_data(data)
  191. r.add_header("content-type","application/octet-stream")
  192. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  193. r.add_header("Authorization", "Basic %s" % base64string)
  194. f=self.opener.open(r)
  195. #f contains json as a return value
  196. def remoteDirExists(self,url):
  197. status,dirs=self.listRemoteDir(url);
  198. return status
  199. def mkdir(self,remoteDir):
  200. if self.remoteDirExists(remoteDir):
  201. return False
  202. r=MethodRequest(remoteDir,method="MKCOL")
  203. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  204. r.add_header("Authorization", "Basic %s" % base64string)
  205. try:
  206. f=self.opener.open(r)
  207. except:
  208. print "Error: Failed MKCOL {}".format(remoteDir)
  209. return False
  210. return True
  211. def mkdirs(self,remoteDir):
  212. labkeyPath=self.GetLabkeyPathFromRemotePath(remoteDir)
  213. s=0
  214. while True:
  215. s1=labkeyPath.find('/',s)
  216. if s1<0:
  217. break
  218. path=labkeyPath[0:s1]
  219. remotePath=self.GetLabkeyUrl()+'/'+path
  220. dirExists=self.remoteDirExists(remotePath)
  221. if not dirExists:
  222. if not self.mkdir(remotePath):
  223. return False
  224. s=s1+1
  225. return self.mkdir(remoteDir)
  226. def rmdir(self,remoteDir):
  227. if not self.remoteDirExists(remoteDir):
  228. return True
  229. r=MethodRequest(remoteDir,method="DELETE")
  230. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  231. r.add_header("Authorization", "Basic %s" % base64string)
  232. try:
  233. f=self.opener.open(r)
  234. except:
  235. print "Error: Failed DELETE {}".format(remoteDir)
  236. return False
  237. return True
  238. def listDir(self,dir):
  239. print "Listing for {0}".format(dir)
  240. dirUrl=self.GetLabkeyUrl()+"/"+dir
  241. status,dirs=self.listRemoteDir(dirUrl)
  242. return dirs
  243. def listRemoteDir(self,dirUrl):
  244. r=MethodRequest(dirUrl,method="PROPFIND")
  245. PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
  246. <propfind xmlns="DAV:">\n
  247. <prop>\n
  248. <getetag/>\n
  249. </prop>\n
  250. </propfind>"""
  251. r.add_header('content-type','text/xml; charset="utf-8"')
  252. r.add_header('content-length',str(len(PROPFIND)))
  253. r.add_header('Depth','1')
  254. r.add_data(PROPFIND)
  255. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  256. r.add_header("Authorization", "Basic %s" % base64string)
  257. print "PROPFIND: {0}".format(dirUrl)
  258. dirs=[]
  259. try:
  260. f=self.opener.open(r)
  261. except:
  262. return False,dirs
  263. tree=ET.XML(f.read())
  264. rps=tree.findall('{DAV:}response')
  265. for r in rps:
  266. hr=r.find('{DAV:}href')
  267. dirent=hr.text
  268. dirent=re.sub('/labkey/_webdav/','',dirent)
  269. dirs.append(dirent)
  270. del dirs[0]
  271. return True,dirs
  272. def toRelativePath(self,dirs):
  273. flist1=[]
  274. for d in dirs:
  275. if d[-1]=='/':
  276. d=d[:-1]
  277. if d.rfind('/')>-1:
  278. d=d[d.rfind('/')+1:]
  279. flist1.append(d)
  280. return flist1
  281. def readFile(self, serverUrl, path):
  282. dirUrl=serverUrl+"/labkey/_webdav"+"/"+path
  283. f=self.get(dirUrl)
  284. return StringIO.StringIO(f.read())
  285. def uploadFile(self,localPath):
  286. #get upstream directories sorted out
  287. labkeyPath=self.GetLabkeyPathFromLocalPath(localPath)
  288. if labkeyPath=="NULL":
  289. errorCode="Failed to upload {}. Potential incorrect location"
  290. errorCode+=". Should be in labkeyCache!"
  291. print errorCode.format(localPath)
  292. return False
  293. labkeyDir=labkeyPath[0:labkeyPath.rfind('/')]
  294. remoteDir=self.GetLabkeyUrl()+'/'+labkeyDir
  295. if not self.remoteDirExists(remoteDir):
  296. if not self.mkdirs(remoteDir):
  297. errorCode="UploadFile: Could not create directory {}"
  298. print errorCode.format(remoteDir)
  299. return False
  300. #make an URL request
  301. with open(localPath, 'r') as f:
  302. data=f.read()
  303. remotePath=self.GetLabkeyUrl()+'/'+labkeyPath
  304. self.put(remotePath,data)
  305. def loadDir(self, path):
  306. #dirURL=serverUrl+"/labkey/_webdav/"+path
  307. files=self.listDir(path)
  308. fdir="NONE"
  309. for f in files:
  310. #returns local path
  311. try:
  312. fdir=os.path.dirname(self.GetFile(f))
  313. except:
  314. #fails if there is a subdirectory; go recursively
  315. self.readDir(f)
  316. return fdir