slicerNetwork.py 23 KB

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