cognite-neat 0.76.1__py3-none-any.whl → 0.76.3__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 (53) hide show
  1. cognite/neat/_version.py +1 -1
  2. cognite/neat/app/api/routers/core.py +1 -1
  3. cognite/neat/app/api/routers/rules.py +1 -1
  4. cognite/neat/graph/extractors/_mock_graph_generator.py +2 -2
  5. cognite/neat/rules/_shared.py +1 -1
  6. cognite/neat/rules/analysis/_information_rules.py +3 -3
  7. cognite/neat/rules/exporters/_base.py +1 -1
  8. cognite/neat/rules/exporters/_rules2dms.py +8 -49
  9. cognite/neat/rules/exporters/_rules2excel.py +71 -40
  10. cognite/neat/rules/exporters/_rules2ontology.py +2 -2
  11. cognite/neat/rules/exporters/_rules2yaml.py +1 -1
  12. cognite/neat/rules/exporters/_validation.py +2 -2
  13. cognite/neat/rules/importers/_base.py +1 -1
  14. cognite/neat/rules/importers/_dms2rules.py +93 -108
  15. cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +1 -1
  16. cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +2 -3
  17. cognite/neat/rules/importers/_owl2rules/_owl2classes.py +1 -1
  18. cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +2 -2
  19. cognite/neat/rules/importers/_owl2rules/_owl2properties.py +1 -1
  20. cognite/neat/rules/importers/_owl2rules/_owl2rules.py +1 -1
  21. cognite/neat/rules/importers/_spreadsheet2rules.py +87 -62
  22. cognite/neat/rules/importers/_yaml2rules.py +3 -3
  23. cognite/neat/rules/issues/base.py +5 -0
  24. cognite/neat/rules/issues/dms.py +65 -0
  25. cognite/neat/rules/models/__init__.py +27 -0
  26. cognite/neat/rules/models/dms/__init__.py +18 -0
  27. cognite/neat/rules/models/dms/_converter.py +140 -0
  28. cognite/neat/rules/models/dms/_exporter.py +405 -0
  29. cognite/neat/rules/models/dms/_rules.py +379 -0
  30. cognite/neat/rules/models/{rules/_dms_rules_write.py → dms/_rules_input.py} +42 -33
  31. cognite/neat/rules/models/{rules/_dms_schema.py → dms/_schema.py} +36 -4
  32. cognite/neat/rules/models/dms/_serializer.py +126 -0
  33. cognite/neat/rules/models/dms/_validation.py +288 -0
  34. cognite/neat/rules/models/{rules/_domain_rules.py → domain.py} +1 -0
  35. cognite/neat/rules/models/information/__init__.py +3 -0
  36. cognite/neat/rules/models/information/_converter.py +195 -0
  37. cognite/neat/rules/models/{rules/_information_rules.py → information/_rules.py} +35 -202
  38. cognite/neat/workflows/steps/data_contracts.py +1 -1
  39. cognite/neat/workflows/steps/lib/current/rules_exporter.py +10 -3
  40. cognite/neat/workflows/steps/lib/current/rules_importer.py +1 -1
  41. cognite/neat/workflows/steps/lib/current/rules_validator.py +1 -2
  42. {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/METADATA +1 -1
  43. {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/RECORD +51 -44
  44. cognite/neat/rules/models/rules/__init__.py +0 -14
  45. cognite/neat/rules/models/rules/_dms_architect_rules.py +0 -1255
  46. /cognite/neat/rules/models/{rules/_base.py → _base.py} +0 -0
  47. /cognite/neat/rules/models/{rdfpath.py → _rdfpath.py} +0 -0
  48. /cognite/neat/rules/models/{rules/_types → _types}/__init__.py +0 -0
  49. /cognite/neat/rules/models/{rules/_types → _types}/_base.py +0 -0
  50. /cognite/neat/rules/models/{rules/_types → _types}/_field.py +0 -0
  51. {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/LICENSE +0 -0
  52. {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/WHEEL +0 -0
  53. {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/entry_points.txt +0 -0
@@ -9,6 +9,7 @@ from .base import NeatValidationError, ValidationWarning
9
9
  __all__ = [
10
10
  "DMSSchemaError",
11
11
  "DMSSchemaWarning",
12
+ "IncompleteSchemaError",
12
13
  "MissingSpaceError",
13
14
  "MissingContainerError",
14
15
  "MissingContainerPropertyError",
@@ -19,12 +20,14 @@ __all__ = [
19
20
  "DirectRelationMissingSourceWarning",
20
21
  "ViewModelVersionNotMatchingWarning",
21
22
  "ViewModelSpaceNotMatchingWarning",
23
+ "ViewMapsToTooManyContainersWarning",
22
24
  "DuplicatedViewInDataModelError",
23
25
  "ContainerPropertyUsedMultipleTimesError",
24
26
  "EmptyContainerWarning",
25
27
  "UnsupportedConnectionWarning",
26
28
  "MultipleReferenceWarning",
27
29
  "HasDataFilterOnNoPropertiesViewWarning",
30
+ "HasDataFilterAppliedToTooManyContainersWarning",
28
31
  "ReverseRelationMissingOtherSideWarning",
29
32
  "NodeTypeFilterOnParentViewWarning",
30
33
  "ChangingContainerError",
@@ -40,6 +43,24 @@ class DMSSchemaError(NeatValidationError, ABC): ...
40
43
  class DMSSchemaWarning(ValidationWarning, ABC): ...
41
44
 
42
45
 
46
+ @dataclass(frozen=True)
47
+ class IncompleteSchemaError(DMSSchemaError):
48
+ description = "This error is raised when the schema is claimed to be complete but missing some components"
49
+ fix = "Either provide the missing components or change the schema to partial"
50
+ missing_component: dm.ContainerId | dm.ViewId
51
+
52
+ def message(self) -> str:
53
+ return (
54
+ "The data model schema is set to be complete, however, "
55
+ f"the referred component {self.missing_component} is not preset."
56
+ )
57
+
58
+ def dump(self) -> dict[str, Any]:
59
+ output = super().dump()
60
+ output["missing_component"] = self.missing_component
61
+ return output
62
+
63
+
43
64
  @dataclass(frozen=True)
44
65
  class MissingSpaceError(DMSSchemaError):
45
66
  description = "The spaced referred to by the Container/View/Node/Edge/DataModel does not exist"
@@ -250,6 +271,28 @@ class ViewModelSpaceNotMatchingWarning(DMSSchemaWarning):
250
271
  return output
251
272
 
252
273
 
274
+ @dataclass(frozen=True)
275
+ class ViewMapsToTooManyContainersWarning(DMSSchemaWarning):
276
+ description = "The view maps to more than 10 containers which impacts read/write performance of data model"
277
+ fix = "Try to have as few containers as possible to which the view maps to"
278
+ error_name: ClassVar[str] = "ViewMapsToTooManyContainers"
279
+ view_id: dm.ViewId
280
+ container_ids: set[dm.ContainerId]
281
+
282
+ def message(self) -> str:
283
+ return (
284
+ f"The view {self.view_id} maps to total of {len(self.container_ids)},."
285
+ "Mapping to more than 10 containers is not recommended and can lead to poor performances."
286
+ "Re-iterate the data model design to reduce the number of containers to which the view maps to."
287
+ )
288
+
289
+ def dump(self) -> dict[str, Any]:
290
+ output = super().dump()
291
+ output["view_id"] = self.view_id.dump()
292
+ output["container_ids"] = [container_id.dump() for container_id in self.container_ids]
293
+ return output
294
+
295
+
253
296
  @dataclass(frozen=True)
254
297
  class ContainerPropertyUsedMultipleTimesError(DMSSchemaError):
255
298
  description = "The container property is used multiple times by the same view property"
@@ -442,6 +485,28 @@ class HasDataFilterOnNoPropertiesViewWarning(DMSSchemaWarning):
442
485
  return output
443
486
 
444
487
 
488
+ @dataclass(frozen=True)
489
+ class HasDataFilterAppliedToTooManyContainersWarning(DMSSchemaWarning):
490
+ description = "The view filter hasData applied to more than 10 containers this will cause DMS API Error"
491
+ fix = "Do not map to more than 10 containers, alternatively override the filter by using rawFilter"
492
+ error_name: ClassVar[str] = "HasDataFilterAppliedToTooManyContainers"
493
+ view_id: dm.ViewId
494
+ container_ids: set[dm.ContainerId]
495
+
496
+ def message(self) -> str:
497
+ return (
498
+ f"The view {self.view_id} HasData filter applied to total of {len(self.container_ids)},."
499
+ "Applying HasData filter to more than 10 containers is not recommended and can lead to DMS API error."
500
+ "Re-iterate the data model design to reduce the number of containers to which the view maps to."
501
+ )
502
+
503
+ def dump(self) -> dict[str, Any]:
504
+ output = super().dump()
505
+ output["view_id"] = self.view_id.dump()
506
+ output["container_ids"] = [container_id.dump() for container_id in self.container_ids]
507
+ return output
508
+
509
+
445
510
  @dataclass(frozen=True)
446
511
  class NodeTypeFilterOnParentViewWarning(DMSSchemaWarning):
447
512
  description = (
@@ -0,0 +1,27 @@
1
+ from cognite.neat.rules.models.domain import DomainRules
2
+ from cognite.neat.rules.models.information._rules import InformationRules
3
+
4
+ from ._base import DataModelType, ExtensionCategory, RoleTypes, SchemaCompleteness, SheetEntity, SheetList
5
+ from .dms._rules import DMSRules
6
+ from .dms._schema import DMSSchema
7
+
8
+ RULES_PER_ROLE: dict[RoleTypes, type[DomainRules] | type[InformationRules] | type[DMSRules]] = {
9
+ RoleTypes.domain_expert: DomainRules,
10
+ RoleTypes.information_architect: InformationRules,
11
+ RoleTypes.dms_architect: DMSRules,
12
+ }
13
+
14
+
15
+ __all__ = [
16
+ "DomainRules",
17
+ "InformationRules",
18
+ "DMSRules",
19
+ "RULES_PER_ROLE",
20
+ "DMSSchema",
21
+ "RoleTypes",
22
+ "SchemaCompleteness",
23
+ "ExtensionCategory",
24
+ "DataModelType",
25
+ "SheetList",
26
+ "SheetEntity",
27
+ ]
@@ -0,0 +1,18 @@
1
+ from ._rules import DMSContainer, DMSMetadata, DMSProperty, DMSRules, DMSView
2
+ from ._rules_input import DMSContainerInput, DMSMetadataInput, DMSPropertyInput, DMSRulesInput, DMSViewInput
3
+ from ._schema import DMSSchema, PipelineSchema
4
+
5
+ __all__ = [
6
+ "DMSRules",
7
+ "DMSSchema",
8
+ "DMSMetadata",
9
+ "DMSView",
10
+ "DMSProperty",
11
+ "DMSContainer",
12
+ "PipelineSchema",
13
+ "DMSRulesInput",
14
+ "DMSMetadataInput",
15
+ "DMSViewInput",
16
+ "DMSPropertyInput",
17
+ "DMSContainerInput",
18
+ ]
@@ -0,0 +1,140 @@
1
+ import warnings
2
+ from datetime import datetime
3
+ from typing import cast
4
+
5
+ from rdflib import Namespace
6
+
7
+ from cognite.neat.rules import issues
8
+ from cognite.neat.rules.models._base import SheetList
9
+ from cognite.neat.rules.models.data_types import DataType
10
+ from cognite.neat.rules.models.domain import DomainRules
11
+ from cognite.neat.rules.models.entities import (
12
+ ClassEntity,
13
+ ContainerEntity,
14
+ DMSUnknownEntity,
15
+ ParentClassEntity,
16
+ ReferenceEntity,
17
+ UnknownEntity,
18
+ ViewEntity,
19
+ ViewPropertyEntity,
20
+ )
21
+ from cognite.neat.rules.models.information._rules import InformationRules
22
+
23
+ from ._rules import DMSProperty, DMSRules, DMSView
24
+
25
+
26
+ class _DMSRulesConverter:
27
+ def __init__(self, dms: DMSRules):
28
+ self.dms = dms
29
+
30
+ def as_domain_rules(self) -> "DomainRules":
31
+ raise NotImplementedError("DomainRules not implemented yet")
32
+
33
+ def as_information_architect_rules(
34
+ self,
35
+ created: datetime | None = None,
36
+ updated: datetime | None = None,
37
+ name: str | None = None,
38
+ namespace: Namespace | None = None,
39
+ ) -> "InformationRules":
40
+ from cognite.neat.rules.models.information._rules import (
41
+ InformationClass,
42
+ InformationMetadata,
43
+ InformationProperty,
44
+ InformationRules,
45
+ )
46
+
47
+ dms = self.dms.metadata
48
+ prefix = dms.space
49
+
50
+ metadata = InformationMetadata(
51
+ schema_=dms.schema_,
52
+ prefix=prefix,
53
+ namespace=namespace or Namespace(f"https://purl.orgl/neat/{prefix}/"),
54
+ version=dms.version,
55
+ name=name or dms.name or "Missing name",
56
+ creator=dms.creator,
57
+ created=dms.created or created or datetime.now(),
58
+ updated=dms.updated or updated or datetime.now(),
59
+ )
60
+
61
+ classes = [
62
+ InformationClass(
63
+ # we do not want a version in class as we use URI for the class
64
+ class_=ClassEntity(prefix=view.class_.prefix, suffix=view.class_.suffix),
65
+ description=view.description,
66
+ parent=[
67
+ # we do not want a version in class as we use URI for the class
68
+ ParentClassEntity(prefix=implemented_view.prefix, suffix=implemented_view.suffix)
69
+ # We only want parents in the same namespace, parent in a different namespace is a reference
70
+ for implemented_view in view.implements or []
71
+ if implemented_view.prefix == view.class_.prefix
72
+ ],
73
+ reference=self._get_class_reference(view),
74
+ )
75
+ for view in self.dms.views
76
+ ]
77
+
78
+ properties: list[InformationProperty] = []
79
+ value_type: DataType | ClassEntity | str
80
+ for property_ in self.dms.properties:
81
+ if isinstance(property_.value_type, DataType):
82
+ value_type = property_.value_type
83
+ elif isinstance(property_.value_type, ViewEntity | ViewPropertyEntity):
84
+ value_type = ClassEntity(
85
+ prefix=property_.value_type.prefix,
86
+ suffix=property_.value_type.suffix,
87
+ )
88
+ elif isinstance(property_.value_type, DMSUnknownEntity):
89
+ value_type = UnknownEntity()
90
+ else:
91
+ raise ValueError(f"Unsupported value type: {property_.value_type.type_}")
92
+
93
+ properties.append(
94
+ InformationProperty(
95
+ # Removing version
96
+ class_=ClassEntity(suffix=property_.class_.suffix, prefix=property_.class_.prefix),
97
+ property_=property_.view_property,
98
+ value_type=value_type,
99
+ description=property_.description,
100
+ min_count=0 if property_.nullable or property_.nullable is None else 1,
101
+ max_count=float("inf") if property_.is_list or property_.nullable is None else 1,
102
+ reference=self._get_property_reference(property_),
103
+ )
104
+ )
105
+
106
+ return InformationRules(
107
+ metadata=metadata,
108
+ properties=SheetList[InformationProperty](data=properties),
109
+ classes=SheetList[InformationClass](data=classes),
110
+ last=self.dms.last.as_information_architect_rules() if self.dms.last else None,
111
+ reference=self.dms.reference.as_information_architect_rules() if self.dms.reference else None,
112
+ )
113
+
114
+ @classmethod
115
+ def _get_class_reference(cls, view: DMSView) -> ReferenceEntity | None:
116
+ parents_other_namespace = [parent for parent in view.implements or [] if parent.prefix != view.class_.prefix]
117
+ if len(parents_other_namespace) == 0:
118
+ return None
119
+ if len(parents_other_namespace) > 1:
120
+ warnings.warn(
121
+ issues.dms.MultipleReferenceWarning(
122
+ view_id=view.view.as_id(), implements=[v.as_id() for v in parents_other_namespace]
123
+ ),
124
+ stacklevel=2,
125
+ )
126
+ other_parent = parents_other_namespace[0]
127
+
128
+ return ReferenceEntity(prefix=other_parent.prefix, suffix=other_parent.suffix)
129
+
130
+ @classmethod
131
+ def _get_property_reference(cls, property_: DMSProperty) -> ReferenceEntity | None:
132
+ has_container_other_namespace = property_.container and property_.container.prefix != property_.class_.prefix
133
+ if not has_container_other_namespace:
134
+ return None
135
+ container = cast(ContainerEntity, property_.container)
136
+ return ReferenceEntity(
137
+ prefix=container.prefix,
138
+ suffix=container.suffix,
139
+ property=property_.container_property,
140
+ )