From 2e7f05817d05b07938f3ba585d0634cecc3ad4ed Mon Sep 17 00:00:00 2001 From: florian <f.spreckelsen@inidscale.com> Date: Wed, 26 Oct 2022 13:59:49 +0200 Subject: [PATCH] ENH: Allow entity comparisons with inspection of referenced records --- src/caosdb/apiutils.py | 46 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/caosdb/apiutils.py b/src/caosdb/apiutils.py index b1c74a59..0de24ca7 100644 --- a/src/caosdb/apiutils.py +++ b/src/caosdb/apiutils.py @@ -283,9 +283,28 @@ def compare_entities(old_entity: Entity, new_entity: Entity, compare_referenced_ matching[0].unit if (prop.value != matching[0].value): - olddiff["properties"][prop.name]["value"] = prop.value - newdiff["properties"][prop.name]["value"] = \ - matching[0].value + # basic comparison of value objects says they are different + same_value = False + if compare_referenced_records: + # scalar reference + if isinstance(prop.value, Entity) and isinstance(matching[0].value, Entity): + # exlicitely not recursive to prevent infinite recursion + same_value = empty_diff( + prop.value, matching[0].value, compare_referenced_records=False) + # list of references + elif isinstance(prop.value, list) and isinstance(matching[0].value, list): + # all elements in both lists actually are entity objects + if all([isinstance(x, Entity) for x in prop.value]) and all([isinstance(x, Entity) for x in matching[0].value]): + # can't be the same if the lengths are different + if len(prop.value) == len(matching[0].value): + # do a one-by-one comparison; the values are the same, if all diffs are empty + same_value = all( + [empty_diff(x, y, False) for x, y in zip(prop.value, matching[0].value)]) + + if not same_value: + olddiff["properties"][prop.name]["value"] = prop.value + newdiff["properties"][prop.name]["value"] = \ + matching[0].value if (len(newdiff["properties"][prop.name]) == 0 and len(olddiff["properties"][prop.name]) == 0): @@ -338,7 +357,7 @@ def empty_diff(old_entity: Entity, new_entity: Entity, compare_referenced_record return True -def merge_entities(entity_a: Entity, entity_b: Entity): +def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_empty_diffs=True): """ Merge entity_b into entity_a such that they have the same parents and properties. @@ -352,13 +371,30 @@ def merge_entities(entity_a: Entity, entity_b: Entity): Returns entity_a. WARNING: This function is currently experimental and insufficiently tested. Use with care. + + Parameters + ---------- + entity_a, entity_b : Entity + The entities to be merged. entity_b will be merged into entity_a in place + merge_references_with_empty_diffs : bool, optional + Whether the merge is performed if entity_a and entity_b both reference + record(s) that may be different Python objects but have empty diffs. If + set to `False` a merge conflict will be raised in this case + instead. Default is True. + + Returns + ------- + entity_a : Entity + The initial entity_a after the in-place merge + """ logging.warning( "This function is currently experimental and insufficiently tested. Use with care.") # Compare both entities: - diff_r1, diff_r2 = compare_entities(entity_a, entity_b) + diff_r1, diff_r2 = compare_entities( + entity_a, entity_b, compare_referenced_records=merge_references_with_empty_diffs) # Go through the comparison and try to apply changes to entity_a: for key in diff_r2["parents"]: -- GitLab