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.
- cognite/neat/_version.py +1 -1
- cognite/neat/constants.py +3 -0
- cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
- cognite/neat/issues/_base.py +2 -1
- cognite/neat/issues/errors/__init__.py +2 -1
- cognite/neat/issues/errors/_general.py +7 -0
- cognite/neat/issues/warnings/_models.py +1 -1
- cognite/neat/issues/warnings/user_modeling.py +1 -1
- cognite/neat/rules/_shared.py +49 -6
- cognite/neat/rules/analysis/_base.py +1 -1
- cognite/neat/rules/exporters/_base.py +7 -18
- cognite/neat/rules/exporters/_rules2dms.py +8 -18
- cognite/neat/rules/exporters/_rules2excel.py +5 -12
- cognite/neat/rules/exporters/_rules2ontology.py +9 -19
- cognite/neat/rules/exporters/_rules2yaml.py +3 -6
- cognite/neat/rules/importers/_base.py +7 -52
- cognite/neat/rules/importers/_dms2rules.py +171 -115
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +26 -18
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +14 -30
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +7 -3
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +3 -3
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +18 -11
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +9 -18
- cognite/neat/rules/importers/_rdf/_inference2rules.py +10 -33
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
- cognite/neat/rules/importers/_rdf/_shared.py +1 -1
- cognite/neat/rules/importers/_spreadsheet2rules.py +22 -86
- cognite/neat/rules/importers/_yaml2rules.py +14 -41
- cognite/neat/rules/models/__init__.py +21 -5
- cognite/neat/rules/models/_base_input.py +162 -0
- cognite/neat/rules/models/{_base.py → _base_rules.py} +1 -12
- cognite/neat/rules/models/asset/__init__.py +5 -2
- cognite/neat/rules/models/asset/_rules.py +2 -20
- cognite/neat/rules/models/asset/_rules_input.py +40 -115
- cognite/neat/rules/models/asset/_validation.py +1 -1
- cognite/neat/rules/models/data_types.py +150 -44
- cognite/neat/rules/models/dms/__init__.py +19 -7
- cognite/neat/rules/models/dms/_exporter.py +72 -26
- cognite/neat/rules/models/dms/_rules.py +42 -155
- cognite/neat/rules/models/dms/_rules_input.py +186 -254
- cognite/neat/rules/models/dms/_serializer.py +44 -3
- cognite/neat/rules/models/dms/_validation.py +3 -4
- cognite/neat/rules/models/domain.py +52 -1
- cognite/neat/rules/models/entities/__init__.py +63 -0
- cognite/neat/rules/models/entities/_constants.py +73 -0
- cognite/neat/rules/models/entities/_loaders.py +76 -0
- cognite/neat/rules/models/entities/_multi_value.py +67 -0
- cognite/neat/rules/models/{entities.py → entities/_single_value.py} +74 -232
- cognite/neat/rules/models/entities/_types.py +86 -0
- cognite/neat/rules/models/{wrapped_entities.py → entities/_wrapped.py} +1 -1
- cognite/neat/rules/models/information/__init__.py +10 -2
- cognite/neat/rules/models/information/_rules.py +3 -14
- cognite/neat/rules/models/information/_rules_input.py +57 -204
- cognite/neat/rules/models/information/_validation.py +1 -1
- cognite/neat/rules/transformers/__init__.py +21 -0
- cognite/neat/rules/transformers/_base.py +69 -3
- cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +216 -20
- cognite/neat/rules/transformers/_map_onto.py +97 -0
- cognite/neat/rules/transformers/_pipelines.py +61 -0
- cognite/neat/rules/transformers/_verification.py +136 -0
- cognite/neat/store/_provenance.py +10 -1
- cognite/neat/utils/cdf/data_classes.py +20 -0
- cognite/neat/utils/regex_patterns.py +6 -0
- cognite/neat/workflows/steps/lib/current/rules_exporter.py +106 -37
- cognite/neat/workflows/steps/lib/current/rules_importer.py +24 -22
- {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +71 -66
- cognite/neat/rules/models/_constants.py +0 -2
- cognite/neat/rules/models/_types/__init__.py +0 -19
- cognite/neat/rules/models/asset/_converter.py +0 -4
- cognite/neat/rules/models/dms/_converter.py +0 -143
- /cognite/neat/rules/models/{_types/_field.py → _types.py} +0 -0
- {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.3.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
- {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.
|
|
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
|
-
|
|
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"
|
|
181
|
-
value_type: DataType |
|
|
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:
|
|
203
|
-
) -> DataType |
|
|
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
|
|
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
|
|
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 |
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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)
|