diff --git a/src/caosdb/__init__.py b/src/caosdb/__init__.py index 75847bb15b8a64345119e0f0aefc00bd9f116081..d59c60dba6010cdd29e6fc6c64494c6db5941d00 100644 --- a/src/caosdb/__init__.py +++ b/src/caosdb/__init__.py @@ -31,6 +31,7 @@ import caosdb.apiutils from caosdb.common import administration from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER, REFERENCE, TEXT, LIST) +from caosdb.common.state import State # Import of the basic API classes: from caosdb.common.models import (ACL, ALL, FIX, NONE, OBLIGATORY, RECOMMENDED, SUGGESTED, Container, DropOffBox, Entity, diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index a5adf6af68b72663c0fe8cd6ab58c39cdcea5d27..eb625610c228f094343cca219ad91b56b05a2aca 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -43,6 +43,7 @@ from warnings import warn from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER, LIST, REFERENCE, TEXT, is_reference) from caosdb.common.versioning import Version +from caosdb.common.state import State from caosdb.common.utils import uuid, xml2str from caosdb.configuration import get_config from caosdb.connection.connection import get_connection @@ -109,6 +110,7 @@ class Entity(object): self.name = name self.description = description self.id = id + self.state = None @property def version(self): @@ -907,6 +909,9 @@ class Entity(object): if self.acl is not None: xml.append(self.acl.to_xml()) + if self.state is not None: + xml.append(self.state.to_xml()) + return xml @staticmethod @@ -951,6 +956,8 @@ class Entity(object): entity.add_message(child) elif isinstance(child, Version): entity.version = child + elif isinstance(child, State): + entity.state = child elif child is None or hasattr(child, "encode"): vals.append(child) elif isinstance(child, Entity): @@ -1070,7 +1077,7 @@ class Entity(object): flags=flags)[0] def update(self, strict=False, raise_exception_on_error=True, - unique=True, flags=None): + unique=True, flags=None, sync=True): """Update this entity. There are two possible work-flows to perform this update: @@ -1104,6 +1111,7 @@ class Entity(object): return Container().append(self).update( strict=strict, + sync=sync, raise_exception_on_error=raise_exception_on_error, unique=unique, flags=flags)[0] @@ -1239,6 +1247,7 @@ class QueryTemplate(): self.is_valid = lambda: False self.is_deleted = lambda: False self.version = None + self.state = None def retrieve(self, strict=True, raise_exception_on_error=True, unique=True, sync=True, flags=None): @@ -2215,6 +2224,7 @@ def _basic_sync(e_local, e_remote): e_local.is_valid = e_remote.is_valid e_local.is_deleted = e_remote.is_deleted e_local.version = e_remote.version + e_local.state = e_remote.state if hasattr(e_remote, "query"): e_local.query = e_remote.query @@ -2828,7 +2838,7 @@ class Container(list): entity_url_segments = [_ENTITY_URI_SEGMENT, "&".join(id_str)] _log_request("DELETE: " + str(entity_url_segments) + - ("?" + flags if flags is not None else '')) + ("?" + str(flags) if flags is not None else '')) http_response = c.delete(entity_url_segments, query_dict=flags) cresp = Container._response_to_entities(http_response) @@ -3850,6 +3860,8 @@ def _parse_single_xml_element(elem): return entity elif elem.tag.lower() == "version": return Version.from_xml(elem) + elif elem.tag.lower() == "state": + return State.from_xml(elem) elif elem.tag.lower() == "emptystring": return "" elif elem.tag.lower() == "value": diff --git a/src/caosdb/common/state.py b/src/caosdb/common/state.py new file mode 100644 index 0000000000000000000000000000000000000000..431675fb1893ee215f22b356a78ee50fad987b97 --- /dev/null +++ b/src/caosdb/common/state.py @@ -0,0 +1,33 @@ +from lxml import etree +class State: + + def __init__(self, model, name): + self.name = name + self.model = model + + def __eq__(self, other): + return (other is not None + and hasattr(other, "model") + and hasattr(other, "name") + and self.name == other.name + and self.model == other.model) + + def __hash__(self): + return hash(self.name) + hash(self.model) + + def __repr__(self): + return f"State('{self.model}', '{self.name}')" + + def to_xml(self): + xml = etree.Element("State") + if self.name is not None: + xml.set("name", self.name) + if self.model is not None: + xml.set("model", self.model) + return xml + + @staticmethod + def from_xml(xml): + name = xml.get("name") + model = xml.get("model") + return State(name=name, model=model) diff --git a/unittests/test_state.py b/unittests/test_state.py new file mode 100644 index 0000000000000000000000000000000000000000..7fc16363af0fe123f507ebe27edf56597c2955c7 --- /dev/null +++ b/unittests/test_state.py @@ -0,0 +1,27 @@ +import caosdb as db +from caosdb import State +from caosdb.common.models import parse_xml +from lxml import etree + +def test_state_xml(): + state = State(model="model1", name="state1") + xml = etree.tostring(state.to_xml()) + + assert xml == b'<State name="state1" model="model1"/>' + state = State.from_xml(etree.fromstring(xml)) + assert state.name == "state1" + assert state.model == "model1" + + assert xml == etree.tostring(state.to_xml()) + +def test_entity_xml(): + r = db.Record() + assert r.state is None + r.state = State(model="model1", name="state1") + + xml = etree.tostring(r.to_xml()) + assert xml == b'<Record><State name="state1" model="model1"/></Record>' + + r = parse_xml(xml) + assert r.state == State(model="model1", name="state1") +