cognite-neat 0.96.6__py3-none-any.whl → 0.97.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.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_constants.py +3 -1
- cognite/neat/_graph/extractors/__init__.py +3 -0
- cognite/neat/_graph/extractors/_base.py +1 -1
- cognite/neat/_graph/extractors/_classic_cdf/_assets.py +1 -1
- cognite/neat/_graph/extractors/_classic_cdf/_base.py +1 -1
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +1 -1
- cognite/neat/_graph/extractors/_classic_cdf/_data_sets.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/_dms.py +1 -1
- cognite/neat/_graph/extractors/_iodd.py +1 -1
- cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
- cognite/neat/_graph/extractors/_rdf_file.py +1 -1
- cognite/neat/_graph/loaders/_rdf2dms.py +1 -1
- cognite/neat/_graph/queries/_base.py +1 -1
- cognite/neat/_graph/transformers/__init__.py +3 -1
- cognite/neat/_graph/transformers/_rdfpath.py +60 -1
- cognite/neat/_issues/errors/__init__.py +2 -0
- cognite/neat/_issues/errors/_properties.py +12 -0
- cognite/neat/_issues/warnings/__init__.py +2 -0
- cognite/neat/_issues/warnings/_models.py +11 -0
- cognite/neat/_rules/importers/__init__.py +11 -0
- cognite/neat/_rules/importers/_base.py +7 -0
- cognite/neat/_rules/importers/_dms2rules.py +12 -3
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +17 -2
- cognite/neat/_rules/models/asset/_rules.py +6 -2
- cognite/neat/_rules/models/asset/_rules_input.py +6 -1
- cognite/neat/_rules/models/data_types.py +6 -0
- cognite/neat/_rules/models/dms/_rules.py +8 -1
- cognite/neat/_rules/models/dms/_rules_input.py +8 -0
- cognite/neat/_rules/models/dms/_validation.py +64 -2
- cognite/neat/_rules/models/domain.py +10 -0
- cognite/neat/_rules/models/entities/_loaders.py +3 -5
- cognite/neat/_rules/models/information/_rules.py +6 -2
- cognite/neat/_rules/models/information/_rules_input.py +6 -1
- cognite/neat/_rules/transformers/_base.py +7 -0
- cognite/neat/_rules/transformers/_converters.py +56 -4
- cognite/neat/_session/_base.py +94 -23
- cognite/neat/_session/_inspect.py +12 -4
- cognite/neat/_session/_prepare.py +144 -21
- cognite/neat/_session/_read.py +137 -30
- cognite/neat/_session/_set.py +22 -3
- cognite/neat/_session/_show.py +171 -45
- cognite/neat/_session/_state.py +79 -30
- cognite/neat/_session/_to.py +16 -17
- cognite/neat/_session/engine/__init__.py +4 -0
- cognite/neat/_session/engine/_import.py +7 -0
- cognite/neat/_session/engine/_interface.py +24 -0
- cognite/neat/_session/engine/_load.py +129 -0
- cognite/neat/_session/exceptions.py +13 -3
- cognite/neat/_shared.py +6 -1
- cognite/neat/_store/_base.py +3 -24
- cognite/neat/_store/_provenance.py +185 -42
- cognite/neat/_utils/rdf_.py +34 -1
- cognite/neat/_utils/reader/__init__.py +3 -0
- cognite/neat/_utils/reader/_base.py +162 -0
- cognite/neat/_version.py +2 -1
- {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.0.dist-info}/METADATA +5 -3
- {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.0.dist-info}/RECORD +67 -62
- cognite/neat/_graph/models.py +0 -7
- {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.0.dist-info}/entry_points.txt +0 -0
cognite/neat/_session/_show.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import Any, cast
|
|
|
5
5
|
import networkx as nx
|
|
6
6
|
from IPython.display import HTML, display
|
|
7
7
|
from pyvis.network import Network as PyVisNetwork # type: ignore
|
|
8
|
+
from rdflib import URIRef
|
|
8
9
|
|
|
9
10
|
from cognite.neat._constants import IN_NOTEBOOK, IN_PYODIDE
|
|
10
11
|
from cognite.neat._rules._constants import EntityTypes
|
|
@@ -67,19 +68,28 @@ class ShowDataModelAPI(ShowBaseAPI):
|
|
|
67
68
|
def __init__(self, state: SessionState) -> None:
|
|
68
69
|
super().__init__(state)
|
|
69
70
|
self._state = state
|
|
71
|
+
self.provenance = ShowDataModelProvenanceAPI(self._state)
|
|
72
|
+
self.implements = ShowDataModelImplementsAPI(self._state)
|
|
70
73
|
|
|
71
74
|
def __call__(self) -> Any:
|
|
72
|
-
if not self._state.has_verified_rules:
|
|
75
|
+
if not self._state.data_model.has_verified_rules:
|
|
73
76
|
raise NeatSessionError(
|
|
74
77
|
"No verified data model available. Try using [bold].verify()[/bold] to verify data model."
|
|
75
78
|
)
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
rules = self._state.data_model.last_verified_rule[1]
|
|
81
|
+
|
|
82
|
+
if isinstance(rules, DMSRules):
|
|
83
|
+
di_graph = self._generate_dms_di_graph(self._state.data_model.last_verified_dms_rules[1])
|
|
79
84
|
name = "dms_data_model.html"
|
|
80
|
-
|
|
81
|
-
di_graph = self._generate_info_di_graph(self._state.last_verified_information_rules)
|
|
85
|
+
elif isinstance(rules, InformationRules):
|
|
86
|
+
di_graph = self._generate_info_di_graph(self._state.data_model.last_verified_information_rules[1])
|
|
82
87
|
name = "information_data_model.html"
|
|
88
|
+
else:
|
|
89
|
+
# This should never happen, but we need to handle it to satisfy mypy
|
|
90
|
+
raise NeatSessionError(
|
|
91
|
+
f"Unsupported type {type(rules) }. Make sure you have either information or DMS rules."
|
|
92
|
+
)
|
|
83
93
|
|
|
84
94
|
return self._generate_visualization(di_graph, name)
|
|
85
95
|
|
|
@@ -91,20 +101,7 @@ class ShowDataModelAPI(ShowBaseAPI):
|
|
|
91
101
|
for view in rules.views:
|
|
92
102
|
# if possible use human readable label coming from the view name
|
|
93
103
|
if not di_graph.has_node(view.view.suffix):
|
|
94
|
-
di_graph.add_node(view.view.suffix, label=view.
|
|
95
|
-
|
|
96
|
-
# add implements as edges
|
|
97
|
-
if view.implements:
|
|
98
|
-
for implement in view.implements:
|
|
99
|
-
if not di_graph.has_node(implement.suffix):
|
|
100
|
-
di_graph.add_node(implement.suffix, label=implement.suffix)
|
|
101
|
-
|
|
102
|
-
di_graph.add_edge(
|
|
103
|
-
view.view.suffix,
|
|
104
|
-
implement.suffix,
|
|
105
|
-
label="implements",
|
|
106
|
-
dashes=True,
|
|
107
|
-
)
|
|
104
|
+
di_graph.add_node(view.view.suffix, label=view.view.suffix)
|
|
108
105
|
|
|
109
106
|
# Add nodes and edges from Properties sheet
|
|
110
107
|
for prop_ in rules.properties:
|
|
@@ -133,8 +130,89 @@ class ShowDataModelAPI(ShowBaseAPI):
|
|
|
133
130
|
label=class_.name or class_.class_.suffix,
|
|
134
131
|
)
|
|
135
132
|
|
|
133
|
+
# Add nodes and edges from Properties sheet
|
|
134
|
+
for prop_ in rules.properties:
|
|
135
|
+
if prop_.type_ == EntityTypes.object_property:
|
|
136
|
+
if not di_graph.has_node(prop_.class_.suffix):
|
|
137
|
+
di_graph.add_node(prop_.class_.suffix, label=prop_.class_.suffix)
|
|
138
|
+
|
|
139
|
+
di_graph.add_edge(
|
|
140
|
+
prop_.class_.suffix,
|
|
141
|
+
cast(ClassEntity, prop_.value_type).suffix,
|
|
142
|
+
label=prop_.name or prop_.property_,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return di_graph
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@intercept_session_exceptions
|
|
149
|
+
class ShowDataModelImplementsAPI(ShowBaseAPI):
|
|
150
|
+
def __init__(self, state: SessionState) -> None:
|
|
151
|
+
super().__init__(state)
|
|
152
|
+
self._state = state
|
|
153
|
+
|
|
154
|
+
def __call__(self) -> Any:
|
|
155
|
+
if not self._state.data_model.has_verified_rules:
|
|
156
|
+
raise NeatSessionError(
|
|
157
|
+
"No verified data model available. Try using [bold].verify()[/bold] to verify data model."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
rules = self._state.data_model.last_verified_rule[1]
|
|
161
|
+
|
|
162
|
+
if isinstance(rules, DMSRules):
|
|
163
|
+
di_graph = self._generate_dms_di_graph(self._state.data_model.last_verified_dms_rules[1])
|
|
164
|
+
name = "dms_data_model_implements.html"
|
|
165
|
+
elif isinstance(rules, InformationRules):
|
|
166
|
+
di_graph = self._generate_info_di_graph(self._state.data_model.last_verified_information_rules[1])
|
|
167
|
+
name = "information_data_model_implements.html"
|
|
168
|
+
else:
|
|
169
|
+
# This should never happen, but we need to handle it to satisfy mypy
|
|
170
|
+
raise NeatSessionError(
|
|
171
|
+
f"Unsupported type {type(rules) }. Make sure you have either information or DMS rules."
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return self._generate_visualization(di_graph, name)
|
|
175
|
+
|
|
176
|
+
def _generate_dms_di_graph(self, rules: DMSRules) -> nx.DiGraph:
|
|
177
|
+
"""Generate a DiGraph from the last verified DMS rules."""
|
|
178
|
+
di_graph = nx.DiGraph()
|
|
179
|
+
|
|
180
|
+
# Add nodes and edges from Views sheet
|
|
181
|
+
for view in rules.views:
|
|
182
|
+
# add implements as edges
|
|
183
|
+
if view.implements:
|
|
184
|
+
if not di_graph.has_node(view.view.suffix):
|
|
185
|
+
di_graph.add_node(view.view.suffix, label=view.view.suffix)
|
|
186
|
+
for implement in view.implements:
|
|
187
|
+
if not di_graph.has_node(implement.suffix):
|
|
188
|
+
di_graph.add_node(implement.suffix, label=implement.suffix)
|
|
189
|
+
|
|
190
|
+
di_graph.add_edge(
|
|
191
|
+
view.view.suffix,
|
|
192
|
+
implement.suffix,
|
|
193
|
+
label="implements",
|
|
194
|
+
dashes=True,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return di_graph
|
|
198
|
+
|
|
199
|
+
def _generate_info_di_graph(self, rules: InformationRules) -> nx.DiGraph:
|
|
200
|
+
"""Generate DiGraph representing information data model."""
|
|
201
|
+
|
|
202
|
+
di_graph = nx.DiGraph()
|
|
203
|
+
|
|
204
|
+
# Add nodes and edges from Views sheet
|
|
205
|
+
for class_ in rules.classes:
|
|
206
|
+
# if possible use human readable label coming from the view name
|
|
207
|
+
|
|
136
208
|
# add subClassOff as edges
|
|
137
209
|
if class_.parent:
|
|
210
|
+
if not di_graph.has_node(class_.class_.suffix):
|
|
211
|
+
di_graph.add_node(
|
|
212
|
+
class_.class_.suffix,
|
|
213
|
+
label=class_.name or class_.class_.suffix,
|
|
214
|
+
)
|
|
215
|
+
|
|
138
216
|
for parent in class_.parent:
|
|
139
217
|
if not di_graph.has_node(parent.suffix):
|
|
140
218
|
di_graph.add_node(parent.suffix, label=parent.suffix)
|
|
@@ -145,20 +223,68 @@ class ShowDataModelAPI(ShowBaseAPI):
|
|
|
145
223
|
dashes=True,
|
|
146
224
|
)
|
|
147
225
|
|
|
148
|
-
|
|
149
|
-
for prop_ in rules.properties:
|
|
150
|
-
if prop_.type_ == EntityTypes.object_property:
|
|
151
|
-
if not di_graph.has_node(prop_.class_.suffix):
|
|
152
|
-
di_graph.add_node(prop_.class_.suffix, label=prop_.class_.suffix)
|
|
226
|
+
return di_graph
|
|
153
227
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
228
|
+
|
|
229
|
+
@intercept_session_exceptions
|
|
230
|
+
class ShowDataModelProvenanceAPI(ShowBaseAPI):
|
|
231
|
+
def __init__(self, state: SessionState) -> None:
|
|
232
|
+
super().__init__(state)
|
|
233
|
+
self._state = state
|
|
234
|
+
|
|
235
|
+
def __call__(self) -> Any:
|
|
236
|
+
if not self._state.data_model.provenance:
|
|
237
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to load data model.")
|
|
238
|
+
|
|
239
|
+
di_graph = self._generate_dm_provenance_di_graph_and_types()
|
|
240
|
+
return self._generate_visualization(di_graph, name="data_model_provenance.html")
|
|
241
|
+
|
|
242
|
+
def _generate_dm_provenance_di_graph_and_types(self) -> nx.DiGraph:
|
|
243
|
+
di_graph = nx.DiGraph()
|
|
244
|
+
hex_colored_types = _generate_hex_color_per_type(["Agent", "Entity", "Activity"])
|
|
245
|
+
|
|
246
|
+
for change in self._state.data_model.provenance:
|
|
247
|
+
source = self._shorten_id(change.source_entity.id_)
|
|
248
|
+
target = self._shorten_id(change.target_entity.id_)
|
|
249
|
+
agent = self._shorten_id(change.agent.id_)
|
|
250
|
+
|
|
251
|
+
di_graph.add_node(
|
|
252
|
+
source,
|
|
253
|
+
label=source,
|
|
254
|
+
type="Entity",
|
|
255
|
+
title="Entity",
|
|
256
|
+
color=hex_colored_types["Entity"],
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
di_graph.add_node(
|
|
260
|
+
target,
|
|
261
|
+
label=target,
|
|
262
|
+
type="Entity",
|
|
263
|
+
title="Entity",
|
|
264
|
+
color=hex_colored_types["Entity"],
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
di_graph.add_node(
|
|
268
|
+
agent,
|
|
269
|
+
label=agent,
|
|
270
|
+
type="Agent",
|
|
271
|
+
title="Agent",
|
|
272
|
+
color=hex_colored_types["Agent"],
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
di_graph.add_edge(source, agent, label="used", color="grey")
|
|
276
|
+
di_graph.add_edge(agent, target, label="generated", color="grey")
|
|
159
277
|
|
|
160
278
|
return di_graph
|
|
161
279
|
|
|
280
|
+
@staticmethod
|
|
281
|
+
def _shorten_id(thing: URIRef) -> str:
|
|
282
|
+
if "https://cognitedata.com/dms/data-model/" in thing:
|
|
283
|
+
return "DMS(" + ",".join(thing.replace("https://cognitedata.com/dms/data-model/", "").split("/")) + ")"
|
|
284
|
+
elif "http://purl.org/cognite/neat/data-model/" in thing:
|
|
285
|
+
return "NEAT(" + ",".join(thing.replace("http://purl.org/cognite/neat/data-model/", "").split("/")) + ")"
|
|
286
|
+
return remove_namespace_from_uri(thing)
|
|
287
|
+
|
|
162
288
|
|
|
163
289
|
@intercept_session_exceptions
|
|
164
290
|
class ShowInstanceAPI(ShowBaseAPI):
|
|
@@ -167,7 +293,7 @@ class ShowInstanceAPI(ShowBaseAPI):
|
|
|
167
293
|
self._state = state
|
|
168
294
|
|
|
169
295
|
def __call__(self) -> Any:
|
|
170
|
-
if not self._state.store.graph:
|
|
296
|
+
if not self._state.instances.store.graph:
|
|
171
297
|
raise NeatSessionError("No instances available. Try using [bold].read[/bold] to load instances.")
|
|
172
298
|
|
|
173
299
|
di_graph = self._generate_instance_di_graph_and_types()
|
|
@@ -189,8 +315,8 @@ class ShowInstanceAPI(ShowBaseAPI):
|
|
|
189
315
|
|
|
190
316
|
di_graph = nx.DiGraph()
|
|
191
317
|
|
|
192
|
-
types = [type_ for type_, _ in self._state.store.queries.summarize_instances()]
|
|
193
|
-
hex_colored_types =
|
|
318
|
+
types = [type_ for type_, _ in self._state.instances.store.queries.summarize_instances()]
|
|
319
|
+
hex_colored_types = _generate_hex_color_per_type(types)
|
|
194
320
|
|
|
195
321
|
for ( # type: ignore
|
|
196
322
|
subject,
|
|
@@ -198,7 +324,7 @@ class ShowInstanceAPI(ShowBaseAPI):
|
|
|
198
324
|
object,
|
|
199
325
|
subject_type,
|
|
200
326
|
object_type,
|
|
201
|
-
) in self._state.store.graph.query(query):
|
|
327
|
+
) in self._state.instances.store.graph.query(query):
|
|
202
328
|
subject = remove_namespace_from_uri(subject)
|
|
203
329
|
property_ = remove_namespace_from_uri(property_)
|
|
204
330
|
object = remove_namespace_from_uri(object)
|
|
@@ -223,15 +349,15 @@ class ShowInstanceAPI(ShowBaseAPI):
|
|
|
223
349
|
|
|
224
350
|
return di_graph
|
|
225
351
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
352
|
+
|
|
353
|
+
def _generate_hex_color_per_type(types: list[str]) -> dict:
|
|
354
|
+
hex_colored_types = {}
|
|
355
|
+
random.seed(381)
|
|
356
|
+
for type_ in types:
|
|
357
|
+
hue = random.random()
|
|
358
|
+
saturation = random.uniform(0.5, 1.0)
|
|
359
|
+
lightness = random.uniform(0.4, 0.6)
|
|
360
|
+
rgb = colorsys.hls_to_rgb(hue, lightness, saturation)
|
|
361
|
+
hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}"
|
|
362
|
+
hex_colored_types[type_] = hex_color
|
|
363
|
+
return hex_colored_types
|
cognite/neat/_session/_state.py
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from typing import Literal, cast
|
|
3
3
|
|
|
4
|
+
from rdflib import URIRef
|
|
5
|
+
|
|
4
6
|
from cognite.neat._issues import IssueList
|
|
5
|
-
from cognite.neat._rules._shared import ReadRules, VerifiedRules
|
|
7
|
+
from cognite.neat._rules._shared import JustRules, ReadRules, VerifiedRules
|
|
6
8
|
from cognite.neat._rules.models.dms._rules import DMSRules
|
|
7
9
|
from cognite.neat._rules.models.information._rules import InformationRules
|
|
8
10
|
from cognite.neat._rules.models.information._rules_input import InformationInputRules
|
|
9
11
|
from cognite.neat._store import NeatGraphStore
|
|
12
|
+
from cognite.neat._store._provenance import Change, Provenance
|
|
10
13
|
from cognite.neat._utils.upload import UploadResultList
|
|
11
14
|
|
|
12
15
|
from .exceptions import NeatSessionError
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
@dataclass
|
|
16
18
|
class SessionState:
|
|
19
|
+
def __init__(self, store_type: Literal["memory", "oxigraph"]) -> None:
|
|
20
|
+
self.instances = InstancesState(store_type)
|
|
21
|
+
self.data_model = DataModelState()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class InstancesState:
|
|
17
26
|
store_type: Literal["memory", "oxigraph"]
|
|
18
|
-
input_rules: list[ReadRules] = field(default_factory=list)
|
|
19
|
-
verified_rules: list[VerifiedRules] = field(default_factory=list)
|
|
20
27
|
issue_lists: list[IssueList] = field(default_factory=list)
|
|
21
28
|
outcome: list[UploadResultList] = field(default_factory=list)
|
|
22
29
|
_store: NeatGraphStore | None = field(init=False, default=None)
|
|
@@ -31,62 +38,96 @@ class SessionState:
|
|
|
31
38
|
return cast(NeatGraphStore, self._store)
|
|
32
39
|
|
|
33
40
|
@property
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
raise NeatSessionError("No input data model available. Try using [bold].read[/bold] to load a data model.")
|
|
37
|
-
return self.input_rules[-1]
|
|
41
|
+
def has_store(self) -> bool:
|
|
42
|
+
return self._store is not None
|
|
38
43
|
|
|
39
44
|
@property
|
|
40
45
|
def last_outcome(self) -> UploadResultList:
|
|
41
46
|
if not self.outcome:
|
|
42
47
|
raise NeatSessionError(
|
|
43
|
-
"No outcome available. Try using [bold].to.cdf[/bold] to upload a data
|
|
48
|
+
"No outcome available. Try using [bold].to.cdf.instances[/bold] to upload a data minstances."
|
|
44
49
|
)
|
|
45
50
|
return self.outcome[-1]
|
|
46
51
|
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class DataModelState:
|
|
55
|
+
_rules: dict[URIRef, ReadRules | JustRules | VerifiedRules] = field(init=False, default_factory=dict)
|
|
56
|
+
issue_lists: list[IssueList] = field(default_factory=list)
|
|
57
|
+
provenance: Provenance = field(default_factory=Provenance)
|
|
58
|
+
outcome: list[UploadResultList] = field(default_factory=list)
|
|
59
|
+
|
|
60
|
+
def write(self, rules: ReadRules | JustRules | VerifiedRules, change: Change) -> None:
|
|
61
|
+
if change.target_entity.id_ in self._rules:
|
|
62
|
+
raise NeatSessionError(f"Data model <{change.target_entity.id_}> already exists.")
|
|
63
|
+
|
|
64
|
+
else:
|
|
65
|
+
self._rules[change.target_entity.id_] = rules
|
|
66
|
+
self.provenance.append(change)
|
|
67
|
+
|
|
68
|
+
def read(self, id_: URIRef) -> ReadRules | JustRules | VerifiedRules:
|
|
69
|
+
if id_ not in self._rules:
|
|
70
|
+
raise NeatSessionError(f"Data model <{id_}> not found.")
|
|
71
|
+
return self._rules[id_]
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def unverified_rules(self) -> dict[URIRef, ReadRules]:
|
|
75
|
+
return {id_: rules for id_, rules in self._rules.items() if isinstance(rules, ReadRules)}
|
|
76
|
+
|
|
47
77
|
@property
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
def verified_rules(self) -> dict[URIRef, VerifiedRules]:
|
|
79
|
+
return {id_: rules for id_, rules in self._rules.items() if isinstance(rules, VerifiedRules)}
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def last_unverified_rule(self) -> tuple[URIRef, ReadRules]:
|
|
83
|
+
if not self.unverified_rules:
|
|
84
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to load a data model.")
|
|
85
|
+
return next(reversed(self.unverified_rules.items()))
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def last_info_unverified_rule(self) -> tuple[URIRef, ReadRules]:
|
|
89
|
+
if self.unverified_rules:
|
|
90
|
+
for id_, rule in reversed(self.unverified_rules.items()):
|
|
51
91
|
if isinstance(rule.rules, InformationInputRules):
|
|
52
|
-
return rule
|
|
53
|
-
|
|
92
|
+
return id_, rule
|
|
93
|
+
|
|
94
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to load a data model.")
|
|
54
95
|
|
|
55
96
|
@property
|
|
56
|
-
def last_verified_rule(self) -> VerifiedRules:
|
|
97
|
+
def last_verified_rule(self) -> tuple[URIRef, VerifiedRules]:
|
|
57
98
|
if not self.verified_rules:
|
|
58
99
|
raise NeatSessionError(
|
|
59
100
|
"No data model available to verify. Try using [bold].read[/bold] to load a data model."
|
|
60
101
|
)
|
|
61
|
-
return self.verified_rules
|
|
102
|
+
return next(reversed(self.verified_rules.items()))
|
|
62
103
|
|
|
63
104
|
@property
|
|
64
|
-
def
|
|
105
|
+
def last_verified_information_rules(self) -> tuple[URIRef, InformationRules]:
|
|
65
106
|
if self.verified_rules:
|
|
66
|
-
for rules in self.verified_rules
|
|
67
|
-
if isinstance(rules,
|
|
68
|
-
return rules
|
|
107
|
+
for id_, rules in reversed(self.verified_rules.items()):
|
|
108
|
+
if isinstance(rules, InformationRules):
|
|
109
|
+
return id_, rules
|
|
69
110
|
|
|
70
111
|
raise NeatSessionError(
|
|
71
|
-
|
|
72
|
-
" to convert
|
|
112
|
+
"No verified information data model. Try using [bold].verify()[/bold]"
|
|
113
|
+
" to convert unverified information model to verified information model."
|
|
73
114
|
)
|
|
74
115
|
|
|
75
116
|
@property
|
|
76
|
-
def
|
|
117
|
+
def last_verified_dms_rules(self) -> tuple[URIRef, DMSRules]:
|
|
77
118
|
if self.verified_rules:
|
|
78
|
-
for rules in self.verified_rules
|
|
79
|
-
if isinstance(rules,
|
|
80
|
-
return rules
|
|
119
|
+
for id_, rules in reversed(self.verified_rules.items()):
|
|
120
|
+
if isinstance(rules, DMSRules):
|
|
121
|
+
return id_, rules
|
|
81
122
|
|
|
82
123
|
raise NeatSessionError(
|
|
83
|
-
|
|
84
|
-
" to convert
|
|
124
|
+
'No verified DMS data model. Try using [bold].convert("DMS")[/bold]'
|
|
125
|
+
" to convert verified information model to verified DMS model."
|
|
85
126
|
)
|
|
86
127
|
|
|
87
128
|
@property
|
|
88
|
-
def
|
|
89
|
-
return self.
|
|
129
|
+
def has_unverified_rules(self) -> bool:
|
|
130
|
+
return bool(self.unverified_rules)
|
|
90
131
|
|
|
91
132
|
@property
|
|
92
133
|
def has_verified_rules(self) -> bool:
|
|
@@ -97,3 +138,11 @@ class SessionState:
|
|
|
97
138
|
if not self.issue_lists:
|
|
98
139
|
raise NeatSessionError("No issues available. Try using [bold].verify()[/bold] to verify a data model.")
|
|
99
140
|
return self.issue_lists[-1]
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def last_outcome(self) -> UploadResultList:
|
|
144
|
+
if not self.outcome:
|
|
145
|
+
raise NeatSessionError(
|
|
146
|
+
"No outcome available. Try using [bold].to.cdf.data_model[/bold] to upload a data model."
|
|
147
|
+
)
|
|
148
|
+
return self.outcome[-1]
|
cognite/neat/_session/_to.py
CHANGED
|
@@ -10,7 +10,7 @@ from cognite.neat._session._wizard import space_wizard
|
|
|
10
10
|
from cognite.neat._utils.upload import UploadResultCore
|
|
11
11
|
|
|
12
12
|
from ._state import SessionState
|
|
13
|
-
from .exceptions import intercept_session_exceptions
|
|
13
|
+
from .exceptions import NeatSessionError, intercept_session_exceptions
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@intercept_session_exceptions
|
|
@@ -25,7 +25,7 @@ class ToAPI:
|
|
|
25
25
|
io: Any,
|
|
26
26
|
) -> None:
|
|
27
27
|
exporter = exporters.ExcelExporter()
|
|
28
|
-
exporter.export_to_file(self._state.last_verified_rule, Path(io))
|
|
28
|
+
exporter.export_to_file(self._state.data_model.last_verified_rule[1], Path(io))
|
|
29
29
|
return None
|
|
30
30
|
|
|
31
31
|
@overload
|
|
@@ -37,9 +37,9 @@ class ToAPI:
|
|
|
37
37
|
def yaml(self, io: Any | None = None) -> str | None:
|
|
38
38
|
exporter = exporters.YAMLExporter()
|
|
39
39
|
if io is None:
|
|
40
|
-
return exporter.export(self._state.last_verified_rule)
|
|
40
|
+
return exporter.export(self._state.data_model.last_verified_rule[1])
|
|
41
41
|
|
|
42
|
-
exporter.export_to_file(self._state.last_verified_rule, Path(io))
|
|
42
|
+
exporter.export_to_file(self._state.data_model.last_verified_rule[1], Path(io))
|
|
43
43
|
return None
|
|
44
44
|
|
|
45
45
|
|
|
@@ -51,16 +51,17 @@ class CDFToAPI:
|
|
|
51
51
|
self._verbose = verbose
|
|
52
52
|
|
|
53
53
|
def instances(self, space: str | None = None):
|
|
54
|
-
if not self.
|
|
55
|
-
raise
|
|
54
|
+
if not self._client:
|
|
55
|
+
raise NeatSessionError("No CDF client provided!")
|
|
56
56
|
|
|
57
57
|
loader = loaders.DMSLoader.from_rules(
|
|
58
|
-
self._state.last_verified_dms_rules,
|
|
58
|
+
self._state.data_model.last_verified_dms_rules[1],
|
|
59
|
+
self._state.instances.store,
|
|
60
|
+
space_wizard(space=space),
|
|
59
61
|
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
result = loader.load_into_cdf(self._client)
|
|
63
|
+
self._state.instances.outcome.append(result)
|
|
64
|
+
print("You can inspect the details with the .inspect.instances.outcome(...) method.")
|
|
64
65
|
return loader.load_into_cdf(self._client)
|
|
65
66
|
|
|
66
67
|
def data_model(
|
|
@@ -84,20 +85,18 @@ class CDFToAPI:
|
|
|
84
85
|
- "force": If any component already exists, it will be deleted and recreated.
|
|
85
86
|
|
|
86
87
|
"""
|
|
87
|
-
if not self._state.last_verified_dms_rules:
|
|
88
|
-
raise ValueError("No verified DMS data model available")
|
|
89
88
|
|
|
90
89
|
exporter = exporters.DMSExporter(existing_handling=existing_handling)
|
|
91
90
|
|
|
92
91
|
if not self._client:
|
|
93
|
-
raise
|
|
92
|
+
raise NeatSessionError("No client provided!")
|
|
94
93
|
|
|
95
94
|
conversion_issues = IssueList(action="to.cdf.data_model")
|
|
96
95
|
with catch_warnings(conversion_issues):
|
|
97
96
|
result = exporter.export_to_cdf(
|
|
98
|
-
self._state.last_verified_dms_rules, self._client, dry_run, fallback_one_by_one
|
|
97
|
+
self._state.data_model.last_verified_dms_rules[1], self._client, dry_run, fallback_one_by_one
|
|
99
98
|
)
|
|
100
99
|
result.insert(0, UploadResultCore(name="schema", issues=conversion_issues))
|
|
101
|
-
self._state.outcome.append(result)
|
|
102
|
-
print("You can inspect the details with the .inspect.outcome(...) method.")
|
|
100
|
+
self._state.data_model.outcome.append(result)
|
|
101
|
+
print("You can inspect the details with the .inspect.data_model.outcome(...) method.")
|
|
103
102
|
return result
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from typing import Any, Protocol
|
|
3
|
+
|
|
4
|
+
from rdflib import Literal, URIRef
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Extractor(Protocol):
|
|
8
|
+
def extract(self) -> Iterable[tuple[URIRef, URIRef, Literal | URIRef]]: ...
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConfigAPI(Protocol):
|
|
12
|
+
source: str | None
|
|
13
|
+
file: Any | None
|
|
14
|
+
type: str | None
|
|
15
|
+
primary_key: str | None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NeatEngine(Protocol):
|
|
19
|
+
version: str = "1.0.0"
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def set(self) -> ConfigAPI: ...
|
|
23
|
+
|
|
24
|
+
def create_extractor(self) -> Extractor: ...
|