Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object endpoint to find internal links for resources #145

Merged
merged 19 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7a592ea
Object endpoint returns system links.
recalcitrantsupplant Jun 25, 2023
aa61e61
Update documentation.
recalcitrantsupplant Jun 25, 2023
e493bdd
Update documentation.
recalcitrantsupplant Jun 26, 2023
6f19d4e
Object endpoint returns system links.
recalcitrantsupplant Jun 25, 2023
554352f
Update documentation.
recalcitrantsupplant Jun 25, 2023
c61ca27
Update documentation.
recalcitrantsupplant Jun 26, 2023
bd93347
Minimal tests working
recalcitrantsupplant Aug 9, 2023
934b174
Roll out link generation across catprez / vocprez / spaceprez
recalcitrantsupplant Aug 16, 2023
7219353
Test bugfix: best guess the prefixes are not actualy generated in a d…
recalcitrantsupplant Aug 16, 2023
31383e9
Bugfix for profiles test
recalcitrantsupplant Aug 16, 2023
d01e9df
Add prez:endpointComponentURI for parents in endpoints such that the …
recalcitrantsupplant Aug 18, 2023
578c272
Add TODOs.
recalcitrantsupplant Aug 22, 2023
aa082b5
Update tests to include endpointComponentURIs
recalcitrantsupplant Aug 22, 2023
ab067f3
Update profiles endpoints to include prez links
recalcitrantsupplant Aug 25, 2023
4d5a0ba
Remove inadvertently added OGC content
recalcitrantsupplant Aug 25, 2023
f144613
Change use of prez:endpointComponentURI -> dcterms:identifier "abc"^^…
recalcitrantsupplant Aug 29, 2023
3fba851
Fix bug with identifiers not being included for concepts
recalcitrantsupplant Aug 30, 2023
a719592
Update test responses, comment one test. One bugfix to get_classes
recalcitrantsupplant Aug 30, 2023
3e5927d
Update prez/cache.py
recalcitrantsupplant Aug 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions README-Dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,21 @@ using the properties listed below.
| provenance | dcterms:provenance | dcterms:source | altr-ext:hasExplanationPredicate |
| other | (None) | schema:color | altr-ext:otherAnnotationProps |

## High Level Sequence
## High Level Sequence `/object` endpoint

Prez provides a `/object` endpoint as an endpoint that supplies any information known about a given URI. If an annotated
mediatype is requested, prez will additionally provide all system links for endpoints which can render the object. The
high level sequence for this endpoint is as follows:

1. Get the URI for the object from the query string
2. Get the class(es) of the object from the triplestore
3. Use prez's reference data for endpoints to determine which endpoints can render this object, and, a template for
these endpoints, specifying any variables that need to be substituted (such as parent URIs).
4. Get the object information from the triplestore, using an open profile, and in parallel any system information needed
to construct the system links.
5. Return the response

## High Level Sequence listing and individual object endpoints

Prez follows the following logic to determine what information to return, based on a profile, and in what mediatype to return it.

Expand Down Expand Up @@ -287,8 +301,8 @@ SELECT ?profile ?title ?class (count(?mid) as ?distance) ?req_profile ?def_profi
WHERE {
VALUES ?class {<https://prez.dev/DatasetList>}
?class rdfs:subClassOf* ?mid .
?mid rdfs:subClassOf* ?general_class .
VALUES ?general_class { dcat:Dataset geo:FeatureCollection prez:FeatureCollectionList prez:FeatureList geo:Feature
?mid rdfs:subClassOf* ?base_class .
VALUES ?base_class { dcat:Dataset geo:FeatureCollection prez:FeatureCollectionList prez:FeatureList geo:Feature
skos:ConceptScheme skos:Concept skos:Collection prez:DatasetList prez:VocPrezCollectionList prez:SchemesList
prez:CatalogList dcat:Catalog dcat:Resource }
?profile altr-ext:constrainsClass ?class ;
Expand Down
15 changes: 15 additions & 0 deletions prez/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@
from prez.routers.identifier import router as identifier_router
from prez.services.app_service import healthcheck_sparql_endpoints, count_objects
from prez.services.app_service import populate_api_info, add_prefixes_to_prefix_graph
from prez.services.app_service import (
healthcheck_sparql_endpoints,
count_objects,
create_endpoints_graph,
populate_api_info,
add_prefixes_to_prefix_graph,
)
from prez.services.app_service import (
healthcheck_sparql_endpoints,
count_objects,
create_endpoints_graph,
populate_api_info,
add_prefixes_to_prefix_graph,
)
from prez.services.exception_catchers import (
catch_400,
catch_404,
Expand Down Expand Up @@ -109,6 +123,7 @@ async def app_startup():
await healthcheck_sparql_endpoints()
await get_all_search_methods()
await create_profiles_graph()
await create_endpoints_graph()
await count_objects()
await populate_api_info()
await add_prefixes_to_prefix_graph()
Expand Down
3 changes: 3 additions & 0 deletions prez/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
profiles_graph_cache = ConjunctiveGraph()
profiles_graph_cache.bind("prez", "https://prez.dev/")

endpoints_graph_cache = ConjunctiveGraph()
profiles_graph_cache.bind("prez", "https://prez.dev/")
recalcitrantsupplant marked this conversation as resolved.
Show resolved Hide resolved

prez_system_graph = Graph()
prez_system_graph.bind("prez", "https://prez.dev/")

Expand Down
11 changes: 6 additions & 5 deletions prez/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Settings(BaseSettings):
system_uri: Documentation property. An IRI for the Prez system as a whole. This value appears in the landing page RDF delivered by Prez ('/')
top_level_classes:
collection_classes:
general_classes:
base_classes:
log_level:
log_output:
cql_props: dict = {
Expand Down Expand Up @@ -52,7 +52,8 @@ class Settings(BaseSettings):
system_uri: Optional[str]
top_level_classes: Optional[dict]
collection_classes: Optional[dict]
general_classes: Optional[dict]
order_lists_by_label: bool = True
base_classes: Optional[dict]
prez_flavours: Optional[list] = ["SpacePrez", "VocPrez", "CatPrez", "ProfilesPrez"]
log_level = "INFO"
log_output = "stdout"
Expand Down Expand Up @@ -135,16 +136,16 @@ def populate_collection_classes(cls, values):
return values

@root_validator()
def populate_general_classes(cls, values):
def populate_base_classes(cls, values):
additional_classes = {
"SpacePrez": [GEO.Feature],
"VocPrez": [SKOS.Concept],
"CatPrez": [DCAT.Dataset],
"Profiles": [PROF.Profile],
}
values["general_classes"] = {}
values["base_classes"] = {}
for prez in list(additional_classes.keys()) + ["Profiles"]:
values["general_classes"][prez] = (
values["base_classes"][prez] = (
values["collection_classes"].get(prez) + additional_classes[prez]
)
return values
Expand Down
6 changes: 0 additions & 6 deletions prez/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
from prez.models.catprez_item import CatalogItem
from prez.models.catprez_listings import CatalogMembers
from prez.models.vocprez_item import VocabItem
from prez.models.vocprez_listings import VocabMembers
from prez.models.spaceprez_item import SpatialItem
from prez.models.spaceprez_listings import SpatialMembers
from prez.models.search_method import SearchMethod
52 changes: 0 additions & 52 deletions prez/models/catprez_item.py

This file was deleted.

28 changes: 0 additions & 28 deletions prez/models/catprez_listings.py

This file was deleted.

43 changes: 43 additions & 0 deletions prez/models/listing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Optional, FrozenSet

from pydantic import BaseModel, root_validator
from rdflib import URIRef, Literal, XSD

from prez.cache import endpoints_graph_cache
from prez.reference_data.prez_ns import ONT


class ListingModel(BaseModel):
uri: Optional[
URIRef
] = None # this is the URI of the focus object (if listing by membership)
endpoint_uri: Optional[URIRef] = None
selected_class: Optional[FrozenSet[URIRef]] = None
profile: Optional[URIRef] = None
top_level_listing: Optional[bool] = False

def __hash__(self):
return hash(self.uri)

@root_validator
def populate(cls, values):
endpoint_uri_str = values.get("endpoint_uri")
if endpoint_uri_str:
endpoint_uri = URIRef(endpoint_uri_str)
values["classes"] = frozenset(
[
klass
for klass in endpoints_graph_cache.objects(
endpoint_uri, ONT.deliversClasses, None
)
]
)
values["base_class"] = endpoints_graph_cache.value(
endpoint_uri, ONT.baseClass
)
tll_text = endpoints_graph_cache.value(endpoint_uri, ONT.isTopLevelEndpoint)
if tll_text == Literal("true", datatype=XSD.boolean):
values["top_level_listing"] = True
else:
values["top_level_listing"] = False
return values
41 changes: 41 additions & 0 deletions prez/models/object_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import Optional
from typing import Set

from pydantic import BaseModel, root_validator
from rdflib import URIRef, PROF

from prez.models.model_exceptions import ClassNotFoundException
from prez.reference_data.prez_ns import PREZ
from prez.services.curie_functions import get_uri_for_curie_id
from prez.services.model_methods import get_classes


class ObjectItem(BaseModel):
uri: Optional[URIRef] = None
object_curie: Optional[str] = None
classes: Optional[Set[URIRef]] = frozenset([PROF.Profile])
selected_class: Optional[URIRef] = None
profile: Optional[URIRef] = PREZ["profile/open"]
link_constructor: Optional[str] = None # TODO remove when no longer being used

def __hash__(self):
return hash(self.uri)

@root_validator
def populate(cls, values):
values["top_level_listing"] = False # this class is for objects, not listings.
uri_str = values.get("uri")
if uri_str:
values["uri"] = URIRef(uri_str)
else:
values["uri"] = get_uri_for_curie_id(values["object_curie"])
try:
values["classes"] = frozenset(
tup[1] for tup in get_classes([values["uri"]])
)
except ClassNotFoundException:
# TODO return a generic DESCRIBE on the object - we can't use any of prez's profiles/endpoints to render
# information about the object, but we can provide any RDF we have for it.
pass

return values
2 changes: 1 addition & 1 deletion prez/models/profiles_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ProfileItem(BaseModel):
link_constructor: str = "/profiles"
label: str = None

# general_class: Optional[URIRef] = None
# base_class: Optional[URIRef] = None
# url_path: Optional[str] = None
selected_class: Optional[URIRef] = None

Expand Down
10 changes: 5 additions & 5 deletions prez/models/profiles_listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class ProfilesMembers(BaseModel):
url_path: str
uri: Optional[URIRef] = None
general_class: Optional[URIRef]
base_class: Optional[URIRef]
classes: Optional[FrozenSet[URIRef]] = frozenset([PREZ.ProfilesList])
selected_class: Optional[URIRef] = None
link_constructor: Optional[str]
Expand All @@ -20,15 +20,15 @@ class ProfilesMembers(BaseModel):
def populate(cls, values):
url_path = values.get("url_path")
if url_path.startswith("/v/"):
values["general_class"] = PREZ.VocPrezProfile
values["base_class"] = PREZ.VocPrezProfile
values["link_constructor"] = "/v/profiles"
elif url_path.startswith("/c/"):
values["general_class"] = PREZ.CatPrezProfile
values["base_class"] = PREZ.CatPrezProfile
values["link_constructor"] = "/c/profiles"
elif url_path.startswith("/s/"):
values["general_class"] = PREZ.SpacePrezProfile
values["base_class"] = PREZ.SpacePrezProfile
values["link_constructor"] = "/s/profiles"
else:
values["general_class"] = PROF.Profile
values["base_class"] = PROF.Profile
values["link_constructor"] = "/profiles"
return values
4 changes: 4 additions & 0 deletions prez/models/search_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
from pydantic import BaseModel
from rdflib import URIRef, Namespace, Literal

from pydantic import BaseConfig

BaseConfig.arbitrary_types_allowed = True

PREZ = Namespace("https://prez.dev/")


Expand Down
Loading