cognite-neat 0.75.9__py3-none-any.whl → 0.76.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_version.py +1 -1
- cognite/neat/rules/exporters/_models.py +3 -0
- cognite/neat/rules/exporters/_rules2dms.py +46 -4
- cognite/neat/rules/exporters/_rules2excel.py +0 -9
- cognite/neat/rules/importers/_base.py +6 -0
- cognite/neat/rules/importers/_dms2rules.py +319 -125
- cognite/neat/rules/importers/_spreadsheet2rules.py +14 -8
- cognite/neat/rules/issues/base.py +3 -0
- cognite/neat/rules/issues/dms.py +142 -54
- cognite/neat/rules/issues/fileread.py +41 -0
- cognite/neat/rules/issues/importing.py +155 -0
- cognite/neat/rules/issues/spreadsheet.py +12 -9
- cognite/neat/rules/models/entities.py +29 -6
- cognite/neat/rules/models/rules/_base.py +5 -6
- cognite/neat/rules/models/rules/_dms_architect_rules.py +492 -332
- cognite/neat/rules/models/rules/_dms_rules_write.py +32 -30
- cognite/neat/rules/models/rules/_dms_schema.py +112 -22
- cognite/neat/rules/models/rules/_domain_rules.py +5 -0
- cognite/neat/rules/models/rules/_information_rules.py +13 -6
- cognite/neat/rules/models/wrapped_entities.py +166 -0
- {cognite_neat-0.75.9.dist-info → cognite_neat-0.76.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.75.9.dist-info → cognite_neat-0.76.0.dist-info}/RECORD +25 -24
- {cognite_neat-0.75.9.dist-info → cognite_neat-0.76.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.75.9.dist-info → cognite_neat-0.76.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.75.9.dist-info → cognite_neat-0.76.0.dist-info}/entry_points.txt +0 -0
cognite/neat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.76.0"
|
|
@@ -2,6 +2,8 @@ from abc import ABC
|
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
from functools import total_ordering
|
|
4
4
|
|
|
5
|
+
from cognite.neat.rules.issues import IssueList
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
@total_ordering
|
|
7
9
|
@dataclass
|
|
@@ -32,6 +34,7 @@ class UploadResult(UploadResultCore):
|
|
|
32
34
|
failed_changed: int = 0
|
|
33
35
|
failed_deleted: int = 0
|
|
34
36
|
error_messages: list[str] = field(default_factory=list)
|
|
37
|
+
issues: IssueList = field(default_factory=IssueList)
|
|
35
38
|
|
|
36
39
|
@property
|
|
37
40
|
def total(self) -> int:
|
|
@@ -4,10 +4,13 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Literal, TypeAlias, cast
|
|
5
5
|
|
|
6
6
|
from cognite.client import CogniteClient
|
|
7
|
-
from cognite.client.data_classes._base import CogniteResourceList
|
|
7
|
+
from cognite.client.data_classes._base import CogniteResource, CogniteResourceList
|
|
8
|
+
from cognite.client.data_classes.data_modeling import DataModelApply, DataModelId
|
|
8
9
|
from cognite.client.exceptions import CogniteAPIError
|
|
9
10
|
|
|
11
|
+
from cognite.neat.rules import issues
|
|
10
12
|
from cognite.neat.rules._shared import Rules
|
|
13
|
+
from cognite.neat.rules.issues import IssueList
|
|
11
14
|
from cognite.neat.rules.models.rules import InformationRules
|
|
12
15
|
from cognite.neat.rules.models.rules._base import ExtensionCategory, SheetList
|
|
13
16
|
from cognite.neat.rules.models.rules._dms_architect_rules import DMSContainer, DMSRules
|
|
@@ -43,6 +46,8 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
43
46
|
export_pipeline (bool, optional): Whether to export the pipeline. Defaults to False. This means setting
|
|
44
47
|
up transformations, RAW databases and tables to populate the data model.
|
|
45
48
|
instance_space (str, optional): The space to use for the instance. Defaults to None.
|
|
49
|
+
suppress_warnings (bool, optional): Suppress warnings. Defaults to False.
|
|
50
|
+
|
|
46
51
|
... note::
|
|
47
52
|
|
|
48
53
|
- "fail": If any component already exists, the export will fail.
|
|
@@ -59,12 +64,14 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
59
64
|
existing_handling: Literal["fail", "skip", "update", "force"] = "update",
|
|
60
65
|
export_pipeline: bool = False,
|
|
61
66
|
instance_space: str | None = None,
|
|
67
|
+
suppress_warnings: bool = False,
|
|
62
68
|
):
|
|
63
69
|
self.export_components = {export_components} if isinstance(export_components, str) else set(export_components)
|
|
64
70
|
self.include_space = include_space
|
|
65
71
|
self.existing_handling = existing_handling
|
|
66
72
|
self.export_pipeline = export_pipeline
|
|
67
73
|
self.instance_space = instance_space
|
|
74
|
+
self.suppress_warnings = suppress_warnings
|
|
68
75
|
self._schema: DMSSchema | None = None
|
|
69
76
|
|
|
70
77
|
def export_to_file(self, rules: Rules, filepath: Path) -> None:
|
|
@@ -115,11 +122,11 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
115
122
|
)
|
|
116
123
|
is_new_model = dms_rules.reference is None
|
|
117
124
|
if is_new_model or is_solution_model:
|
|
118
|
-
return dms_rules.as_schema(self.export_pipeline, self.instance_space)
|
|
125
|
+
return dms_rules.as_schema(False, self.export_pipeline, self.instance_space)
|
|
119
126
|
|
|
120
127
|
# This is an extension of an existing model.
|
|
121
128
|
reference_rules = cast(DMSRules, dms_rules.reference).model_copy(deep=True)
|
|
122
|
-
reference_schema = reference_rules.as_schema(self.export_pipeline)
|
|
129
|
+
reference_schema = reference_rules.as_schema(include_ref=False, include_pipeline=self.export_pipeline)
|
|
123
130
|
|
|
124
131
|
# Todo Move this to an appropriate location
|
|
125
132
|
# Merging Reference with User Rules
|
|
@@ -142,7 +149,7 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
142
149
|
property_.reference = None
|
|
143
150
|
combined_rules.properties.append(property_)
|
|
144
151
|
|
|
145
|
-
schema = combined_rules.as_schema(self.export_pipeline, self.instance_space)
|
|
152
|
+
schema = combined_rules.as_schema(True, self.export_pipeline, self.instance_space)
|
|
146
153
|
|
|
147
154
|
if dms_rules.metadata.extension in (ExtensionCategory.addition, ExtensionCategory.reshape):
|
|
148
155
|
# We do not freeze views as they might be changed, even for addition,
|
|
@@ -206,6 +213,7 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
206
213
|
redeploy_data_model = False
|
|
207
214
|
|
|
208
215
|
for all_items, loader in to_export:
|
|
216
|
+
issue_list = IssueList()
|
|
209
217
|
all_item_ids = loader.get_ids(all_items)
|
|
210
218
|
skipped = sum(1 for item_id in all_item_ids if item_id in schema.frozen_ids)
|
|
211
219
|
item_ids = [item_id for item_id in all_item_ids if item_id not in schema.frozen_ids]
|
|
@@ -249,6 +257,9 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
249
257
|
else:
|
|
250
258
|
raise ValueError(f"Unsupported existing_handling {self.existing_handling}")
|
|
251
259
|
|
|
260
|
+
warning_list = self._validate(loader, items)
|
|
261
|
+
issue_list.extend(warning_list)
|
|
262
|
+
|
|
252
263
|
error_messages: list[str] = []
|
|
253
264
|
if not dry_run:
|
|
254
265
|
if to_delete:
|
|
@@ -284,6 +295,7 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
284
295
|
failed_created=failed_created,
|
|
285
296
|
failed_changed=failed_changed,
|
|
286
297
|
error_messages=error_messages,
|
|
298
|
+
issues=issue_list,
|
|
287
299
|
)
|
|
288
300
|
|
|
289
301
|
if loader.resource_name == "views" and (created or changed) and not redeploy_data_model:
|
|
@@ -307,3 +319,33 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
307
319
|
to_export.append((schema.raw_tables, RawTableLoader(client)))
|
|
308
320
|
to_export.append((schema.transformations, TransformationLoader(client)))
|
|
309
321
|
return schema, to_export
|
|
322
|
+
|
|
323
|
+
def _validate(self, loader: ResourceLoader, items: list[CogniteResource]) -> IssueList:
|
|
324
|
+
issue_list = IssueList()
|
|
325
|
+
if isinstance(loader, DataModelLoader):
|
|
326
|
+
models = cast(list[DataModelApply], items)
|
|
327
|
+
if other_models := self._exist_other_data_models(loader, models):
|
|
328
|
+
warning = issues.dms.OtherDataModelsInSpaceWarning(models[0].space, other_models)
|
|
329
|
+
if not self.suppress_warnings:
|
|
330
|
+
warnings.warn(warning, stacklevel=2)
|
|
331
|
+
issue_list.append(warning)
|
|
332
|
+
|
|
333
|
+
return issue_list
|
|
334
|
+
|
|
335
|
+
@classmethod
|
|
336
|
+
def _exist_other_data_models(cls, loader: DataModelLoader, models: list[DataModelApply]) -> list[DataModelId]:
|
|
337
|
+
if not models:
|
|
338
|
+
return []
|
|
339
|
+
space = models[0].space
|
|
340
|
+
external_id = models[0].external_id
|
|
341
|
+
try:
|
|
342
|
+
data_models = loader.client.data_modeling.data_models.list(space=space, limit=25, all_versions=False)
|
|
343
|
+
except CogniteAPIError as e:
|
|
344
|
+
warnings.warn(issues.importing.APIWarning(str(e)), stacklevel=2)
|
|
345
|
+
return []
|
|
346
|
+
else:
|
|
347
|
+
return [
|
|
348
|
+
data_model.as_id()
|
|
349
|
+
for data_model in data_models
|
|
350
|
+
if (data_model.space, data_model.external_id) != (space, external_id)
|
|
351
|
+
]
|
|
@@ -120,13 +120,6 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
120
120
|
else:
|
|
121
121
|
sheet = workbook.create_sheet(sheet_name)
|
|
122
122
|
|
|
123
|
-
# Reorder such that the first column is class + the first field of the subclass
|
|
124
|
-
# of sheet entity. This is to make the properties/classes/views/containers sheet more readable.
|
|
125
|
-
# For example, for the properties these that means class, property, name, description
|
|
126
|
-
# instead of class, name, description, property
|
|
127
|
-
move = len(SheetEntity.model_fields) - 1 # -1 is for the class field
|
|
128
|
-
headers = headers[:1] + headers[move : move + 1] + headers[1:move] + headers[move + 1 :]
|
|
129
|
-
|
|
130
123
|
main_header = self._main_header_by_sheet_name[sheet_name]
|
|
131
124
|
sheet.append([main_header] + [""] * (len(headers) - 1))
|
|
132
125
|
sheet.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(headers))
|
|
@@ -150,8 +143,6 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
150
143
|
cell.border = Border(left=side, right=side, top=side, bottom=side)
|
|
151
144
|
fill_color = next(fill_colors)
|
|
152
145
|
|
|
153
|
-
# Need to do the same reordering as for the headers above
|
|
154
|
-
row = row[:1] + row[move : move + 1] + row[1:move] + row[move + 1 :]
|
|
155
146
|
sheet.append(row)
|
|
156
147
|
if self._styling_level > 2 and is_properties:
|
|
157
148
|
for cell in sheet[sheet.max_row]:
|
|
@@ -60,6 +60,12 @@ class BaseImporter(ABC):
|
|
|
60
60
|
else:
|
|
61
61
|
return output, issues
|
|
62
62
|
|
|
63
|
+
@classmethod
|
|
64
|
+
def _return_or_raise(cls, issue_list: IssueList, errors: Literal["raise", "continue"]) -> tuple[None, IssueList]:
|
|
65
|
+
if errors == "raise":
|
|
66
|
+
raise issue_list.as_errors()
|
|
67
|
+
return None, issue_list
|
|
68
|
+
|
|
63
69
|
def _default_metadata(self):
|
|
64
70
|
return {
|
|
65
71
|
"prefix": "neat",
|