cognite-neat 0.88.0__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/app/api/routers/configuration.py +1 -1
- cognite/neat/app/ui/neat-app/build/asset-manifest.json +7 -7
- cognite/neat/app/ui/neat-app/build/index.html +1 -1
- cognite/neat/app/ui/neat-app/build/static/css/{main.38a62222.css → main.72e3d92e.css} +2 -2
- cognite/neat/app/ui/neat-app/build/static/css/main.72e3d92e.css.map +1 -0
- cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js +3 -0
- cognite/neat/app/ui/neat-app/build/static/js/{main.ec7f72e2.js.LICENSE.txt → main.5a52cf09.js.LICENSE.txt} +0 -9
- cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js.map +1 -0
- cognite/neat/config.py +44 -27
- cognite/neat/exceptions.py +8 -2
- cognite/neat/graph/extractors/_classic_cdf/_assets.py +21 -73
- cognite/neat/graph/extractors/_classic_cdf/_base.py +102 -0
- cognite/neat/graph/extractors/_classic_cdf/_events.py +46 -42
- cognite/neat/graph/extractors/_classic_cdf/_files.py +41 -45
- cognite/neat/graph/extractors/_classic_cdf/_labels.py +75 -52
- cognite/neat/graph/extractors/_classic_cdf/_relationships.py +49 -27
- cognite/neat/graph/extractors/_classic_cdf/_sequences.py +47 -50
- cognite/neat/graph/extractors/_classic_cdf/_timeseries.py +47 -49
- 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/graph/queries/_base.py +22 -29
- cognite/neat/graph/queries/_shared.py +1 -1
- cognite/neat/graph/stores/_base.py +19 -11
- cognite/neat/graph/transformers/_rdfpath.py +3 -2
- cognite/neat/issues/__init__.py +16 -0
- cognite/neat/{issues.py → issues/_base.py} +78 -2
- 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/_rules2ontology.py +28 -20
- cognite/neat/rules/exporters/_validation.py +15 -21
- 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 +31 -28
- 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 +20 -134
- cognite/neat/rules/issues/ontology.py +298 -0
- cognite/neat/rules/issues/spreadsheet.py +51 -3
- cognite/neat/rules/issues/tables.py +72 -0
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/_types/_field.py +14 -21
- 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/_rules.py +5 -4
- cognite/neat/rules/models/information/_validation.py +1 -1
- cognite/neat/utils/rdf_.py +17 -9
- cognite/neat/utils/regex_patterns.py +52 -0
- 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.0.dist-info → cognite_neat-0.88.2.dist-info}/METADATA +2 -6
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/RECORD +85 -72
- cognite/neat/app/ui/neat-app/build/static/css/main.38a62222.css.map +0 -1
- cognite/neat/app/ui/neat-app/build/static/js/main.ec7f72e2.js +0 -3
- cognite/neat/app/ui/neat-app/build/static/js/main.ec7f72e2.js.map +0 -1
- cognite/neat/graph/issues/loader.py +0 -104
- cognite/neat/graph/stores/_oxrdflib.py +0 -247
- cognite/neat/rules/exceptions.py +0 -2972
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +0 -215
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +0 -213
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +0 -203
- cognite/neat/rules/issues/importing.py +0 -408
- cognite/neat/rules/models/_types/_base.py +0 -16
- cognite/neat/workflows/examples/Export_Rules_to_Ontology/workflow.yaml +0 -152
- cognite/neat/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml +0 -139
- cognite/neat/workflows/examples/Ontology_to_Data_Model/workflow.yaml +0 -116
- /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.0.dist-info → cognite_neat-0.88.2.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from cognite.neat.issues import MultiValueError
|
|
2
2
|
|
|
3
|
-
from . import dms, fileread,
|
|
3
|
+
from . import dms, fileread, spreadsheet, spreadsheet_file
|
|
4
4
|
from .base import (
|
|
5
5
|
DefaultPydanticError,
|
|
6
|
-
IssueList,
|
|
7
6
|
NeatValidationError,
|
|
8
7
|
ValidationIssue,
|
|
9
8
|
ValidationWarning,
|
|
@@ -12,15 +11,12 @@ from .base import (
|
|
|
12
11
|
__all__ = [
|
|
13
12
|
"DefaultPydanticError",
|
|
14
13
|
"MultiValueError",
|
|
15
|
-
"IssueList",
|
|
16
14
|
"NeatValidationError",
|
|
17
15
|
"ValidationIssue",
|
|
18
16
|
"ValidationIssue",
|
|
19
17
|
"ValidationWarning",
|
|
20
18
|
"dms",
|
|
21
19
|
"fileread",
|
|
22
|
-
"formatters",
|
|
23
|
-
"importing",
|
|
24
20
|
"spreadsheet",
|
|
25
21
|
"spreadsheet_file",
|
|
26
22
|
]
|
|
@@ -4,14 +4,13 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from pydantic_core import ErrorDetails
|
|
6
6
|
|
|
7
|
-
from cognite.neat.issues import MultiValueError, NeatError, NeatIssue,
|
|
7
|
+
from cognite.neat.issues import MultiValueError, NeatError, NeatIssue, NeatWarning
|
|
8
8
|
|
|
9
9
|
__all__ = [
|
|
10
10
|
"ValidationIssue",
|
|
11
11
|
"NeatValidationError",
|
|
12
12
|
"DefaultPydanticError",
|
|
13
13
|
"ValidationWarning",
|
|
14
|
-
"IssueList",
|
|
15
14
|
"MultiValueError",
|
|
16
15
|
]
|
|
17
16
|
|
|
@@ -21,22 +20,7 @@ class ValidationIssue(NeatIssue, ABC): ...
|
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
@dataclass(frozen=True)
|
|
24
|
-
class NeatValidationError(NeatError, ValidationIssue, ABC):
|
|
25
|
-
@classmethod
|
|
26
|
-
def from_pydantic_errors(cls, errors: list[ErrorDetails], **kwargs) -> "list[NeatValidationError]":
|
|
27
|
-
"""Convert a list of pydantic errors to a list of Error instances.
|
|
28
|
-
|
|
29
|
-
This is intended to be overridden in subclasses to handle specific error types.
|
|
30
|
-
"""
|
|
31
|
-
all_errors: list[NeatValidationError] = []
|
|
32
|
-
for error in errors:
|
|
33
|
-
if isinstance(ctx := error.get("ctx"), dict) and isinstance(
|
|
34
|
-
multi_error := ctx.get("error"), MultiValueError
|
|
35
|
-
):
|
|
36
|
-
all_errors.extend(multi_error.errors) # type: ignore[arg-type]
|
|
37
|
-
else:
|
|
38
|
-
all_errors.append(DefaultPydanticError.from_pydantic_error(error))
|
|
39
|
-
return all_errors
|
|
23
|
+
class NeatValidationError(NeatError, ValidationIssue, ABC): ...
|
|
40
24
|
|
|
41
25
|
|
|
42
26
|
@dataclass(frozen=True)
|
|
@@ -77,6 +61,3 @@ class DefaultPydanticError(NeatValidationError):
|
|
|
77
61
|
|
|
78
62
|
@dataclass(frozen=True)
|
|
79
63
|
class ValidationWarning(NeatWarning, ValidationIssue, ABC): ...
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class IssueList(NeatIssueList[ValidationIssue]): ...
|
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"
|
|
@@ -432,6 +298,26 @@ class ChangingViewError(DMSSchemaError):
|
|
|
432
298
|
return output
|
|
433
299
|
|
|
434
300
|
|
|
301
|
+
@dataclass(frozen=True)
|
|
302
|
+
class EntityIDNotDMSCompliantWarning(DMSSchemaWarning):
|
|
303
|
+
description = "The entity ID, {entity_id} of type {entity_type}, is not DMS compliant. Violating regex {regex}"
|
|
304
|
+
fix = "Change the entity ID to be DMS compliant"
|
|
305
|
+
error_name: ClassVar[str] = "EntityIDNotDMSCompliantWarning"
|
|
306
|
+
entity_id: str
|
|
307
|
+
entity_type: str
|
|
308
|
+
regex: str
|
|
309
|
+
|
|
310
|
+
def message(self) -> str:
|
|
311
|
+
return self.description.format(entity_id=self.entity_id, entity_type=self.entity_type, regex=self.regex)
|
|
312
|
+
|
|
313
|
+
def dump(self) -> dict[str, Any]:
|
|
314
|
+
output = super().dump()
|
|
315
|
+
output["entity_id"] = self.entity_id
|
|
316
|
+
output["dms_type"] = self.entity_type
|
|
317
|
+
output["regex"] = self.regex
|
|
318
|
+
return output
|
|
319
|
+
|
|
320
|
+
|
|
435
321
|
@dataclass(frozen=True)
|
|
436
322
|
class EmptyContainerWarning(DMSSchemaWarning):
|
|
437
323
|
description = "The container is empty"
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from .base import NeatValidationError, ValidationWarning
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"OntologyError",
|
|
9
|
+
"OntologyWarning",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class OntologyError(NeatValidationError, ABC): ...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class OntologyWarning(ValidationWarning, ABC): ...
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class OntologyMultiLabeledPropertyWarning(OntologyWarning):
|
|
23
|
+
"""This warning occurs when a property is given multiple labels, typically if the
|
|
24
|
+
same property is defined for different classes but different name is given
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
property_id: property id that raised warning due to multiple labels
|
|
28
|
+
names: list of names of property
|
|
29
|
+
|
|
30
|
+
Notes:
|
|
31
|
+
This would be automatically fixed by taking the first label (aka name) of the property.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
description = (
|
|
35
|
+
"This warning occurs when a property is given multiple labels,"
|
|
36
|
+
" typically if the same property is defined for different "
|
|
37
|
+
"classes but different name is given."
|
|
38
|
+
)
|
|
39
|
+
fix = "This would be automatically fixed by taking the first label (aka name) of the property."
|
|
40
|
+
|
|
41
|
+
property_id: str
|
|
42
|
+
names: list[str] | None = None
|
|
43
|
+
|
|
44
|
+
def message(self) -> str:
|
|
45
|
+
message = (
|
|
46
|
+
"Property should have single preferred label (human readable name)."
|
|
47
|
+
f"Currently property '{self.property_id}' has multiple preferred labels: {', '.join(self.names or [])} !"
|
|
48
|
+
f"Only the first name, i.e. '{self.names[0] if self.names else ''}' will be considered!"
|
|
49
|
+
)
|
|
50
|
+
message += f"\nDescription: {self.description}"
|
|
51
|
+
message += f"\nFix: {self.fix}"
|
|
52
|
+
return message
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class OntologyMultiDefinitionPropertyWarning(OntologyWarning):
|
|
57
|
+
"""This warning occurs when a property is given multiple human readable definitions,
|
|
58
|
+
typically if the same property is defined for different classes where each definition
|
|
59
|
+
is different.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
property_id: property id that raised warning due to multiple definitions
|
|
63
|
+
|
|
64
|
+
Notes:
|
|
65
|
+
This would be automatically fixed by concatenating all definitions.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
description = (
|
|
69
|
+
"This warning occurs when a property is given multiple human readable definitions,"
|
|
70
|
+
" typically if the same property is defined for different "
|
|
71
|
+
"classes where each definition is different."
|
|
72
|
+
)
|
|
73
|
+
fix = "This would be automatically fixed by concatenating all definitions."
|
|
74
|
+
|
|
75
|
+
property_id: str
|
|
76
|
+
|
|
77
|
+
def message(self):
|
|
78
|
+
message = (
|
|
79
|
+
f"Multiple definitions (aka comments) of property '{self.property_id}' detected."
|
|
80
|
+
" Definitions will be concatenated."
|
|
81
|
+
)
|
|
82
|
+
message += f"\nDescription: {self.description}"
|
|
83
|
+
message += f"\nFix: {self.fix}"
|
|
84
|
+
return message
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class OntologyMultiTypePropertyWarning(OntologyWarning):
|
|
89
|
+
"""This warning occurs when a same property is define for two object/classes where
|
|
90
|
+
its expected value type is different in one definition, e.g. acts as an edge, while in
|
|
91
|
+
other definition acts as and attribute
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
property_id: property id that raised warning due to multi type definition
|
|
95
|
+
types: list of types of property
|
|
96
|
+
|
|
97
|
+
Notes:
|
|
98
|
+
If a property takes different value types for different objects, simply define
|
|
99
|
+
new property. It is bad practice to have multi type property!
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
description = (
|
|
103
|
+
"This warning occurs when a same property is define for two object/classes where"
|
|
104
|
+
" its expected value type is different in one definition, e.g. acts as an edge, while in "
|
|
105
|
+
"other definition acts as and attribute"
|
|
106
|
+
)
|
|
107
|
+
fix = "If a property takes different value types for different objects, simply define new property"
|
|
108
|
+
|
|
109
|
+
property_id: str
|
|
110
|
+
types: list[str] | None = None
|
|
111
|
+
|
|
112
|
+
def message(self) -> str:
|
|
113
|
+
message = (
|
|
114
|
+
"It is bad practice to have multi type property! "
|
|
115
|
+
f"Currently property '{self.property_id}' is defined as multi type property: {', '.join(self.types or [])}"
|
|
116
|
+
)
|
|
117
|
+
message += f"\nDescription: {self.description}"
|
|
118
|
+
message += f"\nFix: {self.fix}"
|
|
119
|
+
return message
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass(frozen=True)
|
|
123
|
+
class OntologyMultiRangePropertyWarning(OntologyWarning):
|
|
124
|
+
"""This warning occurs when a property takes range of values which consists of union
|
|
125
|
+
of multiple value types
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
property_id: property id that raised warning due to multi range definition
|
|
129
|
+
range_of_values: list of ranges that property takes
|
|
130
|
+
|
|
131
|
+
Notes:
|
|
132
|
+
If a property takes different range of values, simply define new property.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
description = (
|
|
136
|
+
"This warning occurs when a property takes range of values which consists of union of multiple value types."
|
|
137
|
+
)
|
|
138
|
+
fix = "If a property takes different range of values, simply define new property"
|
|
139
|
+
property_id: str
|
|
140
|
+
range_of_values: list[str] | None = None
|
|
141
|
+
|
|
142
|
+
def message(self) -> str:
|
|
143
|
+
message = (
|
|
144
|
+
"It is bad practice to have property that take various range of values! "
|
|
145
|
+
f"Currently property '{self.property_id}' has multiple ranges: {', '.join(self.range_of_values or [])}"
|
|
146
|
+
)
|
|
147
|
+
message += f"\nDescription: {self.description}"
|
|
148
|
+
message += f"\nFix: {self.fix}"
|
|
149
|
+
return message
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class OntologyMultiDomainPropertyWarning(OntologyWarning):
|
|
154
|
+
"""This warning occurs when a property is reused for more than one classes
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
property_id: property id that raised warning due to reuse definition
|
|
158
|
+
classes: list of classes that use the same property
|
|
159
|
+
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
160
|
+
|
|
161
|
+
Notes:
|
|
162
|
+
No need to fix this, but make sure that property type is consistent across different
|
|
163
|
+
classes and that ideally takes the same range of values
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
description = "This warning occurs when a property is reused for more than one classes."
|
|
167
|
+
fix = (
|
|
168
|
+
"No need to fix this, but make sure that property type is consistent"
|
|
169
|
+
" across different classes and that ideally takes the same range of values"
|
|
170
|
+
)
|
|
171
|
+
property_id: str
|
|
172
|
+
classes: list[str] | None = None
|
|
173
|
+
|
|
174
|
+
def message(self) -> str:
|
|
175
|
+
message = (
|
|
176
|
+
f"Currently property '{self.property_id}' is defined for multiple classes: {', '.join(self.classes or [])}"
|
|
177
|
+
)
|
|
178
|
+
message += f"\nDescription: {self.description}"
|
|
179
|
+
message += f"\nFix: {self.fix}"
|
|
180
|
+
return message
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass(frozen=True)
|
|
184
|
+
class PropertiesDefinedMultipleTimesError(OntologyError):
|
|
185
|
+
"""This error is raised during export of Transformation Rules to DMS schema when
|
|
186
|
+
when properties are defined multiple times for the same class.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
report: report on properties which are defined multiple times
|
|
190
|
+
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
191
|
+
|
|
192
|
+
Notes:
|
|
193
|
+
Make sure to check validation report of Transformation Rules and fix DMS related warnings.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
description = (
|
|
197
|
+
"This error is raised during export of Transformation Rules to "
|
|
198
|
+
"DMS schema when properties are defined multiple times for the same class."
|
|
199
|
+
)
|
|
200
|
+
fix = "Make sure to check validation report of Transformation Rules and fix DMS related warnings."
|
|
201
|
+
|
|
202
|
+
report: str
|
|
203
|
+
|
|
204
|
+
def message(self) -> str:
|
|
205
|
+
message = f"Following properties defined multiple times for the same class(es): {self.report}"
|
|
206
|
+
|
|
207
|
+
message += f"\nDescription: {self.description}"
|
|
208
|
+
message += f"\nFix: {self.fix}"
|
|
209
|
+
return message
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@dataclass(frozen=True)
|
|
213
|
+
class PropertyDefinitionsNotForSamePropertyError(OntologyError):
|
|
214
|
+
"""This error is raised if property definitions are not for linked to the same
|
|
215
|
+
property id when exporting rules to ontological representation.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
description = "This error is raised if property definitions are not for linked to the same property id"
|
|
222
|
+
|
|
223
|
+
def message(self):
|
|
224
|
+
message = "All definitions should have the same property_id! Aborting."
|
|
225
|
+
|
|
226
|
+
message += f"\nDescription: {self.description}"
|
|
227
|
+
return message
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dataclass(frozen=True)
|
|
231
|
+
class PrefixMissingError(OntologyError):
|
|
232
|
+
"""Prefix, which is in the 'Metadata' sheet, is missing.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
236
|
+
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
description = "Prefix is missing from the 'Metadata' sheet."
|
|
240
|
+
example = "There is no prefix in the 'Metadata' sheet."
|
|
241
|
+
fix = "Specify the prefix if prefix in the 'Metadata' sheet."
|
|
242
|
+
|
|
243
|
+
def message(self) -> str:
|
|
244
|
+
message = "Missing prefix stored in 'Metadata' sheet."
|
|
245
|
+
message += f"\nDescription: {self.description}"
|
|
246
|
+
message += f"\nExample: {self.example}"
|
|
247
|
+
message += f"\nFix: {self.fix}"
|
|
248
|
+
return message
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@dataclass(frozen=True)
|
|
252
|
+
class MissingDataModelPrefixOrNamespaceWarning(ValidationWarning):
|
|
253
|
+
"""Prefix and/or namespace are missing in the 'Metadata' sheet
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
257
|
+
|
|
258
|
+
Notes:
|
|
259
|
+
Add missing prefix and/or namespace in the 'Metadata' sheet
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
description = "Either prefix or namespace or both are missing in the 'Metadata' sheet"
|
|
263
|
+
fix = "Add missing prefix and/or namespace in the 'Metadata' sheet"
|
|
264
|
+
|
|
265
|
+
def message(self) -> str:
|
|
266
|
+
message = (
|
|
267
|
+
"Instances sheet is present but prefix and/or namespace are missing in 'Metadata' sheet."
|
|
268
|
+
"Instances sheet will not be processed!"
|
|
269
|
+
)
|
|
270
|
+
message += f"\nDescription: {self.description}"
|
|
271
|
+
message += f"\nFix: {self.fix}"
|
|
272
|
+
return message
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@dataclass(frozen=True)
|
|
276
|
+
class MetadataSheetNamespaceNotDefinedError(OntologyError):
|
|
277
|
+
"""namespace, which is in the 'Metadata' sheet, is not defined
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
namespace: namespace that raised exception
|
|
281
|
+
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
282
|
+
|
|
283
|
+
Notes:
|
|
284
|
+
Check if `namespace` in the `Metadata` sheet is properly constructed as valid URL
|
|
285
|
+
containing only allowed characters.
|
|
286
|
+
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
description = "namespace, which is in the 'Metadata' sheet, is missing"
|
|
290
|
+
example: ClassVar[str] = "Example of a valid namespace 'http://www.w3.org/ns/sparql#'"
|
|
291
|
+
fix = "Define the 'namespace' in the 'Metadata' sheet."
|
|
292
|
+
|
|
293
|
+
def message(self) -> str:
|
|
294
|
+
message = "Missing namespace in 'Metadata' sheet."
|
|
295
|
+
message += f"\nDescription: {self.description}"
|
|
296
|
+
message += f"\nExample: {self.example}"
|
|
297
|
+
message += f"\nFix: {self.fix}"
|
|
298
|
+
return message
|
|
@@ -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
|
|
@@ -38,6 +38,7 @@ __all__ = [
|
|
|
38
38
|
"MultiDefaultError",
|
|
39
39
|
"MultiIndexError",
|
|
40
40
|
"MultiUniqueConstraintError",
|
|
41
|
+
"RegexViolationError",
|
|
41
42
|
]
|
|
42
43
|
|
|
43
44
|
|
|
@@ -58,8 +59,8 @@ class InvalidSheetError(NeatValidationError, ABC):
|
|
|
58
59
|
errors: list[ErrorDetails],
|
|
59
60
|
read_info_by_sheet: dict[str, SpreadsheetRead] | None = None,
|
|
60
61
|
**kwargs: Any,
|
|
61
|
-
) -> "list[
|
|
62
|
-
output: list[
|
|
62
|
+
) -> "list[NeatError]":
|
|
63
|
+
output: list[NeatError] = []
|
|
63
64
|
for error in errors:
|
|
64
65
|
if raised_error := error.get("ctx", {}).get("error"):
|
|
65
66
|
if isinstance(raised_error, MultiValueError):
|
|
@@ -277,6 +278,24 @@ class PropertiesDefinedForUndefinedClassesError(NeatValidationError):
|
|
|
277
278
|
)
|
|
278
279
|
|
|
279
280
|
|
|
281
|
+
@dataclass(frozen=True)
|
|
282
|
+
class RegexViolationError(NeatValidationError):
|
|
283
|
+
description = "Value, {value} failed regex, {regex}, validation."
|
|
284
|
+
fix = "Make sure that the name follows the regex pattern."
|
|
285
|
+
|
|
286
|
+
value: str
|
|
287
|
+
regex: str
|
|
288
|
+
|
|
289
|
+
def dump(self) -> dict[str, str]:
|
|
290
|
+
output = super().dump()
|
|
291
|
+
output["value"] = self.value
|
|
292
|
+
output["regex"] = self.regex
|
|
293
|
+
return output
|
|
294
|
+
|
|
295
|
+
def message(self) -> str:
|
|
296
|
+
return self.description.format(value=self.value, regex=self.regex)
|
|
297
|
+
|
|
298
|
+
|
|
280
299
|
@dataclass(frozen=True)
|
|
281
300
|
class ClassNoPropertiesNoParentError(NeatValidationError):
|
|
282
301
|
description = "Class has no properties and no parents."
|
|
@@ -295,6 +314,35 @@ class ClassNoPropertiesNoParentError(NeatValidationError):
|
|
|
295
314
|
return f"Class {self.classes[0]} have no direct or inherited properties. This may be a mistake."
|
|
296
315
|
|
|
297
316
|
|
|
317
|
+
@dataclass(frozen=True)
|
|
318
|
+
class DefaultValueTypeNotProperError(NeatValidationError):
|
|
319
|
+
"""This exceptions is raised when default value type is not proper, i.e. it is not
|
|
320
|
+
according to the expected value type set in Rules.
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
default_value_type: default value type that raised exception
|
|
325
|
+
expected_value_type: expected value type that raised exception
|
|
326
|
+
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
description = (
|
|
330
|
+
"This exceptions is raised when default value type is not proper, i.e. it is not "
|
|
331
|
+
"according to the expected value type set in Rules."
|
|
332
|
+
)
|
|
333
|
+
property_id: str
|
|
334
|
+
default_value_type: str
|
|
335
|
+
expected_value_type: str
|
|
336
|
+
|
|
337
|
+
def message(self) -> str:
|
|
338
|
+
message = (
|
|
339
|
+
f"Default value for property {self.property_id} is of type {self.default_value_type} "
|
|
340
|
+
f"which is different from the expected value type {self.expected_value_type}!"
|
|
341
|
+
)
|
|
342
|
+
message += f"\nDescription: {self.description}"
|
|
343
|
+
return message
|
|
344
|
+
|
|
345
|
+
|
|
298
346
|
@dataclass(frozen=True)
|
|
299
347
|
class AssetRulesHaveCircularDependencyError(NeatValidationError):
|
|
300
348
|
description = "Asset rules have circular dependencies."
|