slicerNetwork.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  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. import json
  14. #see https://gist.github.com/logic/2715756, allow requests to do Put
  15. class MethodRequest(urllib2.Request):
  16. def __init__(self, *args, **kwargs):
  17. if 'method' in kwargs:
  18. self._method = kwargs['method']
  19. del kwargs['method']
  20. else:
  21. self._method = None
  22. return urllib2.Request.__init__(self, *args, **kwargs)
  23. def get_method(self, *args, **kwargs):
  24. if self._method is not None:
  25. return self._method
  26. return urllib2.Request.get_method(self, *args, **kwargs)
  27. class slicerNetwork(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
  28. def __init__(self, parent):
  29. slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self,parent)
  30. self.parent.title="slicerNetwork"
  31. pass
  32. class labkeyURIHandler(slicer.vtkURIHandler):
  33. def __init__(self):
  34. slicer.vtkURIHandler.__init__(self)
  35. self.className="labkeyURIHandler"
  36. slicer.mrmlScene.AddURIHandler(self)
  37. try:
  38. fhome=os.environ["HOME"]
  39. except:
  40. #in windows, the variable is called HOMEPATH
  41. fhome=os.environ['HOMEDRIVE']+os.environ['HOMEPATH']
  42. self.localCacheDirectory=os.path.join(fhome,"labkeyCache")
  43. self.configDir=os.path.join(fhome,".labkey")
  44. self.mode="http"
  45. #try initializing network from default config file, if found
  46. self.initFromConfig()
  47. def CanHandleURI(self,uri):
  48. print("labkeyURIHandler::CanHandleURI({0})").format(uri)
  49. if uri.find('labkey://')==0:
  50. return 1
  51. return 0
  52. def GetClassName(self):
  53. return self.className
  54. def GetHostName(self):
  55. return self.hostname
  56. def SetHostName(self,host):
  57. self.hostname=host
  58. def SetLocalCahceDirectory(self,dir):
  59. self.localCacheDirectory=dir
  60. def GetLocalCacheDirectory(self):
  61. return self.localCacheDirectory
  62. def GetLabkeyUrl(self):
  63. return self.hostname+"/labkey"
  64. def GetLabkeyWebdavUrl(self):
  65. return self.GetLabkeyUrl()+"/_webdav"
  66. def GetLocalPath(self,source):
  67. debug=False
  68. relativePath=re.sub('labkey://','',source)
  69. sp=os.sep.encode('string-escape')
  70. if debug:
  71. print("Substituting / with {0} in {1}").format(sp,relativePath)
  72. relativePath=re.sub('/',sp,relativePath)
  73. return os.path.join(self.localCacheDirectory,relativePath)
  74. def GetRemotePath(self,source):
  75. return self.GetLabkeyWebdavUrl()+"/"+GetLabkeyPathFromLocalPath(source)
  76. def GetLabkeyPathFromLocalPath(self,f):
  77. #report it with URL separator, forward-slash
  78. if f.find(self.localCacheDirectory)<-1:
  79. print("Localpath misformation. Exiting")
  80. return "NULL"
  81. dest=re.sub(self.localCacheDirectory,'',f)
  82. #leaves /ContextPath/%40files/subdirectory_list/files
  83. #remove first separator
  84. sp=os.sep.encode('string-escape')
  85. if dest[0]==sp:
  86. dest=dest[1:len(dest)]
  87. return re.sub(sp,'/',dest)
  88. def GetLabkeyPathFromRemotePath(self,f):
  89. #used to query labkey stuff, so URL separator is used
  90. f=re.sub('labkey://','',f)
  91. f=re.sub(self.GetLabkeyWebdavUrl(),'',f)
  92. if f[0]=='/':
  93. f=f[1:len(f)]
  94. return f;
  95. def GetFile(self,source):
  96. # check for file in cache. If available, use, if not, download
  97. localPath=self.GetLocalPath(source)
  98. if os.path.isfile(localPath):
  99. return localPath
  100. self.StageFileRead(source,localPath)
  101. return localPath
  102. def StageFileRead(self,source,dest):
  103. debug=False
  104. if debug:
  105. print("labkeyURIHandler::StageFileRead({0},{1})").format(source,dest)
  106. labkeyPath=re.sub('labkey://','',source)
  107. remote=self.readFile(self.hostname,labkeyPath)
  108. #make all necessary directories
  109. path=os.path.dirname(dest)
  110. if not os.path.isdir(path):
  111. os.makedirs(path)
  112. local=open(dest,'wb')
  113. #make sure we are at the begining of the file
  114. #check file size
  115. <<<<<<< HEAD
  116. remote.seek(0,2)
  117. sz=remote.tell()
  118. print "Remote size: {0}".format(sz)
  119. remote.seek(0)
  120. shutil.copyfileobj(remote,local)
  121. print "Local size: {0}".format(local.tell())
  122. =======
  123. if debug:
  124. remote.seek(0,2)
  125. sz=remote.tell()
  126. print("Remote size: {0}").format(sz)
  127. remote.seek(0)
  128. shutil.copyfileobj(remote,local)
  129. if debug:
  130. print("Local size: {0}").format(local.tell())
  131. >>>>>>> 7212758e9e58c9afa6f1d2a0b25eaef3d0a6ad64
  132. local.close()
  133. def StageFileWrite(self,source,dest):
  134. print("labkeyURIHandler::StageFileWrite({0},{1}) not implemented yet").format(
  135. source,dest)
  136. def fileTypesAvailable(self):
  137. return ('VolumeFile','SegmentationFile','TransformFile')
  138. #mimic slicer.util.loadNodeFromFile
  139. # def loadNodeFromFile(self, filename, filetype, properties={}, returnNode=False):
  140. # #this is the only relevant part - file must be downloaded to cache
  141. # localPath=self.GetFile(filename)
  142. # slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode)
  143. # #remove retrieved file
  144. # try:
  145. # if not(properties['keepCachedFile']) :
  146. # os.remove(localPath)
  147. # except:
  148. # pass
  149. # def loadVolume(self,filename, properties={}, returnNode=False):
  150. # filetype = 'VolumeFile'
  151. # #redirect to self.loadNodeFromFile first to get the cached file
  152. # return self.loadNodeFromFile(filename,filetype, properties,returnNode)
  153. #
  154. # def loadSegmentation(self,filename,properties={},returnNode=False):
  155. # filetype='SegmentationFile'
  156. # #redirect to self.loadNodeFromFile first to get the cached file
  157. # return self.loadNodeFromFile(filename,filetype, properties,returnNode)
  158. # #add others if needed
  159. ## setup & initialization routines
  160. def configureSSL(self,cert,key,pwd,cacert):
  161. #do this first
  162. try:
  163. self.ctx=ssl.SSLContext(ssl.PROTOCOL_SSLv23)
  164. self.ctx.load_cert_chain(cert,key,pwd)
  165. self.ctx.verify_mode=ssl.CERT_REQUIRED
  166. self.ctx.load_verify_locations(cacert)
  167. except ssl.SSLError as err:
  168. print(" Failed to configure SSL: {0}").format(str(err))
  169. self.mode="https"
  170. def initRemote(self):
  171. if self.mode=="https":
  172. http_handler=urllib2.HTTPSHandler(context=self.ctx)
  173. if self.mode=="http":
  174. http_handler=urllib2.HTTPHandler()
  175. #cookie part
  176. cj=cookielib.CookieJar()
  177. cookie_handler=urllib2.HTTPCookieProcessor(cj)
  178. self.opener=urllib2.build_opener(http_handler,cookie_handler)
  179. def initFromConfig(self):
  180. path=os.path.join(self.configDir,"Remote.json")
  181. try:
  182. self.parseConfig(path)
  183. except OSError:
  184. return
  185. self.initRemote()
  186. def parseConfig(self,fname):
  187. try:
  188. f=open(fname)
  189. except OSError as e:
  190. print("Confgiuration error: OS error({0}): {1}").format(e.errno, e.strerror)
  191. raise
  192. dt=json.load(f)
  193. self.mode="http"
  194. if dt.has_key('SSL'):
  195. self.configureSSL(
  196. dt['SSL']['user'],
  197. dt['SSL']['key'],
  198. dt['SSL']['keyPwd'],
  199. dt['SSL']['ca']
  200. )
  201. self.hostname=dt['host']
  202. self.auth_name=dt['labkey']['user']
  203. self.auth_pass=dt['labkey']['password']
  204. #cj in opener contains the cookies
  205. def get(self,url):
  206. debug=False
  207. if debug:
  208. print("GET: {0}").format(url)
  209. print("as {0}").format(self.auth_name)
  210. r=urllib2.Request(url)
  211. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  212. r.add_header("Authorization", "Basic %s" % base64string)
  213. return self.opener.open(r)
  214. #f contains json as a return value
  215. def post(self,url,data):
  216. r=urllib2.Request(url)
  217. #makes it a post
  218. r.add_data(data)
  219. r.add_header("Content-Type","application/json")
  220. #add csrf
  221. csrf=self.getCSRF()
  222. r.add_header("X-LABKEY-CSRF",csrf)
  223. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  224. r.add_header("Authorization", "Basic %s" % base64string)
  225. print("{}: {}").format(r.get_method(),r.get_full_url())
  226. print("data: {}").format(r.get_data())
  227. print("Content-Type: {}").format(r.get_header('Content-Type'))
  228. try:
  229. return self.opener.open(r)
  230. except urllib2.HTTPError as e:
  231. print e.code
  232. print e.read()
  233. return e
  234. #f contains json as a return value
  235. def put(self,url,data):
  236. print("PUT: {0}").format(url)
  237. r=MethodRequest(url.encode('utf-8'),method="PUT")
  238. #makes it a post
  239. r.add_data(data)
  240. r.add_header("content-type","application/octet-stream")
  241. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  242. r.add_header("Authorization", "Basic %s" % base64string)
  243. return self.opener.open(r)
  244. #f contains json as a return value
  245. def getCSRF(self):
  246. url=self.GetLabkeyUrl()+'/login/whoAmI.view'
  247. jsonData=json.load(self.get(url))
  248. return jsonData["CSRF"]
  249. def remoteDirExists(self,url):
  250. status,dirs=self.listRemoteDir(url);
  251. return status
  252. def mkdir(self,remoteDir):
  253. if self.remoteDirExists(remoteDir):
  254. return False
  255. r=MethodRequest(remoteDir,method="MKCOL")
  256. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  257. r.add_header("Authorization", "Basic %s" % base64string)
  258. try:
  259. f=self.opener.open(r)
  260. except:
  261. print("Error: Failed MKCOL {}").format(remoteDir)
  262. return False
  263. return True
  264. def mkdirs(self,remoteDir):
  265. labkeyPath=self.GetLabkeyPathFromRemotePath(remoteDir)
  266. s=0
  267. while True:
  268. s1=labkeyPath.find('/',s)
  269. if s1<0:
  270. break
  271. path=labkeyPath[0:s1]
  272. remotePath=self.GetLabkeyWebdavUrl()+'/'+path
  273. dirExists=self.remoteDirExists(remotePath)
  274. if not dirExists:
  275. if not self.mkdir(remotePath):
  276. return False
  277. s=s1+1
  278. return self.mkdir(remoteDir)
  279. def rmdir(self,remoteDir):
  280. if not self.remoteDirExists(remoteDir):
  281. return True
  282. r=MethodRequest(remoteDir,method="DELETE")
  283. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  284. r.add_header("Authorization", "Basic %s" % base64string)
  285. try:
  286. f=self.opener.open(r)
  287. except:
  288. print("Error: Failed DELETE {}").format(remoteDir)
  289. return False
  290. return True
  291. def listDir(self,dir):
  292. print("Listing for {0}").format(dir)
  293. dirUrl=self.GetLabkeyWebdavUrl()+"/"+dir
  294. status,dirs=self.listRemoteDir(dirUrl)
  295. return dirs
  296. def isDir(self, remotePath):
  297. #print "isDir: {}".format(remotePath)
  298. r=MethodRequest(remotePath,method="PROPFIND")
  299. PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
  300. <a:propfind xmlns:a="DAV:">\n
  301. <a:prop>\n
  302. <a:resourcetype/>\n
  303. </a:prop>\n
  304. </a:propfind>"""
  305. r.add_header('content-type','text/xml; charset="utf-8"')
  306. r.add_header('content-length',str(len(PROPFIND)))
  307. r.add_header('Depth','0')
  308. r.add_data(PROPFIND)
  309. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  310. r.add_header("Authorization", "Basic %s" % base64string)
  311. print("PROPFIND: {0}").format(remotePath)
  312. try:
  313. f=self.opener.open(r)
  314. except:
  315. return False
  316. tree=ET.XML(f.read())
  317. try:
  318. rps=tree.find('{DAV:}response').find('{DAV:}propstat')
  319. rps=rps.find('{DAV:}prop')
  320. rps=rps.find('{DAV:}resourcetype').find('{DAV:}collection')
  321. if rps != None:
  322. return True
  323. else:
  324. return False
  325. except:
  326. return False
  327. def listRemoteDir(self,dirUrl):
  328. r=MethodRequest(dirUrl,method="PROPFIND")
  329. PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
  330. <propfind xmlns="DAV:">\n
  331. <prop>\n
  332. <getetag/>\n
  333. </prop>\n
  334. </propfind>"""
  335. r.add_header('content-type','text/xml; charset="utf-8"')
  336. r.add_header('content-length',str(len(PROPFIND)))
  337. r.add_header('Depth','1')
  338. r.add_data(PROPFIND)
  339. base64string = base64.b64encode('%s:%s' % (self.auth_name, self.auth_pass))
  340. r.add_header("Authorization", "Basic %s" % base64string)
  341. print("PROPFIND: {0}").format(dirUrl)
  342. dirs=[]
  343. try:
  344. f=self.opener.open(r)
  345. except:
  346. return False,dirs
  347. tree=ET.XML(f.read())
  348. rps=tree.findall('{DAV:}response')
  349. for r in rps:
  350. hr=r.find('{DAV:}href')
  351. dirent=hr.text
  352. dirent=re.sub('/labkey/_webdav/','',dirent)
  353. dirs.append(dirent)
  354. del dirs[0]
  355. return True,dirs
  356. def toRelativePath(self,dirs):
  357. flist1=[]
  358. for d in dirs:
  359. if d[-1]=='/':
  360. d=d[:-1]
  361. if d.rfind('/')>-1:
  362. d=d[d.rfind('/')+1:]
  363. flist1.append(d)
  364. return flist1
  365. def readFile(self, serverUrl, path):
  366. dirUrl=serverUrl+"/labkey/_webdav"+"/"+path
  367. f=self.get(dirUrl)
  368. return StringIO.StringIO(f.read())
  369. def uploadFile(self,localPath):
  370. #get upstream directories sorted out
  371. labkeyPath=self.GetLabkeyPathFromLocalPath(localPath)
  372. if labkeyPath=="NULL":
  373. errorCode="Failed to upload {}. Potential incorrect location"
  374. errorCode+=". Should be in labkeyCache!"
  375. print(errorCode.format(localPath))
  376. return False
  377. labkeyDir=labkeyPath[0:labkeyPath.rfind('/')]
  378. remoteDir=self.GetLabkeyWebdavUrl()+'/'+labkeyDir
  379. if not self.remoteDirExists(remoteDir):
  380. if not self.mkdirs(remoteDir):
  381. errorCode="UploadFile: Could not create directory {}"
  382. print(errorCode.format(remoteDir))
  383. return False
  384. #make an URL request
  385. with open(localPath, 'r') as f:
  386. data=f.read()
  387. remotePath=self.GetLabkeyWebdavUrl()+'/'+labkeyPath
  388. self.put(remotePath,data)
  389. def copyFileToRemote(self,localPath, remotePath):
  390. #get upstream directories sorted out
  391. labkeyDir=os.path.dirname(remotePath)
  392. if not self.remoteDirExists(labkeyDir):
  393. if not self.mkdirs(labkeyDir):
  394. errorCode="UploadFile: Could not create directory {}"
  395. print(errorCode.format(labkeyDir))
  396. return False
  397. #make an URL request
  398. if (self.isDir(remotePath)):
  399. remotePath=remotePath+'/'+os.path.basename(localPath)
  400. with open(localPath, 'r') as f:
  401. data=f.read()
  402. self.put(remotePath,data)
  403. def loadDir(self, path):
  404. #dirURL=serverUrl+"/labkey/_webdav/"+path
  405. files=self.listDir(path)
  406. fdir="NONE"
  407. for f in files:
  408. #returns local path
  409. try:
  410. fdir=os.path.dirname(self.GetFile(f))
  411. except:
  412. #fails if there is a subdirectory; go recursively
  413. print("self.readDir(f) not implemented")
  414. return fdir
  415. def loadDataset(self,project,dataset):
  416. url=self.GetLabkeyUrl()+'/'+project
  417. url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
  418. return json.load(self.get(url))
  419. def filterDataset(self,project,dataset,variable,value,oper="eq"):
  420. debug=True
  421. url=self.GetLabkeyUrl()+'/'+project
  422. url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
  423. url+="&query."+variable+"~"+oper+"="+value
  424. if debug:
  425. print("Sending {}").format(url)
  426. return json.load(self.get(url))
  427. def modifyDataset(self,method,project,dataset,rows):
  428. #method can be insert or update
  429. data={}
  430. data['schemaName']='study'
  431. data['queryName']=dataset
  432. data['rows']=rows
  433. url=self.GetLabkeyUrl()+'/'+project
  434. url+='/query-'+method+'Rows.api?'
  435. return self.post(url,json.dumps(data))