slicerNetwork.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  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. return e
  212. def post(self,url,data):
  213. debug=False
  214. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  215. headers["Content-Type"]="application/json"
  216. #add csrf;also sets self.cookie
  217. headers["X-LABKEY-CSRF"]=self.getCSRF()
  218. headers["Cookie"]=self.cookie
  219. try:
  220. return self.http.request('POST',url,headers=headers,body=data)
  221. #f contains json as a return value
  222. except urllib3.exceptions.HTTPError as e:
  223. print(e)
  224. return e
  225. def put(self,url,data):
  226. debug=False
  227. if debug:
  228. print("PUT: {}".format(url))
  229. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  230. headers["Content-Type"]="application/octet-stream"
  231. #add csrf
  232. headers["X-LABKEY-CSRF"]=self.getCSRF()
  233. headers["Cookie"]=self.cookie
  234. try:
  235. return self.http.request('PUT',url,headers=headers,body=data)
  236. #f contains json as a return value
  237. except urllib3.exceptions.HTTPError as e:
  238. print(e)
  239. return e
  240. def sendRequest(self,url,requestCode):
  241. debug=False
  242. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  243. #add csrf;also sets self.cookie
  244. headers["X-LABKEY-CSRF"]=self.getCSRF()
  245. headers["Cookie"]=self.cookie
  246. try:
  247. return self.http.request(requestCode,url,headers=headers)
  248. #f contains json as a return value
  249. except urllib3.exceptions.HTTPError as e:
  250. print(e)
  251. return e
  252. def mkcol(self,url):
  253. return self.sendRequest(url,'MKCOL')
  254. def delete(self,url):
  255. return self.sendRequest(url,'DELETE')
  256. def propfind(self,url,PROPFIND):
  257. headers=urllib3.util.make_headers(basic_auth=self.getBasicAuth())
  258. headers["Content-Type"]='text/xml; charset="utf-8"'
  259. headers['content-length']=str(len(PROPFIND))
  260. headers['Depth']='1'
  261. #add csrf
  262. headers["X-LABKEY-CSRF"]=self.getCSRF()
  263. headers["Cookie"]=self.cookie
  264. try:
  265. return self.http.request('PROPFIND',url,headers=headers,body=PROPFIND)
  266. #f contains json as a return value
  267. except urllib3.exceptions.HTTPError as e:
  268. print(e)
  269. return e
  270. @staticmethod
  271. def HTTPStatus(response,method=None):
  272. if response.status==200:
  273. return True
  274. if method=='propfind':
  275. if response.status==207:
  276. return True
  277. print("Status: {}".format(response.status))
  278. print("Data: {}".format(response.data))
  279. return False
  280. #a good testing routine; CSRF is required for content modifying requests
  281. def getCSRF(self):
  282. url=self.GetLabkeyUrl()+'/login/whoAmI.view'
  283. try:
  284. response=self.get(url)
  285. if not labkeyURIHandler.HTTPStatus(response):
  286. return None
  287. encoding=chardet.detect(response.data)['encoding']
  288. jsonData=json.loads(response.data.decode(encoding))
  289. except AttributeError:
  290. print("Response: {}".response.data)
  291. print("Failed")
  292. return None
  293. #local cookie jar
  294. self.cookie=response.getheader('Set-Cookie')
  295. print("CSRF: {}".format(jsonData["CSRF"]))
  296. return jsonData["CSRF"]
  297. #file manipulation routiens
  298. #was GetFile
  299. def DownloadFileToCache(self,relativePath):
  300. debug=True
  301. # check for file in cache. If available, use, if not, download
  302. localPath=self.GetLocalPathFromRelativePath(relativePath)
  303. if os.path.isfile(localPath):
  304. if debug:
  305. print("Returning localFile {}".format(localPath))
  306. return localPath
  307. if debug:
  308. print("labkeyURIHandler::DownloadFileToCache({0}->{1})".format(relativePath,localPath))
  309. #make all necessary directories LOCALLY
  310. path=os.path.dirname(localPath)
  311. if debug:
  312. print("Target directory: '{}''".format(path))
  313. if not os.path.isdir(path):
  314. os.makedirs(path)
  315. #make sure we are at the begining of the file
  316. #labkeyPath=self.GetLabkeyPathFromRelativePath(relativePath)
  317. remoteBuffer=self.readFileToBuffer(relativePath)
  318. #check file size
  319. if debug:
  320. print("Remote size: {}".format(remoteBuffer.seek(0,2)))
  321. remoteBuffer.seek(0)
  322. with open(localPath,'wb') as localBuffer:
  323. shutil.copyfileobj(remoteBuffer,localBuffer)
  324. print("Local size: {}".format(os.path.getsize(localPath)))
  325. return localPath
  326. def fileTypesAvailable(self):
  327. return ('VolumeFile','SegmentationFile','TransformFile')
  328. #mimic slicer.util.loadNodeFromFile
  329. def loadNode(self, relativeName, filetype, properties={},returnNode=False, keepCached=True):
  330. #this is the only relevant part - file must be downloaded to cache
  331. #labkeyName is just the relative part (from labkey onwards)
  332. localPath=self.DownloadFileToCache(relativeName)
  333. if not returnNode:
  334. slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode=False)
  335. if not keepCached:
  336. os.remove(localPath)
  337. return
  338. ok,node=slicer.util.loadNodeFromFile(localPath,filetype,properties,returnNode=True)
  339. if ok:
  340. if not keepCached:
  341. os.remove(localPath)
  342. return node
  343. os.remove(localPath)
  344. return None
  345. # #remove retrieved file
  346. def storeNode(self,node,project,dir=None):
  347. removeFile=True
  348. relativePath=project+'/%40files'
  349. if not dir==None:
  350. relativePath+='/'+dir
  351. labkeyPath=self.GetLabkeyPathFromRelativePath(relativePath)
  352. print ("Remote: {}".format(labkeyPath))
  353. #checks if exists
  354. self.mkdir(labkeyPath)
  355. localPath=self.GetLocalPathFromRelativePath(relativePath)
  356. localPath.replace('/',os.path.sep)
  357. nodeName=node.GetName()
  358. suffix=".nrrd"
  359. if node.__class__.__name__=="vtkMRMLDoubleArrayNode":
  360. suffix=".mcsv"
  361. if node.__class__.__name__=="vtkMRMLTransformNode":
  362. suffix=".h5"
  363. fileName=nodeName+suffix
  364. file=os.path.join(localPath,fileName)
  365. slicer.util.saveNode(node,file)
  366. print("Stored to: {}".format(file))
  367. f=open(file,"rb")
  368. f.seek(0,2)
  369. sz=f.tell()
  370. print("File size in memory: {0}".format(sz))
  371. f.seek(0)
  372. localBuffer=f.read()
  373. print("Local buffer size: {}".format(len(localBuffer)))
  374. remoteFile=labkeyPath+'/'+fileName
  375. resp=self.put(remoteFile,localBuffer)
  376. print(resp.read())
  377. f.close()
  378. if removeFile:
  379. os.remove(file)
  380. def entryExists(self,url):
  381. #use head
  382. response=self.head(url)
  383. return labkeyURIHandler.HTTPStatus(response)
  384. def remoteDirExists(self,url):
  385. #weaker, just checks if there is an entry at url, does not check wheter it is a dir
  386. return self.entryEists(url)
  387. #stronger, but more complicated
  388. return self.isRemoteDir(url)
  389. def mkdir(self,remoteDir):
  390. if self.remoteDirExists(remoteDir):
  391. return False
  392. response=self.mkcol(remoteDir)
  393. return labkeyURIHandler.HTTPStatus(response)
  394. def mkdirs(self,remoteDir):
  395. relativePath=self.GetRelativePathFromLabkeyPath(remoteDir)
  396. s=0
  397. while True:
  398. s1=relativePath.find('/',s)
  399. if s1<0:
  400. break
  401. path=relativePath[0:s1]
  402. remotePath=self.GetLabkeyPathFromRelativePath(relativePath)
  403. dirExists=self.remoteDirExists(remotePath)
  404. if not dirExists:
  405. if not self.mkdir(remotePath):
  406. return False
  407. s=s1+1
  408. return self.mkdir(remoteDir)
  409. def rmdir(self,remoteDir):
  410. if not self.remoteDirExists(remoteDir):
  411. return True
  412. return labkeyURIHandler.HTTPStatus(self.delete(remoteDir))
  413. #was listDir
  414. def listRelativeDir(self,relativeDir):
  415. print("Listing for {0}".format(relativeDir))
  416. dirUrl=self.GetLabkeyPathFromRelativePath(relativeDir)
  417. print("Setting url: {}".format(dirUrl))
  418. status,dirs=self.listRemoteDir(dirUrl)
  419. dirs=[self.GetRelativePathFromLabkeyPath(d) for d in dirs];
  420. return dirs
  421. #was isDir
  422. def isRemoteDir(self, remotePath):
  423. #print "isDir: {}".format(remotePath)
  424. PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
  425. <a:propfind xmlns:a="DAV:">\n
  426. <a:prop>\n
  427. <a:resourcetype/>\n
  428. </a:prop>\n
  429. </a:propfind>"""
  430. if not labkeyURIHandler.HTTPStatus(self.propfind(remotePath,PROPFIND),method='propfind'):
  431. print("Bad status")
  432. return False
  433. try:
  434. tree=ET.XML(f.data)
  435. except ET.ParseError:
  436. #print(f.data.decode('utf-8'))
  437. #if directory is not there, a 404 response will be made by HTTP, which is not an xml file
  438. #we are safe to assume the directory is not there
  439. return False
  440. try:
  441. rps=tree.find('{DAV:}response').find('{DAV:}propstat')
  442. rps=rps.find('{DAV:}prop')
  443. rps=rps.find('{DAV:}resourcetype').find('{DAV:}collection')
  444. if rps != None:
  445. return True
  446. else:
  447. return False
  448. except:
  449. return False
  450. def listRemoteDir(self,dirUrl):
  451. #input is remoteDir, result are remoteDirs
  452. PROPFIND=u"""<?xml version="1.0" encoding="utf-8"?>\n
  453. <propfind xmlns="DAV:">\n
  454. <prop>\n
  455. <getetag/>\n
  456. </prop>\n
  457. </propfind>"""
  458. response=self.propfind(dirUrl,PROPFIND)
  459. if not labkeyURIHandler.HTTPStatus(response,method='propfind'):
  460. print("Bad status")
  461. return False,[]
  462. try:
  463. tree=ET.XML(response.data)
  464. except ET.ParseError:
  465. print("Fail to parse XML")
  466. return False,[]
  467. rps=tree.findall('{DAV:}response')
  468. dirs=[]
  469. for r in rps:
  470. hr=r.find('{DAV:}href')
  471. dirent=hr.text
  472. #dirent=re.sub('/labkey/_webdav/','',dirent)
  473. dirent=self.GetHostName()+dirent
  474. dirs.append(dirent)
  475. del dirs[0]
  476. return True,dirs
  477. def toRelativePath(self,dirs):
  478. flist1=[]
  479. for d in dirs:
  480. if d[-1]=='/':
  481. d=d[:-1]
  482. if d.rfind('/')>-1:
  483. d=d[d.rfind('/')+1:]
  484. flist1.append(d)
  485. return flist1
  486. def readFileToBuffer(self, relativePath):
  487. dirUrl=self.GetLabkeyPathFromRelativePath(relativePath)
  488. response=self.get(dirUrl,binary=True)
  489. if not labkeyURIHandler.HTTPStatus(response):
  490. return io.BytesIO()
  491. #this will collect for all data
  492. remoteBuffer=io.BytesIO(response.data)
  493. response.release_conn()
  494. return remoteBuffer
  495. def uploadFile(self,localPath):
  496. #get upstream directories sorted out
  497. relativePath=self.GetRelativePathFromLocalPath(localPath)
  498. if relativePath=="NULL":
  499. errorCode="Failed to upload {}. Potential incorrect location"
  500. errorCode+=". Should be in labkeyCache!"
  501. print(errorCode.format(relativePath))
  502. return False
  503. relativeDir=relativePath[0:labkeyPath.rfind('/')]
  504. remoteDir=self.GetLabkeyPathFromRemotePath(relativeDir)
  505. if not self.remoteDirExists(remoteDir):
  506. if not self.mkdirs(remoteDir):
  507. errorCode="UploadFile: Could not create directory {}"
  508. print(errorCode.format(remoteDir))
  509. return False
  510. #make an URL request
  511. with open(localPath, 'rb') as f:
  512. data=f.read()
  513. remotePath=self.GetLabkeyPathFromRelativePath(relativePath)
  514. self.put(remotePath,data)
  515. #was copyFileToRemote
  516. def copyLocalFileToRemote(self,localPath, remotePath):
  517. #get upstream directories sorted out
  518. remoteDir=os.path.dirname(remotePath)
  519. if not self.remoteDirExists(remoteDir):
  520. if not self.mkdirs(remoteDir):
  521. errorCode="UploadFile: Could not create directory {}"
  522. print(errorCode.format(remoteDir))
  523. return False
  524. #make an URL request
  525. if (self.isRemoteDir(remotePath)):
  526. remotePath=remotePath+'/'+os.path.basename(localPath)
  527. with open(localPath, 'rb') as f:
  528. data=f.read()
  529. self.put(remotePath,data)
  530. #was loadDir
  531. def DownloadDirToCache(self, relativePath):
  532. files=self.listRelativeDir(relativePath)
  533. fdir="NONE"
  534. for f in files:
  535. #f is local path
  536. try:
  537. localDir=os.path.dirname(self.DownloadFileToCache(f))
  538. except:
  539. #fails if there is a subdirectory; go recursively
  540. print("self.readDir(f) not implemented")
  541. return localDir
  542. #database routines
  543. @staticmethod
  544. def getJSON(response):
  545. encoding=chardet.detect(response.data)['encoding']
  546. return json.loads(response.data.decode(encoding))
  547. def loadDataset(self,project,dataset):
  548. url=self.GetLabkeyUrl()+'/'+project
  549. url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
  550. return labkeyURIHandler.getJSON(self.get(url))
  551. def filterDataset(self,project,dataset,filter):
  552. debug=False
  553. url=self.GetLabkeyUrl()+'/'+project
  554. url+='/query-selectRows.api?schemaName=study&query.queryName='+dataset
  555. for f in filter:
  556. url+="&query."+f['variable']+"~"+f['oper']+"="+f['value']
  557. if debug:
  558. print("Sending {}".format(url))
  559. return labkeyURIHandler.getJSON(self.get(url))
  560. def modifyDataset(self,method,project,dataset,rows):
  561. #method can be insert or update
  562. data={}
  563. data['schemaName']='study'
  564. data['queryName']=dataset
  565. data['rows']=rows
  566. url=self.GetLabkeyUrl()+'/'+project
  567. url+='/query-'+method+'Rows.api?'
  568. return self.post(url,json.dumps(data)).data
  569. def filterList(self,project,dataset,filter):
  570. schemaName='lists'
  571. debug=False
  572. url=self.GetLabkeyUrl()+'/'+project
  573. url+='/query-selectRows.api?schemaName='+schemaName+'&query.queryName='+dataset
  574. for f in filter:
  575. url+="&query."+f['variable']+"~"+f['oper']+"="+f['value']
  576. if debug:
  577. print("Sending {}".format(url))
  578. return labkeyURIHandler.getJSON(self.get(url))
  579. def modifyList(self,method,project,dataset,rows):
  580. #method can be insert or update
  581. data={}
  582. schemaName='lists'
  583. data['schemaName']=schemaName
  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