From 72aa0fb16b0f9de1f8df349c56e12e9dda7dc469 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <h.tomwoerden@indiscale.com>
Date: Tue, 13 Jul 2021 11:35:18 +0000
Subject: [PATCH] ENH: Add is_reference to db.Property

---
 CHANGELOG.md                |  2 ++
 src/caosdb/common/models.py | 37 +++++++++++++++++++++++++++++
 unittests/test_property.py  | 47 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6255ff1c..5aa59299 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added ###
 
+* is_reference function for Properties
+
 ### Changed ###
 
 ### Deprecated ###
diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py
index 0142b044..57b07bd6 100644
--- a/src/caosdb/common/models.py
+++ b/src/caosdb/common/models.py
@@ -31,6 +31,7 @@ from __future__ import print_function, unicode_literals
 import re
 import sys
 from builtins import str
+from copy import deepcopy
 from functools import cmp_to_key
 from hashlib import sha512
 from os import listdir
@@ -1504,6 +1505,42 @@ class Property(Entity):
 
         return super(Property, self).to_xml(xml, add_properties)
 
+    def is_reference(self, server_retrieval=False):
+        """Returns whether this Property is a reference
+
+        Parameters
+        ----------
+        server_retrieval : bool, optional
+            If True and the datatype is not set, the Property is retrieved from the server, by default False
+
+        Returns
+        -------
+        bool, NoneType
+            Returns whether this Property is a reference or None if a server call is needed to
+            check correctly, but server_retrieval is set to False.
+
+        """
+
+        if self.datatype is None:
+
+            if not self.is_valid():
+                # this is a workaround to prevent side effects
+                # since retrieve currently changes the object
+
+                if server_retrieval:
+                    tmp_prop = deepcopy(self)
+                    tmp_prop.retrieve()
+
+                    return tmp_prop.is_reference()
+                else:
+                    return None
+            else:
+                # a valid property without datatype has to be an RT
+
+                return True
+        else:
+            return is_reference(self.datatype)
+
 
 class Message(object):
 
diff --git a/unittests/test_property.py b/unittests/test_property.py
index 752ee01f..834b1be5 100644
--- a/unittests/test_property.py
+++ b/unittests/test_property.py
@@ -24,9 +24,10 @@
 # ** end header
 #
 """Tests for the Property class."""
+import caosdb as db
+from caosdb import Entity, Property, Record
 # pylint: disable=missing-docstring
 from lxml import etree
-from caosdb import Entity, Property, Record
 
 parser = etree.XMLParser(remove_comments=True)
 testrecord = Record._from_xml(Record(),
@@ -89,3 +90,47 @@ def test_get_property_with_entity():
 def test_selected_reference_list():
     assert len(testrecord.get_property("Conductor").value) == 1
     assert isinstance(testrecord.get_property("Conductor").value[0], Entity)
+
+
+def test_is_reference():
+    PROPS = {
+        10:  db.INTEGER,
+        20:  db.REFERENCE,
+        30:  "SomeRT",
+    }
+
+    def dummy_retrieve(self):
+        self.datatype = PROPS[self.id]
+        self.is_valid = lambda: True
+    # replace retrieve function by dummy
+    real_retrieve = Entity.retrieve
+    Entity.retrieve = dummy_retrieve
+
+    p1 = Property(id=1, datatype=db.INTEGER)
+    p2 = Property(id=2, datatype=db.DOUBLE)
+    p3 = Property(id=3, datatype=db.TEXT)
+    p4 = Property(id=4, datatype=db.DATETIME)
+    p5 = Property(id=5, datatype=db.BOOLEAN)
+    p6 = Property(id=6, datatype=db.REFERENCE)
+    assert p1.is_reference() is False
+    assert p2.is_reference() is False
+    assert p3.is_reference() is False
+    assert p4.is_reference() is False
+    assert p5.is_reference() is False
+    assert p6.is_reference() is True
+
+    p7 = Property(id=7)
+    p8 = Property(id=8, value=db.RecordType(id=1000))
+    p8.is_valid = lambda: True
+    assert p7.is_reference() is None  # cannot be resolved without calling a server
+    assert p8.is_reference() is True
+
+    p10 = Property(id=10)
+    p20 = Property(id=20)
+    p30 = Property(id=30)
+    assert p10.is_reference(server_retrieval=True) is False
+    assert p20.is_reference(server_retrieval=True) is True
+    assert p30.is_reference(server_retrieval=True) is True
+
+    # restore retrieve function with original
+    Entity.retrieve = real_retrieve
-- 
GitLab