From 1b4c088a026b36bd0ba3b97467226c979ace8578 Mon Sep 17 00:00:00 2001 From: Silvano Cirujano Cuesta Date: Thu, 28 Nov 2024 18:27:06 +0100 Subject: [PATCH] feat(schemaview): add is_simple_dict method Add method to SchemaView that helps checking if a slot is a simple dictionary or not. Signed-off-by: Silvano Cirujano Cuesta --- linkml_runtime/utils/schemaview.py | 41 +++++++++++++++++++ .../input/schemaview_is_inlined.yaml | 4 ++ tests/test_utils/test_schemaview.py | 21 ++++++++++ 3 files changed, 66 insertions(+) diff --git a/linkml_runtime/utils/schemaview.py b/linkml_runtime/utils/schemaview.py index b937964d..4f158589 100644 --- a/linkml_runtime/utils/schemaview.py +++ b/linkml_runtime/utils/schemaview.py @@ -1020,6 +1020,47 @@ def is_multivalued(self, slot_name: SlotDefinition) -> bool: induced_slot = self.induced_slot(slot_name) return True if induced_slot.multivalued else False + @lru_cache(None) + def is_simple_dict(self, slot_name: SlotDefinition) -> bool: + """ + returns True if slot is a simple dictionary, else returns False + :param slot_name: slot to test for simple dictionary + :return boolean: + """ + islot = self.induced_slot(slot_name) + if not islot.multivalued or not islot.inlined or islot.inlined_as_list: + return False + range_class = islot.range + if range_class is None: + return False + range_class_id_slot = self.get_identifier_slot(range_class.name, use_key=True) + if range_class_id_slot is None: + return False + + non_id_slots = [s for s in self.class_induced_slots(range_class.name) if s.name != range_class_id_slot.name] + if len(non_id_slots) == 1: + return True + elif len(non_id_slots) > 1: + candidate_non_id_slots = [] + for non_id_slot in non_id_slots: + if isinstance(non_id_slot.annotations, dict): + is_simple_dict_value = non_id_slot.annotations.get("simple_dict_value", False) + else: + is_simple_dict_value = getattr(non_id_slot.annotations, "simple_dict_value", False) + if is_simple_dict_value: + candidate_non_id_slots.append(non_id_slot) + if len(candidate_non_id_slots) == 1: + return True + else: + candidate_non_id_slots = [] + for non_id_slot in non_id_slots: + if non_id_slot.required: + candidate_non_id_slots.append(non_id_slot) + if len(candidate_non_id_slots) == 1: + return True + + return False + @lru_cache(None) def slot_is_true_for_metadata_property(self, slot_name: SlotDefinition, metadata_property: str) -> bool: """ diff --git a/tests/test_utils/input/schemaview_is_inlined.yaml b/tests/test_utils/input/schemaview_is_inlined.yaml index 349e2b60..3704b469 100644 --- a/tests/test_utils/input/schemaview_is_inlined.yaml +++ b/tests/test_utils/input/schemaview_is_inlined.yaml @@ -45,6 +45,10 @@ slots: an_integer: range: integer + + inlined_simple_dict: + range: ThingWithId + inlined_as_list: false # Pathological cases inlined_integer: diff --git a/tests/test_utils/test_schemaview.py b/tests/test_utils/test_schemaview.py index c9f28ff8..e3893742 100644 --- a/tests/test_utils/test_schemaview.py +++ b/tests/test_utils/test_schemaview.py @@ -865,6 +865,7 @@ def test_is_inlined(): ("inlined_thing_without_id", True), ("inlined_as_list_thing_without_id", True), ("an_integer", False), + ("inlined_simple_dict", True), ("inlined_integer", False), ("inlined_as_list_integer", False) ] @@ -873,6 +874,26 @@ def test_is_inlined(): assert sv.is_inlined(slot) == expected_result +def test_is_simple_dict(): + schema_path = os.path.join(INPUT_DIR, "schemaview_is_inlined.yaml") + sv = SchemaView(schema_path) + cases = [ + ("a_thing_with_id", False), + ("inlined_thing_with_id", False), + ("inlined_as_list_thing_with_id", False), + ("a_thing_without_id", False), + ("inlined_thing_without_id", False), + ("inlined_as_list_thing_without_id", False), + ("an_integer", False), + ("inlined_simple_dict", True), + ("inlined_integer", False), + ("inlined_as_list_integer", False) + ] + for slot_name, expected_result in cases: + slot = sv.get_slot(slot_name) + assert sv.is_simple_dict(slot) == expected_result + + def test_materialize_nonscalar_slot_usage(): schema_path = os.path.join(INPUT_DIR, "DJ_controller_schema.yaml") sv = SchemaView(schema_path)