From 8176aeb609d57454e518f54560ef2e2a5125b635 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <henrik@trineo.org>
Date: Mon, 5 Aug 2019 10:05:04 +0200
Subject: [PATCH] DEV,TST: added entity checks

Added some utitlity functions and corresponding tests:
assure_entity_is_in_list for checking whether an entity has another
entity in one of its properties and possibly sets it
assure_has_parent checks whether an entity has a parent and possibly
adds it
insert_id_based_on_name and get_ids_for_entities_with_names for adding
ids to entities in a container such that they can be updated
---
 src/caosadvancedtools/cfood.py | 76 ++++++++++++++++++++++++++++++++++
 unittests/test_cfood.py        | 32 ++++++++++++--
 2 files changed, 105 insertions(+), 3 deletions(-)

diff --git a/src/caosadvancedtools/cfood.py b/src/caosadvancedtools/cfood.py
index 5a52b958..32703b65 100644
--- a/src/caosadvancedtools/cfood.py
+++ b/src/caosadvancedtools/cfood.py
@@ -145,3 +145,79 @@ class AbstractCFood(object):
             entity.add_property(prop, value, datatype=datatype)
         else:
             entity.add_property(prop, value)
+
+
+def assure_entity_is_in_list(entity, containing_object, property_name,
+                             to_be_updated):
+    """
+    Checks whether `entity` is one of the values in the list property
+    `property_name` of the supplied entity  containing_object`.
+
+    This check is done based on ids.
+
+    If this is the case this function returns. Otherwise the entity is added to
+    the property `property_name` and the entity `containing_object` is added to
+    the supplied list to_be_updated in order to indicate, that the entity
+    `containing_object` should be updated.
+
+    If the property is missing, it is added first and then the entity is added.
+    """
+
+    if containing_object.get_property(property_name) is None:
+        containing_object.add_property(property_name, value=[],
+                                       datatype=db.LIST(property_name))
+    current_list = containing_object.get_property(property_name).value
+    contained = False
+
+    for el in current_list:
+        if el.id == entity.id:
+            contained = True
+
+            break
+
+    if contained:
+        return
+
+    current_list.append(entity)
+    to_be_updated.append(containing_object)
+
+
+def assure_has_parent(entity, parent, to_be_updated):
+    """
+    Checks whether `entity` has a parent with name `parent`.
+
+    If this is the case this function returns. Otherwise the entity is assigned
+    a new parent and is added to the supplied list to_be_updated in order to
+    indicate, that the entity `entity` should be updated.
+    """
+    parents = entity.get_parents()
+    contained = False
+
+    for el in parents:
+        if el.name == parent:
+            contained = True
+
+            break
+
+    if contained:
+        return
+
+    entity.add_parent(parent)
+    to_be_updated.append(entity)
+
+
+def insert_id_based_on_name(entity):
+    if entity.name is not None and (entity.id is None or entity.id < 0):
+        entity.id = get_entity(entity.name).id
+
+
+def get_ids_for_entities_with_names(entities):
+    for ent in entities:
+        insert_id_based_on_name(ent)
+
+        for prop in ent.get_properties():
+            insert_id_based_on_name(prop)
+
+        for parent in ent.get_parents():
+            insert_id_based_on_name(parent)
+            insert_id_based_on_name(ent)
diff --git a/unittests/test_cfood.py b/unittests/test_cfood.py
index b69e4f0e..a7d584a9 100644
--- a/unittests/test_cfood.py
+++ b/unittests/test_cfood.py
@@ -22,12 +22,11 @@
 #
 # ** end header
 import unittest
-from tempfile import NamedTemporaryFile
 
 import caosdb as db
 
-from caosadvancedtools.cfood import AbstractCFood
-
+from caosadvancedtools.cfood import (AbstractCFood, assure_entity_is_in_list,
+                                     assure_has_parent)
 
 PATTERN = "h.*"
 
@@ -47,3 +46,30 @@ class CFoodReTest(unittest.TestCase):
         self.assertIsNotNone(TestCFood._pattern)
         self.assertIsNotNone(TestCFood.match("hallo"))
         self.assertIsNone(TestCFood.match("allo"))
+
+
+class InsertionTest(unittest.TestCase):
+    def test_contained_in_list(self):
+        entity_with_list = db.Record()
+        prop_name = "list_prop"
+        contained_entity = db.Record(id=1005)
+        to_be_updated = []
+        assure_entity_is_in_list(contained_entity, entity_with_list, prop_name,
+                                 to_be_updated)
+        assert to_be_updated[0] is entity_with_list
+        assert (to_be_updated[0].get_property(prop_name).value[0] is
+                contained_entity)
+        to_be_updated = []
+        assure_entity_is_in_list(contained_entity, entity_with_list, prop_name,
+                                 to_be_updated)
+        assert len(to_be_updated) == 0
+
+    def test_parent(self):
+        entity = db.Record()
+        to_be_updated = []
+        assure_has_parent(entity, "parent", to_be_updated)
+        assert to_be_updated[0] is entity
+        assert (to_be_updated[0].get_parents()[0].name == "parent")
+        to_be_updated = []
+        assure_has_parent(entity, "parent", to_be_updated)
+        assert len(to_be_updated) == 0
-- 
GitLab