Skip to content

Commit c9972b4

Browse files
authored
Merge pull request #291 from linkml/pv_descendants
add schemaview method to return permissible_value descendants and children
2 parents bfec27d + 641c7b5 commit c9972b4

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

linkml_runtime/utils/schemaview.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,40 @@ def permissible_value_parent(self, permissible_value: str, enum_name: ENUM_NAME)
618618
else:
619619
return []
620620

621+
@lru_cache()
622+
def permissible_value_children(self, permissible_value: str, enum_name: ENUM_NAME) -> Union[
623+
str, PermissibleValueText, None, ValueError]:
624+
"""
625+
:param enum_name: parent enum name
626+
:param permissible_value: permissible value
627+
:return: all direct child permissible values (is_a)
628+
629+
CAT:
630+
LION:
631+
is_a: CAT
632+
ANGRY_LION:
633+
is_a: LION
634+
TABBY:
635+
is_a: CAT
636+
BIRD:
637+
EAGLE:
638+
is_a: BIRD
639+
640+
"""
641+
642+
enum = self.get_enum(enum_name, strict=True)
643+
children = []
644+
if enum:
645+
if permissible_value in enum.permissible_values:
646+
pv = enum.permissible_values[permissible_value]
647+
for isapv in enum.permissible_values:
648+
isapv_entity = enum.permissible_values[isapv]
649+
if isapv_entity.is_a and pv.text == isapv_entity.is_a:
650+
children.append(isapv)
651+
return children
652+
else:
653+
raise ValueError(f'No such enum as "{enum_name}"')
654+
621655
@lru_cache()
622656
def slot_parents(self, slot_name: SLOT_NAME, imports=True, mixins=True, is_a=True) -> List[SlotDefinitionName]:
623657
"""
@@ -719,6 +753,22 @@ def permissible_value_ancestors(self, permissible_value_text: str,
719753
reflexive=reflexive,
720754
depth_first=depth_first)
721755

756+
@lru_cache()
757+
def permissible_value_descendants(self, permissible_value_text: str,
758+
enum_name: ENUM_NAME,
759+
reflexive=True,
760+
depth_first=True) -> List[str]:
761+
"""
762+
Closure of permissible_value_children method
763+
:enum
764+
"""
765+
766+
767+
return _closure(lambda x: self.permissible_value_children(x, enum_name),
768+
permissible_value_text,
769+
reflexive=reflexive,
770+
depth_first=depth_first)
771+
722772
@lru_cache()
723773
def enum_ancestors(self, enum_name: ENUM_NAME, imports=True, mixins=True, reflexive=True, is_a=True,
724774
depth_first=True) -> List[EnumDefinitionName]:
@@ -931,7 +981,6 @@ def get_uri(self, element: Union[ElementName, Element], imports=True, expand=Fal
931981
if schema == None:
932982
raise ValueError(f'Cannot find {e.from_schema} in schema_map')
933983
else:
934-
logging.warning(f'from_schema not populated for element {e.name}')
935984
schema = self.schema_map[self.in_schema(e.name)]
936985
pfx = schema.default_prefix
937986
uri = f'{pfx}:{e_name}'

tests/test_utils/input/kitchen_sink_noimports.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ enums:
364364
is_a: OtherEnum
365365
permissible_values:
366366
CAT:
367+
TABBY:
368+
is_a: CAT
367369
LION:
368370
is_a: CAT
369371
ANGRY_LION:

tests/test_utils/test_schemaview.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,26 @@ def test_all_aliases(self):
4747

4848
def test_schemaview_enums(self):
4949
view = SchemaView(SCHEMA_NO_IMPORTS)
50+
with self.assertRaises(ValueError):
51+
view.permissible_value_parent("not_a_pv", "not_an_enum")
5052
for en, e in view.all_enums().items():
5153
if e.name == "Animals":
5254
for pv, v in e.permissible_values.items():
5355
if pv == "CAT":
5456
self.assertEqual(view.permissible_value_parent(pv, e.name), None)
5557
self.assertEqual(view.permissible_value_ancestors(pv, e.name), ['CAT'])
58+
self.assertIn("LION", view.permissible_value_descendants(pv, e.name))
59+
self.assertIn("ANGRY_LION", view.permissible_value_descendants(pv, e.name))
60+
self.assertIn("TABBY", view.permissible_value_descendants(pv, e.name))
61+
self.assertIn("TABBY", view.permissible_value_children(pv, e.name))
62+
self.assertIn("LION", view.permissible_value_children(pv, e.name))
63+
self.assertNotIn("EAGLE", view.permissible_value_descendants(pv, e.name))
64+
if pv == "LION":
65+
self.assertIn("ANGRY_LION", view.permissible_value_children(pv, e.name))
5666
if pv == "ANGRY_LION":
5767
self.assertEqual(view.permissible_value_parent(pv, e.name), ['LION'])
5868
self.assertEqual(view.permissible_value_ancestors(pv, e.name), ['ANGRY_LION', 'LION', 'CAT'])
69+
self.assertEquals(["ANGRY_LION"], view.permissible_value_descendants(pv, e.name))
5970
for cn, c in view.all_classes().items():
6071
if c.name == "Adult":
6172
self.assertEqual(view.class_ancestors(c.name), ['Adult', 'Person', 'HasAliases', 'Thing'])

0 commit comments

Comments
 (0)