diff --git a/examples/notebooks/use-cases/BBP KG Creating and Modifying Schemas.ipynb b/examples/notebooks/use-cases/BBP KG Creating and Modifying Schemas.ipynb new file mode 100644 index 00000000..3764c948 --- /dev/null +++ b/examples/notebooks/use-cases/BBP KG Creating and Modifying Schemas.ipynb @@ -0,0 +1,254 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "b14744df-3567-40f3-bb0c-06c6767ab695", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "import uuid\n", + "\n", + "from kgforge.core import KnowledgeGraphForge\n", + "from kgforge.specializations.resources import Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "72596414-c9ac-4312-a621-5bd4fdc3477b", + "metadata": {}, + "outputs": [], + "source": [ + "import getpass\n", + "TOKEN = getpass.getpass()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c124caae-b9ae-4eb4-8d8e-bc437d28d3e6", + "metadata": {}, + "outputs": [], + "source": [ + "BUCKET = \"dke/kgforge\"\n", + "\n", + "forge = KnowledgeGraphForge(\"../use-cases/prod-forge-nexus.yml\",\n", + " bucket=BUCKET,\n", + " token=TOKEN\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "29bceff6-d7b4-4fda-9cc3-fe1923c0f865", + "metadata": {}, + "outputs": [], + "source": [ + "def make_id(i):\n", + " return f\"{forge._store.endpoint}/schemas/{forge._store.bucket}/dummy_schema_{i}\"\n", + "\n", + "def create_schema(i):\n", + " payload = {\n", + " \"context\": \"https://incf.github.io/neuroshapes/contexts/schema.json\",\n", + " \"id\": make_id(i),\n", + " \"type\": \"Schema\",\n", + " \"imports\": [\n", + " \"https://neuroshapes.org/commons/entity\"\n", + " ],\n", + " \"shapes\": [\n", + " {\n", + " \"@id\": f\"{make_id(i)}/shapes/DummyShape\",\n", + " \"@type\": \"NodeShape\",\n", + " \"node\": \"https://neuroshapes.org/commons/entity/shapes/EntityShape\",\n", + " }\n", + " ]\n", + " }\n", + " resource = forge.from_json(payload)\n", + " forge.register(resource, schema_id=forge._store.service.SHACL_SCHEMA)\n", + " return resource" + ] + }, + { + "cell_type": "markdown", + "id": "e79f1807-dbfa-4cca-ab08-4809c9962517", + "metadata": {}, + "source": [ + "## 1. Create dummy schemas" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9a022c3d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " _register_one\n", + " True\n", + " _register_one\n", + " True\n", + " _register_one\n", + " True\n" + ] + } + ], + "source": [ + "dummies = [create_schema(uuid.uuid4()) for j in range(3)]" + ] + }, + { + "cell_type": "markdown", + "id": "d10b21d5-212b-48cc-b4a2-7ef2f739b2ed", + "metadata": {}, + "source": [ + "## 2. Retrieve the reciently created schemas using the ids and tag them" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "515b3e19", + "metadata": {}, + "outputs": [], + "source": [ + "retrieved = [forge.retrieve(dummy.id) for dummy in dummies]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2c5594ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " context: https://incf.github.io/neuroshapes/contexts/schema.json\n", + " id: https://bbp.epfl.ch/nexus/v1/schemas/dke/kgforge/dummy_schema_7fab9433-af89-43eb-878a-607389240256\n", + " type: Schema\n", + " imports:\n", + " [\n", + " https://neuroshapes.org/commons/entity\n", + " ]\n", + " shapes:\n", + " [\n", + " {\n", + " id: https://bbp.epfl.ch/nexus/v1/schemas/dke/kgforge/dummy_schema_7fab9433-af89-43eb-878a-607389240256/shapes/DummyShape\n", + " type: NodeShape\n", + " node: https://neuroshapes.org/commons/entity/shapes/EntityShape\n", + " }\n", + " ]\n", + "}\n" + ] + } + ], + "source": [ + "print(retrieved[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "efb9cb41", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "https://bluebrain.github.io/nexus/schemas/shacl-20170720.ttl\n" + ] + } + ], + "source": [ + "print(retrieved[0]._store_metadata['_constrainedBy'])" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a4f95633", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " _tag_one\n", + " True\n", + " _tag_one\n", + " True\n", + " _tag_one\n", + " True\n" + ] + } + ], + "source": [ + "for r in retrieved:\n", + " forge.tag(r, 't01')" + ] + }, + { + "cell_type": "markdown", + "id": "551f3fdc-4760-4c6b-861b-6ac085f27e00", + "metadata": {}, + "source": [ + "## 3. Deprecate the schemas" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e6859730", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " _deprecate_one\n", + " True\n", + " _deprecate_one\n", + " True\n", + " _deprecate_one\n", + " True\n" + ] + } + ], + "source": [ + "for r in retrieved:\n", + " forge.deprecate(r)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "dev-forge", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/kgforge/specializations/stores/bluebrain_nexus.py b/kgforge/specializations/stores/bluebrain_nexus.py index 366b852f..19c5de74 100644 --- a/kgforge/specializations/stores/bluebrain_nexus.py +++ b/kgforge/specializations/stores/bluebrain_nexus.py @@ -164,9 +164,8 @@ def register_callback(task: Task): def _register_one(self, resource: Resource, schema_id: str) -> None: method, url, resource, exception_, headers, params, payload = ( - prepare_methods.prepare_create(service=self.service, resource=resource) + prepare_methods.prepare_create(service=self.service, resource=resource, schema_id=schema_id) ) - response = requests.request( method=method, url=url, diff --git a/kgforge/specializations/stores/nexus/prepare_methods.py b/kgforge/specializations/stores/nexus/prepare_methods.py index 461e1e2e..5dddbb9d 100644 --- a/kgforge/specializations/stores/nexus/prepare_methods.py +++ b/kgforge/specializations/stores/nexus/prepare_methods.py @@ -18,6 +18,20 @@ # but since the service is available, the param can be retrieved here +def _make_url(service: Service, + schema_id: str, + identifier: str): + if schema_id == service.SHACL_SCHEMA: + url = Service.add_resource_id_to_endpoint( + service.url_schemas, resource_id=identifier + ) + else: + url = Service.add_schema_and_id_to_endpoint( + service.url_resources, schema_id=schema_id, resource_id=identifier + ) + return url + + def prepare_create( service: Service, resource: Resource, @@ -30,9 +44,7 @@ def prepare_create( identifier = resource.get_identifier() - url = Service.add_schema_and_id_to_endpoint( - service.url_resources, schema_id=schema_id, resource_id=identifier - ) + url = _make_url(service, schema_id, identifier) context = service.model_context or service.context @@ -86,9 +98,7 @@ def _prepare_uri( if schema_id == service.UNCONSTRAINED_SCHEMA and not keep_unconstrained: schema_id = None - url = Service.add_schema_and_id_to_endpoint( - service.url_resources, schema_id, resource_id=resource.id - ) + url = _make_url(service, schema_id, resource.id) rev = resource._store_metadata._rev params = {"rev": rev} diff --git a/kgforge/specializations/stores/nexus/service.py b/kgforge/specializations/stores/nexus/service.py index ab9fdc01..e0e8e64e 100644 --- a/kgforge/specializations/stores/nexus/service.py +++ b/kgforge/specializations/stores/nexus/service.py @@ -69,6 +69,7 @@ class Service: UNCONSTRAINED_SCHEMA = ( "https://bluebrain.github.io/nexus/schemas/unconstrained.json" ) + SHACL_SCHEMA = "https://bluebrain.github.io/nexus/schemas/shacl-20170720.ttl" SPARQL_ENDPOINT_TYPE = "sparql" ELASTIC_ENDPOINT_TYPE = "elastic" @@ -174,6 +175,7 @@ def __init__( self.url_files = Service.make_endpoint(self.endpoint, "files", org, prj) self.url_resources = Service.make_endpoint(self.endpoint, "resources", org, prj) self.url_resolver = Service.make_endpoint(self.endpoint, "resolvers", org, prj) + self.url_schemas = Service.make_endpoint(self.endpoint, "schemas", org, prj) self.metadata_context = Context( recursive_resolve( @@ -242,6 +244,13 @@ def make_endpoint( (endpoint, endpoint_type, quote_plus(organisation), quote_plus(project)) ) + @staticmethod + def add_resource_id_to_endpoint(endpoint: str, resource_id: Optional[str]): + if resource_id: + return "/".join([endpoint, quote_plus(resource_id)]) + else: + return endpoint + @staticmethod def add_schema_and_id_to_endpoint( endpoint: str, schema_id: Optional[str], resource_id: Optional[str] diff --git a/tests/specializations/stores/test_bluebrain_nexus.py b/tests/specializations/stores/test_bluebrain_nexus.py index a47817c2..b931d4f5 100644 --- a/tests/specializations/stores/test_bluebrain_nexus.py +++ b/tests/specializations/stores/test_bluebrain_nexus.py @@ -304,6 +304,14 @@ def test_to_resource(nexus_store, registered_building, building_jsonld, store_co ("/".join((NEXUS, "resources", BUCKET, quote_plus("_"), "{}", "tags"))), id="tag-unconstrained", ), + pytest.param( + (Service.SHACL_SCHEMA), + (Service.SHACL_SCHEMA), + ({"rev": 1}), + ("/".join((NEXUS, "schemas", BUCKET, "{}"))), + ("/".join((NEXUS, "schemas", BUCKET, "{}", "tags"))), + id="tag-schema", + ), ], ) def test_prepare_tag_uri(