cognite-neat 0.87.0__py3-none-any.whl → 0.87.3__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/configuration.py +1 -10
- cognite/neat/app/api/routers/data_exploration.py +1 -1
- cognite/neat/config.py +84 -17
- cognite/neat/graph/extractors/_classic_cdf/_assets.py +1 -1
- cognite/neat/graph/extractors/_classic_cdf/_events.py +1 -1
- cognite/neat/graph/extractors/_classic_cdf/_files.py +1 -1
- cognite/neat/graph/extractors/_classic_cdf/_labels.py +1 -1
- cognite/neat/graph/extractors/_classic_cdf/_relationships.py +1 -1
- cognite/neat/graph/extractors/_classic_cdf/_sequences.py +1 -1
- cognite/neat/graph/extractors/_classic_cdf/_timeseries.py +1 -1
- cognite/neat/graph/extractors/_dexpi.py +1 -1
- cognite/neat/graph/extractors/_mock_graph_generator.py +1 -1
- cognite/neat/graph/loaders/_rdf2asset.py +108 -52
- cognite/neat/graph/loaders/_rdf2dms.py +6 -6
- cognite/neat/graph/queries/_base.py +20 -11
- cognite/neat/graph/queries/_construct.py +3 -3
- cognite/neat/graph/queries/_shared.py +1 -1
- cognite/neat/graph/stores/_base.py +14 -3
- cognite/neat/graph/transformers/__init__.py +3 -0
- cognite/neat/graph/transformers/_rdfpath.py +42 -0
- cognite/neat/legacy/graph/extractors/_mock_graph_generator.py +1 -1
- cognite/neat/legacy/graph/loaders/_asset_loader.py +2 -2
- cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py +5 -2
- cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py +4 -1
- cognite/neat/legacy/graph/loaders/rdf_to_dms.py +3 -1
- cognite/neat/legacy/graph/transformations/query_generator/sparql.py +1 -1
- cognite/neat/legacy/graph/transformations/transformer.py +1 -1
- cognite/neat/legacy/rules/exporters/_rules2dms.py +8 -3
- cognite/neat/legacy/rules/exporters/_rules2graphql.py +1 -1
- cognite/neat/legacy/rules/exporters/_rules2ontology.py +2 -1
- cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py +3 -4
- cognite/neat/legacy/rules/importers/_dms2rules.py +4 -1
- cognite/neat/legacy/rules/importers/_graph2rules.py +5 -32
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py +1 -1
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py +2 -1
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py +1 -1
- cognite/neat/legacy/rules/models/raw_rules.py +1 -1
- cognite/neat/rules/analysis/_asset.py +15 -0
- cognite/neat/rules/analysis/_base.py +1 -1
- cognite/neat/rules/analysis/_information.py +40 -12
- cognite/neat/rules/exporters/_rules2dms.py +1 -1
- cognite/neat/rules/exporters/_rules2ontology.py +2 -1
- cognite/neat/rules/importers/_dms2rules.py +3 -1
- cognite/neat/rules/importers/_inference2rules.py +1 -5
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +1 -1
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +2 -1
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +1 -1
- cognite/neat/rules/issues/spreadsheet.py +35 -0
- cognite/neat/rules/models/_rdfpath.py +17 -21
- cognite/neat/rules/models/asset/_validation.py +38 -1
- cognite/neat/rules/models/dms/_exporter.py +7 -3
- cognite/neat/rules/models/dms/_schema.py +5 -4
- cognite/neat/rules/models/entities.py +26 -8
- cognite/neat/rules/models/information/_validation.py +1 -1
- cognite/neat/utils/__init__.py +0 -3
- cognite/neat/utils/auth.py +47 -28
- cognite/neat/utils/auxiliary.py +141 -1
- cognite/neat/utils/cdf/__init__.py +0 -0
- cognite/neat/utils/{cdf_classes.py → cdf/data_classes.py} +122 -2
- cognite/neat/utils/{cdf_loaders → cdf/loaders}/_data_modeling.py +37 -0
- cognite/neat/utils/{cdf_loaders → cdf/loaders}/_ingestion.py +2 -1
- cognite/neat/utils/collection_.py +18 -0
- cognite/neat/utils/rdf_.py +165 -0
- cognite/neat/utils/text.py +4 -0
- cognite/neat/utils/time_.py +17 -0
- cognite/neat/utils/upload.py +13 -1
- cognite/neat/workflows/_exceptions.py +5 -5
- cognite/neat/workflows/base.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_validator.py +2 -2
- cognite/neat/workflows/steps/lib/legacy/graph_extractor.py +1 -1
- cognite/neat/workflows/steps/lib/legacy/graph_loader.py +1 -1
- cognite/neat/workflows/steps/lib/legacy/rules_exporter.py +1 -1
- cognite/neat/workflows/steps/lib/legacy/rules_importer.py +1 -1
- {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/METADATA +2 -2
- {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/RECORD +81 -81
- cognite/neat/utils/cdf.py +0 -59
- cognite/neat/utils/cdf_loaders/data_classes.py +0 -121
- cognite/neat/utils/exceptions.py +0 -41
- cognite/neat/utils/utils.py +0 -429
- /cognite/neat/utils/{cdf_loaders → cdf/loaders}/__init__.py +0 -0
- /cognite/neat/utils/{cdf_loaders → cdf/loaders}/_base.py +0 -0
- {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/LICENSE +0 -0
- {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/WHEEL +0 -0
- {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from collections.abc import (
|
|
3
|
+
Hashable,
|
|
3
4
|
ItemsView,
|
|
4
5
|
Iterable,
|
|
5
6
|
Iterator,
|
|
@@ -8,11 +9,18 @@ from collections.abc import (
|
|
|
8
9
|
MutableMapping,
|
|
9
10
|
ValuesView,
|
|
10
11
|
)
|
|
12
|
+
from dataclasses import dataclass
|
|
11
13
|
from typing import Any, TypeVar, cast, final
|
|
12
14
|
|
|
13
15
|
import pandas as pd
|
|
14
16
|
import yaml
|
|
15
|
-
from cognite.client
|
|
17
|
+
from cognite.client import CogniteClient
|
|
18
|
+
from cognite.client.data_classes._base import (
|
|
19
|
+
CogniteResourceList,
|
|
20
|
+
T_CogniteResource,
|
|
21
|
+
WriteableCogniteResource,
|
|
22
|
+
WriteableCogniteResourceList,
|
|
23
|
+
)
|
|
16
24
|
from cognite.client.data_classes.data_modeling import (
|
|
17
25
|
ContainerApply,
|
|
18
26
|
ContainerId,
|
|
@@ -29,7 +37,119 @@ from cognite.client.utils._pandas_helpers import (
|
|
|
29
37
|
convert_nullable_int_cols,
|
|
30
38
|
)
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
# The Table, TableWrite data classes in the Cognite-SDK lacks the database attribute.
|
|
41
|
+
# This is a problem when creating the RawTableLoader that needs the data class to be able to create, update, retrieve
|
|
42
|
+
# and delete tables.
|
|
43
|
+
# This is a reimplemented version of the Table, TableWrite data classes with the database attribute added.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True)
|
|
47
|
+
class RawTableID:
|
|
48
|
+
table: str
|
|
49
|
+
database: str
|
|
50
|
+
|
|
51
|
+
def as_tuple(self) -> tuple[str, str]:
|
|
52
|
+
return self.database, self.table
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class RawTableCore(WriteableCogniteResource["RawTableWrite"], ABC):
|
|
56
|
+
"""A NoSQL database table to store customer data
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
name (str | None): Unique name of the table
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
name: str | None = None,
|
|
65
|
+
database: str | None = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
self.name = name
|
|
68
|
+
self.database = database
|
|
69
|
+
|
|
70
|
+
def as_id(self) -> RawTableID:
|
|
71
|
+
if self.name is None or self.database is None:
|
|
72
|
+
raise ValueError("name and database are required to create a TableID")
|
|
73
|
+
return RawTableID(table=self.name, database=self.database)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RawTable(RawTableCore):
|
|
77
|
+
"""A NoSQL database table to store customer data.
|
|
78
|
+
This is the reading version of the Table class, which is used when retrieving a table.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
name (str | None): Unique name of the table
|
|
82
|
+
created_time (int | None): Time the table was created.
|
|
83
|
+
cognite_client (CogniteClient | None): The client to associate with this object.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
name: str | None = None,
|
|
89
|
+
database: str | None = None,
|
|
90
|
+
created_time: int | None = None,
|
|
91
|
+
cognite_client: CogniteClient | None = None,
|
|
92
|
+
) -> None:
|
|
93
|
+
super().__init__(name, database)
|
|
94
|
+
self.created_time = created_time
|
|
95
|
+
self._cognite_client = cast("CogniteClient", cognite_client)
|
|
96
|
+
|
|
97
|
+
self._db_name: str | None = None
|
|
98
|
+
|
|
99
|
+
def as_write(self) -> "RawTableWrite":
|
|
100
|
+
"""Returns this Table as a TableWrite"""
|
|
101
|
+
if self.name is None or self.database is None:
|
|
102
|
+
raise ValueError("name and database are required to create a Table")
|
|
103
|
+
return RawTableWrite(name=self.name, database=self.database)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class RawTableWrite(RawTableCore):
|
|
107
|
+
"""A NoSQL database table to store customer data
|
|
108
|
+
This is the writing version of the Table class, which is used when creating a table.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
name (str): Unique name of the table
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
name: str,
|
|
117
|
+
database: str,
|
|
118
|
+
) -> None:
|
|
119
|
+
super().__init__(name, database)
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> "RawTableWrite":
|
|
123
|
+
return cls(resource["name"], resource["database"])
|
|
124
|
+
|
|
125
|
+
def as_write(self) -> "RawTableWrite":
|
|
126
|
+
"""Returns this TableWrite instance."""
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class RawTableWriteList(CogniteResourceList[RawTableWrite]):
|
|
131
|
+
_RESOURCE = RawTableWrite
|
|
132
|
+
|
|
133
|
+
def as_ids(self) -> list[RawTableID]:
|
|
134
|
+
"""Returns this TableWriteList as a list of TableIDs"""
|
|
135
|
+
return [table.as_id() for table in self.data]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class RawTableList(
|
|
139
|
+
WriteableCogniteResourceList[RawTableWrite, RawTable],
|
|
140
|
+
):
|
|
141
|
+
_RESOURCE = RawTable
|
|
142
|
+
|
|
143
|
+
def as_write(self) -> RawTableWriteList:
|
|
144
|
+
"""Returns this TableList as a TableWriteList"""
|
|
145
|
+
return RawTableWriteList([table.as_write() for table in self.data])
|
|
146
|
+
|
|
147
|
+
def as_ids(self) -> list[RawTableID]:
|
|
148
|
+
"""Returns this TableList as a list of TableIDs"""
|
|
149
|
+
return [table.as_id() for table in self.data]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
T_ID = TypeVar("T_ID", bound=Hashable)
|
|
33
153
|
|
|
34
154
|
|
|
35
155
|
# Inheriting from dict as we are extending it,
|
|
@@ -3,6 +3,7 @@ from graphlib import TopologicalSorter
|
|
|
3
3
|
from typing import Any, Literal, cast
|
|
4
4
|
|
|
5
5
|
from cognite.client import CogniteClient
|
|
6
|
+
from cognite.client.data_classes import filters
|
|
6
7
|
from cognite.client.data_classes._base import (
|
|
7
8
|
T_CogniteResourceList,
|
|
8
9
|
T_WritableCogniteResource,
|
|
@@ -95,6 +96,42 @@ class SpaceLoader(DataModelingLoader[str, SpaceApply, Space, SpaceApplyList, Spa
|
|
|
95
96
|
ids = [cast(Space | SpaceApply, item).space for item in ids]
|
|
96
97
|
return self.client.data_modeling.spaces.delete(cast(SequenceNotStr[str], ids))
|
|
97
98
|
|
|
99
|
+
def clean(self, space: str) -> None:
|
|
100
|
+
"""Deletes all data in a space.
|
|
101
|
+
|
|
102
|
+
This means all nodes, edges, views, containers, and data models located in the given space.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
client: Connected CogniteClient
|
|
106
|
+
space: The space to delete.
|
|
107
|
+
|
|
108
|
+
"""
|
|
109
|
+
edges = self.client.data_modeling.instances.list(
|
|
110
|
+
"edge", limit=-1, filter=filters.Equals(["edge", "space"], space)
|
|
111
|
+
)
|
|
112
|
+
if edges:
|
|
113
|
+
instances = self.client.data_modeling.instances.delete(edges=edges.as_ids())
|
|
114
|
+
print(f"Deleted {len(instances.edges)} edges")
|
|
115
|
+
nodes = self.client.data_modeling.instances.list(
|
|
116
|
+
"node", limit=-1, filter=filters.Equals(["node", "space"], space)
|
|
117
|
+
)
|
|
118
|
+
if nodes:
|
|
119
|
+
instances = self.client.data_modeling.instances.delete(nodes=nodes.as_ids())
|
|
120
|
+
print(f"Deleted {len(instances.nodes)} nodes")
|
|
121
|
+
views = self.client.data_modeling.views.list(limit=-1, space=space)
|
|
122
|
+
if views:
|
|
123
|
+
deleted_views = self.client.data_modeling.views.delete(views.as_ids())
|
|
124
|
+
print(f"Deleted {len(deleted_views)} views")
|
|
125
|
+
containers = self.client.data_modeling.containers.list(limit=-1, space=space)
|
|
126
|
+
if containers:
|
|
127
|
+
deleted_containers = self.client.data_modeling.containers.delete(containers.as_ids())
|
|
128
|
+
print(f"Deleted {len(deleted_containers)} containers")
|
|
129
|
+
if data_models := self.client.data_modeling.data_models.list(limit=-1, space=space):
|
|
130
|
+
deleted_data_models = self.client.data_modeling.data_models.delete(data_models.as_ids())
|
|
131
|
+
print(f"Deleted {len(deleted_data_models)} data models")
|
|
132
|
+
deleted_space = self.client.data_modeling.spaces.delete(space)
|
|
133
|
+
print(f"Deleted space {deleted_space}")
|
|
134
|
+
|
|
98
135
|
|
|
99
136
|
class ViewLoader(DataModelingLoader[ViewId, ViewApply, View, ViewApplyList, ViewList]):
|
|
100
137
|
resource_name = "views"
|
|
@@ -15,8 +15,9 @@ from cognite.client.data_classes import (
|
|
|
15
15
|
from cognite.client.exceptions import CogniteAPIError
|
|
16
16
|
from cognite.client.utils.useful_types import SequenceNotStr
|
|
17
17
|
|
|
18
|
+
from cognite.neat.utils.cdf.data_classes import RawTable, RawTableID, RawTableList, RawTableWrite, RawTableWriteList
|
|
19
|
+
|
|
18
20
|
from ._base import ResourceLoader
|
|
19
|
-
from .data_classes import RawTable, RawTableID, RawTableList, RawTableWrite, RawTableWriteList
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class TransformationLoader(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from collections import Counter
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
T_Element = TypeVar("T_Element")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def remove_none_elements_from_set(s: set[T_Element]) -> set[T_Element]:
|
|
9
|
+
return {x for x in s if x is not None}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def most_occurring_element(list_of_elements: list[T_Element]) -> T_Element:
|
|
13
|
+
counts = Counter(list_of_elements)
|
|
14
|
+
return counts.most_common(1)[0][0]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def chunker(sequence: Sequence[T_Element], chunk_size: int) -> list[Sequence[T_Element]]:
|
|
18
|
+
return [sequence[i : i + chunk_size] for i in range(0, len(sequence), chunk_size)]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Any, Literal, TypeAlias, cast, overload
|
|
3
|
+
|
|
4
|
+
from pydantic import HttpUrl, TypeAdapter, ValidationError
|
|
5
|
+
from rdflib import Literal as RdfLiteral
|
|
6
|
+
from rdflib import Namespace, URIRef
|
|
7
|
+
|
|
8
|
+
Triple: TypeAlias = tuple[URIRef, URIRef, RdfLiteral | URIRef]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@overload
|
|
12
|
+
def remove_namespace_from_uri(
|
|
13
|
+
*URI: URIRef | str,
|
|
14
|
+
special_separator: str = "#_",
|
|
15
|
+
validation: Literal["full", "prefix"] = "prefix",
|
|
16
|
+
) -> str: ...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@overload
|
|
20
|
+
def remove_namespace_from_uri(
|
|
21
|
+
*URI: tuple[URIRef | str, ...],
|
|
22
|
+
special_separator: str = "#_",
|
|
23
|
+
validation: Literal["full", "prefix"] = "prefix",
|
|
24
|
+
) -> tuple[str, ...]: ...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def remove_namespace_from_uri(
|
|
28
|
+
*URI: URIRef | str | tuple[URIRef | str, ...],
|
|
29
|
+
special_separator: str = "#_",
|
|
30
|
+
validation: Literal["full", "prefix"] = "prefix",
|
|
31
|
+
) -> tuple[str, ...] | str:
|
|
32
|
+
"""Removes namespace from URI
|
|
33
|
+
|
|
34
|
+
Args
|
|
35
|
+
URI: URIRef | str
|
|
36
|
+
URI of an entity
|
|
37
|
+
special_separator : str
|
|
38
|
+
Special separator to use instead of # or / if present in URI
|
|
39
|
+
Set by default to "#_" which covers special client use case
|
|
40
|
+
validation: str
|
|
41
|
+
Validation type to use for URI. If set to "full", URI will be validated using pydantic
|
|
42
|
+
If set to "prefix", only check if URI starts with http or https will be made
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
Entities id without namespace
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
|
|
49
|
+
>>> remove_namespace_from_uri("http://www.example.org/index.html#section2")
|
|
50
|
+
'section2'
|
|
51
|
+
>>> remove_namespace_from_uri("http://www.example.org/index.html#section2", "http://www.example.org/index.html#section3")
|
|
52
|
+
('section2', 'section3')
|
|
53
|
+
"""
|
|
54
|
+
if isinstance(URI, str | URIRef):
|
|
55
|
+
uris = (URI,)
|
|
56
|
+
elif isinstance(URI, tuple):
|
|
57
|
+
# Assume that all elements in the tuple are of the same type following type hint
|
|
58
|
+
uris = cast(tuple[URIRef | str, ...], URI)
|
|
59
|
+
else:
|
|
60
|
+
raise TypeError(f"URI must be of type URIRef or str, got {type(URI)}")
|
|
61
|
+
|
|
62
|
+
output = []
|
|
63
|
+
for u in uris:
|
|
64
|
+
if validation == "full":
|
|
65
|
+
try:
|
|
66
|
+
_ = TypeAdapter(HttpUrl).validate_python(u)
|
|
67
|
+
output.append(u.split(special_separator if special_separator in u else "#" if "#" in u else "/")[-1])
|
|
68
|
+
except ValidationError:
|
|
69
|
+
output.append(str(u))
|
|
70
|
+
else:
|
|
71
|
+
if u.lower().startswith("http"):
|
|
72
|
+
output.append(u.split(special_separator if special_separator in u else "#" if "#" in u else "/")[-1])
|
|
73
|
+
else:
|
|
74
|
+
output.append(str(u))
|
|
75
|
+
|
|
76
|
+
return tuple(output) if len(output) > 1 else output[0]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_namespace(URI: URIRef, special_separator: str = "#_") -> str:
|
|
80
|
+
"""Removes namespace from URI
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
URI : URIRef
|
|
85
|
+
URI of an entity
|
|
86
|
+
special_separator : str
|
|
87
|
+
Special separator to use instead of # or / if present in URI
|
|
88
|
+
Set by default to "#_" which covers special client use case
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
str
|
|
93
|
+
Entity id without namespace
|
|
94
|
+
"""
|
|
95
|
+
if special_separator in URI:
|
|
96
|
+
return URI.split(special_separator)[0] + special_separator
|
|
97
|
+
elif "#" in URI:
|
|
98
|
+
return URI.split("#")[0] + "#"
|
|
99
|
+
else:
|
|
100
|
+
return "/".join(URI.split("/")[:-1]) + "/"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def as_neat_compliant_uri(uri: URIRef) -> URIRef:
|
|
104
|
+
namespace = get_namespace(uri)
|
|
105
|
+
id_ = remove_namespace_from_uri(uri)
|
|
106
|
+
compliant_uri = re.sub(r"[^a-zA-Z0-9-_.]", "", id_)
|
|
107
|
+
return URIRef(f"{namespace}{compliant_uri}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def convert_rdflib_content(content: RdfLiteral | URIRef | dict | list) -> Any:
|
|
111
|
+
if isinstance(content, RdfLiteral) or isinstance(content, URIRef):
|
|
112
|
+
return content.toPython()
|
|
113
|
+
elif isinstance(content, dict):
|
|
114
|
+
return {key: convert_rdflib_content(value) for key, value in content.items()}
|
|
115
|
+
elif isinstance(content, list):
|
|
116
|
+
return [convert_rdflib_content(item) for item in content]
|
|
117
|
+
else:
|
|
118
|
+
return content
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def uri_to_short_form(URI: URIRef, prefixes: dict[str, Namespace]) -> str | URIRef:
|
|
122
|
+
"""Returns the short form of a URI if its namespace is present in the prefixes dict,
|
|
123
|
+
otherwise returns the URI itself
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
URI: URI to be converted to form prefix:entityName
|
|
127
|
+
prefixes: dict of prefixes
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
shortest form of the URI if its namespace is present in the prefixes dict,
|
|
131
|
+
otherwise returns the URI itself
|
|
132
|
+
"""
|
|
133
|
+
uris: set[str | URIRef] = {URI}
|
|
134
|
+
for prefix, namespace in prefixes.items():
|
|
135
|
+
if URI.startswith(namespace):
|
|
136
|
+
uris.add(f"{prefix}:{URI.replace(namespace, '')}")
|
|
137
|
+
return min(uris, key=len)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _traverse(hierarchy: dict, graph: dict, names: list[str]) -> dict:
|
|
141
|
+
"""traverse the graph and return the hierarchy"""
|
|
142
|
+
for name in names:
|
|
143
|
+
hierarchy[name] = _traverse({}, graph, graph[name])
|
|
144
|
+
return hierarchy
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_inheritance_path(child: Any, child_parent: dict[Any, list[Any]]) -> list:
|
|
148
|
+
"""Returns the inheritance path for a given child
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
child: Child class
|
|
152
|
+
child_parent: Dictionary of child to parent relationship
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Inheritance path for a given child
|
|
156
|
+
|
|
157
|
+
!!! note "No Circular Inheritance"
|
|
158
|
+
This method assumes that the child_parent dictionary is a tree and does not contain any cycles.
|
|
159
|
+
"""
|
|
160
|
+
path = []
|
|
161
|
+
if child in child_parent:
|
|
162
|
+
path.extend(child_parent[child])
|
|
163
|
+
for parent in child_parent[child]:
|
|
164
|
+
path.extend(get_inheritance_path(parent, child_parent))
|
|
165
|
+
return path
|
cognite/neat/utils/text.py
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
if sys.version_info >= (3, 11):
|
|
5
|
+
from datetime import UTC
|
|
6
|
+
else:
|
|
7
|
+
from datetime import timezone
|
|
8
|
+
|
|
9
|
+
UTC = timezone.utc
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def epoch_now_ms() -> int:
|
|
13
|
+
return int((datetime.now(UTC) - datetime(1970, 1, 1, tzinfo=UTC)).total_seconds() * 1000)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def datetime_utc_now():
|
|
17
|
+
return datetime.now(UTC)
|
cognite/neat/utils/upload.py
CHANGED
|
@@ -36,6 +36,7 @@ class UploadResultList(NeatList[UploadResultCore]): ...
|
|
|
36
36
|
@dataclass
|
|
37
37
|
class UploadResult(UploadResultCore, Generic[T_ID]):
|
|
38
38
|
created: set[T_ID] = field(default_factory=set)
|
|
39
|
+
upserted: set[T_ID] = field(default_factory=set)
|
|
39
40
|
deleted: set[T_ID] = field(default_factory=set)
|
|
40
41
|
changed: set[T_ID] = field(default_factory=set)
|
|
41
42
|
unchanged: set[T_ID] = field(default_factory=set)
|
|
@@ -53,12 +54,21 @@ class UploadResult(UploadResultCore, Generic[T_ID]):
|
|
|
53
54
|
|
|
54
55
|
@property
|
|
55
56
|
def success(self) -> int:
|
|
56
|
-
return
|
|
57
|
+
return (
|
|
58
|
+
len(self.created)
|
|
59
|
+
+ len(self.deleted)
|
|
60
|
+
+ len(self.changed)
|
|
61
|
+
+ len(self.upserted)
|
|
62
|
+
+ len(self.unchanged)
|
|
63
|
+
+ len(self.skipped)
|
|
64
|
+
)
|
|
57
65
|
|
|
58
66
|
def dump(self, aggregate: bool = True) -> dict[str, Any]:
|
|
59
67
|
output = super().dump(aggregate)
|
|
60
68
|
if self.created:
|
|
61
69
|
output["created"] = len(self.created) if aggregate else list(self.created)
|
|
70
|
+
if self.upserted:
|
|
71
|
+
output["upserted"] = len(self.upserted) if aggregate else list(self.upserted)
|
|
62
72
|
if self.deleted:
|
|
63
73
|
output["deleted"] = len(self.deleted) if aggregate else list(self.deleted)
|
|
64
74
|
if self.changed:
|
|
@@ -69,6 +79,8 @@ class UploadResult(UploadResultCore, Generic[T_ID]):
|
|
|
69
79
|
output["skipped"] = len(self.skipped) if aggregate else list(self.skipped)
|
|
70
80
|
if self.failed_created:
|
|
71
81
|
output["failed_created"] = len(self.failed_created) if aggregate else list(self.failed_created)
|
|
82
|
+
if self.failed_upserted:
|
|
83
|
+
output["failed_upserted"] = len(self.failed_upserted) if aggregate else list(self.failed_upserted)
|
|
72
84
|
if self.failed_changed:
|
|
73
85
|
output["failed_changed"] = len(self.failed_changed) if aggregate else list(self.failed_changed)
|
|
74
86
|
if self.failed_deleted:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from cognite.neat.
|
|
1
|
+
from cognite.neat.exceptions import NeatException
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class InvalidStepOutputException(
|
|
4
|
+
class InvalidStepOutputException(NeatException):
|
|
5
5
|
type_ = "invalid_step_output"
|
|
6
6
|
code = 400
|
|
7
7
|
description = "The step output is invalid."
|
|
@@ -12,7 +12,7 @@ class InvalidStepOutputException(NeatError):
|
|
|
12
12
|
super().__init__(self.message)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class ConfigurationNotSet(
|
|
15
|
+
class ConfigurationNotSet(NeatException):
|
|
16
16
|
type_ = "configuration_not_set"
|
|
17
17
|
code = 400
|
|
18
18
|
description = "The configuration is not set."
|
|
@@ -29,13 +29,13 @@ class ConfigurationNotSet(NeatError):
|
|
|
29
29
|
return self.message
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
class StepNotInitialized(
|
|
32
|
+
class StepNotInitialized(NeatException):
|
|
33
33
|
def __init__(self, step_name: str):
|
|
34
34
|
self.message = f"Step {step_name} has not been initialized."
|
|
35
35
|
super().__init__(self.message)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class StepFlowContextNotInitialized(
|
|
38
|
+
class StepFlowContextNotInitialized(NeatException):
|
|
39
39
|
def __init__(self, step_name: str):
|
|
40
40
|
self.message = f"Step {step_name} requires flow context which is missing."
|
|
41
41
|
super().__init__(self.message)
|
cognite/neat/workflows/base.py
CHANGED
|
@@ -13,7 +13,7 @@ from prometheus_client import Gauge
|
|
|
13
13
|
|
|
14
14
|
from cognite.neat.app.monitoring.metrics import NeatMetricsCollector
|
|
15
15
|
from cognite.neat.config import Config
|
|
16
|
-
from cognite.neat.utils.
|
|
16
|
+
from cognite.neat.utils.auxiliary import retry_decorator
|
|
17
17
|
from cognite.neat.workflows import cdf_store, utils
|
|
18
18
|
from cognite.neat.workflows._exceptions import ConfigurationNotSet, InvalidStepOutputException
|
|
19
19
|
from cognite.neat.workflows.cdf_store import CdfStore
|
|
@@ -9,7 +9,7 @@ from cognite.neat.rules.issues import IssueList
|
|
|
9
9
|
from cognite.neat.rules.issues.dms import MissingContainerError, MissingSpaceError, MissingViewError
|
|
10
10
|
from cognite.neat.rules.issues.formatters import FORMATTER_BY_NAME
|
|
11
11
|
from cognite.neat.rules.models import DMSRules, SchemaCompleteness
|
|
12
|
-
from cognite.neat.utils import
|
|
12
|
+
from cognite.neat.utils.cdf.loaders import ViewLoader
|
|
13
13
|
from cognite.neat.workflows._exceptions import StepNotInitialized
|
|
14
14
|
from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
|
|
15
15
|
from cognite.neat.workflows.steps.data_contracts import MultiRuleData
|
|
@@ -69,7 +69,7 @@ class ValidateRulesAgainstCDF(Step):
|
|
|
69
69
|
retrieved_containers = cdf_client.data_modeling.containers.retrieve(missing_containers).as_write()
|
|
70
70
|
# Converting read format of views to write format requires to account for parents (implements)
|
|
71
71
|
# Thus we use the loader to convert the views to write format.
|
|
72
|
-
view_loader =
|
|
72
|
+
view_loader = ViewLoader(cdf_client)
|
|
73
73
|
retrieved_views = [
|
|
74
74
|
view_loader.as_write(view) for view in cdf_client.data_modeling.views.retrieve(missing_views)
|
|
75
75
|
]
|
|
@@ -14,7 +14,7 @@ from cognite.neat.legacy.graph.extractors._mock_graph_generator import (
|
|
|
14
14
|
generate_triples as generate_mock_triples,
|
|
15
15
|
)
|
|
16
16
|
from cognite.neat.legacy.rules.exporters._rules2triples import get_instances_as_triples
|
|
17
|
-
from cognite.neat.utils.
|
|
17
|
+
from cognite.neat.utils.auxiliary import create_sha256_hash
|
|
18
18
|
from cognite.neat.workflows._exceptions import StepNotInitialized
|
|
19
19
|
from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
|
|
20
20
|
from cognite.neat.workflows.steps.data_contracts import (
|
|
@@ -26,7 +26,7 @@ from cognite.neat.legacy.graph.loaders.core.rdf_to_relationships import (
|
|
|
26
26
|
from cognite.neat.legacy.graph.loaders.rdf_to_dms import upload_edges, upload_nodes
|
|
27
27
|
from cognite.neat.legacy.graph.loaders.validator import validate_asset_hierarchy
|
|
28
28
|
from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType
|
|
29
|
-
from cognite.neat.utils.
|
|
29
|
+
from cognite.neat.utils.auxiliary import generate_exception_report
|
|
30
30
|
from cognite.neat.workflows._exceptions import StepFlowContextNotInitialized, StepNotInitialized
|
|
31
31
|
from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
|
|
32
32
|
from cognite.neat.workflows.steps.data_contracts import (
|
|
@@ -12,7 +12,7 @@ from cognite.neat.legacy.rules import exporters
|
|
|
12
12
|
from cognite.neat.legacy.rules.exporters._rules2dms import DMSSchemaComponents
|
|
13
13
|
from cognite.neat.legacy.rules.exporters._rules2graphql import GraphQLSchema
|
|
14
14
|
from cognite.neat.legacy.rules.exporters._rules2ontology import Ontology
|
|
15
|
-
from cognite.neat.utils.
|
|
15
|
+
from cognite.neat.utils.auxiliary import generate_exception_report
|
|
16
16
|
from cognite.neat.workflows._exceptions import StepNotInitialized
|
|
17
17
|
from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
|
|
18
18
|
from cognite.neat.workflows.steps.data_contracts import CogniteClient, DMSSchemaComponentsData, RulesData
|
|
@@ -13,7 +13,7 @@ from cognite.neat.legacy.rules import exporters, importers
|
|
|
13
13
|
from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType
|
|
14
14
|
from cognite.neat.legacy.rules.models.rules import Class, Classes, Metadata, Properties, Property, Rules
|
|
15
15
|
from cognite.neat.legacy.rules.models.value_types import ValueType
|
|
16
|
-
from cognite.neat.utils.
|
|
16
|
+
from cognite.neat.utils.auxiliary import generate_exception_report
|
|
17
17
|
from cognite.neat.workflows import utils
|
|
18
18
|
from cognite.neat.workflows._exceptions import StepNotInitialized
|
|
19
19
|
from cognite.neat.workflows.cdf_store import CdfStore
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 0.87.
|
|
3
|
+
Version: 0.87.3
|
|
4
4
|
Summary: Knowledge graph transformation
|
|
5
5
|
Home-page: https://cognite-neat.readthedocs-hosted.com/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -21,7 +21,7 @@ Provides-Extra: oxi
|
|
|
21
21
|
Provides-Extra: service
|
|
22
22
|
Requires-Dist: PyYAML
|
|
23
23
|
Requires-Dist: backports.strenum (>=1.2,<2.0) ; python_version < "3.11"
|
|
24
|
-
Requires-Dist: cognite-sdk (>=7.
|
|
24
|
+
Requires-Dist: cognite-sdk (>=7.54.6,<8.0.0)
|
|
25
25
|
Requires-Dist: deepdiff
|
|
26
26
|
Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0) ; python_version < "3.11"
|
|
27
27
|
Requires-Dist: fastapi (>=0,<1) ; extra == "service" or extra == "all"
|