From dd2f8728d5525fd9d07fe1769dc50ceaac71d22e Mon Sep 17 00:00:00 2001 From: Swen Vermeul <swen@ethz.ch> Date: Tue, 24 May 2016 09:24:49 +0200 Subject: [PATCH] get_samples implemented, tests added --- src/python/PyBis/pybis/__init__.py | 3 +- src/python/PyBis/pybis/pybis.py | 139 ++++++++++++++++++++++--- src/python/PyBis/pybis/pybis_test.py | 22 ---- src/python/PyBis/pybis/scripts/cli.py | 46 +------- src/python/PyBis/setup.py | 3 +- src/python/pybis/tests/conftest.py | 12 +++ src/python/pybis/tests/test_imports.py | 4 + src/python/pybis/tests/test_openbis.py | 25 +++++ 8 files changed, 172 insertions(+), 82 deletions(-) delete mode 100644 src/python/PyBis/pybis/pybis_test.py create mode 100644 src/python/pybis/tests/conftest.py create mode 100644 src/python/pybis/tests/test_imports.py create mode 100644 src/python/pybis/tests/test_openbis.py diff --git a/src/python/PyBis/pybis/__init__.py b/src/python/PyBis/pybis/__init__.py index eea0a080777..52b99ce1741 100644 --- a/src/python/PyBis/pybis/__init__.py +++ b/src/python/PyBis/pybis/__init__.py @@ -1,4 +1,5 @@ __author__ = 'Chandrasekhar Ramakrishnan <chandrasekhar.ramakrishnan@id.ethz.ch>' __version__ = '0.1.0' -from . import pybis \ No newline at end of file +from . import pybis +from .pybis import Openbis diff --git a/src/python/PyBis/pybis/pybis.py b/src/python/PyBis/pybis/pybis.py index b3a970a580b..ae5dc5a7c15 100644 --- a/src/python/PyBis/pybis/pybis.py +++ b/src/python/PyBis/pybis/pybis.py @@ -11,6 +11,9 @@ Copyright (c) 2016 ETH Zuerich. All rights reserved. """ import os +import requests +import json +import re class OpenbisCredentials: @@ -79,23 +82,54 @@ class OpenbisCredentialStore: class Openbis: """Interface for communicating with openBIS.""" - def __init__(self, url, credentials): + def __init__(self, host=None): """Initialize an interface to openBIS with information necessary to connect to the server. - :param url: - :param credentials: + :param host: """ - self.url = url - self.credentials = credentials + self.host = host + self.token = None - def login(self): + def token(self): + if self.token is None: + raise ValueError('no valid session available') + + def logout(self): + + logout_request = { + "method":"logout", + "params":[self.token], + "id":"1", + "jsonrpc":"2.0" + } + resp = requests.post(self.host, json.dumps(logout_request)) + if resp.ok: + self.token = None + + + def login(self, username='openbis_test_js', password='password', store_credentials=False): """Log into openBIS. - Expects credentials with username and password and updates the token on the credentials object. + Expects a username and a password and updates the token (session-ID). + The token is then used for every request. Clients may want to store the credentials object in a credentials store after successful login. Throw a ValueError with the error message if login failed. """ - if not self.credentials.has_username_and_password: - raise ValueError('Cannot log into openBIS without a username and password') - # TODO Implement the logic of this method. + + login_request = { + "method":"login", + "params":[username, password], + "id":"1", + "jsonrpc":"2.0" + } + resp = requests.post(self.host, json.dumps(login_request)) + if resp.ok: + self.token = resp.json()['result'] + + else: + raise ValueError( + 'Cannot log into openBIS. Got status code ' + resp.status_code + + ' with reason: ' + resp.reason + ) + def is_token_valid(self): """Check if the connection to openBIS is valid. @@ -103,9 +137,18 @@ class Openbis: user to login again. :return: Return True if the token is valid, False if it is not valid. """ - if not self.credentials.has_token(): + + if not self.token: return False - # TODO Implement the logic of this method. + + def get_dataset(self, permid): + + search = { + "permId": permid, + "@type": "as.dto.dataset.id.DataSetPermId" + } + + def get_samples(self, sample_identifiers): """Retrieve metadata for the sample. @@ -113,7 +156,77 @@ class Openbis: to the same information visible in the ELN UI. The metadata will be on the file system. :param sample_identifiers: A list of sample identifiers to retrieve. """ - pass + + if not isinstance(sample_identifiers, list): + sample_identifiers = [sample_identifiers] + + + searches = [] + + for ident in sample_identifiers: + + # assume we got a sample identifier e.g. /TEST/TEST-SAMPLE + match = re.match('/', ident) + if match: + searches.append({ + "identifier": ident, + "@type": "as.dto.sample.id.SampleIdentifier" + }) + continue + + # look if we got a PermID eg. 234567654345-123 + match = re.match('\d+\-\d+', ident) + if match: + searches.append({ + "permId": ident, + "@type": "as.dto.sample.id.SamplePermId" + }) + continue + + raise ValueError( + '"' + ident + '" is neither a Sample Identifier nor a PermID' + ) + + sample_request = { + "method": "getSamples", + "params": [ + self.token, + searches, + { + "type": { + "@type": "as.dto.sample.fetchoptions.SampleTypeFetchOptions" + }, + "properties": { + "@type": "as.dto.property.fetchoptions.PropertyFetchOptions" + }, + "parents": { + "@id": 20, + "@type": "as.dto.sample.fetchoptions.SampleFetchOptions", + "properties": { + "@type": "as.dto.property.fetchoptions.PropertyFetchOptions" + } + }, + "dataSets": { + "@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions", + "properties": { + "@type": "as.dto.property.fetchoptions.PropertyFetchOptions" + } + } + } + ], + "id": "1", + "jsonrpc": "2.0" + } + + resp = requests.post(self.host, json.dumps(sample_request)) + if resp.ok: + data = resp.json() + if "error" in data: + raise ValueError("Request produced an error: " + data["message"]) + else: + return data['result'] + + # TODO Implement the logic of this method def get_samples_with_data(self, sample_identifiers): diff --git a/src/python/PyBis/pybis/pybis_test.py b/src/python/PyBis/pybis/pybis_test.py deleted file mode 100644 index 8f22386a658..00000000000 --- a/src/python/PyBis/pybis/pybis_test.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -pybis_test.py - -Tests for the pybis module. Written using pytest. - -Created by Chandrasekhar Ramakrishnan on 2016-05-10. -Copyright (c) 2016 ETH Zuerich. All rights reserved. -""" - -from .pybis import OpenbisCredentials, OpenbisCredentialStore - - -def test_credentials_store(tmpdir): - credentials = OpenbisCredentials("magic_token") - store = OpenbisCredentialStore(str(tmpdir)) - store.write(credentials) - disk_credentials = store.read() - assert credentials.token == disk_credentials.token - assert not disk_credentials.has_username_and_password() diff --git a/src/python/PyBis/pybis/scripts/cli.py b/src/python/PyBis/pybis/scripts/cli.py index 21e627ef723..5cdd73c2908 100644 --- a/src/python/PyBis/pybis/scripts/cli.py +++ b/src/python/PyBis/pybis/scripts/cli.py @@ -1,48 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -cli.py - -Command-line interface to interact with openBIS. - -Created by Chandrasekhar Ramakrishnan on 2016-05-09. -Copyright (c) 2016 ETH Zuerich. All rights reserved. -""" - -from datetime import datetime - -import click - - -def click_progress(progress_data): - timestamp = datetime.now().strftime("%H:%M:%S") - click.echo("{} {}".format(timestamp, progress_data['message'])) - - -def click_progress(progress_data): - timestamp = datetime.now().strftime("%H:%M:%S") - click.echo("{} {}".format(timestamp, progress_data['message'])) - - -@click.group() -@click.option('-q', '--quiet', default=False, is_flag=True, help='Suppress status reporting.') -@click.pass_context -def cli(ctx, quiet): - ctx.obj['quiet'] = quiet - - -@cli.command() -@click.pass_context -@click.option('-u', '--username', prompt=True) -@click.option('-p', '--password', prompt=True, hide_input=True, confirmation_prompt=False) -def login(ctx, username, password): - click.echo("login {}".format(username)) def main(): - cli(obj={}) - - -if __name__ == '__main__': - main() + print("called") diff --git a/src/python/PyBis/setup.py b/src/python/PyBis/setup.py index 57008ab0cef..827a006e855 100644 --- a/src/python/PyBis/setup.py +++ b/src/python/PyBis/setup.py @@ -18,4 +18,5 @@ setup(name='pybis', [console_scripts] pybis=pybis.scripts.cli:main ''', - zip_safe=False) + zip_safe=False +) diff --git a/src/python/pybis/tests/conftest.py b/src/python/pybis/tests/conftest.py new file mode 100644 index 00000000000..e5e7d3b35b3 --- /dev/null +++ b/src/python/pybis/tests/conftest.py @@ -0,0 +1,12 @@ +import pytest + +from pybis import Openbis + +@pytest.yield_fixture(scope="module") +def openbis_instance(): + instance = Openbis("http://localhost:20000/openbis/openbis/rmi-application-server-v3.json") + print("\nLOGGING IN...") + instance.login() + yield instance + instance.logout() + print("LOGGED OUT...") diff --git a/src/python/pybis/tests/test_imports.py b/src/python/pybis/tests/test_imports.py new file mode 100644 index 00000000000..174a61ea0ce --- /dev/null +++ b/src/python/pybis/tests/test_imports.py @@ -0,0 +1,4 @@ + + +def test_import(): + from pybis import Openbis diff --git a/src/python/pybis/tests/test_openbis.py b/src/python/pybis/tests/test_openbis.py new file mode 100644 index 00000000000..ce5bd9cb802 --- /dev/null +++ b/src/python/pybis/tests/test_openbis.py @@ -0,0 +1,25 @@ +import json + +def test_token(openbis_instance): + assert openbis_instance.token is not None + +def test_get_samples_by_id(openbis_instance): + response = openbis_instance.get_samples('/TEST/TEST-SAMPLE-2-CHILD-1') + assert response is not None + assert response['/TEST/TEST-SAMPLE-2-CHILD-1'] is not None + +def test_get_samples_by_permid(openbis_instance): + response = openbis_instance.get_samples('20130415091923485-402') + assert response is not None + assert response['20130415091923485-402'] is not None + +def test_get_parents(openbis_instance): + id = '/TEST/TEST-SAMPLE-2' + response = openbis_instance.get_samples(id) + assert response is not None + #print(json.dumps(response)) + assert 'parents' in response[id] + assert 'identifier' in response[id]['parents'][0] + assert response[id]['parents'][0]['identifier']['identifier'] == '/TEST/TEST-SAMPLE-2-PARENT' + + response.parents[0].id -- GitLab