cognite-neat 0.103.1__py3-none-any.whl → 0.105.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.

Files changed (175) hide show
  1. cognite/neat/_client/_api/data_modeling_loaders.py +83 -23
  2. cognite/neat/_client/_api/schema.py +2 -1
  3. cognite/neat/_client/data_classes/neat_sequence.py +261 -0
  4. cognite/neat/_client/data_classes/schema.py +5 -1
  5. cognite/neat/_client/testing.py +33 -0
  6. cognite/neat/_constants.py +56 -0
  7. cognite/neat/_graph/extractors/_classic_cdf/_base.py +6 -5
  8. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +225 -11
  9. cognite/neat/_graph/extractors/_mock_graph_generator.py +2 -2
  10. cognite/neat/_graph/loaders/_rdf2dms.py +13 -2
  11. cognite/neat/_graph/transformers/__init__.py +3 -1
  12. cognite/neat/_graph/transformers/_base.py +109 -1
  13. cognite/neat/_graph/transformers/_classic_cdf.py +6 -1
  14. cognite/neat/_graph/transformers/_prune_graph.py +103 -47
  15. cognite/neat/_graph/transformers/_rdfpath.py +41 -17
  16. cognite/neat/_graph/transformers/_value_type.py +188 -151
  17. cognite/neat/_issues/__init__.py +0 -2
  18. cognite/neat/_issues/_base.py +54 -43
  19. cognite/neat/_issues/warnings/__init__.py +4 -1
  20. cognite/neat/_issues/warnings/_general.py +7 -0
  21. cognite/neat/_issues/warnings/_resources.py +12 -1
  22. cognite/neat/_rules/_shared.py +18 -34
  23. cognite/neat/_rules/exporters/_base.py +28 -2
  24. cognite/neat/_rules/exporters/_rules2dms.py +39 -1
  25. cognite/neat/_rules/exporters/_rules2excel.py +13 -2
  26. cognite/neat/_rules/exporters/_rules2instance_template.py +4 -0
  27. cognite/neat/_rules/exporters/_rules2ontology.py +13 -1
  28. cognite/neat/_rules/exporters/_rules2yaml.py +4 -0
  29. cognite/neat/_rules/importers/_base.py +9 -0
  30. cognite/neat/_rules/importers/_dms2rules.py +80 -57
  31. cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +5 -2
  32. cognite/neat/_rules/importers/_rdf/_base.py +10 -8
  33. cognite/neat/_rules/importers/_rdf/_imf2rules.py +4 -0
  34. cognite/neat/_rules/importers/_rdf/_inference2rules.py +7 -0
  35. cognite/neat/_rules/importers/_rdf/_owl2rules.py +4 -0
  36. cognite/neat/_rules/importers/_spreadsheet2rules.py +17 -8
  37. cognite/neat/_rules/importers/_yaml2rules.py +21 -7
  38. cognite/neat/_rules/models/_base_input.py +1 -1
  39. cognite/neat/_rules/models/_base_rules.py +9 -1
  40. cognite/neat/_rules/models/dms/_rules.py +4 -0
  41. cognite/neat/_rules/models/dms/_rules_input.py +9 -0
  42. cognite/neat/_rules/models/entities/_wrapped.py +10 -5
  43. cognite/neat/_rules/models/information/_rules.py +4 -0
  44. cognite/neat/_rules/models/information/_rules_input.py +9 -0
  45. cognite/neat/_rules/models/mapping/_classic2core.py +2 -5
  46. cognite/neat/_rules/models/mapping/_classic2core.yaml +239 -38
  47. cognite/neat/_rules/transformers/__init__.py +13 -6
  48. cognite/neat/_rules/transformers/_base.py +41 -65
  49. cognite/neat/_rules/transformers/_converters.py +404 -234
  50. cognite/neat/_rules/transformers/_mapping.py +93 -72
  51. cognite/neat/_rules/transformers/_verification.py +50 -38
  52. cognite/neat/_session/_base.py +32 -121
  53. cognite/neat/_session/_inspect.py +5 -3
  54. cognite/neat/_session/_mapping.py +17 -105
  55. cognite/neat/_session/_prepare.py +138 -268
  56. cognite/neat/_session/_read.py +39 -195
  57. cognite/neat/_session/_set.py +6 -30
  58. cognite/neat/_session/_show.py +40 -21
  59. cognite/neat/_session/_state.py +49 -107
  60. cognite/neat/_session/_to.py +44 -33
  61. cognite/neat/_shared.py +23 -2
  62. cognite/neat/_store/_provenance.py +3 -82
  63. cognite/neat/_store/_rules_store.py +368 -10
  64. cognite/neat/_store/exceptions.py +23 -0
  65. cognite/neat/_utils/graph_transformations_report.py +36 -0
  66. cognite/neat/_utils/rdf_.py +8 -0
  67. cognite/neat/_utils/reader/_base.py +27 -0
  68. cognite/neat/_utils/spreadsheet.py +5 -4
  69. cognite/neat/_version.py +1 -1
  70. {cognite_neat-0.103.1.dist-info → cognite_neat-0.105.0.dist-info}/METADATA +3 -2
  71. cognite_neat-0.105.0.dist-info/RECORD +179 -0
  72. {cognite_neat-0.103.1.dist-info → cognite_neat-0.105.0.dist-info}/WHEEL +1 -1
  73. cognite/neat/_app/api/__init__.py +0 -0
  74. cognite/neat/_app/api/asgi/metrics.py +0 -4
  75. cognite/neat/_app/api/configuration.py +0 -98
  76. cognite/neat/_app/api/context_manager/__init__.py +0 -3
  77. cognite/neat/_app/api/context_manager/manager.py +0 -16
  78. cognite/neat/_app/api/data_classes/__init__.py +0 -0
  79. cognite/neat/_app/api/data_classes/rest.py +0 -59
  80. cognite/neat/_app/api/explorer.py +0 -66
  81. cognite/neat/_app/api/routers/configuration.py +0 -25
  82. cognite/neat/_app/api/routers/crud.py +0 -102
  83. cognite/neat/_app/api/routers/metrics.py +0 -10
  84. cognite/neat/_app/api/routers/workflows.py +0 -224
  85. cognite/neat/_app/api/utils/__init__.py +0 -0
  86. cognite/neat/_app/api/utils/data_mapping.py +0 -17
  87. cognite/neat/_app/api/utils/logging.py +0 -26
  88. cognite/neat/_app/api/utils/query_templates.py +0 -92
  89. cognite/neat/_app/main.py +0 -17
  90. cognite/neat/_app/monitoring/__init__.py +0 -0
  91. cognite/neat/_app/monitoring/metrics.py +0 -69
  92. cognite/neat/_app/ui/index.html +0 -1
  93. cognite/neat/_app/ui/neat-app/.gitignore +0 -23
  94. cognite/neat/_app/ui/neat-app/README.md +0 -70
  95. cognite/neat/_app/ui/neat-app/build/asset-manifest.json +0 -14
  96. cognite/neat/_app/ui/neat-app/build/favicon.ico +0 -0
  97. cognite/neat/_app/ui/neat-app/build/img/architect-icon.svg +0 -116
  98. cognite/neat/_app/ui/neat-app/build/img/developer-icon.svg +0 -112
  99. cognite/neat/_app/ui/neat-app/build/img/sme-icon.svg +0 -34
  100. cognite/neat/_app/ui/neat-app/build/index.html +0 -1
  101. cognite/neat/_app/ui/neat-app/build/logo192.png +0 -0
  102. cognite/neat/_app/ui/neat-app/build/manifest.json +0 -25
  103. cognite/neat/_app/ui/neat-app/build/robots.txt +0 -3
  104. cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css +0 -2
  105. cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css.map +0 -1
  106. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js +0 -3
  107. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.LICENSE.txt +0 -88
  108. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.map +0 -1
  109. cognite/neat/_app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg +0 -1
  110. cognite/neat/_app/ui/neat-app/package-lock.json +0 -18306
  111. cognite/neat/_app/ui/neat-app/package.json +0 -62
  112. cognite/neat/_app/ui/neat-app/public/favicon.ico +0 -0
  113. cognite/neat/_app/ui/neat-app/public/img/architect-icon.svg +0 -116
  114. cognite/neat/_app/ui/neat-app/public/img/developer-icon.svg +0 -112
  115. cognite/neat/_app/ui/neat-app/public/img/sme-icon.svg +0 -34
  116. cognite/neat/_app/ui/neat-app/public/index.html +0 -43
  117. cognite/neat/_app/ui/neat-app/public/logo192.png +0 -0
  118. cognite/neat/_app/ui/neat-app/public/manifest.json +0 -25
  119. cognite/neat/_app/ui/neat-app/public/robots.txt +0 -3
  120. cognite/neat/_app/ui/neat-app/src/App.css +0 -38
  121. cognite/neat/_app/ui/neat-app/src/App.js +0 -17
  122. cognite/neat/_app/ui/neat-app/src/App.test.js +0 -8
  123. cognite/neat/_app/ui/neat-app/src/MainContainer.tsx +0 -70
  124. cognite/neat/_app/ui/neat-app/src/components/JsonViewer.tsx +0 -43
  125. cognite/neat/_app/ui/neat-app/src/components/LocalUploader.tsx +0 -124
  126. cognite/neat/_app/ui/neat-app/src/components/OverviewComponentEditorDialog.tsx +0 -63
  127. cognite/neat/_app/ui/neat-app/src/components/StepEditorDialog.tsx +0 -511
  128. cognite/neat/_app/ui/neat-app/src/components/TabPanel.tsx +0 -36
  129. cognite/neat/_app/ui/neat-app/src/components/Utils.tsx +0 -56
  130. cognite/neat/_app/ui/neat-app/src/components/WorkflowDeleteDialog.tsx +0 -60
  131. cognite/neat/_app/ui/neat-app/src/components/WorkflowExecutionReport.tsx +0 -112
  132. cognite/neat/_app/ui/neat-app/src/components/WorkflowImportExportDialog.tsx +0 -67
  133. cognite/neat/_app/ui/neat-app/src/components/WorkflowMetadataDialog.tsx +0 -79
  134. cognite/neat/_app/ui/neat-app/src/index.css +0 -13
  135. cognite/neat/_app/ui/neat-app/src/index.js +0 -13
  136. cognite/neat/_app/ui/neat-app/src/logo.svg +0 -1
  137. cognite/neat/_app/ui/neat-app/src/reportWebVitals.js +0 -13
  138. cognite/neat/_app/ui/neat-app/src/setupTests.js +0 -5
  139. cognite/neat/_app/ui/neat-app/src/types/WorkflowTypes.ts +0 -388
  140. cognite/neat/_app/ui/neat-app/src/views/AboutView.tsx +0 -61
  141. cognite/neat/_app/ui/neat-app/src/views/ConfigView.tsx +0 -184
  142. cognite/neat/_app/ui/neat-app/src/views/GlobalConfigView.tsx +0 -180
  143. cognite/neat/_app/ui/neat-app/src/views/WorkflowView.tsx +0 -570
  144. cognite/neat/_app/ui/neat-app/tsconfig.json +0 -27
  145. cognite/neat/_rules/transformers/_pipelines.py +0 -70
  146. cognite/neat/_workflows/__init__.py +0 -17
  147. cognite/neat/_workflows/base.py +0 -590
  148. cognite/neat/_workflows/cdf_store.py +0 -393
  149. cognite/neat/_workflows/examples/Export_DMS/workflow.yaml +0 -89
  150. cognite/neat/_workflows/examples/Export_Semantic_Data_Model/workflow.yaml +0 -66
  151. cognite/neat/_workflows/examples/Import_DMS/workflow.yaml +0 -65
  152. cognite/neat/_workflows/examples/Validate_Rules/workflow.yaml +0 -67
  153. cognite/neat/_workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
  154. cognite/neat/_workflows/manager.py +0 -292
  155. cognite/neat/_workflows/model.py +0 -203
  156. cognite/neat/_workflows/steps/__init__.py +0 -0
  157. cognite/neat/_workflows/steps/data_contracts.py +0 -109
  158. cognite/neat/_workflows/steps/lib/__init__.py +0 -0
  159. cognite/neat/_workflows/steps/lib/current/__init__.py +0 -6
  160. cognite/neat/_workflows/steps/lib/current/graph_extractor.py +0 -100
  161. cognite/neat/_workflows/steps/lib/current/graph_loader.py +0 -51
  162. cognite/neat/_workflows/steps/lib/current/graph_store.py +0 -48
  163. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -537
  164. cognite/neat/_workflows/steps/lib/current/rules_importer.py +0 -398
  165. cognite/neat/_workflows/steps/lib/current/rules_validator.py +0 -106
  166. cognite/neat/_workflows/steps/lib/io/__init__.py +0 -1
  167. cognite/neat/_workflows/steps/lib/io/io_steps.py +0 -393
  168. cognite/neat/_workflows/steps/step_model.py +0 -79
  169. cognite/neat/_workflows/steps_registry.py +0 -218
  170. cognite/neat/_workflows/tasks.py +0 -18
  171. cognite/neat/_workflows/triggers.py +0 -169
  172. cognite/neat/_workflows/utils.py +0 -19
  173. cognite_neat-0.103.1.dist-info/RECORD +0 -275
  174. {cognite_neat-0.103.1.dist-info → cognite_neat-0.105.0.dist-info}/LICENSE +0 -0
  175. {cognite_neat-0.103.1.dist-info → cognite_neat-0.105.0.dist-info}/entry_points.txt +0 -0
@@ -1,23 +1,16 @@
1
- import tempfile
2
- from datetime import datetime, timezone
3
- from pathlib import Path
4
1
  from typing import Any, Literal, cast
5
2
 
6
3
  from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier
7
4
 
8
5
  from cognite.neat._client import NeatClient
9
- from cognite.neat._constants import COGNITE_SPACES
10
6
  from cognite.neat._graph import examples as instances_examples
11
7
  from cognite.neat._graph import extractors
12
8
  from cognite.neat._issues import IssueList
13
9
  from cognite.neat._issues.errors import NeatValueError
10
+ from cognite.neat._issues.warnings import MissingCogniteClientWarning
14
11
  from cognite.neat._rules import catalog, importers
15
- from cognite.neat._rules._shared import ReadRules
16
12
  from cognite.neat._rules.importers import BaseImporter
17
- from cognite.neat._store._provenance import Activity as ProvenanceActivity
18
- from cognite.neat._store._provenance import Change
19
- from cognite.neat._store._provenance import Entity as ProvenanceEntity
20
- from cognite.neat._utils.reader import GitHubReader, HttpFileReader, NeatReader, PathReader
13
+ from cognite.neat._utils.reader import NeatReader
21
14
 
22
15
  from ._state import SessionState
23
16
  from ._wizard import NeatObjectType, RDFFileType, XMLFileType, object_wizard, rdf_dm_wizard, xml_format_wizard
@@ -47,26 +40,6 @@ class BaseReadAPI:
47
40
  self._verbose = verbose
48
41
  self._client = client
49
42
 
50
- def _store_rules(self, rules: ReadRules, change: Change) -> IssueList:
51
- if self._verbose:
52
- if rules.issues.has_errors:
53
- print("Data model read failed")
54
- else:
55
- print("Data model read passed")
56
-
57
- if rules.rules:
58
- self._state.data_model.write(rules, change)
59
-
60
- return rules.issues
61
-
62
- def _return_filepath(self, io: Any) -> Path:
63
- if isinstance(io, str):
64
- return Path(io)
65
- elif isinstance(io, Path):
66
- return io
67
- else:
68
- raise NeatValueError(f"Expected str or Path, got {type(io)}")
69
-
70
43
 
71
44
  @session_class_wrapper
72
45
  class CDFReadAPI(BaseReadAPI):
@@ -103,31 +76,8 @@ class CDFReadAPI(BaseReadAPI):
103
76
  if not data_model_id.version:
104
77
  raise NeatSessionError("Data model version is required to read a data model.")
105
78
 
106
- # actual reading of data model
107
- start = datetime.now(timezone.utc)
108
79
  importer = importers.DMSImporter.from_data_model_id(self._get_client, data_model_id)
109
- rules = importer.to_rules()
110
- end = datetime.now(timezone.utc)
111
-
112
- # provenance information
113
- source_entity = ProvenanceEntity.from_data_model_id(data_model_id)
114
- agent = importer.agent
115
- activity = ProvenanceActivity(
116
- was_associated_with=agent,
117
- ended_at_time=end,
118
- used=source_entity,
119
- started_at_time=start,
120
- )
121
- target_entity = ProvenanceEntity.from_rules(rules, agent, activity)
122
- change = Change(
123
- source_entity=source_entity,
124
- agent=agent,
125
- activity=activity,
126
- target_entity=target_entity,
127
- description=f"DMS Data model {data_model_id.as_tuple()} read as unverified data model",
128
- )
129
-
130
- return self._store_rules(rules, change)
80
+ return self._state.rule_import(importer)
131
81
 
132
82
 
133
83
  @session_class_wrapper
@@ -215,24 +165,8 @@ class ExcelReadAPI(BaseReadAPI):
215
165
  io: file path to the Excel sheet
216
166
  """
217
167
  reader = NeatReader.create(io)
218
- start = datetime.now(timezone.utc)
219
- if not isinstance(reader, PathReader):
220
- raise NeatValueError("Only file paths are supported for Excel files")
221
- importer: importers.ExcelImporter = importers.ExcelImporter(reader.path)
222
- input_rules: ReadRules = importer.to_rules()
223
- end = datetime.now(timezone.utc)
224
-
225
- if input_rules.rules:
226
- change = Change.from_rules_activity(
227
- input_rules,
228
- importer.agent,
229
- start,
230
- end,
231
- description=f"Excel file {reader!s} read as unverified data model",
232
- )
233
- self._store_rules(input_rules, change)
234
- self._state.data_model.issue_lists.append(input_rules.issues)
235
- return input_rules.issues
168
+ path = reader.materialize_path()
169
+ return self._state.rule_import(importers.ExcelImporter(path))
236
170
 
237
171
 
238
172
  @session_class_wrapper
@@ -241,85 +175,43 @@ class ExcelExampleAPI(BaseReadAPI):
241
175
 
242
176
  @property
243
177
  def pump_example(self) -> IssueList:
244
- """Reads the Nordic 44 knowledge graph into the NeatSession graph store."""
245
- start = datetime.now(timezone.utc)
178
+ """Reads the Hello World pump example into the NeatSession."""
246
179
  importer: importers.ExcelImporter = importers.ExcelImporter(catalog.hello_world_pump)
247
- input_rules: ReadRules = importer.to_rules()
248
- end = datetime.now(timezone.utc)
249
-
250
- if input_rules.rules:
251
- change = Change.from_rules_activity(
252
- input_rules,
253
- importer.agent,
254
- start,
255
- end,
256
- description="Pump Example read as unverified data model",
257
- )
258
- self._store_rules(input_rules, change)
259
- self._state.data_model.issue_lists.append(input_rules.issues)
260
- return input_rules.issues
180
+ return self._state.rule_import(importer)
261
181
 
262
182
 
263
183
  @session_class_wrapper
264
184
  class YamlReadAPI(BaseReadAPI):
265
- """Reads a yaml with either neat rules, or several toolkit yaml files to import Data Model(s) into NeatSession.
266
-
267
- Args:
268
- io: file path to the Yaml file in the case of "neat" yaml, or path to a zip folder or directory with several
269
- Yaml files in the case of "toolkit".
185
+ def __call__(self, io: Any, format: Literal["neat", "toolkit"] = "neat") -> IssueList:
186
+ """Reads a yaml with either neat rules, or several toolkit yaml files to import Data Model(s) into NeatSession.
270
187
 
271
- Example:
272
- ```python
273
- neat.read.yaml("path_to_toolkit_yamls")
274
- ```
275
- """
188
+ Args:
189
+ io: File path to the Yaml file in the case of "neat" yaml, or path to a zip folder or directory with several
190
+ Yaml files in the case of "toolkit".
191
+ format: The format of the yaml file(s). Can be either "neat" or "toolkit".
276
192
 
277
- def __call__(self, io: Any, format: Literal["neat", "toolkit"] = "neat") -> IssueList:
193
+ Example:
194
+ ```python
195
+ neat.read.yaml("path_to_toolkit_yamls")
196
+ ```
197
+ """
278
198
  reader = NeatReader.create(io)
279
- if not isinstance(reader, PathReader):
280
- raise NeatValueError("Only file paths are supported for YAML files")
281
- start = datetime.now(timezone.utc)
199
+ path = reader.materialize_path()
282
200
  importer: BaseImporter
283
201
  if format == "neat":
284
- importer = importers.YAMLImporter.from_file(reader.path)
202
+ importer = importers.YAMLImporter.from_file(path, source_name=f"{reader!s}")
285
203
  elif format == "toolkit":
286
- if reader.path.is_file():
287
- dms_importer = importers.DMSImporter.from_zip_file(reader.path)
288
- elif reader.path.is_dir():
289
- dms_importer = importers.DMSImporter.from_directory(reader.path)
290
- else:
291
- raise NeatValueError(f"Unsupported YAML format: {format}")
292
- ref_containers = dms_importer.root_schema.referenced_container()
293
- if system_container_ids := [
294
- container_id for container_id in ref_containers if container_id.space in COGNITE_SPACES
295
- ]:
296
- if self._client is None:
297
- raise NeatSessionError(
298
- "No client provided. You are referencing Cognite containers in your data model, "
299
- "NEAT needs a client to lookup the container definitions. "
300
- "Please set the client in the session, NeatSession(client=client)."
301
- )
302
- system_containers = self._client.loaders.containers.retrieve(system_container_ids)
303
- dms_importer.update_referenced_containers(system_containers)
304
-
204
+ dms_importer = importers.DMSImporter.from_path(path, self._client)
205
+ if dms_importer.issue_list.has_warning_type(MissingCogniteClientWarning):
206
+ raise NeatSessionError(
207
+ "No client provided. You are referencing Cognite containers in your data model, "
208
+ "NEAT needs a client to lookup the container definitions. "
209
+ "Please set the client in the session, NeatSession(client=client)."
210
+ )
305
211
  importer = dms_importer
306
212
  else:
307
213
  raise NeatValueError(f"Unsupported YAML format: {format}")
308
- input_rules: ReadRules = importer.to_rules()
309
-
310
- end = datetime.now(timezone.utc)
311
-
312
- if input_rules.rules:
313
- change = Change.from_rules_activity(
314
- input_rules,
315
- importer.agent,
316
- start,
317
- end,
318
- description=f"YAML file {reader!s} read as unverified data model",
319
- )
320
- self._store_rules(input_rules, change)
321
-
322
- return input_rules.issues
214
+ return self._state.rule_import(importer)
323
215
 
324
216
 
325
217
  @session_class_wrapper
@@ -342,17 +234,9 @@ class CSVReadAPI(BaseReadAPI):
342
234
  """
343
235
 
344
236
  def __call__(self, io: Any, type: str, primary_key: str) -> None:
345
- reader = NeatReader.create(io)
346
- if isinstance(reader, HttpFileReader):
347
- path = Path(tempfile.gettempdir()).resolve() / reader.name
348
- path.write_text(reader.read_text(), encoding="utf-8", newline="\n")
349
- elif isinstance(reader, PathReader):
350
- path = reader.path
351
- else:
352
- raise NeatValueError("Only file paths are supported for CSV files")
353
237
  engine = import_engine()
354
238
  engine.set.format = "csv"
355
- engine.set.file = path
239
+ engine.set.file = NeatReader.create(io).materialize_path()
356
240
  engine.set.type = type
357
241
  engine.set.primary_key = primary_key
358
242
  extractor = engine.create_extractor()
@@ -374,14 +258,7 @@ class XMLReadAPI(BaseReadAPI):
374
258
  io: Any,
375
259
  format: XMLFileType | None = None,
376
260
  ) -> None:
377
- reader = NeatReader.create(io)
378
- if isinstance(reader, GitHubReader):
379
- path = Path(tempfile.gettempdir()).resolve() / reader.name
380
- path.write_text(reader.read_text())
381
- elif isinstance(reader, PathReader):
382
- path = reader.path
383
- else:
384
- raise NeatValueError("Only file paths are supported for XML files")
261
+ path = NeatReader.create(io).materialize_path()
385
262
  if format is None:
386
263
  format = xml_format_wizard()
387
264
 
@@ -394,7 +271,7 @@ class XMLReadAPI(BaseReadAPI):
394
271
  else:
395
272
  raise NeatValueError("Only support XML files of DEXPI format at the moment.")
396
273
 
397
- def dexpi(self, path):
274
+ def dexpi(self, io: Any) -> None:
398
275
  """Reads a DEXPI file into the NeatSession.
399
276
 
400
277
  Args:
@@ -405,13 +282,14 @@ class XMLReadAPI(BaseReadAPI):
405
282
  neat.read.xml.dexpi("url_or_path_to_dexpi_file")
406
283
  ```
407
284
  """
285
+ path = NeatReader.create(io).materialize_path()
408
286
  engine = import_engine()
409
287
  engine.set.format = "dexpi"
410
288
  engine.set.file = path
411
289
  extractor = engine.create_extractor()
412
290
  self._state.instances.store.write(extractor)
413
291
 
414
- def aml(self, path):
292
+ def aml(self, io: Any):
415
293
  """Reads an AML file into NeatSession.
416
294
 
417
295
  Args:
@@ -422,6 +300,7 @@ class XMLReadAPI(BaseReadAPI):
422
300
  neat.read.xml.aml("url_or_path_to_aml_file")
423
301
  ```
424
302
  """
303
+ path = NeatReader.create(io).materialize_path()
425
304
  engine = import_engine()
426
305
  engine.set.format = "aml"
427
306
  engine.set.file = path
@@ -452,25 +331,9 @@ class RDFReadAPI(BaseReadAPI):
452
331
  neat.read.rdf.ontology("url_or_path_to_owl_source")
453
332
  ```
454
333
  """
455
- start = datetime.now(timezone.utc)
456
334
  reader = NeatReader.create(io)
457
- if not isinstance(reader, PathReader):
458
- raise NeatValueError("Only file paths are supported for RDF files")
459
- importer = importers.OWLImporter.from_file(reader.path)
460
- input_rules: ReadRules = importer.to_rules()
461
- end = datetime.now(timezone.utc)
462
-
463
- if input_rules.rules:
464
- change = Change.from_rules_activity(
465
- input_rules,
466
- importer.agent,
467
- start,
468
- end,
469
- description=f"Ontology file {reader!s} read as unverified data model",
470
- )
471
- self._store_rules(input_rules, change)
472
-
473
- return input_rules.issues
335
+ importer = importers.OWLImporter.from_file(reader.materialize_path(), source_name=f"file {reader!s}")
336
+ return self._state.rule_import(importer)
474
337
 
475
338
  def imf(self, io: Any) -> IssueList:
476
339
  """Reads IMF Types provided as SHACL shapes into NeatSession.
@@ -483,25 +346,9 @@ class RDFReadAPI(BaseReadAPI):
483
346
  neat.read.rdf.imf("url_or_path_to_imf_source")
484
347
  ```
485
348
  """
486
- start = datetime.now(timezone.utc)
487
349
  reader = NeatReader.create(io)
488
- if not isinstance(reader, PathReader):
489
- raise NeatValueError("Only file paths are supported for RDF files")
490
- importer = importers.IMFImporter.from_file(reader.path)
491
- input_rules: ReadRules = importer.to_rules()
492
- end = datetime.now(timezone.utc)
493
-
494
- if input_rules.rules:
495
- change = Change.from_rules_activity(
496
- input_rules,
497
- importer.agent,
498
- start,
499
- end,
500
- description=f"IMF Types file {reader!s} read as unverified data model",
501
- )
502
- self._store_rules(input_rules, change)
503
-
504
- return input_rules.issues
350
+ importer = importers.IMFImporter.from_file(reader.materialize_path(), source_name=f"file {reader!s}")
351
+ return self._state.rule_import(importer)
505
352
 
506
353
  def __call__(
507
354
  self,
@@ -527,10 +374,7 @@ class RDFReadAPI(BaseReadAPI):
527
374
 
528
375
  elif type == "instances":
529
376
  reader = NeatReader.create(io)
530
- if not isinstance(reader, PathReader):
531
- raise NeatValueError("Only file paths are supported for RDF files")
532
-
533
- self._state.instances.store.write(extractors.RdfFileExtractor(reader.path))
377
+ self._state.instances.store.write(extractors.RdfFileExtractor(reader.materialize_path()))
534
378
  return IssueList()
535
379
  else:
536
380
  raise NeatSessionError(f"Expected data model or instances, got {type}")
@@ -1,10 +1,9 @@
1
- from datetime import datetime, timezone
2
-
3
1
  from cognite.client import data_modeling as dm
4
2
 
5
3
  from cognite.neat._constants import COGNITE_MODELS
4
+ from cognite.neat._issues import IssueList
5
+ from cognite.neat._rules.models import DMSRules
6
6
  from cognite.neat._rules.transformers import SetIDDMSModel
7
- from cognite.neat._store._provenance import Change
8
7
 
9
8
  from ._state import SessionState
10
9
  from .exceptions import NeatSessionError, session_class_wrapper
@@ -18,7 +17,7 @@ class SetAPI:
18
17
  self._state = state
19
18
  self._verbose = verbose
20
19
 
21
- def data_model_id(self, new_model_id: dm.DataModelId | tuple[str, str, str]) -> None:
20
+ def data_model_id(self, new_model_id: dm.DataModelId | tuple[str, str, str]) -> IssueList:
22
21
  """Sets the data model ID of the latest verified data model. Set the data model id as a tuple of strings
23
22
  following the template (<data_model_space>, <data_model_name>, <data_model_version>).
24
23
 
@@ -28,34 +27,11 @@ class SetAPI:
28
27
  neat.set.data_model_id(("my_data_model_space", "My_Data_Model", "v1"))
29
28
  ```
30
29
  """
31
- if res := self._state.data_model.last_verified_dms_rules:
32
- source_id, rules = res
33
-
30
+ rules = self._state.rule_store.get_last_successful_entity().result
31
+ if isinstance(rules, DMSRules):
34
32
  if rules.metadata.as_data_model_id() in COGNITE_MODELS:
35
33
  raise NeatSessionError(
36
34
  "Cannot change the data model ID of a Cognite Data Model in NeatSession"
37
35
  " due to temporarily issue with the reverse direct relation interpretation"
38
36
  )
39
-
40
- start = datetime.now(timezone.utc)
41
- transformer = SetIDDMSModel(new_model_id)
42
-
43
- output = transformer.transform(rules)
44
- end = datetime.now(timezone.utc)
45
-
46
- # Provenance
47
- change = Change.from_rules_activity(
48
- output.rules,
49
- transformer.agent,
50
- start,
51
- end,
52
- "Changed data model id",
53
- self._state.data_model.provenance.source_entity(source_id)
54
- or self._state.data_model.provenance.target_entity(source_id),
55
- )
56
-
57
- self._state.data_model.write(output.rules, change)
58
- if self._verbose:
59
- print(f"Data model ID set to {new_model_id}")
60
- else:
61
- print("No verified DMS data model available")
37
+ return self._state.rule_transform(SetIDDMSModel(new_model_id))
@@ -5,7 +5,6 @@ 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
9
8
 
10
9
  from cognite.neat._constants import IN_NOTEBOOK, IN_PYODIDE
11
10
  from cognite.neat._rules._constants import EntityTypes
@@ -14,7 +13,7 @@ from cognite.neat._rules.models.entities._single_value import ClassEntity, ViewE
14
13
  from cognite.neat._rules.models.information._rules import InformationRules
15
14
  from cognite.neat._session.exceptions import NeatSessionError
16
15
  from cognite.neat._utils.io_ import to_directory_compatible
17
- from cognite.neat._utils.rdf_ import remove_namespace_from_uri
16
+ from cognite.neat._utils.rdf_ import remove_namespace_from_uri, uri_display_name
18
17
 
19
18
  from ._state import SessionState
20
19
  from .exceptions import session_class_wrapper
@@ -99,12 +98,12 @@ class ShowDataModelAPI(ShowBaseAPI):
99
98
  self.implements = ShowDataModelImplementsAPI(self._state)
100
99
 
101
100
  def __call__(self) -> Any:
102
- if not self._state.data_model.has_verified_rules:
101
+ if not self._state.rule_store.has_verified_rules:
103
102
  raise NeatSessionError(
104
103
  "No verified data model available. Try using [bold].verify()[/bold] to verify data model."
105
104
  )
106
105
 
107
- rules = self._state.data_model.last_verified_rule[1]
106
+ rules = self._state.rule_store.last_verified_rule
108
107
 
109
108
  if isinstance(rules, DMSRules):
110
109
  di_graph = self._generate_dms_di_graph(rules)
@@ -188,12 +187,12 @@ class ShowDataModelImplementsAPI(ShowBaseAPI):
188
187
  self._state = state
189
188
 
190
189
  def __call__(self) -> Any:
191
- if not self._state.data_model.has_verified_rules:
190
+ if not self._state.rule_store.has_verified_rules:
192
191
  raise NeatSessionError(
193
192
  "No verified data model available. Try using [bold].verify()[/bold] to verify data model."
194
193
  )
195
194
 
196
- rules = self._state.data_model.last_verified_rule[1]
195
+ rules = self._state.rule_store.last_verified_rule
197
196
 
198
197
  if isinstance(rules, DMSRules):
199
198
  di_graph = self._generate_dms_di_graph(rules)
@@ -270,20 +269,21 @@ class ShowDataModelProvenanceAPI(ShowBaseAPI):
270
269
  self._state = state
271
270
 
272
271
  def __call__(self) -> Any:
273
- if not self._state.data_model.provenance:
272
+ if not self._state.rule_store.provenance:
274
273
  raise NeatSessionError("No data model available. Try using [bold].read[/bold] to load data model.")
275
274
 
276
275
  di_graph = self._generate_dm_provenance_di_graph_and_types()
277
- return self._generate_visualization(di_graph, name="data_model_provenance.html")
276
+ unique_hash = self._state.rule_store.calculate_provenance_hash(shorten=True)
277
+ return self._generate_visualization(di_graph, name=f"data_model_provenance_{unique_hash}.html")
278
278
 
279
279
  def _generate_dm_provenance_di_graph_and_types(self) -> nx.DiGraph:
280
280
  di_graph = nx.DiGraph()
281
- hex_colored_types = _generate_hex_color_per_type(["Agent", "Entity", "Activity"])
281
+ hex_colored_types = _generate_hex_color_per_type(["Agent", "Entity", "Activity", "Export", "Pruned"])
282
282
 
283
- for change in self._state.data_model.provenance:
284
- source = self._shorten_id(change.source_entity.id_)
285
- target = self._shorten_id(change.target_entity.id_)
286
- agent = self._shorten_id(change.agent.id_)
283
+ for change in self._state.rule_store.provenance:
284
+ source = uri_display_name(change.source_entity.id_)
285
+ target = uri_display_name(change.target_entity.id_)
286
+ agent = uri_display_name(change.agent.id_)
287
287
 
288
288
  di_graph.add_node(
289
289
  source,
@@ -312,15 +312,34 @@ class ShowDataModelProvenanceAPI(ShowBaseAPI):
312
312
  di_graph.add_edge(source, agent, label="used", color="grey")
313
313
  di_graph.add_edge(agent, target, label="generated", color="grey")
314
314
 
315
- return di_graph
315
+ for source_id, exports in self._state.rule_store.exports_by_source_entity_id.items():
316
+ source_shorten = uri_display_name(source_id)
317
+ for export in exports:
318
+ export_id = uri_display_name(export.target_entity.id_)
319
+ di_graph.add_node(
320
+ export_id,
321
+ label=export_id,
322
+ type="Export",
323
+ title="Export",
324
+ color=hex_colored_types["Export"],
325
+ )
326
+ di_graph.add_edge(source_shorten, export_id, label="exported", color="grey")
316
327
 
317
- @staticmethod
318
- def _shorten_id(thing: URIRef) -> str:
319
- if "https://cognitedata.com/dms/data-model/" in thing:
320
- return "DMS(" + ",".join(thing.replace("https://cognitedata.com/dms/data-model/", "").split("/")) + ")"
321
- elif "http://purl.org/cognite/neat/data-model/" in thing:
322
- return "NEAT(" + ",".join(thing.replace("http://purl.org/cognite/neat/data-model/", "").split("/")) + ")"
323
- return remove_namespace_from_uri(thing)
328
+ for pruned_lists in self._state.rule_store.pruned_by_source_entity_id.values():
329
+ for prune_path in pruned_lists:
330
+ for change in prune_path:
331
+ source = uri_display_name(change.source_entity.id_)
332
+ target = uri_display_name(change.target_entity.id_)
333
+ di_graph.add_node(
334
+ target,
335
+ label=target,
336
+ type="Pruned",
337
+ title="Pruned",
338
+ color=hex_colored_types["Pruned"],
339
+ )
340
+ di_graph.add_edge(source, target, label="pruned", color="grey")
341
+
342
+ return di_graph
324
343
 
325
344
 
326
345
  @session_class_wrapper