diff --git a/src/caosadvancedtools/models/data_model.py b/src/caosadvancedtools/models/data_model.py index bb40939a52a0700883f119ff03ddf499c2589845..9730bf5ff0312196b358cbe2ba427a09bc69aa6d 100644 --- a/src/caosadvancedtools/models/data_model.py +++ b/src/caosadvancedtools/models/data_model.py @@ -29,8 +29,9 @@ from copy import deepcopy # remove this, when we drop support for old Python versions. from typing import List -import caosdb as db -from caosdb.apiutils import compare_entities, describe_diff +import linkahead as db +import linkahead.common.models as models +from linkahead.apiutils import compare_entities, describe_diff CAOSDB_INTERNAL_PROPERTIES = [ @@ -263,28 +264,56 @@ class DataModel(dict): return list(all_ents.values()) - def get_deep(self, name: str, visited: set = None): + def get_deep(self, name: str, visited_props: set = None, visited_parents: set = None): """Attempt to resolve references for the given ``name``. - This methods only uses data which is available in this datamodel, which acts kind of like a - cache pool. + The returned entity has all the properties it inherits from its ancestry and all properties + have the correct descriptions and datatypes. This methods only uses data which is available + in this DataModel, which acts kind of like a cache pool. + + Note that this may change this data model (subsequent "get" like calls may also return + deeper content.) - Note that this may change this data model (subsequent "get" like calls may also return deep - content.) """ entity = self.get(name) if not entity: return entity - if not visited: - visited = set() + if not visited_props: + visited_props = set() + if not visited_parents: + visited_parents = set() + + importances = { + models.OBLIGATORY: 0, + models.SUGGESTED: 1, + models.RECOMMENDED: 2, + } + + for parent in list(entity.get_parents()): # Make a change-resistant list copy. + if parent.name in visited_parents: + continue + visited_parents.add(parent.name) + parent_importance = importances.get(parent._flags.get("inheritance"), 999) + if parent.name in self: + deep_parent = self.get_deep(parent.name, # visited_props=visited_props, + visited_parents=visited_parents + ) + + for prop in deep_parent.properties: + importance = importances[deep_parent.get_importance(prop.name)] + if (importance <= parent_importance + and prop.name not in [prop.name for prop in entity.properties]): + entity.add_property(prop) + else: + print(f"Referenced parent \"{parent.name}\" not found in data model.") - # new_props = [] for prop in list(entity.get_properties()): # Make a change-resistant list copy. - if prop.name in visited: + if prop.name in visited_props: continue - visited.add(prop.name) + visited_props.add(prop.name) if prop.name in self: - deep_prop = self.get_deep(prop.name, visited=visited) + deep_prop = self.get_deep(prop.name, visited_props=visited_props, + visited_parents=visited_parents) linked_prop = entity.get_property(prop) if not linked_prop.datatype: if deep_prop.role == "Property": @@ -295,4 +324,5 @@ class DataModel(dict): linked_prop.description = deep_prop.description else: print(f"Referenced property \"{prop.name}\" not found in data model.") + return entity diff --git a/unittests/test_json_schema_exporter.py b/unittests/test_json_schema_exporter.py index b182f576093ee82bf668ffb320feb608bb14bcdf..f8d80ad50a04a0bcf9abb47138d691a2350691f3 100644 --- a/unittests/test_json_schema_exporter.py +++ b/unittests/test_json_schema_exporter.py @@ -739,5 +739,33 @@ RT2: """ model = parse_model_from_string(model_str) rt2_deep = model.get_deep("RT2") - assert "some_date" in [prop.name for prop in rt2_deep.properties] + + model_str = """ +RT1: + obligatory_properties: + RT2: +RT2: + inherit_from_suggested: + - RT1 +RT3: + inherit_from_suggested: + - RT4 +RT4: + inherit_from_suggested: + - RT3 +RT5: + inherit_from_suggested: + - RT5 + """ + model = parse_model_from_string(model_str) + # This must not lead to an infinite recursion + rt1_deep = model.get_deep("RT1") + rt2_deep = model.get_deep("RT2") + assert rt2_deep.get_property("RT2").name == rt1_deep.get_property("RT2").name + rt3_deep = model.get_deep("RT3") + assert rt3_deep.get_parents()[0].name == "RT4" + rt4_deep = model.get_deep("RT4") + assert rt4_deep.get_parents()[0].name == "RT3" + rt5_deep = model.get_deep("RT5") + assert rt5_deep.get_parents()[0].name == "RT5"