cognite-neat 0.88.2__py3-none-any.whl → 0.89.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_version.py +1 -1
- cognite/neat/constants.py +3 -0
- cognite/neat/graph/__init__.py +0 -3
- cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
- cognite/neat/graph/loaders/_base.py +3 -3
- cognite/neat/graph/loaders/_rdf2asset.py +24 -25
- cognite/neat/graph/loaders/_rdf2dms.py +20 -15
- cognite/neat/issues/__init__.py +1 -3
- cognite/neat/issues/_base.py +261 -71
- cognite/neat/issues/errors/__init__.py +73 -0
- cognite/neat/issues/errors/_external.py +67 -0
- cognite/neat/issues/errors/_general.py +35 -0
- cognite/neat/issues/errors/_properties.py +62 -0
- cognite/neat/issues/errors/_resources.py +111 -0
- cognite/neat/issues/errors/_workflow.py +36 -0
- cognite/neat/issues/formatters.py +1 -1
- cognite/neat/issues/warnings/__init__.py +66 -0
- cognite/neat/issues/warnings/_external.py +40 -0
- cognite/neat/issues/warnings/_general.py +29 -0
- cognite/neat/issues/warnings/_models.py +92 -0
- cognite/neat/issues/warnings/_properties.py +44 -0
- cognite/neat/issues/warnings/_resources.py +55 -0
- cognite/neat/issues/warnings/user_modeling.py +113 -0
- cognite/neat/rules/_shared.py +53 -2
- cognite/neat/rules/analysis/_base.py +1 -1
- cognite/neat/rules/exporters/_base.py +7 -18
- cognite/neat/rules/exporters/_rules2dms.py +17 -20
- cognite/neat/rules/exporters/_rules2excel.py +9 -16
- cognite/neat/rules/exporters/_rules2ontology.py +77 -64
- cognite/neat/rules/exporters/_rules2yaml.py +6 -9
- cognite/neat/rules/exporters/_validation.py +11 -96
- cognite/neat/rules/importers/_base.py +9 -58
- cognite/neat/rules/importers/_dms2rules.py +188 -135
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +48 -35
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +36 -45
- cognite/neat/rules/importers/_dtdl2rules/spec.py +7 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +8 -4
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +3 -3
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +18 -11
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +12 -19
- cognite/neat/rules/importers/_rdf/_inference2rules.py +14 -37
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
- cognite/neat/rules/importers/_rdf/_shared.py +4 -4
- cognite/neat/rules/importers/_spreadsheet2rules.py +46 -97
- cognite/neat/rules/importers/_yaml2rules.py +32 -58
- cognite/neat/rules/models/__init__.py +21 -5
- cognite/neat/rules/models/_base_input.py +162 -0
- cognite/neat/rules/models/{_base.py → _base_rules.py} +1 -12
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/{_types/_field.py → _types.py} +5 -10
- cognite/neat/rules/models/asset/__init__.py +5 -2
- cognite/neat/rules/models/asset/_rules.py +3 -23
- cognite/neat/rules/models/asset/_rules_input.py +40 -115
- cognite/neat/rules/models/asset/_validation.py +14 -10
- cognite/neat/rules/models/data_types.py +150 -44
- cognite/neat/rules/models/dms/__init__.py +19 -7
- cognite/neat/rules/models/dms/_exporter.py +102 -34
- cognite/neat/rules/models/dms/_rules.py +65 -162
- cognite/neat/rules/models/dms/_rules_input.py +186 -254
- cognite/neat/rules/models/dms/_schema.py +87 -78
- cognite/neat/rules/models/dms/_serializer.py +44 -3
- cognite/neat/rules/models/dms/_validation.py +106 -68
- cognite/neat/rules/models/domain.py +52 -1
- cognite/neat/rules/models/entities/__init__.py +63 -0
- cognite/neat/rules/models/entities/_constants.py +73 -0
- cognite/neat/rules/models/entities/_loaders.py +76 -0
- cognite/neat/rules/models/entities/_multi_value.py +67 -0
- cognite/neat/rules/models/{entities.py → entities/_single_value.py} +74 -232
- cognite/neat/rules/models/entities/_types.py +86 -0
- cognite/neat/rules/models/{wrapped_entities.py → entities/_wrapped.py} +1 -1
- cognite/neat/rules/models/information/__init__.py +10 -2
- cognite/neat/rules/models/information/_rules.py +10 -22
- cognite/neat/rules/models/information/_rules_input.py +57 -204
- cognite/neat/rules/models/information/_validation.py +48 -25
- cognite/neat/rules/transformers/__init__.py +21 -0
- cognite/neat/rules/transformers/_base.py +81 -0
- cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +217 -21
- cognite/neat/rules/transformers/_map_onto.py +97 -0
- cognite/neat/rules/transformers/_pipelines.py +61 -0
- cognite/neat/rules/transformers/_verification.py +136 -0
- cognite/neat/{graph/stores → store}/_provenance.py +10 -1
- cognite/neat/utils/auxiliary.py +2 -35
- cognite/neat/utils/cdf/data_classes.py +20 -0
- cognite/neat/utils/regex_patterns.py +6 -0
- cognite/neat/utils/text.py +17 -0
- cognite/neat/workflows/base.py +4 -4
- cognite/neat/workflows/cdf_store.py +3 -3
- cognite/neat/workflows/steps/data_contracts.py +1 -1
- cognite/neat/workflows/steps/lib/current/graph_extractor.py +3 -3
- cognite/neat/workflows/steps/lib/current/graph_loader.py +2 -2
- cognite/neat/workflows/steps/lib/current/graph_store.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_exporter.py +116 -47
- cognite/neat/workflows/steps/lib/current/rules_importer.py +30 -28
- cognite/neat/workflows/steps/lib/current/rules_validator.py +5 -6
- cognite/neat/workflows/steps/lib/io/io_steps.py +5 -5
- cognite/neat/workflows/steps_registry.py +4 -5
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +105 -106
- cognite/neat/exceptions.py +0 -145
- cognite/neat/graph/exceptions.py +0 -90
- cognite/neat/issues/errors/external.py +0 -21
- cognite/neat/issues/errors/properties.py +0 -75
- cognite/neat/issues/errors/resources.py +0 -123
- cognite/neat/issues/errors/schema.py +0 -0
- cognite/neat/issues/neat_warnings/__init__.py +0 -2
- cognite/neat/issues/neat_warnings/identifier.py +0 -27
- cognite/neat/issues/neat_warnings/models.py +0 -22
- cognite/neat/issues/neat_warnings/properties.py +0 -77
- cognite/neat/issues/neat_warnings/resources.py +0 -125
- cognite/neat/rules/issues/__init__.py +0 -22
- cognite/neat/rules/issues/base.py +0 -63
- cognite/neat/rules/issues/dms.py +0 -549
- cognite/neat/rules/issues/fileread.py +0 -197
- cognite/neat/rules/issues/ontology.py +0 -298
- cognite/neat/rules/issues/spreadsheet.py +0 -563
- cognite/neat/rules/issues/spreadsheet_file.py +0 -151
- cognite/neat/rules/issues/tables.py +0 -72
- cognite/neat/rules/models/_constants.py +0 -1
- cognite/neat/rules/models/_types/__init__.py +0 -19
- cognite/neat/rules/models/asset/_converter.py +0 -4
- cognite/neat/rules/models/dms/_converter.py +0 -145
- cognite/neat/workflows/_exceptions.py +0 -41
- /cognite/neat/{graph/stores → store}/__init__.py +0 -0
- /cognite/neat/{graph/stores → store}/_base.py +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from functools import total_ordering
|
|
5
|
-
from typing import Any, ClassVar
|
|
6
|
-
|
|
7
|
-
from cognite.client.data_classes import data_modeling as dm
|
|
8
|
-
from cognite.client.data_classes.data_modeling import ContainerId, ViewId
|
|
9
|
-
from pydantic_core import ErrorDetails
|
|
10
|
-
from rdflib import Namespace
|
|
11
|
-
|
|
12
|
-
from cognite.neat.issues import MultiValueError, NeatError
|
|
13
|
-
from cognite.neat.utils.spreadsheet import SpreadsheetRead
|
|
14
|
-
|
|
15
|
-
from .base import DefaultPydanticError, NeatValidationError
|
|
16
|
-
|
|
17
|
-
if sys.version_info >= (3, 11):
|
|
18
|
-
from typing import Self
|
|
19
|
-
else:
|
|
20
|
-
from typing_extensions import Self
|
|
21
|
-
|
|
22
|
-
__all__ = [
|
|
23
|
-
"InvalidSheetError",
|
|
24
|
-
"InvalidRowError",
|
|
25
|
-
"InvalidPropertyError",
|
|
26
|
-
"InvalidClassError",
|
|
27
|
-
"PrefixNamespaceCollisionError",
|
|
28
|
-
"InvalidContainerError",
|
|
29
|
-
"InvalidViewError",
|
|
30
|
-
"InvalidRowUnknownSheetError",
|
|
31
|
-
"NonExistingContainerError",
|
|
32
|
-
"NonExistingViewError",
|
|
33
|
-
"ClassNoPropertiesNoParentError",
|
|
34
|
-
"InconsistentContainerDefinitionError",
|
|
35
|
-
"MultiValueTypeError",
|
|
36
|
-
"MultiValueIsListError",
|
|
37
|
-
"MultiNullableError",
|
|
38
|
-
"MultiDefaultError",
|
|
39
|
-
"MultiIndexError",
|
|
40
|
-
"MultiUniqueConstraintError",
|
|
41
|
-
"RegexViolationError",
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@dataclass(frozen=True)
|
|
46
|
-
class InvalidSheetError(NeatValidationError, ABC):
|
|
47
|
-
@classmethod
|
|
48
|
-
@abstractmethod
|
|
49
|
-
def from_pydantic_error(
|
|
50
|
-
cls,
|
|
51
|
-
error: ErrorDetails,
|
|
52
|
-
read_info_by_sheet: dict[str, SpreadsheetRead] | None = None,
|
|
53
|
-
) -> Self:
|
|
54
|
-
raise NotImplementedError
|
|
55
|
-
|
|
56
|
-
@classmethod
|
|
57
|
-
def from_pydantic_errors(
|
|
58
|
-
cls,
|
|
59
|
-
errors: list[ErrorDetails],
|
|
60
|
-
read_info_by_sheet: dict[str, SpreadsheetRead] | None = None,
|
|
61
|
-
**kwargs: Any,
|
|
62
|
-
) -> "list[NeatError]":
|
|
63
|
-
output: list[NeatError] = []
|
|
64
|
-
for error in errors:
|
|
65
|
-
if raised_error := error.get("ctx", {}).get("error"):
|
|
66
|
-
if isinstance(raised_error, MultiValueError):
|
|
67
|
-
for caught_error in raised_error.errors:
|
|
68
|
-
reader = (read_info_by_sheet or {}).get("Properties", SpreadsheetRead())
|
|
69
|
-
if isinstance(caught_error, InconsistentContainerDefinitionError):
|
|
70
|
-
row_numbers = list(caught_error.row_numbers)
|
|
71
|
-
# The Error classes are immutable, so we have to reuse the set.
|
|
72
|
-
caught_error.row_numbers.clear()
|
|
73
|
-
for row_no in row_numbers:
|
|
74
|
-
# Adjusting the row number to the actual row number in the spreadsheet
|
|
75
|
-
caught_error.row_numbers.add(reader.adjusted_row_number(row_no))
|
|
76
|
-
if isinstance(caught_error, InvalidRowError):
|
|
77
|
-
# Adjusting the row number to the actual row number in the spreadsheet
|
|
78
|
-
new_row = reader.adjusted_row_number(caught_error.row)
|
|
79
|
-
# The error is frozen, so we have to use __setattr__ to change the row number
|
|
80
|
-
object.__setattr__(caught_error, "row", new_row)
|
|
81
|
-
output.append(caught_error) # type: ignore[arg-type]
|
|
82
|
-
continue
|
|
83
|
-
|
|
84
|
-
if len(error["loc"]) >= 4:
|
|
85
|
-
sheet_name, *_ = error["loc"]
|
|
86
|
-
error_cls = _INVALID_ROW_ERROR_BY_SHEET_NAME.get(str(sheet_name), InvalidRowUnknownSheetError)
|
|
87
|
-
output.append(error_cls.from_pydantic_error(error, read_info_by_sheet))
|
|
88
|
-
continue
|
|
89
|
-
|
|
90
|
-
output.append(DefaultPydanticError.from_pydantic_error(error))
|
|
91
|
-
return output
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
@dataclass(frozen=True)
|
|
95
|
-
@total_ordering
|
|
96
|
-
class InvalidRowError(InvalidSheetError, ABC):
|
|
97
|
-
description: ClassVar[str] = "This is a generic class for all invalid row specifications."
|
|
98
|
-
fix: ClassVar[str] = "Follow the instruction in the error message."
|
|
99
|
-
sheet_name: ClassVar[str]
|
|
100
|
-
|
|
101
|
-
column: str
|
|
102
|
-
row: int
|
|
103
|
-
type: str
|
|
104
|
-
msg: str
|
|
105
|
-
input: Any
|
|
106
|
-
url: str | None
|
|
107
|
-
|
|
108
|
-
def __lt__(self, other: object) -> bool:
|
|
109
|
-
if not isinstance(other, InvalidRowError):
|
|
110
|
-
return NotImplemented
|
|
111
|
-
return (self.sheet_name, self.row, self.column) < (
|
|
112
|
-
other.sheet_name,
|
|
113
|
-
other.row,
|
|
114
|
-
other.column,
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
def __eq__(self, other: object) -> bool:
|
|
118
|
-
if not isinstance(other, InvalidRowError):
|
|
119
|
-
return NotImplemented
|
|
120
|
-
return (self.sheet_name, self.row, self.column) == (
|
|
121
|
-
other.sheet_name,
|
|
122
|
-
other.row,
|
|
123
|
-
other.column,
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
@classmethod
|
|
127
|
-
def from_pydantic_error(
|
|
128
|
-
cls,
|
|
129
|
-
error: ErrorDetails,
|
|
130
|
-
read_info_by_sheet: dict[str, SpreadsheetRead] | None = None,
|
|
131
|
-
) -> Self:
|
|
132
|
-
sheet_name, _, row, column, *__ = error["loc"]
|
|
133
|
-
reader = (read_info_by_sheet or {}).get(str(sheet_name), SpreadsheetRead())
|
|
134
|
-
return cls(
|
|
135
|
-
column=str(column),
|
|
136
|
-
row=reader.adjusted_row_number(int(row)),
|
|
137
|
-
type=error["type"],
|
|
138
|
-
msg=error["msg"],
|
|
139
|
-
input=error.get("input"),
|
|
140
|
-
url=str(url) if (url := error.get("url")) else None,
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
def dump(self) -> dict[str, Any]:
|
|
144
|
-
output = super().dump()
|
|
145
|
-
output["sheet_name"] = self.sheet_name
|
|
146
|
-
output["column"] = self.column
|
|
147
|
-
output["row"] = self.row
|
|
148
|
-
output["type"] = self.type
|
|
149
|
-
output["msg"] = self.msg
|
|
150
|
-
output["input"] = self.input
|
|
151
|
-
output["url"] = self.url
|
|
152
|
-
return output
|
|
153
|
-
|
|
154
|
-
def message(self) -> str:
|
|
155
|
-
input_str = str(self.input) if self.input is not None else ""
|
|
156
|
-
input_str = input_str[:50] + "..." if len(input_str) > 50 else input_str
|
|
157
|
-
output = (
|
|
158
|
-
f"In {self.sheet_name}, row={self.row}, column={self.column}: {self.msg}. "
|
|
159
|
-
f"[type={self.type}, input_value={input_str}]"
|
|
160
|
-
)
|
|
161
|
-
if self.url:
|
|
162
|
-
output += f" For further information visit {self.url}"
|
|
163
|
-
return output
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
@dataclass(frozen=True)
|
|
167
|
-
class InvalidPropertyError(InvalidRowError):
|
|
168
|
-
sheet_name = "Properties"
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
@dataclass(frozen=True)
|
|
172
|
-
class InvalidClassError(InvalidRowError):
|
|
173
|
-
sheet_name = "Classes"
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
@dataclass(frozen=True)
|
|
177
|
-
class InvalidContainerError(InvalidRowError):
|
|
178
|
-
sheet_name = "Containers"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
@dataclass(frozen=True)
|
|
182
|
-
class InvalidViewError(InvalidRowError):
|
|
183
|
-
sheet_name = "Views"
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
@dataclass(frozen=True)
|
|
187
|
-
class InvalidRowUnknownSheetError(InvalidRowError):
|
|
188
|
-
sheet_name = "Unknown"
|
|
189
|
-
|
|
190
|
-
actual_sheet_name: str
|
|
191
|
-
|
|
192
|
-
@classmethod
|
|
193
|
-
def from_pydantic_error(
|
|
194
|
-
cls,
|
|
195
|
-
error: ErrorDetails,
|
|
196
|
-
read_info_by_sheet: dict[str, SpreadsheetRead] | None = None,
|
|
197
|
-
) -> Self:
|
|
198
|
-
sheet_name, _, row, column, *__ = error["loc"]
|
|
199
|
-
reader = (read_info_by_sheet or {}).get(str(sheet_name), SpreadsheetRead())
|
|
200
|
-
try:
|
|
201
|
-
return cls(
|
|
202
|
-
column=str(column),
|
|
203
|
-
row=reader.adjusted_row_number(int(row)),
|
|
204
|
-
actual_sheet_name=str(sheet_name),
|
|
205
|
-
type=error["type"],
|
|
206
|
-
msg=error["msg"],
|
|
207
|
-
input=error.get("input"),
|
|
208
|
-
url=str(url) if (url := error.get("url")) else None,
|
|
209
|
-
)
|
|
210
|
-
except ValueError:
|
|
211
|
-
return DefaultPydanticError.from_pydantic_error(error) # type: ignore[return-value]
|
|
212
|
-
|
|
213
|
-
def dump(self) -> dict[str, Any]:
|
|
214
|
-
output = super().dump()
|
|
215
|
-
output["actual_sheet_name"] = self.actual_sheet_name
|
|
216
|
-
return output
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
_INVALID_ROW_ERROR_BY_SHEET_NAME = {
|
|
220
|
-
cls_.sheet_name: cls_ for cls_ in InvalidRowError.__subclasses__() if cls_ is not InvalidRowError
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
@dataclass(frozen=True)
|
|
225
|
-
class NonExistingContainerError(InvalidPropertyError):
|
|
226
|
-
description = "The container referenced by the property is missing in the container sheet"
|
|
227
|
-
fix = "Add the container to the container sheet"
|
|
228
|
-
|
|
229
|
-
container_id: ContainerId
|
|
230
|
-
|
|
231
|
-
def message(self) -> str:
|
|
232
|
-
return (
|
|
233
|
-
f"In {self.sheet_name}, row={self.row}, column={self.column}: The container with "
|
|
234
|
-
f"id {self.container_id} is missing in the container sheet."
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
def dump(self) -> dict[str, Any]:
|
|
238
|
-
output = super().dump()
|
|
239
|
-
output["container_id"] = self.container_id
|
|
240
|
-
return output
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
@dataclass(frozen=True)
|
|
244
|
-
class NonExistingViewError(InvalidPropertyError):
|
|
245
|
-
description = "The view referenced by the property is missing in the view sheet"
|
|
246
|
-
fix = "Add the view to the view sheet"
|
|
247
|
-
|
|
248
|
-
view_id: ViewId
|
|
249
|
-
|
|
250
|
-
def message(self) -> str:
|
|
251
|
-
return (
|
|
252
|
-
f"In {self.sheet_name}, row={self.row}, column={self.column}: The view with "
|
|
253
|
-
f"id {self.view_id} is missing in the view sheet."
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
def dump(self) -> dict[str, Any]:
|
|
257
|
-
output = super().dump()
|
|
258
|
-
output["view_id"] = self.view_id
|
|
259
|
-
return output
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
@dataclass(frozen=True)
|
|
263
|
-
class PropertiesDefinedForUndefinedClassesError(NeatValidationError):
|
|
264
|
-
description = "Properties are defined for undefined classes."
|
|
265
|
-
fix = "Make sure to define class in the Classes sheet."
|
|
266
|
-
|
|
267
|
-
classes: list[str]
|
|
268
|
-
|
|
269
|
-
def dump(self) -> dict[str, list[str]]:
|
|
270
|
-
output = super().dump()
|
|
271
|
-
output["classes"] = self.classes
|
|
272
|
-
return output
|
|
273
|
-
|
|
274
|
-
def message(self) -> str:
|
|
275
|
-
return (
|
|
276
|
-
f"Classes {', '.join(self.classes)} have properties assigned to them, but"
|
|
277
|
-
" they are not defined in the Classes sheet."
|
|
278
|
-
)
|
|
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
|
-
|
|
299
|
-
@dataclass(frozen=True)
|
|
300
|
-
class ClassNoPropertiesNoParentError(NeatValidationError):
|
|
301
|
-
description = "Class has no properties and no parents."
|
|
302
|
-
fix = "Check if the class should have properties or parents."
|
|
303
|
-
|
|
304
|
-
classes: list[str]
|
|
305
|
-
|
|
306
|
-
def dump(self) -> dict[str, list[str]]:
|
|
307
|
-
output = super().dump()
|
|
308
|
-
output["classes"] = self.classes
|
|
309
|
-
return output
|
|
310
|
-
|
|
311
|
-
def message(self) -> str:
|
|
312
|
-
if len(self.classes) > 1:
|
|
313
|
-
return f"Classes {', '.join(self.classes)} have no direct or inherited properties. This may be a mistake."
|
|
314
|
-
return f"Class {self.classes[0]} have no direct or inherited properties. This may be a mistake."
|
|
315
|
-
|
|
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
|
-
|
|
346
|
-
@dataclass(frozen=True)
|
|
347
|
-
class AssetRulesHaveCircularDependencyError(NeatValidationError):
|
|
348
|
-
description = "Asset rules have circular dependencies."
|
|
349
|
-
fix = "Linking between classes via property that maps to parent_external_id must yield hierarchy structure."
|
|
350
|
-
|
|
351
|
-
classes: list[str]
|
|
352
|
-
|
|
353
|
-
def dump(self) -> dict[str, list[tuple[str, str]]]:
|
|
354
|
-
output = super().dump()
|
|
355
|
-
output["classes"] = self.classes
|
|
356
|
-
return output
|
|
357
|
-
|
|
358
|
-
def message(self) -> str:
|
|
359
|
-
return f"Asset rules have circular dependencies between classes {', '.join(self.classes)}."
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
@dataclass(frozen=True)
|
|
363
|
-
class AssetParentPropertyPointsToDataValueTypeError(NeatValidationError):
|
|
364
|
-
description = "Parent property points to a data value type instead of a class."
|
|
365
|
-
fix = "Make sure that the parent property points to a class."
|
|
366
|
-
|
|
367
|
-
class_property_with_data_value_type: list[tuple[str, str]]
|
|
368
|
-
|
|
369
|
-
def dump(self) -> dict[str, list[tuple[str, str]]]:
|
|
370
|
-
output = super().dump()
|
|
371
|
-
output["class_property"] = self.class_property_with_data_value_type
|
|
372
|
-
return output
|
|
373
|
-
|
|
374
|
-
def message(self) -> str:
|
|
375
|
-
text = [
|
|
376
|
-
f"class {class_} property {property_}" for class_, property_ in self.class_property_with_data_value_type
|
|
377
|
-
]
|
|
378
|
-
return f"Following {', and'.join(text)} point to data value type instead to classes. This is a mistake."
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
@dataclass(frozen=True)
|
|
382
|
-
class ParentClassesNotDefinedError(NeatValidationError):
|
|
383
|
-
description = "Parent classes are not defined."
|
|
384
|
-
fix = "Check if the parent classes are defined in Classes sheet."
|
|
385
|
-
|
|
386
|
-
classes: list[str]
|
|
387
|
-
|
|
388
|
-
def dump(self) -> dict[str, list[str]]:
|
|
389
|
-
output = super().dump()
|
|
390
|
-
output["classes"] = self.classes
|
|
391
|
-
return output
|
|
392
|
-
|
|
393
|
-
def message(self) -> str:
|
|
394
|
-
if len(self.classes) > 1:
|
|
395
|
-
return f"Parent classes {', '.join(self.classes)} are not defined. This may be a mistake."
|
|
396
|
-
return f"Parent classes {', '.join(self.classes[0])} are not defined. This may be a mistake."
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
@dataclass(frozen=True)
|
|
400
|
-
class PrefixNamespaceCollisionError(NeatValidationError):
|
|
401
|
-
description = "Same namespaces are assigned to different prefixes."
|
|
402
|
-
fix = "Make sure that each unique namespace is assigned to a unique prefix"
|
|
403
|
-
|
|
404
|
-
namespaces: list[Namespace]
|
|
405
|
-
prefixes: list[str]
|
|
406
|
-
|
|
407
|
-
def dump(self) -> dict[str, list[str]]:
|
|
408
|
-
output = super().dump()
|
|
409
|
-
output["prefixes"] = self.prefixes
|
|
410
|
-
output["namespaces"] = self.namespaces
|
|
411
|
-
return output
|
|
412
|
-
|
|
413
|
-
def message(self) -> str:
|
|
414
|
-
return (
|
|
415
|
-
f"Namespaces {', '.join(self.namespaces)} are assigned multiple times."
|
|
416
|
-
f" Impacted prefixes: {', '.join(self.prefixes)}."
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
@dataclass(frozen=True)
|
|
421
|
-
class ValueTypeNotDefinedError(NeatValidationError):
|
|
422
|
-
description = "Value types referred by properties are not defined in Rules."
|
|
423
|
-
fix = "Make sure that all value types are defined in Rules."
|
|
424
|
-
|
|
425
|
-
value_types: list[str]
|
|
426
|
-
|
|
427
|
-
def dump(self) -> dict[str, list[str]]:
|
|
428
|
-
output = super().dump()
|
|
429
|
-
output["classes"] = self.value_types
|
|
430
|
-
return output
|
|
431
|
-
|
|
432
|
-
def message(self) -> str:
|
|
433
|
-
if len(self.value_types) > 1:
|
|
434
|
-
return f"Value types {', '.join(self.value_types)} are not defined. This may be a mistake."
|
|
435
|
-
return f"Value types {', '.join(self.value_types[0])} are not defined. This may be a mistake."
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
@dataclass(frozen=True)
|
|
439
|
-
class InconsistentContainerDefinitionError(NeatValidationError, ABC):
|
|
440
|
-
description = "This is a base class for all errors related to inconsistent container definitions"
|
|
441
|
-
fix = "Ensure all properties using the same container have the same type, constraints, and indexes."
|
|
442
|
-
container: dm.ContainerId
|
|
443
|
-
property_name: str
|
|
444
|
-
row_numbers: set[int]
|
|
445
|
-
|
|
446
|
-
def dump(self) -> dict[str, Any]:
|
|
447
|
-
output = super().dump()
|
|
448
|
-
output.update(
|
|
449
|
-
{
|
|
450
|
-
"container": self.container.dump(),
|
|
451
|
-
"property_name": self.property_name,
|
|
452
|
-
"row_numbers": sorted(self.row_numbers),
|
|
453
|
-
}
|
|
454
|
-
)
|
|
455
|
-
return output
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
@dataclass(frozen=True)
|
|
459
|
-
class MultiValueTypeError(InconsistentContainerDefinitionError):
|
|
460
|
-
description = "The property has multiple value types"
|
|
461
|
-
fix = "Use the same value type for all properties using the same container."
|
|
462
|
-
value_types: set[str]
|
|
463
|
-
|
|
464
|
-
def message(self) -> str:
|
|
465
|
-
return (
|
|
466
|
-
f"{self.container}.{self.property_name} defined in rows: {sorted(self.row_numbers)} "
|
|
467
|
-
f"has different value types: {self.value_types}"
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
def dump(self) -> dict[str, Any]:
|
|
471
|
-
output = super().dump()
|
|
472
|
-
output["value_types"] = sorted(self.value_types)
|
|
473
|
-
return output
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
@dataclass(frozen=True)
|
|
477
|
-
class MultiValueIsListError(InconsistentContainerDefinitionError):
|
|
478
|
-
description = "The property has multiple list definitions"
|
|
479
|
-
fix = "Use the same list definition for all properties using the same container."
|
|
480
|
-
list_definitions: set[bool]
|
|
481
|
-
|
|
482
|
-
def message(self) -> str:
|
|
483
|
-
return (
|
|
484
|
-
f"{self.container}.{self.property_name} defined in rows: {sorted(self.row_numbers)} "
|
|
485
|
-
f"has different list definitions: {self.list_definitions}"
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
def dump(self) -> dict[str, Any]:
|
|
489
|
-
output = super().dump()
|
|
490
|
-
output["list_definitions"] = sorted(self.list_definitions)
|
|
491
|
-
return output
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
@dataclass(frozen=True)
|
|
495
|
-
class MultiNullableError(InconsistentContainerDefinitionError):
|
|
496
|
-
description = "The property has multiple nullable definitions"
|
|
497
|
-
fix = "Use the same nullable definition for all properties using the same container."
|
|
498
|
-
nullable_definitions: set[bool]
|
|
499
|
-
|
|
500
|
-
def message(self) -> str:
|
|
501
|
-
return (
|
|
502
|
-
f"{self.container}.{self.property_name} defined in rows: {sorted(self.row_numbers)} "
|
|
503
|
-
f"has different nullable definitions: {self.nullable_definitions}"
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
def dump(self) -> dict[str, Any]:
|
|
507
|
-
output = super().dump()
|
|
508
|
-
output["nullable_definitions"] = sorted(self.nullable_definitions)
|
|
509
|
-
return output
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
@dataclass(frozen=True)
|
|
513
|
-
class MultiDefaultError(InconsistentContainerDefinitionError):
|
|
514
|
-
description = "The property has multiple default definitions"
|
|
515
|
-
fix = "Use the same default definition for all properties using the same container."
|
|
516
|
-
default_definitions: list[str | int | dict | None]
|
|
517
|
-
|
|
518
|
-
def message(self) -> str:
|
|
519
|
-
return (
|
|
520
|
-
f"{self.container}.{self.property_name} defined in rows: {sorted(self.row_numbers)} "
|
|
521
|
-
f"has different default definitions: {self.default_definitions}"
|
|
522
|
-
)
|
|
523
|
-
|
|
524
|
-
def dump(self) -> dict[str, Any]:
|
|
525
|
-
output = super().dump()
|
|
526
|
-
output["default_definitions"] = self.default_definitions
|
|
527
|
-
return output
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
@dataclass(frozen=True)
|
|
531
|
-
class MultiIndexError(InconsistentContainerDefinitionError):
|
|
532
|
-
description = "The property has multiple index definitions"
|
|
533
|
-
fix = "Use the same index definition for all properties using the same container."
|
|
534
|
-
index_definitions: set[str]
|
|
535
|
-
|
|
536
|
-
def message(self) -> str:
|
|
537
|
-
return (
|
|
538
|
-
f"{self.container}.{self.property_name} defined in rows: {sorted(self.row_numbers)} "
|
|
539
|
-
f"has different index definitions: {self.index_definitions}"
|
|
540
|
-
)
|
|
541
|
-
|
|
542
|
-
def dump(self) -> dict[str, Any]:
|
|
543
|
-
output = super().dump()
|
|
544
|
-
output["index_definitions"] = sorted(self.index_definitions)
|
|
545
|
-
return output
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
@dataclass(frozen=True)
|
|
549
|
-
class MultiUniqueConstraintError(InconsistentContainerDefinitionError):
|
|
550
|
-
description = "The property has multiple unique constraint definitions"
|
|
551
|
-
fix = "Use the same unique constraint definition for all properties using the same container."
|
|
552
|
-
unique_constraint_definitions: set[str]
|
|
553
|
-
|
|
554
|
-
def message(self) -> str:
|
|
555
|
-
return (
|
|
556
|
-
f"{self.container}.{self.property_name} defined in rows: {sorted(self.row_numbers)} "
|
|
557
|
-
f"has different unique constraint definitions: {self.unique_constraint_definitions}"
|
|
558
|
-
)
|
|
559
|
-
|
|
560
|
-
def dump(self) -> dict[str, Any]:
|
|
561
|
-
output = super().dump()
|
|
562
|
-
output["unique_constraint_definitions"] = sorted(self.unique_constraint_definitions)
|
|
563
|
-
return output
|