Newer
Older
Adam Laskowski
committed
# Copyright ETH 2018 - 2023 Zürich, Scientific IT Services
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from tabulate import tabulate
Adam Laskowski
committed
Adam Laskowski
committed
from .utils import (
check_datatype
)
self.__dict__["_openbis"] = openbis_obj
self.__dict__["_property_names"] = {}
if type is None:
return
Adam Laskowski
committed
"propertyAssignments" in type.data
and type.data["propertyAssignments"] is not None
):
for prop in type.data["propertyAssignments"]:
property_name = prop["propertyType"]["code"].lower()
self._property_names[property_name] = prop["propertyType"]
self._property_names[property_name]["mandatory"] = prop["mandatory"]
self._property_names[property_name]["showInEditView"] = prop[
"showInEditView"
]
if prop["propertyType"]["dataType"] == "CONTROLLEDVOCABULARY":
pt = self._openbis.get_property_type(prop["propertyType"]["code"])
# get the vocabulary of a property type.
# In some cases, the «code» of an assigned property is not identical to the «vocabulary» attribute
voc = self._openbis.get_vocabulary(pt.vocabulary)
terms = voc.get_terms()
self._property_names[property_name]["terms"] = terms
for code in self._type.codes():
props[code] = getattr(self, code)
return props
props = {}
for code in self._type.codes():
props[code] = getattr(self, code)
return props
def all_nonempty(self):
props = {}
for code in self._type.codes():
value = getattr(self, code)
if value is not None:
props[code] = value
return props
def __call__(self, *args):
if len(args) == 0:
return self.all()
elif len(args) == 1:
return getattr(self, args[0])
elif len(args) == 2:
return setattr(self, args[0], args[1])
else:
raise ValueError("called properties with more than 2 arguments")
def get(self, *args):
if len(args) == 0:
return self.all()
elif len(args) == 1 and not isinstance(args[0], list):
return getattr(self, args[0])
if isinstance(args[0], list):
args = args[0]
return {arg: getattr(self, arg, None) for arg in args}
def set(self, *args):
if len(args) == 2:
setattr(self, args[0], args[1])
elif len(args) == 1 and isinstance(args[0], dict):
for key in args[0]:
setattr(self, key, args[0][key])
def __getitem__(self, key):
"""For properties that contain either a dot or a dash or any other non-valid method character,
a user can use a key-lookup instead, e.g. sample.props['my-weird.property-name']
"""
return getattr(self, key)
"""attribute syntax can be found out by
adding an underscore at the end of the property name
"""
if name == "_ipython_canary_method_should_not_exist_":
if name.endswith("_"):
name = name.rstrip("_")
if name in self._property_names:
property_type = self._property_names[name]
if property_type["dataType"] == "CONTROLLEDVOCABULARY":
return property_type["terms"]
# return self._get_terms(property_type['code'])
Swen Vermeul
committed
else:
syntax = {property_type["label"]: property_type["dataType"]}
Swen Vermeul
committed
if property_type["dataType"] == "TIMESTAMP":
syntax["syntax"] = "YYYY-MM-DD HH:MIN:SS"
Swen Vermeul
committed
return syntax
Swen Vermeul
committed
"""This special method allows a PropertyHolder object
to check the attributes that are assigned to that object
"""
f"No such property: «{name}». Allowed properties are: {', '.join(self._property_names.keys())}"
data_type = property_type["dataType"]
Adam Laskowski
committed
if "multiValue" in property_type and property_type["multiValue"] is not True and type(
value) == list and data_type.startswith('ARRAY_') is False:
Adam Laskowski
committed
raise ValueError(
f'Property type {property_type["code"]} is not a multi-value property!')
if data_type == "CONTROLLEDVOCABULARY":
terms = property_type["terms"]
if "multiValue" in property_type and property_type["multiValue"] is True:
if type(value) != list:
value = [value]
for single_value in value:
if single_value != '' and str(single_value).upper() not in terms.df["code"].values:
raise ValueError(
f"Value for attribute «{name}» must be one of these terms: {', '.join(terms.df['code'].values)}"
)
else:
value = str(value).upper()
if value != '' and value not in terms.df["code"].values:
raise ValueError(
f"Value for attribute «{name}» must be one of these terms: {', '.join(terms.df['code'].values)} VALUE:{value}"
Adam Laskowski
committed
elif data_type == "SAMPLE":
if "multiValue" in property_type and property_type["multiValue"] is True:
if type(value) != list:
value = [value]
Adam Laskowski
committed
elif data_type in ("INTEGER", "BOOLEAN", "VARCHAR", "REAL", "ARRAY_INTEGER", "ARRAY_REAL", "ARRAY_STRING"):
is_multi_value = property_type["multiValue"] is True if "multiValue" in property_type else False
if not check_datatype(data_type, value, is_multi_value):
if is_multi_value:
raise ValueError(f"Multi-value property '{property_type['code']}' must be of type {data_type} - Provided value:{value}")
else:
raise ValueError(f"Property '{property_type['code']}' must be of type {data_type} - Provided value:{value}")
def __setitem__(self, key, value):
"""For properties that contain either a dot or a dash or any other non-valid method character,
a user can use a key instead, e.g. sample.props['my-weird.property-name']
"""
return setattr(self, key, value)
def __dir__(self):
return self._property_names
def _repr_html_(self):
html = """
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>property</th>
<th>value</th>
<th>description</th>
<th>type</th>
<th>mandatory</th>
for prop_name, prop in self._property_names.items():
html += "<tr>"
html += "".join(
f"<td>{item}</td>"
for item in [
prop_name,
nvl(getattr(self, prop_name, ""), ""),
prop.get("description"),
prop.get("dataType"),
prop.get("mandatory"),
]
html += """
</tbody>
</table>
"""
return html
if val is None:
return string
return False
return str(val)
headers = ["property", "value", "mandatory"]
lines = []
for prop_name in self._property_names:
lines.append([prop_name, nvl(getattr(self, prop_name, ""))])
return tabulate(lines, headers=headers)