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.

Files changed (129) hide show
  1. cognite/neat/_version.py +1 -1
  2. cognite/neat/constants.py +3 -0
  3. cognite/neat/graph/__init__.py +0 -3
  4. cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
  5. cognite/neat/graph/loaders/_base.py +3 -3
  6. cognite/neat/graph/loaders/_rdf2asset.py +24 -25
  7. cognite/neat/graph/loaders/_rdf2dms.py +20 -15
  8. cognite/neat/issues/__init__.py +1 -3
  9. cognite/neat/issues/_base.py +261 -71
  10. cognite/neat/issues/errors/__init__.py +73 -0
  11. cognite/neat/issues/errors/_external.py +67 -0
  12. cognite/neat/issues/errors/_general.py +35 -0
  13. cognite/neat/issues/errors/_properties.py +62 -0
  14. cognite/neat/issues/errors/_resources.py +111 -0
  15. cognite/neat/issues/errors/_workflow.py +36 -0
  16. cognite/neat/issues/formatters.py +1 -1
  17. cognite/neat/issues/warnings/__init__.py +66 -0
  18. cognite/neat/issues/warnings/_external.py +40 -0
  19. cognite/neat/issues/warnings/_general.py +29 -0
  20. cognite/neat/issues/warnings/_models.py +92 -0
  21. cognite/neat/issues/warnings/_properties.py +44 -0
  22. cognite/neat/issues/warnings/_resources.py +55 -0
  23. cognite/neat/issues/warnings/user_modeling.py +113 -0
  24. cognite/neat/rules/_shared.py +53 -2
  25. cognite/neat/rules/analysis/_base.py +1 -1
  26. cognite/neat/rules/exporters/_base.py +7 -18
  27. cognite/neat/rules/exporters/_rules2dms.py +17 -20
  28. cognite/neat/rules/exporters/_rules2excel.py +9 -16
  29. cognite/neat/rules/exporters/_rules2ontology.py +77 -64
  30. cognite/neat/rules/exporters/_rules2yaml.py +6 -9
  31. cognite/neat/rules/exporters/_validation.py +11 -96
  32. cognite/neat/rules/importers/_base.py +9 -58
  33. cognite/neat/rules/importers/_dms2rules.py +188 -135
  34. cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +48 -35
  35. cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +36 -45
  36. cognite/neat/rules/importers/_dtdl2rules/spec.py +7 -0
  37. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +8 -4
  38. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +3 -3
  39. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +18 -11
  40. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +12 -19
  41. cognite/neat/rules/importers/_rdf/_inference2rules.py +14 -37
  42. cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +1 -0
  43. cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -0
  44. cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
  45. cognite/neat/rules/importers/_rdf/_shared.py +4 -4
  46. cognite/neat/rules/importers/_spreadsheet2rules.py +46 -97
  47. cognite/neat/rules/importers/_yaml2rules.py +32 -58
  48. cognite/neat/rules/models/__init__.py +21 -5
  49. cognite/neat/rules/models/_base_input.py +162 -0
  50. cognite/neat/rules/models/{_base.py → _base_rules.py} +1 -12
  51. cognite/neat/rules/models/_rdfpath.py +4 -4
  52. cognite/neat/rules/models/{_types/_field.py → _types.py} +5 -10
  53. cognite/neat/rules/models/asset/__init__.py +5 -2
  54. cognite/neat/rules/models/asset/_rules.py +3 -23
  55. cognite/neat/rules/models/asset/_rules_input.py +40 -115
  56. cognite/neat/rules/models/asset/_validation.py +14 -10
  57. cognite/neat/rules/models/data_types.py +150 -44
  58. cognite/neat/rules/models/dms/__init__.py +19 -7
  59. cognite/neat/rules/models/dms/_exporter.py +102 -34
  60. cognite/neat/rules/models/dms/_rules.py +65 -162
  61. cognite/neat/rules/models/dms/_rules_input.py +186 -254
  62. cognite/neat/rules/models/dms/_schema.py +87 -78
  63. cognite/neat/rules/models/dms/_serializer.py +44 -3
  64. cognite/neat/rules/models/dms/_validation.py +106 -68
  65. cognite/neat/rules/models/domain.py +52 -1
  66. cognite/neat/rules/models/entities/__init__.py +63 -0
  67. cognite/neat/rules/models/entities/_constants.py +73 -0
  68. cognite/neat/rules/models/entities/_loaders.py +76 -0
  69. cognite/neat/rules/models/entities/_multi_value.py +67 -0
  70. cognite/neat/rules/models/{entities.py → entities/_single_value.py} +74 -232
  71. cognite/neat/rules/models/entities/_types.py +86 -0
  72. cognite/neat/rules/models/{wrapped_entities.py → entities/_wrapped.py} +1 -1
  73. cognite/neat/rules/models/information/__init__.py +10 -2
  74. cognite/neat/rules/models/information/_rules.py +10 -22
  75. cognite/neat/rules/models/information/_rules_input.py +57 -204
  76. cognite/neat/rules/models/information/_validation.py +48 -25
  77. cognite/neat/rules/transformers/__init__.py +21 -0
  78. cognite/neat/rules/transformers/_base.py +81 -0
  79. cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +217 -21
  80. cognite/neat/rules/transformers/_map_onto.py +97 -0
  81. cognite/neat/rules/transformers/_pipelines.py +61 -0
  82. cognite/neat/rules/transformers/_verification.py +136 -0
  83. cognite/neat/{graph/stores → store}/_provenance.py +10 -1
  84. cognite/neat/utils/auxiliary.py +2 -35
  85. cognite/neat/utils/cdf/data_classes.py +20 -0
  86. cognite/neat/utils/regex_patterns.py +6 -0
  87. cognite/neat/utils/text.py +17 -0
  88. cognite/neat/workflows/base.py +4 -4
  89. cognite/neat/workflows/cdf_store.py +3 -3
  90. cognite/neat/workflows/steps/data_contracts.py +1 -1
  91. cognite/neat/workflows/steps/lib/current/graph_extractor.py +3 -3
  92. cognite/neat/workflows/steps/lib/current/graph_loader.py +2 -2
  93. cognite/neat/workflows/steps/lib/current/graph_store.py +1 -1
  94. cognite/neat/workflows/steps/lib/current/rules_exporter.py +116 -47
  95. cognite/neat/workflows/steps/lib/current/rules_importer.py +30 -28
  96. cognite/neat/workflows/steps/lib/current/rules_validator.py +5 -6
  97. cognite/neat/workflows/steps/lib/io/io_steps.py +5 -5
  98. cognite/neat/workflows/steps_registry.py +4 -5
  99. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
  100. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +105 -106
  101. cognite/neat/exceptions.py +0 -145
  102. cognite/neat/graph/exceptions.py +0 -90
  103. cognite/neat/issues/errors/external.py +0 -21
  104. cognite/neat/issues/errors/properties.py +0 -75
  105. cognite/neat/issues/errors/resources.py +0 -123
  106. cognite/neat/issues/errors/schema.py +0 -0
  107. cognite/neat/issues/neat_warnings/__init__.py +0 -2
  108. cognite/neat/issues/neat_warnings/identifier.py +0 -27
  109. cognite/neat/issues/neat_warnings/models.py +0 -22
  110. cognite/neat/issues/neat_warnings/properties.py +0 -77
  111. cognite/neat/issues/neat_warnings/resources.py +0 -125
  112. cognite/neat/rules/issues/__init__.py +0 -22
  113. cognite/neat/rules/issues/base.py +0 -63
  114. cognite/neat/rules/issues/dms.py +0 -549
  115. cognite/neat/rules/issues/fileread.py +0 -197
  116. cognite/neat/rules/issues/ontology.py +0 -298
  117. cognite/neat/rules/issues/spreadsheet.py +0 -563
  118. cognite/neat/rules/issues/spreadsheet_file.py +0 -151
  119. cognite/neat/rules/issues/tables.py +0 -72
  120. cognite/neat/rules/models/_constants.py +0 -1
  121. cognite/neat/rules/models/_types/__init__.py +0 -19
  122. cognite/neat/rules/models/asset/_converter.py +0 -4
  123. cognite/neat/rules/models/dms/_converter.py +0 -145
  124. cognite/neat/workflows/_exceptions.py +0 -41
  125. /cognite/neat/{graph/stores → store}/__init__.py +0 -0
  126. /cognite/neat/{graph/stores → store}/_base.py +0 -0
  127. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
  128. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
  129. {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.properties import ReferredPropertyNotFoundError
30
- from cognite.neat.issues.errors.resources import ReferredResourceNotFoundError
31
- from cognite.neat.issues.neat_warnings.resources import FailedLoadingResourcesWarning, MultipleResourcesWarning
32
- from cognite.neat.rules import issues
33
- from cognite.neat.rules.issues.dms import (
34
- ContainerPropertyUsedMultipleTimesError,
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 view_id in view_inheritance:
97
- if implemented_view := view_by_id.get(view_id):
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 IncompleteSchemaError(missing_component=view_id).as_exception()
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
- warnings.warn(MissingViewInModelWarning(data_model.as_id(), connection_referenced_view_ids), stacklevel=2)
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(FailedLoadingResourcesWarning[dm.ViewId](frozenset(failed), "View"), stacklevel=2)
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(issues.fileread.InvalidFileFormatWarning(yaml_file, str(e)), stacklevel=2)
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(issues.fileread.InvalidFileFormatWarning(filename, str(e)), stacklevel=2)
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 issues.fileread.FailedStringLoadError(".yaml", str(e)).as_exception() from None
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 issues.fileread.FailedStringLoadError(
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
- warnings.warn(
427
- MultipleResourcesWarning[str](
428
- frozenset([item.get("externalId", "Unknown") for item in items]),
429
- "DataModel",
430
- ),
431
- stacklevel=2,
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
- issues.fileread.FailedLoadWarning(data_model_file, dm.DataModelApply.__name__, str(e)),
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
- issues.fileread.FailedLoadWarning(Path("UNKNOWN"), attr.type.__name__, trigger_error), stacklevel=2
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(issues.fileread.FailedLoadWarning(filepath, single_cls.__name__, repr(e)), stacklevel=2)
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
- ReferredResourceNotFoundError[str, dm.ContainerId](
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(ReferredResourceNotFoundError[str, dm.ViewId](view.space, "Space", view_id, "View"))
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
- ReferredPropertyNotFoundError[dm.ContainerId, dm.ViewId](
573
+ PropertyNotFoundError(
562
574
  prop.container,
563
- "Container",
575
+ "container",
576
+ prop.container_property_identifier,
564
577
  view_id,
565
- "View",
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=view_id, property=prop_name), stacklevel=2
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
- ContainerPropertyUsedMultipleTimesError(
619
- container=container_id,
620
- property=container_property_identifier,
621
- referred_by=frozenset({(view_id, prop_name) for prop_name in view_properties}),
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(DuplicatedViewInDataModelError(referred_by=model.as_id(), view=view_id))
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(issues.fileread.InvalidFileFormatWarning(yaml_file, str(e)), stacklevel=2)
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(issues.fileread.InvalidFileFormatWarning(filepath, str(e)), stacklevel=2)
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
- # Value type can have a property as well
91
- prop[self.prop_value_type] = prop[self.prop_value_type].replace(self.default_version, "")
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