cognite-neat 0.88.3__py3-none-any.whl → 0.90.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 +6 -3
- cognite/neat/graph/extractors/__init__.py +2 -0
- cognite/neat/graph/extractors/_classic_cdf/_base.py +2 -2
- cognite/neat/graph/extractors/_dms.py +158 -0
- cognite/neat/graph/extractors/_mock_graph_generator.py +50 -9
- cognite/neat/graph/loaders/_rdf2dms.py +16 -13
- cognite/neat/graph/models.py +1 -0
- cognite/neat/graph/queries/_base.py +4 -2
- cognite/neat/issues/_base.py +3 -1
- cognite/neat/issues/errors/__init__.py +2 -1
- cognite/neat/issues/errors/_general.py +7 -0
- cognite/neat/issues/warnings/__init__.py +2 -0
- cognite/neat/issues/warnings/_models.py +1 -1
- cognite/neat/issues/warnings/_properties.py +12 -0
- 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 +172 -116
- 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 +37 -65
- 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 +82 -39
- 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} +226 -21
- 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/_base.py +2 -2
- 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.90.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.3.dist-info → cognite_neat-0.90.0.dist-info}/RECORD +80 -74
- 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.90.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.3.dist-info → cognite_neat-0.90.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.3.dist-info → cognite_neat-0.90.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
from collections import defaultdict
|
|
3
|
-
from collections.abc import Sequence
|
|
3
|
+
from collections.abc import Collection, Sequence
|
|
4
4
|
from typing import Any, cast
|
|
5
5
|
|
|
6
6
|
from cognite.client.data_classes import data_modeling as dm
|
|
7
7
|
from cognite.client.data_classes.data_modeling.containers import BTreeIndex
|
|
8
|
+
from cognite.client.data_classes.data_modeling.data_types import EnumValue as DMSEnumValue
|
|
8
9
|
from cognite.client.data_classes.data_modeling.data_types import ListablePropertyType
|
|
9
10
|
from cognite.client.data_classes.data_modeling.views import (
|
|
10
11
|
SingleEdgeConnectionApply,
|
|
@@ -12,6 +13,7 @@ from cognite.client.data_classes.data_modeling.views import (
|
|
|
12
13
|
ViewPropertyApply,
|
|
13
14
|
)
|
|
14
15
|
|
|
16
|
+
from cognite.neat.issues.errors import NeatTypeError, ResourceNotFoundError
|
|
15
17
|
from cognite.neat.issues.warnings import NotSupportedWarning, PropertyNotFoundWarning
|
|
16
18
|
from cognite.neat.issues.warnings.user_modeling import (
|
|
17
19
|
EmptyContainerWarning,
|
|
@@ -19,20 +21,25 @@ from cognite.neat.issues.warnings.user_modeling import (
|
|
|
19
21
|
HasDataFilterOnViewWithReferencesWarning,
|
|
20
22
|
NodeTypeFilterOnParentViewWarning,
|
|
21
23
|
)
|
|
22
|
-
from cognite.neat.rules.models.
|
|
23
|
-
from cognite.neat.rules.models.data_types import DataType
|
|
24
|
+
from cognite.neat.rules.models._base_rules import DataModelType, ExtensionCategory, SchemaCompleteness
|
|
25
|
+
from cognite.neat.rules.models.data_types import DataType, Double, Enum, Float
|
|
24
26
|
from cognite.neat.rules.models.entities import (
|
|
27
|
+
ClassEntity,
|
|
25
28
|
ContainerEntity,
|
|
29
|
+
DMSFilter,
|
|
26
30
|
DMSNodeEntity,
|
|
27
31
|
DMSUnknownEntity,
|
|
32
|
+
EdgeEntity,
|
|
33
|
+
HasDataFilter,
|
|
34
|
+
NodeTypeFilter,
|
|
28
35
|
ReferenceEntity,
|
|
36
|
+
ReverseConnectionEntity,
|
|
37
|
+
UnitEntity,
|
|
29
38
|
ViewEntity,
|
|
30
|
-
ViewPropertyEntity,
|
|
31
39
|
)
|
|
32
|
-
from cognite.neat.rules.models.wrapped_entities import DMSFilter, HasDataFilter, NodeTypeFilter
|
|
33
40
|
from cognite.neat.utils.cdf.data_classes import ContainerApplyDict, NodeApplyDict, SpaceApplyDict, ViewApplyDict
|
|
34
41
|
|
|
35
|
-
from ._rules import DMSMetadata, DMSProperty, DMSRules, DMSView
|
|
42
|
+
from ._rules import DMSEnum, DMSMetadata, DMSProperty, DMSRules, DMSView
|
|
36
43
|
from ._schema import DMSSchema, PipelineSchema
|
|
37
44
|
|
|
38
45
|
|
|
@@ -111,9 +118,16 @@ class _DMSExporter:
|
|
|
111
118
|
]
|
|
112
119
|
self._update_with_properties(selected_properties, container_properties_by_id, None)
|
|
113
120
|
|
|
114
|
-
containers = self._create_containers(container_properties_by_id)
|
|
121
|
+
containers = self._create_containers(container_properties_by_id, rules.enum) # type: ignore[arg-type]
|
|
115
122
|
|
|
116
|
-
views,
|
|
123
|
+
views, view_node_type_filters = self._create_views_with_node_types(view_properties_by_id)
|
|
124
|
+
if rules.nodes:
|
|
125
|
+
node_types = NodeApplyDict(
|
|
126
|
+
[node.as_node() for node in rules.nodes]
|
|
127
|
+
+ [dm.NodeApply(node.space, node.external_id) for node in view_node_type_filters]
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
node_types = NodeApplyDict([dm.NodeApply(node.space, node.external_id) for node in view_node_type_filters])
|
|
117
131
|
|
|
118
132
|
last_schema: DMSSchema | None = None
|
|
119
133
|
if self.rules.last:
|
|
@@ -184,7 +198,7 @@ class _DMSExporter:
|
|
|
184
198
|
def _create_views_with_node_types(
|
|
185
199
|
self,
|
|
186
200
|
view_properties_by_id: dict[dm.ViewId, list[DMSProperty]],
|
|
187
|
-
) -> tuple[ViewApplyDict,
|
|
201
|
+
) -> tuple[ViewApplyDict, set[dm.NodeId]]:
|
|
188
202
|
input_views = list(self.rules.views)
|
|
189
203
|
if self.rules.last:
|
|
190
204
|
existing = {view.view.as_id() for view in input_views}
|
|
@@ -245,17 +259,19 @@ class _DMSExporter:
|
|
|
245
259
|
# as they are expected for the solution model.
|
|
246
260
|
unique_node_types.add(dm.NodeId(space=view.space, external_id=view.external_id))
|
|
247
261
|
|
|
248
|
-
return views,
|
|
249
|
-
[dm.NodeApply(space=node.space, external_id=node.external_id) for node in unique_node_types]
|
|
250
|
-
)
|
|
262
|
+
return views, unique_node_types
|
|
251
263
|
|
|
252
264
|
@classmethod
|
|
253
265
|
def _create_edge_type_from_prop(cls, prop: DMSProperty) -> dm.DirectRelationReference:
|
|
254
|
-
if isinstance(prop.
|
|
266
|
+
if isinstance(prop.connection, EdgeEntity) and prop.connection.edge_type is not None:
|
|
267
|
+
return prop.connection.edge_type.as_reference()
|
|
268
|
+
elif isinstance(prop.reference, ReferenceEntity):
|
|
255
269
|
ref_view_prop = prop.reference.as_view_property_id()
|
|
256
270
|
return cls._create_edge_type_from_view_id(cast(dm.ViewId, ref_view_prop.source), ref_view_prop.property)
|
|
257
|
-
|
|
271
|
+
elif isinstance(prop.value_type, ViewEntity):
|
|
258
272
|
return cls._create_edge_type_from_view_id(prop.view.as_id(), prop.view_property)
|
|
273
|
+
else:
|
|
274
|
+
raise NeatTypeError(f"Invalid valueType {prop.value_type!r}")
|
|
259
275
|
|
|
260
276
|
@staticmethod
|
|
261
277
|
def _create_edge_type_from_view_id(view_id: dm.ViewId, property_: str) -> dm.DirectRelationReference:
|
|
@@ -268,7 +284,12 @@ class _DMSExporter:
|
|
|
268
284
|
def _create_containers(
|
|
269
285
|
self,
|
|
270
286
|
container_properties_by_id: dict[dm.ContainerId, list[DMSProperty]],
|
|
287
|
+
enum: Collection[DMSEnum] | None,
|
|
271
288
|
) -> ContainerApplyDict:
|
|
289
|
+
enum_values_by_collection: dict[ClassEntity, list[DMSEnum]] = defaultdict(list)
|
|
290
|
+
for enum_value in enum or []:
|
|
291
|
+
enum_values_by_collection[enum_value.collection].append(enum_value)
|
|
292
|
+
|
|
272
293
|
containers = list(self.rules.containers or [])
|
|
273
294
|
if self.rules.last:
|
|
274
295
|
existing = {container.container.as_id() for container in containers}
|
|
@@ -297,17 +318,34 @@ class _DMSExporter:
|
|
|
297
318
|
type_cls = prop.value_type.dms
|
|
298
319
|
else:
|
|
299
320
|
type_cls = dm.DirectRelation
|
|
300
|
-
|
|
321
|
+
|
|
322
|
+
args: dict[str, Any] = {}
|
|
301
323
|
if issubclass(type_cls, ListablePropertyType):
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
324
|
+
args["is_list"] = prop.is_list or False
|
|
325
|
+
if isinstance(prop.value_type, Double | Float) and isinstance(prop.value_type.unit, UnitEntity):
|
|
326
|
+
args["unit"] = prop.value_type.unit.as_reference()
|
|
327
|
+
if isinstance(prop.value_type, Enum):
|
|
328
|
+
if prop.value_type.collection not in enum_values_by_collection:
|
|
329
|
+
raise ResourceNotFoundError(
|
|
330
|
+
prop.value_type.collection, "enum collection", prop.container, "container"
|
|
331
|
+
)
|
|
332
|
+
args["unknown_value"] = prop.value_type.unknown_value
|
|
333
|
+
args["values"] = {
|
|
334
|
+
value.value: DMSEnumValue(
|
|
335
|
+
name=value.name,
|
|
336
|
+
description=value.description,
|
|
337
|
+
)
|
|
338
|
+
for value in enum_values_by_collection[prop.value_type.collection]
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
type_ = type_cls(**args)
|
|
305
342
|
container.properties[prop.container_property] = dm.ContainerProperty(
|
|
306
343
|
type=type_,
|
|
307
344
|
# If not set, nullable is True and immutable is False
|
|
308
345
|
nullable=prop.nullable if prop.nullable is not None else True,
|
|
309
346
|
immutable=prop.immutable if prop.immutable is not None else False,
|
|
310
|
-
|
|
347
|
+
# Guarding against default value being set for connection properties
|
|
348
|
+
default_value=prop.default if not prop.connection else None,
|
|
311
349
|
name=prop.name,
|
|
312
350
|
description=prop.description,
|
|
313
351
|
)
|
|
@@ -463,7 +501,7 @@ class _DMSExporter:
|
|
|
463
501
|
description=prop.description,
|
|
464
502
|
**extra_args,
|
|
465
503
|
)
|
|
466
|
-
elif prop.connection
|
|
504
|
+
elif isinstance(prop.connection, EdgeEntity):
|
|
467
505
|
if isinstance(prop.value_type, ViewEntity):
|
|
468
506
|
source_view_id = prop.value_type.as_id()
|
|
469
507
|
else:
|
|
@@ -472,6 +510,9 @@ class _DMSExporter:
|
|
|
472
510
|
"If this error occurs it is a bug in NEAT, please report"
|
|
473
511
|
f"Debug Info, Invalid valueType edge: {prop.model_dump_json()}"
|
|
474
512
|
)
|
|
513
|
+
edge_source: dm.ViewId | None = None
|
|
514
|
+
if prop.connection.properties is not None:
|
|
515
|
+
edge_source = prop.connection.properties.as_id()
|
|
475
516
|
edge_cls: type[dm.EdgeConnectionApply] = dm.MultiEdgeConnectionApply
|
|
476
517
|
# If is_list is not set, we default to a MultiEdgeConnection
|
|
477
518
|
if prop.is_list is False:
|
|
@@ -483,13 +524,11 @@ class _DMSExporter:
|
|
|
483
524
|
direction="outwards",
|
|
484
525
|
name=prop.name,
|
|
485
526
|
description=prop.description,
|
|
527
|
+
edge_source=edge_source,
|
|
486
528
|
)
|
|
487
|
-
elif prop.connection
|
|
488
|
-
reverse_prop_id
|
|
489
|
-
if isinstance(prop.value_type,
|
|
490
|
-
source_view_id = prop.value_type.as_view_id()
|
|
491
|
-
reverse_prop_id = prop.value_type.property_
|
|
492
|
-
elif isinstance(prop.value_type, ViewEntity):
|
|
529
|
+
elif isinstance(prop.connection, ReverseConnectionEntity):
|
|
530
|
+
reverse_prop_id = prop.connection.property_
|
|
531
|
+
if isinstance(prop.value_type, ViewEntity):
|
|
493
532
|
source_view_id = prop.value_type.as_id()
|
|
494
533
|
else:
|
|
495
534
|
# Should have been validated.
|
|
@@ -497,16 +536,17 @@ class _DMSExporter:
|
|
|
497
536
|
"If this error occurs it is a bug in NEAT, please report"
|
|
498
537
|
f"Debug Info, Invalid valueType reverse connection: {prop.model_dump_json()}"
|
|
499
538
|
)
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
539
|
+
edge_source = None
|
|
540
|
+
reverse_prop = next(
|
|
541
|
+
(prop for prop in view_properties_by_id.get(source_view_id, []) if prop.property_ == reverse_prop_id),
|
|
542
|
+
None,
|
|
543
|
+
)
|
|
544
|
+
if (
|
|
545
|
+
reverse_prop
|
|
546
|
+
and isinstance(reverse_prop.connection, EdgeEntity)
|
|
547
|
+
and reverse_prop.connection.properties is not None
|
|
548
|
+
):
|
|
549
|
+
edge_source = reverse_prop.connection.properties.as_id()
|
|
510
550
|
|
|
511
551
|
if reverse_prop is None:
|
|
512
552
|
warnings.warn(
|
|
@@ -520,7 +560,7 @@ class _DMSExporter:
|
|
|
520
560
|
stacklevel=2,
|
|
521
561
|
)
|
|
522
562
|
|
|
523
|
-
if reverse_prop is None or reverse_prop.connection
|
|
563
|
+
if reverse_prop is None or isinstance(reverse_prop.connection, EdgeEntity):
|
|
524
564
|
inwards_edge_cls = (
|
|
525
565
|
dm.MultiEdgeConnectionApply if prop.is_list in [True, None] else SingleEdgeConnectionApply
|
|
526
566
|
)
|
|
@@ -530,10 +570,13 @@ class _DMSExporter:
|
|
|
530
570
|
name=prop.name,
|
|
531
571
|
description=prop.description,
|
|
532
572
|
direction="inwards",
|
|
573
|
+
edge_source=edge_source,
|
|
533
574
|
)
|
|
534
|
-
elif
|
|
575
|
+
elif reverse_prop and reverse_prop.connection == "direct":
|
|
535
576
|
reverse_direct_cls = (
|
|
536
|
-
dm.MultiReverseDirectRelationApply
|
|
577
|
+
dm.MultiReverseDirectRelationApply
|
|
578
|
+
if prop.is_list in [True, None]
|
|
579
|
+
else SingleReverseDirectRelationApply
|
|
537
580
|
)
|
|
538
581
|
return reverse_direct_cls(
|
|
539
582
|
source=source_view_id,
|
|
@@ -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)
|