diff --git a/src/caosdb/high_level_api.py b/src/caosdb/high_level_api.py index 8facccff02e825ed7e186cab09eb89ce799e31a3..057640a999569e4a816b04b715872141daecd6c2 100644 --- a/src/caosdb/high_level_api.py +++ b/src/caosdb/high_level_api.py @@ -77,6 +77,7 @@ class CaosDBPythonUnresolvedReference: def __init__(self, id=None): self.id = id + class CaosDBPythonEntity(object): _last_id = 0 @@ -392,12 +393,16 @@ class CaosDBPythonEntity(object): using a real record type. Strings as argument for parent will automatically be converted to an - unresolved parent. + unresolved parent. Likewise, integers as argument will be automatically converted + to unresolved parents with just an id. """ if isinstance(parent, str): parent = CaosDBPythonUnresolvedParent(name=parent) + if isinstance(parent, int): + parent = CaosDBPythonUnresolvedParent(id=parent) + if self.has_parent(parent): raise RuntimeError("Duplicate parent.") self._parents.append(parent) @@ -406,15 +411,27 @@ class CaosDBPythonEntity(object): CaosDBPythonUnresolvedParent, "CaosDBPythonRecordType"]): """ Check whether this parent already exists for this entity. + + Strings as argument for parent will automatically be converted to an + unresolved parent. Likewise, integers as argument will be automatically converted + to unresolved parents with just an id. """ + + if isinstance(parent, str): + parent = CaosDBPythonUnresolvedParent(name=parent) + + if isinstance(parent, int): + parent = CaosDBPythonUnresolvedParent(id=parent) + for p in self._parents: - if p.id == parent.id: + if p.id is not None and p.id == parent.id: return True - elif p.name == parent.name: + elif p.name is not None and p.name == parent.name: return True return False def resolve_references(self, deep=False, visited=dict()): + raise NotImplementedError() for i in self._references: if isinstance(self._references[i], list): for j in range(len(self._references[i])): @@ -453,11 +470,17 @@ class CaosDBPythonEntity(object): return [p for p in self.__dict__ if p not in self._forbidden] - + + def deserialize(self, serialization: dict): + raise NotImplementedError() def serialize(self, without_metadata: bool = False): """ Serialize necessary information into a dict. + + without_metadata: bool + If True don't set the metadata field in order to increase + readability. Not recommended if deserialization is needed. """ metadata: dict[str, Any] = dict() properties = dict() @@ -476,7 +499,9 @@ class CaosDBPythonEntity(object): m = self.get_property_metadata(p) metadata[p] = dict() for f in fields(m): - metadata[p][f.name] = m.__getattribute__(f.name) + val = m.__getattribute__(f.name) + if val is not None: + metadata[p][f.name] = val val = self.get_property(p) if isinstance(val, CaosDBPythonUnresolvedReference): @@ -513,18 +538,39 @@ class CaosDBPythonProperty(CaosDBPythonEntity): pass +class CaosDBMultiProperty: + """ + This implements a multi property using a python list. + """ + + def __init__(self): + raise NotImplementedError() + + class CaosDBPythonFile(CaosDBPythonEntity): def get_File(self, target=None): f = db.File(id=self._id).retrieve() self._file = f.download(target) +BASE_ATTRIBUTES = ( + "id", "name", "description", "version", "path", "file") + + def _single_convert_to_python_object(robj: CaosDBPythonEntity, entity: db.Entity): - robj.id = entity.id - robj.name = entity.name - robj.description = entity.description - robj.version = entity.version + """ + Convert a db.Entity from the standard API to a (previously created) + CaosDBPythonEntity from the high level API. + + This method will not resolve any unresolved references, so reference properties + as well as parents will become unresolved references in the first place. + + Returns the input object robj. + """ + for base_attribute in BASE_ATTRIBUTES: + robj.__setattr__(base_attribute, + entity.__getattribute__(base_attribute)) for prop in entity.properties: robj._set_property_from_entity(prop, entity.get_importance(prop)) @@ -533,32 +579,36 @@ def _single_convert_to_python_object(robj: CaosDBPythonEntity, robj.add_parent(CaosDBPythonUnresolvedParent(id=parent.id, name=parent.name)) - robj.path = entity.path - robj.file = entity.file - return robj -def _single_convert_to_entity(entity, robj, recursive_depth, **kwargs): +def _single_convert_to_entity(entity: db.Entity, + robj: CaosDBPythonEntity, + recursive_depth, **kwargs): """ recursive_depth: disabled if 0 """ - if robj._id is not None: - entity.id = robj._id - - if robj._path is not None: - entity.path = robj._path - if robj._file is not None: - entity.file = robj._file + for base_attribute in BASE_ATTRIBUTES: + modified_base_attribute = base_attribute + if base_attribute in ("file", "path"): + modified_base_attribute = "_" + modified_base_attribute + + entity.__setattr__(modified_base_attribute, + robj.__getattribute__(base_attribute)) children = [] for parent in robj._parents: - if hasattr(parent, "encode"): - entity.add_parent(name=parent) + if isinstance(parent, CaosDBPythonUnresolvedParent): + entity.add_parent(name=parent.name, id=parent.id) + elif isinstance(parent, CaosDBPythonRecordType): + raise NotImplementedError() else: - entity.add_parent(id=parent) + raise RuntimeError("Incompatible class used as parent.") + + # TODO: implementation incomplete + raise NotImplementedError() def add_property(entity, prop, name, _recursive=False, datatype=None): if datatype is None: @@ -584,10 +634,15 @@ def _single_convert_to_entity(entity, robj, recursive_depth, **kwargs): else: recursive = True - for prop in robj._properties: + for prop in robj.get_properties(): value = robj.__getattribute__(prop) + metadata = robj.get_property_metadata(prop) - if isinstance(value, list): + if isinstance(value, CaosDBPythonUnresolvedReference): + pass + elif isinstance(value, CaosDBMultiProperty): + raise NotImplementedError() + elif isinstance(value, list): if robj._datatypes[prop][0:4] == "LIST": lst = []