cognite-neat 0.88.3__py3-none-any.whl → 0.89.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 (75) hide show
  1. cognite/neat/_version.py +1 -1
  2. cognite/neat/constants.py +3 -0
  3. cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
  4. cognite/neat/issues/_base.py +2 -1
  5. cognite/neat/issues/errors/__init__.py +2 -1
  6. cognite/neat/issues/errors/_general.py +7 -0
  7. cognite/neat/issues/warnings/_models.py +1 -1
  8. cognite/neat/issues/warnings/user_modeling.py +1 -1
  9. cognite/neat/rules/_shared.py +49 -6
  10. cognite/neat/rules/analysis/_base.py +1 -1
  11. cognite/neat/rules/exporters/_base.py +7 -18
  12. cognite/neat/rules/exporters/_rules2dms.py +8 -18
  13. cognite/neat/rules/exporters/_rules2excel.py +5 -12
  14. cognite/neat/rules/exporters/_rules2ontology.py +9 -19
  15. cognite/neat/rules/exporters/_rules2yaml.py +3 -6
  16. cognite/neat/rules/importers/_base.py +7 -52
  17. cognite/neat/rules/importers/_dms2rules.py +171 -115
  18. cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +26 -18
  19. cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +14 -30
  20. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +7 -3
  21. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +3 -3
  22. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +18 -11
  23. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +9 -18
  24. cognite/neat/rules/importers/_rdf/_inference2rules.py +10 -33
  25. cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
  26. cognite/neat/rules/importers/_rdf/_shared.py +1 -1
  27. cognite/neat/rules/importers/_spreadsheet2rules.py +22 -86
  28. cognite/neat/rules/importers/_yaml2rules.py +14 -41
  29. cognite/neat/rules/models/__init__.py +21 -5
  30. cognite/neat/rules/models/_base_input.py +162 -0
  31. cognite/neat/rules/models/{_base.py → _base_rules.py} +1 -12
  32. cognite/neat/rules/models/asset/__init__.py +5 -2
  33. cognite/neat/rules/models/asset/_rules.py +2 -20
  34. cognite/neat/rules/models/asset/_rules_input.py +40 -115
  35. cognite/neat/rules/models/asset/_validation.py +1 -1
  36. cognite/neat/rules/models/data_types.py +150 -44
  37. cognite/neat/rules/models/dms/__init__.py +19 -7
  38. cognite/neat/rules/models/dms/_exporter.py +72 -26
  39. cognite/neat/rules/models/dms/_rules.py +42 -155
  40. cognite/neat/rules/models/dms/_rules_input.py +186 -254
  41. cognite/neat/rules/models/dms/_serializer.py +44 -3
  42. cognite/neat/rules/models/dms/_validation.py +3 -4
  43. cognite/neat/rules/models/domain.py +52 -1
  44. cognite/neat/rules/models/entities/__init__.py +63 -0
  45. cognite/neat/rules/models/entities/_constants.py +73 -0
  46. cognite/neat/rules/models/entities/_loaders.py +76 -0
  47. cognite/neat/rules/models/entities/_multi_value.py +67 -0
  48. cognite/neat/rules/models/{entities.py → entities/_single_value.py} +74 -232
  49. cognite/neat/rules/models/entities/_types.py +86 -0
  50. cognite/neat/rules/models/{wrapped_entities.py → entities/_wrapped.py} +1 -1
  51. cognite/neat/rules/models/information/__init__.py +10 -2
  52. cognite/neat/rules/models/information/_rules.py +3 -14
  53. cognite/neat/rules/models/information/_rules_input.py +57 -204
  54. cognite/neat/rules/models/information/_validation.py +1 -1
  55. cognite/neat/rules/transformers/__init__.py +21 -0
  56. cognite/neat/rules/transformers/_base.py +69 -3
  57. cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +216 -20
  58. cognite/neat/rules/transformers/_map_onto.py +97 -0
  59. cognite/neat/rules/transformers/_pipelines.py +61 -0
  60. cognite/neat/rules/transformers/_verification.py +136 -0
  61. cognite/neat/store/_provenance.py +10 -1
  62. cognite/neat/utils/cdf/data_classes.py +20 -0
  63. cognite/neat/utils/regex_patterns.py +6 -0
  64. cognite/neat/workflows/steps/lib/current/rules_exporter.py +106 -37
  65. cognite/neat/workflows/steps/lib/current/rules_importer.py +24 -22
  66. {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
  67. {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +71 -66
  68. cognite/neat/rules/models/_constants.py +0 -2
  69. cognite/neat/rules/models/_types/__init__.py +0 -19
  70. cognite/neat/rules/models/asset/_converter.py +0 -4
  71. cognite/neat/rules/models/dms/_converter.py +0 -143
  72. /cognite/neat/rules/models/{_types/_field.py → _types.py} +0 -0
  73. {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
  74. {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
  75. {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,6 @@
1
1
  import math
2
- import re
3
2
  import sys
4
3
  import warnings
5
- from collections import defaultdict
6
4
  from datetime import datetime
7
5
  from typing import TYPE_CHECKING, Any, ClassVar, Literal
8
6
 
@@ -16,7 +14,7 @@ from cognite.neat.issues.warnings import (
16
14
  PrincipleMatchingSpaceAndVersionWarning,
17
15
  PrincipleSolutionBuildsOnEnterpriseWarning,
18
16
  )
19
- from cognite.neat.rules.models._base import (
17
+ from cognite.neat.rules.models._base_rules import (
20
18
  BaseMetadata,
21
19
  BaseRules,
22
20
  DataModelType,
@@ -33,24 +31,27 @@ from cognite.neat.rules.models._types import (
33
31
  VersionType,
34
32
  )
35
33
  from cognite.neat.rules.models.data_types import DataType
36
- from cognite.neat.rules.models.domain import DomainRules
37
34
  from cognite.neat.rules.models.entities import (
38
35
  ClassEntity,
39
36
  ContainerEntity,
40
37
  ContainerEntityList,
38
+ DMSNodeEntity,
41
39
  DMSUnknownEntity,
40
+ EdgeEntity,
41
+ HasDataFilter,
42
+ NodeTypeFilter,
43
+ RawFilter,
42
44
  ReferenceEntity,
45
+ ReverseConnectionEntity,
43
46
  URLEntity,
44
47
  ViewEntity,
45
48
  ViewEntityList,
46
- ViewPropertyEntity,
47
49
  )
48
- from cognite.neat.rules.models.wrapped_entities import HasDataFilter, NodeTypeFilter, RawFilter
49
50
 
50
51
  from ._schema import DMSSchema
51
52
 
52
53
  if TYPE_CHECKING:
53
- from cognite.neat.rules.models.information._rules import InformationRules
54
+ pass
54
55
 
55
56
  if sys.version_info >= (3, 11):
56
57
  pass
@@ -139,35 +140,6 @@ class DMSMetadata(BaseMetadata):
139
140
  def as_identifier(self) -> str:
140
141
  return repr(self.as_data_model_id())
141
142
 
142
- @classmethod
143
- def _get_description_and_creator(cls, description_raw: str | None) -> tuple[str | None, list[str]]:
144
- if description_raw and (description_match := re.search(r"Creator: (.+)", description_raw)):
145
- creator = description_match.group(1).split(", ")
146
- description = description_raw.replace(description_match.string, "").strip() or None
147
- elif description_raw:
148
- creator = ["MISSING"]
149
- description = description_raw
150
- else:
151
- creator = ["MISSING"]
152
- description = None
153
- return description, creator
154
-
155
- @classmethod
156
- def from_data_model(cls, data_model: dm.DataModelApply, has_reference: bool) -> "DMSMetadata":
157
- description, creator = cls._get_description_and_creator(data_model.description)
158
- return cls(
159
- schema_=SchemaCompleteness.complete,
160
- data_model_type=DataModelType.solution if has_reference else DataModelType.enterprise,
161
- space=data_model.space,
162
- name=data_model.name or None,
163
- description=description,
164
- external_id=data_model.external_id,
165
- version=data_model.version,
166
- creator=creator,
167
- created=datetime.now(),
168
- updated=datetime.now(),
169
- )
170
-
171
143
  def get_prefix(self) -> str:
172
144
  return self.space
173
145
 
@@ -177,8 +149,8 @@ class DMSProperty(SheetEntity):
177
149
  view_property: str = Field(alias="View Property")
178
150
  name: str | None = Field(alias="Name", default=None)
179
151
  description: str | None = Field(alias="Description", default=None)
180
- connection: Literal["direct", "edge", "reverse"] | None = Field(None, alias="Connection")
181
- value_type: DataType | ViewPropertyEntity | ViewEntity | DMSUnknownEntity = Field(alias="Value Type")
152
+ connection: Literal["direct"] | ReverseConnectionEntity | EdgeEntity | None = Field(None, alias="Connection")
153
+ value_type: DataType | ViewEntity | DMSUnknownEntity = Field(alias="Value Type")
182
154
  nullable: bool | None = Field(default=None, alias="Nullable")
183
155
  immutable: bool | None = Field(default=None, alias="Immutable")
184
156
  is_list: bool | None = Field(default=None, alias="Is List")
@@ -199,25 +171,23 @@ class DMSProperty(SheetEntity):
199
171
 
200
172
  @field_validator("value_type", mode="after")
201
173
  def connections_value_type(
202
- cls, value: ViewPropertyEntity | ViewEntity | DMSUnknownEntity, info: ValidationInfo
203
- ) -> DataType | ViewPropertyEntity | ViewEntity | DMSUnknownEntity:
174
+ cls, value: EdgeEntity | ViewEntity | DMSUnknownEntity, info: ValidationInfo
175
+ ) -> DataType | EdgeEntity | ViewEntity | DMSUnknownEntity:
204
176
  if (connection := info.data.get("connection")) is None:
205
177
  return value
206
178
  if connection == "direct" and not isinstance(value, ViewEntity | DMSUnknownEntity):
207
179
  raise ValueError(f"Direct relation must have a value type that points to a view, got {value}")
208
- elif connection == "edge" and not isinstance(value, ViewEntity):
180
+ elif isinstance(connection, EdgeEntity) and not isinstance(value, ViewEntity):
209
181
  raise ValueError(f"Edge connection must have a value type that points to a view, got {value}")
210
- elif connection == "reverse" and not isinstance(value, ViewPropertyEntity | ViewEntity):
211
- raise ValueError(
212
- f"Reverse connection must have a value type that points to a view or view property, got {value}"
213
- )
182
+ elif isinstance(connection, ReverseConnectionEntity) and not isinstance(value, ViewEntity):
183
+ raise ValueError(f"Reverse connection must have a value type that points to a view, got {value}")
214
184
  return value
215
185
 
216
186
  @field_serializer("value_type", when_used="always")
217
187
  @staticmethod
218
- def as_dms_type(value_type: DataType | ViewPropertyEntity | ViewEntity) -> str:
188
+ def as_dms_type(value_type: DataType | EdgeEntity | ViewEntity) -> str:
219
189
  if isinstance(value_type, DataType):
220
- return value_type.dms._type
190
+ return value_type._suffix_extra_args(value_type.dms._type)
221
191
  else:
222
192
  return str(value_type)
223
193
 
@@ -228,6 +198,7 @@ class DMSContainer(SheetEntity):
228
198
  description: str | None = Field(alias="Description", default=None)
229
199
  reference: URLEntity | ReferenceEntity | None = Field(alias="Reference", default=None, union_mode="left_to_right")
230
200
  constraint: ContainerEntityList | None = Field(None, alias="Constraint")
201
+ used_for: Literal["node", "edge", "all"] | None = Field("all", alias="Used For")
231
202
  class_: ClassEntity = Field(alias="Class (linage)")
232
203
 
233
204
  def as_container(self) -> dm.ContainerApply:
@@ -244,22 +215,7 @@ class DMSContainer(SheetEntity):
244
215
  description=self.description,
245
216
  constraints=constraints or None,
246
217
  properties={},
247
- )
248
-
249
- @classmethod
250
- def from_container(cls, container: dm.ContainerApply) -> "DMSContainer":
251
- constraints: list[ContainerEntity] = []
252
- for _, constraint_obj in (container.constraints or {}).items():
253
- if isinstance(constraint_obj, dm.RequiresConstraint):
254
- constraints.append(ContainerEntity.from_id(constraint_obj.require))
255
- # UniquenessConstraint it handled in the properties
256
- container_entity = ContainerEntity.from_id(container.as_id())
257
- return cls(
258
- class_=container_entity.as_class(),
259
- container=container_entity,
260
- name=container.name or None,
261
- description=container.description,
262
- constraint=constraints or None,
218
+ used_for=self.used_for,
263
219
  )
264
220
 
265
221
 
@@ -292,19 +248,27 @@ class DMSView(SheetEntity):
292
248
  properties={},
293
249
  )
294
250
 
295
- @classmethod
296
- def from_view(cls, view: dm.ViewApply, in_model: bool) -> "DMSView":
297
- view_entity = ViewEntity.from_id(view.as_id())
298
- class_entity = view_entity.as_class(skip_version=True)
299
-
300
- return cls(
301
- class_=class_entity,
302
- view=view_entity,
303
- description=view.description,
304
- name=view.name,
305
- implements=[ViewEntity.from_id(parent, _DEFAULT_VERSION) for parent in view.implements] or None,
306
- in_model=in_model,
307
- )
251
+
252
+ class DMSNode(SheetEntity):
253
+ node: DMSNodeEntity = Field(alias="Node")
254
+ usage: Literal["type", "collection"] = Field(alias="Usage")
255
+ name: str | None = Field(alias="Name", default=None)
256
+ description: str | None = Field(alias="Description", default=None)
257
+
258
+ def as_node(self) -> dm.NodeApply:
259
+ if self.usage == "type":
260
+ return dm.NodeApply(space=self.node.space, external_id=self.node.external_id)
261
+ elif self.usage == "collection":
262
+ raise NotImplementedError("Collection nodes are not supported yet")
263
+ else:
264
+ raise ValueError(f"Unknown usage {self.usage}")
265
+
266
+
267
+ class DMSEnum(SheetEntity):
268
+ collection: ClassEntity = Field(alias="Collection")
269
+ value: str = Field(alias="Value")
270
+ name: str | None = Field(alias="Name", default=None)
271
+ description: str | None = Field(alias="Description", default=None)
308
272
 
309
273
 
310
274
  class DMSRules(BaseRules):
@@ -312,6 +276,8 @@ class DMSRules(BaseRules):
312
276
  properties: SheetList[DMSProperty] = Field(alias="Properties")
313
277
  views: SheetList[DMSView] = Field(alias="Views")
314
278
  containers: SheetList[DMSContainer] | None = Field(None, alias="Containers")
279
+ enum: SheetList[DMSEnum] | None = Field(None, alias="Enum")
280
+ nodes: SheetList[DMSNode] | None = Field(None, alias="Nodes")
315
281
  last: "DMSRules | None" = Field(None, alias="Last", description="The previous version of the data model")
316
282
  reference: "DMSRules | None" = Field(None, alias="Reference")
317
283
 
@@ -401,82 +367,3 @@ class DMSRules(BaseRules):
401
367
  from ._exporter import _DMSExporter
402
368
 
403
369
  return _DMSExporter(self, include_pipeline, instance_space).to_schema()
404
-
405
- def as_information_rules(self) -> "InformationRules":
406
- from ._converter import _DMSRulesConverter
407
-
408
- return _DMSRulesConverter(self).as_information_rules()
409
-
410
- def as_domain_expert_rules(self) -> DomainRules:
411
- from ._converter import _DMSRulesConverter
412
-
413
- return _DMSRulesConverter(self).as_domain_rules()
414
-
415
- def create_reference(
416
- self, reference: "DMSRules", view_extension_mapping: dict[str, str], default_extension: str | None = None
417
- ) -> None:
418
- """Takes this data models and makes it into an extension of the reference data model.
419
-
420
- The argument view_extension_mapping is a dictionary where the keys are views of this data model,
421
- and each value is the view of the reference data model that the view should extend. For example:
422
-
423
- ```python
424
- view_extension_mapping = {"Pump": "Asset"}
425
- ```
426
-
427
- This would make the view "Pump" in this data model extend the view "Asset" in the reference data model.
428
- Note that all the keys in the dictionary must be external ids of views in this data model,
429
- and all the values must be external ids of views in the reference data model.
430
-
431
- Args:
432
- reference: The reference data model
433
- view_extension_mapping: A dictionary mapping views in this data model to views in the reference data model
434
- default_extension: The default view in the reference data model that views in this
435
- data model should extend if no mapping is provided.
436
-
437
- """
438
- if self.reference is not None:
439
- raise ValueError("Reference already exists")
440
- self.reference = reference
441
- view_by_external_id = {view.view.external_id: view for view in self.views}
442
- ref_view_by_external_id = {view.view.external_id: view for view in reference.views}
443
-
444
- if invalid_views := set(view_extension_mapping.keys()) - set(view_by_external_id.keys()):
445
- raise ValueError(f"Views are not in this dat model {invalid_views}")
446
- if invalid_views := set(view_extension_mapping.values()) - set(ref_view_by_external_id.keys()):
447
- raise ValueError(f"Views are not in the reference data model {invalid_views}")
448
- if default_extension and default_extension not in ref_view_by_external_id:
449
- raise ValueError(f"Default extension view not in the reference data model {default_extension}")
450
-
451
- properties_by_view_external_id: dict[str, dict[str, DMSProperty]] = defaultdict(dict)
452
- for prop in self.properties:
453
- properties_by_view_external_id[prop.view.external_id][prop.view_property] = prop
454
-
455
- ref_properties_by_view_external_id: dict[str, dict[str, DMSProperty]] = defaultdict(dict)
456
- for prop in reference.properties:
457
- ref_properties_by_view_external_id[prop.view.external_id][prop.view_property] = prop
458
-
459
- for view_external_id, view in view_by_external_id.items():
460
- if view_external_id in view_extension_mapping:
461
- ref_external_id = view_extension_mapping[view_external_id]
462
- elif default_extension:
463
- ref_external_id = default_extension
464
- else:
465
- continue
466
-
467
- ref_view = ref_view_by_external_id[ref_external_id]
468
- shared_properties = set(properties_by_view_external_id[view_external_id].keys()) & set(
469
- ref_properties_by_view_external_id[ref_external_id].keys()
470
- )
471
- if shared_properties:
472
- if view.implements is None:
473
- view.implements = [ref_view.view]
474
- elif isinstance(view.implements, list) and ref_view.view not in view.implements:
475
- view.implements.append(ref_view.view)
476
- for prop_name in shared_properties:
477
- prop = properties_by_view_external_id[view_external_id][prop_name]
478
- ref_prop = ref_properties_by_view_external_id[ref_external_id][prop_name]
479
- if ref_prop.container and ref_prop.container_property:
480
- prop.container = ref_prop.container
481
- prop.container_property = ref_prop.container_property
482
- prop.reference = ReferenceEntity.from_entity(ref_prop.view, ref_prop.view_property)