From 32c337d3facd601f9c63d519de4ee467dc1b5ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <h.tomwoerden@indiscale.com> Date: Fri, 24 May 2024 11:29:03 +0200 Subject: [PATCH] MAINT: review corrections --- src/caoscrawler/identifiable.py | 17 ++++----- src/caoscrawler/identifiable_adapters.py | 2 +- src/caoscrawler/sync_graph.py | 45 ++++++------------------ src/caoscrawler/sync_node.py | 14 ++------ 4 files changed, 21 insertions(+), 57 deletions(-) diff --git a/src/caoscrawler/identifiable.py b/src/caoscrawler/identifiable.py index aead3769..d396fee5 100644 --- a/src/caoscrawler/identifiable.py +++ b/src/caoscrawler/identifiable.py @@ -84,9 +84,8 @@ class Identifiable(): def _value_representation(value) -> str: """returns the string representation of property values to be used in the hash function - The string is the CaosDB ID of other Entities - (Python Id only if there is no CaosDB ID) and the string representation of bool, float, int - and str. + The string is the CaosDB ID in case of SyncNode objects (SyncNode objects must have an ID) + and the string representation of None, bool, float, int, datetime and str. """ if value is None: @@ -95,7 +94,7 @@ class Identifiable(): if value.id is not None: return str(value.id) else: - raise RuntimeError("Python Entity without id not allowed") + raise RuntimeError("Python Entity (SyncNode) without ID not allowed") elif isinstance(value, list): return "[" + ", ".join([Identifiable._value_representation(el) for el in value]) + "]" elif (isinstance(value, str) or isinstance(value, int) or isinstance(value, float) @@ -104,7 +103,7 @@ class Identifiable(): else: raise ValueError(f"Unknown datatype of the value: {value}") - @ staticmethod + @staticmethod def _create_hashable_string(identifiable: Identifiable) -> str: """ creates a string from the attributes of an identifiable that can be hashed @@ -121,13 +120,10 @@ class Identifiable(): return rec_string def __eq__(self, other) -> bool: - """ - Identifiables are equal if they belong to the same Record. - equal if attribute representations are equal - """ + """ Identifiables are equal if they share the same ID or if the representation is equal """ if not isinstance(other, Identifiable): raise ValueError("Identifiable can only be compared to other Identifiable objects.") - elif self.record_id is not None and other.record_id is not None: + if self.record_id is not None and other.record_id is not None: return self.record_id == other.record_id elif self.get_representation() == other.get_representation(): return True @@ -135,6 +131,7 @@ class Identifiable(): return False def __repr__(self): + """ deterministic text representation of the identifiable """ pstring = json.dumps({k: str(v) for k, v in self.properties.items()}) return (f"{self.__class__.__name__} for RT {self.record_type}: id={self.record_id}; " f"name={self.name}\n" diff --git a/src/caoscrawler/identifiable_adapters.py b/src/caoscrawler/identifiable_adapters.py index 9fa297cc..89d93847 100644 --- a/src/caoscrawler/identifiable_adapters.py +++ b/src/caoscrawler/identifiable_adapters.py @@ -298,7 +298,7 @@ startswith: bool, optional name = None if se.registered_identifiable is None: - raise ValueError("no register_identifiable") + raise ValueError("no registered_identifiable") # fill the values: for prop in se.registered_identifiable.properties: diff --git a/src/caoscrawler/sync_graph.py b/src/caoscrawler/sync_graph.py index 3fd06833..738b0773 100644 --- a/src/caoscrawler/sync_graph.py +++ b/src/caoscrawler/sync_graph.py @@ -43,25 +43,21 @@ from .sync_node import SyncNode, TempID logger = logging.getLogger(__name__) -def _for_each_scalar_value(node: SyncNode, condition: Callable[[Any], bool], kind: str, - value: Callable[[Any], Any] = None): - """ helper function that performs an action on each value element of each property of a node +def _set_each_scalar_value(node: SyncNode, condition: Callable[[Any], bool], value: Any): + """ helper function that conditionally replaces each value element of each property of a node - The argument "kind" determines which action is performed on each property value: - The action (remove or set) is performed on each property value of each property: in case of - lists, it is performed on each list element. The action is only performed if the condition that + If the property value is a list, the replacement is done for each list entry. + The replacement is only performed if the condition that is provided is fulfilled, i.e. the callable ``condition`` returns True. The callable ``condition`` must take the property value (or list element) as the sole argument. - Thus, with "remove" you can conditionally remove values and with "set" you can conditionally - replace values. - Args: node (SyncNode): The node which provides the properties (and their values) to operate on. - condition (Callable): A function with one argument which is interpreted as a condition: Only if - it returns True for the property value, the action is executed. - kind (str): Either "remove" or "set" depending on whether you want to remove the property or replace the property. - value (Callable): A function returning a new value that is set as the property value. This function receives the old value as the single argument. + condition (Callable): A function with one argument which is interpreted as a condition: + Only if it returns True for the property value, the action is + executed. + value (Callable): A function returning a new value that is set as the property value. This + function receives the old value as the single argument. Last review by Alexander Schlemmer on 2024-05-24. """ @@ -69,25 +65,9 @@ def _for_each_scalar_value(node: SyncNode, condition: Callable[[Any], bool], kin if isinstance(p.value, list): for ii, el in enumerate(p.value): if condition(el): - if kind == "remove": - p.value.remove(el) - elif kind == "set": - p.value[ii] = value(el) + p.value[ii] = value(el) elif condition(p.value): - if kind == "remove": - node.properties.remove(p) - elif kind == "set": - p.value = value(p.value) - - -def _remove_each_scalar_value(node: SyncNode, condition: Callable[[Any], bool]): - """ "remove" version of _for_each_scalar_value """ - _for_each_scalar_value(node, condition, "remove") - - -def _set_each_scalar_value(node: SyncNode, condition: Callable[[Any], bool], value: Any): - """ "set" version of _for_each_scalar_value """ - _for_each_scalar_value(node, condition, "set", value=value) + p.value = value(p.value) class SyncGraph(): @@ -652,6 +632,3 @@ class SyncGraph(): for other_node in self._get_nodes_whose_identity_relies_on(node): if self._identifiable_is_needed(other_node): self._set_identifiable_of_node(other_node) - - def __repr__(self): - return f"SyncNode with ID={self.id}" diff --git a/src/caoscrawler/sync_node.py b/src/caoscrawler/sync_node.py index 26dbb411..80a7e4a1 100644 --- a/src/caoscrawler/sync_node.py +++ b/src/caoscrawler/sync_node.py @@ -167,7 +167,7 @@ class SyncNode: if unequal: logger.error( "The Crawler is trying to create an entity," - " but there are have conflicting property values." + " but there are conflicting property values." f"Problematic Property: {p.name}\n" f"First value:\n{entval}\n" f"Second value:\n{pval}\n" @@ -180,11 +180,8 @@ class SyncNode: return ent def __repr__(self) -> str: + """ somewhat concise text representation of the SyncNode """ res = f"\n=====================================================\n{self.role}\n" - if hasattr(self, "_metadata"): - res += f"user: {self._metadata['user']}\n" - res += f"json: {self._metadata['json']}\n" - res += "---------------------------------------------------\n" res += yaml.dump( { "id": self.id, @@ -219,13 +216,6 @@ class SyncNode: + "=====================================================\n" ) - def is_unidentifiable(self) -> bool: - """returns whether this is an unidentifiable Node""" - return ( - self.registered_identifiable is not None - and self.registered_identifiable.get_property("no-ident") is not None - ) - def parent_in_list(parent: Parent, plist: _ParentList) -> bool: """helper function that checks whether a parent with the same name or ID is in the plist""" -- GitLab