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