From 2445e2dcc25306cd0643e5367a48d34be1737e32 Mon Sep 17 00:00:00 2001 From: alaskowski <alaskowski@ethz.ch> Date: Fri, 19 May 2023 11:41:10 +0200 Subject: [PATCH] SSDM-13524: Added handling of array-type properties to pybis. Minor code refactor. --- .../src/python/pybis/definitions.py | 5 ++++ .../src/python/pybis/entity_type.py | 29 +++++++------------ .../src/python/pybis/experiment.py | 9 +++--- .../src/python/pybis/openbis_object.py | 3 ++ .../src/python/pybis/property.py | 9 ++---- .../src/python/pybis/property_reformatter.py | 21 ++++++++++++++ .../src/python/pybis/pybis.py | 4 +-- .../src/python/pybis/sample.py | 5 +++- .../src/python/pybis/utils.py | 5 ++-- 9 files changed, 55 insertions(+), 35 deletions(-) diff --git a/api-openbis-python3-pybis/src/python/pybis/definitions.py b/api-openbis-python3-pybis/src/python/pybis/definitions.py index 25c31e6656e..035130bb4d9 100644 --- a/api-openbis-python3-pybis/src/python/pybis/definitions.py +++ b/api-openbis-python3-pybis/src/python/pybis/definitions.py @@ -167,6 +167,11 @@ def openbis_definitions(entity): "MATERIAL", "HYPERLINK", "XML", + "ARRAY_INTEGER", + "ARRAY_REAL", + "ARRAY_STRING", + "ARRAY_TIMESTAMP", + "JSON" ], "identifier": "typeId", }, diff --git a/api-openbis-python3-pybis/src/python/pybis/entity_type.py b/api-openbis-python3-pybis/src/python/pybis/entity_type.py index 139d3a9d0cf..a17be6eb6b3 100644 --- a/api-openbis-python3-pybis/src/python/pybis/entity_type.py +++ b/api-openbis-python3-pybis/src/python/pybis/entity_type.py @@ -12,29 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from tabulate import tabulate -from texttable import Texttable from pandas import DataFrame + +from .definitions import ( + get_method_for_entity, + get_type_for_entity, + get_definition_for_entity, +) from .openbis_object import OpenBisObject +from .semantic_annotation import SemanticAnnotation from .things import Things from .utils import ( - check_datatype, - split_identifier, format_timestamp, - is_identifier, - is_permid, - nvl, - extract_permid, extract_code, extract_name, VERBOSE, ) -from .definitions import ( - get_method_for_entity, - get_type_for_entity, - get_definition_for_entity, -) -from .semantic_annotation import SemanticAnnotation class EntityType: @@ -193,7 +186,7 @@ class EntityType: # assign plugin if plugin is not None: plugin_obj = self.openbis.get_plugin(plugin) - new_assignment["plugin"] = plugin_obj.name + new_assignment["plugin"] = plugin_obj.permId request = self._get_request_for_pa(new_assignment, "Add") try: @@ -229,11 +222,11 @@ class EntityType: } request = self._get_request_for_pa(items, "Remove", force) resp = self.openbis._post_request(self.openbis.as_v3, request) - if not resp and VERBOSE: + if not resp: new_data = self._get_method(self.permId, only_data=True) self._set_entity_data(new_data) - - print(f"Property {property_type} revoked from {self.permId}") + if VERBOSE: + print(f"Property {property_type} revoked from {self.permId}") def _get_request_for_pa(self, items, item_action, force=False): diff --git a/api-openbis-python3-pybis/src/python/pybis/experiment.py b/api-openbis-python3-pybis/src/python/pybis/experiment.py index f078aef9b9b..14eba9723d4 100644 --- a/api-openbis-python3-pybis/src/python/pybis/experiment.py +++ b/api-openbis-python3-pybis/src/python/pybis/experiment.py @@ -12,11 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from .property import PropertyHolder -from .attribute import AttrHolder from .openbis_object import OpenBisObject -from .definitions import openbis_definitions -from .utils import VERBOSE class Experiment( @@ -30,8 +26,11 @@ class Experiment( self.a(data) self.__dict__["data"] = data - # put the properties in the self.p namespace (without checking them) + # put the properties in the self.p namespace for key, value in data["properties"].items(): + data_type = self.p._property_names[key.lower()]['dataType'] + if data_type in ("ARRAY_INTEGER", "ARRAY_REAL", "ARRAY_STRING", "ARRAY_TIMESTAMP"): + value = self.formatter.to_array(data_type, value) self.p.__dict__[key.lower()] = value def __str__(self): diff --git a/api-openbis-python3-pybis/src/python/pybis/openbis_object.py b/api-openbis-python3-pybis/src/python/pybis/openbis_object.py index 5afb398015b..d351da74857 100644 --- a/api-openbis-python3-pybis/src/python/pybis/openbis_object.py +++ b/api-openbis-python3-pybis/src/python/pybis/openbis_object.py @@ -71,6 +71,9 @@ class OpenBisObject: # put the properties in the self.p namespace (without checking them) if "properties" in data: for key, value in data["properties"].items(): + data_type = self.p._property_names[key.lower()]['dataType'] + if data_type in ("ARRAY_INTEGER", "ARRAY_REAL", "ARRAY_STRING", "ARRAY_TIMESTAMP"): + value = self.formatter.to_array(data_type, value) self.p.__dict__[key.lower()] = value # object is already saved to openBIS, so it is not new anymore diff --git a/api-openbis-python3-pybis/src/python/pybis/property.py b/api-openbis-python3-pybis/src/python/pybis/property.py index c792454a77b..54b8577de72 100644 --- a/api-openbis-python3-pybis/src/python/pybis/property.py +++ b/api-openbis-python3-pybis/src/python/pybis/property.py @@ -13,14 +13,9 @@ # limitations under the License. # from tabulate import tabulate -from texttable import Texttable + from pybis.utils import ( check_datatype, - split_identifier, - format_timestamp, - is_identifier, - is_permid, - nvl, ) @@ -147,7 +142,7 @@ class PropertyHolder: raise ValueError( f"Value for attribute «{name}» must be one of these terms: {', '.join(terms.df['code'].values)}" ) - elif data_type in ("INTEGER", "BOOLEAN", "VARCHAR"): + elif data_type in ("INTEGER", "BOOLEAN", "VARCHAR", "ARRAY_INTEGER", "ARRAY_REAL", "ARRAY_STRING", "ARRAY_TIMESTAMP"): if not check_datatype(data_type, value): raise ValueError(f"Value must be of type {data_type}") self.__dict__[name] = value diff --git a/api-openbis-python3-pybis/src/python/pybis/property_reformatter.py b/api-openbis-python3-pybis/src/python/pybis/property_reformatter.py index 59739cd644f..5257595ca8d 100644 --- a/api-openbis-python3-pybis/src/python/pybis/property_reformatter.py +++ b/api-openbis-python3-pybis/src/python/pybis/property_reformatter.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import re from datetime import datetime import pandas as pd @@ -44,9 +45,15 @@ class PropertyReformatter: raise ValueError('properties can not be None!') for key, value in properties.items(): + if value is None: + continue property_type = self.openbis.get_property_type(key) if property_type.dataType == 'TIMESTAMP': properties[key] = self._format_timestamp(value) + elif property_type.dataType == 'ARRAY_TIMESTAMP': + properties[key] = ",".join([self._format_timestamp(x) for x in value]) + elif property_type.dataType.startswith('ARRAY'): + properties[key] = ",".join(map(str, value)) return properties @@ -60,3 +67,17 @@ class PropertyReformatter: print( f'WARNING: "{value}" is not of any OpenBis supported datetime formats. Reformatting to "{result}"') return result + + def to_array(self, data_type, prop_value): + if prop_value is None or prop_value == "": + return [] + result = [] + if data_type == "ARRAY_INTEGER": + result = [int(x.strip()) for x in prop_value.split(',')] + elif data_type == "ARRAY_REAL": + result = [float(x.strip()) for x in prop_value.split(',')] + elif data_type == "ARRAY_STRING": + result = [x.strip() for x in re.split(r"(?<!\\),", prop_value)] + elif data_type == "ARRAY_TIMESTAMP": + result = [x.strip() for x in prop_value.split(',')] + return result diff --git a/api-openbis-python3-pybis/src/python/pybis/pybis.py b/api-openbis-python3-pybis/src/python/pybis/pybis.py index a48ba586b9a..3cb5587b4de 100644 --- a/api-openbis-python3-pybis/src/python/pybis/pybis.py +++ b/api-openbis-python3-pybis/src/python/pybis/pybis.py @@ -1292,7 +1292,6 @@ class Openbis: if resp.ok: resp = resp.json() if "error" in resp: - # print(full_url) print(json.dumps(request)) raise ValueError(resp["error"]["message"]) elif "result" in resp: @@ -1300,7 +1299,8 @@ class Openbis: else: raise ValueError("request did not return either result nor error") else: - raise ValueError("general error while performing post request") + raise ValueError( + f"general error while performing post request. {resp.status_code}:{resp.reason}") def logout(self): """Log out of openBIS. After logout, the session token is no longer valid.""" diff --git a/api-openbis-python3-pybis/src/python/pybis/sample.py b/api-openbis-python3-pybis/src/python/pybis/sample.py index 5514a7f6821..be92088af1c 100644 --- a/api-openbis-python3-pybis/src/python/pybis/sample.py +++ b/api-openbis-python3-pybis/src/python/pybis/sample.py @@ -90,8 +90,11 @@ class Sample(OpenBisObject, entity="sample", single_item_method_name="get_sample self.a(data) self.__dict__["data"] = data - # put the properties in the self.p namespace (without checking them) + # put the properties in the self.p namespace for key, value in data["properties"].items(): + data_type = self.p._property_names[key.lower()]['dataType'] + if data_type in ("ARRAY_INTEGER", "ARRAY_REAL", "ARRAY_STRING", "ARRAY_TIMESTAMP"): + value = self.formatter.to_array(data_type, value) self.p.__dict__[key.lower()] = value def __dir__(self): diff --git a/api-openbis-python3-pybis/src/python/pybis/utils.py b/api-openbis-python3-pybis/src/python/pybis/utils.py index 9dff312fda7..11bfb5d8742 100644 --- a/api-openbis-python3-pybis/src/python/pybis/utils.py +++ b/api-openbis-python3-pybis/src/python/pybis/utils.py @@ -12,9 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from datetime import datetime -from pathlib import Path import re +from datetime import datetime # display messages when in a interactive context (IPython or Jupyter) try: @@ -128,6 +127,8 @@ def check_datatype(type_name, value): return isinstance(value, bool) if type_name == "VARCHAR": return isinstance(value, str) + if type_name is not None and type_name.startswith("ARRAY"): + return isinstance(value, list) return True -- GitLab