Skip to content

Commit

Permalink
Add back in relative properties - using other annotations for broader…
Browse files Browse the repository at this point in the history
…/narrower is a bit blunt. It includes a lot of unintended annotations as it applies at the profile level. The only other option would be to interpret a combination of sequence and inverse path as concepts are skos:inScheme the focus node (inverse path), and the additional properties we want on them (broader, narrower) are two steps away from the focus node. This is trivial in SPARQL but probably requires more careful design in SHACL.
  • Loading branch information
recalcitrantsupplant committed Jun 16, 2023
1 parent 8bcd0f1 commit 2fcd93a
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 2 deletions.
7 changes: 6 additions & 1 deletion prez/reference_data/profiles/vocprez_default_profiles.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ prez:VocPrezProfile
dcterms:identifier "vocpub"^^xsd:token ;
dcterms:title "VocPub" ;
altr-ext:hasLabelPredicate skos:prefLabel ;
altr-ext:otherAnnotationProps schema:color , skos:broader , skos:narrower ;
altr-ext:otherAnnotationProps schema:color ;
altr-ext:constrainsClass
skos:ConceptScheme ,
skos:Concept ,
Expand Down Expand Up @@ -97,6 +97,11 @@ prez:VocPrezProfile
sh:targetClass skos:ConceptScheme ;
altr-ext:inboundChildren skos:inScheme ;
] ;
altr-ext:hasNodeShape [
a sh:NodeShape ;
sh:targetClass skos:ConceptScheme ;
altr-ext:relativeProperties skos:broader , skos:narrower ;
] ;
altr-ext:hasNodeShape [
a sh:NodeShape ;
sh:targetClass skos:Concept ;
Expand Down
61 changes: 60 additions & 1 deletion prez/sparql/objects_listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def generate_listing_construct(
inbound_parents,
outbound_children,
outbound_parents,
relative_properties,
) = get_listing_predicates(profile, focus_item.selected_class)
if (
focus_item.uri
Expand All @@ -64,6 +65,8 @@ def generate_listing_construct(
and not inbound_parents
and not outbound_children
and not outbound_parents
# do not need to check relative properties - they will only be used if one of the inbound/outbound parent/child
# relations are defined
):
log.warning(
f"Requested listing of objects related to {focus_item.uri}, however the profile {profile} does not"
Expand Down Expand Up @@ -95,6 +98,8 @@ def generate_listing_construct(
{f'{uri_or_tl_item} ?outbound_parents ?parent_item .{chr(10)}' if outbound_parents else ""}\
{f'?inbound_child_s ?inbound_child {uri_or_tl_item} .{chr(10)}' if inbound_children else ""}\
{f'?inbound_parent_s ?inbound_parent {uri_or_tl_item} .{chr(10)}' if inbound_parents else ""}\
{generate_relative_properties("construct", relative_properties, inbound_children, inbound_parents,
outbound_children, outbound_parents)}\
{f"{uri_or_tl_item} ?p ?o ." if include_predicates else ""}\
}}
WHERE {{
Expand All @@ -104,6 +109,8 @@ def generate_listing_construct(
{sequence_construct_where}\
{generate_outbound_predicates(uri_or_tl_item, outbound_children, outbound_parents)} \
{generate_inbound_predicates(uri_or_tl_item, inbound_children, inbound_parents)} {chr(10)} \
{generate_relative_properties("select", relative_properties, inbound_children, inbound_parents,
outbound_children, outbound_parents)}\
{{
SELECT ?top_level_item
WHERE {{
Expand Down Expand Up @@ -199,6 +206,43 @@ def search_query_construct():
)


def generate_relative_properties(
construct_select,
relative_properties,
in_children,
in_parents,
out_children,
out_parents,
):
"""
Generate the relative properties construct or select for a listing query.
i.e. properties on nodes related to the focus item NOT the focus item itself
"""
if not relative_properties:
return ""
rel_string = ""
kvs = {
"ic": in_children,
"ip": in_parents,
"oc": out_children,
"op": out_parents,
}
other_kvs = {
"ic": "inbound_child_s",
"ip": "inbound_parent_s",
"oc": "child_item",
"op": "parent_item",
}
for k, v in kvs.items():
if v:
if construct_select == "select":
rel_string += f"""OPTIONAL {{ """
rel_string += f"""?{other_kvs[k]} ?rel_{k}_props ?rel_{k}_val .\n"""
if construct_select == "select":
rel_string += f"""VALUES ?rel_{k}_props {{ {" ".join('<' + str(pred) + '>' for pred in relative_properties)} }} }}\n"""
return rel_string


def generate_outbound_predicates(uri_or_tl_item, outbound_children, outbound_parents):
where = ""
if outbound_children:
Expand Down Expand Up @@ -597,10 +641,14 @@ def get_listing_predicates(profile, selected_class):
the predicate dcterms:hasPart
- outbound parents, for example where the object of interest is a Concept, and is linked to Concept Scheme(s) via
the predicate skos:inScheme
- relative properties, properties of the parent/child objects that should also be returned. For example, if the
focus object is a Concept Scheme, and the predicate skos:inScheme is used to link from Concept(s) (using
altr-ext:inboundChildren) then specifying skos:broader as a relative property will cause the broader concepts to
be returned for each concept
"""
shape_bns = get_relevant_shape_bns_for_profile(selected_class, profile)
if not shape_bns:
return [], [], [], []
return [], [], [], [], []
inbound_children = [
i[2]
for i in profiles_graph_cache.triples_choices(
Expand Down Expand Up @@ -641,11 +689,22 @@ def get_listing_predicates(profile, selected_class):
)
)
]
relative_properties = [
i[2]
for i in profiles_graph_cache.triples_choices(
(
shape_bns,
ALTREXT.relativeProperties,
None,
)
)
]
return (
inbound_children,
inbound_parents,
outbound_children,
outbound_parents,
relative_properties,
)


Expand Down

0 comments on commit 2fcd93a

Please sign in to comment.