slicerNetwork.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. import qt
  2. import urllib3
  3. import xml.etree.ElementTree as ET
  4. import re
  5. import slicer
  6. import shutil
  7. import distutils
  8. import os
  9. import json
  10. import chardet
  11. import io
  12. #mimics labkeyInterface
  13. class slicerNetwork(slicer.ScriptedLoadableModule.ScriptedLoadableModule):
  14. def __init__(self, parent):
  15. slicer.ScriptedLoadableModule.ScriptedLoadableModule.__init__(self,parent)
  16. self.parent.title="slicerNetwork"
  17. pass
  18. class DummyResponse:
  19. pass
  20. class labkeyURIHandler(slicer.vtkURIHandler):
  21. def __init__(self):
  22. slicer.vtkURIHandler.__init__(self)
  23. self.className="labkeyURIHandler"
  24. slicer.mrmlScene.AddURIHandler(self)
  25. fhome=os.path.expanduser('~')
  26. self.localCacheDirectory=os.path.join(fhome,"labkeyCache")
  27. self.configDir=os.path.join(fhome,".labkey")
  28. #try initializing network from default config file, if found
  29. self.initFromConfig()
  30. def CanHandleURI(self,uri):
  31. print("labkeyURIHandler::CanHandleURI({0})".format(uri))
  32. if uri.find('labkey://')==0:
  33. return 1
  34. return 0
  35. def GetClassName(self):
  36. return self.className
  37. def GetHostName(self):
  38. self.hostname=self.connectionConfig['host']
  39. print("Host name:{}".format(self.hostname))
  40. return self.hostname
  41. def SetHostName(self,host):
  42. try:
  43. self.connectionConfig['host']=host
  44. except AttributeError:
  45. self.connectionConfig={}
  46. self.connectionConfig['host']=host
  47. self.hostname=host
  48. def SetLocalCahceDirectory(self,dir):
  49. self.localCacheDirectory=dir
  50. def GetLocalCacheDirectory(self):
  51. return self.localCacheDirectory
  52. def GetLabkeyUrl(self):
  53. return self.GetHostName()+"/labkey"
  54. def GetLabkeyWebdavUrl(self):
  55. return self.GetLabkeyUrl()+"/_webdav"
  56. #configuration part
  57. def configureSSL(self,cert=None,key=None,pwd=None,cacert=None):
  58. print("configure SSL: {}".format(cert))
  59. if not cert==None:
  60. self.http = urllib3.PoolManager(\
  61. cert_file=cert,\
  62. cert_reqs='CERT_REQUIRED',\
  63. key_file=key,\
  64. ca_certs=cacert)
  65. else:
  66. self.http = urllib3.PoolManager(\
  67. cert_reqs='CERT_REQUIRED',\
  68. ca_certs=cacert)
  69. def initRemote(self):
  70. #assume self.connectionConfig is set
  71. self.http=urllib3.PoolManager()
  72. try:
  73. config=self.connectionConfig
  74. except AttributeError:
  75. print("ConnectionConfig not initialized")
  76. return
  77. if 'SSL' in self.connectionConfig:
  78. print("Setting up SSL")
  79. try:
  80. cert=self.connectionConfig['SSL']['user']
  81. except KeyError:
  82. print("No user cert supplied")
  83. return
  84. try:
  85. key=self.connectionConfig['SSL']['key']
  86. except KeyError:
  87. print("No user key supplied")
  88. return
  89. try:
  90. ca=self.connectionConfig['SSL']['ca']
  91. except KeyError:
  92. print("No CA cert supplied")
  93. return
  94. self.configureSSL(cert,key,None,ca)
  95. def initFromConfig(self):
  96. print("slicerNetwork: initFromConfig")
  97. path=os.path.join(self.configDir,"Remote.json")
  98. try:
  99. self.parseConfig(path)
  100. except OSError:
  101. return
  102. self.initRemote()
  103. def parseConfig(self,fname,parent=None):
  104. print("slicerNetwork: parseConfig")
  105. try:
  106. with open(fname) as f:
  107. self.connectionConfig=json.load(f)
  108. except OSError as e:
  109. print("Confgiuration error: OS error({0}): {1}".format(e.errno, e.strerror))
  110. raise
  111. except AttributeError:
  112. print("Troubles parsing json at {}".format(fname))
  113. if not parent==None:
  114. parent.userCertButton.setText(self.connectionConfig['SSL']['user'])
  115. parent.caCertButton.setText(self.connectionConfig['SSL']['ca'])
  116. parent.privateKeyButton.setText(self.connectionConfig['SSL']['key'])
  117. parent.pwd=self.connectionConfig['SSL']['keyPwd']
  118. self.hostname=self.connectionConfig['host']
  119. self.auth_name=self.connectionConfig['labkey']['user']
  120. self.auth_pass=self.connectionConfig['labkey']['password']
  121. if not parent==None:
  122. parent.serverURL.setText(self.connectionConfig['host'])
  123. parent.authName.setText(self.connectionConfig['labkey']['user'])
  124. parent.authPass.setText(self.connectionConfig['labkey']['password'])
  125. #path convention
  126. #localPath is a path on local filesystem. It means it has to adhere to OS
  127. #policy, especially in separators
  128. #relativePath is just the portion of the path that is equal both locally
  129. #and remotely. In relativePath, separator is according to http convention,
  130. #which equals separator in osx and *nix
  131. #labkeyPath or remotePath is the full URL to the resource,
  132. #including http-like header and all intermediate portions
  133. #the following functions convert between different notations
  134. #was GetLocalPath
  135. def GetLocalPathFromRelativePath(self,relativePath):
  136. debug=False
  137. relativePath=re.sub('labkey://','',relativePath)
  138. sp=os.sep.encode('unicode_escape').decode('utf-8')
  139. if debug:
  140. print("Substituting / with {0} in {1}".format(sp,relativePath))
  141. relativePath=re.sub('/',sp,relativePath)
  142. return os.path.join(self.GetLocalCacheDirectory(),relativePath)
  143. #was GetRemotePath
  144. def GetLabkeyPathFromRelativePath(self,source):
  145. return self.GetLabkeyWebdavUrl()+"/"+source
  146. #was GetLabkeyPathFromLocalPath
  147. def GetRelativePathFromLocalPath(self,f):
  148. debug=False
  149. #report it with URL separator, forward-slash
  150. if f.find(self.localCacheDirectory)<-1:
  151. print("Localpath misformation. Exiting")
  152. return "NULL"
  153. f0=re.sub('\\\\','\\\\\\\\',self.localCacheDirectory)
  154. relativePath=re.sub(re.compile(f0),'',f)
  155. if debug:
  156. print("[SEP] Relative path {}".format(relativePath))
  157. #leaves /ContextPath/%40files/subdirectory_list/files
  158. #remove first separator
  159. sp=os.path.sep
  160. f1=re.sub('\\\\','\\\\\\\\',sp)
  161. if relativePath[0]==sp:
  162. relativePath=relativePath[1:len(relativePath)]
  163. if debug:
  164. print("[SLASH] Relative path {}".format(relativePath))
  165. return re.sub(f1,'/',relativePath)
  166. #was GetLabkeyPathFromRemotePath
  167. def GetRelativePathFromLabkeyPath(self,f):
  168. #used to query labkey stuff, so URL separator is used
  169. #in old conventions, labkey://was used to tag a labkeyPath
  170. f=re.sub('labkey://','',f)
  171. f=re.sub(self.GetLabkeyWebdavUrl(),'',f)
  172. if f[0]=='/':
  173. f=f[1:len(f)]
  174. return f;
  175. def getBasicAuth(self):
  176. user=self.connectionConfig['labkey']['user']
  177. pwd=self.connectionConfig['labkey']['password']
  178. #debug
  179. return user+":"+pwd
  180. #standard HTTP
  181. def get(self,url,binary=False):
  182. debug=True
  183. if debug:
  184. print("GET: {0}".format(url))
  185. print("as {0}".format(self.connectionConfig['labkey']['user']))
  186. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  187. try:
  188. if not binary:
  189. return self.http.request('GET',url,headers=headers)
  190. else:
  191. return self.http.request('GET',url,headers=headers,preload_content=False)
  192. #f contains json as a return value
  193. #f contains json as a return value
  194. except urllib3.exceptions.HTTPError as e:
  195. print("HTTP error {}".format(e))
  196. response=DummyResponse()
  197. response.status=1000
  198. response.data=str(e)
  199. return response
  200. #a HEAD request
  201. def head(self,url):
  202. debug=False
  203. if debug:
  204. print("HEAD: {0}".format(url))
  205. print("as {0}".format(self.auth_name))
  206. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  207. try:
  208. return self.http.request('HEAD',url,headers=headers)
  209. except urllib3.exceptions.HTTPError as e:
  210. print(e)
  211. response=DummyResponse()
  212. response.status=1000
  213. response.data=str(e)
  214. return response
  215. def post(self,url,data):
  216. debug=False
  217. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  218. headers["Content-Type"]="application/json"
  219. #add csrf;also sets self.cookie
  220. headers["X-LABKEY-CSRF"]=self.getCSRF()
  221. headers["Cookie"]=self.cookie
  222. try:
  223. return self.http.request('POST',url,headers=headers,body=data)
  224. #f contains json as a return value
  225. except urllib3.exceptions.HTTPError as e:
  226. print(e)
  227. response=DummyResponse()
  228. response.status=1000
  229. response.data=str(e)
  230. return response
  231. def put(self,url,data):
  232. debug=False
  233. if debug:
  234. print("PUT: {}".format(url))
  235. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  236. headers["Content-Type"]="application/octet-stream"
  237. #add csrf
  238. headers["X-LABKEY-CSRF"]=self.getCSRF()
  239. headers["Cookie"]=self.cookie
  240. try:
  241. return self.http.request('PUT',url,headers=headers,body=data)
  242. #f contains json as a return value
  243. except urllib3.exceptions.HTTPError as e:
  244. print(e)
  245. response=DummyResponse()
  246. response.status=1000
  247. response.data=str(e)
  248. return response
  249. def sendRequest(self,url,requestCode):
  250. debug=False
  251. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  252. #add csrf;also sets self.cookie
  253. headers["X-LABKEY-CSRF"]=self.getCSRF()
  254. headers["Cookie"]=self.cookie
  255. try:
  256. return self.http.request(requestCode,url,headers=headers)
  257. #f contains json as a return value
  258. except urllib3.exceptions.HTTPError as e:
  259. print(e)
  260. response=DummyResponse()
  261. response.status=1000
  262. response.data=str(e)
  263. return response
  264. def mkcol(self,url):
  265. return self.sendRequest(url,'MKCOL')
  266. def delete(self,url):
  267. return self.sendRequest(url,'DELETE')
  268. def propfind(self,url,PROPFIND):
  269. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  270. headers["Content-Type"]='text/xml; charset="utf-8"'
  271. headers['content-length']=str(len(PROPFIND))
  272. headers['Depth']='1'
  273. #add csrf
  274. headers["X-LABKEY-CSRF"]=self.getCSRF()
  275. headers["Cookie"]=self.cookie
  276. try:
  277. return self.http.request('PROPFIND',url,headers=headers,body=PROPFIND)
  278. #f contains json as a return value
  279. except urllib3.exceptions.HTTPError as e:
  280. print(e)
  281. response=DummyResponse()
  282. response.status=1000
  283. response.data=str(e)
  284. return response
  285. @staticmethod
  286. def HTTPStatus(response,method=None):
  287. if response.status==200:
  288. return True
  289. if method=='propfind':
  290. if response.status==207:
  291. return True
  292. print("Status: {}".format(response.status))
  293. print("Data: {}".format(response.data))
  294. return False
  295. #a good testing routine; CSRF is required for content modifying requests
  296. def getCSRF(self):
  297. url=self.GetLabkeyUrl()+'/login/whoAmI.view'
  298. try:
  299. response=self.get(url)
  300. if not labkeyURIHandler.HTTPStatus(response):
  301. return None
  302. encoding=chardet.detect(response.data)['encoding']
  303. jsonData=json.loads(response.data.decode(encoding))
  304. except AttributeError:
  305. print("Response: {}".response.data)
  306. print("Failed")
  307. return None
  308. #local cookie jar
  309. self.cookie=response.getheader('Set-Cookie')
  310. print("CSRF: {}".format(jsonData["CSRF"]))
  311. user=jsonData['email']
  312. if not user==self.connectionConfig['labkey']['user']:
  313. print("User mismatch: {}/{}".format(user,self.connectionConfig['labkey']['user']))
  314. return None
  315. return jsonData["CSRF"]
  316. #file manipulation routiens
  317. #was GetFile
  318. def DownloadFileToCache(self,relativePath):
  319. debug=True
  320. # check for file in cache. If available, use, if not, download
  321. localPath=self.GetLocalPathFromRelativePath(relativePath)
  322. if os.path.isfile(localPath):
  323. if debug:
  324. print("Returning localFile {}".format(localPath))
  325. return localPath
  326. if debug:
  327. print("labkeyURIHandler::DownloadFileToCache({0}->{1})".format(relativePath,localPath))
  328. #make all necessary directories LOCALLY
  329. path=os.path.dirname(localPath)
  330. if debug:
  331. print("Target directory: '{}''".format(path))
  332. if not os.path.isdir(path):
  333. os.makedirs(path)
  334. #make sure we are at the begining of the file
  335. #labkeyPath=self.GetLabkeyPathFromRelativePath(relativePath)
  336. remoteBuffer=self.readFileToBuffer(relativePath)
  337. #check file size
  338. if debug:
  339. print("Remote size: {}".format(remoteBuffer.seek(0,2)))
  340. remoteBuffer.seek(0)
  341. with open(localPath,'wb') as localBuffer:
  342. shutil.copyfileobj(remoteBuffer,localBuffer)
  343. print("Local size: {}".format(os.path.getsize(localPath)))
  344. return localPath
  345. def fileTypesAvailable(self):
  346. return ('VolumeFile','SegmentationFile','TransformFile')
  347. #mimic slicer.util.loadNodeFromFile
  348. def loadNode(self, relativeName, filetype, properties={},returnNode=False, keepCached=True):
  349. #this is the only relevant part - file must be downloaded to cache
  350. #labkeyName is just the relative part (from labkey onwards)
  351. localPath=self.DownloadFileToCache(relativeName)
  352. if not returnNode:
  353. slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode=False)
  354. if not keepCached:
  355. os.remove(localPath)
  356. return
  357. ok,node=slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode=True)
  358. if ok:
  359. if not keepCached:
  360. os.remove(localPath)
  361. return node
  362. os.remove(localPath)
  363. return None
  364. # #remove retrieved file
  365. def storeNode(self,node,project,dir=None):
  366. removeFile=True
  367. relativePath=project+'/%40files'
  368. if not dir==None:
  369. relativePath+='/'+dir
  370. labkeyPath=self.GetLabkeyPathFromRelativePath(relativePath)
  371. print ("Remote: {}".format(labkeyPath))
  372. #checks if exists
  373. self.mkdir(labkeyPath)
  374. localPath=self.GetLocalPathFromRelativePath(relativePath)
  375. localPath.replace('/',os.path.sep)
  376. nodeName=node.GetName()
  377. suffix=".nrrd"
  378. if node.__class__.__name__=="vtkMRMLDoubleArrayNode":
  379. suffix=".mcsv"
  380. if node.__class__.__name__=="vtkMRMLTransformNode":
  381. suffix=".h5"
  382. fileName=nodeName+suffix
  383. file=os.path.join(localPath,fileName)
  384. slicer.util.saveNode(node,file)
  385. print("Stored to: {}".format(file))
  386. f=open(file,"rb")
  387. f.seek(0,2)
  388. sz=f.tell()
  389. print("File size in memory: {0}".format(sz))
  390. f.seek(0)
  391. localBuffer=f.read()
  392. print("Local buffer size: {}".format(len(localBuffer)))
  393. remoteFile=labkeyPath+'/'+fileName
  394. resp=self.put(remoteFile,localBuffer)
  395. print(resp.read())
  396. f.close()
  397. if removeFile:
  398. os.remove(file)
  399. def entryExists(self,url):
  400. #use head
  401. response=self.head(url)
  402. return labkeyURIHandler.HTTPStatus(response)
  403. def remoteDirExists(self,url):
  404. #weaker, just checks if there is an entry at url, does not check wheter it is a dir
  405. return self.entryExists(url)
  406. #stronger, but more complicated
  407. return self.isRemoteDir(url)
  408. def mkdir(self,remoteDir):
  409. if self.remoteDirExists(remoteDir):
  410. return False
  411. response=self.mkcol(remoteDir)
  412. return labkeyURIHandler.HTTPStatus(response)
  413. def mkdirs(self,remoteDir):
  414. relativePath=self.GetRelativePathFromLabkeyPath(remoteDir)
  415. s=0
  416. while True:
  417. s1=relativePath.find('/',s)
  418. if s1<0:
  419. break
  420. path=relativePath[0:s1]
  421. remotePath=self.GetLabkeyPathFromRelativePath(relativePath)
  422. dirExists=self.remoteDirExists(remotePath)
  423. if not dirExists:
  424. if not self.mkdir(remotePath):
  425. return False
  426. s=s1+1
  427. return self.mkdir(remoteDir)
  428. def rmdir(self,remoteDir):
  429. if not self.remoteDirExists(remoteDir):
  430. return True
  431. return labkeyURIHandler.HTTPStatus(self.delete(remoteDir))
  432. #was listDir
  433. def listRelativeDir(self,relativeDir):
  434. print("Listing for {0}".format(relativeDir))
  435. dirUrl=self.GetLabkeyPathFromRelativePath(relativeDir)
  436. print("Setting url: {}".format(dirUrl))
  437. status,dirs=self.listRemoteDir(dirUrl)
  438. dirs=[self.GetRelativePathFromLabkeyPath(d) for d in dirs];
  439. return dirs
  440. #was isDir
  441. def isRemoteDir(self, remotePath):
  442. #print "isDir: {}".format(remotePath)
  443. PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
  444. <a:propfind xmlns:a="DAV:">\n
  445. <a:prop>\n
  446. <a:resourcetype/>\n
  447. </a:prop>\n
  448. </a:propfind>"""
  449. response=self.propfind(remotePath,PROPFIND)
  450. if not labkeyURIHandler.HTTPStatus(response,method='propfind'):
  451. print("Bad status")
  452. return False
  453. try:
  454. tree=ET.XML(response.data)
  455. except ET.ParseError:
  456. #print(f.data.decode('utf-8'))
  457. #if directory is not there, a 404 response will be made by HTTP, which is not an xml file
  458. #we are safe to assume the directory is not there
  459. return False
  460. try:
  461. rps=tree.find('{DAV:}response').find('{DAV:}propstat')
  462. rps=rps.find('{DAV:}prop')
  463. rps=rps.find('{DAV:}resourcetype').find('{DAV:}collection')
  464. if rps != None:
  465. return True
  466. else:
  467. return False
  468. except:
  469. return False
  470. def listRemoteDir(self,dirUrl):
  471. #input is remoteDir, result are remoteDirs
  472. PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
  473. <propfind xmlns="DAV:">\n
  474. <prop>\n
  475. <getetag/>\n
  476. </prop>\n
  477. </propfind>"""
  478. response=self.propfind(dirUrl,PROPFIND)
  479. if not labkeyURIHandler.HTTPStatus(response,method='propfind'):
  480. print("Bad status")
  481. return False,[]
  482. try:
  483. tree=ET.XML(response.data)
  484. except ET.ParseError:
  485. print("Fail to parse XML")
  486. return False,[]
  487. rps=tree.findall('{DAV:}response')
  488. dirs=[]
  489. for r in rps:
  490. hr=r.find('{DAV:}href')
  491. dirent=hr.text
  492. #dirent=re.sub('/labkey/_webdav/','',dirent)
  493. dirent=self.GetHostName()+dirent
  494. dirs.append(dirent)
  495. del dirs[0]
  496. return True,dirs
  497. def toRelativePath(self,dirs):
  498. flist1=[]
  499. for d in dirs:
  500. if d[-1]=='/':
  501. d=d[:-1]
  502. if d.rfind('/')>-1:
  503. d=d[d.rfind('/')+1:]
  504. flist1.append(d)
  505. return flist1
  506. def readFileToBuffer(self, relativePath):
  507. dirUrl=self.GetLabkeyPathFromRelativePath(relativePath)
  508. response=self.get(dirUrl,binary=True)
  509. if not labkeyURIHandler.HTTPStatus(response):
  510. return io.BytesIO()
  511. #this will collect for all data
  512. remoteBuffer=io.BytesIO(response.data)
  513. response.release_conn()
  514. return remoteBuffer
  515. def uploadFile(self,localPath):
  516. #get upstream directories sorted out
  517. relativePath=self.GetRelativePathFromLocalPath(localPath)
  518. if relativePath=="NULL":
  519. errorCode="Failed to upload {}. Potential incorrect location"
  520. errorCode+=". Should be in labkeyCache!"
  521. print(errorCode.format(relativePath))
  522. return False
  523. relativeDir=relativePath[0:labkeyPath.rfind('/')]
  524. remoteDir=self.GetLabkeyPathFromRemotePath(relativeDir)
  525. if not self.remoteDirExists(remoteDir):
  526. if not self.mkdirs(remoteDir):
  527. errorCode="UploadFile: Could not create directory {}"
  528. print(errorCode.format(remoteDir))
  529. return False
  530. #make an URL request
  531. with open(localPath, 'rb') as f:
  532. data=f.read()
  533. remotePath=self.GetLabkeyPathFromRelativePath(relativePath)
  534. self.put(remotePath,data)
  535. #was copyFileToRemote
  536. def copyLocalFileToRemote(self,localPath, remotePath):
  537. #get upstream directories sorted out
  538. remoteDir=os.path.dirname(remotePath)
  539. if not self.remoteDirExists(remoteDir):
  540. if not self.mkdirs(remoteDir):
  541. errorCode="UploadFile: Could not create directory {}"
  542. print(errorCode.format(remoteDir))
  543. return False
  544. #make an URL request
  545. if (self.isRemoteDir(remotePath)):
  546. remotePath=remotePath+'/'+os.path.basename(localPath)
  547. with open(localPath, 'rb') as f:
  548. data=f.read()
  549. self.put(remotePath,data)
  550. #was loadDir
  551. def DownloadDirToCache(self, relativePath):
  552. files=self.listRelativeDir(relativePath)
  553. fdir="NONE"
  554. for f in files:
  555. #f is local path
  556. try:
  557. localDir=os.path.dirname(self.DownloadFileToCache(f))
  558. except:
  559. #fails if there is a subdirectory; go recursively
  560. print("self.readDir(f) not implemented")
  561. return localDir
  562. #database routines
  563. @staticmethod
  564. def getJSON(response):
  565. encoding=chardet.detect(response.data)['encoding']
  566. return json.loads(response.data.decode(encoding))
  567. def loadDataset(self,project,dataset):
  568. url=self.GetLabkeyUrl()+'/'+project
  569. url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
  570. return labkeyURIHandler.getJSON(self.get(url))
  571. def filterDataset(self,project,dataset,filter):
  572. debug=False
  573. url=self.GetLabkeyUrl()+'/'+project
  574. url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
  575. for f in filter:
  576. url+="&query."+f['variable']+"~"+f['oper']+"="+f['value']
  577. if debug:
  578. print("Sending {}".format(url))
  579. return labkeyURIHandler.getJSON(self.get(url))
  580. def modifyDataset(self,method,project,dataset,rows):
  581. #method can be insert or update
  582. data={}
  583. data['schemaName']='study'
  584. data['queryName']=dataset
  585. data['rows']=rows
  586. url=self.GetLabkeyUrl()+'/'+project
  587. url+='/query-'+method+'Rows.api?'
  588. return self.post(url,json.dumps(data)).data
  589. def filterList(self,project,dataset,filter):
  590. schemaName='lists'
  591. debug=False
  592. url=self.GetLabkeyUrl()+'/'+project
  593. url+='/query-selectRows.api?schemaName='+schemaName+'&query.queryName='+dataset
  594. for f in filter:
  595. url+="&query."+f['variable']+"~"+f['oper']+"="+f['value']
  596. if debug:
  597. print("Sending {}".format(url))
  598. return labkeyURIHandler.getJSON(self.get(url))
  599. def modifyList(self,method,project,dataset,rows):
  600. #method can be insert or update
  601. data={}
  602. schemaName='lists'
  603. data['schemaName']=schemaName
  604. data['queryName']=dataset
  605. data['rows']=rows
  606. url=self.GetLabkeyUrl()+'/'+project
  607. url+='/query-'+method+'Rows.api?'
  608. return self.post(url,json.dumps(data)).data