cognite-neat 0.88.1__py3-none-any.whl → 0.88.2__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/exceptions.py +2 -2
- cognite/neat/graph/loaders/_base.py +4 -4
- cognite/neat/graph/loaders/_rdf2asset.py +12 -14
- cognite/neat/graph/loaders/_rdf2dms.py +14 -10
- cognite/neat/issues/__init__.py +16 -0
- cognite/neat/{issues.py → issues/_base.py} +73 -5
- cognite/neat/issues/errors/external.py +21 -0
- cognite/neat/issues/errors/properties.py +75 -0
- cognite/neat/issues/errors/resources.py +123 -0
- cognite/neat/issues/errors/schema.py +0 -0
- cognite/neat/{rules/issues → issues}/formatters.py +9 -9
- cognite/neat/issues/neat_warnings/__init__.py +2 -0
- cognite/neat/issues/neat_warnings/identifier.py +27 -0
- cognite/neat/issues/neat_warnings/models.py +22 -0
- cognite/neat/issues/neat_warnings/properties.py +77 -0
- cognite/neat/issues/neat_warnings/resources.py +125 -0
- cognite/neat/rules/exporters/_rules2dms.py +3 -2
- cognite/neat/rules/exporters/_validation.py +2 -2
- cognite/neat/rules/importers/__init__.py +7 -3
- cognite/neat/rules/importers/_base.py +3 -3
- cognite/neat/rules/importers/_dms2rules.py +39 -18
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +44 -53
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +6 -5
- cognite/neat/rules/importers/_rdf/__init__.py +0 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/__init__.py +3 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +82 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +34 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +123 -0
- cognite/neat/rules/importers/{_owl2rules/_owl2rules.py → _rdf/_imf2rules/_imf2rules.py} +15 -11
- cognite/neat/rules/importers/{_inference2rules.py → _rdf/_inference2rules.py} +1 -1
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +57 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2metadata.py +68 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +59 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +76 -0
- cognite/neat/rules/importers/_rdf/_shared.py +586 -0
- cognite/neat/rules/importers/_spreadsheet2rules.py +1 -1
- cognite/neat/rules/importers/_yaml2rules.py +2 -1
- cognite/neat/rules/issues/__init__.py +1 -5
- cognite/neat/rules/issues/base.py +2 -21
- cognite/neat/rules/issues/dms.py +0 -134
- cognite/neat/rules/issues/spreadsheet.py +3 -3
- cognite/neat/rules/models/_types/_field.py +5 -2
- cognite/neat/rules/models/asset/_validation.py +1 -1
- cognite/neat/rules/models/dms/_schema.py +53 -30
- cognite/neat/rules/models/dms/_validation.py +2 -2
- cognite/neat/rules/models/entities.py +3 -0
- cognite/neat/rules/models/information/_validation.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_importer.py +73 -1
- cognite/neat/workflows/steps/lib/current/rules_validator.py +19 -7
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.2.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.2.dist-info}/RECORD +57 -42
- cognite/neat/graph/issues/loader.py +0 -104
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +0 -215
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +0 -209
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +0 -203
- cognite/neat/rules/issues/importing.py +0 -423
- /cognite/neat/{graph/issues → issues/errors}/__init__.py +0 -0
- /cognite/neat/rules/importers/{_owl2rules → _rdf/_owl2rules}/__init__.py +0 -0
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.2.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.2.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.2.dist-info}/entry_points.txt +0 -0
cognite/neat/rules/issues/dms.py
CHANGED
|
@@ -10,13 +10,6 @@ __all__ = [
|
|
|
10
10
|
"DMSSchemaError",
|
|
11
11
|
"DMSSchemaWarning",
|
|
12
12
|
"IncompleteSchemaError",
|
|
13
|
-
"MissingSpaceError",
|
|
14
|
-
"MissingContainerError",
|
|
15
|
-
"MissingContainerPropertyError",
|
|
16
|
-
"MissingViewError",
|
|
17
|
-
"MissingParentViewError",
|
|
18
|
-
"MissingSourceViewError",
|
|
19
|
-
"MissingEdgeViewError",
|
|
20
13
|
"DirectRelationMissingSourceWarning",
|
|
21
14
|
"ViewModelVersionNotMatchingWarning",
|
|
22
15
|
"ViewModelSpaceNotMatchingWarning",
|
|
@@ -87,133 +80,6 @@ class IncompleteSchemaError(DMSSchemaError):
|
|
|
87
80
|
return output
|
|
88
81
|
|
|
89
82
|
|
|
90
|
-
@dataclass(frozen=True)
|
|
91
|
-
class MissingSpaceError(DMSSchemaError):
|
|
92
|
-
description = "The spaced referred to by the Container/View/Node/Edge/DataModel does not exist"
|
|
93
|
-
fix = "Create the space"
|
|
94
|
-
space: str
|
|
95
|
-
referred_by: dm.ContainerId | dm.ViewId | dm.NodeId | dm.EdgeId | dm.DataModelId
|
|
96
|
-
|
|
97
|
-
def message(self) -> str:
|
|
98
|
-
return f"The space {self.space} referred to by {self.referred_by} does not exist"
|
|
99
|
-
|
|
100
|
-
def dump(self) -> dict[str, Any]:
|
|
101
|
-
output = super().dump()
|
|
102
|
-
output["space"] = self.space
|
|
103
|
-
output["referred_by"] = self.referred_by
|
|
104
|
-
return output
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
@dataclass(frozen=True)
|
|
108
|
-
class MissingContainerError(DMSSchemaError):
|
|
109
|
-
description = "The container referred to by the View does not exist"
|
|
110
|
-
fix = "Create the container"
|
|
111
|
-
error_name: ClassVar[str] = "MissingContainer"
|
|
112
|
-
container: dm.ContainerId
|
|
113
|
-
referred_by: dm.ViewId | dm.ContainerId
|
|
114
|
-
|
|
115
|
-
def message(self) -> str:
|
|
116
|
-
return f"The container {self.container} referred to by {self.referred_by} does not exist"
|
|
117
|
-
|
|
118
|
-
def dump(self) -> dict[str, Any]:
|
|
119
|
-
output = super().dump()
|
|
120
|
-
output["container"] = self.container
|
|
121
|
-
output["referred_by"] = self.referred_by
|
|
122
|
-
return output
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
@dataclass(frozen=True)
|
|
126
|
-
class MissingContainerPropertyError(DMSSchemaError):
|
|
127
|
-
description = "The property referred to by the View does not exist in the container"
|
|
128
|
-
fix = "Create the property"
|
|
129
|
-
error_name: ClassVar[str] = "MissingContainerProperty"
|
|
130
|
-
container: dm.ContainerId
|
|
131
|
-
property: str
|
|
132
|
-
referred_by: dm.ViewId
|
|
133
|
-
|
|
134
|
-
def message(self) -> str:
|
|
135
|
-
return (
|
|
136
|
-
f"The property {self.property} referred to by the container {self.container} "
|
|
137
|
-
f"does not exist in {self.referred_by}"
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
def dump(self) -> dict[str, Any]:
|
|
141
|
-
output = super().dump()
|
|
142
|
-
output["container"] = self.container
|
|
143
|
-
output["property"] = self.property
|
|
144
|
-
output["referred_by"] = self.referred_by
|
|
145
|
-
return output
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
@dataclass(frozen=True)
|
|
149
|
-
class MissingViewError(DMSSchemaError):
|
|
150
|
-
description = "The view referred to by the View/DataModel does not exist"
|
|
151
|
-
fix = "Create the view"
|
|
152
|
-
error_name: ClassVar[str] = "MissingView"
|
|
153
|
-
view: dm.ViewId
|
|
154
|
-
referred_by: dm.DataModelId | dm.ViewId
|
|
155
|
-
|
|
156
|
-
def message(self) -> str:
|
|
157
|
-
return f"The view {self.view} referred to by {self.referred_by} does not exist"
|
|
158
|
-
|
|
159
|
-
def dump(self) -> dict[str, Any]:
|
|
160
|
-
output = super().dump()
|
|
161
|
-
output["view"] = self.view
|
|
162
|
-
output["referred_by"] = self.referred_by
|
|
163
|
-
return output
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
@dataclass(frozen=True)
|
|
167
|
-
class MissingParentViewError(MissingViewError):
|
|
168
|
-
description = "The parent view referred to by the View does not exist"
|
|
169
|
-
fix = "Create the parent view"
|
|
170
|
-
error_name: ClassVar[str] = "MissingParentView"
|
|
171
|
-
referred_by: dm.ViewId
|
|
172
|
-
|
|
173
|
-
def message(self) -> str:
|
|
174
|
-
return f"The parent view {self.view} referred to by {self.referred_by} does not exist"
|
|
175
|
-
|
|
176
|
-
def dump(self) -> dict[str, Any]:
|
|
177
|
-
output = super().dump()
|
|
178
|
-
output["referred_by"] = self.referred_by
|
|
179
|
-
return output
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
@dataclass(frozen=True)
|
|
183
|
-
class MissingSourceViewError(MissingViewError):
|
|
184
|
-
description = "The source view referred to by the View does not exist"
|
|
185
|
-
fix = "Create the source view"
|
|
186
|
-
error_name: ClassVar[str] = "MissingSourceView"
|
|
187
|
-
property: str
|
|
188
|
-
referred_by: dm.ViewId
|
|
189
|
-
|
|
190
|
-
def message(self) -> str:
|
|
191
|
-
return f"The source view {self.view} referred to by {self.referred_by}.{self.property} does not exist"
|
|
192
|
-
|
|
193
|
-
def dump(self) -> dict[str, Any]:
|
|
194
|
-
output = super().dump()
|
|
195
|
-
output["property"] = self.property
|
|
196
|
-
return output
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
@dataclass(frozen=True)
|
|
200
|
-
class MissingEdgeViewError(MissingViewError):
|
|
201
|
-
description = "The edge view referred to by the View does not exist"
|
|
202
|
-
fix = "Create the edge view"
|
|
203
|
-
error_name: ClassVar[str] = "MissingEdgeView"
|
|
204
|
-
property: str
|
|
205
|
-
referred_by: dm.ViewId
|
|
206
|
-
|
|
207
|
-
def message(self) -> str:
|
|
208
|
-
return f"The edge view {self.view} referred to by {self.referred_by}.{self.property} does not exist"
|
|
209
|
-
|
|
210
|
-
def dump(self) -> dict[str, Any]:
|
|
211
|
-
output = super().dump()
|
|
212
|
-
output["property"] = self.property
|
|
213
|
-
output["referred_by"] = self.referred_by
|
|
214
|
-
return output
|
|
215
|
-
|
|
216
|
-
|
|
217
83
|
@dataclass(frozen=True)
|
|
218
84
|
class DuplicatedViewInDataModelError(DMSSchemaError):
|
|
219
85
|
description = "The view is duplicated in the DataModel"
|
|
@@ -9,7 +9,7 @@ from cognite.client.data_classes.data_modeling import ContainerId, ViewId
|
|
|
9
9
|
from pydantic_core import ErrorDetails
|
|
10
10
|
from rdflib import Namespace
|
|
11
11
|
|
|
12
|
-
from cognite.neat.issues import MultiValueError
|
|
12
|
+
from cognite.neat.issues import MultiValueError, NeatError
|
|
13
13
|
from cognite.neat.utils.spreadsheet import SpreadsheetRead
|
|
14
14
|
|
|
15
15
|
from .base import DefaultPydanticError, NeatValidationError
|
|
@@ -59,8 +59,8 @@ class InvalidSheetError(NeatValidationError, ABC):
|
|
|
59
59
|
errors: list[ErrorDetails],
|
|
60
60
|
read_info_by_sheet: dict[str, SpreadsheetRead] | None = None,
|
|
61
61
|
**kwargs: Any,
|
|
62
|
-
) -> "list[
|
|
63
|
-
output: list[
|
|
62
|
+
) -> "list[NeatError]":
|
|
63
|
+
output: list[NeatError] = []
|
|
64
64
|
for error in errors:
|
|
65
65
|
if raised_error := error.get("ctx", {}).get("error"):
|
|
66
66
|
if isinstance(raised_error, MultiValueError):
|
|
@@ -16,7 +16,7 @@ from pydantic import (
|
|
|
16
16
|
from pydantic.functional_serializers import PlainSerializer
|
|
17
17
|
from pydantic_core import PydanticCustomError
|
|
18
18
|
|
|
19
|
-
from cognite.neat.
|
|
19
|
+
from cognite.neat.issues.neat_warnings.identifier import RegexViolationWarning
|
|
20
20
|
from cognite.neat.rules.issues.spreadsheet import RegexViolationError
|
|
21
21
|
from cognite.neat.utils.regex_patterns import (
|
|
22
22
|
PATTERNS,
|
|
@@ -91,7 +91,10 @@ def _property_validation(value: str) -> str:
|
|
|
91
91
|
if not PATTERNS.property_id_compliance.match(value):
|
|
92
92
|
_raise(RegexViolationError(value, PROPERTY_ID_COMPLIANCE_REGEX).as_pydantic_exception())
|
|
93
93
|
if PATTERNS.more_than_one_alphanumeric.search(value):
|
|
94
|
-
warnings.warn(
|
|
94
|
+
warnings.warn(
|
|
95
|
+
RegexViolationWarning(value, PROPERTY_ID_COMPLIANCE_REGEX, "property", "MoreThanOneNonAlphanumeric"),
|
|
96
|
+
stacklevel=2,
|
|
97
|
+
)
|
|
95
98
|
return value
|
|
96
99
|
|
|
97
100
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from graphlib import CycleError
|
|
2
2
|
from typing import cast
|
|
3
3
|
|
|
4
|
+
from cognite.neat.issues import IssueList
|
|
4
5
|
from cognite.neat.rules import issues
|
|
5
|
-
from cognite.neat.rules.issues.base import IssueList
|
|
6
6
|
from cognite.neat.rules.models._base import SheetList
|
|
7
7
|
from cognite.neat.rules.models.asset._rules import AssetProperty, AssetRules
|
|
8
8
|
from cognite.neat.rules.models.entities import AssetEntity, AssetFields, ClassEntity
|
|
@@ -25,20 +25,16 @@ from cognite.client.data_classes.data_modeling.views import (
|
|
|
25
25
|
)
|
|
26
26
|
from cognite.client.data_classes.transformations.common import Edges, EdgeType, Nodes, ViewInfo
|
|
27
27
|
|
|
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
|
|
28
32
|
from cognite.neat.rules import issues
|
|
29
33
|
from cognite.neat.rules.issues.dms import (
|
|
30
34
|
ContainerPropertyUsedMultipleTimesError,
|
|
31
35
|
DirectRelationMissingSourceWarning,
|
|
32
|
-
DMSSchemaError,
|
|
33
36
|
DuplicatedViewInDataModelError,
|
|
34
37
|
IncompleteSchemaError,
|
|
35
|
-
MissingContainerError,
|
|
36
|
-
MissingContainerPropertyError,
|
|
37
|
-
MissingEdgeViewError,
|
|
38
|
-
MissingParentViewError,
|
|
39
|
-
MissingSourceViewError,
|
|
40
|
-
MissingSpaceError,
|
|
41
|
-
MissingViewError,
|
|
42
38
|
MissingViewInModelWarning,
|
|
43
39
|
)
|
|
44
40
|
from cognite.neat.rules.models.data_types import _DATA_TYPE_BY_DMS_TYPE
|
|
@@ -168,14 +164,10 @@ class DMSSchema:
|
|
|
168
164
|
connection_referenced_view_ids |= cls._connection_references(view)
|
|
169
165
|
connection_referenced_view_ids = connection_referenced_view_ids - existing_view_ids
|
|
170
166
|
if connection_referenced_view_ids:
|
|
171
|
-
warnings.warn(
|
|
172
|
-
MissingViewInModelWarning(data_model.as_id(), connection_referenced_view_ids), UserWarning, stacklevel=2
|
|
173
|
-
)
|
|
167
|
+
warnings.warn(MissingViewInModelWarning(data_model.as_id(), connection_referenced_view_ids), stacklevel=2)
|
|
174
168
|
connection_referenced_views = view_loader.retrieve(list(connection_referenced_view_ids))
|
|
175
169
|
if failed := connection_referenced_view_ids - set(connection_referenced_views.as_ids()):
|
|
176
|
-
warnings.warn(
|
|
177
|
-
issues.importing.FailedImportWarning({repr(v) for v in failed}), UserWarning, stacklevel=2
|
|
178
|
-
)
|
|
170
|
+
warnings.warn(FailedLoadingResourcesWarning[dm.ViewId](frozenset(failed), "View"), stacklevel=2)
|
|
179
171
|
views.extend(connection_referenced_views)
|
|
180
172
|
|
|
181
173
|
# We need to include parent views in the schema to make sure that the schema is valid.
|
|
@@ -432,8 +424,9 @@ class DMSSchema:
|
|
|
432
424
|
if attr.name == "data_model":
|
|
433
425
|
if isinstance(items, list) and len(items) > 1:
|
|
434
426
|
warnings.warn(
|
|
435
|
-
|
|
436
|
-
[item.get("externalId", "Unknown") for item in items]
|
|
427
|
+
MultipleResourcesWarning[str](
|
|
428
|
+
frozenset([item.get("externalId", "Unknown") for item in items]),
|
|
429
|
+
"DataModel",
|
|
437
430
|
),
|
|
438
431
|
stacklevel=2,
|
|
439
432
|
)
|
|
@@ -522,8 +515,8 @@ class DMSSchema:
|
|
|
522
515
|
else:
|
|
523
516
|
raise ValueError(f"Cannot sort item of type {type(item)}")
|
|
524
517
|
|
|
525
|
-
def validate(self) -> list[
|
|
526
|
-
errors: set[
|
|
518
|
+
def validate(self) -> list[NeatError]:
|
|
519
|
+
errors: set[NeatError] = set()
|
|
527
520
|
defined_spaces = self.spaces.copy()
|
|
528
521
|
defined_containers = self.containers.copy()
|
|
529
522
|
defined_views = self.views.copy()
|
|
@@ -535,28 +528,42 @@ class DMSSchema:
|
|
|
535
528
|
|
|
536
529
|
for container in self.containers.values():
|
|
537
530
|
if container.space not in defined_spaces:
|
|
538
|
-
errors.add(
|
|
531
|
+
errors.add(
|
|
532
|
+
ReferredResourceNotFoundError[str, dm.ContainerId](
|
|
533
|
+
container.space, "Space", container.as_id(), "Container"
|
|
534
|
+
)
|
|
535
|
+
)
|
|
539
536
|
|
|
540
537
|
for view in self.views.values():
|
|
541
538
|
view_id = view.as_id()
|
|
542
539
|
if view.space not in defined_spaces:
|
|
543
|
-
errors.add(
|
|
540
|
+
errors.add(ReferredResourceNotFoundError[str, dm.ViewId](view.space, "Space", view_id, "View"))
|
|
544
541
|
|
|
545
542
|
for parent in view.implements or []:
|
|
546
543
|
if parent not in defined_views:
|
|
547
|
-
errors.add(
|
|
544
|
+
errors.add(
|
|
545
|
+
ReferredPropertyNotFoundError[dm.ViewId, dm.ViewId](
|
|
546
|
+
parent, "View", view_id, "View", property_name="implements"
|
|
547
|
+
)
|
|
548
|
+
)
|
|
548
549
|
|
|
549
550
|
for prop_name, prop in (view.properties or {}).items():
|
|
550
551
|
if isinstance(prop, dm.MappedPropertyApply):
|
|
551
552
|
ref_container = defined_containers.get(prop.container)
|
|
552
553
|
if ref_container is None:
|
|
553
|
-
errors.add(
|
|
554
|
+
errors.add(
|
|
555
|
+
ReferredResourceNotFoundError[dm.ContainerId, dm.ViewId](
|
|
556
|
+
prop.container, "Container", view_id, "View"
|
|
557
|
+
)
|
|
558
|
+
)
|
|
554
559
|
elif prop.container_property_identifier not in ref_container.properties:
|
|
555
560
|
errors.add(
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
561
|
+
ReferredPropertyNotFoundError[dm.ContainerId, dm.ViewId](
|
|
562
|
+
prop.container,
|
|
563
|
+
"Container",
|
|
564
|
+
view_id,
|
|
565
|
+
"View",
|
|
566
|
+
property_name=prop.container_property_identifier,
|
|
560
567
|
)
|
|
561
568
|
)
|
|
562
569
|
else:
|
|
@@ -568,14 +575,22 @@ class DMSSchema:
|
|
|
568
575
|
)
|
|
569
576
|
|
|
570
577
|
if isinstance(prop, dm.EdgeConnectionApply) and prop.source not in defined_views:
|
|
571
|
-
errors.add(
|
|
578
|
+
errors.add(
|
|
579
|
+
ReferredPropertyNotFoundError[dm.ViewId, dm.ViewId](
|
|
580
|
+
prop.source, "View", view_id, "View", property_name=prop_name
|
|
581
|
+
)
|
|
582
|
+
)
|
|
572
583
|
|
|
573
584
|
if (
|
|
574
585
|
isinstance(prop, dm.EdgeConnectionApply)
|
|
575
586
|
and prop.edge_source is not None
|
|
576
587
|
and prop.edge_source not in defined_views
|
|
577
588
|
):
|
|
578
|
-
errors.add(
|
|
589
|
+
errors.add(
|
|
590
|
+
ReferredPropertyNotFoundError[dm.ViewId, dm.ViewId](
|
|
591
|
+
prop.edge_source, "View", view_id, "View", property_name=prop_name
|
|
592
|
+
)
|
|
593
|
+
)
|
|
579
594
|
|
|
580
595
|
# This allows for multiple view properties to be mapped to the same container property,
|
|
581
596
|
# as long as they have different external_id, otherwise this will lead to raising
|
|
@@ -610,13 +625,21 @@ class DMSSchema:
|
|
|
610
625
|
if self.data_model:
|
|
611
626
|
model = self.data_model
|
|
612
627
|
if model.space not in defined_spaces:
|
|
613
|
-
errors.add(
|
|
628
|
+
errors.add(
|
|
629
|
+
ReferredResourceNotFoundError[str, dm.DataModelId](
|
|
630
|
+
model.space, "Space", model.as_id(), "Data Model"
|
|
631
|
+
)
|
|
632
|
+
)
|
|
614
633
|
|
|
615
634
|
view_counts: dict[dm.ViewId, int] = defaultdict(int)
|
|
616
635
|
for view_id_or_class in model.views or []:
|
|
617
636
|
view_id = view_id_or_class if isinstance(view_id_or_class, dm.ViewId) else view_id_or_class.as_id()
|
|
618
637
|
if view_id not in defined_views:
|
|
619
|
-
errors.add(
|
|
638
|
+
errors.add(
|
|
639
|
+
ReferredResourceNotFoundError[dm.ViewId, dm.DataModelId](
|
|
640
|
+
view_id, "View", model.as_id(), "DataModel"
|
|
641
|
+
)
|
|
642
|
+
)
|
|
620
643
|
view_counts[view_id] += 1
|
|
621
644
|
|
|
622
645
|
for view_id, count in view_counts.items():
|
|
@@ -3,8 +3,8 @@ from typing import Any, ClassVar
|
|
|
3
3
|
|
|
4
4
|
from cognite.client import data_modeling as dm
|
|
5
5
|
|
|
6
|
+
from cognite.neat.issues import IssueList, NeatIssueList
|
|
6
7
|
from cognite.neat.rules import issues
|
|
7
|
-
from cognite.neat.rules.issues import IssueList
|
|
8
8
|
from cognite.neat.rules.models._base import DataModelType, ExtensionCategory, SchemaCompleteness
|
|
9
9
|
from cognite.neat.rules.models._constants import DMS_CONTAINER_SIZE_LIMIT
|
|
10
10
|
from cognite.neat.rules.models.data_types import DataType
|
|
@@ -31,7 +31,7 @@ class DMSPostValidation:
|
|
|
31
31
|
self.views = rules.views
|
|
32
32
|
self.issue_list = IssueList()
|
|
33
33
|
|
|
34
|
-
def validate(self) ->
|
|
34
|
+
def validate(self) -> NeatIssueList:
|
|
35
35
|
self._validate_raw_filter()
|
|
36
36
|
self._consistent_container_properties()
|
|
37
37
|
|
|
@@ -418,6 +418,9 @@ class ViewEntity(DMSVersionedEntity[ViewId]):
|
|
|
418
418
|
) -> ViewId:
|
|
419
419
|
return ViewId(space=self.space, external_id=self.external_id, version=self.version)
|
|
420
420
|
|
|
421
|
+
def to_property_id(self, property_id: str) -> PropertyId:
|
|
422
|
+
return PropertyId(source=self.as_id(), property=property_id)
|
|
423
|
+
|
|
421
424
|
@classmethod
|
|
422
425
|
def from_id(cls, id: ViewId, default_version: str | None = None) -> "ViewEntity":
|
|
423
426
|
if id.version is not None:
|
|
@@ -2,8 +2,8 @@ import itertools
|
|
|
2
2
|
from collections import Counter
|
|
3
3
|
from typing import cast
|
|
4
4
|
|
|
5
|
+
from cognite.neat.issues import IssueList
|
|
5
6
|
from cognite.neat.rules import issues
|
|
6
|
-
from cognite.neat.rules.issues import IssueList
|
|
7
7
|
from cognite.neat.rules.models._base import DataModelType, SchemaCompleteness
|
|
8
8
|
from cognite.neat.rules.models.entities import ClassEntity, EntityTypes, UnknownEntity
|
|
9
9
|
from cognite.neat.utils.rdf_ import get_inheritance_path
|
|
@@ -5,8 +5,8 @@ from typing import ClassVar
|
|
|
5
5
|
from cognite.client import CogniteClient
|
|
6
6
|
from cognite.client.data_classes.data_modeling import DataModelId
|
|
7
7
|
|
|
8
|
+
from cognite.neat.issues.formatters import FORMATTER_BY_NAME
|
|
8
9
|
from cognite.neat.rules import importers
|
|
9
|
-
from cognite.neat.rules.issues.formatters import FORMATTER_BY_NAME
|
|
10
10
|
from cognite.neat.rules.models import RoleTypes
|
|
11
11
|
from cognite.neat.rules.models.entities import DataModelEntity, DMSUnknownEntity
|
|
12
12
|
from cognite.neat.workflows._exceptions import StepNotInitialized
|
|
@@ -19,6 +19,7 @@ CATEGORY = __name__.split(".")[-1].replace("_", " ").title()
|
|
|
19
19
|
__all__ = [
|
|
20
20
|
"ExcelToRules",
|
|
21
21
|
"OntologyToRules",
|
|
22
|
+
"IMFToRules",
|
|
22
23
|
"DMSToRules",
|
|
23
24
|
"RulesInferenceFromRdfFile",
|
|
24
25
|
]
|
|
@@ -162,6 +163,77 @@ class OntologyToRules(Step):
|
|
|
162
163
|
return FlowMessage(output_text=output_text), MultiRuleData.from_rules(rules)
|
|
163
164
|
|
|
164
165
|
|
|
166
|
+
class IMFToRules(Step):
|
|
167
|
+
"""This step import rules from the IMF-types and validates them."""
|
|
168
|
+
|
|
169
|
+
description = "This step imports rules from an RDF file "
|
|
170
|
+
version = "private-beta"
|
|
171
|
+
category = CATEGORY
|
|
172
|
+
configurables: ClassVar[list[Configurable]] = [
|
|
173
|
+
Configurable(
|
|
174
|
+
name="File name",
|
|
175
|
+
value="",
|
|
176
|
+
label="""Full file name of the RDF-file containing the IMF-types in the rules folder.
|
|
177
|
+
If not provided, step will attempt to get file name from payload
|
|
178
|
+
of 'File Uploader' step (if exist)""",
|
|
179
|
+
),
|
|
180
|
+
Configurable(
|
|
181
|
+
name="Report formatter",
|
|
182
|
+
value=next(iter(FORMATTER_BY_NAME.keys())),
|
|
183
|
+
label="The format of the report for the validation of the rules",
|
|
184
|
+
options=list(FORMATTER_BY_NAME),
|
|
185
|
+
),
|
|
186
|
+
Configurable(
|
|
187
|
+
name="Role",
|
|
188
|
+
value="infer",
|
|
189
|
+
label="For what role Rules are intended?",
|
|
190
|
+
options=["infer", *RoleTypes.__members__.keys()],
|
|
191
|
+
),
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
def run(self, flow_message: FlowMessage) -> (FlowMessage, MultiRuleData): # type: ignore[syntax, override]
|
|
195
|
+
if self.configs is None or self.data_store_path is None:
|
|
196
|
+
raise StepNotInitialized(type(self).__name__)
|
|
197
|
+
|
|
198
|
+
file_name = self.configs.get("File name", None)
|
|
199
|
+
|
|
200
|
+
full_path = flow_message.payload.get("full_path", None) if flow_message.payload else None
|
|
201
|
+
|
|
202
|
+
if file_name:
|
|
203
|
+
rules_file_path = self.config.rules_store_path / file_name
|
|
204
|
+
|
|
205
|
+
elif full_path:
|
|
206
|
+
rules_file_path = full_path
|
|
207
|
+
else:
|
|
208
|
+
error_text = "Expected either 'File name' in the step config or 'File uploader' step uploading Excel Rules."
|
|
209
|
+
return FlowMessage(error_text=error_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
|
|
210
|
+
|
|
211
|
+
# if role is None, it will be inferred from the rules file
|
|
212
|
+
role = self.configs.get("Role")
|
|
213
|
+
role_enum = None
|
|
214
|
+
if role != "infer" and role is not None:
|
|
215
|
+
role_enum = RoleTypes[role]
|
|
216
|
+
|
|
217
|
+
ontology_importer = importers.IMFImporter(filepath=rules_file_path)
|
|
218
|
+
rules, issues = ontology_importer.to_rules(errors="continue", role=role_enum)
|
|
219
|
+
|
|
220
|
+
if rules is None:
|
|
221
|
+
output_dir = self.config.staging_path
|
|
222
|
+
report_writer = FORMATTER_BY_NAME[self.configs["Report formatter"]]()
|
|
223
|
+
report_writer.write_to_file(issues, file_or_dir_path=output_dir)
|
|
224
|
+
report_file = report_writer.default_file_name
|
|
225
|
+
error_text = (
|
|
226
|
+
"<p></p>"
|
|
227
|
+
f'<a href="/data/staging/{report_file}?{time.time()}" '
|
|
228
|
+
f'target="_blank">Failed to validate rules, click here for report</a>'
|
|
229
|
+
)
|
|
230
|
+
return FlowMessage(error_text=error_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
|
|
231
|
+
|
|
232
|
+
output_text = "Rules validation passed successfully!"
|
|
233
|
+
|
|
234
|
+
return FlowMessage(output_text=output_text), MultiRuleData.from_rules(rules)
|
|
235
|
+
|
|
236
|
+
|
|
165
237
|
class DMSToRules(Step):
|
|
166
238
|
"""This step imports rules from CDF Data Model"""
|
|
167
239
|
|
|
@@ -5,9 +5,9 @@ from typing import ClassVar
|
|
|
5
5
|
|
|
6
6
|
from cognite.client import CogniteClient
|
|
7
7
|
|
|
8
|
-
from cognite.neat.
|
|
9
|
-
from cognite.neat.
|
|
10
|
-
from cognite.neat.
|
|
8
|
+
from cognite.neat.issues import NeatIssueList
|
|
9
|
+
from cognite.neat.issues.errors.resources import ReferredResourceNotFoundError
|
|
10
|
+
from cognite.neat.issues.formatters import FORMATTER_BY_NAME
|
|
11
11
|
from cognite.neat.rules.models import DMSRules, SchemaCompleteness
|
|
12
12
|
from cognite.neat.utils.cdf.loaders import ViewLoader
|
|
13
13
|
from cognite.neat.workflows._exceptions import StepNotInitialized
|
|
@@ -61,9 +61,21 @@ class ValidateRulesAgainstCDF(Step):
|
|
|
61
61
|
if not errors:
|
|
62
62
|
return FlowMessage(output_text="Rules are complete and valid. No need to fetch from CDF.")
|
|
63
63
|
|
|
64
|
-
missing_spaces = [
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
missing_spaces = [
|
|
65
|
+
error.identifier
|
|
66
|
+
for error in errors
|
|
67
|
+
if isinstance(error, ReferredResourceNotFoundError) and error.resource_type == "Space"
|
|
68
|
+
]
|
|
69
|
+
missing_views = [
|
|
70
|
+
error.identifier
|
|
71
|
+
for error in errors
|
|
72
|
+
if isinstance(error, ReferredResourceNotFoundError) and error.resource_type == "View"
|
|
73
|
+
]
|
|
74
|
+
missing_containers = [
|
|
75
|
+
error.identifier
|
|
76
|
+
for error in errors
|
|
77
|
+
if isinstance(error, ReferredResourceNotFoundError) and error.resource_type == "Container"
|
|
78
|
+
]
|
|
67
79
|
|
|
68
80
|
retrieved_spaces = cdf_client.data_modeling.spaces.retrieve(missing_spaces).as_write()
|
|
69
81
|
retrieved_containers = cdf_client.data_modeling.containers.retrieve(missing_containers).as_write()
|
|
@@ -87,7 +99,7 @@ class ValidateRulesAgainstCDF(Step):
|
|
|
87
99
|
output_dir = self.data_store_path / Path("staging")
|
|
88
100
|
report_writer = FORMATTER_BY_NAME[self.configs["Report Formatter"]]()
|
|
89
101
|
report_writer.write_to_file(
|
|
90
|
-
|
|
102
|
+
NeatIssueList(errors, title=dms_rules.metadata.name or dms_rules.metadata.external_id),
|
|
91
103
|
file_or_dir_path=output_dir,
|
|
92
104
|
)
|
|
93
105
|
report_file = report_writer.default_file_name
|