cognite-neat 0.96.6__py3-none-any.whl → 0.97.1__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.

Files changed (68) hide show
  1. cognite/neat/_constants.py +3 -1
  2. cognite/neat/_graph/extractors/__init__.py +3 -0
  3. cognite/neat/_graph/extractors/_base.py +1 -1
  4. cognite/neat/_graph/extractors/_classic_cdf/_assets.py +1 -1
  5. cognite/neat/_graph/extractors/_classic_cdf/_base.py +1 -1
  6. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +1 -1
  7. cognite/neat/_graph/extractors/_classic_cdf/_data_sets.py +1 -1
  8. cognite/neat/_graph/extractors/_classic_cdf/_events.py +1 -1
  9. cognite/neat/_graph/extractors/_classic_cdf/_files.py +1 -1
  10. cognite/neat/_graph/extractors/_classic_cdf/_labels.py +1 -1
  11. cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +1 -1
  12. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +1 -1
  13. cognite/neat/_graph/extractors/_classic_cdf/_timeseries.py +1 -1
  14. cognite/neat/_graph/extractors/_dexpi.py +1 -1
  15. cognite/neat/_graph/extractors/_dms.py +1 -1
  16. cognite/neat/_graph/extractors/_iodd.py +1 -1
  17. cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
  18. cognite/neat/_graph/extractors/_rdf_file.py +1 -1
  19. cognite/neat/_graph/loaders/_rdf2dms.py +1 -1
  20. cognite/neat/_graph/queries/_base.py +1 -1
  21. cognite/neat/_graph/transformers/__init__.py +3 -1
  22. cognite/neat/_graph/transformers/_rdfpath.py +60 -1
  23. cognite/neat/_issues/errors/__init__.py +2 -0
  24. cognite/neat/_issues/errors/_properties.py +12 -0
  25. cognite/neat/_issues/warnings/__init__.py +2 -0
  26. cognite/neat/_issues/warnings/_models.py +11 -0
  27. cognite/neat/_rules/importers/__init__.py +11 -0
  28. cognite/neat/_rules/importers/_base.py +7 -0
  29. cognite/neat/_rules/importers/_dms2rules.py +12 -3
  30. cognite/neat/_rules/importers/_rdf/_inference2rules.py +17 -2
  31. cognite/neat/_rules/models/asset/_rules.py +6 -2
  32. cognite/neat/_rules/models/asset/_rules_input.py +6 -1
  33. cognite/neat/_rules/models/data_types.py +6 -0
  34. cognite/neat/_rules/models/dms/_rules.py +8 -1
  35. cognite/neat/_rules/models/dms/_rules_input.py +8 -0
  36. cognite/neat/_rules/models/dms/_validation.py +64 -2
  37. cognite/neat/_rules/models/domain.py +10 -0
  38. cognite/neat/_rules/models/entities/_loaders.py +3 -5
  39. cognite/neat/_rules/models/information/_rules.py +6 -2
  40. cognite/neat/_rules/models/information/_rules_input.py +6 -1
  41. cognite/neat/_rules/transformers/_base.py +7 -0
  42. cognite/neat/_rules/transformers/_converters.py +56 -4
  43. cognite/neat/_session/_base.py +94 -23
  44. cognite/neat/_session/_inspect.py +12 -4
  45. cognite/neat/_session/_prepare.py +146 -21
  46. cognite/neat/_session/_read.py +137 -30
  47. cognite/neat/_session/_set.py +22 -3
  48. cognite/neat/_session/_show.py +184 -45
  49. cognite/neat/_session/_state.py +79 -30
  50. cognite/neat/_session/_to.py +16 -17
  51. cognite/neat/_session/engine/__init__.py +4 -0
  52. cognite/neat/_session/engine/_import.py +7 -0
  53. cognite/neat/_session/engine/_interface.py +24 -0
  54. cognite/neat/_session/engine/_load.py +129 -0
  55. cognite/neat/_session/exceptions.py +13 -3
  56. cognite/neat/_shared.py +6 -1
  57. cognite/neat/_store/_base.py +3 -24
  58. cognite/neat/_store/_provenance.py +185 -42
  59. cognite/neat/_utils/rdf_.py +34 -1
  60. cognite/neat/_utils/reader/__init__.py +3 -0
  61. cognite/neat/_utils/reader/_base.py +162 -0
  62. cognite/neat/_version.py +2 -1
  63. {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.1.dist-info}/METADATA +5 -3
  64. {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.1.dist-info}/RECORD +67 -62
  65. cognite/neat/_graph/models.py +0 -7
  66. {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.1.dist-info}/LICENSE +0 -0
  67. {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.1.dist-info}/WHEEL +0 -0
  68. {cognite_neat-0.96.6.dist-info → cognite_neat-0.97.1.dist-info}/entry_points.txt +0 -0
@@ -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
- try:
78
- di_graph = self._generate_dms_di_graph(self._state.last_verified_dms_rules)
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
- except NeatSessionError:
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.name or view.view.suffix)
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
- # Add nodes and edges from Properties sheet
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
- di_graph.add_edge(
155
- prop_.class_.suffix,
156
- cast(ClassEntity, prop_.value_type).suffix,
157
- label=prop_.name or prop_.property_,
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,20 @@ 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 IN_PYODIDE:
297
+ raise NeatSessionError(
298
+ "Instances visualization not available if neat is run in PYODIDE as "
299
+ "PYODIDE dues not support oxigraph storage for NeatSession."
300
+ "Try running neat in regular Jupyter notebook and set [bold]NeatSession(storage='oxigraph')[/bold]."
301
+ )
302
+
303
+ if not self._state.instances.store_type == "oxigraph":
304
+ raise NeatSessionError(
305
+ "Visualization is only available for Oxigraph store. "
306
+ 'Try setting [bold]NeatSession(storage="oxigraph")[/bold] enable Oxigraph store.'
307
+ )
308
+
309
+ if not self._state.instances.store.graph:
171
310
  raise NeatSessionError("No instances available. Try using [bold].read[/bold] to load instances.")
172
311
 
173
312
  di_graph = self._generate_instance_di_graph_and_types()
@@ -189,8 +328,8 @@ class ShowInstanceAPI(ShowBaseAPI):
189
328
 
190
329
  di_graph = nx.DiGraph()
191
330
 
192
- types = [type_ for type_, _ in self._state.store.queries.summarize_instances()]
193
- hex_colored_types = self._generate_hex_color_per_type(types)
331
+ types = [type_ for type_, _ in self._state.instances.store.queries.summarize_instances()]
332
+ hex_colored_types = _generate_hex_color_per_type(types)
194
333
 
195
334
  for ( # type: ignore
196
335
  subject,
@@ -198,7 +337,7 @@ class ShowInstanceAPI(ShowBaseAPI):
198
337
  object,
199
338
  subject_type,
200
339
  object_type,
201
- ) in self._state.store.graph.query(query):
340
+ ) in self._state.instances.store.graph.query(query):
202
341
  subject = remove_namespace_from_uri(subject)
203
342
  property_ = remove_namespace_from_uri(property_)
204
343
  object = remove_namespace_from_uri(object)
@@ -223,15 +362,15 @@ class ShowInstanceAPI(ShowBaseAPI):
223
362
 
224
363
  return di_graph
225
364
 
226
- @staticmethod
227
- def _generate_hex_color_per_type(types: list[str]) -> dict:
228
- hex_colored_types = {}
229
- random.seed(381)
230
- for type_ in types:
231
- hue = random.random()
232
- saturation = random.uniform(0.5, 1.0)
233
- lightness = random.uniform(0.4, 0.6)
234
- rgb = colorsys.hls_to_rgb(hue, lightness, saturation)
235
- hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}"
236
- hex_colored_types[type_] = hex_color
237
- return hex_colored_types
365
+
366
+ def _generate_hex_color_per_type(types: list[str]) -> dict:
367
+ hex_colored_types = {}
368
+ random.seed(381)
369
+ for type_ in types:
370
+ hue = random.random()
371
+ saturation = random.uniform(0.5, 1.0)
372
+ lightness = random.uniform(0.4, 0.6)
373
+ rgb = colorsys.hls_to_rgb(hue, lightness, saturation)
374
+ hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}"
375
+ hex_colored_types[type_] = hex_color
376
+ return hex_colored_types
@@ -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 input_rule(self) -> ReadRules:
35
- if not self.input_rules:
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 model/instances."
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 information_input_rule(self) -> ReadRules | None:
49
- if self.input_rules:
50
- for rule in self.input_rules[::-1]:
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
- return None
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[-1]
102
+ return next(reversed(self.verified_rules.items()))
62
103
 
63
104
  @property
64
- def last_verified_dms_rules(self) -> DMSRules:
105
+ def last_verified_information_rules(self) -> tuple[URIRef, InformationRules]:
65
106
  if self.verified_rules:
66
- for rules in self.verified_rules[::-1]:
67
- if isinstance(rules, DMSRules):
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
- 'No verified DMS data model. Try using [bold].convert("DMS")[/bold]'
72
- " to convert verified information model to verified DMS model."
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 last_verified_information_rules(self) -> InformationRules:
117
+ def last_verified_dms_rules(self) -> tuple[URIRef, DMSRules]:
77
118
  if self.verified_rules:
78
- for rules in self.verified_rules[::-1]:
79
- if isinstance(rules, InformationRules):
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
- "No verified information data model. Try using [bold].verify()[/bold]"
84
- " to convert unverified information model to verified information model."
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 has_store(self) -> bool:
89
- return self._store is not None
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]
@@ -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._state.last_verified_dms_rules:
55
- raise ValueError("No verified DMS data model available")
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, self._state.store, space_wizard(space=space)
58
+ self._state.data_model.last_verified_dms_rules[1],
59
+ self._state.instances.store,
60
+ space_wizard(space=space),
59
61
  )
60
-
61
- if not self._client:
62
- raise ValueError("No client provided!")
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 ValueError("No client provided!")
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,4 @@
1
+ from ._import import import_engine
2
+ from ._load import load_neat_engine
3
+
4
+ __all__ = ["load_neat_engine", "import_engine"]
@@ -0,0 +1,7 @@
1
+ from ._interface import NeatEngine
2
+
3
+
4
+ def import_engine() -> NeatEngine:
5
+ from neatengine import NeatEngine # type: ignore[import-not-found]
6
+
7
+ return NeatEngine()
@@ -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: ...