From e4af3d31c999a3ef78d1d0e0fbd439fbc3393c12 Mon Sep 17 00:00:00 2001 From: David Habgood Date: Thu, 15 Aug 2024 12:21:56 +1000 Subject: [PATCH] Add profiles profile. (#250) * Add profiles profile. Add connegp debug print output; adds tabulate development dependency. Reduce pagination count limit to 100. * Fix profiles so tests pass. * fix: print -> log.debug --- poetry.lock | 16 +++++++- prez/config.py | 2 +- .../profiles/ogc_records_profile.ttl | 10 ++++- .../profiles/prez_default_profiles.ttl | 41 ++++++++++++++++++- prez/services/connegp_service.py | 25 +++++++++++ pyproject.toml | 2 + tests/test_endpoints_profiles.py | 2 +- 7 files changed, 92 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 89b66f19..a56f70b8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1708,6 +1708,20 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + [[package]] name = "toml" version = "0.10.2" @@ -2125,4 +2139,4 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "d1789d9e68944a6885dd243d4d064ad1b98649b5277d7fa1a790dad1a0926182" +content-hash = "f825e1e30c6eb383bbbf9a78c6e2d826f01f3111602b59b05c42b8667878bf85" diff --git a/prez/config.py b/prez/config.py index b1df20ac..a3f507b4 100755 --- a/prez/config.py +++ b/prez/config.py @@ -39,7 +39,7 @@ class Settings(BaseSettings): curie_separator: str = ":" system_uri: Optional[str] = f"{protocol}://{host}:{port}" order_lists_by_label: bool = True - listing_count_limit: int = 1000 + listing_count_limit: int = 100 label_predicates: Optional[List[URIRef]] = [ SKOS.prefLabel, DCTERMS.title, diff --git a/prez/reference_data/profiles/ogc_records_profile.ttl b/prez/reference_data/profiles/ogc_records_profile.ttl index 81d43f4d..81f7eafd 100644 --- a/prez/reference_data/profiles/ogc_records_profile.ttl +++ b/prez/reference_data/profiles/ogc_records_profile.ttl @@ -23,9 +23,15 @@ prez:OGCRecordsProfile dcterms:title "OGC Profile" ; altr-ext:constrainsClass prez:CatPrez ; altr-ext:hasDefaultResourceFormat "text/anot+turtle" ; + altr-ext:hasResourceFormat + "application/json" , + "application/ld+json" , + "application/rdf+xml" , + "text/anot+turtle" , + "text/turtle" ; altr-ext:hasNodeShape [ a sh:NodeShape ; - sh:targetClass prof:Profile , dcat:Catalog , dcat:Resource , skos:Concept , geo:Feature , geo:FeatureCollection + sh:targetClass dcat:Catalog , dcat:Resource , skos:Concept , geo:Feature , geo:FeatureCollection , skos:Collection , rdf:Resource , prez:SearchResult , prez:CQLObjectList ; altr-ext:hasDefaultProfile prez:OGCListingProfile ] , [ @@ -38,7 +44,7 @@ prez:OGCRecordsProfile altr-ext:hasDefaultProfile prez:OGCSchemesObjectProfile ] , [ a sh:NodeShape ; - sh:targetClass prof:Profile , dcat:Catalog , dcat:Resource , skos:Concept , geo:Feature , geo:FeatureCollection + sh:targetClass dcat:Catalog , dcat:Resource , skos:Concept , geo:Feature , geo:FeatureCollection , skos:Collection , rdf:Resource ; altr-ext:hasDefaultProfile prez:OGCItemProfile ] diff --git a/prez/reference_data/profiles/prez_default_profiles.ttl b/prez/reference_data/profiles/prez_default_profiles.ttl index b55e9402..479bdcac 100644 --- a/prez/reference_data/profiles/prez_default_profiles.ttl +++ b/prez/reference_data/profiles/prez_default_profiles.ttl @@ -19,8 +19,14 @@ PREFIX xsd: a prof:Profile ; dcterms:identifier "prez"^^xsd:token ; dcterms:description "A profile for the Prez Linked Data API" ; - skos:prefLabel "Prez profile" ; + dcterms:title "Prez profile" ; altr-ext:hasDefaultResourceFormat "text/anot+turtle" ; + altr-ext:hasResourceFormat + "application/json" , + "application/ld+json" , + "application/rdf+xml" , + "text/anot+turtle" , + "text/turtle" ; altr-ext:hasNodeShape [ a sh:NodeShape ; sh:targetClass prez:SPARQLQuery ; @@ -29,6 +35,10 @@ PREFIX xsd: a sh:NodeShape ; sh:targetClass prez:AltProfilesList ; altr-ext:hasDefaultProfile + ] , [ + a sh:NodeShape ; + sh:targetClass prof:Profile ; + altr-ext:hasDefaultProfile ] . @@ -138,3 +148,32 @@ altr-ext:alt-profile ) ] ; . + + + a prof:Profile , prez:ObjectProfile , prez:ListingProfile ; + dcterms:description "A Profile for displaying profiles" ; + dcterms:identifier "prof"^^xsd:token ; + dcterms:title "Profiles Profile" ; + altr-ext:hasDefaultResourceFormat "text/anot+turtle" ; + altr-ext:hasResourceFormat + "application/ld+json" , + "application/anot+ld+json" , + "application/rdf+xml" , + "text/anot+turtle" , + "text/turtle" ; + altr-ext:constrainsClass + prof:Profile ; + sh:property [ + sh:path ( + sh:union ( + rdf:type + altr-ext:hasResourceFormat + altr-ext:hasDefaultResourceFormat + dcterms:description + dcterms:title + dcterms:identifier + ) + ) + ] ; + shext:bnode-depth 20 ; + . diff --git a/prez/services/connegp_service.py b/prez/services/connegp_service.py index d3955a58..87870636 100755 --- a/prez/services/connegp_service.py +++ b/prez/services/connegp_service.py @@ -6,6 +6,7 @@ from pydantic import BaseModel from rdflib import Graph, Namespace, URIRef +from prez.config import settings from prez.exceptions.model_exceptions import NoProfilesException from prez.repositories.base import Repo from prez.services.curie_functions import get_curie_id_for_uri, get_uri_for_curie_id @@ -302,4 +303,28 @@ async def _do_query(self, query: str) -> tuple[Graph, list]: response = await self.system_repo.send_queries([], [(None, query)]) if not response[1][0][1]: raise NoProfilesException(self.classes) + + if settings.log_level == "DEBUG": + from tabulate import tabulate + table_data = [ + [ + item['profile']['value'], + item['title']['value'], + item['class']['value'], + item['distance']['value'], + item['def_profile']['value'], + item['format']['value'], + item['req_format']['value'], + item['def_format']['value'], + ] + for item in response[1][0][1] + ] + + # Define headers + headers = ["Profile", "Title", "Class", "Distance", "Default Profile", "Format", "Requested Format", + "Default Format"] + + # Render as a table + log.debug(tabulate(table_data, headers=headers, tablefmt="grid")) + return response diff --git a/pyproject.toml b/pyproject.toml index 46fbf721..7fca1e6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,8 @@ scalene = "^1.5.18" python-dotenv = "^1.0.0" pyoxigraph = "^0.3.19" coverage = "^7.3.2" +tabulate = "^0.9.0" + [build-system] requires = ["poetry-core>=1.9.0"] diff --git a/tests/test_endpoints_profiles.py b/tests/test_endpoints_profiles.py index f7501f5c..c237c222 100755 --- a/tests/test_endpoints_profiles.py +++ b/tests/test_endpoints_profiles.py @@ -3,7 +3,7 @@ def test_profile(client_no_override): - r = client_no_override.get("/profiles") + r = client_no_override.get("/profiles?per_page=50") g = Graph().parse(data=r.text) assert (URIRef("https://prez.dev/profile/prez"), RDF.type, PROF.Profile) in g