cognite-neat 0.75.7__py3-none-any.whl → 0.75.9__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/configuration.py +4 -9
- cognite/neat/app/api/routers/configuration.py +2 -1
- cognite/neat/app/api/routers/crud.py +5 -5
- cognite/neat/app/api/routers/data_exploration.py +3 -1
- cognite/neat/app/api/routers/rules.py +3 -3
- cognite/neat/app/api/routers/workflows.py +3 -3
- cognite/neat/app/ui/neat-app/build/asset-manifest.json +3 -3
- cognite/neat/app/ui/neat-app/build/index.html +1 -1
- cognite/neat/app/ui/neat-app/build/static/js/{main.4345d42f.js → main.ec7f72e2.js} +3 -3
- cognite/neat/app/ui/neat-app/build/static/js/{main.4345d42f.js.map → main.ec7f72e2.js.map} +1 -1
- cognite/neat/config.py +147 -12
- cognite/neat/constants.py +1 -0
- cognite/neat/graph/exceptions.py +1 -2
- cognite/neat/graph/extractors/_mock_graph_generator.py +6 -5
- cognite/neat/legacy/graph/exceptions.py +1 -2
- cognite/neat/legacy/graph/extractors/_mock_graph_generator.py +1 -2
- cognite/neat/legacy/graph/loaders/_asset_loader.py +8 -13
- cognite/neat/legacy/graph/loaders/_base.py +2 -4
- cognite/neat/legacy/graph/loaders/_exceptions.py +1 -3
- cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py +4 -8
- cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py +2 -4
- cognite/neat/legacy/graph/loaders/rdf_to_dms.py +2 -4
- cognite/neat/legacy/graph/loaders/validator.py +1 -1
- cognite/neat/legacy/graph/transformations/transformer.py +1 -2
- cognite/neat/legacy/rules/exporters/_rules2dms.py +1 -2
- cognite/neat/legacy/rules/exporters/_validation.py +4 -8
- cognite/neat/legacy/rules/importers/_base.py +0 -4
- cognite/neat/legacy/rules/importers/_dms2rules.py +0 -2
- cognite/neat/legacy/rules/models/rdfpath.py +1 -2
- cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml +89 -0
- cognite/neat/legacy/workflows/examples/Export_Rules_to_Ontology/workflow.yaml +152 -0
- cognite/neat/legacy/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml +139 -0
- cognite/neat/legacy/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +270 -0
- cognite/neat/legacy/workflows/examples/Import_DMS/workflow.yaml +65 -0
- cognite/neat/legacy/workflows/examples/Ontology_to_Data_Model/workflow.yaml +116 -0
- cognite/neat/legacy/workflows/examples/Validate_Rules/workflow.yaml +67 -0
- cognite/neat/legacy/workflows/examples/Validate_Solution_Model/workflow.yaml +64 -0
- cognite/neat/legacy/workflows/examples/Visualize_Data_Model_Using_Mock_Graph/workflow.yaml +95 -0
- cognite/neat/legacy/workflows/examples/Visualize_Semantic_Data_Model/workflow.yaml +111 -0
- cognite/neat/rules/analysis/_base.py +1 -1
- cognite/neat/rules/analysis/_information_rules.py +4 -4
- cognite/neat/rules/exporters/_rules2excel.py +2 -2
- cognite/neat/rules/exporters/_rules2ontology.py +20 -17
- cognite/neat/rules/exporters/_validation.py +8 -10
- cognite/neat/rules/importers/_base.py +2 -4
- cognite/neat/rules/importers/_dms2rules.py +16 -19
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +21 -19
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +2 -4
- cognite/neat/rules/importers/_dtdl2rules/spec.py +3 -5
- cognite/neat/rules/importers/_owl2rules/_owl2rules.py +4 -6
- cognite/neat/rules/importers/_spreadsheet2rules.py +10 -9
- cognite/neat/rules/importers/_yaml2rules.py +10 -6
- cognite/neat/rules/issues/dms.py +3 -5
- cognite/neat/rules/issues/formatters.py +3 -1
- cognite/neat/rules/models/data_types.py +54 -31
- cognite/neat/rules/models/entities.py +184 -42
- cognite/neat/rules/models/rdfpath.py +112 -13
- cognite/neat/rules/models/rules/_base.py +2 -2
- cognite/neat/rules/models/rules/_dms_architect_rules.py +119 -189
- cognite/neat/rules/models/rules/_dms_rules_write.py +344 -0
- cognite/neat/rules/models/rules/_dms_schema.py +3 -3
- cognite/neat/rules/models/rules/_domain_rules.py +6 -3
- cognite/neat/rules/models/rules/_information_rules.py +68 -61
- cognite/neat/rules/models/rules/_types/__init__.py +0 -47
- cognite/neat/rules/models/rules/_types/_base.py +1 -309
- cognite/neat/rules/models/rules/_types/_field.py +0 -225
- cognite/neat/utils/cdf_loaders/_data_modeling.py +3 -1
- cognite/neat/utils/cdf_loaders/_ingestion.py +2 -4
- cognite/neat/utils/spreadsheet.py +2 -4
- cognite/neat/utils/utils.py +2 -4
- cognite/neat/workflows/base.py +5 -5
- cognite/neat/workflows/manager.py +32 -22
- cognite/neat/workflows/model.py +3 -3
- cognite/neat/workflows/steps/lib/__init__.py +0 -7
- cognite/neat/workflows/steps/lib/current/__init__.py +6 -0
- cognite/neat/workflows/steps/lib/{rules_exporter.py → current/rules_exporter.py} +8 -8
- cognite/neat/workflows/steps/lib/{rules_importer.py → current/rules_importer.py} +7 -7
- cognite/neat/workflows/steps/lib/io/__init__.py +1 -0
- cognite/neat/workflows/steps/lib/{v1 → legacy}/graph_contextualization.py +2 -2
- cognite/neat/workflows/steps/lib/{v1 → legacy}/graph_extractor.py +9 -9
- cognite/neat/workflows/steps/lib/{v1 → legacy}/graph_loader.py +9 -9
- cognite/neat/workflows/steps/lib/{v1 → legacy}/graph_store.py +4 -4
- cognite/neat/workflows/steps/lib/{v1 → legacy}/graph_transformer.py +2 -2
- cognite/neat/workflows/steps/lib/{v1 → legacy}/rules_exporter.py +15 -17
- cognite/neat/workflows/steps/lib/{v1 → legacy}/rules_importer.py +7 -7
- cognite/neat/workflows/steps/step_model.py +5 -9
- cognite/neat/workflows/steps_registry.py +20 -11
- {cognite_neat-0.75.7.dist-info → cognite_neat-0.75.9.dist-info}/METADATA +1 -1
- {cognite_neat-0.75.7.dist-info → cognite_neat-0.75.9.dist-info}/RECORD +100 -90
- cognite/neat/app/api/data_classes/configuration.py +0 -121
- cognite/neat/rules/models/_entity.py +0 -142
- cognite/neat/rules/models/rules/_types/_value.py +0 -159
- /cognite/neat/app/ui/neat-app/build/static/js/{main.4345d42f.js.LICENSE.txt → main.ec7f72e2.js.LICENSE.txt} +0 -0
- /cognite/neat/workflows/steps/lib/{graph_extractor.py → current/graph_extractor.py} +0 -0
- /cognite/neat/workflows/steps/lib/{graph_loader.py → current/graph_loader.py} +0 -0
- /cognite/neat/workflows/steps/lib/{graph_store.py → current/graph_store.py} +0 -0
- /cognite/neat/workflows/steps/lib/{rules_validator.py → current/rules_validator.py} +0 -0
- /cognite/neat/workflows/steps/lib/{io_steps.py → io/io_steps.py} +0 -0
- /cognite/neat/workflows/steps/lib/{v1 → legacy}/__init__.py +0 -0
- {cognite_neat-0.75.7.dist-info → cognite_neat-0.75.9.dist-info}/LICENSE +0 -0
- {cognite_neat-0.75.7.dist-info → cognite_neat-0.75.9.dist-info}/WHEEL +0 -0
- {cognite_neat-0.75.7.dist-info → cognite_neat-0.75.9.dist-info}/entry_points.txt +0 -0
|
@@ -19,23 +19,11 @@ from pydantic_core import PydanticCustomError
|
|
|
19
19
|
from cognite.neat.rules import exceptions
|
|
20
20
|
|
|
21
21
|
from ._base import (
|
|
22
|
-
CLASS_ID_COMPLIANCE_REGEX,
|
|
23
|
-
ENTITY_ID_REGEX_COMPILED,
|
|
24
22
|
MORE_THAN_ONE_NONE_ALPHANUMERIC_REGEX,
|
|
25
23
|
PREFIX_COMPLIANCE_REGEX,
|
|
26
24
|
PROPERTY_ID_COMPLIANCE_REGEX,
|
|
27
25
|
VERSION_COMPLIANCE_REGEX,
|
|
28
|
-
VERSIONED_ENTITY_REGEX_COMPILED,
|
|
29
|
-
ClassEntity,
|
|
30
|
-
ContainerEntity,
|
|
31
|
-
ParentClassEntity,
|
|
32
|
-
ReferenceEntity,
|
|
33
|
-
Undefined,
|
|
34
|
-
Unknown,
|
|
35
|
-
ViewEntity,
|
|
36
|
-
ViewPropEntity,
|
|
37
26
|
)
|
|
38
|
-
from ._value import DMS_VALUE_TYPE_MAPPINGS, XSD_VALUE_TYPE_MAPPINGS, DMSValueType, XSDValueType
|
|
39
27
|
|
|
40
28
|
|
|
41
29
|
def _custom_error(exc_factory: Callable[[str | None, Exception], Any]) -> Any:
|
|
@@ -52,39 +40,6 @@ def _raise(exception: PydanticCustomError):
|
|
|
52
40
|
raise exception
|
|
53
41
|
|
|
54
42
|
|
|
55
|
-
def _split_parent(value: str | list[ParentClassEntity]) -> list[ParentClassEntity] | None:
|
|
56
|
-
if isinstance(value, list) and all(isinstance(x, ParentClassEntity) for x in value):
|
|
57
|
-
return value
|
|
58
|
-
|
|
59
|
-
if not (isinstance(value, str) and value):
|
|
60
|
-
return None
|
|
61
|
-
|
|
62
|
-
parents = []
|
|
63
|
-
for v in value.replace(", ", ",").split(","):
|
|
64
|
-
if ENTITY_ID_REGEX_COMPILED.match(v) or VERSIONED_ENTITY_REGEX_COMPILED.match(v):
|
|
65
|
-
parents.append(ParentClassEntity.from_string(entity_string=v))
|
|
66
|
-
else:
|
|
67
|
-
parents.append(ParentClassEntity(prefix=Undefined, suffix=v, name=v))
|
|
68
|
-
|
|
69
|
-
return parents
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def _check_parent(value: list[ParentClassEntity]) -> list[ParentClassEntity]:
|
|
73
|
-
if not value:
|
|
74
|
-
return value
|
|
75
|
-
if illegal_ids := [v for v in value if re.search(MORE_THAN_ONE_NONE_ALPHANUMERIC_REGEX, v.suffix)]:
|
|
76
|
-
raise exceptions.MoreThanOneNonAlphanumericCharacter(
|
|
77
|
-
"parent", ", ".join(cast(list[str], illegal_ids))
|
|
78
|
-
).to_pydantic_custom_error()
|
|
79
|
-
if illegal_ids := [v for v in value if not re.match(CLASS_ID_COMPLIANCE_REGEX, v.suffix)]:
|
|
80
|
-
for v in illegal_ids:
|
|
81
|
-
print(v.id)
|
|
82
|
-
raise exceptions.ClassSheetParentClassIDRegexViolation(
|
|
83
|
-
cast(list[str], illegal_ids), CLASS_ID_COMPLIANCE_REGEX
|
|
84
|
-
).to_pydantic_custom_error()
|
|
85
|
-
return value
|
|
86
|
-
|
|
87
|
-
|
|
88
43
|
StrOrListType = Annotated[
|
|
89
44
|
str | list[str],
|
|
90
45
|
BeforeValidator(lambda value: value.replace(", ", ",").split(",") if isinstance(value, str) and value else value),
|
|
@@ -141,43 +96,6 @@ VersionType = Annotated[
|
|
|
141
96
|
]
|
|
142
97
|
|
|
143
98
|
|
|
144
|
-
ParentClassType = Annotated[
|
|
145
|
-
list[ParentClassEntity] | None,
|
|
146
|
-
BeforeValidator(_split_parent),
|
|
147
|
-
AfterValidator(_check_parent),
|
|
148
|
-
PlainSerializer(
|
|
149
|
-
lambda v: ",".join([entry.versioned_id for entry in v]) if v else None,
|
|
150
|
-
return_type=str,
|
|
151
|
-
when_used="unless-none",
|
|
152
|
-
),
|
|
153
|
-
]
|
|
154
|
-
|
|
155
|
-
ClassType = Annotated[
|
|
156
|
-
ClassEntity,
|
|
157
|
-
BeforeValidator(lambda value: (ClassType.from_raw(value) if isinstance(value, str) else value)),
|
|
158
|
-
AfterValidator(
|
|
159
|
-
lambda value: (
|
|
160
|
-
_raise(exceptions.MoreThanOneNonAlphanumericCharacter("class_", value.suffix).to_pydantic_custom_error())
|
|
161
|
-
if re.search(MORE_THAN_ONE_NONE_ALPHANUMERIC_REGEX, value.suffix)
|
|
162
|
-
else (
|
|
163
|
-
value
|
|
164
|
-
if re.match(CLASS_ID_COMPLIANCE_REGEX, value.suffix)
|
|
165
|
-
else _raise(
|
|
166
|
-
exceptions.ClassSheetClassIDRegexViolation(
|
|
167
|
-
value.suffix, CLASS_ID_COMPLIANCE_REGEX
|
|
168
|
-
).to_pydantic_custom_error()
|
|
169
|
-
)
|
|
170
|
-
)
|
|
171
|
-
)
|
|
172
|
-
),
|
|
173
|
-
PlainSerializer(
|
|
174
|
-
lambda v: v.versioned_id,
|
|
175
|
-
return_type=str,
|
|
176
|
-
when_used="unless-none",
|
|
177
|
-
),
|
|
178
|
-
]
|
|
179
|
-
|
|
180
|
-
|
|
181
99
|
PropertyType = Annotated[
|
|
182
100
|
str,
|
|
183
101
|
AfterValidator(
|
|
@@ -194,146 +112,3 @@ PropertyType = Annotated[
|
|
|
194
112
|
)
|
|
195
113
|
),
|
|
196
114
|
]
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def _reference_entity_type_before_validator(value: Any | None = None) -> Any:
|
|
200
|
-
if not value:
|
|
201
|
-
return None
|
|
202
|
-
elif isinstance(value, rdflib.URIRef | ReferenceEntity):
|
|
203
|
-
return value
|
|
204
|
-
elif ReferenceEntity.from_raw(value).prefix == Undefined:
|
|
205
|
-
# case1: then this must be a URIRef!
|
|
206
|
-
return rdflib.URIRef(str(TypeAdapter(HttpUrl).validate_python(value)))
|
|
207
|
-
else:
|
|
208
|
-
# case2: this is a ReferenceEntity
|
|
209
|
-
return ReferenceEntity.from_raw(value)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
ReferenceType = Annotated[
|
|
213
|
-
ReferenceEntity | rdflib.URIRef | None,
|
|
214
|
-
BeforeValidator(_reference_entity_type_before_validator),
|
|
215
|
-
PlainSerializer(
|
|
216
|
-
lambda v: v.versioned_id if isinstance(v, ReferenceEntity) else str(v),
|
|
217
|
-
return_type=str,
|
|
218
|
-
when_used="unless-none",
|
|
219
|
-
),
|
|
220
|
-
]
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
ContainerType = Annotated[
|
|
224
|
-
ContainerEntity,
|
|
225
|
-
BeforeValidator(ContainerEntity.from_raw),
|
|
226
|
-
PlainSerializer(
|
|
227
|
-
lambda v: v.versioned_id,
|
|
228
|
-
return_type=str,
|
|
229
|
-
when_used="unless-none",
|
|
230
|
-
),
|
|
231
|
-
]
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
ViewType = Annotated[
|
|
235
|
-
ViewEntity,
|
|
236
|
-
BeforeValidator(ViewEntity.from_raw),
|
|
237
|
-
PlainSerializer(
|
|
238
|
-
lambda v: v.versioned_id,
|
|
239
|
-
return_type=str,
|
|
240
|
-
when_used="unless-none",
|
|
241
|
-
),
|
|
242
|
-
]
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def _semantic_value_type_before_validator(value: Any) -> Any:
|
|
246
|
-
if isinstance(value, XSDValueType | ClassEntity) or not isinstance(value, str):
|
|
247
|
-
return value
|
|
248
|
-
elif value in XSD_VALUE_TYPE_MAPPINGS:
|
|
249
|
-
return XSD_VALUE_TYPE_MAPPINGS[value]
|
|
250
|
-
elif value.lower() in XSD_VALUE_TYPE_MAPPINGS:
|
|
251
|
-
return XSD_VALUE_TYPE_MAPPINGS[value.lower()]
|
|
252
|
-
elif value == "#N/A":
|
|
253
|
-
return ClassEntity(prefix=Undefined, suffix=Unknown)
|
|
254
|
-
else:
|
|
255
|
-
return ClassEntity.from_raw(value)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def _semantic_value_type_serializer(value: Any) -> str:
|
|
259
|
-
if isinstance(value, ClassEntity):
|
|
260
|
-
return value.versioned_id
|
|
261
|
-
elif isinstance(value, XSDValueType):
|
|
262
|
-
return value.suffix
|
|
263
|
-
else:
|
|
264
|
-
return str(value)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def _dms_value_type_before_validator(value: Any) -> Any:
|
|
268
|
-
if isinstance(value, DMSValueType | ViewPropEntity) or not isinstance(value, str):
|
|
269
|
-
return value
|
|
270
|
-
elif value in DMS_VALUE_TYPE_MAPPINGS:
|
|
271
|
-
return DMS_VALUE_TYPE_MAPPINGS[value]
|
|
272
|
-
elif value.lower() in DMS_VALUE_TYPE_MAPPINGS:
|
|
273
|
-
return DMS_VALUE_TYPE_MAPPINGS[value.lower()]
|
|
274
|
-
else:
|
|
275
|
-
return ViewPropEntity.from_raw(value)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
SemanticValueType = Annotated[
|
|
279
|
-
XSDValueType | ClassEntity,
|
|
280
|
-
BeforeValidator(_semantic_value_type_before_validator),
|
|
281
|
-
PlainSerializer(
|
|
282
|
-
_semantic_value_type_serializer,
|
|
283
|
-
return_type=str,
|
|
284
|
-
when_used="unless-none",
|
|
285
|
-
),
|
|
286
|
-
]
|
|
287
|
-
|
|
288
|
-
CdfValueType = Annotated[
|
|
289
|
-
DMSValueType | ViewPropEntity,
|
|
290
|
-
BeforeValidator(_dms_value_type_before_validator),
|
|
291
|
-
PlainSerializer(
|
|
292
|
-
lambda v: v.versioned_id if isinstance(v, ViewPropEntity | ViewEntity) else v.dms()._type,
|
|
293
|
-
return_type=str,
|
|
294
|
-
when_used="unless-none",
|
|
295
|
-
),
|
|
296
|
-
]
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
def _from_str_or_list_container(value: Any) -> list[ContainerEntity] | Any:
|
|
300
|
-
if not value:
|
|
301
|
-
return value
|
|
302
|
-
if isinstance(value, str):
|
|
303
|
-
return [ContainerEntity.from_raw(entry.strip()) for entry in value.split(",")]
|
|
304
|
-
elif isinstance(value, list):
|
|
305
|
-
return [ContainerEntity.from_raw(entry.strip()) if isinstance(entry, str) else entry for entry in value]
|
|
306
|
-
else:
|
|
307
|
-
return value
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def _from_str_or_list_view(value: Any) -> list[ViewEntity] | Any:
|
|
311
|
-
if not value:
|
|
312
|
-
return value
|
|
313
|
-
if isinstance(value, str):
|
|
314
|
-
return [ViewEntity.from_raw(entry.strip()) for entry in value.split(",")]
|
|
315
|
-
elif isinstance(value, list):
|
|
316
|
-
return [ViewEntity.from_raw(entry.strip()) if isinstance(entry, str) else entry for entry in value]
|
|
317
|
-
else:
|
|
318
|
-
return value
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
ContainerListType = Annotated[
|
|
322
|
-
list[ContainerEntity],
|
|
323
|
-
BeforeValidator(_from_str_or_list_container),
|
|
324
|
-
PlainSerializer(
|
|
325
|
-
lambda v: ",".join([entry.versioned_id for entry in v]),
|
|
326
|
-
return_type=str,
|
|
327
|
-
when_used="unless-none",
|
|
328
|
-
),
|
|
329
|
-
]
|
|
330
|
-
|
|
331
|
-
ViewListType = Annotated[
|
|
332
|
-
list[ViewEntity],
|
|
333
|
-
BeforeValidator(_from_str_or_list_view),
|
|
334
|
-
PlainSerializer(
|
|
335
|
-
lambda v: ",".join([entry.versioned_id for entry in v]),
|
|
336
|
-
return_type=str,
|
|
337
|
-
when_used="unless-none",
|
|
338
|
-
),
|
|
339
|
-
]
|
|
@@ -60,7 +60,9 @@ class DataModelingLoader(
|
|
|
60
60
|
except CogniteAPIError as e:
|
|
61
61
|
failed_items = {failed.as_id() for failed in e.failed if hasattr(failed, "as_id")}
|
|
62
62
|
to_redeploy = [
|
|
63
|
-
item
|
|
63
|
+
item
|
|
64
|
+
for item in items
|
|
65
|
+
if item.as_id() in failed_items and item.as_id() not in tried_force_deploy # type: ignore[attr-defined]
|
|
64
66
|
]
|
|
65
67
|
if not to_redeploy:
|
|
66
68
|
# Avoid infinite loop
|
|
@@ -81,12 +81,10 @@ class RawTableLoader(ResourceLoader[RawTableID, RawTableWrite, RawTable, RawTabl
|
|
|
81
81
|
return item.as_id()
|
|
82
82
|
|
|
83
83
|
@overload
|
|
84
|
-
def _groupby_database(self, items: Sequence[RawTableWrite]) -> Iterable[tuple[str, Iterable[RawTableWrite]]]:
|
|
85
|
-
...
|
|
84
|
+
def _groupby_database(self, items: Sequence[RawTableWrite]) -> Iterable[tuple[str, Iterable[RawTableWrite]]]: ...
|
|
86
85
|
|
|
87
86
|
@overload
|
|
88
|
-
def _groupby_database(self, items: SequenceNotStr[RawTableID]) -> Iterable[tuple[str, Iterable[RawTableID]]]:
|
|
89
|
-
...
|
|
87
|
+
def _groupby_database(self, items: SequenceNotStr[RawTableID]) -> Iterable[tuple[str, Iterable[RawTableID]]]: ...
|
|
90
88
|
|
|
91
89
|
def _groupby_database(
|
|
92
90
|
self, items: Sequence[RawTableWrite] | SequenceNotStr[RawTableID]
|
|
@@ -37,8 +37,7 @@ def read_individual_sheet(
|
|
|
37
37
|
sheet_name: str,
|
|
38
38
|
return_read_info: Literal[True],
|
|
39
39
|
expected_headers: list[str] | None = None,
|
|
40
|
-
) -> tuple[list[dict], SpreadsheetRead]:
|
|
41
|
-
...
|
|
40
|
+
) -> tuple[list[dict], SpreadsheetRead]: ...
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
@overload
|
|
@@ -47,8 +46,7 @@ def read_individual_sheet(
|
|
|
47
46
|
sheet_name: str,
|
|
48
47
|
return_read_info: Literal[False] = False,
|
|
49
48
|
expected_headers: list[str] | None = None,
|
|
50
|
-
) -> list[dict]:
|
|
51
|
-
...
|
|
49
|
+
) -> list[dict]: ...
|
|
52
50
|
|
|
53
51
|
|
|
54
52
|
def read_individual_sheet(
|
cognite/neat/utils/utils.py
CHANGED
|
@@ -74,13 +74,11 @@ def _get_cognite_client(config: CogniteClientConfig, credentials: CredentialProv
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
@overload
|
|
77
|
-
def remove_namespace(*URI: URIRef | str, special_separator: str = "#_") -> str:
|
|
78
|
-
...
|
|
77
|
+
def remove_namespace(*URI: URIRef | str, special_separator: str = "#_") -> str: ...
|
|
79
78
|
|
|
80
79
|
|
|
81
80
|
@overload
|
|
82
|
-
def remove_namespace(*URI: tuple[URIRef | str, ...], special_separator: str = "#_") -> tuple[str, ...]:
|
|
83
|
-
...
|
|
81
|
+
def remove_namespace(*URI: tuple[URIRef | str, ...], special_separator: str = "#_") -> tuple[str, ...]: ...
|
|
84
82
|
|
|
85
83
|
|
|
86
84
|
def remove_namespace(
|
cognite/neat/workflows/base.py
CHANGED
|
@@ -11,8 +11,8 @@ import yaml
|
|
|
11
11
|
from cognite.client import ClientConfig, CogniteClient
|
|
12
12
|
from prometheus_client import Gauge
|
|
13
13
|
|
|
14
|
-
from cognite.neat.app.api.configuration import Config
|
|
15
14
|
from cognite.neat.app.monitoring.metrics import NeatMetricsCollector
|
|
15
|
+
from cognite.neat.config import Config
|
|
16
16
|
from cognite.neat.utils.utils import retry_decorator
|
|
17
17
|
from cognite.neat.workflows import cdf_store, utils
|
|
18
18
|
from cognite.neat.workflows._exceptions import ConfigurationNotSet, InvalidStepOutputException
|
|
@@ -45,7 +45,7 @@ class BaseWorkflow:
|
|
|
45
45
|
self,
|
|
46
46
|
name: str,
|
|
47
47
|
client: CogniteClient,
|
|
48
|
-
steps_registry: StepsRegistry
|
|
48
|
+
steps_registry: StepsRegistry,
|
|
49
49
|
workflow_steps: list[WorkflowStepDefinition] | None = None,
|
|
50
50
|
default_dataset_id: int | None = None,
|
|
51
51
|
):
|
|
@@ -81,7 +81,7 @@ class BaseWorkflow:
|
|
|
81
81
|
self.auto_workflow_cleanup = False
|
|
82
82
|
self.step_classes = None
|
|
83
83
|
self.data: dict[str, DataContract | FlowMessage | CdfStore | CogniteClient | None] = {}
|
|
84
|
-
self.steps_registry: StepsRegistry = steps_registry
|
|
84
|
+
self.steps_registry: StepsRegistry = steps_registry
|
|
85
85
|
|
|
86
86
|
def start(self, sync=False, is_ephemeral=False, **kwargs) -> FlowMessage | None:
|
|
87
87
|
"""Starts workflow execution.sync=True will block until workflow is completed and
|
|
@@ -223,7 +223,7 @@ class BaseWorkflow:
|
|
|
223
223
|
|
|
224
224
|
def copy(self) -> "BaseWorkflow":
|
|
225
225
|
"""Create a copy of the workflow"""
|
|
226
|
-
new_instance = self.__class__(self.name, self.cdf_client)
|
|
226
|
+
new_instance = self.__class__(self.name, self.cdf_client, self.steps_registry)
|
|
227
227
|
new_instance.workflow_steps = self.workflow_steps
|
|
228
228
|
new_instance.configs = self.configs
|
|
229
229
|
new_instance.set_task_builder(self.task_builder)
|
|
@@ -345,7 +345,7 @@ class BaseWorkflow:
|
|
|
345
345
|
raise Exception(f"Workflow {self.name} is not running , step {step_name} is skipped")
|
|
346
346
|
self.state = WorkflowState.RUNNING_WAITING
|
|
347
347
|
timeout = 3000000.0
|
|
348
|
-
if
|
|
348
|
+
if step.params.get("wait_timeout"):
|
|
349
349
|
timeout = float(step.params["wait_timeout"])
|
|
350
350
|
# reporting workflow execution before waiting for event
|
|
351
351
|
logging.info(f"Workflow {self.name} is waiting for event")
|
|
@@ -4,15 +4,15 @@ import shutil
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
import traceback
|
|
7
|
-
from pathlib import Path
|
|
8
7
|
|
|
9
8
|
from cognite.client import CogniteClient
|
|
10
9
|
from prometheus_client import Gauge
|
|
11
10
|
from pydantic import BaseModel
|
|
12
11
|
|
|
12
|
+
from cognite.neat.config import Config
|
|
13
13
|
from cognite.neat.workflows import BaseWorkflow
|
|
14
14
|
from cognite.neat.workflows.base import WorkflowDefinition
|
|
15
|
-
from cognite.neat.workflows.model import FlowMessage, InstanceStartMethod, WorkflowState
|
|
15
|
+
from cognite.neat.workflows.model import FlowMessage, InstanceStartMethod, WorkflowState, WorkflowStepDefinition
|
|
16
16
|
from cognite.neat.workflows.steps_registry import StepsRegistry
|
|
17
17
|
from cognite.neat.workflows.tasks import WorkflowTaskBuilder
|
|
18
18
|
|
|
@@ -28,32 +28,21 @@ class WorkflowStartStatus(BaseModel, arbitrary_types_allowed=True):
|
|
|
28
28
|
class WorkflowManager:
|
|
29
29
|
"""Workflow manager is responsible for loading, saving and managing workflows
|
|
30
30
|
client: CogniteClient
|
|
31
|
-
|
|
32
|
-
workflows_storage_path: Path = Path("workflows")
|
|
33
|
-
rules_storage_path: Path = Path("rules")
|
|
34
|
-
data_set_id: int = None,
|
|
31
|
+
config: Config
|
|
35
32
|
"""
|
|
36
33
|
|
|
37
|
-
def __init__(
|
|
38
|
-
self,
|
|
39
|
-
client: CogniteClient | None = None,
|
|
40
|
-
registry_storage_type: str = "file",
|
|
41
|
-
workflows_storage_path: Path | None = None,
|
|
42
|
-
rules_storage_path: Path | None = None,
|
|
43
|
-
data_store_path: Path | None = None,
|
|
44
|
-
data_set_id: int | None = None,
|
|
45
|
-
):
|
|
34
|
+
def __init__(self, client: CogniteClient, config: Config):
|
|
46
35
|
self.client = client
|
|
47
|
-
self.data_set_id =
|
|
48
|
-
self.data_store_path = data_store_path
|
|
36
|
+
self.data_set_id = config.cdf_default_dataset_id
|
|
37
|
+
self.data_store_path = config.data_store_path
|
|
49
38
|
self.workflow_registry: dict[str, BaseWorkflow] = {}
|
|
50
39
|
self.ephemeral_instance_registry: dict[str, BaseWorkflow] = {}
|
|
51
|
-
self.workflows_storage_type =
|
|
52
|
-
|
|
53
|
-
self.workflows_storage_path =
|
|
54
|
-
self.rules_storage_path =
|
|
40
|
+
self.workflows_storage_type = config.workflows_store_type
|
|
41
|
+
self.config = config
|
|
42
|
+
self.workflows_storage_path = config.workflows_store_path
|
|
43
|
+
self.rules_storage_path = config.rules_store_path
|
|
55
44
|
self.task_builder = WorkflowTaskBuilder(client, self)
|
|
56
|
-
self.steps_registry = StepsRegistry(self.
|
|
45
|
+
self.steps_registry = StepsRegistry(self.config)
|
|
57
46
|
self.steps_registry.load_step_classes()
|
|
58
47
|
|
|
59
48
|
def update_cdf_client(self, client: CogniteClient):
|
|
@@ -206,10 +195,21 @@ class WorkflowManager:
|
|
|
206
195
|
self, workflow_name: str, step_id: str = "", flow_msg: FlowMessage | None = None, sync: bool | None = None
|
|
207
196
|
) -> WorkflowStartStatus:
|
|
208
197
|
retrieved = self.get_workflow(workflow_name)
|
|
198
|
+
|
|
209
199
|
if retrieved is None:
|
|
210
200
|
return WorkflowStartStatus(
|
|
211
201
|
workflow_instance=None, is_success=False, status_text="Workflow not found in registry"
|
|
212
202
|
)
|
|
203
|
+
|
|
204
|
+
if self._is_workflow_made_of_mixed_steps(retrieved.workflow_steps):
|
|
205
|
+
retrieved.state = WorkflowState.FAILED
|
|
206
|
+
return WorkflowStartStatus(
|
|
207
|
+
workflow_instance=None,
|
|
208
|
+
is_success=False,
|
|
209
|
+
status_text="Workflow consists of both legacy and current steps. "
|
|
210
|
+
"Please update the workflow to use only current steps.",
|
|
211
|
+
)
|
|
212
|
+
|
|
213
213
|
workflow = retrieved
|
|
214
214
|
retrieved_step = workflow.get_trigger_step(step_id)
|
|
215
215
|
if retrieved_step is None:
|
|
@@ -280,3 +280,13 @@ class WorkflowManager:
|
|
|
280
280
|
return WorkflowStartStatus(
|
|
281
281
|
workflow_instance=None, is_success=False, status_text="Unsupported workflow start method"
|
|
282
282
|
)
|
|
283
|
+
|
|
284
|
+
def _is_workflow_made_of_mixed_steps(self, steps: list[WorkflowStepDefinition]):
|
|
285
|
+
legacy_steps = 0
|
|
286
|
+
current_steps = 0
|
|
287
|
+
for step in steps:
|
|
288
|
+
if step.method in self.steps_registry.categorized_steps["legacy"]:
|
|
289
|
+
legacy_steps += 1
|
|
290
|
+
if step.method in self.steps_registry.categorized_steps["current"]:
|
|
291
|
+
current_steps += 1
|
|
292
|
+
return legacy_steps > 0 and current_steps > 0
|
cognite/neat/workflows/model.py
CHANGED
|
@@ -45,9 +45,9 @@ class FlowMessage(BaseModel):
|
|
|
45
45
|
headers: dict[str, str] | None = None # The headers of the message
|
|
46
46
|
output_text: str | None = None # The output text of the step that is captured in the execution log
|
|
47
47
|
error_text: str | None = None # The error text of the step that is captured in the execution log
|
|
48
|
-
next_step_ids: list[
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
next_step_ids: list[str] | None = (
|
|
49
|
+
None # If set, the workflow will skip default route and go to the next step in the list
|
|
50
|
+
)
|
|
51
51
|
step_execution_status: StepExecutionStatus = StepExecutionStatus.UNKNOWN # The status of the step execution
|
|
52
52
|
|
|
53
53
|
|
|
@@ -101,7 +101,7 @@ class DeleteDataModelFromCDF(Step):
|
|
|
101
101
|
report_lines.append("\n\n# ERRORS\n\n")
|
|
102
102
|
report_lines.extend(errors)
|
|
103
103
|
|
|
104
|
-
output_dir = self.
|
|
104
|
+
output_dir = self.config.staging_path
|
|
105
105
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
106
106
|
report_file = "dms_component_creation_report.txt"
|
|
107
107
|
report_full_path = output_dir / report_file
|
|
@@ -203,7 +203,7 @@ class RulesToDMS(Step):
|
|
|
203
203
|
existing_handling=existing_components_handling,
|
|
204
204
|
)
|
|
205
205
|
|
|
206
|
-
output_dir = self.
|
|
206
|
+
output_dir = self.config.staging_path
|
|
207
207
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
208
208
|
file_name = (
|
|
209
209
|
input_rules.metadata.external_id
|
|
@@ -223,7 +223,7 @@ class RulesToDMS(Step):
|
|
|
223
223
|
report_lines.append("\n\n# ERRORS\n\n")
|
|
224
224
|
report_lines.extend(errors)
|
|
225
225
|
|
|
226
|
-
output_dir = self.
|
|
226
|
+
output_dir = self.config.staging_path
|
|
227
227
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
228
228
|
report_file = "dms_component_creation_report.txt"
|
|
229
229
|
report_full_path = output_dir / report_file
|
|
@@ -348,7 +348,7 @@ class RulesToOntology(Step):
|
|
|
348
348
|
step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
|
|
349
349
|
)
|
|
350
350
|
|
|
351
|
-
default_path = self.
|
|
351
|
+
default_path = self.config.staging_path / _get_default_file_name(rules, "ontology", "ttl")
|
|
352
352
|
|
|
353
353
|
storage_path = (
|
|
354
354
|
self.data_store_path / Path(self.configs["File path"]) if self.configs["File path"] else default_path
|
|
@@ -399,7 +399,7 @@ class RulesToSHACL(Step):
|
|
|
399
399
|
step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
|
|
400
400
|
)
|
|
401
401
|
|
|
402
|
-
default_path = self.
|
|
402
|
+
default_path = self.config.staging_path / _get_default_file_name(rules, "shacl", "ttl")
|
|
403
403
|
|
|
404
404
|
storage_path = (
|
|
405
405
|
self.data_store_path / Path(self.configs["File path"]) if self.configs["File path"] else default_path
|
|
@@ -450,7 +450,7 @@ class RulesToSemanticDataModel(Step):
|
|
|
450
450
|
step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
|
|
451
451
|
)
|
|
452
452
|
|
|
453
|
-
default_path = self.
|
|
453
|
+
default_path = self.config.staging_path / _get_default_file_name(rules, "semantic-data-model", "ttl")
|
|
454
454
|
|
|
455
455
|
storage_path = (
|
|
456
456
|
self.data_store_path / Path(self.configs["File path"]) if self.configs["File path"] else default_path
|
|
@@ -513,7 +513,7 @@ class RulesToCDFTransformations(Step):
|
|
|
513
513
|
dms_exporter = exporters.DMSExporter(
|
|
514
514
|
export_pipeline=True, instance_space=instance_space, export_components=["spaces"]
|
|
515
515
|
)
|
|
516
|
-
output_dir = self.
|
|
516
|
+
output_dir = self.config.staging_path
|
|
517
517
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
518
518
|
file_name = (
|
|
519
519
|
input_rules.metadata.external_id
|
|
@@ -533,7 +533,7 @@ class RulesToCDFTransformations(Step):
|
|
|
533
533
|
report_lines.append("\n\n# ERRORS\n\n")
|
|
534
534
|
report_lines.extend(errors)
|
|
535
535
|
|
|
536
|
-
output_dir = self.
|
|
536
|
+
output_dir = self.config.staging_path
|
|
537
537
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
538
538
|
report_file = "pipeline_creation_report.txt"
|
|
539
539
|
report_full_path = output_dir / report_file
|
|
@@ -6,8 +6,8 @@ from cognite.client import CogniteClient
|
|
|
6
6
|
|
|
7
7
|
from cognite.neat.rules import importers
|
|
8
8
|
from cognite.neat.rules.issues.formatters import FORMATTER_BY_NAME
|
|
9
|
+
from cognite.neat.rules.models.entities import DataModelEntity, DMSUnknownEntity
|
|
9
10
|
from cognite.neat.rules.models.rules import RoleTypes
|
|
10
|
-
from cognite.neat.rules.models.rules._types import DataModelEntity, Undefined
|
|
11
11
|
from cognite.neat.workflows._exceptions import StepNotInitialized
|
|
12
12
|
from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
|
|
13
13
|
from cognite.neat.workflows.steps.data_contracts import MultiRuleData
|
|
@@ -75,7 +75,7 @@ class ExcelToRules(Step):
|
|
|
75
75
|
rules, issues = excel_importer.to_rules(errors="continue", role=role_enum)
|
|
76
76
|
|
|
77
77
|
if rules is None:
|
|
78
|
-
output_dir = self.
|
|
78
|
+
output_dir = self.config.staging_path
|
|
79
79
|
report_writer = FORMATTER_BY_NAME[self.configs["Report formatter"]]()
|
|
80
80
|
report_writer.write_to_file(issues, file_or_dir_path=output_dir)
|
|
81
81
|
report_file = report_writer.default_file_name
|
|
@@ -137,7 +137,7 @@ class OntologyToRules(Step):
|
|
|
137
137
|
make_compliant = self.configs.get("Make compliant", "True") == "True"
|
|
138
138
|
|
|
139
139
|
if file_name:
|
|
140
|
-
rules_file_path =
|
|
140
|
+
rules_file_path = self.config.rules_store_path / file_name
|
|
141
141
|
elif full_path:
|
|
142
142
|
rules_file_path = full_path
|
|
143
143
|
else:
|
|
@@ -154,7 +154,7 @@ class OntologyToRules(Step):
|
|
|
154
154
|
rules, issues = ontology_importer.to_rules(errors="continue", role=role_enum)
|
|
155
155
|
|
|
156
156
|
if rules is None:
|
|
157
|
-
output_dir = self.
|
|
157
|
+
output_dir = self.config.staging_path
|
|
158
158
|
report_writer = FORMATTER_BY_NAME[self.configs["Report formatter"]]()
|
|
159
159
|
report_writer.write_to_file(issues, file_or_dir_path=output_dir)
|
|
160
160
|
report_file = report_writer.default_file_name
|
|
@@ -207,8 +207,8 @@ class DMSToRules(Step):
|
|
|
207
207
|
error_text = "Expected input payload to contain 'Data model id' key."
|
|
208
208
|
return FlowMessage(error_text=error_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
|
|
209
209
|
|
|
210
|
-
datamodel_entity = DataModelEntity.
|
|
211
|
-
if datamodel_entity
|
|
210
|
+
datamodel_entity = DataModelEntity.load(datamodel_id_str)
|
|
211
|
+
if isinstance(datamodel_entity, DMSUnknownEntity):
|
|
212
212
|
error_text = (
|
|
213
213
|
f"Data model id should be in the format 'my_space:my_data_model(version=1)' "
|
|
214
214
|
f"or 'my_space:my_data_model', failed to parse space from {datamodel_id_str}"
|
|
@@ -226,7 +226,7 @@ class DMSToRules(Step):
|
|
|
226
226
|
rules, issues = dms_importer.to_rules(errors="continue", role=role_enum)
|
|
227
227
|
|
|
228
228
|
if rules is None:
|
|
229
|
-
output_dir = self.
|
|
229
|
+
output_dir = self.config.staging_path
|
|
230
230
|
report_writer = FORMATTER_BY_NAME[self.configs["Report formatter"]]()
|
|
231
231
|
report_writer.write_to_file(issues, file_or_dir_path=output_dir)
|
|
232
232
|
report_file = report_writer.default_file_name
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .io_steps import * # noqa
|
|
@@ -7,11 +7,11 @@ from cognite.neat.workflows.steps.data_contracts import SolutionGraph, SourceGra
|
|
|
7
7
|
from cognite.neat.workflows.steps.step_model import Configurable, Step
|
|
8
8
|
|
|
9
9
|
__all__ = ["SimpleGraphEntityMatcher"]
|
|
10
|
-
CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [
|
|
10
|
+
CATEGORY = __name__.split(".")[-1].replace("_", " ").title() + " [LEGACY]"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class SimpleGraphEntityMatcher(Step):
|
|
14
|
-
version = "
|
|
14
|
+
version = "legacy"
|
|
15
15
|
description = "The step matches entities in the graph and creates links based on provided configurations"
|
|
16
16
|
category = CATEGORY
|
|
17
17
|
configurables: ClassVar[list[Configurable]] = [
|