diff --git a/buildingmotif/utils.py b/buildingmotif/utils.py index fe8eb711..98f3d7e6 100644 --- a/buildingmotif/utils.py +++ b/buildingmotif/utils.py @@ -12,8 +12,8 @@ from rdflib.compare import _TripleCanonicalizer from rdflib.paths import ZeroOrOne from rdflib.term import Node -from sqlalchemy.exc import NoResultFound +from buildingmotif.database.errors import LibraryNotFound from buildingmotif.namespaces import OWL, PARAM, RDF, SH, XSD, bind_prefixes if TYPE_CHECKING: @@ -63,7 +63,7 @@ def _guarantee_unique_template_name(library: "Library", name: str) -> str: while library.get_template_by_name(name): name = f"{original_name}_{idx}" idx += 1 - except NoResultFound: + except LibraryNotFound: # this means that the template does not exist and we can use the original name pass return name @@ -538,6 +538,25 @@ def _inline_sh_and(sg: Graph): sg.add((parent, p, o)) +def _inline_sh_qualified_value_shape(sg: Graph): + """ + This detects the use of 'sh:qualifiedValueShape' on SHACL PropertyShapes and inlines + all of the included shapes + """ + q = """ + PREFIX sh: + SELECT ?parent ?child WHERE { + ?parent a sh:PropertyShape ; + sh:qualifiedValueShape ?child . + }""" + for row in sg.query(q): + parent, child = row # type: ignore + sg.remove((parent, SH["qualifiedValueShape"], child)) + pos = sg.predicate_objects(child) + for (p, o) in pos: + sg.add((parent, p, o)) + + def rewrite_shape_graph(g: Graph) -> Graph: """ Rewrites the input graph to make the resulting validation report more useful. @@ -555,6 +574,7 @@ def rewrite_shape_graph(g: Graph) -> Graph: _inline_sh_and(sg) # make sure to handle sh:node *after* sh:and _inline_sh_node(sg) + _inline_sh_qualified_value_shape(sg) return sg