From e9155a2db486b7159fd496eb77a37bb4afbbf49d Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Thu, 25 Jun 2020 14:56:23 +0200 Subject: [PATCH] EHN: get_property_values --- src/caosdb/common/models.py | 125 ++++++++++++++++++++++++++++++++++-- unittests/test_container.py | 69 ++++++++++++++++++++ 2 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 unittests/test_container.py diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index 5ab31f48..184be277 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -368,7 +368,7 @@ class Entity(object): del copy_kwargs['importance'] del copy_kwargs['inheritance'] - new_property = Property(value=value, **copy_kwargs) + new_property = Property(**copy_kwargs) if abstract_property is not None: new_property._wrap(property) @@ -378,6 +378,8 @@ class Entity(object): if new_property.datatype is None and isinstance( property, (RecordType, Record, File)): new_property.datatype = property + new_property.value = value + self.properties.append( property=new_property, importance=( kwargs['importance'] if 'importance' in kwargs else None), inheritance=( @@ -586,6 +588,72 @@ class Entity(object): return None + def get_property_values(self, *properties): + """ Return a list of tuples of the values of the given properties. + + This represents an entity's properties as if it was a row of a table + with the given columns. + + If the elements of the properties parameter are tuples, they will return + the properties of the referenced entity, if present. + + All tuples of the returned list have the same length as the properties + parameter and the ordering of the tuple's values correspond to the + order of the parameter as well. + + The tuple contains None for all values that are not available in the + entity. That does not necessarily mean, that the values are not stored + in the database. + + Parameters + ---------- + properties : str or tuple of str + A list of property names, e.g. `"height", "width"` or a list of + tuples of properties, e.g. `("window", "height"), ("window", + "width")`. + + Raises + ------ + TypeError : + If the properties parameter contains anything else than str or + tuple of str elements. + + Returns + ------- + row : tuple + A row-like representation of the entities properties. + """ + selectors = properties + row = tuple() + for sel in selectors: + val = None + if isinstance(sel, str): + if hasattr(self, sel.lower()): + val = getattr(self, sel.lower()) + else: + prop = self.get_property(sel) + if prop is not None: + val = prop.value + + elif isinstance(sel, (tuple, list)) and len(sel) > 0: + ref = self + while len(sel) > 0 and isinstance(ref, Entity): + prop = ref.get_property(sel[0]) + if prop is not None: + ref = prop.value + else: + ref = None + sel = sel[1:] + val = ref if len(sel) == 0 else None + else: + raise TypeError("The elements of the parameter `properties` " + "must contain str or non-empty tuples of str." + " Was {}.".format(sel)) + if isinstance(val, Entity): + val = val.id if val.id is not None else val.name + row += (val,) + return row + def get_messages(self): """Get all messages of this entity. @@ -814,17 +882,26 @@ class Entity(object): entity.add_message(child) elif child is None or hasattr(child, "encode"): vals.append(child) + elif isinstance(child, Entity): + vals.append(child) else: raise TypeError( 'Child was neither a Property, nor a Parent, nor a Message.\ Was ' + str(type(child))) - # parse VALUE + # add VALUE + value = None if len(vals): # The value[s] have been inside a <Value> tag. - entity.value = vals + value = vals elif elem.text is not None and elem.text.strip() != "": - entity.value = elem.text.strip() + value = elem.text.strip() + + try: + entity.value = value + except ValueError: + # circumvent the parsing. + entity.__value = value return entity @@ -3094,6 +3171,46 @@ class Container(list): return self + def get_property_values(self, *properties): + """ Return a list of tuples of the values of the given properties. + + I.e. a tabular representation of the container's content. + + If the elements of the properties parameter are tuples, they will return + the properties of the referenced entity, if present. + + All tuples of the returned list have the same length as the properties + parameter and the ordering of the tuple's values correspond to the + order of the parameter as well. + + The tuple contains None for all values that are not available in the + entity. That does not necessarily mean, that the values are not stored + in the database. + + Parameters + ---------- + properties : list + A list of property names, e.g. `["height", "width"]` or a list of + tuples of properties, e.g. `[("window", "height"), ("window", + "width")]`. + + Raises + ------ + TypeError : + If the properties parameter contains anything else than str or + tuple of str elements. + + Returns + ------- + table : list of tuples + A tabular representation of the container's content. + """ + table = [] + for e in self: + table.append(e.get_property_values(*properties)) + return table + + def sync_global_acl(): c = get_connection() diff --git a/unittests/test_container.py b/unittests/test_container.py new file mode 100644 index 00000000..f2b73b0a --- /dev/null +++ b/unittests/test_container.py @@ -0,0 +1,69 @@ + +# -*- encoding: utf-8 -*- +# +# ** header v3.0 +# This file is a part of the CaosDB Project. +# +# Copyright (C) 2018 Research Group Biomedical Physics, +# Max-Planck-Institute for Dynamics and Self-Organization Göttingen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# ** end header +# +"""Tests for the Container class.""" +from __future__ import absolute_import +import caosdb as c + + +def test_get_property_values(): + rt_house = c.RecordType("House") + rt_window = c.RecordType("Window") + rt_owner = c.RecordType("Owner") + p_height = c.Property("Height", datatype=c.DOUBLE) + window = c.Record().add_parent(rt_window) + window.id = 1001 + window.add_property(p_height, 20.5) + + owner = c.Record("The Queen").add_parent(rt_owner) + + house = c.Record("Buckingham Palace") + house.add_parent(rt_house) + house.add_property(rt_owner, owner) + house.add_property(rt_window, window) + house.add_property(p_height, 40.2) + + container = c.Container() + container.extend([ + house, + owner + ]) + + table = container.get_property_values("naME", + "height", + "window", + ("window", "heiGHT"), + "owner") + assert len(table) == 2 + house_row = table[0] + assert house_row == (house.name, 40.2, window.id, 20.5, owner.name) + + owner_row = table[1] + assert owner_row == (owner.name, None, None, None, None) + + assert container.get_property_values("sdfg") == [(None,), (None,)] + assert container.get_property_values("name") == [(house.name,), + (owner.name,)] + + -- GitLab