cognite-neat 0.99.0__py3-none-any.whl → 0.100.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/_client/_api/data_modeling_loaders.py +390 -116
- cognite/neat/_client/_api/schema.py +63 -2
- cognite/neat/_client/data_classes/data_modeling.py +4 -0
- cognite/neat/_client/data_classes/schema.py +2 -348
- cognite/neat/_constants.py +27 -4
- cognite/neat/_graph/extractors/_base.py +7 -0
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +28 -18
- cognite/neat/_graph/loaders/_rdf2dms.py +52 -13
- cognite/neat/_graph/transformers/__init__.py +3 -3
- cognite/neat/_graph/transformers/_classic_cdf.py +135 -56
- cognite/neat/_issues/_base.py +26 -17
- cognite/neat/_issues/errors/__init__.py +4 -2
- cognite/neat/_issues/errors/_external.py +7 -0
- cognite/neat/_issues/errors/_properties.py +2 -7
- cognite/neat/_issues/errors/_resources.py +1 -1
- cognite/neat/_issues/warnings/__init__.py +6 -2
- cognite/neat/_issues/warnings/_external.py +9 -1
- cognite/neat/_issues/warnings/_resources.py +41 -2
- cognite/neat/_issues/warnings/user_modeling.py +4 -4
- cognite/neat/_rules/_constants.py +2 -6
- cognite/neat/_rules/analysis/_base.py +15 -5
- cognite/neat/_rules/analysis/_dms.py +20 -0
- cognite/neat/_rules/analysis/_information.py +22 -0
- cognite/neat/_rules/exporters/_base.py +3 -5
- cognite/neat/_rules/exporters/_rules2dms.py +190 -200
- cognite/neat/_rules/importers/__init__.py +1 -3
- cognite/neat/_rules/importers/_base.py +1 -1
- cognite/neat/_rules/importers/_dms2rules.py +3 -25
- cognite/neat/_rules/importers/_rdf/__init__.py +5 -0
- cognite/neat/_rules/importers/_rdf/_base.py +34 -11
- cognite/neat/_rules/importers/_rdf/_imf2rules.py +91 -0
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +40 -7
- cognite/neat/_rules/importers/_rdf/_owl2rules.py +80 -0
- cognite/neat/_rules/importers/_rdf/_shared.py +138 -441
- cognite/neat/_rules/models/_base_rules.py +19 -0
- cognite/neat/_rules/models/_types.py +5 -0
- cognite/neat/_rules/models/dms/__init__.py +2 -0
- cognite/neat/_rules/models/dms/_exporter.py +247 -123
- cognite/neat/_rules/models/dms/_rules.py +7 -49
- cognite/neat/_rules/models/dms/_rules_input.py +8 -3
- cognite/neat/_rules/models/dms/_validation.py +421 -123
- cognite/neat/_rules/models/entities/_multi_value.py +3 -0
- cognite/neat/_rules/models/information/__init__.py +2 -0
- cognite/neat/_rules/models/information/_rules.py +17 -61
- cognite/neat/_rules/models/information/_rules_input.py +11 -2
- cognite/neat/_rules/models/information/_validation.py +107 -11
- cognite/neat/_rules/models/mapping/_classic2core.py +1 -1
- cognite/neat/_rules/models/mapping/_classic2core.yaml +8 -4
- cognite/neat/_rules/transformers/__init__.py +2 -1
- cognite/neat/_rules/transformers/_converters.py +163 -61
- cognite/neat/_rules/transformers/_mapping.py +132 -2
- cognite/neat/_rules/transformers/_pipelines.py +1 -1
- cognite/neat/_rules/transformers/_verification.py +29 -4
- cognite/neat/_session/_base.py +46 -60
- cognite/neat/_session/_mapping.py +105 -5
- cognite/neat/_session/_prepare.py +49 -14
- cognite/neat/_session/_read.py +50 -4
- cognite/neat/_session/_set.py +1 -0
- cognite/neat/_session/_to.py +38 -12
- cognite/neat/_session/_wizard.py +5 -0
- cognite/neat/_session/engine/_interface.py +3 -2
- cognite/neat/_session/exceptions.py +4 -0
- cognite/neat/_store/_base.py +79 -19
- cognite/neat/_utils/collection_.py +22 -0
- cognite/neat/_utils/rdf_.py +30 -4
- cognite/neat/_version.py +2 -2
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +3 -91
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +2 -16
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +3 -5
- {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/RECORD +74 -82
- cognite/neat/_rules/importers/_rdf/_imf2rules/__init__.py +0 -3
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +0 -86
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +0 -29
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +0 -130
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2rules.py +0 -154
- cognite/neat/_rules/importers/_rdf/_owl2rules/__init__.py +0 -3
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +0 -58
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +0 -65
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +0 -59
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2rules.py +0 -39
- {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,6 +2,8 @@ import functools
|
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from cognite.neat._issues.errors import CDFMissingClientError
|
|
6
|
+
|
|
5
7
|
from ._collector import _COLLECTOR
|
|
6
8
|
|
|
7
9
|
try:
|
|
@@ -27,6 +29,8 @@ def _session_method_wrapper(func: Callable, cls_name: str):
|
|
|
27
29
|
except NeatSessionError as e:
|
|
28
30
|
action = _get_action()
|
|
29
31
|
print(f"{_PREFIX} Cannot {action}: {e}")
|
|
32
|
+
except CDFMissingClientError as e:
|
|
33
|
+
print(f"{_PREFIX} {e.as_message()}")
|
|
30
34
|
except ModuleNotFoundError as e:
|
|
31
35
|
if e.name == "neatengine":
|
|
32
36
|
action = _get_action()
|
cognite/neat/_store/_base.py
CHANGED
|
@@ -174,34 +174,68 @@ class NeatGraphStore:
|
|
|
174
174
|
self._add_triples(extractor.extract())
|
|
175
175
|
|
|
176
176
|
if success:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
177
|
+
_end = datetime.now(timezone.utc)
|
|
178
|
+
# Need to do the hasattr in case the extractor comes from NeatEngine.
|
|
179
|
+
activities = (
|
|
180
|
+
extractor._get_activity_names()
|
|
181
|
+
if hasattr(extractor, "_get_activity_names")
|
|
182
|
+
else [type(extractor).__name__]
|
|
184
183
|
)
|
|
184
|
+
for activity in activities:
|
|
185
|
+
self.provenance.append(
|
|
186
|
+
Change.record(
|
|
187
|
+
activity=activity,
|
|
188
|
+
start=_start,
|
|
189
|
+
end=_end,
|
|
190
|
+
description=f"Extracted triples to graph store using {type(extractor).__name__}",
|
|
191
|
+
)
|
|
192
|
+
)
|
|
185
193
|
|
|
186
|
-
def
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if
|
|
194
|
+
def _read_via_rules_linkage(
|
|
195
|
+
self, class_neat_id: URIRef, property_link_pairs: dict[str, URIRef] | None
|
|
196
|
+
) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
|
|
197
|
+
if self.rules is None:
|
|
190
198
|
warnings.warn("Rules not found in graph store!", stacklevel=2)
|
|
191
|
-
return
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
if cls := InformationAnalysis(self.rules).classes_by_neat_id.get(class_neat_id):
|
|
202
|
+
if property_link_pairs:
|
|
203
|
+
property_renaming_config = {
|
|
204
|
+
prop_uri: prop_name
|
|
205
|
+
for prop_name, prop_neat_id in property_link_pairs.items()
|
|
206
|
+
if (
|
|
207
|
+
prop_uri := InformationAnalysis(self.rules).neat_id_to_transformation_property_uri(prop_neat_id)
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
yield from self._read_via_class_entity(cls.class_, property_renaming_config)
|
|
212
|
+
return
|
|
213
|
+
else:
|
|
214
|
+
warnings.warn("Rules not linked", stacklevel=2)
|
|
215
|
+
return
|
|
216
|
+
else:
|
|
217
|
+
warnings.warn("Class with neat id {class_neat_id} found in rules", stacklevel=2)
|
|
218
|
+
return
|
|
192
219
|
|
|
193
|
-
|
|
220
|
+
def _read_via_class_entity(
|
|
221
|
+
self,
|
|
222
|
+
class_entity: ClassEntity,
|
|
223
|
+
property_renaming_config: dict[URIRef, str] | None = None,
|
|
224
|
+
) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
|
|
225
|
+
if self.rules is None:
|
|
226
|
+
warnings.warn("Rules not found in graph store!", stacklevel=2)
|
|
227
|
+
return
|
|
194
228
|
|
|
195
229
|
if class_entity not in [definition.class_ for definition in self.rules.classes]:
|
|
196
230
|
warnings.warn("Desired type not found in graph!", stacklevel=2)
|
|
197
|
-
return
|
|
231
|
+
return
|
|
198
232
|
|
|
199
233
|
if not (class_uri := InformationAnalysis(self.rules).class_uri(class_entity)):
|
|
200
234
|
warnings.warn(
|
|
201
|
-
f"Class {
|
|
235
|
+
f"Class {class_entity.suffix} does not have namespace defined for prefix {class_entity.prefix} Rules!",
|
|
202
236
|
stacklevel=2,
|
|
203
237
|
)
|
|
204
|
-
return
|
|
238
|
+
return
|
|
205
239
|
|
|
206
240
|
has_hop_transformations = InformationAnalysis(self.rules).has_hop_transformations()
|
|
207
241
|
has_self_reference_transformations = InformationAnalysis(
|
|
@@ -221,13 +255,15 @@ class NeatGraphStore:
|
|
|
221
255
|
msg,
|
|
222
256
|
stacklevel=2,
|
|
223
257
|
)
|
|
224
|
-
return
|
|
258
|
+
return
|
|
225
259
|
|
|
226
260
|
# get all the instances for give class_uri
|
|
227
261
|
instance_ids = self.queries.list_instances_ids_of_class(class_uri)
|
|
228
262
|
|
|
229
263
|
# get potential property renaming config
|
|
230
|
-
property_renaming_config = InformationAnalysis(
|
|
264
|
+
property_renaming_config = property_renaming_config or InformationAnalysis(
|
|
265
|
+
self.rules
|
|
266
|
+
).define_property_renaming_config(class_entity)
|
|
231
267
|
|
|
232
268
|
# get property types to guide process of removing or not namespaces from results
|
|
233
269
|
property_types = InformationAnalysis(self.rules).property_types(class_entity)
|
|
@@ -235,12 +271,36 @@ class NeatGraphStore:
|
|
|
235
271
|
for instance_id in instance_ids:
|
|
236
272
|
if res := self.queries.describe(
|
|
237
273
|
instance_id=instance_id,
|
|
238
|
-
instance_type=
|
|
274
|
+
instance_type=class_entity.suffix,
|
|
239
275
|
property_renaming_config=property_renaming_config,
|
|
240
276
|
property_types=property_types,
|
|
241
277
|
):
|
|
242
278
|
yield res
|
|
243
279
|
|
|
280
|
+
def read(
|
|
281
|
+
self,
|
|
282
|
+
class_: str,
|
|
283
|
+
) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
|
|
284
|
+
"""Read instances for given class from the graph store.
|
|
285
|
+
|
|
286
|
+
!!! note "Assumption"
|
|
287
|
+
This method assumes that the class_ belongs to the same (name)space as
|
|
288
|
+
the rules which are attached to the graph store.
|
|
289
|
+
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
if not self.rules:
|
|
293
|
+
warnings.warn("Rules not found in graph store!", stacklevel=2)
|
|
294
|
+
return
|
|
295
|
+
|
|
296
|
+
class_entity = ClassEntity(prefix=self.rules.metadata.prefix, suffix=class_)
|
|
297
|
+
|
|
298
|
+
if class_entity not in [definition.class_ for definition in self.rules.classes]:
|
|
299
|
+
warnings.warn("Desired type not found in graph!", stacklevel=2)
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
yield from self._read_via_class_entity(class_entity)
|
|
303
|
+
|
|
244
304
|
def _parse_file(
|
|
245
305
|
self,
|
|
246
306
|
filepath: Path,
|
|
@@ -2,6 +2,8 @@ from collections import Counter
|
|
|
2
2
|
from collections.abc import Iterable, Sequence
|
|
3
3
|
from typing import TypeVar
|
|
4
4
|
|
|
5
|
+
from cognite.neat._constants import IN_PYODIDE
|
|
6
|
+
|
|
5
7
|
T_Element = TypeVar("T_Element")
|
|
6
8
|
|
|
7
9
|
|
|
@@ -21,3 +23,23 @@ def chunker(sequence: Sequence[T_Element], chunk_size: int) -> Iterable[Sequence
|
|
|
21
23
|
|
|
22
24
|
def remove_list_elements(input_list: list, elements_to_remove: list) -> list:
|
|
23
25
|
return [element for element in input_list if element not in elements_to_remove]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def iterate_progress_bar(iterable: Iterable[T_Element], total: int, description: str) -> Iterable[T_Element]:
|
|
29
|
+
if IN_PYODIDE:
|
|
30
|
+
try:
|
|
31
|
+
from tqdm import tqdm # type: ignore [import]
|
|
32
|
+
except ModuleNotFoundError:
|
|
33
|
+
return iterable
|
|
34
|
+
return tqdm(iterable, total=total, desc=description)
|
|
35
|
+
# Progress bar from rich requires multi-threading, which is not supported in Pyodide
|
|
36
|
+
try:
|
|
37
|
+
from rich.progress import track
|
|
38
|
+
except ModuleNotFoundError:
|
|
39
|
+
return iterable
|
|
40
|
+
|
|
41
|
+
return track(
|
|
42
|
+
iterable,
|
|
43
|
+
total=total,
|
|
44
|
+
description=description,
|
|
45
|
+
)
|
cognite/neat/_utils/rdf_.py
CHANGED
|
@@ -115,13 +115,15 @@ def as_neat_compliant_uri(uri: URIRef) -> URIRef:
|
|
|
115
115
|
return URIRef(f"{namespace}{compliant_uri}")
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
def convert_rdflib_content(content: RdfLiteral | URIRef | dict | list) -> Any:
|
|
119
|
-
if isinstance(content, RdfLiteral)
|
|
118
|
+
def convert_rdflib_content(content: RdfLiteral | URIRef | dict | list, remove_namespace: bool = False) -> Any:
|
|
119
|
+
if isinstance(content, RdfLiteral):
|
|
120
120
|
return content.toPython()
|
|
121
|
+
elif isinstance(content, URIRef):
|
|
122
|
+
return remove_namespace_from_uri(content) if remove_namespace else content.toPython()
|
|
121
123
|
elif isinstance(content, dict):
|
|
122
|
-
return {key: convert_rdflib_content(value) for key, value in content.items()}
|
|
124
|
+
return {key: convert_rdflib_content(value, remove_namespace) for key, value in content.items()}
|
|
123
125
|
elif isinstance(content, list):
|
|
124
|
-
return [convert_rdflib_content(item) for item in content]
|
|
126
|
+
return [convert_rdflib_content(item, remove_namespace) for item in content]
|
|
125
127
|
else:
|
|
126
128
|
return content
|
|
127
129
|
|
|
@@ -206,6 +208,30 @@ def add_triples_in_batch(graph: Graph, triples: Iterable[Triple], batch_size: in
|
|
|
206
208
|
check_commit(force_commit=True)
|
|
207
209
|
|
|
208
210
|
|
|
211
|
+
def remove_triples_in_batch(graph: Graph, triples: Iterable[Triple], batch_size: int = 10_000) -> None:
|
|
212
|
+
"""Removes triples from the graph store in batches.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
triples: list of triples to be removed from the graph store
|
|
216
|
+
batch_size: Batch size of triples per commit, by default 10_000
|
|
217
|
+
"""
|
|
218
|
+
batch_count = 0
|
|
219
|
+
|
|
220
|
+
def check_commit(force_commit: bool = False):
|
|
221
|
+
"""Commit nodes to the graph if batch counter is reached or if force_commit is True"""
|
|
222
|
+
nonlocal batch_count
|
|
223
|
+
batch_count += 1
|
|
224
|
+
if force_commit or batch_count >= batch_size:
|
|
225
|
+
graph.commit()
|
|
226
|
+
batch_count = 0
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
for triple in triples:
|
|
230
|
+
graph.remove(triple)
|
|
231
|
+
check_commit()
|
|
232
|
+
check_commit(force_commit=True)
|
|
233
|
+
|
|
234
|
+
|
|
209
235
|
def remove_instance_ids_in_batch(graph: Graph, instance_ids: Iterable[URIRef], batch_size: int = 1_000) -> None:
|
|
210
236
|
"""Removes all triples related to the given instances in the graph store in batches.
|
|
211
237
|
|
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.
|
|
2
|
-
__engine__ = "^
|
|
1
|
+
__version__ = "0.100.0"
|
|
2
|
+
__engine__ = "^2.0.1"
|
|
@@ -21,7 +21,6 @@ __all__ = [
|
|
|
21
21
|
"RulesToOntology",
|
|
22
22
|
"RulesToSHACL",
|
|
23
23
|
"RulesToSemanticDataModel",
|
|
24
|
-
"RulesToCDFTransformations",
|
|
25
24
|
"DeleteDataModelFromCDF",
|
|
26
25
|
]
|
|
27
26
|
|
|
@@ -68,7 +67,7 @@ class DeleteDataModelFromCDF(Step):
|
|
|
68
67
|
if self.configs is None or self.data_store_path is None:
|
|
69
68
|
raise WorkflowStepNotInitializedError(type(self).__name__)
|
|
70
69
|
components_to_delete = {
|
|
71
|
-
cast(Literal["
|
|
70
|
+
cast(Literal["spaces", "data_models", "views", "containers"], key)
|
|
72
71
|
for key, value in self.complex_configs["Components"].items()
|
|
73
72
|
if value
|
|
74
73
|
}
|
|
@@ -181,7 +180,7 @@ class RulesToDMS(Step):
|
|
|
181
180
|
)
|
|
182
181
|
multi_space_components_create: bool = self.configs["Multi-space components create"] == "True"
|
|
183
182
|
components_to_create = {
|
|
184
|
-
cast(Literal["
|
|
183
|
+
cast(Literal["spaces", "data_models", "views", "containers"], key)
|
|
185
184
|
for key, value in self.complex_configs["Components"].items()
|
|
186
185
|
if value
|
|
187
186
|
}
|
|
@@ -209,7 +208,7 @@ class RulesToDMS(Step):
|
|
|
209
208
|
dms_exporter = exporters.DMSExporter(
|
|
210
209
|
export_components=frozenset(components_to_create),
|
|
211
210
|
include_space=(None if multi_space_components_create else {dms_rules.metadata.space}),
|
|
212
|
-
|
|
211
|
+
existing=existing_components_handling,
|
|
213
212
|
)
|
|
214
213
|
|
|
215
214
|
output_dir = self.config.staging_path
|
|
@@ -532,93 +531,6 @@ class RulesToSemanticDataModel(Step):
|
|
|
532
531
|
return FlowMessage(output_text=output_text)
|
|
533
532
|
|
|
534
533
|
|
|
535
|
-
class RulesToCDFTransformations(Step):
|
|
536
|
-
description = "This step exports transformations and RAW tables to populate a data model in CDF"
|
|
537
|
-
version = "private-alpha"
|
|
538
|
-
category = CATEGORY
|
|
539
|
-
configurables: ClassVar[list[Configurable]] = [
|
|
540
|
-
Configurable(
|
|
541
|
-
name="Dry run",
|
|
542
|
-
value="False",
|
|
543
|
-
label=("Whether to perform a dry run of the export. "),
|
|
544
|
-
options=["True", "False"],
|
|
545
|
-
),
|
|
546
|
-
Configurable(
|
|
547
|
-
name="Instance space",
|
|
548
|
-
value="",
|
|
549
|
-
label=(
|
|
550
|
-
"The space to use for the transformations instances. If provided, "
|
|
551
|
-
"the transformations will be set to populate"
|
|
552
|
-
"this space. If not provided, the space from the input rules will be used."
|
|
553
|
-
),
|
|
554
|
-
),
|
|
555
|
-
]
|
|
556
|
-
|
|
557
|
-
def run(self, rules: MultiRuleData, cdf_client: CogniteClient) -> FlowMessage: # type: ignore[override]
|
|
558
|
-
if self.configs is None or self.data_store_path is None:
|
|
559
|
-
raise WorkflowStepNotInitializedError(type(self).__name__)
|
|
560
|
-
|
|
561
|
-
input_rules = rules.dms or rules.information
|
|
562
|
-
if input_rules is None:
|
|
563
|
-
return FlowMessage(
|
|
564
|
-
error_text="Missing DMS or Information rules in the input data! "
|
|
565
|
-
"Please ensure that a DMS or Information rules is provided!",
|
|
566
|
-
step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
|
|
567
|
-
)
|
|
568
|
-
if isinstance(input_rules, DMSRules):
|
|
569
|
-
dms_rules = input_rules
|
|
570
|
-
elif isinstance(input_rules, InformationRules):
|
|
571
|
-
dms_rules = InformationToDMS().transform(input_rules).rules
|
|
572
|
-
else:
|
|
573
|
-
raise NotImplementedError(f"Unsupported rules type {type(input_rules)}")
|
|
574
|
-
|
|
575
|
-
instance_space = self.configs.get("Instance space") or dms_rules.metadata.space
|
|
576
|
-
dry_run = self.configs.get("Dry run", "False") == "True"
|
|
577
|
-
dms_exporter = exporters.DMSExporter(
|
|
578
|
-
export_pipeline=True, instance_space=instance_space, export_components=["spaces"]
|
|
579
|
-
)
|
|
580
|
-
output_dir = self.config.staging_path
|
|
581
|
-
output_dir.mkdir(parents=True, exist_ok=True)
|
|
582
|
-
file_name = dms_rules.metadata.external_id.replace(":", "_")
|
|
583
|
-
schema_zip = f"{file_name}_pipeline.zip"
|
|
584
|
-
schema_full_path = output_dir / schema_zip
|
|
585
|
-
|
|
586
|
-
dms_exporter.export_to_file(dms_rules, schema_full_path)
|
|
587
|
-
|
|
588
|
-
report_lines = ["# DMS Schema Export to CDF\n\n"]
|
|
589
|
-
errors = []
|
|
590
|
-
for result in dms_exporter.export_to_cdf_iterable(
|
|
591
|
-
rules=dms_rules, client=NeatClient(cdf_client), dry_run=dry_run
|
|
592
|
-
):
|
|
593
|
-
report_lines.append(str(result))
|
|
594
|
-
errors.extend(result.error_messages)
|
|
595
|
-
|
|
596
|
-
report_lines.append("\n\n# ERRORS\n\n")
|
|
597
|
-
report_lines.extend(errors)
|
|
598
|
-
|
|
599
|
-
output_dir = self.config.staging_path
|
|
600
|
-
output_dir.mkdir(parents=True, exist_ok=True)
|
|
601
|
-
report_file = "pipeline_creation_report.txt"
|
|
602
|
-
report_full_path = output_dir / report_file
|
|
603
|
-
report_full_path.write_text("\n".join(report_lines))
|
|
604
|
-
|
|
605
|
-
output_text = (
|
|
606
|
-
"<p></p>"
|
|
607
|
-
"Download Pipeline Export "
|
|
608
|
-
f'<a href="/data/staging/{report_file}?{time.time()}" '
|
|
609
|
-
f'target="_blank">Report</a>'
|
|
610
|
-
"<p></p>"
|
|
611
|
-
"Download Pipeline exported schema"
|
|
612
|
-
f'- <a href="/data/staging/{schema_zip}?{time.time()}" '
|
|
613
|
-
f'target="_blank">{schema_zip}</a>'
|
|
614
|
-
)
|
|
615
|
-
|
|
616
|
-
if errors:
|
|
617
|
-
return FlowMessage(error_text=output_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
|
|
618
|
-
else:
|
|
619
|
-
return FlowMessage(output_text=output_text)
|
|
620
|
-
|
|
621
|
-
|
|
622
534
|
def _get_default_file_name(rules: MultiRuleData, file_category: str = "ontology", extension: str = "ttl") -> str:
|
|
623
535
|
name = rules.information.metadata.prefix if rules.information else cast(DMSRules, rules.dms).metadata.space
|
|
624
536
|
version = rules.information.metadata.version if rules.information else cast(DMSRules, rules.dms).metadata.version
|
|
@@ -3,7 +3,6 @@ from pathlib import Path
|
|
|
3
3
|
from typing import ClassVar
|
|
4
4
|
|
|
5
5
|
from cognite.client import CogniteClient
|
|
6
|
-
from cognite.client.data_classes.data_modeling import DataModelId
|
|
7
6
|
|
|
8
7
|
from cognite.neat._client import NeatClient
|
|
9
8
|
from cognite.neat._issues.errors import WorkflowStepNotInitializedError
|
|
@@ -288,21 +287,8 @@ class DMSToRules(Step):
|
|
|
288
287
|
f"or 'my_space:my_data_model', failed to parse space from {datamodel_id_str}"
|
|
289
288
|
)
|
|
290
289
|
return FlowMessage(error_text=error_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if ref_datamodel_str:
|
|
294
|
-
ref_model = DataModelEntity.load(ref_datamodel_str)
|
|
295
|
-
if isinstance(ref_model, DMSUnknownEntity):
|
|
296
|
-
error_text = (
|
|
297
|
-
f"Reference data model id should be in the format 'my_space:my_data_model(version=1)' "
|
|
298
|
-
f"or 'my_space:my_data_model', failed to parse space from {ref_datamodel_str}"
|
|
299
|
-
)
|
|
300
|
-
return FlowMessage(error_text=error_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
|
|
301
|
-
ref_model_id = ref_model.as_id()
|
|
302
|
-
|
|
303
|
-
dms_importer = importers.DMSImporter.from_data_model_id(
|
|
304
|
-
NeatClient(cdf_client), datamodel_entity.as_id(), ref_model_id
|
|
305
|
-
)
|
|
290
|
+
|
|
291
|
+
dms_importer = importers.DMSImporter.from_data_model_id(NeatClient(cdf_client), datamodel_entity.as_id())
|
|
306
292
|
|
|
307
293
|
# if role is None, it will be inferred from the rules file
|
|
308
294
|
role = self.configs.get("Role")
|
|
@@ -11,6 +11,7 @@ from cognite.neat._issues import NeatIssueList
|
|
|
11
11
|
from cognite.neat._issues.errors import ResourceNotFoundError, WorkflowStepNotInitializedError
|
|
12
12
|
from cognite.neat._issues.formatters import FORMATTER_BY_NAME
|
|
13
13
|
from cognite.neat._rules.models import DMSRules
|
|
14
|
+
from cognite.neat._rules.models.dms import DMSValidation
|
|
14
15
|
from cognite.neat._workflows.model import FlowMessage, StepExecutionStatus
|
|
15
16
|
from cognite.neat._workflows.steps.data_contracts import MultiRuleData
|
|
16
17
|
from cognite.neat._workflows.steps.step_model import Configurable, Step
|
|
@@ -51,10 +52,7 @@ class ValidateRulesAgainstCDF(Step):
|
|
|
51
52
|
)
|
|
52
53
|
dms_rules = rules.dms
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
errors = schema.validate()
|
|
56
|
-
if not errors:
|
|
57
|
-
return FlowMessage(output_text="Rules are complete and valid. No need to fetch from CDF.")
|
|
55
|
+
errors = DMSValidation(dms_rules, NeatClient(cdf_client)).validate()
|
|
58
56
|
|
|
59
57
|
missing_spaces = [
|
|
60
58
|
error.identifier
|
|
@@ -85,11 +83,11 @@ class ValidateRulesAgainstCDF(Step):
|
|
|
85
83
|
f"and {len(retrieved_views)} views from CDF."
|
|
86
84
|
)
|
|
87
85
|
|
|
86
|
+
schema = dms_rules.as_schema()
|
|
88
87
|
schema.spaces.update({space.space: space for space in retrieved_spaces})
|
|
89
88
|
schema.containers.update({container.as_id(): container for container in retrieved_containers})
|
|
90
89
|
schema.views.update({view.as_id(): view for view in retrieved_views})
|
|
91
90
|
|
|
92
|
-
errors = schema.validate()
|
|
93
91
|
if errors:
|
|
94
92
|
output_dir = self.data_store_path / Path("staging")
|
|
95
93
|
report_writer = FORMATTER_BY_NAME[self.configs["Report Formatter"]]()
|