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.
- cognite/neat/_version.py +1 -1
- cognite/neat/app/api/routers/core.py +1 -1
- cognite/neat/app/api/routers/rules.py +1 -1
- cognite/neat/graph/extractors/_mock_graph_generator.py +2 -2
- cognite/neat/rules/_shared.py +1 -1
- cognite/neat/rules/analysis/_information_rules.py +3 -3
- cognite/neat/rules/exporters/_base.py +1 -1
- cognite/neat/rules/exporters/_rules2dms.py +8 -49
- cognite/neat/rules/exporters/_rules2excel.py +71 -40
- cognite/neat/rules/exporters/_rules2ontology.py +2 -2
- cognite/neat/rules/exporters/_rules2yaml.py +1 -1
- cognite/neat/rules/exporters/_validation.py +2 -2
- cognite/neat/rules/importers/_base.py +1 -1
- cognite/neat/rules/importers/_dms2rules.py +93 -108
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +1 -1
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +2 -3
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +1 -1
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +2 -2
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +1 -1
- cognite/neat/rules/importers/_owl2rules/_owl2rules.py +1 -1
- cognite/neat/rules/importers/_spreadsheet2rules.py +87 -62
- cognite/neat/rules/importers/_yaml2rules.py +3 -3
- cognite/neat/rules/issues/base.py +5 -0
- cognite/neat/rules/issues/dms.py +65 -0
- cognite/neat/rules/models/__init__.py +27 -0
- cognite/neat/rules/models/dms/__init__.py +18 -0
- cognite/neat/rules/models/dms/_converter.py +140 -0
- cognite/neat/rules/models/dms/_exporter.py +405 -0
- cognite/neat/rules/models/dms/_rules.py +379 -0
- cognite/neat/rules/models/{rules/_dms_rules_write.py → dms/_rules_input.py} +42 -33
- cognite/neat/rules/models/{rules/_dms_schema.py → dms/_schema.py} +36 -4
- cognite/neat/rules/models/dms/_serializer.py +126 -0
- cognite/neat/rules/models/dms/_validation.py +288 -0
- cognite/neat/rules/models/{rules/_domain_rules.py → domain.py} +1 -0
- cognite/neat/rules/models/information/__init__.py +3 -0
- cognite/neat/rules/models/information/_converter.py +195 -0
- cognite/neat/rules/models/{rules/_information_rules.py → information/_rules.py} +35 -202
- cognite/neat/workflows/steps/data_contracts.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_exporter.py +10 -3
- cognite/neat/workflows/steps/lib/current/rules_importer.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_validator.py +1 -2
- {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/METADATA +1 -1
- {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/RECORD +51 -44
- cognite/neat/rules/models/rules/__init__.py +0 -14
- cognite/neat/rules/models/rules/_dms_architect_rules.py +0 -1255
- /cognite/neat/rules/models/{rules/_base.py → _base.py} +0 -0
- /cognite/neat/rules/models/{rdfpath.py → _rdfpath.py} +0 -0
- /cognite/neat/rules/models/{rules/_types → _types}/__init__.py +0 -0
- /cognite/neat/rules/models/{rules/_types → _types}/_base.py +0 -0
- /cognite/neat/rules/models/{rules/_types → _types}/_field.py +0 -0
- {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/LICENSE +0 -0
- {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/WHEEL +0 -0
- {cognite_neat-0.76.1.dist-info → cognite_neat-0.76.3.dist-info}/entry_points.txt +0 -0
cognite/neat/rules/issues/dms.py
CHANGED
|
@@ -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
|
+
)
|