diff --git a/CHANGELOG.md b/CHANGELOG.md index 31fcec0f757c0c4b574cf914d05b1518772de6d5..89b40b875cb8aede419d152b812998888d92af50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ### +* #81 compare_entities from apiutils does not compare entity values + ### Security ### ## [0.6.0] - 2021-10-19 ## diff --git a/src/caosdb/apiutils.py b/src/caosdb/apiutils.py index b3d5df698baceacf671c5171430dfa48b3c881b5..21a8cc7ca700a8b119786dbef082db286ead5d57 100644 --- a/src/caosdb/apiutils.py +++ b/src/caosdb/apiutils.py @@ -30,15 +30,15 @@ Some simplified functions for generation of records etc. import sys import tempfile -from collections.abc import Iterable import warnings +from collections.abc import Iterable from subprocess import call from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER, REFERENCE, TEXT, is_reference) from caosdb.common.models import (Container, Entity, File, Property, Query, - Record, RecordType, get_config, - execute_query) + Record, RecordType, execute_query, + get_config) def new_record(record_type, name=None, description=None, @@ -561,7 +561,23 @@ def getCommitIn(folder): COMPARED = ["name", "role", "datatype", "description", "importance"] -def compare_entities(old_entity, new_entity): +def compare_entities(old_entity: Entity, new_entity: Entity): + """ + Compare two entites. + + Return a tuple of dictionaries, the first index belongs to additional information for old + entity, the second index belongs to additional information for new entity. + + Additional information means in detail: + - Additional parents (a list under key "parents") + - Information about properties: + - Each property lists either an additional property or a property with a changed: + - ... datatype + - ... importance or + - ... value (not implemented yet) + In case of changed information the value listed under the respective key shows the + value that is stored in the respective entity. + """ olddiff = {"properties": {}, "parents": []} newdiff = {"properties": {}, "parents": []} @@ -616,13 +632,16 @@ def compare_entities(old_entity, new_entity): newdiff["properties"][prop.name]["importance"] = \ new_entity.get_importance(prop.name) - if ((prop.datatype is not None and - matching[0].datatype is not None) and - (prop.datatype != matching[0].datatype)): + if (prop.datatype != matching[0].datatype): olddiff["properties"][prop.name]["datatype"] = prop.datatype newdiff["properties"][prop.name]["datatype"] = \ matching[0].datatype + if (prop.value != matching[0].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): newdiff["properties"].pop(prop.name) @@ -700,6 +719,7 @@ def apply_to_ids(entities, func): entities : list of Entity func : function with one parameter. """ + for entity in entities: _apply_to_ids_of_entity(entity, func) diff --git a/src/caosdb/yamlapi.py b/src/caosdb/yamlapi.py index 69928af1568bf2150288844d74d606e39d598d0b..80bb4b13e4d1626c5d29c8950f3a22bbb73e0fdb 100644 --- a/src/caosdb/yamlapi.py +++ b/src/caosdb/yamlapi.py @@ -98,7 +98,7 @@ def yaml_to_xml(yamlstr): The string to load the yaml document from. """ - return dict_to_xml(yaml.safe_load(yamlstr)) + return dict_to_xml(yaml.load(yamlstr, Loader=yaml.SafeLoader)) def process(text): diff --git a/unittests/test_apiutils.py b/unittests/test_apiutils.py index 264b4c880022e6fd135426864bf9c5084c047eca..717595d049a5a7c27b644dc6079d02efa1cefb71 100644 --- a/unittests/test_apiutils.py +++ b/unittests/test_apiutils.py @@ -26,11 +26,14 @@ # Test apiutils # A. Schlemmer, 02/2018 -import caosdb as db import pickle import tempfile -from caosdb.apiutils import apply_to_ids, create_id_query, resolve_reference + +import caosdb as db import caosdb.apiutils +from caosdb.apiutils import (apply_to_ids, compare_entities, create_id_query, + resolve_reference) + from .test_property import testrecord @@ -67,7 +70,8 @@ def test_apply_to_ids(): def test_id_query(): ids = [1, 2, 3, 4, 5] - assert create_id_query(ids) == 'FIND ENTITY WITH ID=1 OR ID=2 OR ID=3 OR ID=4 OR ID=5' + assert create_id_query(ids) == 'FIND ENTITY WITH ID=1 OR ID=2 OR ID=3 OR '\ + 'ID=4 OR ID=5' def test_resolve_reference(): @@ -77,8 +81,10 @@ def test_resolve_reference(): prop = db.Property(id=1, datatype=db.REFERENCE, value=100) prop.is_valid = lambda: True items = [200, 300, 400] - prop_list = db.Property(datatype=db.LIST(db.REFERENCE), value=items) - prop_list2 = db.Property(datatype=db.LIST(db.REFERENCE), value=[db.Record(id=500)]) + prop_list = db.Property(datatype=db.LIST(db.REFERENCE), + value=items) + prop_list2 = db.Property(datatype=db.LIST(db.REFERENCE), + value=[db.Record(id=500)]) resolve_reference(prop) resolve_reference(prop_list) resolve_reference(prop_list2) @@ -86,6 +92,7 @@ def test_resolve_reference(): assert isinstance(prop.value, db.Entity) prop_list_ids = [] + for i in prop_list.value: prop_list_ids.append(i.id) assert isinstance(i, db.Entity) @@ -102,3 +109,38 @@ def test_resolve_reference(): # restore retrive_entity_with_id caosdb.apiutils.retrieve_entity_with_id = original_retrieve_entity_with_id + + +def test_compare_entities(): + r1 = db.Record() + r2 = db.Record() + r1.add_parent("bla") + r2.add_parent("bla") + r1.add_parent("lopp") + r1.add_property("test", value=2) + r2.add_property("test", value=2) + r1.add_property("tests", value=3) + r2.add_property("tests", value=45) + r1.add_property("tester", value=3) + r2.add_property("tester", ) + r1.add_property("tests_234234", value=45) + r2.add_property("tests_TT", value=45) + + diff_r1, diff_r2 = compare_entities(r1, r2) + + assert len(diff_r1["parents"]) == 1 + assert len(diff_r2["parents"]) == 0 + assert len(diff_r1["properties"]) == 3 + assert len(diff_r2["properties"]) == 3 + + assert "test" not in diff_r1["properties"] + assert "test" not in diff_r2["properties"] + + assert "tests" in diff_r1["properties"] + assert "tests" in diff_r2["properties"] + + assert "tester" in diff_r1["properties"] + assert "tester" in diff_r2["properties"] + + assert "tests_234234" in diff_r1["properties"] + assert "tests_TT" in diff_r2["properties"] diff --git a/unittests/test_property.py b/unittests/test_property.py index 834b1be582c58c60f70331de9cb0d0d6414fd6c9..7c756117765e510587c00d818e39fb3945d44c53 100644 --- a/unittests/test_property.py +++ b/unittests/test_property.py @@ -24,15 +24,18 @@ # ** end header # """Tests for the Property class.""" +import os + import caosdb as db from caosdb import Entity, Property, Record # pylint: disable=missing-docstring from lxml import etree parser = etree.XMLParser(remove_comments=True) -testrecord = Record._from_xml(Record(), - etree.parse("unittests/test_record.xml", - parser).getroot()) +testrecord = Record._from_xml( + Record(), + etree.parse(os.path.join(os.path.dirname(__file__), "test_record.xml"), + parser).getroot()) def test_is_entity(): @@ -48,7 +51,8 @@ def test_instance_variables(): def test_null_empty_text_value_1(): - assert testrecord.get_property("LISTofTEXT").value == ["One", "Two", "Three", None, ""] + assert testrecord.get_property("LISTofTEXT").value == ["One", "Two", + "Three", None, ""] def test_null_empty_text_value_2():