cognite-neat 0.88.2__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/__init__.py +0 -3
- cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
- cognite/neat/graph/loaders/_base.py +3 -3
- cognite/neat/graph/loaders/_rdf2asset.py +24 -25
- cognite/neat/graph/loaders/_rdf2dms.py +20 -15
- cognite/neat/issues/__init__.py +1 -3
- cognite/neat/issues/_base.py +261 -71
- cognite/neat/issues/errors/__init__.py +73 -0
- cognite/neat/issues/errors/_external.py +67 -0
- cognite/neat/issues/errors/_general.py +35 -0
- cognite/neat/issues/errors/_properties.py +62 -0
- cognite/neat/issues/errors/_resources.py +111 -0
- cognite/neat/issues/errors/_workflow.py +36 -0
- cognite/neat/issues/formatters.py +1 -1
- cognite/neat/issues/warnings/__init__.py +66 -0
- cognite/neat/issues/warnings/_external.py +40 -0
- cognite/neat/issues/warnings/_general.py +29 -0
- cognite/neat/issues/warnings/_models.py +92 -0
- cognite/neat/issues/warnings/_properties.py +44 -0
- cognite/neat/issues/warnings/_resources.py +55 -0
- cognite/neat/issues/warnings/user_modeling.py +113 -0
- cognite/neat/rules/_shared.py +53 -2
- cognite/neat/rules/analysis/_base.py +1 -1
- cognite/neat/rules/exporters/_base.py +7 -18
- cognite/neat/rules/exporters/_rules2dms.py +17 -20
- cognite/neat/rules/exporters/_rules2excel.py +9 -16
- cognite/neat/rules/exporters/_rules2ontology.py +77 -64
- cognite/neat/rules/exporters/_rules2yaml.py +6 -9
- cognite/neat/rules/exporters/_validation.py +11 -96
- cognite/neat/rules/importers/_base.py +9 -58
- cognite/neat/rules/importers/_dms2rules.py +188 -135
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +48 -35
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +36 -45
- cognite/neat/rules/importers/_dtdl2rules/spec.py +7 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +8 -4
- 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 +12 -19
- cognite/neat/rules/importers/_rdf/_inference2rules.py +14 -37
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
- cognite/neat/rules/importers/_rdf/_shared.py +4 -4
- cognite/neat/rules/importers/_spreadsheet2rules.py +46 -97
- cognite/neat/rules/importers/_yaml2rules.py +32 -58
- 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/_rdfpath.py +4 -4
- cognite/neat/rules/models/{_types/_field.py → _types.py} +5 -10
- cognite/neat/rules/models/asset/__init__.py +5 -2
- cognite/neat/rules/models/asset/_rules.py +3 -23
- cognite/neat/rules/models/asset/_rules_input.py +40 -115
- cognite/neat/rules/models/asset/_validation.py +14 -10
- 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 +102 -34
- cognite/neat/rules/models/dms/_rules.py +65 -162
- cognite/neat/rules/models/dms/_rules_input.py +186 -254
- cognite/neat/rules/models/dms/_schema.py +87 -78
- cognite/neat/rules/models/dms/_serializer.py +44 -3
- cognite/neat/rules/models/dms/_validation.py +106 -68
- 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 +10 -22
- cognite/neat/rules/models/information/_rules_input.py +57 -204
- cognite/neat/rules/models/information/_validation.py +48 -25
- cognite/neat/rules/transformers/__init__.py +21 -0
- cognite/neat/rules/transformers/_base.py +81 -0
- cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +217 -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/{graph/stores → store}/_provenance.py +10 -1
- cognite/neat/utils/auxiliary.py +2 -35
- cognite/neat/utils/cdf/data_classes.py +20 -0
- cognite/neat/utils/regex_patterns.py +6 -0
- cognite/neat/utils/text.py +17 -0
- cognite/neat/workflows/base.py +4 -4
- cognite/neat/workflows/cdf_store.py +3 -3
- cognite/neat/workflows/steps/data_contracts.py +1 -1
- cognite/neat/workflows/steps/lib/current/graph_extractor.py +3 -3
- cognite/neat/workflows/steps/lib/current/graph_loader.py +2 -2
- cognite/neat/workflows/steps/lib/current/graph_store.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_exporter.py +116 -47
- cognite/neat/workflows/steps/lib/current/rules_importer.py +30 -28
- cognite/neat/workflows/steps/lib/current/rules_validator.py +5 -6
- cognite/neat/workflows/steps/lib/io/io_steps.py +5 -5
- cognite/neat/workflows/steps_registry.py +4 -5
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +105 -106
- cognite/neat/exceptions.py +0 -145
- cognite/neat/graph/exceptions.py +0 -90
- cognite/neat/issues/errors/external.py +0 -21
- cognite/neat/issues/errors/properties.py +0 -75
- cognite/neat/issues/errors/resources.py +0 -123
- cognite/neat/issues/errors/schema.py +0 -0
- cognite/neat/issues/neat_warnings/__init__.py +0 -2
- cognite/neat/issues/neat_warnings/identifier.py +0 -27
- cognite/neat/issues/neat_warnings/models.py +0 -22
- cognite/neat/issues/neat_warnings/properties.py +0 -77
- cognite/neat/issues/neat_warnings/resources.py +0 -125
- cognite/neat/rules/issues/__init__.py +0 -22
- cognite/neat/rules/issues/base.py +0 -63
- cognite/neat/rules/issues/dms.py +0 -549
- cognite/neat/rules/issues/fileread.py +0 -197
- cognite/neat/rules/issues/ontology.py +0 -298
- cognite/neat/rules/issues/spreadsheet.py +0 -563
- cognite/neat/rules/issues/spreadsheet_file.py +0 -151
- cognite/neat/rules/issues/tables.py +0 -72
- cognite/neat/rules/models/_constants.py +0 -1
- 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 -145
- cognite/neat/workflows/_exceptions.py +0 -41
- /cognite/neat/{graph/stores → store}/__init__.py +0 -0
- /cognite/neat/{graph/stores → store}/_base.py +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/entry_points.txt +0 -0
|
@@ -26,17 +26,20 @@ from cognite.client.data_classes.data_modeling.views import (
|
|
|
26
26
|
from cognite.client.data_classes.transformations.common import Edges, EdgeType, Nodes, ViewInfo
|
|
27
27
|
|
|
28
28
|
from cognite.neat.issues import NeatError
|
|
29
|
-
from cognite.neat.issues.errors
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
DirectRelationMissingSourceWarning,
|
|
36
|
-
DuplicatedViewInDataModelError,
|
|
37
|
-
IncompleteSchemaError,
|
|
38
|
-
MissingViewInModelWarning,
|
|
29
|
+
from cognite.neat.issues.errors import (
|
|
30
|
+
NeatYamlError,
|
|
31
|
+
PropertyMappingDuplicatedError,
|
|
32
|
+
PropertyNotFoundError,
|
|
33
|
+
ResourceDuplicatedError,
|
|
34
|
+
ResourceNotFoundError,
|
|
39
35
|
)
|
|
36
|
+
from cognite.neat.issues.warnings import (
|
|
37
|
+
FileTypeUnexpectedWarning,
|
|
38
|
+
ResourceNotFoundWarning,
|
|
39
|
+
ResourceRetrievalWarning,
|
|
40
|
+
ResourcesDuplicatedWarning,
|
|
41
|
+
)
|
|
42
|
+
from cognite.neat.issues.warnings.user_modeling import DirectRelationMissingSourceWarning
|
|
40
43
|
from cognite.neat.rules.models.data_types import _DATA_TYPE_BY_DMS_TYPE
|
|
41
44
|
from cognite.neat.utils.cdf.data_classes import (
|
|
42
45
|
CogniteResourceDict,
|
|
@@ -93,11 +96,11 @@ class DMSSchema:
|
|
|
93
96
|
directly_referenced_containers = view_by_id[view_id].referenced_containers()
|
|
94
97
|
inherited_referenced_containers = set()
|
|
95
98
|
|
|
96
|
-
for
|
|
97
|
-
if implemented_view := view_by_id.get(
|
|
99
|
+
for parent_id in view_inheritance:
|
|
100
|
+
if implemented_view := view_by_id.get(parent_id):
|
|
98
101
|
inherited_referenced_containers |= implemented_view.referenced_containers()
|
|
99
102
|
else:
|
|
100
|
-
raise
|
|
103
|
+
raise ResourceNotFoundError(parent_id, "view", view_id, "view")
|
|
101
104
|
|
|
102
105
|
return directly_referenced_containers | inherited_referenced_containers
|
|
103
106
|
|
|
@@ -164,10 +167,14 @@ class DMSSchema:
|
|
|
164
167
|
connection_referenced_view_ids |= cls._connection_references(view)
|
|
165
168
|
connection_referenced_view_ids = connection_referenced_view_ids - existing_view_ids
|
|
166
169
|
if connection_referenced_view_ids:
|
|
167
|
-
|
|
170
|
+
for view_id in connection_referenced_view_ids:
|
|
171
|
+
warnings.warn(
|
|
172
|
+
ResourceNotFoundWarning(view_id, "view", data_model_write.as_id(), "data model"),
|
|
173
|
+
stacklevel=2,
|
|
174
|
+
)
|
|
168
175
|
connection_referenced_views = view_loader.retrieve(list(connection_referenced_view_ids))
|
|
169
176
|
if failed := connection_referenced_view_ids - set(connection_referenced_views.as_ids()):
|
|
170
|
-
warnings.warn(
|
|
177
|
+
warnings.warn(ResourceRetrievalWarning(frozenset(failed), "view"), stacklevel=2)
|
|
171
178
|
views.extend(connection_referenced_views)
|
|
172
179
|
|
|
173
180
|
# We need to include parent views in the schema to make sure that the schema is valid.
|
|
@@ -262,10 +269,11 @@ class DMSSchema:
|
|
|
262
269
|
data.setdefault(attr_name, [])
|
|
263
270
|
context.setdefault(attr_name, [])
|
|
264
271
|
try:
|
|
265
|
-
# Using CSafeLoader over safe_load for ~10x speedup
|
|
266
272
|
loaded = yaml.safe_load(yaml_file.read_text())
|
|
267
273
|
except Exception as e:
|
|
268
|
-
warnings.warn(
|
|
274
|
+
warnings.warn(
|
|
275
|
+
FileTypeUnexpectedWarning(yaml_file, frozenset([".yaml", ".yml"]), str(e)), stacklevel=2
|
|
276
|
+
)
|
|
269
277
|
continue
|
|
270
278
|
|
|
271
279
|
if isinstance(loaded, list):
|
|
@@ -355,7 +363,9 @@ class DMSSchema:
|
|
|
355
363
|
try:
|
|
356
364
|
loaded = yaml.safe_load(zip_ref.read(file_info).decode())
|
|
357
365
|
except Exception as e:
|
|
358
|
-
warnings.warn(
|
|
366
|
+
warnings.warn(
|
|
367
|
+
FileTypeUnexpectedWarning(filename, frozenset([".yaml", ".yml"]), str(e)), stacklevel=2
|
|
368
|
+
)
|
|
359
369
|
continue
|
|
360
370
|
if isinstance(loaded, list):
|
|
361
371
|
data[attr_name].extend(loaded)
|
|
@@ -411,11 +421,9 @@ class DMSSchema:
|
|
|
411
421
|
try:
|
|
412
422
|
data_dict = yaml.safe_load(data)
|
|
413
423
|
except Exception as e:
|
|
414
|
-
raise
|
|
424
|
+
raise NeatYamlError(str(e)) from None
|
|
415
425
|
if not isinstance(data_dict, dict) and all(isinstance(v, list) for v in data_dict.values()):
|
|
416
|
-
raise
|
|
417
|
-
"dict[str, list[Any]]", f"Invalid data structure: {type(data)}"
|
|
418
|
-
).as_exception() from None
|
|
426
|
+
raise NeatYamlError(f"Invalid data structure: {type(data)}", "dict[str, list[Any]]") from None
|
|
419
427
|
else:
|
|
420
428
|
data_dict = data
|
|
421
429
|
loaded: dict[str, Any] = {}
|
|
@@ -423,20 +431,32 @@ class DMSSchema:
|
|
|
423
431
|
if items := data_dict.get(attr.name) or data_dict.get(to_camel(attr.name)):
|
|
424
432
|
if attr.name == "data_model":
|
|
425
433
|
if isinstance(items, list) and len(items) > 1:
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
434
|
+
try:
|
|
435
|
+
data_model_ids = [dm.DataModelId.load(item) for item in items]
|
|
436
|
+
except Exception as e:
|
|
437
|
+
data_model_file = context.get(attr.name, [Path("UNKNOWN")])[0]
|
|
438
|
+
warnings.warn(
|
|
439
|
+
FileTypeUnexpectedWarning(
|
|
440
|
+
data_model_file, frozenset([dm.DataModelApply.__name__]), str(e)
|
|
441
|
+
),
|
|
442
|
+
stacklevel=2,
|
|
443
|
+
)
|
|
444
|
+
else:
|
|
445
|
+
warnings.warn(
|
|
446
|
+
ResourcesDuplicatedWarning(
|
|
447
|
+
frozenset(data_model_ids),
|
|
448
|
+
"data model",
|
|
449
|
+
"Will use the first DataModel.",
|
|
450
|
+
),
|
|
451
|
+
stacklevel=2,
|
|
452
|
+
)
|
|
433
453
|
item = items[0] if isinstance(items, list) else items
|
|
434
454
|
try:
|
|
435
455
|
loaded[attr.name] = dm.DataModelApply.load(item)
|
|
436
456
|
except Exception as e:
|
|
437
457
|
data_model_file = context.get(attr.name, [Path("UNKNOWN")])[0]
|
|
438
458
|
warnings.warn(
|
|
439
|
-
|
|
459
|
+
FileTypeUnexpectedWarning(data_model_file, frozenset([dm.DataModelApply.__name__]), str(e)),
|
|
440
460
|
stacklevel=2,
|
|
441
461
|
)
|
|
442
462
|
else:
|
|
@@ -453,7 +473,7 @@ class DMSSchema:
|
|
|
453
473
|
resources = attr.type([])
|
|
454
474
|
if not hasattr(attr.type, "_RESOURCE"):
|
|
455
475
|
warnings.warn(
|
|
456
|
-
|
|
476
|
+
FileTypeUnexpectedWarning(Path("UNKNOWN"), frozenset([attr.type.__name__]), trigger_error), stacklevel=2
|
|
457
477
|
)
|
|
458
478
|
return resources
|
|
459
479
|
# Fallback to load individual resources.
|
|
@@ -467,7 +487,9 @@ class DMSSchema:
|
|
|
467
487
|
except IndexError:
|
|
468
488
|
filepath = Path("UNKNOWN")
|
|
469
489
|
# We use repr(e) instead of str(e) to include the exception type in the warning message
|
|
470
|
-
warnings.warn(
|
|
490
|
+
warnings.warn(
|
|
491
|
+
FileTypeUnexpectedWarning(filepath, frozenset([single_cls.__name__]), repr(e)), stacklevel=2
|
|
492
|
+
)
|
|
471
493
|
else:
|
|
472
494
|
resources.append(loaded_instance)
|
|
473
495
|
return resources
|
|
@@ -529,41 +551,31 @@ class DMSSchema:
|
|
|
529
551
|
for container in self.containers.values():
|
|
530
552
|
if container.space not in defined_spaces:
|
|
531
553
|
errors.add(
|
|
532
|
-
|
|
533
|
-
container.space, "Space", container.as_id(), "Container"
|
|
534
|
-
)
|
|
554
|
+
ResourceNotFoundError[str, dm.ContainerId](container.space, "space", container.as_id(), "container")
|
|
535
555
|
)
|
|
536
556
|
|
|
537
557
|
for view in self.views.values():
|
|
538
558
|
view_id = view.as_id()
|
|
539
559
|
if view.space not in defined_spaces:
|
|
540
|
-
errors.add(
|
|
560
|
+
errors.add(ResourceNotFoundError(view.space, "space", view_id, "view"))
|
|
541
561
|
|
|
542
562
|
for parent in view.implements or []:
|
|
543
563
|
if parent not in defined_views:
|
|
544
|
-
errors.add(
|
|
545
|
-
ReferredPropertyNotFoundError[dm.ViewId, dm.ViewId](
|
|
546
|
-
parent, "View", view_id, "View", property_name="implements"
|
|
547
|
-
)
|
|
548
|
-
)
|
|
564
|
+
errors.add(PropertyNotFoundError(parent, "view", "implements", view_id, "view"))
|
|
549
565
|
|
|
550
566
|
for prop_name, prop in (view.properties or {}).items():
|
|
551
567
|
if isinstance(prop, dm.MappedPropertyApply):
|
|
552
568
|
ref_container = defined_containers.get(prop.container)
|
|
553
569
|
if ref_container is None:
|
|
554
|
-
errors.add(
|
|
555
|
-
ReferredResourceNotFoundError[dm.ContainerId, dm.ViewId](
|
|
556
|
-
prop.container, "Container", view_id, "View"
|
|
557
|
-
)
|
|
558
|
-
)
|
|
570
|
+
errors.add(ResourceNotFoundError(prop.container, "container", view_id, "view"))
|
|
559
571
|
elif prop.container_property_identifier not in ref_container.properties:
|
|
560
572
|
errors.add(
|
|
561
|
-
|
|
573
|
+
PropertyNotFoundError(
|
|
562
574
|
prop.container,
|
|
563
|
-
"
|
|
575
|
+
"container",
|
|
576
|
+
prop.container_property_identifier,
|
|
564
577
|
view_id,
|
|
565
|
-
"
|
|
566
|
-
property_name=prop.container_property_identifier,
|
|
578
|
+
"view",
|
|
567
579
|
)
|
|
568
580
|
)
|
|
569
581
|
else:
|
|
@@ -571,26 +583,19 @@ class DMSSchema:
|
|
|
571
583
|
|
|
572
584
|
if isinstance(container_property.type, dm.DirectRelation) and prop.source is None:
|
|
573
585
|
warnings.warn(
|
|
574
|
-
DirectRelationMissingSourceWarning(view_id
|
|
586
|
+
DirectRelationMissingSourceWarning(view_id, prop_name),
|
|
587
|
+
stacklevel=2,
|
|
575
588
|
)
|
|
576
589
|
|
|
577
590
|
if isinstance(prop, dm.EdgeConnectionApply) and prop.source not in defined_views:
|
|
578
|
-
errors.add(
|
|
579
|
-
ReferredPropertyNotFoundError[dm.ViewId, dm.ViewId](
|
|
580
|
-
prop.source, "View", view_id, "View", property_name=prop_name
|
|
581
|
-
)
|
|
582
|
-
)
|
|
591
|
+
errors.add(PropertyNotFoundError(prop.source, "view", prop_name, view_id, "view"))
|
|
583
592
|
|
|
584
593
|
if (
|
|
585
594
|
isinstance(prop, dm.EdgeConnectionApply)
|
|
586
595
|
and prop.edge_source is not None
|
|
587
596
|
and prop.edge_source not in defined_views
|
|
588
597
|
):
|
|
589
|
-
errors.add(
|
|
590
|
-
ReferredPropertyNotFoundError[dm.ViewId, dm.ViewId](
|
|
591
|
-
prop.edge_source, "View", view_id, "View", property_name=prop_name
|
|
592
|
-
)
|
|
593
|
-
)
|
|
598
|
+
errors.add(PropertyNotFoundError(prop.edge_source, "view", prop_name, view_id, "view"))
|
|
594
599
|
|
|
595
600
|
# This allows for multiple view properties to be mapped to the same container property,
|
|
596
601
|
# as long as they have different external_id, otherwise this will lead to raising
|
|
@@ -615,36 +620,36 @@ class DMSSchema:
|
|
|
615
620
|
== (container_id, container_property_identifier)
|
|
616
621
|
]
|
|
617
622
|
errors.add(
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
623
|
+
PropertyMappingDuplicatedError(
|
|
624
|
+
container_id,
|
|
625
|
+
"container",
|
|
626
|
+
container_property_identifier,
|
|
627
|
+
frozenset({dm.PropertyId(view_id, prop_name) for prop_name in view_properties}),
|
|
628
|
+
"view property",
|
|
622
629
|
)
|
|
623
630
|
)
|
|
624
631
|
|
|
625
632
|
if self.data_model:
|
|
626
633
|
model = self.data_model
|
|
627
634
|
if model.space not in defined_spaces:
|
|
628
|
-
errors.add(
|
|
629
|
-
ReferredResourceNotFoundError[str, dm.DataModelId](
|
|
630
|
-
model.space, "Space", model.as_id(), "Data Model"
|
|
631
|
-
)
|
|
632
|
-
)
|
|
635
|
+
errors.add(ResourceNotFoundError(model.space, "space", model.as_id(), "data model"))
|
|
633
636
|
|
|
634
637
|
view_counts: dict[dm.ViewId, int] = defaultdict(int)
|
|
635
638
|
for view_id_or_class in model.views or []:
|
|
636
639
|
view_id = view_id_or_class if isinstance(view_id_or_class, dm.ViewId) else view_id_or_class.as_id()
|
|
637
640
|
if view_id not in defined_views:
|
|
638
|
-
errors.add(
|
|
639
|
-
ReferredResourceNotFoundError[dm.ViewId, dm.DataModelId](
|
|
640
|
-
view_id, "View", model.as_id(), "DataModel"
|
|
641
|
-
)
|
|
642
|
-
)
|
|
641
|
+
errors.add(ResourceNotFoundError(view_id, "view", model.as_id(), "data model"))
|
|
643
642
|
view_counts[view_id] += 1
|
|
644
643
|
|
|
645
644
|
for view_id, count in view_counts.items():
|
|
646
645
|
if count > 1:
|
|
647
|
-
errors.add(
|
|
646
|
+
errors.add(
|
|
647
|
+
ResourceDuplicatedError(
|
|
648
|
+
view_id,
|
|
649
|
+
"view",
|
|
650
|
+
repr(model.as_id()),
|
|
651
|
+
)
|
|
652
|
+
)
|
|
648
653
|
|
|
649
654
|
return list(errors)
|
|
650
655
|
|
|
@@ -852,7 +857,9 @@ class PipelineSchema(DMSSchema):
|
|
|
852
857
|
try:
|
|
853
858
|
loaded = yaml.safe_load(yaml_file.read_text())
|
|
854
859
|
except Exception as e:
|
|
855
|
-
warnings.warn(
|
|
860
|
+
warnings.warn(
|
|
861
|
+
FileTypeUnexpectedWarning(yaml_file, frozenset([".yaml", ".yml"]), str(e)), stacklevel=2
|
|
862
|
+
)
|
|
856
863
|
continue
|
|
857
864
|
if isinstance(loaded, list):
|
|
858
865
|
data[attr_name].extend(loaded)
|
|
@@ -918,7 +925,9 @@ class PipelineSchema(DMSSchema):
|
|
|
918
925
|
try:
|
|
919
926
|
loaded = yaml.safe_load(zip_ref.read(file_info).decode())
|
|
920
927
|
except Exception as e:
|
|
921
|
-
warnings.warn(
|
|
928
|
+
warnings.warn(
|
|
929
|
+
FileTypeUnexpectedWarning(filepath, frozenset([".yaml", ".yml"]), str(e)), stacklevel=2
|
|
930
|
+
)
|
|
922
931
|
continue
|
|
923
932
|
if isinstance(loaded, list):
|
|
924
933
|
data[attr_name].extend(loaded)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Any, ClassVar, cast
|
|
2
2
|
|
|
3
3
|
from cognite.neat.rules.models import DMSRules
|
|
4
|
-
from cognite.neat.rules.models.dms import DMSContainer, DMSProperty, DMSView
|
|
4
|
+
from cognite.neat.rules.models.dms import DMSContainer, DMSEnum, DMSNode, DMSProperty, DMSView
|
|
5
5
|
from cognite.neat.rules.models.entities import ReferenceEntity, ViewEntity
|
|
6
6
|
|
|
7
7
|
|
|
@@ -23,14 +23,19 @@ class _DMSRulesSerializer:
|
|
|
23
23
|
self.view_name = "views"
|
|
24
24
|
self.container_name = "containers"
|
|
25
25
|
self.metadata_name = "metadata"
|
|
26
|
+
self.enum_name = "enum"
|
|
27
|
+
self.nodes_name = "nodes"
|
|
26
28
|
self.prop_view = "view"
|
|
27
29
|
self.prop_container = "container"
|
|
28
30
|
self.prop_view_property = "view_property"
|
|
29
31
|
self.prop_value_type = "value_type"
|
|
32
|
+
self.prop_connection = "connection"
|
|
30
33
|
self.view_view = "view"
|
|
31
34
|
self.view_implements = "implements"
|
|
32
35
|
self.container_container = "container"
|
|
33
36
|
self.container_constraint = "constraint"
|
|
37
|
+
self.nodes_node = "node"
|
|
38
|
+
self.enum_collection = "collection"
|
|
34
39
|
self.reference = "Reference" if by_alias else "reference"
|
|
35
40
|
|
|
36
41
|
if by_alias:
|
|
@@ -45,6 +50,7 @@ class _DMSRulesSerializer:
|
|
|
45
50
|
self.prop_container = DMSProperty.model_fields[self.prop_container].alias or self.prop_container
|
|
46
51
|
self.prop_view_property = DMSProperty.model_fields[self.prop_view_property].alias or self.prop_view_property
|
|
47
52
|
self.prop_value_type = DMSProperty.model_fields[self.prop_value_type].alias or self.prop_value_type
|
|
53
|
+
self.prop_connection = DMSProperty.model_fields[self.prop_connection].alias or self.prop_connection
|
|
48
54
|
self.view_view = DMSView.model_fields[self.view_view].alias or self.view_view
|
|
49
55
|
self.view_implements = DMSView.model_fields[self.view_implements].alias or self.view_implements
|
|
50
56
|
self.container_container = (
|
|
@@ -53,10 +59,15 @@ class _DMSRulesSerializer:
|
|
|
53
59
|
self.container_constraint = (
|
|
54
60
|
DMSContainer.model_fields[self.container_constraint].alias or self.container_constraint
|
|
55
61
|
)
|
|
62
|
+
self.nodes_node = DMSNode.model_fields[self.nodes_node].alias or self.nodes_node
|
|
63
|
+
|
|
56
64
|
self.prop_name = DMSRules.model_fields[self.prop_name].alias or self.prop_name
|
|
57
65
|
self.view_name = DMSRules.model_fields[self.view_name].alias or self.view_name
|
|
58
66
|
self.container_name = DMSRules.model_fields[self.container_name].alias or self.container_name
|
|
59
67
|
self.metadata_name = DMSRules.model_fields[self.metadata_name].alias or self.metadata_name
|
|
68
|
+
self.nodes_name = DMSRules.model_fields[self.nodes_name].alias or self.nodes_name
|
|
69
|
+
self.enum_name = DMSRules.model_fields[self.enum_name].alias or self.enum_name
|
|
70
|
+
self.enum_collection = DMSEnum.model_fields[self.enum_collection].alias or self.enum_collection
|
|
60
71
|
|
|
61
72
|
def clean(self, dumped: dict[str, Any], as_reference: bool) -> dict[str, Any]:
|
|
62
73
|
# Sorting to get a deterministic order
|
|
@@ -69,6 +80,16 @@ class _DMSRulesSerializer:
|
|
|
69
80
|
else:
|
|
70
81
|
dumped.pop(self.container_name, None)
|
|
71
82
|
|
|
83
|
+
if enum_data := dumped.get(self.enum_name):
|
|
84
|
+
dumped[self.enum_name] = sorted(enum_data["data"], key=lambda e: e[self.enum_collection])
|
|
85
|
+
else:
|
|
86
|
+
dumped.pop(self.enum_name, None)
|
|
87
|
+
|
|
88
|
+
if node_types_data := dumped.get(self.nodes_name):
|
|
89
|
+
dumped[self.nodes_name] = sorted(node_types_data["data"], key=lambda n: n[self.nodes_node])
|
|
90
|
+
else:
|
|
91
|
+
dumped.pop(self.nodes_name, None)
|
|
92
|
+
|
|
72
93
|
for prop in dumped[self.prop_name]:
|
|
73
94
|
if as_reference:
|
|
74
95
|
view_entity = cast(ViewEntity, ViewEntity.load(prop[self.prop_view]))
|
|
@@ -87,8 +108,25 @@ class _DMSRulesSerializer:
|
|
|
87
108
|
continue
|
|
88
109
|
if value := prop.get(field_name):
|
|
89
110
|
prop[field_name] = value.removeprefix(self.default_space).removesuffix(self.default_version_wrapped)
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
if isinstance(prop.get(self.prop_connection), str):
|
|
112
|
+
# Remove default values from connection (type, direction, properties)
|
|
113
|
+
default_type = f"type={self.default_space}{prop[self.view_view]}.{prop[self.prop_view_property]}"
|
|
114
|
+
default_type_space = f"type={self.default_space}"
|
|
115
|
+
default_properties = f"properties={self.default_space}"
|
|
116
|
+
default_direction = "direction=outwards"
|
|
117
|
+
prop[self.prop_connection] = (
|
|
118
|
+
prop[self.prop_connection]
|
|
119
|
+
.replace(self.default_version, "")
|
|
120
|
+
.replace(default_type, "")
|
|
121
|
+
.replace(default_type_space, "type=")
|
|
122
|
+
.replace(default_properties, "properties=")
|
|
123
|
+
.replace(default_direction, "")
|
|
124
|
+
.replace("()", "")
|
|
125
|
+
.replace("(,)", "")
|
|
126
|
+
.replace("(,,)", "")
|
|
127
|
+
.replace("(,", "(")
|
|
128
|
+
.replace(",)", ")")
|
|
129
|
+
)
|
|
92
130
|
|
|
93
131
|
for view in dumped[self.view_name]:
|
|
94
132
|
if as_reference:
|
|
@@ -113,4 +151,7 @@ class _DMSRulesSerializer:
|
|
|
113
151
|
container[self.container_constraint] = ",".join(
|
|
114
152
|
constraint.strip().removeprefix(self.default_space) for constraint in value.split(",")
|
|
115
153
|
)
|
|
154
|
+
|
|
155
|
+
for node in dumped.get(self.nodes_name, []):
|
|
156
|
+
node[self.nodes_node] = node[self.nodes_node].removeprefix(self.default_space)
|
|
116
157
|
return dumped
|