kgm 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kgm/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ import importlib.metadata
2
+
3
+ __version__ = importlib.metadata.version(__package__)
4
+
5
+ from .documentor import (
6
+ catalogue as create_catalogue,
7
+ )
8
+ from .documentor import (
9
+ table as create_table,
10
+ )
11
+ from .labeller import label as label
12
+ from .loader import load as load
13
+ from .validator import validate as validate
kgm/cli/__init__.py ADDED
@@ -0,0 +1,18 @@
1
+ from kgm.cli.app import app
2
+ from kgm.cli.commands.label import app as label_app
3
+ from kgm.cli.commands.validate import validate_command
4
+
5
+ app.add_typer(label_app, name="label")
6
+
7
+ from kgm.cli.commands.document import app as document_app
8
+
9
+ app.add_typer(document_app, name="document")
10
+
11
+ from kgm.cli.commands.load import app as load_app
12
+
13
+ app.add_typer(load_app, name="load")
14
+
15
+ from kgm.cli.commands.event.cli import event_app
16
+ from kgm.cli.commands.sync import sync_command
17
+
18
+ app.add_typer(event_app)
kgm/cli/app.py ADDED
@@ -0,0 +1,60 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ from typing import Annotated
5
+
6
+ import typer
7
+
8
+ from kgm import __version__
9
+ from kgm.cli.console import console
10
+
11
+
12
+ # Configure logging
13
+ def setup_logging():
14
+ """Set up logging configuration for the CLI."""
15
+ # Get log level from environment or default to WARNING
16
+ log_level_str = os.getenv("KGM_LOG_LEVEL", "WARNING").upper()
17
+ log_level = getattr(logging, log_level_str, logging.WARNING)
18
+
19
+ # Create logger
20
+ logger = logging.getLogger("kgm")
21
+ logger.setLevel(log_level)
22
+
23
+ # Remove any existing handlers
24
+ for handler in logger.handlers[:]:
25
+ logger.removeHandler(handler)
26
+
27
+ # Create console handler
28
+ console_handler = logging.StreamHandler(sys.stderr)
29
+ console_handler.setLevel(log_level)
30
+
31
+ # Create formatter
32
+ formatter = logging.Formatter(
33
+ "%(asctime)s [%(levelname)s] %(name)s:%(lineno)d %(message)s"
34
+ )
35
+ console_handler.setFormatter(formatter)
36
+
37
+ # Add handler to logger
38
+ logger.addHandler(console_handler)
39
+
40
+
41
+ # Set up logging when the module is imported
42
+ setup_logging()
43
+
44
+ app = typer.Typer(
45
+ invoke_without_command=True,
46
+ context_settings={
47
+ "help_option_names": ["-h", "--help"],
48
+ },
49
+ add_completion=False,
50
+ )
51
+
52
+
53
+ @app.callback(invoke_without_command=True)
54
+ def main(
55
+ version: Annotated[bool, typer.Option("--version", "-v", is_eager=True)] = False,
56
+ ):
57
+ """KGM top-level Command Line Interface. Ask for help (-h) for each Command"""
58
+ if version:
59
+ console.print(__version__)
60
+ raise typer.Exit()
File without changes
@@ -0,0 +1,37 @@
1
+ from pathlib import Path
2
+
3
+ import typer
4
+
5
+ from kgm.documentor import TableFormats, catalogue, table
6
+
7
+ app = typer.Typer(help="Create documentation from a KGM")
8
+
9
+
10
+ @app.command(
11
+ name="table",
12
+ help="Create a Markdown or ASCIIDOC table for the resources listed in a KGM",
13
+ )
14
+ def table_command(
15
+ manifest: Path = typer.Argument(
16
+ ..., help="The path of the KGM file to be documented"
17
+ ),
18
+ table_format: TableFormats = typer.Option(
19
+ TableFormats.markdown,
20
+ "--format",
21
+ "-f",
22
+ help="The format of the table to be created",
23
+ ),
24
+ ) -> None:
25
+ print(table(manifest, table_format))
26
+
27
+
28
+ @app.command(
29
+ name="catalogue",
30
+ help="Add the resources listed in a KGM to a catalogue RDF file",
31
+ )
32
+ def catalogue_command(
33
+ manifest: Path = typer.Argument(
34
+ ..., help="The path of the KGM file to be documented"
35
+ ),
36
+ ) -> None:
37
+ print(catalogue(manifest).serialize(format="longturtle"))
File without changes
@@ -0,0 +1,52 @@
1
+ from pathlib import Path
2
+ from typing import Annotated
3
+
4
+ import typer
5
+
6
+ from kgm.event.asb_client import AzureServiceBusEventClient
7
+ from kgm.event.syncer import sync_rdf_delta
8
+ from kgm.utils import make_httpx_client
9
+
10
+ app = typer.Typer()
11
+
12
+
13
+ @app.command(
14
+ name="azure-service-bus",
15
+ help="Synchronize a KGM's resources by sending RDF patch logs to Azure Service Bus.",
16
+ )
17
+ def event_sync_command(
18
+ manifest: Path = typer.Argument(
19
+ ..., help="The path of the KGM file to be loaded"
20
+ ),
21
+ endpoint: str = typer.Argument(..., help="The URL of the SPARQL Endpoint"),
22
+ connection: str = typer.Argument(
23
+ ..., help="The Azure Service Bus connection string"
24
+ ),
25
+ topic: str = typer.Argument(..., help="The Azure Service Bus topic name"),
26
+ subscription: str = typer.Argument(
27
+ ..., help="The Azure Service Bus subscription name"
28
+ ),
29
+ session: str = typer.Argument(..., help="The Azure Service Bus session ID"),
30
+ websocket: bool = typer.Option(False, "--websocket", help="Use WebSockets"),
31
+ username: Annotated[
32
+ str, typer.Option("--username", "-u", help="SPARQL Endpoint username")
33
+ ] = None,
34
+ password: Annotated[
35
+ str, typer.Option("--password", "-p", help="SPARQL Endpoint password")
36
+ ] = None,
37
+ timeout: Annotated[
38
+ int, typer.Option("--timeout", "-t", help="Timeout per request")
39
+ ] = 60,
40
+ ):
41
+ cwd = Path.cwd()
42
+ http_client = make_httpx_client(username, password, timeout)
43
+ event_client = AzureServiceBusEventClient(
44
+ connection, topic, subscription, session, websocket
45
+ )
46
+ try:
47
+ sync_rdf_delta(cwd, manifest, endpoint, http_client, event_client)
48
+ print(
49
+ "The KGM synchronization event has been sent to Azure Service Bus."
50
+ )
51
+ finally:
52
+ http_client.close()
@@ -0,0 +1,19 @@
1
+ import typer
2
+
3
+ from kgm.cli.commands.event.rdf_delta import app as rdf_delta_app
4
+
5
+ event_app = typer.Typer(name="event", help="Event-based KGMs actions")
6
+ sync_app = typer.Typer()
7
+ event_app.add_typer(
8
+ sync_app, name="sync", help="Synchronize a KGM with an event system"
9
+ )
10
+ sync_app.add_typer(rdf_delta_app)
11
+
12
+ try:
13
+ import azure.servicebus
14
+
15
+ from kgm.cli.commands.event.asb import app as asb_app
16
+
17
+ sync_app.add_typer(asb_app)
18
+ except ImportError:
19
+ pass
@@ -0,0 +1,43 @@
1
+ from pathlib import Path
2
+ from typing import Annotated
3
+
4
+ import typer
5
+
6
+ from kgm.event.client import DeltaEventClient
7
+ from kgm.event.syncer import sync_rdf_delta
8
+ from kgm.utils import make_httpx_client
9
+
10
+ app = typer.Typer()
11
+
12
+
13
+ @app.command(
14
+ name="rdf-delta",
15
+ help="Synchronize a KGM's resources by sending RDF patch logs to RDF Delta.",
16
+ )
17
+ def event_sync_command(
18
+ manifest: Path = typer.Argument(
19
+ ..., help="The path of the KGM file to be loaded"
20
+ ),
21
+ endpoint: str = typer.Argument(..., help="The URL of the SPARQL Endpoint"),
22
+ delta_url: str = typer.Argument(..., help="The URL of the RDF Delta endpoint"),
23
+ delta_datasource: str = typer.Argument(
24
+ ..., help="The name of the RDF Delta datasource"
25
+ ),
26
+ username: Annotated[
27
+ str, typer.Option("--username", "-u", help="SPARQL Endpoint username")
28
+ ] = None,
29
+ password: Annotated[
30
+ str, typer.Option("--password", "-p", help="SPARQL Endpoint password")
31
+ ] = None,
32
+ timeout: Annotated[
33
+ int, typer.Option("--timeout", "-t", help="Timeout per request")
34
+ ] = 60,
35
+ ):
36
+ cwd = Path.cwd()
37
+ http_client = make_httpx_client(username, password, timeout)
38
+ event_client = DeltaEventClient(delta_url, delta_datasource)
39
+ try:
40
+ sync_rdf_delta(cwd, manifest, endpoint, http_client, event_client)
41
+ print("The KGM synchronization event has been sent to RDF Delta.")
42
+ finally:
43
+ http_client.close()
@@ -0,0 +1,97 @@
1
+ from pathlib import Path
2
+ from typing import Annotated
3
+
4
+ import typer
5
+
6
+ from kgm.labeller import LabellerOutputTypes, label
7
+ from kgm.utils import make_httpx_client
8
+
9
+ app = typer.Typer(
10
+ help="Discover labels missing from data in a in a KGM and patch them"
11
+ )
12
+
13
+
14
+ @app.command(
15
+ name="iris",
16
+ help="Find all the IRIs of objects in the Manifest's resources without labels",
17
+ )
18
+ def iris_command(
19
+ manifest: Path = typer.Argument(
20
+ ..., help="The path of the KGM file to be labelled"
21
+ ),
22
+ context: str = typer.Argument(
23
+ None,
24
+ help="The path of an RDF file, a directory of RDF files or the URL of a SPARQL endpoint from which to obtain labels",
25
+ ),
26
+ username: Annotated[
27
+ str, typer.Option("--username", "-u", help="SPARQL Endpoint username")
28
+ ] = None,
29
+ password: Annotated[
30
+ str, typer.Option("--password", "-p", help="SPARQL Endpoint password")
31
+ ] = None,
32
+ ) -> None:
33
+ for iri in label(
34
+ manifest,
35
+ LabellerOutputTypes.iris,
36
+ context,
37
+ make_httpx_client(username, password),
38
+ ):
39
+ print(str(iri))
40
+
41
+
42
+ @app.command(
43
+ name="rdf",
44
+ help="Create labels for all the objects in the Manifest's resources without labels",
45
+ )
46
+ def rdf_command(
47
+ manifest: Path = typer.Argument(
48
+ ..., help="The path of the KGM file to be labelled"
49
+ ),
50
+ context: str = typer.Argument(
51
+ None,
52
+ help="The path of an RDF file, a directory of RDF files or the URL of a SPARQL endpoint from which to obtain labels",
53
+ ),
54
+ username: Annotated[
55
+ str, typer.Option("--username", "-u", help="SPARQL Endpoint username")
56
+ ] = None,
57
+ password: Annotated[
58
+ str, typer.Option("--password", "-p", help="SPARQL Endpoint password")
59
+ ] = None,
60
+ ) -> None:
61
+ print(
62
+ label(
63
+ manifest,
64
+ LabellerOutputTypes.rdf,
65
+ context,
66
+ make_httpx_client(username, password),
67
+ ).serialize(format="longturtle")
68
+ )
69
+
70
+
71
+ @app.command(
72
+ name="manifest",
73
+ help="Create labels for all the objects in the Manifest's resources without labels and store them as a new Manifest resource",
74
+ )
75
+ def manifest_command(
76
+ manifest: Path = typer.Argument(
77
+ ..., help="The path of the KGM file to be labelled"
78
+ ),
79
+ context: Path = typer.Argument(
80
+ ...,
81
+ help="The path of an RDF file, a directory of RDF files or the URL of a SPARQL endpoint from which to obtain labels",
82
+ ),
83
+ username: Annotated[
84
+ str, typer.Option("--username", "-u", help="SPARQL Endpoint username")
85
+ ] = None,
86
+ password: Annotated[
87
+ str, typer.Option("--password", "-p", help="SPARQL Endpoint password")
88
+ ] = None,
89
+ ) -> None:
90
+ label(
91
+ manifest,
92
+ LabellerOutputTypes.manifest,
93
+ context,
94
+ make_httpx_client(username, password),
95
+ )
96
+
97
+ print("A new Resource containing labels has been added to the Manifest")
@@ -0,0 +1,47 @@
1
+ from pathlib import Path
2
+ from typing import Annotated
3
+
4
+ import typer
5
+
6
+ from kgm.loader import load
7
+
8
+ app = typer.Typer(help="Load a KGM's content into a file or DB")
9
+
10
+
11
+ @app.command(
12
+ name="sparql", help="Load a KGM's resources into a SPARQL Endpoint"
13
+ )
14
+ def sparql_command(
15
+ manifest: Path = typer.Argument(
16
+ ..., help="The path of the KGM file to be loaded"
17
+ ),
18
+ endpoint: str = typer.Argument(..., help="The URL of the SPARQL Endpoint"),
19
+ username: Annotated[
20
+ str, typer.Option("--username", "-u", help="SPARQL Endpoint username.")
21
+ ] = None,
22
+ password: Annotated[
23
+ str, typer.Option("--password", "-p", help="SPARQL Endpoint password.")
24
+ ] = None,
25
+ timeout: Annotated[
26
+ int, typer.Option("--timeout", "-t", help="Timeout per request")
27
+ ] = 60,
28
+ ) -> None:
29
+ load(
30
+ manifest,
31
+ sparql_endpoint=endpoint,
32
+ sparql_username=username,
33
+ sparql_password=password,
34
+ timeout=timeout,
35
+ )
36
+
37
+
38
+ @app.command(
39
+ name="file", help="Load a KGM's resources into a single RDF quads file"
40
+ )
41
+ def file_command(
42
+ manifest: Path = typer.Argument(
43
+ ..., help="The path of the KGM file to be loaded"
44
+ ),
45
+ file: Path = typer.Argument(..., help="The path of the quads file"),
46
+ ) -> None:
47
+ load(manifest, destination_file=file)
@@ -0,0 +1,75 @@
1
+ import collections
2
+ import json
3
+ from pathlib import Path
4
+ from typing import Annotated
5
+
6
+ import typer
7
+ from rich.table import Table
8
+
9
+ from kgm.cli.app import app
10
+ from kgm.cli.console import console
11
+ from kgm.syncer import sync
12
+ from kgm.utils import make_httpx_client
13
+
14
+
15
+ @app.command(
16
+ name="sync",
17
+ help="Synchronize a KGM's resources with loaded copies of them in a SPARQL Endpoint",
18
+ )
19
+ def sync_command(
20
+ manifest: Path = typer.Argument(
21
+ ..., help="The path of the KGM file to be loaded"
22
+ ),
23
+ endpoint: str = typer.Argument(..., help="The URL of the SPARQL Endpoint"),
24
+ update_remote: bool = typer.Argument(
25
+ True, help="Copy more recent local artifacts to DB"
26
+ ),
27
+ update_local: bool = typer.Argument(
28
+ True, help="Copy more recent DB artifacts to local"
29
+ ),
30
+ add_remote: bool = typer.Argument(True, help="Add new local artifacts to DB"),
31
+ add_local: bool = typer.Argument(True, help="Add new DB artifacts to local"),
32
+ username: Annotated[
33
+ str, typer.Option("--username", "-u", help="SPARQL Endpoint username")
34
+ ] = None,
35
+ password: Annotated[
36
+ str, typer.Option("--password", "-p", help="SPARQL Endpoint password")
37
+ ] = None,
38
+ timeout: Annotated[
39
+ int, typer.Option("--timeout", "-t", help="Timeout per request")
40
+ ] = 60,
41
+ response_format: str = typer.Option(
42
+ "table",
43
+ "--response-format",
44
+ "-f",
45
+ help="The response format of the SPARQL query. Either 'table' (default) or 'json'",
46
+ ),
47
+ ) -> None:
48
+ r = sync(
49
+ manifest,
50
+ endpoint,
51
+ make_httpx_client(username, password, timeout),
52
+ update_remote,
53
+ update_local,
54
+ add_remote,
55
+ add_local,
56
+ )
57
+
58
+ if response_format == "json":
59
+ print(json.dumps(r, indent=4))
60
+ else:
61
+ console.print(result_as_rich_table(r))
62
+
63
+
64
+ def result_as_rich_table(sync_status: dict):
65
+ t = Table()
66
+ t.add_column("Artifact")
67
+ t.add_column("Main Entity")
68
+ t.add_column("Direction")
69
+
70
+ for k, v in collections.OrderedDict(sorted(sync_status.items())).items():
71
+ t.add_row(str(k), str(v["main_entity"]), v["direction"])
72
+
73
+ # json.dumps(sync_status, indent=4)
74
+
75
+ return t
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+
3
+ import typer
4
+
5
+ from kgm.cli.app import app
6
+ from kgm.validator import validate
7
+
8
+
9
+ @app.command(
10
+ name="validate", help="Validate the structure and content of a KGM"
11
+ )
12
+ def validate_command(
13
+ manifest: Path = typer.Argument(
14
+ ..., help="The path of the KGM file to be validated"
15
+ ),
16
+ ) -> None:
17
+ validate(manifest)
kgm/cli/console.py ADDED
@@ -0,0 +1,3 @@
1
+ from rich.console import Console
2
+
3
+ console = Console()
@@ -0,0 +1,41 @@
1
+ from rdflib.namespace import DefinedNamespace, Namespace
2
+ from rdflib.term import URIRef
3
+
4
+
5
+ class MRR(DefinedNamespace):
6
+ _NS = Namespace("https://prez.dev/ManifestResourceRoles/")
7
+ _fail = True
8
+
9
+ CatalogueData: URIRef
10
+ CatalogueModel: URIRef
11
+ ResourceData: URIRef
12
+ ResourceModel: URIRef
13
+ CatalogueAndResourceModel: URIRef
14
+ CompleteCatalogueAndResourceLabels: URIRef
15
+ IncompleteCatalogueAndResourceLabels: URIRef
16
+
17
+
18
+ class OLIS(DefinedNamespace):
19
+ _NS = Namespace("https://olis.dev/")
20
+ _fail = True
21
+
22
+ NamedGraph: URIRef
23
+ RealGraph: URIRef
24
+ SystemGraph: URIRef
25
+ VirtualGraph: URIRef
26
+
27
+ isAliasFor: URIRef
28
+
29
+
30
+ class PREZ(DefinedNamespace):
31
+ _NS = Namespace("https://prez.dev/")
32
+ _fail = True
33
+
34
+ Manifest: URIRef
35
+
36
+
37
+ class MVT(DefinedNamespace):
38
+ _NS = Namespace("https://prez.dev/ManifestVersionTypes/")
39
+ _fail = True
40
+
41
+ GitCommitHash: URIRef