diff --git a/dsms/core/utils.py b/dsms/core/utils.py index 54d1e00..33bdd5b 100644 --- a/dsms/core/utils.py +++ b/dsms/core/utils.py @@ -2,16 +2,17 @@ import re from typing import Any from urllib.parse import urljoin +from uuid import UUID import requests from requests import Response -def _kitem_id2uri(kitem_id: str) -> str: +def _kitem_id2uri(kitem_id: UUID) -> str: "Convert a kitem id in the DSMS to the full resolvable URI" from dsms import Context - return f"{Context.dsms.config.host_url}/{kitem_id}" + return urljoin(str(Context.dsms.config.host_url), str(kitem_id)) def _uri2kitem_idi(uri: str) -> str: diff --git a/dsms/knowledge/kitem.py b/dsms/knowledge/kitem.py index 4319d7c..b6445d3 100644 --- a/dsms/knowledge/kitem.py +++ b/dsms/knowledge/kitem.py @@ -404,7 +404,9 @@ def dsms(cls, value: "DSMS") -> None: @property def subgraph(cls) -> Optional[Graph]: """Getter for Subgraph""" - return _get_subgraph(cls.id, cls.dsms.config.kitem_repo) + return _get_subgraph( + cls.id, cls.dsms.config.kitem_repo, is_kitem_id=True + ) @property def context(cls) -> "Context": diff --git a/dsms/knowledge/sparql_interface/sparql_interface.py b/dsms/knowledge/sparql_interface/sparql_interface.py index 3ba9c43..f3073bf 100644 --- a/dsms/knowledge/sparql_interface/sparql_interface.py +++ b/dsms/knowledge/sparql_interface/sparql_interface.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING +from dsms.knowledge.sparql_interface.subgraph import Subgraph from dsms.knowledge.sparql_interface.utils import ( _add_rdf, _sparql_query, @@ -21,6 +22,7 @@ class SparqlInterface: def __init__(self, dsms): """Initalize the Sparql interface""" self._dsms: "DSMS" = dsms + self._subgraph = Subgraph(dsms) def query( self, query: str, repository: str = "knowledge" @@ -38,10 +40,15 @@ def update( file_or_pathlike, self._dsms.config.encoding, repository ) - def add_rdf( + def insert( self, file_or_pathlike: "Union[str, TextIO]", repository: str = "knowledge", ) -> None: """Upload RDF to triplestore from local file""" _add_rdf(file_or_pathlike, self._dsms.config.encoding, repository) + + @property + def subgraph(cls) -> Subgraph: + """Subgraph interface for DSMS""" + return cls._subgraph diff --git a/dsms/knowledge/sparql_interface/subgraph.py b/dsms/knowledge/sparql_interface/subgraph.py new file mode 100644 index 0000000..bcefe20 --- /dev/null +++ b/dsms/knowledge/sparql_interface/subgraph.py @@ -0,0 +1,44 @@ +"""DSMS Subgraph interface""" + +from typing import TYPE_CHECKING + +from dsms.knowledge.sparql_interface.utils import ( + _create_subgraph, + _delete_subgraph, + _get_subgraph, + _update_subgraph, +) + +if TYPE_CHECKING: + from rdflib import Graph + + from dsms import DSMS + + +class Subgraph: + """Subgraph interface for DSMS""" + + def __init__(self, dsms): + """Initalize the Sparql interface""" + self._dsms: "DSMS" = dsms + + def update(self, graph: "Graph", repository: str = "knowledge") -> None: + """Update a subgraph in the DSMS""" + _update_subgraph(graph, self._dsms.config.encoding, repository) + + def create(self, graph: "Graph", repository: str = "knowledge") -> None: + """Create a subgraph in the DSMS""" + _create_subgraph(graph, self._dsms.config.encoding, repository) + + def delete(self, identifier: str, repository: str = "knowledge") -> None: + """Delete a subgraph in the DSMS""" + _delete_subgraph(identifier, self._dsms.config.encoding, repository) + + def get( + self, + identifier: str, + repository: str = "knowledge", + is_kitem_id: bool = False, + ) -> None: + """Get a subgraph in the DSMS""" + _get_subgraph(identifier, repository, is_kitem_id) diff --git a/dsms/knowledge/sparql_interface/utils.py b/dsms/knowledge/sparql_interface/utils.py index d58b5fb..24fd0bc 100644 --- a/dsms/knowledge/sparql_interface/utils.py +++ b/dsms/knowledge/sparql_interface/utils.py @@ -7,7 +7,7 @@ from dsms.core.utils import _kitem_id2uri, _perform_request if TYPE_CHECKING: - from typing import Any, Dict, TextIO, Union + from typing import Any, Dict, Optional, TextIO, Union def _sparql_query(query: str, repository: str) -> "Dict[str, Any]": @@ -65,14 +65,20 @@ def _get_file_or_pathlike( def _add_rdf( - file_or_pathlike: "Union[str, TextIO]", encoding: str, repository: str + file_or_pathlike: "Union[str, TextIO]", + encoding: str, + repository: str, + context: "Optional[str]" = None, ) -> None: """Create the subgraph in the remote backend""" + params = {"repository": repository} + if context: + params["context"] = context response = _perform_request( "api/knowledge/add-rdf", "post", files=_get_file_or_pathlike(file_or_pathlike, encoding), - params={"repository": repository}, + params=params, ) if not response.ok: raise RuntimeError( @@ -97,21 +103,32 @@ def _delete_subgraph(identifier: str, encoding: str, repository: str) -> None: _sparql_update(query, encoding, repository) +def _create_subgraph(graph: Graph, encoding: str, respository: str) -> None: + """Create the subgraph in the remote backend""" + upload_file = io.BytesIO(graph.serialize(encoding=encoding)) + _add_rdf( + upload_file, encoding, respository, context=f"<{graph.identifier}>" + ) + + def _update_subgraph(graph: Graph, encoding: str, repository: str) -> None: """Update the subgraph in the remote backend""" _delete_subgraph(graph.identifier, encoding, repository) - _add_rdf(graph, encoding, repository) + _create_subgraph(graph, encoding, repository) -def _get_subgraph(kitem_id: str, repository: str) -> Graph: +def _get_subgraph( + identifier: str, repository: str, is_kitem_id: bool = False +) -> Graph: """Get subgraph related to a certain dataset id.""" - uri = _kitem_id2uri(kitem_id) + if is_kitem_id: + identifier = _kitem_id2uri(identifier) query = f""" SELECT DISTINCT ?s ?p ?o WHERE {{ BIND( - <{uri}> as ?g + <{identifier}> as ?g ) {{ GRAPH ?g {{ ?s ?p ?o . }} @@ -126,10 +143,10 @@ def _get_subgraph(kitem_id: str, repository: str) -> Graph: ) buffer.seek(0) - graph = Graph(identifier=uri) + graph = Graph(identifier=identifier) graph.parse(buffer, format="n3") if len(graph) == 0: - raise ValueError(f"Subgraph for id `{kitem_id}` does not exist.") + raise ValueError(f"Subgraph for id `{identifier}` does not exist.") return graph