cognite-neat 0.109.2__py3-none-any.whl → 0.109.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cognite/neat/_alpha.py +1 -0
- cognite/neat/_client/testing.py +1 -1
- cognite/neat/_issues/_base.py +33 -9
- cognite/neat/_issues/errors/__init__.py +2 -10
- cognite/neat/_issues/errors/_general.py +1 -1
- cognite/neat/_issues/errors/_wrapper.py +11 -0
- cognite/neat/_rules/models/_rdfpath.py +2 -0
- cognite/neat/_rules/models/_types.py +4 -2
- cognite/neat/_rules/models/dms/_rules.py +0 -36
- cognite/neat/_rules/models/entities/_constants.py +3 -0
- cognite/neat/_rules/models/entities/_single_value.py +6 -1
- cognite/neat/_rules/transformers/__init__.py +4 -0
- cognite/neat/_rules/transformers/_converters.py +221 -15
- cognite/neat/_session/_base.py +7 -0
- cognite/neat/_session/_create.py +46 -12
- cognite/neat/_session/_prepare.py +11 -3
- cognite/neat/_session/_state.py +1 -1
- cognite/neat/_session/_to.py +3 -3
- cognite/neat/_utils/text.py +40 -9
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.109.2.dist-info → cognite_neat-0.109.3.dist-info}/METADATA +1 -1
- {cognite_neat-0.109.2.dist-info → cognite_neat-0.109.3.dist-info}/RECORD +25 -25
- cognite/neat/_issues/errors/_workflow.py +0 -36
- {cognite_neat-0.109.2.dist-info → cognite_neat-0.109.3.dist-info}/LICENSE +0 -0
- {cognite_neat-0.109.2.dist-info → cognite_neat-0.109.3.dist-info}/WHEEL +0 -0
- {cognite_neat-0.109.2.dist-info → cognite_neat-0.109.3.dist-info}/entry_points.txt +0 -0
cognite/neat/_alpha.py
CHANGED
cognite/neat/_client/testing.py
CHANGED
|
@@ -19,7 +19,7 @@ class NeatClientMock(CogniteClientMock):
|
|
|
19
19
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
20
20
|
if "parent" in kwargs:
|
|
21
21
|
super().__init__(*args, **kwargs)
|
|
22
|
-
return
|
|
22
|
+
return
|
|
23
23
|
super().__init__(*args, **kwargs)
|
|
24
24
|
self.schema = SchemaAPI(self)
|
|
25
25
|
self.loaders = DataModelLoaderAPI(self)
|
cognite/neat/_issues/_base.py
CHANGED
|
@@ -103,6 +103,8 @@ class NeatIssue:
|
|
|
103
103
|
variables[name] = var_.as_posix()
|
|
104
104
|
elif isinstance(var_, Collection):
|
|
105
105
|
variables[name] = humanize_collection(var_)
|
|
106
|
+
elif isinstance(var_, NeatError):
|
|
107
|
+
variables[name] = var_.as_message(include_type=False)
|
|
106
108
|
else:
|
|
107
109
|
variables[name] = repr(var_)
|
|
108
110
|
return variables, has_all_optional
|
|
@@ -138,6 +140,8 @@ class NeatIssue:
|
|
|
138
140
|
return value.dump(camel_case=True)
|
|
139
141
|
elif isinstance(value, DataModelId):
|
|
140
142
|
return value.dump(camel_case=True, include_type=False)
|
|
143
|
+
elif isinstance(value, NeatError):
|
|
144
|
+
return value.dump()
|
|
141
145
|
raise ValueError(f"Unsupported type: {type(value)}")
|
|
142
146
|
|
|
143
147
|
@classmethod
|
|
@@ -192,6 +196,8 @@ class NeatIssue:
|
|
|
192
196
|
return ContainerId.load(value)
|
|
193
197
|
elif inspect.isclass(type_) and issubclass(type_, Entity):
|
|
194
198
|
return type_.load(value)
|
|
199
|
+
elif type_ is NeatError:
|
|
200
|
+
return cls.load(value)
|
|
195
201
|
return value
|
|
196
202
|
|
|
197
203
|
def __lt__(self, other: "NeatIssue") -> bool:
|
|
@@ -227,22 +233,19 @@ class NeatError(NeatIssue, Exception):
|
|
|
227
233
|
# Skip the error for SheetList, as it is not relevant for the user. This is an
|
|
228
234
|
# internal class used to have helper methods for a lists as .to_pandas()
|
|
229
235
|
continue
|
|
236
|
+
|
|
230
237
|
neat_error: NeatError | None = None
|
|
231
238
|
if isinstance(error, dict) and isinstance(ctx := error.get("ctx"), dict) and "error" in ctx:
|
|
232
239
|
neat_error = ctx["error"]
|
|
233
240
|
elif isinstance(error, NeatError | MultiValueError):
|
|
234
241
|
neat_error = error
|
|
235
242
|
|
|
243
|
+
loc = error["loc"] if isinstance(error, dict) else tuple()
|
|
236
244
|
if isinstance(neat_error, MultiValueError):
|
|
237
|
-
|
|
238
|
-
for caught_error in neat_error.errors:
|
|
239
|
-
cls._adjust_row_numbers(caught_error, read_info_by_sheet) # type: ignore[arg-type]
|
|
240
|
-
all_errors.extend(neat_error.errors) # type: ignore[arg-type]
|
|
245
|
+
all_errors.extend([cls._adjust_error(e, loc, read_info_by_sheet) for e in neat_error.errors])
|
|
241
246
|
elif isinstance(neat_error, NeatError):
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
all_errors.append(neat_error)
|
|
245
|
-
elif isinstance(error, dict) and len(error["loc"]) >= 4 and read_info_by_sheet:
|
|
247
|
+
all_errors.append(cls._adjust_error(neat_error, loc, read_info_by_sheet))
|
|
248
|
+
elif isinstance(error, dict) and len(loc) >= 4 and read_info_by_sheet:
|
|
246
249
|
all_errors.append(RowError.from_pydantic_error(error, read_info_by_sheet))
|
|
247
250
|
elif isinstance(error, dict):
|
|
248
251
|
all_errors.append(DefaultPydanticError.from_pydantic_error(error))
|
|
@@ -251,6 +254,18 @@ class NeatError(NeatIssue, Exception):
|
|
|
251
254
|
raise ValueError(f"Unsupported error type: {error}")
|
|
252
255
|
return all_errors
|
|
253
256
|
|
|
257
|
+
@classmethod
|
|
258
|
+
def _adjust_error(
|
|
259
|
+
cls, error: "NeatError", loc: tuple[str | int, ...], read_info_by_sheet: dict[str, SpreadsheetRead] | None
|
|
260
|
+
) -> "NeatError":
|
|
261
|
+
from .errors._wrapper import MetadataValueError
|
|
262
|
+
|
|
263
|
+
if read_info_by_sheet:
|
|
264
|
+
cls._adjust_row_numbers(error, read_info_by_sheet)
|
|
265
|
+
if len(loc) == 2 and isinstance(loc[0], str) and loc[0].casefold() == "metadata":
|
|
266
|
+
return MetadataValueError(field_name=str(loc[1]), error=error)
|
|
267
|
+
return error
|
|
268
|
+
|
|
254
269
|
@staticmethod
|
|
255
270
|
def _adjust_row_numbers(caught_error: "NeatError", read_info_by_sheet: dict[str, SpreadsheetRead]) -> None:
|
|
256
271
|
from cognite.neat._issues.errors._properties import PropertyDefinitionDuplicatedError
|
|
@@ -288,7 +303,16 @@ class DefaultPydanticError(NeatError, ValueError):
|
|
|
288
303
|
msg: str
|
|
289
304
|
|
|
290
305
|
@classmethod
|
|
291
|
-
def from_pydantic_error(cls, error: ErrorDetails) -> "
|
|
306
|
+
def from_pydantic_error(cls, error: ErrorDetails) -> "NeatError":
|
|
307
|
+
loc = error["loc"]
|
|
308
|
+
if len(loc) >= 2 and isinstance(loc[0], str) and loc[0].casefold() == "metadata":
|
|
309
|
+
from .errors._general import NeatValueError
|
|
310
|
+
from .errors._wrapper import MetadataValueError
|
|
311
|
+
|
|
312
|
+
return MetadataValueError(
|
|
313
|
+
field_name=str(loc[1]), error=NeatValueError(f"{error['msg']} got '{error['input']}'")
|
|
314
|
+
)
|
|
315
|
+
|
|
292
316
|
return cls(
|
|
293
317
|
type=error["type"],
|
|
294
318
|
loc=error["loc"],
|
|
@@ -31,12 +31,7 @@ from ._resources import (
|
|
|
31
31
|
ResourceNotFoundError,
|
|
32
32
|
ResourceRetrievalError,
|
|
33
33
|
)
|
|
34
|
-
from .
|
|
35
|
-
WorkflowConfigurationNotSetError,
|
|
36
|
-
WorkFlowMissingDataError,
|
|
37
|
-
WorkflowStepNotInitializedError,
|
|
38
|
-
WorkflowStepOutputError,
|
|
39
|
-
)
|
|
34
|
+
from ._wrapper import MetadataValueError
|
|
40
35
|
|
|
41
36
|
__all__ = [
|
|
42
37
|
"AuthorizationError",
|
|
@@ -47,6 +42,7 @@ __all__ = [
|
|
|
47
42
|
"FileNotFoundNeatError",
|
|
48
43
|
"FileReadError",
|
|
49
44
|
"FileTypeUnexpectedError",
|
|
45
|
+
"MetadataValueError",
|
|
50
46
|
"NeatError",
|
|
51
47
|
"NeatImportError",
|
|
52
48
|
"NeatTypeError",
|
|
@@ -70,10 +66,6 @@ __all__ = [
|
|
|
70
66
|
"ResourceRetrievalError",
|
|
71
67
|
"ReversedConnectionNotFeasibleError",
|
|
72
68
|
"RowError",
|
|
73
|
-
"WorkFlowMissingDataError",
|
|
74
|
-
"WorkflowConfigurationNotSetError",
|
|
75
|
-
"WorkflowStepNotInitializedError",
|
|
76
|
-
"WorkflowStepOutputError",
|
|
77
69
|
]
|
|
78
70
|
|
|
79
71
|
_NEAT_ERRORS_BY_NAME = {error.__name__: error for error in _get_subclasses(NeatError, include_base=True)}
|
|
@@ -19,7 +19,7 @@ class NeatTypeError(NeatError, TypeError):
|
|
|
19
19
|
|
|
20
20
|
@dataclass(unsafe_hash=True)
|
|
21
21
|
class RegexViolationError(NeatError, ValueError):
|
|
22
|
-
"""
|
|
22
|
+
"""The value '{value}' failed regex, {regex}, validation in {location}.
|
|
23
23
|
Make sure that the name follows the regex pattern."""
|
|
24
24
|
|
|
25
25
|
value: str
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
import sys
|
|
5
|
+
import urllib.parse
|
|
5
6
|
from collections import Counter
|
|
6
7
|
from functools import total_ordering
|
|
7
8
|
from typing import ClassVar, Literal
|
|
@@ -327,6 +328,7 @@ def parse_table_lookup(raw: str) -> TableLookup:
|
|
|
327
328
|
|
|
328
329
|
|
|
329
330
|
def parse_rule(rule_raw: str, rule_type: TransformationRuleType | None) -> RDFPath:
|
|
331
|
+
rule_raw = urllib.parse.unquote(rule_raw)
|
|
330
332
|
match rule_type:
|
|
331
333
|
case TransformationRuleType.rdfpath:
|
|
332
334
|
rule_raw = rule_raw.replace(" ", "")
|
|
@@ -38,7 +38,9 @@ def _custom_error(exc_factory: Callable[[str | None, Exception], Any]) -> Any:
|
|
|
38
38
|
def _validator(value: Any, next_: Any, ctx: ValidationInfo) -> Any:
|
|
39
39
|
try:
|
|
40
40
|
return next_(value, ctx)
|
|
41
|
-
except
|
|
41
|
+
except ValueError as e:
|
|
42
|
+
if value is None:
|
|
43
|
+
raise e
|
|
42
44
|
raise exc_factory(ctx.field_name, value) from None
|
|
43
45
|
|
|
44
46
|
return WrapValidator(_validator)
|
|
@@ -130,7 +132,7 @@ def _external_id_validation_factory(entity_type: EntityTypes, location: str):
|
|
|
130
132
|
|
|
131
133
|
SpaceType = Annotated[
|
|
132
134
|
str,
|
|
133
|
-
AfterValidator(_external_id_validation_factory(EntityTypes.space, "
|
|
135
|
+
AfterValidator(_external_id_validation_factory(EntityTypes.space, "")),
|
|
134
136
|
]
|
|
135
137
|
|
|
136
138
|
InformationPropertyType = Annotated[
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import warnings
|
|
2
1
|
from collections.abc import Hashable
|
|
3
2
|
from typing import TYPE_CHECKING, Any, ClassVar, Literal
|
|
4
3
|
|
|
@@ -8,11 +7,7 @@ from pydantic import Field, field_serializer, field_validator, model_validator
|
|
|
8
7
|
from pydantic_core.core_schema import SerializationInfo, ValidationInfo
|
|
9
8
|
|
|
10
9
|
from cognite.neat._client.data_classes.schema import DMSSchema
|
|
11
|
-
from cognite.neat._constants import COGNITE_SPACES
|
|
12
10
|
from cognite.neat._issues.errors import NeatValueError
|
|
13
|
-
from cognite.neat._issues.warnings import (
|
|
14
|
-
PrincipleMatchingSpaceAndVersionWarning,
|
|
15
|
-
)
|
|
16
11
|
from cognite.neat._rules.models._base_rules import (
|
|
17
12
|
BaseMetadata,
|
|
18
13
|
BaseRules,
|
|
@@ -407,37 +402,6 @@ class DMSRules(BaseRules):
|
|
|
407
402
|
None, alias="Nodes", description="Contains the definition of the node types."
|
|
408
403
|
)
|
|
409
404
|
|
|
410
|
-
@field_validator("views")
|
|
411
|
-
def matching_version_and_space(cls, value: SheetList[DMSView], info: ValidationInfo) -> SheetList[DMSView]:
|
|
412
|
-
if not (metadata := info.data.get("metadata")):
|
|
413
|
-
return value
|
|
414
|
-
model_version = metadata.version
|
|
415
|
-
if different_version := [
|
|
416
|
-
view.view.as_id()
|
|
417
|
-
for view in value
|
|
418
|
-
if view.view.version != model_version and view.view.space not in COGNITE_SPACES
|
|
419
|
-
]:
|
|
420
|
-
for view_id in different_version:
|
|
421
|
-
warnings.warn(
|
|
422
|
-
PrincipleMatchingSpaceAndVersionWarning(
|
|
423
|
-
f"The view {view_id!r} has a different version than the data model version, {model_version}",
|
|
424
|
-
),
|
|
425
|
-
stacklevel=2,
|
|
426
|
-
)
|
|
427
|
-
if different_space := [
|
|
428
|
-
view.view.as_id()
|
|
429
|
-
for view in value
|
|
430
|
-
if view.view.space != metadata.space and view.view.space not in COGNITE_SPACES
|
|
431
|
-
]:
|
|
432
|
-
for view_id in different_space:
|
|
433
|
-
warnings.warn(
|
|
434
|
-
PrincipleMatchingSpaceAndVersionWarning(
|
|
435
|
-
f"The view {view_id!r} is in a different space than the data model space, {metadata.space}",
|
|
436
|
-
),
|
|
437
|
-
stacklevel=2,
|
|
438
|
-
)
|
|
439
|
-
return value
|
|
440
|
-
|
|
441
405
|
@model_validator(mode="after")
|
|
442
406
|
def set_neat_id(self) -> "DMSRules":
|
|
443
407
|
namespace = self.metadata.namespace
|
|
@@ -134,7 +134,12 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
134
134
|
content = result.group("content")
|
|
135
135
|
if content is None:
|
|
136
136
|
return dict(prefix=prefix, suffix=suffix)
|
|
137
|
-
|
|
137
|
+
try:
|
|
138
|
+
extra_args = dict(
|
|
139
|
+
SPLIT_ON_EQUAL_PATTERN.split(pair.strip()) for pair in SPLIT_ON_COMMA_PATTERN.split(content)
|
|
140
|
+
)
|
|
141
|
+
except ValueError:
|
|
142
|
+
raise NeatValueError(f"Invalid {cls.type_.value} entity: {raw!r}") from None
|
|
138
143
|
expected_args = {
|
|
139
144
|
field_.alias or field_name: field_.annotation for field_name, field_ in cls.model_fields.items()
|
|
140
145
|
}
|
|
@@ -13,10 +13,12 @@ from ._converters import (
|
|
|
13
13
|
MergeInformationRules,
|
|
14
14
|
PrefixEntities,
|
|
15
15
|
SetIDDMSModel,
|
|
16
|
+
StandardizeNaming,
|
|
16
17
|
ToCompliantEntities,
|
|
17
18
|
ToDataProductModel,
|
|
18
19
|
ToEnterpriseModel,
|
|
19
20
|
ToExtensionModel,
|
|
21
|
+
ToInformationCompliantEntities,
|
|
20
22
|
ToSolutionModel,
|
|
21
23
|
)
|
|
22
24
|
from ._mapping import AsParentPropertyId, MapOneToOne, RuleMapper
|
|
@@ -40,10 +42,12 @@ __all__ = [
|
|
|
40
42
|
"RuleMapper",
|
|
41
43
|
"RulesTransformer",
|
|
42
44
|
"SetIDDMSModel",
|
|
45
|
+
"StandardizeNaming",
|
|
43
46
|
"ToCompliantEntities",
|
|
44
47
|
"ToDataProductModel",
|
|
45
48
|
"ToEnterpriseModel",
|
|
46
49
|
"ToExtensionModel",
|
|
50
|
+
"ToInformationCompliantEntities",
|
|
47
51
|
"ToSolutionModel",
|
|
48
52
|
"VerifiedRulesTransformer",
|
|
49
53
|
"VerifyAnyRules",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import re
|
|
2
|
+
import urllib.parse
|
|
2
3
|
import warnings
|
|
3
4
|
from abc import ABC
|
|
4
5
|
from collections import Counter, defaultdict
|
|
5
6
|
from collections.abc import Collection, Mapping
|
|
6
7
|
from datetime import date, datetime
|
|
8
|
+
from functools import cached_property
|
|
7
9
|
from typing import ClassVar, Literal, TypeVar, cast, overload
|
|
8
10
|
|
|
9
11
|
from cognite.client.data_classes import data_modeling as dms
|
|
@@ -23,11 +25,12 @@ from cognite.neat._constants import (
|
|
|
23
25
|
from cognite.neat._issues.errors import NeatValueError
|
|
24
26
|
from cognite.neat._issues.warnings import NeatValueWarning
|
|
25
27
|
from cognite.neat._issues.warnings._models import (
|
|
26
|
-
EnterpriseModelNotBuildOnTopOfCDMWarning,
|
|
27
28
|
SolutionModelBuildOnTopOfCDMWarning,
|
|
28
29
|
)
|
|
30
|
+
from cognite.neat._rules._constants import PATTERNS, get_reserved_words
|
|
29
31
|
from cognite.neat._rules._shared import (
|
|
30
32
|
ReadInputRules,
|
|
33
|
+
ReadRules,
|
|
31
34
|
VerifiedRules,
|
|
32
35
|
)
|
|
33
36
|
from cognite.neat._rules.analysis import DMSAnalysis
|
|
@@ -35,6 +38,7 @@ from cognite.neat._rules.importers import DMSImporter
|
|
|
35
38
|
from cognite.neat._rules.models import (
|
|
36
39
|
DMSInputRules,
|
|
37
40
|
DMSRules,
|
|
41
|
+
InformationInputRules,
|
|
38
42
|
InformationRules,
|
|
39
43
|
SheetList,
|
|
40
44
|
data_types,
|
|
@@ -56,9 +60,9 @@ from cognite.neat._rules.models.entities import (
|
|
|
56
60
|
ViewEntity,
|
|
57
61
|
)
|
|
58
62
|
from cognite.neat._rules.models.information import InformationClass, InformationMetadata, InformationProperty
|
|
59
|
-
from cognite.neat._utils.text import to_camel
|
|
63
|
+
from cognite.neat._utils.text import NamingStandardization, to_camel
|
|
60
64
|
|
|
61
|
-
from ._base import T_VerifiedIn, T_VerifiedOut, VerifiedRulesTransformer
|
|
65
|
+
from ._base import RulesTransformer, T_VerifiedIn, T_VerifiedOut, VerifiedRulesTransformer
|
|
62
66
|
from ._verification import VerifyDMSRules
|
|
63
67
|
|
|
64
68
|
T_InputInRules = TypeVar("T_InputInRules", bound=ReadInputRules)
|
|
@@ -71,6 +75,105 @@ class ConversionTransformer(VerifiedRulesTransformer[T_VerifiedIn, T_VerifiedOut
|
|
|
71
75
|
...
|
|
72
76
|
|
|
73
77
|
|
|
78
|
+
class ToInformationCompliantEntities(
|
|
79
|
+
RulesTransformer[ReadRules[InformationInputRules], ReadRules[InformationInputRules]]
|
|
80
|
+
):
|
|
81
|
+
"""Converts input rules to rules that is compliant with the Information Model.
|
|
82
|
+
|
|
83
|
+
This is typically used with importers from arbitrary sources to ensure that classes and properties have valid
|
|
84
|
+
names.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
renaming: How to handle renaming of entities that are not compliant with the Information Model.
|
|
88
|
+
- "warning": Raises a warning and renames the entity.
|
|
89
|
+
- "skip": Renames the entity without raising a warning.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(self, renaming: Literal["warning", "skip"] = "skip") -> None:
|
|
93
|
+
self._renaming = renaming
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def description(self) -> str:
|
|
97
|
+
return "Ensures that all entities are compliant with the Information Model."
|
|
98
|
+
|
|
99
|
+
def transform(self, rules: ReadRules[InformationInputRules]) -> ReadRules[InformationInputRules]:
|
|
100
|
+
if rules.rules is None:
|
|
101
|
+
return rules
|
|
102
|
+
# Doing dump to obtain a copy, and ensure that all entities are created. Input allows
|
|
103
|
+
# string for entities, the dump call will convert these to entities.
|
|
104
|
+
dumped = rules.rules.dump()
|
|
105
|
+
copy = InformationInputRules.load(dumped)
|
|
106
|
+
|
|
107
|
+
new_by_old_class_suffix: dict[str, str] = {}
|
|
108
|
+
for cls in copy.classes:
|
|
109
|
+
cls_entity = cast(ClassEntity, cls.class_) # Safe due to the dump above
|
|
110
|
+
if not PATTERNS.class_id_compliance.match(cls_entity.suffix):
|
|
111
|
+
new_suffix = self._fix_cls_suffix(cls_entity.suffix)
|
|
112
|
+
if self._renaming == "warning":
|
|
113
|
+
warnings.warn(
|
|
114
|
+
NeatValueWarning(f"Invalid class name {cls_entity.suffix!r}.Renaming to {new_suffix}"),
|
|
115
|
+
stacklevel=2,
|
|
116
|
+
)
|
|
117
|
+
cls.class_.suffix = new_suffix # type: ignore[union-attr]
|
|
118
|
+
|
|
119
|
+
for cls_ in copy.classes:
|
|
120
|
+
if cls_.implements:
|
|
121
|
+
for i, parent in enumerate(cls_.implements):
|
|
122
|
+
if isinstance(parent, ClassEntity) and parent.suffix in new_by_old_class_suffix:
|
|
123
|
+
cls_.implements[i].suffix = new_by_old_class_suffix[parent.suffix] # type: ignore[union-attr]
|
|
124
|
+
|
|
125
|
+
for prop in copy.properties:
|
|
126
|
+
if not PATTERNS.information_property_id_compliance.match(prop.property_):
|
|
127
|
+
new_property = self._fix_property(prop.property_)
|
|
128
|
+
if self._renaming == "warning":
|
|
129
|
+
warnings.warn(
|
|
130
|
+
NeatValueWarning(
|
|
131
|
+
f"Invalid property name {prop.class_.suffix}.{prop.property_!r}. Renaming to {new_property}" # type: ignore[union-attr]
|
|
132
|
+
),
|
|
133
|
+
stacklevel=2,
|
|
134
|
+
)
|
|
135
|
+
prop.property_ = new_property
|
|
136
|
+
|
|
137
|
+
if isinstance(prop.class_, ClassEntity) and prop.class_.suffix in new_by_old_class_suffix:
|
|
138
|
+
prop.class_.suffix = new_by_old_class_suffix[prop.class_.suffix]
|
|
139
|
+
|
|
140
|
+
if isinstance(prop.value_type, ClassEntity) and prop.value_type.suffix in new_by_old_class_suffix:
|
|
141
|
+
prop.value_type.suffix = new_by_old_class_suffix[prop.value_type.suffix]
|
|
142
|
+
|
|
143
|
+
if isinstance(prop.value_type, MultiValueTypeInfo):
|
|
144
|
+
for i, value_type in enumerate(prop.value_type.types):
|
|
145
|
+
if isinstance(value_type, ClassEntity) and value_type.suffix in new_by_old_class_suffix:
|
|
146
|
+
prop.value_type.types[i].suffix = new_by_old_class_suffix[value_type.suffix] # type: ignore[union-attr]
|
|
147
|
+
|
|
148
|
+
return ReadRules(rules=copy, read_context=rules.read_context)
|
|
149
|
+
|
|
150
|
+
@cached_property
|
|
151
|
+
def _reserved_class_words(self) -> set[str]:
|
|
152
|
+
return set(get_reserved_words("class"))
|
|
153
|
+
|
|
154
|
+
@cached_property
|
|
155
|
+
def _reserved_property_words(self) -> set[str]:
|
|
156
|
+
return set(get_reserved_words("property"))
|
|
157
|
+
|
|
158
|
+
def _fix_cls_suffix(self, suffix: str) -> str:
|
|
159
|
+
if suffix in self._reserved_class_words:
|
|
160
|
+
return f"My{suffix}"
|
|
161
|
+
suffix = urllib.parse.unquote(suffix)
|
|
162
|
+
suffix = NamingStandardization.standardize_class_str(suffix)
|
|
163
|
+
if len(suffix) > 252:
|
|
164
|
+
suffix = suffix[:252]
|
|
165
|
+
return suffix
|
|
166
|
+
|
|
167
|
+
def _fix_property(self, property_: str) -> str:
|
|
168
|
+
if property_ in self._reserved_property_words:
|
|
169
|
+
return f"my{property_}"
|
|
170
|
+
property_ = urllib.parse.unquote(property_)
|
|
171
|
+
property_ = NamingStandardization.standardize_property_str(property_)
|
|
172
|
+
if len(property_) > 252:
|
|
173
|
+
property_ = property_[:252]
|
|
174
|
+
return property_
|
|
175
|
+
|
|
176
|
+
|
|
74
177
|
class ToCompliantEntities(VerifiedRulesTransformer[InformationRules, InformationRules]): # type: ignore[misc]
|
|
75
178
|
"""Converts input rules to rules with compliant entity IDs that match regex patters used
|
|
76
179
|
by DMS schema components."""
|
|
@@ -246,6 +349,116 @@ class PrefixEntities(ConversionTransformer): # type: ignore[type-var]
|
|
|
246
349
|
return entity
|
|
247
350
|
|
|
248
351
|
|
|
352
|
+
class StandardizeNaming(ConversionTransformer):
|
|
353
|
+
"""Sets views/classes/container names to PascalCase and properties to camelCase."""
|
|
354
|
+
|
|
355
|
+
@property
|
|
356
|
+
def description(self) -> str:
|
|
357
|
+
return "Sets views/classes/containers names to PascalCase and properties to camelCase."
|
|
358
|
+
|
|
359
|
+
@overload
|
|
360
|
+
def transform(self, rules: DMSRules) -> DMSRules: ...
|
|
361
|
+
|
|
362
|
+
@overload
|
|
363
|
+
def transform(self, rules: InformationRules) -> InformationRules: ...
|
|
364
|
+
|
|
365
|
+
def transform(self, rules: InformationRules | DMSRules) -> InformationRules | DMSRules:
|
|
366
|
+
output = rules.model_copy(deep=True)
|
|
367
|
+
if isinstance(output, InformationRules):
|
|
368
|
+
return self._standardize_information_rules(output)
|
|
369
|
+
elif isinstance(output, DMSRules):
|
|
370
|
+
return self._standardize_dms_rules(output)
|
|
371
|
+
raise NeatValueError(f"Unsupported rules type: {type(output)}")
|
|
372
|
+
|
|
373
|
+
def _standardize_information_rules(self, rules: InformationRules) -> InformationRules:
|
|
374
|
+
new_by_old_class_suffix: dict[str, str] = {}
|
|
375
|
+
for cls in rules.classes:
|
|
376
|
+
new_suffix = NamingStandardization.standardize_class_str(cls.class_.suffix)
|
|
377
|
+
new_by_old_class_suffix[cls.class_.suffix] = new_suffix
|
|
378
|
+
cls.class_.suffix = new_suffix
|
|
379
|
+
|
|
380
|
+
for cls in rules.classes:
|
|
381
|
+
if cls.implements:
|
|
382
|
+
for i, parent in enumerate(cls.implements):
|
|
383
|
+
if parent.suffix in new_by_old_class_suffix:
|
|
384
|
+
cls.implements[i].suffix = new_by_old_class_suffix[parent.suffix]
|
|
385
|
+
|
|
386
|
+
for prop in rules.properties:
|
|
387
|
+
prop.property_ = NamingStandardization.standardize_property_str(prop.property_)
|
|
388
|
+
if prop.class_.suffix in new_by_old_class_suffix:
|
|
389
|
+
prop.class_.suffix = new_by_old_class_suffix[prop.class_.suffix]
|
|
390
|
+
|
|
391
|
+
if isinstance(prop.value_type, ClassEntity) and prop.value_type.suffix in new_by_old_class_suffix:
|
|
392
|
+
prop.value_type.suffix = new_by_old_class_suffix[prop.value_type.suffix]
|
|
393
|
+
|
|
394
|
+
if isinstance(prop.value_type, MultiValueTypeInfo):
|
|
395
|
+
for i, value_type in enumerate(prop.value_type.types):
|
|
396
|
+
if isinstance(value_type, ClassEntity) and value_type.suffix in new_by_old_class_suffix:
|
|
397
|
+
prop.value_type.types[i].suffix = new_by_old_class_suffix[value_type.suffix] # type: ignore[union-attr]
|
|
398
|
+
|
|
399
|
+
return rules
|
|
400
|
+
|
|
401
|
+
def _standardize_dms_rules(self, rules: DMSRules) -> DMSRules:
|
|
402
|
+
new_by_old_view: dict[str, str] = {}
|
|
403
|
+
for view in rules.views:
|
|
404
|
+
new_suffix = NamingStandardization.standardize_class_str(view.view.suffix)
|
|
405
|
+
new_by_old_view[view.view.suffix] = new_suffix
|
|
406
|
+
view.view.suffix = new_suffix
|
|
407
|
+
new_by_old_container: dict[str, str] = {}
|
|
408
|
+
if rules.containers:
|
|
409
|
+
for container in rules.containers:
|
|
410
|
+
new_suffix = NamingStandardization.standardize_class_str(container.container.suffix)
|
|
411
|
+
new_by_old_container[container.container.suffix] = new_suffix
|
|
412
|
+
container.container.suffix = new_suffix
|
|
413
|
+
|
|
414
|
+
for view in rules.views:
|
|
415
|
+
if view.implements:
|
|
416
|
+
for i, parent in enumerate(view.implements):
|
|
417
|
+
if parent.suffix in new_by_old_view:
|
|
418
|
+
view.implements[i].suffix = new_by_old_view[parent.suffix]
|
|
419
|
+
if view.filter_ and isinstance(view.filter_, HasDataFilter) and view.filter_.inner:
|
|
420
|
+
for i, item in enumerate(view.filter_.inner):
|
|
421
|
+
if isinstance(item, ContainerEntity) and item.suffix in new_by_old_container:
|
|
422
|
+
view.filter_.inner[i].suffix = new_by_old_container[item.suffix]
|
|
423
|
+
if isinstance(item, ViewEntity) and item.suffix in new_by_old_view:
|
|
424
|
+
view.filter_.inner[i].suffix = new_by_old_view[item.suffix]
|
|
425
|
+
if rules.containers:
|
|
426
|
+
for container in rules.containers:
|
|
427
|
+
if container.constraint:
|
|
428
|
+
for i, constraint in enumerate(container.constraint):
|
|
429
|
+
if constraint.suffix in new_by_old_container:
|
|
430
|
+
container.constraint[i].suffix = new_by_old_container[constraint.suffix]
|
|
431
|
+
new_property_by_view_by_old_property: dict[ViewEntity, dict[str, str]] = defaultdict(dict)
|
|
432
|
+
for prop in rules.properties:
|
|
433
|
+
if prop.view.suffix in new_by_old_view:
|
|
434
|
+
prop.view.suffix = new_by_old_view[prop.view.suffix]
|
|
435
|
+
new_view_property = NamingStandardization.standardize_property_str(prop.view_property)
|
|
436
|
+
new_property_by_view_by_old_property[prop.view][prop.view_property] = new_view_property
|
|
437
|
+
prop.view_property = new_view_property
|
|
438
|
+
if isinstance(prop.value_type, ViewEntity) and prop.value_type.suffix in new_by_old_view:
|
|
439
|
+
prop.value_type.suffix = new_by_old_view[prop.value_type.suffix]
|
|
440
|
+
if (
|
|
441
|
+
isinstance(prop.connection, EdgeEntity)
|
|
442
|
+
and prop.connection.properties
|
|
443
|
+
and prop.connection.properties.suffix in new_by_old_view
|
|
444
|
+
):
|
|
445
|
+
prop.connection.properties.suffix = new_by_old_view[prop.connection.properties.suffix]
|
|
446
|
+
if isinstance(prop.container, ContainerEntity) and prop.container.suffix in new_by_old_container:
|
|
447
|
+
prop.container.suffix = new_by_old_container[prop.container.suffix]
|
|
448
|
+
if prop.container_property:
|
|
449
|
+
prop.container_property = NamingStandardization.standardize_property_str(prop.container_property)
|
|
450
|
+
for prop in rules.properties:
|
|
451
|
+
if (
|
|
452
|
+
isinstance(prop.connection, ReverseConnectionEntity)
|
|
453
|
+
and isinstance(prop.value_type, ViewEntity)
|
|
454
|
+
and prop.value_type in new_property_by_view_by_old_property
|
|
455
|
+
):
|
|
456
|
+
new_by_old_property = new_property_by_view_by_old_property[prop.value_type]
|
|
457
|
+
if prop.connection.property_ in new_by_old_property:
|
|
458
|
+
prop.connection.property_ = new_by_old_property[prop.connection.property_]
|
|
459
|
+
return rules
|
|
460
|
+
|
|
461
|
+
|
|
249
462
|
class InformationToDMS(ConversionTransformer[InformationRules, DMSRules]):
|
|
250
463
|
"""Converts InformationRules to DMSRules."""
|
|
251
464
|
|
|
@@ -339,13 +552,6 @@ class ToEnterpriseModel(ToExtensionModel):
|
|
|
339
552
|
self.move_connections = move_connections
|
|
340
553
|
|
|
341
554
|
def transform(self, rules: DMSRules) -> DMSRules:
|
|
342
|
-
reference_model_id = rules.metadata.as_data_model_id()
|
|
343
|
-
if reference_model_id not in COGNITE_MODELS:
|
|
344
|
-
warnings.warn(
|
|
345
|
-
EnterpriseModelNotBuildOnTopOfCDMWarning(reference_model_id=reference_model_id).as_message(),
|
|
346
|
-
stacklevel=2,
|
|
347
|
-
)
|
|
348
|
-
|
|
349
555
|
return self._to_enterprise(rules)
|
|
350
556
|
|
|
351
557
|
def _to_enterprise(self, reference_model: DMSRules) -> DMSRules:
|
|
@@ -565,12 +771,10 @@ class ToSolutionModel(ToExtensionModel):
|
|
|
565
771
|
renaming: dict[ViewEntity, ViewEntity] = {}
|
|
566
772
|
new_views = SheetList[DMSView]()
|
|
567
773
|
read_view_by_new_view: dict[ViewEntity, ViewEntity] = {}
|
|
568
|
-
skipped_views: set[ViewEntity] = set()
|
|
569
774
|
for ref_view in reference.views:
|
|
570
775
|
if (self.skip_cognite_views and ref_view.view.space in COGNITE_SPACES) or (
|
|
571
776
|
self.exclude_views_in_other_spaces and ref_view.view.space != reference.metadata.space
|
|
572
777
|
):
|
|
573
|
-
skipped_views.add(ref_view.view)
|
|
574
778
|
continue
|
|
575
779
|
new_entity = ViewEntity(
|
|
576
780
|
# MyPy we validate that version is string in the constructor
|
|
@@ -603,15 +807,17 @@ class ToSolutionModel(ToExtensionModel):
|
|
|
603
807
|
new_views.append(ref_view.model_copy(deep=True, update={"implements": None, "view": new_entity}))
|
|
604
808
|
|
|
605
809
|
new_properties = SheetList[DMSProperty]()
|
|
810
|
+
new_view_entities = {view.view for view in new_views}
|
|
606
811
|
for prop in reference.properties:
|
|
607
|
-
if prop.view in skipped_views:
|
|
608
|
-
continue
|
|
609
812
|
new_property = prop.model_copy(deep=True)
|
|
610
813
|
if new_property.value_type in renaming and isinstance(new_property.value_type, ViewEntity):
|
|
611
814
|
new_property.value_type = renaming[new_property.value_type]
|
|
612
815
|
if new_property.view in renaming:
|
|
613
816
|
new_property.view = renaming[new_property.view]
|
|
614
|
-
|
|
817
|
+
if new_property.view in new_view_entities and (
|
|
818
|
+
not isinstance(new_property.value_type, ViewEntity) or new_property.value_type in new_view_entities
|
|
819
|
+
):
|
|
820
|
+
new_properties.append(new_property)
|
|
615
821
|
return new_views, new_properties, read_view_by_new_view
|
|
616
822
|
|
|
617
823
|
def _create_containers_update_view_filter(
|
cognite/neat/_session/_base.py
CHANGED
|
@@ -16,6 +16,7 @@ from cognite.neat._rules.transformers import (
|
|
|
16
16
|
InformationToDMS,
|
|
17
17
|
MergeDMSRules,
|
|
18
18
|
MergeInformationRules,
|
|
19
|
+
ToInformationCompliantEntities,
|
|
19
20
|
VerifyInformationRules,
|
|
20
21
|
)
|
|
21
22
|
from cognite.neat._store._rules_store import RulesEntity
|
|
@@ -235,13 +236,19 @@ class NeatSession:
|
|
|
235
236
|
|
|
236
237
|
def action() -> tuple[InformationRules, DMSRules | None]:
|
|
237
238
|
unverified_information = importer.to_rules()
|
|
239
|
+
unverified_information = ToInformationCompliantEntities(renaming="warning").transform(
|
|
240
|
+
unverified_information
|
|
241
|
+
)
|
|
242
|
+
|
|
238
243
|
extra_info = VerifyInformationRules().transform(unverified_information)
|
|
239
244
|
if not last_entity:
|
|
240
245
|
return extra_info, None
|
|
241
246
|
merged_info = MergeInformationRules(extra_info).transform(last_entity.information)
|
|
242
247
|
if not last_entity.dms:
|
|
243
248
|
return merged_info, None
|
|
249
|
+
|
|
244
250
|
extra_dms = InformationToDMS(reserved_properties="warning").transform(extra_info)
|
|
251
|
+
|
|
245
252
|
merged_dms = MergeDMSRules(extra_dms).transform(last_entity.dms)
|
|
246
253
|
return merged_info, merged_dms
|
|
247
254
|
|
cognite/neat/_session/_create.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import Literal
|
|
|
3
3
|
from cognite.client.data_classes.data_modeling import DataModelIdentifier
|
|
4
4
|
|
|
5
5
|
from cognite.neat._issues import IssueList
|
|
6
|
+
from cognite.neat._rules.models import DMSRules, InformationRules
|
|
6
7
|
from cognite.neat._rules.models.dms import DMSValidation
|
|
7
8
|
from cognite.neat._rules.transformers import (
|
|
8
9
|
IncludeReferenced,
|
|
@@ -38,22 +39,27 @@ class CreateAPI:
|
|
|
38
39
|
org_name: Organization name to use for the views in the enterprise data model.
|
|
39
40
|
dummy_property: The dummy property to use as placeholder for the views in the new data model.
|
|
40
41
|
|
|
42
|
+
What does this function do?
|
|
43
|
+
1. It creates a new view for each view in the current data model that implements the view it is based on.
|
|
44
|
+
2. If dummy_property is set, it will create a container with one property for each view and connect the
|
|
45
|
+
view to the container.
|
|
46
|
+
3. It will repeat all connection properties in the new views and update the ValueTypes to match the new
|
|
47
|
+
views.
|
|
48
|
+
|
|
41
49
|
!!! note "Enterprise Data Model Creation"
|
|
42
50
|
|
|
43
51
|
Always create an enterprise data model from a Cognite Data Model as this will
|
|
44
52
|
assure all the Cognite Data Fusion applications to run smoothly, such as
|
|
45
53
|
- Search
|
|
46
54
|
- Atlas AI
|
|
47
|
-
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
If you want to move the connections to the new data model, set the move_connections
|
|
52
|
-
to True. This will move the connections to the new data model and use new model
|
|
53
|
-
views as the source and target views.
|
|
55
|
+
- Infield
|
|
56
|
+
- Canvas
|
|
57
|
+
- Maintain
|
|
58
|
+
- Charts
|
|
54
59
|
|
|
55
60
|
"""
|
|
56
|
-
|
|
61
|
+
last_rules = self._get_last_rules()
|
|
62
|
+
issues = self._state.rule_transform(
|
|
57
63
|
ToEnterpriseModel(
|
|
58
64
|
new_model_id=data_model_id,
|
|
59
65
|
org_name=org_name,
|
|
@@ -61,6 +67,15 @@ class CreateAPI:
|
|
|
61
67
|
move_connections=True,
|
|
62
68
|
)
|
|
63
69
|
)
|
|
70
|
+
if last_rules and not issues.has_errors:
|
|
71
|
+
self._state.last_reference = last_rules
|
|
72
|
+
return issues
|
|
73
|
+
|
|
74
|
+
def _get_last_rules(self) -> InformationRules | DMSRules | None:
|
|
75
|
+
if not self._state.rule_store.provenance:
|
|
76
|
+
return None
|
|
77
|
+
last_entity = self._state.rule_store.provenance[-1].target_entity
|
|
78
|
+
return last_entity.dms or last_entity.information
|
|
64
79
|
|
|
65
80
|
def solution_model(
|
|
66
81
|
self,
|
|
@@ -76,6 +91,13 @@ class CreateAPI:
|
|
|
76
91
|
and the enterprise data model.
|
|
77
92
|
view_prefix: The prefix to use for the views in the enterprise data model.
|
|
78
93
|
|
|
94
|
+
What does this function do?
|
|
95
|
+
1. It will create two new views for each view in the current data model. The first view will be read-only and
|
|
96
|
+
prefixed with the 'view_prefix'. The second view will be writable and have one property that connects to the
|
|
97
|
+
read-only view named 'direct_property'.
|
|
98
|
+
2. It will repeat all connection properties in the new views and update the ValueTypes to match the new views.
|
|
99
|
+
3. Each writable view will have a container with the single property that connects to the read-only view.
|
|
100
|
+
|
|
79
101
|
!!! note "Solution Data Model Mode"
|
|
80
102
|
|
|
81
103
|
The read-only solution model will only be able to read from the existing containers
|
|
@@ -88,7 +110,8 @@ class CreateAPI:
|
|
|
88
110
|
the containers in the solution data model space.
|
|
89
111
|
|
|
90
112
|
"""
|
|
91
|
-
|
|
113
|
+
last_rules = self._get_last_rules()
|
|
114
|
+
issues = self._state.rule_transform(
|
|
92
115
|
ToSolutionModel(
|
|
93
116
|
new_model_id=data_model_id,
|
|
94
117
|
properties="connection",
|
|
@@ -96,24 +119,32 @@ class CreateAPI:
|
|
|
96
119
|
view_prefix=view_prefix,
|
|
97
120
|
)
|
|
98
121
|
)
|
|
122
|
+
if last_rules and not issues.has_errors:
|
|
123
|
+
self._state.last_reference = last_rules
|
|
124
|
+
return issues
|
|
99
125
|
|
|
100
126
|
def data_product_model(
|
|
101
127
|
self,
|
|
102
128
|
data_model_id: DataModelIdentifier,
|
|
103
129
|
include: Literal["same-space", "all"] = "same-space",
|
|
104
|
-
) ->
|
|
130
|
+
) -> IssueList:
|
|
105
131
|
"""Uses the current data model as a basis to create data product data model.
|
|
106
132
|
|
|
107
133
|
A data product model is a data model that ONLY maps to containers and do not use implements. This is
|
|
108
134
|
typically used for defining the data in a data product.
|
|
109
135
|
|
|
136
|
+
What does this function do?
|
|
137
|
+
1. It creates a new view for each view in the current data model. The new views uses the same filter
|
|
138
|
+
as the view it is based on.
|
|
139
|
+
2. It will repeat all connection properties in the new views and update the ValueTypes to match the new views.
|
|
140
|
+
|
|
110
141
|
Args:
|
|
111
142
|
data_model_id: The data product data model id that is being created.
|
|
112
143
|
include: The views to include in the data product data model. Can be either "same-space" or "all".
|
|
113
144
|
If you set same-space, only the properties of the views in the same space as the data model
|
|
114
145
|
will be included.
|
|
115
146
|
"""
|
|
116
|
-
|
|
147
|
+
last_rules = self._get_last_rules()
|
|
117
148
|
view_ids, container_ids = DMSValidation(
|
|
118
149
|
self._state.rule_store.last_verified_dms_rules
|
|
119
150
|
).imported_views_and_containers_ids()
|
|
@@ -130,4 +161,7 @@ class CreateAPI:
|
|
|
130
161
|
|
|
131
162
|
transformers.append(ToDataProductModel(new_model_id=data_model_id, include=include))
|
|
132
163
|
|
|
133
|
-
self._state.rule_transform(*transformers)
|
|
164
|
+
issues = self._state.rule_transform(*transformers)
|
|
165
|
+
if last_rules and not issues.has_errors:
|
|
166
|
+
self._state.last_reference = last_rules
|
|
167
|
+
return issues
|
|
@@ -3,6 +3,7 @@ from typing import Any
|
|
|
3
3
|
|
|
4
4
|
from rdflib import URIRef
|
|
5
5
|
|
|
6
|
+
from cognite.neat._alpha import AlphaFlags
|
|
6
7
|
from cognite.neat._graph.transformers import (
|
|
7
8
|
ConnectionToLiteral,
|
|
8
9
|
ConvertLiteral,
|
|
@@ -12,9 +13,7 @@ from cognite.neat._graph.transformers import (
|
|
|
12
13
|
from cognite.neat._graph.transformers._rdfpath import MakeConnectionOnExactMatch
|
|
13
14
|
from cognite.neat._issues import IssueList
|
|
14
15
|
from cognite.neat._issues.errors import NeatValueError
|
|
15
|
-
from cognite.neat._rules.transformers import
|
|
16
|
-
PrefixEntities,
|
|
17
|
-
)
|
|
16
|
+
from cognite.neat._rules.transformers import PrefixEntities, StandardizeNaming
|
|
18
17
|
from cognite.neat._utils.text import humanize_collection
|
|
19
18
|
|
|
20
19
|
from ._state import SessionState
|
|
@@ -264,3 +263,12 @@ class DataModelPrepareAPI:
|
|
|
264
263
|
"""
|
|
265
264
|
|
|
266
265
|
return self._state.rule_transform(PrefixEntities(prefix)) # type: ignore[arg-type]
|
|
266
|
+
|
|
267
|
+
def standardize_naming(self) -> IssueList:
|
|
268
|
+
"""Standardize the naming of all views/classes/properties in the data model.
|
|
269
|
+
|
|
270
|
+
For classes/views/containers, the naming will be standardized to PascalCase.
|
|
271
|
+
For properties, the naming will be standardized to camelCase.
|
|
272
|
+
"""
|
|
273
|
+
AlphaFlags.standardize_naming.warn()
|
|
274
|
+
return self._state.rule_transform(StandardizeNaming())
|
cognite/neat/_session/_state.py
CHANGED
|
@@ -78,7 +78,7 @@ class InstancesState:
|
|
|
78
78
|
self.outcome = UploadResultList()
|
|
79
79
|
|
|
80
80
|
# Ensure that error handling is done in the constructor
|
|
81
|
-
self.store = _session_method_wrapper(self._create_store, "NeatSession")()
|
|
81
|
+
self.store: NeatGraphStore = _session_method_wrapper(self._create_store, "NeatSession")()
|
|
82
82
|
|
|
83
83
|
if self.storage_path:
|
|
84
84
|
print("Remember to close neat session .close() once you are done to avoid oxigraph lock.")
|
cognite/neat/_session/_to.py
CHANGED
|
@@ -46,9 +46,9 @@ class ToAPI:
|
|
|
46
46
|
include_reference: If True, the reference data model will be included. Defaults to True.
|
|
47
47
|
Note that this only applies if you have created the data model using the
|
|
48
48
|
create.enterprise_model(...), create.solution_model(), or create.data_product_model() methods.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
include_properties: The properties to include in the Excel file. Defaults to "all".
|
|
50
|
+
- "same-space": Only properties that are in the same space as the data model will be included.
|
|
51
|
+
add_empty_rows: If True, empty rows will be added between each component. Defaults to False.
|
|
52
52
|
|
|
53
53
|
Example:
|
|
54
54
|
Export information model to excel rules sheet
|
cognite/neat/_utils/text.py
CHANGED
|
@@ -17,21 +17,23 @@ def to_camel(string: str) -> str:
|
|
|
17
17
|
>>> to_camel("ScenarioInstance_priceForecast")
|
|
18
18
|
'scenarioInstancePriceForecast'
|
|
19
19
|
"""
|
|
20
|
+
string = re.sub(r"[\s_-]", "_", string)
|
|
21
|
+
string = re.sub("_+", "_", string)
|
|
20
22
|
if "_" in string:
|
|
21
|
-
|
|
22
|
-
parts = string.split("_")
|
|
23
|
-
pascal_splits = [to_pascal(subpart) for part in parts for subpart in part.split("-") if subpart]
|
|
24
|
-
elif "-" in string:
|
|
25
|
-
# Could be a combination of kebab and pascal/camel case
|
|
26
|
-
parts = string.split("-")
|
|
27
|
-
pascal_splits = [to_pascal(subpart) for part in parts for subpart in part.split("_") if subpart]
|
|
23
|
+
pascal_splits = [to_pascal(part) for part in string.split("_")]
|
|
28
24
|
else:
|
|
29
|
-
# Assume is pascal/camel case
|
|
30
25
|
# Ensure pascal
|
|
31
26
|
string = string[0].upper() + string[1:]
|
|
32
27
|
pascal_splits = [string]
|
|
33
|
-
|
|
28
|
+
cleaned: list[str] = []
|
|
34
29
|
for part in pascal_splits:
|
|
30
|
+
if part.upper() == part:
|
|
31
|
+
cleaned.append(part.capitalize())
|
|
32
|
+
else:
|
|
33
|
+
cleaned.append(part)
|
|
34
|
+
|
|
35
|
+
string_split = []
|
|
36
|
+
for part in cleaned:
|
|
35
37
|
string_split.extend(re.findall(r"[A-Z][a-z0-9]*", part))
|
|
36
38
|
if not string_split:
|
|
37
39
|
string_split = [string]
|
|
@@ -135,3 +137,32 @@ def humanize_collection(collection: Collection[Any], /, *, sort: bool = True) ->
|
|
|
135
137
|
sequence = list(strings)
|
|
136
138
|
|
|
137
139
|
return f"{', '.join(sequence[:-1])} and {sequence[-1]}"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class NamingStandardization:
|
|
143
|
+
_clean_pattern = re.compile(r"[^a-zA-Z0-9_]+")
|
|
144
|
+
_multi_underscore_pattern = re.compile(r"_+")
|
|
145
|
+
_start_letter_pattern = re.compile(r"^[a-zA-Z]")
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def standardize_class_str(cls, raw: str) -> str:
|
|
149
|
+
clean = cls._clean_string(raw)
|
|
150
|
+
if not cls._start_letter_pattern.match(clean):
|
|
151
|
+
# Underscore ensure that 'Class' it treated as a separate word
|
|
152
|
+
# in the to_pascale function
|
|
153
|
+
clean = f"Class_{clean}"
|
|
154
|
+
return to_pascal(clean)
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def standardize_property_str(cls, raw: str) -> str:
|
|
158
|
+
clean = cls._clean_string(raw)
|
|
159
|
+
if not cls._start_letter_pattern.match(clean):
|
|
160
|
+
# Underscore ensure that 'property' it treated as a separate word
|
|
161
|
+
# in the to_camel function
|
|
162
|
+
clean = f"property_{clean}"
|
|
163
|
+
return to_camel(clean)
|
|
164
|
+
|
|
165
|
+
@classmethod
|
|
166
|
+
def _clean_string(cls, raw: str) -> str:
|
|
167
|
+
raw = cls._clean_pattern.sub("_", raw)
|
|
168
|
+
return cls._multi_underscore_pattern.sub("_", raw)
|
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.109.
|
|
1
|
+
__version__ = "0.109.3"
|
|
2
2
|
__engine__ = "^2.0.3"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cognite/neat/__init__.py,sha256=AXLMIF5t8RmjOpSaIlfqT8ltbPc__Tb8nAVbc1pkE0Q,176
|
|
2
|
-
cognite/neat/_alpha.py,sha256=
|
|
2
|
+
cognite/neat/_alpha.py,sha256=MXVWltw8esJPFNbLIt74_Xib7z-PXWJOBrTCxK-Z2Qs,483
|
|
3
3
|
cognite/neat/_client/__init__.py,sha256=RQ7MwL8mwGqGHokRzsPqO3XStDzmI4-c12_gz1UPJ74,177
|
|
4
4
|
cognite/neat/_client/_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
cognite/neat/_client/_api/data_modeling_loaders.py,sha256=0BZ5_NeszBvs4DHuHr0cGXytmvkTxrQ8k7v6iaX_mn4,35932
|
|
@@ -9,7 +9,7 @@ cognite/neat/_client/data_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
|
|
|
9
9
|
cognite/neat/_client/data_classes/data_modeling.py,sha256=RvpUp9ygd-yffQFJ7O2mQqMLDPIa-dmip5zPb8QVIiw,6672
|
|
10
10
|
cognite/neat/_client/data_classes/neat_sequence.py,sha256=QZWSfWnwk6KlYJvsInco4Wdwc1U8DnOQKWmHebArbQY,10830
|
|
11
11
|
cognite/neat/_client/data_classes/schema.py,sha256=uD8ExxEiIP3zhK4b--Q5fND-vmcC05a9WU5ItLsqG88,23179
|
|
12
|
-
cognite/neat/_client/testing.py,sha256=
|
|
12
|
+
cognite/neat/_client/testing.py,sha256=M5WUzi3YbtZAN22TXas0SjvMSxmjPFT5m3lNwR0MVkI,1077
|
|
13
13
|
cognite/neat/_config.py,sha256=WT1BS8uADcFvGoUYOOfwFOVq_VBl472TisdoA3wLick,280
|
|
14
14
|
cognite/neat/_constants.py,sha256=OStolbvP16sM9LltZG6irG27W1tioNGwVr84-Hv6Ovk,5752
|
|
15
15
|
cognite/neat/_graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -55,13 +55,13 @@ cognite/neat/_graph/transformers/_prune_graph.py,sha256=X9wVOBl3mhcPB-SSqWvJHV1h
|
|
|
55
55
|
cognite/neat/_graph/transformers/_rdfpath.py,sha256=KLNnuLmeMCobJ6Q81l5kUqUt_fkKAovzuiy9zO97sTI,5003
|
|
56
56
|
cognite/neat/_graph/transformers/_value_type.py,sha256=wsuiIKHG6O8F7PaedicMfynTxiVpLZ55zwVU3mCDEYw,15679
|
|
57
57
|
cognite/neat/_issues/__init__.py,sha256=OVgWivp_Br31p8zPeHjOEXs-Wj7lJU1pU1L3pfhfuqE,518
|
|
58
|
-
cognite/neat/_issues/_base.py,sha256=
|
|
59
|
-
cognite/neat/_issues/errors/__init__.py,sha256=
|
|
58
|
+
cognite/neat/_issues/_base.py,sha256=fZODjRPyiZjVj1ujqKVQOrTh8SaliR9ayG0zRRmyptc,21044
|
|
59
|
+
cognite/neat/_issues/errors/__init__.py,sha256=05PgHRvAwMX9FPRLyAhzOvSXL-JCOs1SqtJGUB975fk,2046
|
|
60
60
|
cognite/neat/_issues/errors/_external.py,sha256=KrF0_ubXlyd903ARUFZlBI45Y-I3q2FS8vSWb9t4WBI,2043
|
|
61
|
-
cognite/neat/_issues/errors/_general.py,sha256=
|
|
61
|
+
cognite/neat/_issues/errors/_general.py,sha256=MnBm9V1S-Agr9kUfTMvviBB4_NkuOXamlgJCu2UojEM,849
|
|
62
62
|
cognite/neat/_issues/errors/_properties.py,sha256=T_nquQeEQS3DQY--DQ13acxhGX_-gpUMjWGTBKCh6r8,2536
|
|
63
63
|
cognite/neat/_issues/errors/_resources.py,sha256=qndhGGPdulWpU7G4PkFHsF4Bjflf3lehGV4A0l-BeNI,3966
|
|
64
|
-
cognite/neat/_issues/errors/
|
|
64
|
+
cognite/neat/_issues/errors/_wrapper.py,sha256=e7Zrj7YkzjwS_T880Bh4r0gwstCjU8256_-2A5MA-iA,239
|
|
65
65
|
cognite/neat/_issues/formatters.py,sha256=ziNWT_YXwovTfU8Av5iYuSLgszzJYKTawM_z67VBdlU,3331
|
|
66
66
|
cognite/neat/_issues/warnings/__init__.py,sha256=1iYRzwaMftwcueiA8KMA_bGdxdWxmSfMRqUPeTmHV_w,2909
|
|
67
67
|
cognite/neat/_issues/warnings/_external.py,sha256=O5GSRmIsAC6HyToQ7itpFFNILWCAg0OehPTVUy8pTuc,1319
|
|
@@ -108,19 +108,19 @@ cognite/neat/_rules/importers/_yaml2rules.py,sha256=k2oDgz--mxFVeyqQG3uvyYcb0BkF
|
|
|
108
108
|
cognite/neat/_rules/models/__init__.py,sha256=tf6tyWiYO0eC2PHCcpy208dwOHjEi9hg0sEaQLcB3uA,1024
|
|
109
109
|
cognite/neat/_rules/models/_base_input.py,sha256=kAVI6QYAzsgQ79eLPc_hANJjl-o1e9IKeD7lF61ru9Y,6656
|
|
110
110
|
cognite/neat/_rules/models/_base_rules.py,sha256=TpDhGaodzfzfyrSGChx76Tw_zN2YvtxqFz2lvOMHFPE,15134
|
|
111
|
-
cognite/neat/_rules/models/_rdfpath.py,sha256=
|
|
112
|
-
cognite/neat/_rules/models/_types.py,sha256=
|
|
111
|
+
cognite/neat/_rules/models/_rdfpath.py,sha256=clZGAzulIFKV2rzwoVZpLRlQUEyrAaiM7SjOoj1KUnY,11948
|
|
112
|
+
cognite/neat/_rules/models/_types.py,sha256=6fHLiVPRlk5QsFjmvDFFqw1htPcPp-RfnUzYoMWNfeg,5418
|
|
113
113
|
cognite/neat/_rules/models/data_types.py,sha256=LJuWUbStlZM4hUJGExOJIJXmAA4uiA0tvO9zKqLUrQg,9805
|
|
114
114
|
cognite/neat/_rules/models/dms/__init__.py,sha256=fRaUH0IwG0YwWd_DNKM6j-jHHFyiIVz4_8DPiS1KR0Y,695
|
|
115
115
|
cognite/neat/_rules/models/dms/_exporter.py,sha256=WFDhkFeY2kJCguru-btcvjDcbMG5ISIAQzIUtDjIlxQ,28097
|
|
116
|
-
cognite/neat/_rules/models/dms/_rules.py,sha256=
|
|
116
|
+
cognite/neat/_rules/models/dms/_rules.py,sha256=VK8QKLf6NxeoaMci5g7EG08XKzSKJZYJwP4wZxa5eUQ,20507
|
|
117
117
|
cognite/neat/_rules/models/dms/_rules_input.py,sha256=noSpYjHKW_GMaeuADOG-Z21ClxiD_tIGxl-dxOKxk-g,13390
|
|
118
118
|
cognite/neat/_rules/models/dms/_validation.py,sha256=PNFHsDNHJZfbHY1qt6D4t3eFOGjqe6Koa702Gzcz0I8,27734
|
|
119
119
|
cognite/neat/_rules/models/entities/__init__.py,sha256=Hlucp3LyV6ncLl79aqRTbSy2qgiGzoyN8x54D_zaJCY,1469
|
|
120
|
-
cognite/neat/_rules/models/entities/_constants.py,sha256=
|
|
120
|
+
cognite/neat/_rules/models/entities/_constants.py,sha256=g52-pFP_Qghp86NYk76pLDM52b3T4MXMztxhpeDW3Oc,344
|
|
121
121
|
cognite/neat/_rules/models/entities/_loaders.py,sha256=jFllRty5XpS6uLklr9wJkx7Bzm-qwg65um6hnVistvw,2728
|
|
122
122
|
cognite/neat/_rules/models/entities/_multi_value.py,sha256=6j-nlUA392yQP_uB_CFB6_qFReNhi54ZlbFTcOKpbKY,2755
|
|
123
|
-
cognite/neat/_rules/models/entities/_single_value.py,sha256=
|
|
123
|
+
cognite/neat/_rules/models/entities/_single_value.py,sha256=XZA4rqXzL-97bsCEAz1imUtn4lir_ikiBfs5LVtziSI,19604
|
|
124
124
|
cognite/neat/_rules/models/entities/_types.py,sha256=df9rnXJJKciv2Bp-Ve2q4xdEJt6WWniq12Z0hW2d6sk,1917
|
|
125
125
|
cognite/neat/_rules/models/entities/_wrapped.py,sha256=SpXIpglXBgaZt6geRparCWdY2F6SH0Fy99fcnA8x8Uk,7648
|
|
126
126
|
cognite/neat/_rules/models/information/__init__.py,sha256=lV7l8RTfWBvN_DFkzea8OzADjq0rZGgWe_0Fiwtfje0,556
|
|
@@ -130,25 +130,25 @@ cognite/neat/_rules/models/information/_validation.py,sha256=HbaLShj6uumu-t9I3FU
|
|
|
130
130
|
cognite/neat/_rules/models/mapping/__init__.py,sha256=T68Hf7rhiXa7b03h4RMwarAmkGnB-Bbhc1H07b2PyC4,100
|
|
131
131
|
cognite/neat/_rules/models/mapping/_classic2core.py,sha256=AhLWoXk4PlBVA6SgBtY9dcLcpqEMaYcsiEatI19miPk,1211
|
|
132
132
|
cognite/neat/_rules/models/mapping/_classic2core.yaml,sha256=jodkmcTborWJmG3At16OChtnol696X6D4lgAa7aaQ78,20491
|
|
133
|
-
cognite/neat/_rules/transformers/__init__.py,sha256=
|
|
133
|
+
cognite/neat/_rules/transformers/__init__.py,sha256=S6iUB7wpAh-7uF5k_EgXT4cGfi8ok5lczGemVxgKckQ,1413
|
|
134
134
|
cognite/neat/_rules/transformers/_base.py,sha256=9LnjKbYIf9238PQXedkTZsMXAyEND6glUD187iEaHfc,2783
|
|
135
|
-
cognite/neat/_rules/transformers/_converters.py,sha256=
|
|
135
|
+
cognite/neat/_rules/transformers/_converters.py,sha256=daDpW4zgHRjm845R2ajfC9XoAPFV2jOq8Wa3z6C4y0M,77621
|
|
136
136
|
cognite/neat/_rules/transformers/_mapping.py,sha256=lf-RKN__5Bg3-tZjEOCa1Sf_JtM_ScQ_TYcnciEnaYQ,18189
|
|
137
137
|
cognite/neat/_rules/transformers/_verification.py,sha256=jKTppklUCVwVlRfYyMfnUtV8r2ACTY-AtsoMF6L-KXo,4593
|
|
138
138
|
cognite/neat/_session/__init__.py,sha256=fxQ5URVlUnmEGYyB8Baw7IDq-uYacqkigbc4b-Pr9Fw,58
|
|
139
|
-
cognite/neat/_session/_base.py,sha256=
|
|
139
|
+
cognite/neat/_session/_base.py,sha256=lNCjSiQIn1hI3zSru1hXGTQ0nVzy3CcXTO7vL4XRzno,11935
|
|
140
140
|
cognite/neat/_session/_collector.py,sha256=RcOGY0DjTCCKJt9j_p0gnQXn4omhsIX2G8Aq3ZFHIt4,4218
|
|
141
|
-
cognite/neat/_session/_create.py,sha256=
|
|
141
|
+
cognite/neat/_session/_create.py,sha256=doDCbDIWMbHCYe3cyk1obQaFdYJjvARg3X4lRUVicCk,7214
|
|
142
142
|
cognite/neat/_session/_drop.py,sha256=gOkDAnddASpFxYxkPjlTyhkpNfnmDEj94GRI8tnHFR0,4167
|
|
143
143
|
cognite/neat/_session/_fix.py,sha256=gpmbJ4TbB_v2nw4fEA2Qtf0ifO3UDEMHGdztha28S_U,898
|
|
144
144
|
cognite/neat/_session/_inspect.py,sha256=VkmDfIjh49NPS8wNa1OdWqhW-oJy1EvMDhKiooMvcjI,9040
|
|
145
145
|
cognite/neat/_session/_mapping.py,sha256=AkQwmqYH-0EgqoXHqCFwJY92hNSGzfojOelhVFlqH4c,2655
|
|
146
|
-
cognite/neat/_session/_prepare.py,sha256=
|
|
146
|
+
cognite/neat/_session/_prepare.py,sha256=fozBTqF8hUTfOf4wzDDulc0Fi5bDfO1C1EeBU0Bii3Q,12088
|
|
147
147
|
cognite/neat/_session/_read.py,sha256=V2F7InkvIODdML7jy4aGXH4PE91tWFzIGcNzOWnsTvM,22514
|
|
148
148
|
cognite/neat/_session/_set.py,sha256=ZYR5G97fi-y68edbG-ftLqM_FRtn9G8V5zDtpslR9go,4070
|
|
149
149
|
cognite/neat/_session/_show.py,sha256=3hUMWBlHCyMiVTbX1GmpVFfthpv3yBIZ293aU0mDxZ8,15156
|
|
150
|
-
cognite/neat/_session/_state.py,sha256=
|
|
151
|
-
cognite/neat/_session/_to.py,sha256=
|
|
150
|
+
cognite/neat/_session/_state.py,sha256=udCCS5tWA2z7P1YK65iyC-weDwgQg07P_MS3TLjJC90,4075
|
|
151
|
+
cognite/neat/_session/_to.py,sha256=Wls8Obfx4Aodgyf_8jq7HxPt8JSHoFQ-DhJIfVoe3JU,11682
|
|
152
152
|
cognite/neat/_session/_wizard.py,sha256=9idlzhZy54h2Iwupe9iXKX3RDb5jJQuBZFEouni50L0,1476
|
|
153
153
|
cognite/neat/_session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4dvca6n48bu_bI,120
|
|
154
154
|
cognite/neat/_session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
|
|
@@ -171,14 +171,14 @@ cognite/neat/_utils/rdf_.py,sha256=b3sE3aTW9lu4gJWQJEaq_NCLbI54cc1o12utz-xbLh8,9
|
|
|
171
171
|
cognite/neat/_utils/reader/__init__.py,sha256=fPkrNB_9hLB7CyHTCFV_xEbIfOMqUQzNly5JN33-QfM,146
|
|
172
172
|
cognite/neat/_utils/reader/_base.py,sha256=Q35hz8tqAiQiELjE4DsDDKQHLtRmSTrty4Gep9rg_CU,5444
|
|
173
173
|
cognite/neat/_utils/spreadsheet.py,sha256=20L_44m0hg3UdBFwCxnmAravxvCOolNZwVOGHfjYGR4,3089
|
|
174
|
-
cognite/neat/_utils/text.py,sha256=
|
|
174
|
+
cognite/neat/_utils/text.py,sha256=9sBlE05S_9irzRxRvafBlWWy-kPgvUPV2Xo-_IgcwHM,4944
|
|
175
175
|
cognite/neat/_utils/time_.py,sha256=O30LUiDH9TdOYz8_a9pFqTtJdg8vEjC3qHCk8xZblG8,345
|
|
176
176
|
cognite/neat/_utils/upload.py,sha256=iWKmsQgw4EHLv-11NjYu7zAj5LtqTAfNa87a1kWeuaU,5727
|
|
177
177
|
cognite/neat/_utils/xml_.py,sha256=FQkq84u35MUsnKcL6nTMJ9ajtG9D5i1u4VBnhGqP2DQ,1710
|
|
178
|
-
cognite/neat/_version.py,sha256=
|
|
178
|
+
cognite/neat/_version.py,sha256=vGciJru9VsVwxQTbuUK6fhVtm7NFfhyVTVYsHrCWLes,46
|
|
179
179
|
cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
180
|
-
cognite_neat-0.109.
|
|
181
|
-
cognite_neat-0.109.
|
|
182
|
-
cognite_neat-0.109.
|
|
183
|
-
cognite_neat-0.109.
|
|
184
|
-
cognite_neat-0.109.
|
|
180
|
+
cognite_neat-0.109.3.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
181
|
+
cognite_neat-0.109.3.dist-info/METADATA,sha256=gB5oGvRGY4ON3W-z80mUOHMVSOlilQy3zUMLhqGJrhg,5361
|
|
182
|
+
cognite_neat-0.109.3.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
183
|
+
cognite_neat-0.109.3.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
|
|
184
|
+
cognite_neat-0.109.3.dist-info/RECORD,,
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
from cognite.neat._issues import NeatError
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@dataclass(unsafe_hash=True)
|
|
7
|
-
class WorkFlowMissingDataError(NeatError, ValueError):
|
|
8
|
-
"""In the workflow step {step_name} the following data is missing: {missing_data}."""
|
|
9
|
-
|
|
10
|
-
step_name: str
|
|
11
|
-
missing_data: frozenset[str]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass(unsafe_hash=True)
|
|
15
|
-
class WorkflowStepNotInitializedError(NeatError, RuntimeError):
|
|
16
|
-
"""Step {step_name} has not been initialized."""
|
|
17
|
-
|
|
18
|
-
step_name: str
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass(unsafe_hash=True)
|
|
22
|
-
class WorkflowConfigurationNotSetError(NeatError, RuntimeError):
|
|
23
|
-
"""The configuration variable '{config_variable}' is not set. Please set the configuration
|
|
24
|
-
before running the workflow."""
|
|
25
|
-
|
|
26
|
-
config_variable: str
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@dataclass(unsafe_hash=True)
|
|
30
|
-
class WorkflowStepOutputError(NeatError, RuntimeError):
|
|
31
|
-
"""Object type {step_type} is not supported as step output.
|
|
32
|
-
|
|
33
|
-
Step output must be of type DataContract or a FlowMessage.
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
step_type: str
|
|
File without changes
|
|
File without changes
|
|
File without changes
|