cognite-neat 0.88.0__py3-none-any.whl → 0.88.2__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.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_version.py +1 -1
- cognite/neat/app/api/routers/configuration.py +1 -1
- cognite/neat/app/ui/neat-app/build/asset-manifest.json +7 -7
- cognite/neat/app/ui/neat-app/build/index.html +1 -1
- cognite/neat/app/ui/neat-app/build/static/css/{main.38a62222.css → main.72e3d92e.css} +2 -2
- cognite/neat/app/ui/neat-app/build/static/css/main.72e3d92e.css.map +1 -0
- cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js +3 -0
- cognite/neat/app/ui/neat-app/build/static/js/{main.ec7f72e2.js.LICENSE.txt → main.5a52cf09.js.LICENSE.txt} +0 -9
- cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js.map +1 -0
- cognite/neat/config.py +44 -27
- cognite/neat/exceptions.py +8 -2
- cognite/neat/graph/extractors/_classic_cdf/_assets.py +21 -73
- cognite/neat/graph/extractors/_classic_cdf/_base.py +102 -0
- cognite/neat/graph/extractors/_classic_cdf/_events.py +46 -42
- cognite/neat/graph/extractors/_classic_cdf/_files.py +41 -45
- cognite/neat/graph/extractors/_classic_cdf/_labels.py +75 -52
- cognite/neat/graph/extractors/_classic_cdf/_relationships.py +49 -27
- cognite/neat/graph/extractors/_classic_cdf/_sequences.py +47 -50
- cognite/neat/graph/extractors/_classic_cdf/_timeseries.py +47 -49
- cognite/neat/graph/loaders/_base.py +4 -4
- cognite/neat/graph/loaders/_rdf2asset.py +12 -14
- cognite/neat/graph/loaders/_rdf2dms.py +14 -10
- cognite/neat/graph/queries/_base.py +22 -29
- cognite/neat/graph/queries/_shared.py +1 -1
- cognite/neat/graph/stores/_base.py +19 -11
- cognite/neat/graph/transformers/_rdfpath.py +3 -2
- cognite/neat/issues/__init__.py +16 -0
- cognite/neat/{issues.py → issues/_base.py} +78 -2
- cognite/neat/issues/errors/external.py +21 -0
- cognite/neat/issues/errors/properties.py +75 -0
- cognite/neat/issues/errors/resources.py +123 -0
- cognite/neat/issues/errors/schema.py +0 -0
- cognite/neat/{rules/issues → issues}/formatters.py +9 -9
- cognite/neat/issues/neat_warnings/__init__.py +2 -0
- cognite/neat/issues/neat_warnings/identifier.py +27 -0
- cognite/neat/issues/neat_warnings/models.py +22 -0
- cognite/neat/issues/neat_warnings/properties.py +77 -0
- cognite/neat/issues/neat_warnings/resources.py +125 -0
- cognite/neat/rules/exporters/_rules2dms.py +3 -2
- cognite/neat/rules/exporters/_rules2ontology.py +28 -20
- cognite/neat/rules/exporters/_validation.py +15 -21
- cognite/neat/rules/importers/__init__.py +7 -3
- cognite/neat/rules/importers/_base.py +3 -3
- cognite/neat/rules/importers/_dms2rules.py +39 -18
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +44 -53
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +6 -5
- cognite/neat/rules/importers/_rdf/__init__.py +0 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/__init__.py +3 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +82 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +34 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +123 -0
- cognite/neat/rules/importers/{_owl2rules/_owl2rules.py → _rdf/_imf2rules/_imf2rules.py} +15 -11
- cognite/neat/rules/importers/{_inference2rules.py → _rdf/_inference2rules.py} +1 -1
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +57 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2metadata.py +68 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +59 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +76 -0
- cognite/neat/rules/importers/_rdf/_shared.py +586 -0
- cognite/neat/rules/importers/_spreadsheet2rules.py +31 -28
- cognite/neat/rules/importers/_yaml2rules.py +2 -1
- cognite/neat/rules/issues/__init__.py +1 -5
- cognite/neat/rules/issues/base.py +2 -21
- cognite/neat/rules/issues/dms.py +20 -134
- cognite/neat/rules/issues/ontology.py +298 -0
- cognite/neat/rules/issues/spreadsheet.py +51 -3
- cognite/neat/rules/issues/tables.py +72 -0
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/_types/_field.py +14 -21
- cognite/neat/rules/models/asset/_validation.py +1 -1
- cognite/neat/rules/models/dms/_schema.py +53 -30
- cognite/neat/rules/models/dms/_validation.py +2 -2
- cognite/neat/rules/models/entities.py +3 -0
- cognite/neat/rules/models/information/_rules.py +5 -4
- cognite/neat/rules/models/information/_validation.py +1 -1
- cognite/neat/utils/rdf_.py +17 -9
- cognite/neat/utils/regex_patterns.py +52 -0
- cognite/neat/workflows/steps/lib/current/rules_importer.py +73 -1
- cognite/neat/workflows/steps/lib/current/rules_validator.py +19 -7
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/METADATA +2 -6
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/RECORD +85 -72
- cognite/neat/app/ui/neat-app/build/static/css/main.38a62222.css.map +0 -1
- cognite/neat/app/ui/neat-app/build/static/js/main.ec7f72e2.js +0 -3
- cognite/neat/app/ui/neat-app/build/static/js/main.ec7f72e2.js.map +0 -1
- cognite/neat/graph/issues/loader.py +0 -104
- cognite/neat/graph/stores/_oxrdflib.py +0 -247
- cognite/neat/rules/exceptions.py +0 -2972
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +0 -215
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +0 -213
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +0 -203
- cognite/neat/rules/issues/importing.py +0 -408
- cognite/neat/rules/models/_types/_base.py +0 -16
- cognite/neat/workflows/examples/Export_Rules_to_Ontology/workflow.yaml +0 -152
- cognite/neat/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml +0 -139
- cognite/neat/workflows/examples/Ontology_to_Data_Model/workflow.yaml +0 -116
- /cognite/neat/{graph/issues → issues/errors}/__init__.py +0 -0
- /cognite/neat/rules/importers/{_owl2rules → _rdf/_owl2rules}/__init__.py +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,40 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
from collections.abc import Iterable
|
|
1
|
+
from collections.abc import Callable, Set
|
|
3
2
|
from datetime import datetime, timezone
|
|
4
3
|
from pathlib import Path
|
|
5
|
-
from typing import cast
|
|
6
4
|
from urllib.parse import quote
|
|
7
5
|
|
|
8
6
|
from cognite.client import CogniteClient
|
|
9
7
|
from cognite.client.data_classes import FileMetadata, FileMetadataList
|
|
10
|
-
from
|
|
11
|
-
from rdflib import RDF, Literal, Namespace, URIRef
|
|
8
|
+
from rdflib import RDF, Literal, Namespace
|
|
12
9
|
|
|
13
|
-
from cognite.neat.constants import DEFAULT_NAMESPACE
|
|
14
|
-
from cognite.neat.graph.extractors._base import BaseExtractor
|
|
15
10
|
from cognite.neat.graph.models import Triple
|
|
16
|
-
from cognite.neat.utils.auxiliary import string_to_ideal_type
|
|
17
11
|
|
|
12
|
+
from ._base import DEFAULT_SKIP_METADATA_VALUES, ClassicCDFExtractor
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
|
|
15
|
+
class FilesExtractor(ClassicCDFExtractor[FileMetadata]):
|
|
20
16
|
"""Extract data from Cognite Data Fusions files metadata into Neat.
|
|
21
17
|
|
|
22
18
|
Args:
|
|
23
|
-
|
|
19
|
+
items (Iterable[FileMetadata]): An iterable of items.
|
|
24
20
|
namespace (Namespace, optional): The namespace to use. Defaults to DEFAULT_NAMESPACE.
|
|
21
|
+
to_type (Callable[[FileMetadata], str | None], optional): A function to convert an item to a type.
|
|
22
|
+
Defaults to None. If None or if the function returns None, the asset will be set to the default type.
|
|
23
|
+
total (int, optional): The total number of items to load. If passed, you will get a progress bar if rich
|
|
24
|
+
is installed. Defaults to None.
|
|
25
|
+
limit (int, optional): The maximal number of items to load. Defaults to None. This is typically used for
|
|
26
|
+
testing setup of the extractor. For example, if you are extracting 100 000 assets, you might want to
|
|
27
|
+
limit the extraction to 1000 assets to test the setup.
|
|
25
28
|
unpack_metadata (bool, optional): Whether to unpack metadata. Defaults to False, which yields the metadata as
|
|
26
29
|
a JSON string.
|
|
30
|
+
skip_metadata_values (set[str] | frozenset[str] | None, optional): If you are unpacking metadata, then
|
|
31
|
+
values in this set will be skipped.
|
|
27
32
|
"""
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
self,
|
|
31
|
-
files_metadata: Iterable[FileMetadata],
|
|
32
|
-
namespace: Namespace | None = None,
|
|
33
|
-
unpack_metadata: bool = True,
|
|
34
|
-
):
|
|
35
|
-
self.namespace = namespace or DEFAULT_NAMESPACE
|
|
36
|
-
self.files_metadata = files_metadata
|
|
37
|
-
self.unpack_metadata = unpack_metadata
|
|
34
|
+
_default_rdf_type = "File"
|
|
38
35
|
|
|
39
36
|
@classmethod
|
|
40
37
|
def from_dataset(
|
|
@@ -42,15 +39,18 @@ class FilesExtractor(BaseExtractor):
|
|
|
42
39
|
client: CogniteClient,
|
|
43
40
|
data_set_external_id: str,
|
|
44
41
|
namespace: Namespace | None = None,
|
|
42
|
+
to_type: Callable[[FileMetadata], str | None] | None = None,
|
|
43
|
+
limit: int | None = None,
|
|
45
44
|
unpack_metadata: bool = True,
|
|
45
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
46
46
|
):
|
|
47
47
|
return cls(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
client.files(data_set_external_ids=data_set_external_id),
|
|
49
|
+
namespace=namespace,
|
|
50
|
+
to_type=to_type,
|
|
51
|
+
limit=limit,
|
|
52
|
+
unpack_metadata=unpack_metadata,
|
|
53
|
+
skip_metadata_values=skip_metadata_values,
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
@classmethod
|
|
@@ -58,24 +58,29 @@ class FilesExtractor(BaseExtractor):
|
|
|
58
58
|
cls,
|
|
59
59
|
file_path: str,
|
|
60
60
|
namespace: Namespace | None = None,
|
|
61
|
+
to_type: Callable[[FileMetadata], str | None] | None = None,
|
|
62
|
+
limit: int | None = None,
|
|
61
63
|
unpack_metadata: bool = True,
|
|
64
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
62
65
|
):
|
|
66
|
+
file_metadata = FileMetadataList.load(Path(file_path).read_text())
|
|
63
67
|
return cls(
|
|
64
|
-
|
|
65
|
-
namespace,
|
|
66
|
-
|
|
68
|
+
file_metadata,
|
|
69
|
+
namespace=namespace,
|
|
70
|
+
to_type=to_type,
|
|
71
|
+
limit=limit,
|
|
72
|
+
total=len(file_metadata),
|
|
73
|
+
unpack_metadata=unpack_metadata,
|
|
74
|
+
skip_metadata_values=skip_metadata_values,
|
|
67
75
|
)
|
|
68
76
|
|
|
69
|
-
def
|
|
70
|
-
"""Extract files metadata as triples."""
|
|
71
|
-
for event in self.files_metadata:
|
|
72
|
-
yield from self._file2triples(event)
|
|
73
|
-
|
|
74
|
-
def _file2triples(self, file: FileMetadata) -> list[Triple]:
|
|
77
|
+
def _item2triples(self, file: FileMetadata) -> list[Triple]:
|
|
75
78
|
id_ = self.namespace[f"File_{file.id}"]
|
|
76
79
|
|
|
80
|
+
type_ = self._get_rdf_type(file)
|
|
81
|
+
|
|
77
82
|
# Set rdf type
|
|
78
|
-
triples: list[Triple] = [(id_, RDF.type, self.namespace
|
|
83
|
+
triples: list[Triple] = [(id_, RDF.type, self.namespace[type_])]
|
|
79
84
|
|
|
80
85
|
# Create attributes
|
|
81
86
|
|
|
@@ -95,16 +100,7 @@ class FilesExtractor(BaseExtractor):
|
|
|
95
100
|
triples.append((id_, self.namespace.source, Literal(file.source)))
|
|
96
101
|
|
|
97
102
|
if file.metadata:
|
|
98
|
-
|
|
99
|
-
for key, value in file.metadata.items():
|
|
100
|
-
if value:
|
|
101
|
-
type_aware_value = string_to_ideal_type(value)
|
|
102
|
-
try:
|
|
103
|
-
triples.append((id_, self.namespace[key], URIRef(str(AnyHttpUrl(type_aware_value))))) # type: ignore
|
|
104
|
-
except ValidationError:
|
|
105
|
-
triples.append((id_, self.namespace[key], Literal(type_aware_value)))
|
|
106
|
-
else:
|
|
107
|
-
triples.append((id_, self.namespace.metadata, Literal(json.dumps(file.metadata))))
|
|
103
|
+
triples.extend(self._metadata_to_triples(id_, file.metadata))
|
|
108
104
|
|
|
109
105
|
if file.source_created_time:
|
|
110
106
|
triples.append(
|
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
from collections.abc import
|
|
1
|
+
from collections.abc import Callable, Set
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import cast
|
|
5
4
|
|
|
6
5
|
from cognite.client import CogniteClient
|
|
7
6
|
from cognite.client.data_classes import LabelDefinition, LabelDefinitionList
|
|
8
7
|
from rdflib import RDF, Literal, Namespace
|
|
9
8
|
|
|
10
|
-
from cognite.neat.constants import DEFAULT_NAMESPACE
|
|
11
|
-
from cognite.neat.graph.extractors._base import BaseExtractor
|
|
12
9
|
from cognite.neat.graph.models import Triple
|
|
13
10
|
from cognite.neat.utils.auxiliary import create_sha256_hash
|
|
14
11
|
|
|
12
|
+
from ._base import DEFAULT_SKIP_METADATA_VALUES, ClassicCDFExtractor
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
|
|
15
|
+
class LabelsExtractor(ClassicCDFExtractor[LabelDefinition]):
|
|
17
16
|
"""Extract data from Cognite Data Fusions Labels into Neat.
|
|
18
17
|
|
|
19
18
|
Args:
|
|
20
|
-
|
|
19
|
+
items (Iterable[LabelDefinition]): An iterable of items.
|
|
21
20
|
namespace (Namespace, optional): The namespace to use. Defaults to DEFAULT_NAMESPACE.
|
|
21
|
+
to_type (Callable[[LabelDefinition], str | None], optional): A function to convert an item to a type.
|
|
22
|
+
Defaults to None. If None or if the function returns None, the asset will be set to the default type.
|
|
23
|
+
total (int, optional): The total number of items to load. If passed, you will get a progress bar if rich
|
|
24
|
+
is installed. Defaults to None.
|
|
25
|
+
limit (int, optional): The maximal number of items to load. Defaults to None. This is typically used for
|
|
26
|
+
testing setup of the extractor. For example, if you are extracting 100 000 assets, you might want to
|
|
27
|
+
limit the extraction to 1000 assets to test the setup.
|
|
28
|
+
unpack_metadata (bool, optional): Whether to unpack metadata. Defaults to False, which yields the metadata as
|
|
29
|
+
a JSON string.
|
|
30
|
+
skip_metadata_values (set[str] | frozenset[str] | None, optional): If you are unpacking metadata, then
|
|
31
|
+
values in this set will be skipped.
|
|
22
32
|
"""
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
self,
|
|
26
|
-
labels: Iterable[LabelDefinition],
|
|
27
|
-
namespace: Namespace | None = None,
|
|
28
|
-
):
|
|
29
|
-
self.namespace = namespace or DEFAULT_NAMESPACE
|
|
30
|
-
self.labels = labels
|
|
34
|
+
_default_rdf_type = "Label"
|
|
31
35
|
|
|
32
36
|
@classmethod
|
|
33
37
|
def from_dataset(
|
|
@@ -35,57 +39,76 @@ class LabelsExtractor(BaseExtractor):
|
|
|
35
39
|
client: CogniteClient,
|
|
36
40
|
data_set_external_id: str,
|
|
37
41
|
namespace: Namespace | None = None,
|
|
42
|
+
to_type: Callable[[LabelDefinition], str | None] | None = None,
|
|
43
|
+
limit: int | None = None,
|
|
44
|
+
unpack_metadata: bool = True,
|
|
45
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
38
46
|
):
|
|
39
47
|
return cls(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
client.labels(data_set_external_ids=data_set_external_id),
|
|
49
|
+
namespace=namespace,
|
|
50
|
+
to_type=to_type,
|
|
51
|
+
limit=limit,
|
|
52
|
+
unpack_metadata=unpack_metadata,
|
|
53
|
+
skip_metadata_values=skip_metadata_values,
|
|
45
54
|
)
|
|
46
55
|
|
|
47
56
|
@classmethod
|
|
48
|
-
def from_file(
|
|
49
|
-
|
|
57
|
+
def from_file(
|
|
58
|
+
cls,
|
|
59
|
+
file_path: str,
|
|
60
|
+
namespace: Namespace | None = None,
|
|
61
|
+
to_type: Callable[[LabelDefinition], str | None] | None = None,
|
|
62
|
+
limit: int | None = None,
|
|
63
|
+
unpack_metadata: bool = True,
|
|
64
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
65
|
+
):
|
|
66
|
+
labels = LabelDefinitionList.load(Path(file_path).read_text())
|
|
67
|
+
return cls(
|
|
68
|
+
labels,
|
|
69
|
+
total=len(labels),
|
|
70
|
+
namespace=namespace,
|
|
71
|
+
to_type=to_type,
|
|
72
|
+
limit=limit,
|
|
73
|
+
unpack_metadata=unpack_metadata,
|
|
74
|
+
skip_metadata_values=skip_metadata_values,
|
|
75
|
+
)
|
|
50
76
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
yield from self._labels2triples(label)
|
|
77
|
+
def _item2triples(self, label: LabelDefinition) -> list[Triple]:
|
|
78
|
+
if not label.external_id:
|
|
79
|
+
return []
|
|
55
80
|
|
|
56
|
-
|
|
57
|
-
if label.external_id:
|
|
58
|
-
id_ = self.namespace[f"Label_{create_sha256_hash(label.external_id)}"]
|
|
81
|
+
id_ = self.namespace[f"Label_{create_sha256_hash(label.external_id)}"]
|
|
59
82
|
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
type_ = self._get_rdf_type(label)
|
|
84
|
+
# Set rdf type
|
|
85
|
+
triples: list[Triple] = [(id_, RDF.type, self.namespace[type_])]
|
|
62
86
|
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
# Create attributes
|
|
88
|
+
triples.append((id_, self.namespace.external_id, Literal(label.external_id)))
|
|
65
89
|
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
if label.name:
|
|
91
|
+
triples.append((id_, self.namespace.name, Literal(label.name)))
|
|
68
92
|
|
|
69
|
-
|
|
70
|
-
|
|
93
|
+
if label.description:
|
|
94
|
+
triples.append((id_, self.namespace.description, Literal(label.description)))
|
|
71
95
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
96
|
+
if label.created_time:
|
|
97
|
+
triples.append(
|
|
98
|
+
(
|
|
99
|
+
id_,
|
|
100
|
+
self.namespace.created_time,
|
|
101
|
+
Literal(datetime.fromtimestamp(label.created_time / 1000, timezone.utc)),
|
|
79
102
|
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if label.data_set_id:
|
|
106
|
+
triples.append(
|
|
107
|
+
(
|
|
108
|
+
id_,
|
|
109
|
+
self.namespace.data_set_id,
|
|
110
|
+
self.namespace[f"Dataset_{label.data_set_id}"],
|
|
88
111
|
)
|
|
112
|
+
)
|
|
89
113
|
|
|
90
|
-
|
|
91
|
-
return []
|
|
114
|
+
return triples
|
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
from collections.abc import
|
|
1
|
+
from collections.abc import Callable, Set
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import cast
|
|
5
4
|
from urllib.parse import quote
|
|
6
5
|
|
|
7
6
|
from cognite.client import CogniteClient
|
|
8
7
|
from cognite.client.data_classes import Relationship, RelationshipList
|
|
9
8
|
from rdflib import RDF, Literal, Namespace
|
|
10
9
|
|
|
11
|
-
from cognite.neat.constants import DEFAULT_NAMESPACE
|
|
12
|
-
from cognite.neat.graph.extractors._base import BaseExtractor
|
|
13
10
|
from cognite.neat.graph.models import Triple
|
|
14
11
|
from cognite.neat.utils.auxiliary import create_sha256_hash
|
|
15
12
|
|
|
13
|
+
from ._base import DEFAULT_SKIP_METADATA_VALUES, ClassicCDFExtractor
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
|
|
16
|
+
class RelationshipsExtractor(ClassicCDFExtractor[Relationship]):
|
|
18
17
|
"""Extract data from Cognite Data Fusions Relationships into Neat.
|
|
19
18
|
|
|
20
19
|
Args:
|
|
21
|
-
|
|
20
|
+
items (Iterable[Relationship]): An iterable of items.
|
|
22
21
|
namespace (Namespace, optional): The namespace to use. Defaults to DEFAULT_NAMESPACE.
|
|
22
|
+
to_type (Callable[[Relationship], str | None], optional): A function to convert an item to a type.
|
|
23
|
+
Defaults to None. If None or if the function returns None, the asset will be set to the default type.
|
|
24
|
+
total (int, optional): The total number of items to load. If passed, you will get a progress bar if rich
|
|
25
|
+
is installed. Defaults to None.
|
|
26
|
+
limit (int, optional): The maximal number of items to load. Defaults to None. This is typically used for
|
|
27
|
+
testing setup of the extractor. For example, if you are extracting 100 000 assets, you might want to
|
|
28
|
+
limit the extraction to 1000 assets to test the setup.
|
|
29
|
+
unpack_metadata (bool, optional): Whether to unpack metadata. Defaults to False, which yields the metadata as
|
|
30
|
+
a JSON string.
|
|
31
|
+
skip_metadata_values (set[str] | frozenset[str] | None, optional): If you are unpacking metadata, then
|
|
32
|
+
values in this set will be skipped.
|
|
23
33
|
"""
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
self,
|
|
27
|
-
relationships: Iterable[Relationship],
|
|
28
|
-
namespace: Namespace | None = None,
|
|
29
|
-
):
|
|
30
|
-
self.namespace = namespace or DEFAULT_NAMESPACE
|
|
31
|
-
self.relationships = relationships
|
|
35
|
+
_default_rdf_type = "Relationship"
|
|
32
36
|
|
|
33
37
|
@classmethod
|
|
34
38
|
def from_dataset(
|
|
@@ -36,33 +40,51 @@ class RelationshipsExtractor(BaseExtractor):
|
|
|
36
40
|
client: CogniteClient,
|
|
37
41
|
data_set_external_id: str,
|
|
38
42
|
namespace: Namespace | None = None,
|
|
43
|
+
to_type: Callable[[Relationship], str | None] | None = None,
|
|
44
|
+
limit: int | None = None,
|
|
45
|
+
unpack_metadata: bool = True,
|
|
46
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
39
47
|
):
|
|
40
48
|
return cls(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
client.relationships(data_set_external_ids=data_set_external_id),
|
|
50
|
+
namespace=namespace,
|
|
51
|
+
to_type=to_type,
|
|
52
|
+
limit=limit,
|
|
53
|
+
unpack_metadata=unpack_metadata,
|
|
54
|
+
skip_metadata_values=skip_metadata_values,
|
|
46
55
|
)
|
|
47
56
|
|
|
48
57
|
@classmethod
|
|
49
|
-
def from_file(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
def from_file(
|
|
59
|
+
cls,
|
|
60
|
+
file_path: str,
|
|
61
|
+
namespace: Namespace | None = None,
|
|
62
|
+
to_type: Callable[[Relationship], str | None] | None = None,
|
|
63
|
+
limit: int | None = None,
|
|
64
|
+
unpack_metadata: bool = True,
|
|
65
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
66
|
+
):
|
|
67
|
+
relationships = RelationshipList.load(Path(file_path).read_text())
|
|
68
|
+
return cls(
|
|
69
|
+
relationships,
|
|
70
|
+
namespace=namespace,
|
|
71
|
+
total=len(relationships),
|
|
72
|
+
to_type=to_type,
|
|
73
|
+
limit=limit,
|
|
74
|
+
unpack_metadata=unpack_metadata,
|
|
75
|
+
skip_metadata_values=skip_metadata_values,
|
|
76
|
+
)
|
|
56
77
|
|
|
57
|
-
def
|
|
78
|
+
def _item2triples(self, relationship: Relationship) -> list[Triple]:
|
|
58
79
|
"""Converts an asset to triples."""
|
|
59
80
|
|
|
60
81
|
if relationship.external_id and relationship.source_external_id and relationship.target_external_id:
|
|
61
82
|
# relationships do not have an internal id, so we generate one
|
|
62
83
|
id_ = self.namespace[f"Relationship_{create_sha256_hash(relationship.external_id)}"]
|
|
63
84
|
|
|
85
|
+
type_ = self._get_rdf_type(relationship)
|
|
64
86
|
# Set rdf type
|
|
65
|
-
triples: list[Triple] = [(id_, RDF.type, self.namespace[
|
|
87
|
+
triples: list[Triple] = [(id_, RDF.type, self.namespace[type_])]
|
|
66
88
|
|
|
67
89
|
# Set source and target types
|
|
68
90
|
if source_type := relationship.source_type:
|
|
@@ -1,39 +1,36 @@
|
|
|
1
|
-
import
|
|
2
|
-
from collections.abc import Iterable
|
|
1
|
+
from collections.abc import Callable, Set
|
|
3
2
|
from datetime import datetime, timezone
|
|
4
3
|
from pathlib import Path
|
|
5
|
-
from typing import cast
|
|
6
4
|
|
|
7
5
|
from cognite.client import CogniteClient
|
|
8
|
-
from cognite.client.data_classes import Sequence, SequenceList
|
|
9
|
-
from
|
|
10
|
-
from rdflib import RDF, Literal, Namespace, URIRef
|
|
6
|
+
from cognite.client.data_classes import Sequence, SequenceFilter, SequenceList
|
|
7
|
+
from rdflib import RDF, Literal, Namespace
|
|
11
8
|
|
|
12
|
-
from cognite.neat.constants import DEFAULT_NAMESPACE
|
|
13
|
-
from cognite.neat.graph.extractors._base import BaseExtractor
|
|
14
9
|
from cognite.neat.graph.models import Triple
|
|
15
|
-
from cognite.neat.utils.auxiliary import string_to_ideal_type
|
|
16
10
|
|
|
11
|
+
from ._base import DEFAULT_SKIP_METADATA_VALUES, ClassicCDFExtractor
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
|
|
14
|
+
class SequencesExtractor(ClassicCDFExtractor[Sequence]):
|
|
19
15
|
"""Extract data from Cognite Data Fusions Sequences into Neat.
|
|
20
16
|
|
|
21
17
|
Args:
|
|
22
|
-
|
|
18
|
+
items (Iterable[Sequence]): An iterable of items.
|
|
23
19
|
namespace (Namespace, optional): The namespace to use. Defaults to DEFAULT_NAMESPACE.
|
|
20
|
+
to_type (Callable[[Sequence], str | None], optional): A function to convert an item to a type.
|
|
21
|
+
Defaults to None. If None or if the function returns None, the asset will be set to the default type.
|
|
22
|
+
total (int, optional): The total number of items to load. If passed, you will get a progress bar if rich
|
|
23
|
+
is installed. Defaults to None.
|
|
24
|
+
limit (int, optional): The maximal number of items to load. Defaults to None. This is typically used for
|
|
25
|
+
testing setup of the extractor. For example, if you are extracting 100 000 assets, you might want to
|
|
26
|
+
limit the extraction to 1000 assets to test the setup.
|
|
24
27
|
unpack_metadata (bool, optional): Whether to unpack metadata. Defaults to False, which yields the metadata as
|
|
25
28
|
a JSON string.
|
|
29
|
+
skip_metadata_values (set[str] | frozenset[str] | None, optional): If you are unpacking metadata, then
|
|
30
|
+
values in this set will be skipped.
|
|
26
31
|
"""
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
self,
|
|
30
|
-
sequence: Iterable[Sequence],
|
|
31
|
-
namespace: Namespace | None = None,
|
|
32
|
-
unpack_metadata: bool = True,
|
|
33
|
-
):
|
|
34
|
-
self.namespace = namespace or DEFAULT_NAMESPACE
|
|
35
|
-
self.sequence = sequence
|
|
36
|
-
self.unpack_metadata = unpack_metadata
|
|
33
|
+
_default_rdf_type = "Sequence"
|
|
37
34
|
|
|
38
35
|
@classmethod
|
|
39
36
|
def from_dataset(
|
|
@@ -41,15 +38,22 @@ class SequencesExtractor(BaseExtractor):
|
|
|
41
38
|
client: CogniteClient,
|
|
42
39
|
data_set_external_id: str,
|
|
43
40
|
namespace: Namespace | None = None,
|
|
41
|
+
to_type: Callable[[Sequence], str | None] | None = None,
|
|
42
|
+
limit: int | None = None,
|
|
44
43
|
unpack_metadata: bool = True,
|
|
44
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
45
45
|
):
|
|
46
|
+
total = client.sequences.aggregate_count(
|
|
47
|
+
filter=SequenceFilter(data_set_ids=[{"externalId": data_set_external_id}])
|
|
48
|
+
)
|
|
46
49
|
return cls(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
unpack_metadata,
|
|
50
|
+
client.sequences(data_set_external_ids=data_set_external_id),
|
|
51
|
+
total=total,
|
|
52
|
+
namespace=namespace,
|
|
53
|
+
to_type=to_type,
|
|
54
|
+
limit=limit,
|
|
55
|
+
unpack_metadata=unpack_metadata,
|
|
56
|
+
skip_metadata_values=skip_metadata_values,
|
|
53
57
|
)
|
|
54
58
|
|
|
55
59
|
@classmethod
|
|
@@ -57,20 +61,28 @@ class SequencesExtractor(BaseExtractor):
|
|
|
57
61
|
cls,
|
|
58
62
|
file_path: str,
|
|
59
63
|
namespace: Namespace | None = None,
|
|
64
|
+
to_type: Callable[[Sequence], str | None] | None = None,
|
|
65
|
+
limit: int | None = None,
|
|
60
66
|
unpack_metadata: bool = True,
|
|
67
|
+
skip_metadata_values: Set[str] | None = DEFAULT_SKIP_METADATA_VALUES,
|
|
61
68
|
):
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
sequences = SequenceList.load(Path(file_path).read_text())
|
|
70
|
+
return cls(
|
|
71
|
+
sequences,
|
|
72
|
+
total=len(sequences),
|
|
73
|
+
namespace=namespace,
|
|
74
|
+
to_type=to_type,
|
|
75
|
+
limit=limit,
|
|
76
|
+
unpack_metadata=unpack_metadata,
|
|
77
|
+
skip_metadata_values=skip_metadata_values,
|
|
78
|
+
)
|
|
68
79
|
|
|
69
|
-
def
|
|
80
|
+
def _item2triples(self, sequence: Sequence) -> list[Triple]:
|
|
70
81
|
id_ = self.namespace[f"Sequence_{sequence.id}"]
|
|
71
82
|
|
|
83
|
+
type_ = self._get_rdf_type(sequence)
|
|
72
84
|
# Set rdf type
|
|
73
|
-
triples: list[Triple] = [(id_, RDF.type, self.namespace
|
|
85
|
+
triples: list[Triple] = [(id_, RDF.type, self.namespace[type_])]
|
|
74
86
|
|
|
75
87
|
# Create attributes
|
|
76
88
|
|
|
@@ -81,22 +93,7 @@ class SequencesExtractor(BaseExtractor):
|
|
|
81
93
|
triples.append((id_, self.namespace.name, Literal(sequence.name)))
|
|
82
94
|
|
|
83
95
|
if sequence.metadata:
|
|
84
|
-
|
|
85
|
-
for key, value in sequence.metadata.items():
|
|
86
|
-
if value:
|
|
87
|
-
type_aware_value = string_to_ideal_type(value)
|
|
88
|
-
try:
|
|
89
|
-
triples.append((id_, self.namespace[key], URIRef(str(AnyHttpUrl(type_aware_value))))) # type: ignore
|
|
90
|
-
except ValidationError:
|
|
91
|
-
triples.append((id_, self.namespace[key], Literal(type_aware_value)))
|
|
92
|
-
else:
|
|
93
|
-
triples.append(
|
|
94
|
-
(
|
|
95
|
-
id_,
|
|
96
|
-
self.namespace.metadata,
|
|
97
|
-
Literal(json.dumps(sequence.metadata)),
|
|
98
|
-
)
|
|
99
|
-
)
|
|
96
|
+
triples.extend(self._metadata_to_triples(id_, sequence.metadata))
|
|
100
97
|
|
|
101
98
|
if sequence.description:
|
|
102
99
|
triples.append((id_, self.namespace.description, Literal(sequence.description)))
|