cognite-neat 0.72.3__py3-none-any.whl → 0.73.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/graph/extractor/_graph_capturing_sheet.py +14 -12
- cognite/neat/rules/examples/power-grid-containers.yaml +3 -0
- cognite/neat/rules/examples/power-grid-model.yaml +3 -0
- cognite/neat/rules/exporter/_rules2dms.py +3 -10
- cognite/neat/rules/exporter/_rules2excel.py +3 -2
- cognite/neat/rules/exporters/_rules2excel.py +94 -4
- cognite/neat/rules/importer/_dms2rules.py +1 -4
- cognite/neat/rules/importers/_dms2rules.py +40 -11
- cognite/neat/rules/issues/base.py +9 -1
- cognite/neat/rules/issues/dms.py +74 -0
- cognite/neat/rules/models/_rules/base.py +24 -2
- cognite/neat/rules/models/_rules/dms_architect_rules.py +123 -15
- cognite/neat/rules/models/_rules/dms_schema.py +5 -1
- cognite/neat/rules/models/_rules/domain_rules.py +14 -1
- cognite/neat/rules/models/_rules/information_rules.py +16 -2
- cognite/neat/utils/spreadsheet.py +2 -2
- {cognite_neat-0.72.3.dist-info → cognite_neat-0.73.0.dist-info}/METADATA +2 -2
- {cognite_neat-0.72.3.dist-info → cognite_neat-0.73.0.dist-info}/RECORD +22 -23
- cognite/neat/py.typed +0 -0
- {cognite_neat-0.72.3.dist-info → cognite_neat-0.73.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.72.3.dist-info → cognite_neat-0.73.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.72.3.dist-info → cognite_neat-0.73.0.dist-info}/entry_points.txt +0 -0
cognite/neat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.73.0"
|
|
@@ -11,6 +11,7 @@ from openpyxl.cell import Cell
|
|
|
11
11
|
from openpyxl.styles import Alignment, Border, Font, NamedStyle, PatternFill, Side
|
|
12
12
|
from openpyxl.utils import get_column_letter
|
|
13
13
|
from openpyxl.worksheet.datavalidation import DataValidation
|
|
14
|
+
from openpyxl.worksheet.worksheet import Worksheet
|
|
14
15
|
from rdflib import RDF, XSD, Literal, Namespace, URIRef
|
|
15
16
|
|
|
16
17
|
from cognite.neat.graph import exceptions
|
|
@@ -321,7 +322,7 @@ def rules2graph_capturing_sheet(
|
|
|
321
322
|
workbook.create_sheet(title=class_)
|
|
322
323
|
|
|
323
324
|
# Add header rows
|
|
324
|
-
workbook[class_].append(["identifier", *list(properties.keys())])
|
|
325
|
+
cast(Worksheet, workbook[class_]).append(["identifier", *list(properties.keys())])
|
|
325
326
|
|
|
326
327
|
if auto_identifier_type and auto_identifier_type == "index-based": # default, easy to read
|
|
327
328
|
logging.debug(f"Configuring index-based automatic identifiers for sheet {class_}")
|
|
@@ -350,35 +351,35 @@ def _add_index_identifiers(workbook: Workbook, sheet: str, no_rows: int):
|
|
|
350
351
|
"""Adds index-based auto identifier to a sheet identifier column"""
|
|
351
352
|
for i in range(no_rows):
|
|
352
353
|
prefix = to_dms_name(sheet, "class", True)
|
|
353
|
-
workbook[sheet][f"A{i+2}"] = f'=IF(ISBLANK(B{i+2}), "","{prefix}-{i+1}")'
|
|
354
|
+
workbook[sheet][f"A{i+2}"] = f'=IF(ISBLANK(B{i+2}), "","{prefix}-{i+1}")' # type: ignore[index]
|
|
354
355
|
|
|
355
356
|
|
|
356
357
|
def _add_uuid_identifiers(workbook: Workbook, sheet: str, no_rows: int):
|
|
357
358
|
"""Adds UUID-based auto identifier to a sheet identifier column"""
|
|
358
359
|
for i in range(no_rows):
|
|
359
360
|
prefix = to_dms_name(sheet, "class", True)
|
|
360
|
-
workbook[sheet][f"A{i+2}"] = f'=IF(ISBLANK(B{i+2}), "","{prefix}-{uuid.uuid4()}")'
|
|
361
|
+
workbook[sheet][f"A{i+2}"] = f'=IF(ISBLANK(B{i+2}), "","{prefix}-{uuid.uuid4()}")' # type: ignore[index]
|
|
361
362
|
|
|
362
363
|
|
|
363
364
|
def _add_drop_down_list(workbook: Workbook, sheet: str, column: str, no_rows: int, value_sheet: str, value_column: str):
|
|
364
365
|
"""Adds a drop down list to a column"""
|
|
365
366
|
drop_down_list = DataValidation(type="list", formula1=f"={value_sheet}!{value_column}$2:{value_column}${no_rows}")
|
|
366
367
|
|
|
367
|
-
workbook[sheet].add_data_validation(drop_down_list)
|
|
368
|
+
cast(Worksheet, workbook[sheet]).add_data_validation(drop_down_list)
|
|
368
369
|
|
|
369
370
|
for i in range(no_rows):
|
|
370
|
-
drop_down_list.add(workbook[sheet][f"{column}{i+2}"])
|
|
371
|
+
drop_down_list.add(workbook[sheet][f"{column}{i+2}"]) # type: ignore[index, misc]
|
|
371
372
|
|
|
372
373
|
|
|
373
374
|
def _adjust_column_width(workbook: Workbook):
|
|
374
375
|
"""Adjusts the column width based on the content"""
|
|
375
376
|
for sheet in workbook.sheetnames:
|
|
376
|
-
for cell_tuple in workbook[sheet].columns:
|
|
377
|
+
for cell_tuple in cast(Worksheet, workbook[sheet]).columns:
|
|
377
378
|
# Wrong type annotation in openpyxl
|
|
378
379
|
cell = cast(Cell, cell_tuple[0]) # type: ignore[index]
|
|
379
380
|
if cell.value:
|
|
380
381
|
adjusted_width = (len(str(cell.value)) + 5) * 1.2
|
|
381
|
-
workbook[sheet].column_dimensions[cell.column_letter].width = adjusted_width
|
|
382
|
+
cast(Worksheet, workbook[sheet]).column_dimensions[cell.column_letter].width = adjusted_width
|
|
382
383
|
|
|
383
384
|
|
|
384
385
|
def _set_header_style(workbook: Workbook):
|
|
@@ -390,12 +391,13 @@ def _set_header_style(workbook: Workbook):
|
|
|
390
391
|
workbook.add_named_style(style)
|
|
391
392
|
|
|
392
393
|
for sheet in workbook.sheetnames:
|
|
393
|
-
for cell_tuple in workbook[sheet].columns:
|
|
394
|
+
for cell_tuple in cast(Worksheet, workbook[sheet]).columns:
|
|
394
395
|
# Wrong type annotation in openpyxl
|
|
395
396
|
cell = cast(Cell, cell_tuple[0]) # type: ignore[index]
|
|
396
|
-
workbook[sheet]
|
|
397
|
+
worksheet = cast(Worksheet, workbook[sheet])
|
|
398
|
+
worksheet[f"{cell.column_letter}1"].style = style
|
|
397
399
|
if f"{cell.column_letter}1" == "A1":
|
|
398
|
-
|
|
400
|
+
worksheet[f"{cell.column_letter}1"].fill = PatternFill("solid", start_color="2FB5F2")
|
|
399
401
|
else:
|
|
400
|
-
|
|
401
|
-
|
|
402
|
+
worksheet[f"{cell.column_letter}1"].fill = PatternFill("solid", start_color="FFB202")
|
|
403
|
+
worksheet[f"{cell.column_letter}1"].alignment = Alignment(horizontal="center", vertical="center")
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
type:
|
|
54
54
|
container: null
|
|
55
55
|
type: direct
|
|
56
|
+
list: false
|
|
56
57
|
space: workshop
|
|
57
58
|
usedFor: node
|
|
58
59
|
- externalId: SubGeographicalRegion
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
type:
|
|
82
83
|
container: null
|
|
83
84
|
type: direct
|
|
85
|
+
list: false
|
|
84
86
|
space: workshop
|
|
85
87
|
usedFor: node
|
|
86
88
|
- externalId: Substation
|
|
@@ -109,5 +111,6 @@
|
|
|
109
111
|
type:
|
|
110
112
|
container: null
|
|
111
113
|
type: direct
|
|
114
|
+
list: false
|
|
112
115
|
space: workshop
|
|
113
116
|
usedFor: node
|
|
@@ -81,6 +81,7 @@ views:
|
|
|
81
81
|
type:
|
|
82
82
|
container: null
|
|
83
83
|
type: direct
|
|
84
|
+
list: false
|
|
84
85
|
source:
|
|
85
86
|
space: workshop
|
|
86
87
|
externalId: GeographicalRegion
|
|
@@ -132,6 +133,7 @@ views:
|
|
|
132
133
|
type:
|
|
133
134
|
container: null
|
|
134
135
|
type: direct
|
|
136
|
+
list: false
|
|
135
137
|
source:
|
|
136
138
|
space: workshop
|
|
137
139
|
externalId: SubGeographicalRegion
|
|
@@ -195,6 +197,7 @@ views:
|
|
|
195
197
|
type:
|
|
196
198
|
container: null
|
|
197
199
|
type: direct
|
|
200
|
+
list: false
|
|
198
201
|
source:
|
|
199
202
|
space: workshop
|
|
200
203
|
externalId: Substation
|
|
@@ -34,7 +34,6 @@ from cognite.client.data_classes.data_modeling import (
|
|
|
34
34
|
ViewApply,
|
|
35
35
|
ViewId,
|
|
36
36
|
)
|
|
37
|
-
from cognite.client.data_classes.data_modeling.data_types import ListablePropertyType
|
|
38
37
|
from cognite.client.data_classes.data_modeling.views import (
|
|
39
38
|
ConnectionDefinitionApply,
|
|
40
39
|
SingleHopConnectionDefinitionApply,
|
|
@@ -284,12 +283,9 @@ class DMSSchemaComponents(BaseModel):
|
|
|
284
283
|
if (
|
|
285
284
|
not isinstance(existing_property.type, DirectRelation)
|
|
286
285
|
and not isinstance(api_container_property.type, DirectRelation)
|
|
287
|
-
and
|
|
288
|
-
!= cast(ListablePropertyType, api_container_property.type).is_list
|
|
286
|
+
and existing_property.type.is_list != api_container_property.type.is_list
|
|
289
287
|
):
|
|
290
|
-
|
|
291
|
-
ListablePropertyType, containers[container_id].properties[container_property_id].type
|
|
292
|
-
).is_list = True
|
|
288
|
+
containers[container_id].properties[container_property_id].type.is_list = True
|
|
293
289
|
|
|
294
290
|
if errors:
|
|
295
291
|
raise ExceptionGroup("Properties value types have been redefined! This is prohibited! Aborting!", errors)
|
|
@@ -302,10 +298,7 @@ class DMSSchemaComponents(BaseModel):
|
|
|
302
298
|
|
|
303
299
|
# Literal, i.e. Node attribute
|
|
304
300
|
if property_.property_type is EntityTypes.data_property:
|
|
305
|
-
property_type = cast(
|
|
306
|
-
type[ListablePropertyType],
|
|
307
|
-
cast(ValueTypeMapping, property_.expected_value_type.mapping).dms,
|
|
308
|
-
)
|
|
301
|
+
property_type = cast(ValueTypeMapping, property_.expected_value_type.mapping).dms
|
|
309
302
|
return ContainerProperty(
|
|
310
303
|
type=property_type(is_list=is_one_to_many),
|
|
311
304
|
nullable=property_.min_count == 0,
|
|
@@ -4,6 +4,7 @@ from typing import cast
|
|
|
4
4
|
from openpyxl import Workbook
|
|
5
5
|
from openpyxl.cell import Cell
|
|
6
6
|
from openpyxl.styles import Alignment, Border, Font, NamedStyle, PatternFill, Side
|
|
7
|
+
from openpyxl.worksheet.worksheet import Worksheet
|
|
7
8
|
|
|
8
9
|
from cognite.neat.rules.models._base import EntityTypes
|
|
9
10
|
|
|
@@ -190,7 +191,7 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
190
191
|
if sheet == "Metadata":
|
|
191
192
|
continue
|
|
192
193
|
if sheet == "Classes" or sheet == "Properties":
|
|
193
|
-
sheet_obj = data[sheet]
|
|
194
|
+
sheet_obj = cast(Worksheet, data[sheet])
|
|
194
195
|
if sheet == "Classes":
|
|
195
196
|
sheet_obj.freeze_panes = "A3"
|
|
196
197
|
else:
|
|
@@ -207,6 +208,6 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
207
208
|
cell.fill = PatternFill("solid", start_color="D5DBD5")
|
|
208
209
|
cell.alignment = Alignment(horizontal="center", vertical="center")
|
|
209
210
|
adjusted_width = (len(str(cell.value)) + 5) * 1.2
|
|
210
|
-
data[sheet].column_dimensions[cell.column_letter].width = adjusted_width
|
|
211
|
+
cast(Worksheet, data[sheet]).column_dimensions[cell.column_letter].width = adjusted_width
|
|
211
212
|
|
|
212
213
|
return data
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import itertools
|
|
4
|
+
from datetime import datetime
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from types import GenericAlias
|
|
6
|
-
from typing import Any, ClassVar, Literal, get_args
|
|
7
|
+
from typing import Any, ClassVar, Literal, cast, get_args
|
|
7
8
|
|
|
8
9
|
from openpyxl import Workbook
|
|
9
10
|
from openpyxl.cell import MergedCell
|
|
10
11
|
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
|
|
12
|
+
from openpyxl.worksheet.worksheet import Worksheet
|
|
11
13
|
|
|
12
14
|
from cognite.neat.rules._shared import Rules
|
|
13
|
-
from cognite.neat.rules.models._rules
|
|
15
|
+
from cognite.neat.rules.models._rules import DMSRules, DomainRules, InformationRules
|
|
16
|
+
from cognite.neat.rules.models._rules.base import RoleTypes, SchemaCompleteness, SheetEntity
|
|
14
17
|
|
|
15
18
|
from ._base import BaseExporter
|
|
16
19
|
|
|
@@ -23,6 +26,9 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
23
26
|
on the different styles.
|
|
24
27
|
output_role: The role to use for the exported spreadsheet. If provided, the rules will be converted to
|
|
25
28
|
this role formate before being written to excel. If not provided, the role from the rules will be used.
|
|
29
|
+
new_model_id: The new model ID to use for the exported spreadsheet. This is only applicable if the input
|
|
30
|
+
rules have 'is_reference' set. If provided, the model ID will be used to automatically create the
|
|
31
|
+
new metadata sheet in the Excel file.
|
|
26
32
|
|
|
27
33
|
The following styles are available:
|
|
28
34
|
|
|
@@ -43,12 +49,18 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
43
49
|
}
|
|
44
50
|
style_options = get_args(Style)
|
|
45
51
|
|
|
46
|
-
def __init__(
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
styling: Style = "default",
|
|
55
|
+
output_role: RoleTypes | None = None,
|
|
56
|
+
new_model_id: tuple[str, str, str] | None = None,
|
|
57
|
+
):
|
|
47
58
|
if styling not in self.style_options:
|
|
48
59
|
raise ValueError(f"Invalid styling: {styling}. Valid options are {self.style_options}")
|
|
49
60
|
self.styling = styling
|
|
50
61
|
self._styling_level = self.style_options.index(styling)
|
|
51
62
|
self.output_role = output_role
|
|
63
|
+
self.new_model_id = new_model_id
|
|
52
64
|
|
|
53
65
|
def export_to_file(self, rules: Rules, filepath: Path) -> None:
|
|
54
66
|
"""Exports transformation rules to excel file."""
|
|
@@ -70,7 +82,7 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
70
82
|
if rules.is_reference:
|
|
71
83
|
# Writes empty reference sheets
|
|
72
84
|
dumped_rules = {
|
|
73
|
-
"Metadata":
|
|
85
|
+
"Metadata": self._create_metadata_sheet_user_rules(rules),
|
|
74
86
|
}
|
|
75
87
|
dumped_rules["Metadata"]["role"] = (
|
|
76
88
|
self.output_role and self.output_role.value
|
|
@@ -155,6 +167,12 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
155
167
|
cell.font = Font(bold=True, size=14)
|
|
156
168
|
|
|
157
169
|
def _write_metadata_sheet(self, workbook: Workbook, metadata: dict[str, Any], is_reference: bool = False) -> None:
|
|
170
|
+
# Excel does not support timezone in datetime strings
|
|
171
|
+
if isinstance(metadata.get("created"), datetime):
|
|
172
|
+
metadata["created"] = metadata["created"].replace(tzinfo=None)
|
|
173
|
+
if isinstance(metadata.get("updated"), datetime):
|
|
174
|
+
metadata["updated"] = metadata["updated"].replace(tzinfo=None)
|
|
175
|
+
|
|
158
176
|
if is_reference:
|
|
159
177
|
metadata_sheet = workbook.create_sheet("RefMetadata")
|
|
160
178
|
else:
|
|
@@ -181,6 +199,7 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
181
199
|
@classmethod
|
|
182
200
|
def _adjust_column_widths(cls, workbook: Workbook) -> None:
|
|
183
201
|
for sheet in workbook:
|
|
202
|
+
sheet = cast(Worksheet, sheet)
|
|
184
203
|
for column_cells in sheet.columns:
|
|
185
204
|
try:
|
|
186
205
|
max_length = max(len(str(cell.value)) for cell in column_cells if cell.value is not None)
|
|
@@ -194,3 +213,74 @@ class ExcelExporter(BaseExporter[Workbook]):
|
|
|
194
213
|
current = sheet.column_dimensions[selected_column.column_letter].width or (max_length + 0.5)
|
|
195
214
|
sheet.column_dimensions[selected_column.column_letter].width = max(current, max_length + 0.5)
|
|
196
215
|
return None
|
|
216
|
+
|
|
217
|
+
def _create_metadata_sheet_user_rules(self, rules: Rules) -> dict[str, Any]:
|
|
218
|
+
metadata: dict[str, Any] = {
|
|
219
|
+
field_alias: None for field_alias in rules.metadata.model_dump(by_alias=True).keys()
|
|
220
|
+
}
|
|
221
|
+
if "creator" in metadata:
|
|
222
|
+
metadata["creator"] = "YOUR NAME"
|
|
223
|
+
|
|
224
|
+
if isinstance(rules, DomainRules):
|
|
225
|
+
return metadata
|
|
226
|
+
elif isinstance(rules, DMSRules):
|
|
227
|
+
existing_model_id = (rules.metadata.space, rules.metadata.external_id, rules.metadata.version)
|
|
228
|
+
elif isinstance(rules, InformationRules):
|
|
229
|
+
existing_model_id = (rules.metadata.prefix, rules.metadata.name, rules.metadata.version)
|
|
230
|
+
else:
|
|
231
|
+
raise ValueError(f"Unsupported rules type: {type(rules)}")
|
|
232
|
+
existing_metadata = rules.metadata.model_dump(by_alias=True)
|
|
233
|
+
if isinstance(existing_metadata["created"], datetime):
|
|
234
|
+
metadata["created"] = existing_metadata["created"].replace(tzinfo=None)
|
|
235
|
+
if isinstance(existing_metadata["updated"], datetime):
|
|
236
|
+
metadata["updated"] = existing_metadata["updated"].replace(tzinfo=None)
|
|
237
|
+
# Excel does not support timezone in datetime strings
|
|
238
|
+
now_iso = datetime.now().replace(tzinfo=None).isoformat()
|
|
239
|
+
is_info = isinstance(rules, InformationRules)
|
|
240
|
+
is_dms = not is_info
|
|
241
|
+
is_extension = self.new_model_id is not None
|
|
242
|
+
is_solution = is_extension and self.new_model_id != existing_model_id
|
|
243
|
+
|
|
244
|
+
if is_solution:
|
|
245
|
+
metadata["prefix" if is_info else "space"] = self.new_model_id[0] # type: ignore[index]
|
|
246
|
+
metadata["title" if is_info else "externalId"] = self.new_model_id[1] # type: ignore[index]
|
|
247
|
+
metadata["version"] = self.new_model_id[2] # type: ignore[index]
|
|
248
|
+
else:
|
|
249
|
+
metadata["prefix" if is_info else "space"] = existing_model_id[0]
|
|
250
|
+
metadata["title" if is_info else "externalId"] = existing_model_id[1]
|
|
251
|
+
metadata["version"] = existing_model_id[2]
|
|
252
|
+
|
|
253
|
+
if is_solution and is_info:
|
|
254
|
+
metadata["namespace"] = f"http://purl.org/{self.new_model_id[0]}/" # type: ignore[index]
|
|
255
|
+
elif is_info:
|
|
256
|
+
metadata["namespace"] = existing_metadata["namespace"]
|
|
257
|
+
|
|
258
|
+
if is_solution and is_dms:
|
|
259
|
+
metadata["name"] = self.new_model_id[1] # type: ignore[index]
|
|
260
|
+
|
|
261
|
+
if is_solution:
|
|
262
|
+
metadata["created"] = now_iso
|
|
263
|
+
else:
|
|
264
|
+
metadata["created"] = existing_metadata["created"]
|
|
265
|
+
|
|
266
|
+
if is_solution or is_extension:
|
|
267
|
+
metadata["updated"] = now_iso
|
|
268
|
+
else:
|
|
269
|
+
metadata["updated"] = existing_metadata["updated"]
|
|
270
|
+
|
|
271
|
+
if is_solution:
|
|
272
|
+
metadata["creator"] = "YOUR NAME"
|
|
273
|
+
else:
|
|
274
|
+
metadata["creator"] = existing_metadata["creator"]
|
|
275
|
+
|
|
276
|
+
if not is_solution:
|
|
277
|
+
metadata["description"] = existing_metadata["description"]
|
|
278
|
+
|
|
279
|
+
if is_extension:
|
|
280
|
+
metadata["schema"] = SchemaCompleteness.extended.value
|
|
281
|
+
else:
|
|
282
|
+
metadata["schema"] = SchemaCompleteness.complete.value
|
|
283
|
+
|
|
284
|
+
metadata["extension"] = "addition"
|
|
285
|
+
|
|
286
|
+
return metadata
|
|
@@ -13,7 +13,6 @@ from cognite.client.data_classes.data_modeling import (
|
|
|
13
13
|
SingleHopConnectionDefinition,
|
|
14
14
|
View,
|
|
15
15
|
)
|
|
16
|
-
from cognite.client.data_classes.data_modeling.data_types import ListablePropertyType
|
|
17
16
|
from cognite.client.data_classes.data_modeling.ids import DataModelIdentifier, ViewId
|
|
18
17
|
|
|
19
18
|
from cognite.neat.rules.models.tables import Tables
|
|
@@ -132,9 +131,7 @@ class DMSImporter(BaseImporter):
|
|
|
132
131
|
|
|
133
132
|
max_count: str | float = "1"
|
|
134
133
|
if isinstance(prop, SingleHopConnectionDefinition) or (
|
|
135
|
-
isinstance(prop, MappedProperty)
|
|
136
|
-
and isinstance(prop.type, ListablePropertyType)
|
|
137
|
-
and prop.type.is_list
|
|
134
|
+
isinstance(prop, MappedProperty) and prop.type.is_list
|
|
138
135
|
):
|
|
139
136
|
max_count = float("nan")
|
|
140
137
|
|
|
@@ -5,9 +5,10 @@ from cognite.client import CogniteClient
|
|
|
5
5
|
from cognite.client import data_modeling as dm
|
|
6
6
|
from cognite.client.data_classes.data_modeling import DataModelIdentifier
|
|
7
7
|
from cognite.client.data_classes.data_modeling.containers import BTreeIndex, InvertedIndex
|
|
8
|
-
from cognite.client.
|
|
8
|
+
from cognite.client.utils import ms_to_datetime
|
|
9
9
|
|
|
10
10
|
from cognite.neat.rules import issues
|
|
11
|
+
from cognite.neat.rules.importers._base import BaseImporter, Rules
|
|
11
12
|
from cognite.neat.rules.issues import IssueList
|
|
12
13
|
from cognite.neat.rules.models._rules import DMSRules, DMSSchema, RoleTypes
|
|
13
14
|
from cognite.neat.rules.models._rules._types import (
|
|
@@ -19,6 +20,7 @@ from cognite.neat.rules.models._rules._types import (
|
|
|
19
20
|
ViewEntity,
|
|
20
21
|
ViewPropEntity,
|
|
21
22
|
)
|
|
23
|
+
from cognite.neat.rules.models._rules.base import ExtensionCategory, SchemaCompleteness
|
|
22
24
|
from cognite.neat.rules.models._rules.dms_architect_rules import (
|
|
23
25
|
DMSContainer,
|
|
24
26
|
DMSMetadata,
|
|
@@ -27,16 +29,47 @@ from cognite.neat.rules.models._rules.dms_architect_rules import (
|
|
|
27
29
|
SheetList,
|
|
28
30
|
)
|
|
29
31
|
|
|
30
|
-
from ._base import BaseImporter, Rules
|
|
31
|
-
|
|
32
32
|
|
|
33
33
|
class DMSImporter(BaseImporter):
|
|
34
|
-
def __init__(self, schema: DMSSchema):
|
|
34
|
+
def __init__(self, schema: DMSSchema, metadata: DMSMetadata | None = None):
|
|
35
35
|
self.schema = schema
|
|
36
|
+
self.metadata = metadata
|
|
36
37
|
|
|
37
38
|
@classmethod
|
|
38
39
|
def from_data_model_id(cls, client: CogniteClient, data_model_id: DataModelIdentifier) -> "DMSImporter":
|
|
39
|
-
|
|
40
|
+
"""Create a DMSImporter ready to convert the given data model to rules.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
client: Instantiated CogniteClient to retrieve data model.
|
|
44
|
+
data_model_id: Data Model to retrieve.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
DMSImporter: DMSImporter instance
|
|
48
|
+
"""
|
|
49
|
+
data_models = client.data_modeling.data_models.retrieve(data_model_id, inline_views=True)
|
|
50
|
+
if len(data_models) == 0:
|
|
51
|
+
raise ValueError(f"Data model {data_model_id} not found")
|
|
52
|
+
data_model = data_models.latest_version()
|
|
53
|
+
schema = DMSSchema.from_data_model(client, data_model)
|
|
54
|
+
description, creator = DMSMetadata._get_description_and_creator(data_model.description)
|
|
55
|
+
|
|
56
|
+
created = ms_to_datetime(data_model.created_time)
|
|
57
|
+
updated = ms_to_datetime(data_model.last_updated_time)
|
|
58
|
+
|
|
59
|
+
metadata = DMSMetadata(
|
|
60
|
+
schema_=SchemaCompleteness.complete,
|
|
61
|
+
extension=ExtensionCategory.addition,
|
|
62
|
+
space=data_model.space,
|
|
63
|
+
external_id=data_model.external_id,
|
|
64
|
+
name=data_model.name or data_model.external_id,
|
|
65
|
+
version=data_model.version or "0.1.0",
|
|
66
|
+
updated=updated,
|
|
67
|
+
created=created,
|
|
68
|
+
creator=creator,
|
|
69
|
+
description=description,
|
|
70
|
+
default_view_version=data_model.version or "0.1.0",
|
|
71
|
+
)
|
|
72
|
+
return cls(schema, metadata)
|
|
40
73
|
|
|
41
74
|
@classmethod
|
|
42
75
|
def from_directory(cls, directory: str | Path) -> "DMSImporter":
|
|
@@ -138,11 +171,7 @@ class DMSImporter(BaseImporter):
|
|
|
138
171
|
description=prop.description,
|
|
139
172
|
value_type=cast(ViewPropEntity | DMSValueType, container_prop.type._type),
|
|
140
173
|
nullable=container_prop.nullable,
|
|
141
|
-
is_list=
|
|
142
|
-
container_prop.type.is_list
|
|
143
|
-
if isinstance(container_prop.type, ListablePropertyType)
|
|
144
|
-
else False
|
|
145
|
-
),
|
|
174
|
+
is_list=container_prop.type.is_list,
|
|
146
175
|
default=container_prop.default_value,
|
|
147
176
|
container=ContainerEntity.from_id(container.as_id()),
|
|
148
177
|
container_property=prop.container_property_identifier,
|
|
@@ -172,7 +201,7 @@ class DMSImporter(BaseImporter):
|
|
|
172
201
|
}
|
|
173
202
|
|
|
174
203
|
dms_rules = DMSRules(
|
|
175
|
-
metadata=DMSMetadata.from_data_model(data_model),
|
|
204
|
+
metadata=self.metadata or DMSMetadata.from_data_model(data_model),
|
|
176
205
|
properties=properties,
|
|
177
206
|
containers=SheetList[DMSContainer](
|
|
178
207
|
data=[DMSContainer.from_container(container) for container in self.schema.containers]
|
|
@@ -68,7 +68,15 @@ class NeatValidationError(ValidationIssue, ABC):
|
|
|
68
68
|
|
|
69
69
|
This is intended to be overridden in subclasses to handle specific error types.
|
|
70
70
|
"""
|
|
71
|
-
|
|
71
|
+
all_errors = []
|
|
72
|
+
for error in errors:
|
|
73
|
+
if isinstance(ctx := error.get("ctx"), dict) and isinstance(
|
|
74
|
+
multi_error := ctx.get("error"), MultiValueError
|
|
75
|
+
):
|
|
76
|
+
all_errors.extend(multi_error.errors)
|
|
77
|
+
else:
|
|
78
|
+
all_errors.append(DefaultPydanticError.from_pydantic_error(error))
|
|
79
|
+
return all_errors
|
|
72
80
|
|
|
73
81
|
|
|
74
82
|
@dataclass(frozen=True)
|
cognite/neat/rules/issues/dms.py
CHANGED
|
@@ -26,6 +26,8 @@ __all__ = [
|
|
|
26
26
|
"MultipleReferenceWarning",
|
|
27
27
|
"HasDataFilterOnNoPropertiesViewWarning",
|
|
28
28
|
"NodeTypeFilterOnParentViewWarning",
|
|
29
|
+
"ChangingContainerError",
|
|
30
|
+
"ChangingViewError",
|
|
29
31
|
]
|
|
30
32
|
|
|
31
33
|
|
|
@@ -225,6 +227,78 @@ class ContainerPropertyUsedMultipleTimesError(DMSSchemaError):
|
|
|
225
227
|
return output
|
|
226
228
|
|
|
227
229
|
|
|
230
|
+
@dataclass(frozen=True)
|
|
231
|
+
class ChangingContainerError(DMSSchemaError):
|
|
232
|
+
description = "You are adding to an existing model. "
|
|
233
|
+
fix = "Keep the container the same"
|
|
234
|
+
error_name: ClassVar[str] = "ChangingContainerError"
|
|
235
|
+
container_id: dm.ContainerId
|
|
236
|
+
changed_properties: list[str] | None = None
|
|
237
|
+
changed_attributes: list[str] | None = None
|
|
238
|
+
|
|
239
|
+
def __post_init__(self):
|
|
240
|
+
# Sorting for deterministic output
|
|
241
|
+
if self.changed_properties:
|
|
242
|
+
self.changed_properties.sort()
|
|
243
|
+
if self.changed_attributes:
|
|
244
|
+
self.changed_attributes.sort()
|
|
245
|
+
|
|
246
|
+
def message(self) -> str:
|
|
247
|
+
if self.changed_properties:
|
|
248
|
+
changed = f" properties {self.changed_properties}."
|
|
249
|
+
elif self.changed_attributes:
|
|
250
|
+
changed = f" attributes {self.changed_attributes}."
|
|
251
|
+
else:
|
|
252
|
+
changed = "."
|
|
253
|
+
return (
|
|
254
|
+
f"The container {self.container_id} has changed{changed}"
|
|
255
|
+
"When extending model with extension set to addition or reshape, the container "
|
|
256
|
+
"properties must remain the same"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def dump(self) -> dict[str, Any]:
|
|
260
|
+
output = super().dump()
|
|
261
|
+
output["container_id"] = self.container_id.dump()
|
|
262
|
+
output["changed_properties"] = self.changed_properties
|
|
263
|
+
return output
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclass(frozen=True)
|
|
267
|
+
class ChangingViewError(DMSSchemaError):
|
|
268
|
+
description = "You are adding to an existing model. "
|
|
269
|
+
fix = "Keep the view the same"
|
|
270
|
+
error_name: ClassVar[str] = "ChangingViewError"
|
|
271
|
+
view_id: dm.ViewId
|
|
272
|
+
changed_properties: list[str] | None = None
|
|
273
|
+
changed_attributes: list[str] | None = None
|
|
274
|
+
|
|
275
|
+
def __post_init__(self):
|
|
276
|
+
# Sorting for deterministic output
|
|
277
|
+
if self.changed_properties:
|
|
278
|
+
self.changed_properties.sort()
|
|
279
|
+
if self.changed_attributes:
|
|
280
|
+
self.changed_attributes.sort()
|
|
281
|
+
|
|
282
|
+
def message(self) -> str:
|
|
283
|
+
if self.changed_properties:
|
|
284
|
+
changed = f" properties {self.changed_properties}."
|
|
285
|
+
elif self.changed_attributes:
|
|
286
|
+
changed = f" attributes {self.changed_attributes}."
|
|
287
|
+
else:
|
|
288
|
+
changed = "."
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
f"The view {self.view_id} has changed{changed}"
|
|
292
|
+
"When extending model with extension set to addition, the view properties must remain the same"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
def dump(self) -> dict[str, Any]:
|
|
296
|
+
output = super().dump()
|
|
297
|
+
output["view_id"] = self.view_id.dump()
|
|
298
|
+
output["difference"] = self.changed_properties
|
|
299
|
+
return output
|
|
300
|
+
|
|
301
|
+
|
|
228
302
|
@dataclass(frozen=True)
|
|
229
303
|
class DirectRelationListWarning(DMSSchemaWarning):
|
|
230
304
|
description = "The container property is set to a direct relation list, which is not supported by the CDF API"
|
|
@@ -10,10 +10,21 @@ import types
|
|
|
10
10
|
from abc import abstractmethod
|
|
11
11
|
from collections.abc import Callable, Iterator
|
|
12
12
|
from functools import wraps
|
|
13
|
-
from typing import Any, ClassVar, Generic, TypeAlias, TypeVar
|
|
13
|
+
from typing import Annotated, Any, ClassVar, Generic, TypeAlias, TypeVar
|
|
14
14
|
|
|
15
15
|
import pandas as pd
|
|
16
|
-
from pydantic import
|
|
16
|
+
from pydantic import (
|
|
17
|
+
BaseModel,
|
|
18
|
+
BeforeValidator,
|
|
19
|
+
ConfigDict,
|
|
20
|
+
Field,
|
|
21
|
+
HttpUrl,
|
|
22
|
+
PlainSerializer,
|
|
23
|
+
constr,
|
|
24
|
+
field_validator,
|
|
25
|
+
model_serializer,
|
|
26
|
+
model_validator,
|
|
27
|
+
)
|
|
17
28
|
from pydantic.fields import FieldInfo
|
|
18
29
|
|
|
19
30
|
from cognite.neat.rules.models._rules._types import ClassType
|
|
@@ -318,3 +329,14 @@ class SheetList(BaseModel, Generic[T_Entity]):
|
|
|
318
329
|
def mandatory_fields(cls, use_alias=False) -> set[str]:
|
|
319
330
|
"""Returns a set of mandatory fields for the model."""
|
|
320
331
|
return _get_required_fields(cls, use_alias)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
ExtensionCategoryType = Annotated[
|
|
335
|
+
ExtensionCategory,
|
|
336
|
+
PlainSerializer(
|
|
337
|
+
lambda v: v.value if isinstance(v, ExtensionCategory) else v,
|
|
338
|
+
return_type=str,
|
|
339
|
+
when_used="unless-none",
|
|
340
|
+
),
|
|
341
|
+
BeforeValidator(lambda v: ExtensionCategory(v) if isinstance(v, str) else v),
|
|
342
|
+
]
|
|
@@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast
|
|
|
10
10
|
from cognite.client import data_modeling as dm
|
|
11
11
|
from cognite.client.data_classes.data_modeling import PropertyType as CognitePropertyType
|
|
12
12
|
from cognite.client.data_classes.data_modeling.containers import BTreeIndex
|
|
13
|
-
from cognite.client.data_classes.data_modeling.data_types import ListablePropertyType
|
|
14
13
|
from cognite.client.data_classes.data_modeling.views import SingleReverseDirectRelationApply, ViewPropertyApply
|
|
15
14
|
from pydantic import Field, field_validator, model_serializer, model_validator
|
|
16
15
|
from pydantic_core.core_schema import SerializationInfo, ValidationInfo
|
|
@@ -134,18 +133,21 @@ class DMSMetadata(BaseMetadata):
|
|
|
134
133
|
)
|
|
135
134
|
|
|
136
135
|
@classmethod
|
|
137
|
-
def
|
|
138
|
-
|
|
139
|
-
if data_model.description and (description_match := re.search(r"Creator: (.+)", data_model.description)):
|
|
136
|
+
def _get_description_and_creator(cls, description_raw: str | None) -> tuple[str | None, list[str]]:
|
|
137
|
+
if description_raw and (description_match := re.search(r"Creator: (.+)", description_raw)):
|
|
140
138
|
creator = description_match.group(1).split(", ")
|
|
141
|
-
|
|
142
|
-
elif
|
|
139
|
+
description = description_raw.replace(description_match.string, "").strip() or None
|
|
140
|
+
elif description_raw:
|
|
143
141
|
creator = ["MISSING"]
|
|
144
|
-
description =
|
|
142
|
+
description = description_raw
|
|
145
143
|
else:
|
|
146
144
|
creator = ["MISSING"]
|
|
147
|
-
description =
|
|
145
|
+
description = None
|
|
146
|
+
return description, creator
|
|
148
147
|
|
|
148
|
+
@classmethod
|
|
149
|
+
def from_data_model(cls, data_model: dm.DataModelApply) -> "DMSMetadata":
|
|
150
|
+
description, creator = cls._get_description_and_creator(data_model.description)
|
|
149
151
|
return cls(
|
|
150
152
|
schema_=SchemaCompleteness.complete,
|
|
151
153
|
space=data_model.space,
|
|
@@ -490,12 +492,117 @@ class DMSRules(BaseRules):
|
|
|
490
492
|
raise issues.MultiValueError(errors)
|
|
491
493
|
return self
|
|
492
494
|
|
|
495
|
+
@model_validator(mode="after")
|
|
496
|
+
def validate_extension(self) -> "DMSRules":
|
|
497
|
+
if self.metadata.schema_ is not SchemaCompleteness.extended:
|
|
498
|
+
return self
|
|
499
|
+
if not self.reference:
|
|
500
|
+
raise ValueError("The schema is set to 'extended', but no reference rules are provided to validate against")
|
|
501
|
+
is_solution = self.metadata.space != self.reference.metadata.space
|
|
502
|
+
if is_solution:
|
|
503
|
+
return self
|
|
504
|
+
if self.metadata.extension is ExtensionCategory.rebuild:
|
|
505
|
+
# Everything is allowed
|
|
506
|
+
return self
|
|
507
|
+
# Is an extension of an existing model.
|
|
508
|
+
user_schema = self.as_schema()
|
|
509
|
+
ref_schema = self.reference.as_schema()
|
|
510
|
+
new_containers = {container.as_id(): container for container in user_schema.containers}
|
|
511
|
+
existing_containers = {container.as_id(): container for container in ref_schema.containers}
|
|
512
|
+
|
|
513
|
+
errors: list[issues.NeatValidationError] = []
|
|
514
|
+
for container_id, container in new_containers.items():
|
|
515
|
+
existing_container = existing_containers.get(container_id)
|
|
516
|
+
if not existing_container or existing_container == container:
|
|
517
|
+
# No problem
|
|
518
|
+
continue
|
|
519
|
+
new_dumped = container.dump()
|
|
520
|
+
existing_dumped = existing_container.dump()
|
|
521
|
+
changed_attributes, changed_properties = self._changed_attributes_and_properties(
|
|
522
|
+
new_dumped, existing_dumped
|
|
523
|
+
)
|
|
524
|
+
errors.append(
|
|
525
|
+
issues.dms.ChangingContainerError(
|
|
526
|
+
container_id=container_id,
|
|
527
|
+
changed_properties=changed_properties or None,
|
|
528
|
+
changed_attributes=changed_attributes or None,
|
|
529
|
+
)
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
if self.metadata.extension is ExtensionCategory.reshape and errors:
|
|
533
|
+
raise issues.MultiValueError(errors)
|
|
534
|
+
elif self.metadata.extension is ExtensionCategory.reshape:
|
|
535
|
+
# Reshape allows changes to views
|
|
536
|
+
return self
|
|
537
|
+
|
|
538
|
+
new_views = {view.as_id(): view for view in user_schema.views}
|
|
539
|
+
existing_views = {view.as_id(): view for view in ref_schema.views}
|
|
540
|
+
for view_id, view in new_views.items():
|
|
541
|
+
existing_view = existing_views.get(view_id)
|
|
542
|
+
if not existing_view or existing_view == view:
|
|
543
|
+
# No problem
|
|
544
|
+
continue
|
|
545
|
+
changed_attributes, changed_properties = self._changed_attributes_and_properties(
|
|
546
|
+
view.dump(), existing_view.dump()
|
|
547
|
+
)
|
|
548
|
+
errors.append(
|
|
549
|
+
issues.dms.ChangingViewError(
|
|
550
|
+
view_id=view_id,
|
|
551
|
+
changed_properties=changed_properties or None,
|
|
552
|
+
changed_attributes=changed_attributes or None,
|
|
553
|
+
)
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
if errors:
|
|
557
|
+
raise issues.MultiValueError(errors)
|
|
558
|
+
return self
|
|
559
|
+
|
|
560
|
+
@staticmethod
|
|
561
|
+
def _changed_attributes_and_properties(
|
|
562
|
+
new_dumped: dict[str, Any], existing_dumped: dict[str, Any]
|
|
563
|
+
) -> tuple[list[str], list[str]]:
|
|
564
|
+
"""Helper method to find the changed attributes and properties between two containers or views."""
|
|
565
|
+
new_attributes = {key: value for key, value in new_dumped.items() if key != "properties"}
|
|
566
|
+
existing_attributes = {key: value for key, value in existing_dumped.items() if key != "properties"}
|
|
567
|
+
changed_attributes = [key for key in new_attributes if new_attributes[key] != existing_attributes.get(key)]
|
|
568
|
+
new_properties = new_dumped.get("properties", {})
|
|
569
|
+
existing_properties = existing_dumped.get("properties", {})
|
|
570
|
+
changed_properties = [prop for prop in new_properties if new_properties[prop] != existing_properties.get(prop)]
|
|
571
|
+
return changed_attributes, changed_properties
|
|
572
|
+
|
|
493
573
|
@model_validator(mode="after")
|
|
494
574
|
def validate_schema(self) -> "DMSRules":
|
|
495
|
-
if self.metadata.schema_ is
|
|
575
|
+
if self.metadata.schema_ is SchemaCompleteness.partial:
|
|
496
576
|
return self
|
|
577
|
+
elif self.metadata.schema_ is SchemaCompleteness.complete:
|
|
578
|
+
rules: DMSRules = self
|
|
579
|
+
elif self.metadata.schema_ is SchemaCompleteness.extended:
|
|
580
|
+
if not self.reference:
|
|
581
|
+
raise ValueError(
|
|
582
|
+
"The schema is set to 'extended', but no reference rules are provided to validate against"
|
|
583
|
+
)
|
|
584
|
+
# This is an extension of the reference rules, we need to merge the two
|
|
585
|
+
rules = self.copy(deep=True)
|
|
586
|
+
rules.properties.extend(self.reference.properties.data)
|
|
587
|
+
existing_views = {view.view.as_id(False) for view in rules.views}
|
|
588
|
+
rules.views.extend([view for view in self.reference.views if view.view.as_id(False) not in existing_views])
|
|
589
|
+
if rules.containers and self.reference.containers:
|
|
590
|
+
existing_containers = {
|
|
591
|
+
container.container.as_id(self.metadata.space) for container in rules.containers.data
|
|
592
|
+
}
|
|
593
|
+
rules.containers.extend(
|
|
594
|
+
[
|
|
595
|
+
container
|
|
596
|
+
for container in self.reference.containers
|
|
597
|
+
if container.container.as_id(self.reference.metadata.space) not in existing_containers
|
|
598
|
+
]
|
|
599
|
+
)
|
|
600
|
+
elif not rules.containers and self.reference.containers:
|
|
601
|
+
rules.containers = self.reference.containers
|
|
602
|
+
else:
|
|
603
|
+
raise ValueError("Unknown schema completeness")
|
|
497
604
|
|
|
498
|
-
schema =
|
|
605
|
+
schema = rules.as_schema()
|
|
499
606
|
errors = schema.validate()
|
|
500
607
|
if errors:
|
|
501
608
|
raise issues.MultiValueError(errors)
|
|
@@ -553,12 +660,16 @@ class DMSRules(BaseRules):
|
|
|
553
660
|
)
|
|
554
661
|
containers.append(dumped)
|
|
555
662
|
|
|
556
|
-
|
|
663
|
+
output = {
|
|
557
664
|
"Metadata" if info.by_alias else "metadata": self.metadata.model_dump(**kwargs),
|
|
558
665
|
"Properties" if info.by_alias else "properties": properties,
|
|
559
666
|
"Views" if info.by_alias else "views": views,
|
|
560
667
|
"Containers" if info.by_alias else "containers": containers,
|
|
668
|
+
"is_reference": self.is_reference,
|
|
561
669
|
}
|
|
670
|
+
if self.reference is not None:
|
|
671
|
+
output["Reference" if info.by_alias else "reference"] = self.reference.model_dump(**kwargs)
|
|
672
|
+
return output
|
|
562
673
|
|
|
563
674
|
def as_schema(self, include_pipeline: bool = False, instance_space: str | None = None) -> DMSSchema:
|
|
564
675
|
return _DMSExporter(include_pipeline, instance_space).to_schema(self)
|
|
@@ -872,10 +983,7 @@ class _DMSExporter:
|
|
|
872
983
|
)
|
|
873
984
|
else:
|
|
874
985
|
type_: CognitePropertyType
|
|
875
|
-
|
|
876
|
-
type_ = type_cls(is_list=prop.is_list or False)
|
|
877
|
-
else:
|
|
878
|
-
type_ = cast(CognitePropertyType, type_cls())
|
|
986
|
+
type_ = type_cls(is_list=prop.is_list or False)
|
|
879
987
|
container.properties[prop_name] = dm.ContainerProperty(
|
|
880
988
|
type=type_,
|
|
881
989
|
nullable=prop.nullable if prop.nullable is not None else True,
|
|
@@ -63,6 +63,10 @@ class DMSSchema:
|
|
|
63
63
|
if len(data_models) == 0:
|
|
64
64
|
raise ValueError(f"Data model {data_model_id} not found")
|
|
65
65
|
data_model = data_models.latest_version()
|
|
66
|
+
return cls.from_data_model(client, data_model)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def from_data_model(cls, client: CogniteClient, data_model: dm.DataModel) -> "DMSSchema":
|
|
66
70
|
views = dm.ViewList(data_model.views)
|
|
67
71
|
container_ids = views.referenced_containers()
|
|
68
72
|
containers = client.data_modeling.containers.retrieve(list(container_ids))
|
|
@@ -267,7 +271,7 @@ class DMSSchema:
|
|
|
267
271
|
if isinstance(item, dm.ContainerApply | dm.ViewApply | dm.DataModelApply | dm.NodeApply | RawTableWrite):
|
|
268
272
|
identifier = item.as_id().as_tuple()
|
|
269
273
|
if len(identifier) == 3 and identifier[2] is None:
|
|
270
|
-
return identifier[:2]
|
|
274
|
+
return identifier[:2] # type: ignore[misc]
|
|
271
275
|
return cast(tuple[str, str] | tuple[str, str, str], identifier)
|
|
272
276
|
elif isinstance(item, dm.SpaceApply):
|
|
273
277
|
return item.space
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import math
|
|
1
2
|
from typing import Any, ClassVar
|
|
2
3
|
|
|
3
|
-
from pydantic import Field, model_serializer
|
|
4
|
+
from pydantic import Field, field_serializer, field_validator, model_serializer
|
|
4
5
|
from pydantic_core.core_schema import SerializationInfo
|
|
5
6
|
|
|
6
7
|
from ._types import ParentClassType, PropertyType, SemanticValueType, StrOrListType
|
|
@@ -24,6 +25,18 @@ class DomainProperty(SheetEntity):
|
|
|
24
25
|
min_count: int | None = Field(alias="Min Count", default=None)
|
|
25
26
|
max_count: int | float | None = Field(alias="Max Count", default=None)
|
|
26
27
|
|
|
28
|
+
@field_serializer("max_count", when_used="json-unless-none")
|
|
29
|
+
def serialize_max_count(self, value: int | float | None) -> int | float | None | str:
|
|
30
|
+
if isinstance(value, float) and math.isinf(value):
|
|
31
|
+
return None
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
@field_validator("max_count", mode="before")
|
|
35
|
+
def parse_max_count(cls, value: int | float | None) -> int | float | None:
|
|
36
|
+
if value is None:
|
|
37
|
+
return float("inf")
|
|
38
|
+
return value
|
|
39
|
+
|
|
27
40
|
|
|
28
41
|
class DomainClass(SheetEntity):
|
|
29
42
|
description: str | None = Field(None, alias="Description")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import math
|
|
1
2
|
import re
|
|
2
3
|
import sys
|
|
3
4
|
import warnings
|
|
@@ -5,7 +6,7 @@ from collections import defaultdict
|
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast
|
|
7
8
|
|
|
8
|
-
from pydantic import Field, field_validator, model_serializer, model_validator
|
|
9
|
+
from pydantic import Field, field_serializer, field_validator, model_serializer, model_validator
|
|
9
10
|
from pydantic_core.core_schema import SerializationInfo
|
|
10
11
|
from rdflib import Namespace
|
|
11
12
|
|
|
@@ -47,6 +48,7 @@ from ._types._base import Unknown
|
|
|
47
48
|
from .base import (
|
|
48
49
|
BaseMetadata,
|
|
49
50
|
ExtensionCategory,
|
|
51
|
+
ExtensionCategoryType,
|
|
50
52
|
MatchType,
|
|
51
53
|
RoleTypes,
|
|
52
54
|
RuleModel,
|
|
@@ -69,7 +71,7 @@ else:
|
|
|
69
71
|
class InformationMetadata(BaseMetadata):
|
|
70
72
|
role: ClassVar[RoleTypes] = RoleTypes.information_architect
|
|
71
73
|
schema_: SchemaCompleteness = Field(alias="schema")
|
|
72
|
-
extension:
|
|
74
|
+
extension: ExtensionCategoryType | None = ExtensionCategory.addition
|
|
73
75
|
prefix: PrefixType
|
|
74
76
|
namespace: NamespaceType
|
|
75
77
|
|
|
@@ -156,6 +158,18 @@ class InformationProperty(SheetEntity):
|
|
|
156
158
|
)
|
|
157
159
|
comment: str | None = Field(alias="Comment", default=None)
|
|
158
160
|
|
|
161
|
+
@field_serializer("max_count", when_used="json-unless-none")
|
|
162
|
+
def serialize_max_count(self, value: int | float | None) -> int | float | None | str:
|
|
163
|
+
if isinstance(value, float) and math.isinf(value):
|
|
164
|
+
return None
|
|
165
|
+
return value
|
|
166
|
+
|
|
167
|
+
@field_validator("max_count", mode="before")
|
|
168
|
+
def parse_max_count(cls, value: int | float | None) -> int | float | None:
|
|
169
|
+
if value is None:
|
|
170
|
+
return float("inf")
|
|
171
|
+
return value
|
|
172
|
+
|
|
159
173
|
@model_validator(mode="after")
|
|
160
174
|
def is_valid_rule(self):
|
|
161
175
|
# TODO: Can we skip rule_type and simply try to parse the rule and if it fails, raise an error?
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
-
from typing import Literal, overload
|
|
2
|
+
from typing import Literal, cast, overload
|
|
3
3
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
from openpyxl import load_workbook
|
|
@@ -58,7 +58,7 @@ def read_individual_sheet(
|
|
|
58
58
|
expected_headers: list[str] | None = None,
|
|
59
59
|
) -> tuple[list[dict], SpreadsheetRead] | list[dict]:
|
|
60
60
|
if expected_headers:
|
|
61
|
-
target_row = _get_row_number(load_workbook(excel_file)[sheet_name], expected_headers)
|
|
61
|
+
target_row = _get_row_number(cast(Worksheet, load_workbook(excel_file)[sheet_name]), expected_headers)
|
|
62
62
|
skiprows = target_row - 1 if target_row is not None else 0
|
|
63
63
|
else:
|
|
64
64
|
skiprows = 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.73.0
|
|
4
4
|
Summary: Knowledge graph transformation
|
|
5
5
|
Home-page: https://cognite-neat.readthedocs-hosted.com/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -19,7 +19,7 @@ Provides-Extra: graphql
|
|
|
19
19
|
Provides-Extra: oxi
|
|
20
20
|
Requires-Dist: PyYAML
|
|
21
21
|
Requires-Dist: backports.strenum (>=1.2,<2.0) ; python_version < "3.11"
|
|
22
|
-
Requires-Dist: cognite-sdk (>=7.
|
|
22
|
+
Requires-Dist: cognite-sdk (>=7.37.0,<8.0.0)
|
|
23
23
|
Requires-Dist: deepdiff
|
|
24
24
|
Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0) ; python_version < "3.11"
|
|
25
25
|
Requires-Dist: fastapi (>=0.100,<0.101)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cognite/neat/__init__.py,sha256=v-rRiDOgZ3sQSMQKq0vgUQZvpeOkoHFXissAx6Ktg84,61
|
|
2
|
-
cognite/neat/_version.py,sha256=
|
|
2
|
+
cognite/neat/_version.py,sha256=TEb-1D6cIr_qgLP1HaUQ8hIQZ7HNg_9m8A0caIDWuDQ,23
|
|
3
3
|
cognite/neat/app/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
cognite/neat/app/api/asgi/metrics.py,sha256=nxFy7L5cChTI0a-zkCiJ59Aq8yLuIJp5c9Dg0wRXtV0,152
|
|
5
5
|
cognite/neat/app/api/configuration.py,sha256=xnKdBE_dtq1nRvKa79YGA_wimI5UhoSRuBQz4LkLzQw,4606
|
|
@@ -50,7 +50,7 @@ cognite/neat/graph/exceptions.py,sha256=G899CjKepLB2dxgwH4585cqqtjvbyH6MLTM2aaqZ
|
|
|
50
50
|
cognite/neat/graph/extractor/__init__.py,sha256=wqCiqz-sXhUpTL5LRcrl_KFTNF0yTBRY4EzXT8pyCvA,258
|
|
51
51
|
cognite/neat/graph/extractor/_base.py,sha256=TOXDnlqske8DgnJwA0THDVRgmR79Acjm56yF0E-2w7I,356
|
|
52
52
|
cognite/neat/graph/extractor/_dexpi.py,sha256=oq7SzCQF4ocMEoPcWV6zDJ7UhtlhNp55rwcGiplFxVY,11232
|
|
53
|
-
cognite/neat/graph/extractor/_graph_capturing_sheet.py,sha256=
|
|
53
|
+
cognite/neat/graph/extractor/_graph_capturing_sheet.py,sha256=JCxzapFxdp4MF8rLMrDfOvHw7YOigLabCk1nv_Lr8so,17652
|
|
54
54
|
cognite/neat/graph/extractor/_mock_graph_generator.py,sha256=C-6jP4CBtaTWHhk03mSdFEO0eAPPZ8orL5WSt0w-5FA,14872
|
|
55
55
|
cognite/neat/graph/extractors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
56
|
cognite/neat/graph/extractors/_base.py,sha256=TOXDnlqske8DgnJwA0THDVRgmR79Acjm56yF0E-2w7I,356
|
|
@@ -79,7 +79,6 @@ cognite/neat/graph/transformation/entity_matcher.py,sha256=RdOz6WQ2VApmFpaztTXNl
|
|
|
79
79
|
cognite/neat/graph/transformation/query_generator/__init__.py,sha256=9N7RIlM8Pf-mJXzK8ulBe-eAa3yeHYBsBFQF7geTgWE,73
|
|
80
80
|
cognite/neat/graph/transformation/query_generator/sparql.py,sha256=-611a0KlanTDfUUFlk2un2pRmN1UkBT0gPtBcoO-StA,18659
|
|
81
81
|
cognite/neat/graph/transformation/transformer.py,sha256=ED_nCTBLh0kx-jbZMYHDsWj3X-RUqxJXbrogWZCMlnM,14673
|
|
82
|
-
cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
82
|
cognite/neat/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
84
83
|
cognite/neat/rules/_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
84
|
cognite/neat/rules/_analysis/_base.py,sha256=xzRf52rKf5P1EajWS2MoBhzO_FZG3qjNRKlIffCFMpU,674
|
|
@@ -89,9 +88,9 @@ cognite/neat/rules/analysis.py,sha256=UL3cvd5jaUkIOXX0nNE5WQ7zr3VvdJFpW_1q2i869F
|
|
|
89
88
|
cognite/neat/rules/examples/Rules-Nordic44-to-TNT.xlsx,sha256=pT2skrX3uWZtw-HIfIipVuPVr3bgb8zOAIqkguVjhCI,58987
|
|
90
89
|
cognite/neat/rules/examples/Rules-Nordic44-to-graphql.xlsx,sha256=eo6K177Xrz0CKyUFsbsGF8qZJsJI1Qf1JrzozdbmkeU,80226
|
|
91
90
|
cognite/neat/rules/examples/__init__.py,sha256=rLVPMLdcxpiW6LzfIgJodDKXfpdoYbD4Q60nmK_wAqU,858
|
|
92
|
-
cognite/neat/rules/examples/power-grid-containers.yaml,sha256=
|
|
91
|
+
cognite/neat/rules/examples/power-grid-containers.yaml,sha256=5Q_TCM8YuLOEOxoCWqosvYJeB4NYBA1EqlE7pyo0AOA,2598
|
|
93
92
|
cognite/neat/rules/examples/power-grid-example.xlsx,sha256=gUklgEhawhJ-EquwZpNp7dFvrLGAOzbKIGh8hTObxMg,77055
|
|
94
|
-
cognite/neat/rules/examples/power-grid-model.yaml,sha256=
|
|
93
|
+
cognite/neat/rules/examples/power-grid-model.yaml,sha256=ttCtAuQYrOkWBHttQo0w5WHWpzbnM0Z7MxG_ubtwTh0,5567
|
|
95
94
|
cognite/neat/rules/examples/rules-template.xlsx,sha256=buyxUbY5N5H52LgKST-D2FVvpjEMW4jC2chAHToknNI,75865
|
|
96
95
|
cognite/neat/rules/examples/sheet2cdf-transformation-rules.xlsx,sha256=_a93q8Yu7nv7Bq3I__RHD7pcdbfrv7urlp_AdgP2o3o,52433
|
|
97
96
|
cognite/neat/rules/examples/skos-rules.xlsx,sha256=XjdBpbfUrJnniTCCQLOBW-8OmJyGpIT1Ig-DHQG3x04,26008
|
|
@@ -102,8 +101,8 @@ cognite/neat/rules/exporter/__init__.py,sha256=StzV_2Kx5e1GaZOW8ZGMgKZpdzq12rWsc
|
|
|
102
101
|
cognite/neat/rules/exporter/_base.py,sha256=ohF0ri6wvxLMylZD7FNTmXdcjf-9p9vZgOd_Fl1I154,1439
|
|
103
102
|
cognite/neat/rules/exporter/_core/__init__.py,sha256=XpimbyjA-Tub7ndk2r8yp__aXwdMwtKMuYMNsMKkAL8,102
|
|
104
103
|
cognite/neat/rules/exporter/_core/rules2labels.py,sha256=oXr-J4-rN5QdnigK45ydWwaXrCEdVBbykkmUx7Aw8tY,764
|
|
105
|
-
cognite/neat/rules/exporter/_rules2dms.py,sha256=
|
|
106
|
-
cognite/neat/rules/exporter/_rules2excel.py,sha256=
|
|
104
|
+
cognite/neat/rules/exporter/_rules2dms.py,sha256=ytXspz0jl42tQEUcrfrEnogpmOeNyKIqZVrA0K5nqD0,36770
|
|
105
|
+
cognite/neat/rules/exporter/_rules2excel.py,sha256=igThjNoFclPUBd8j_bP9OEPDXNiTgtdTD-ZxvtyZcqE,8305
|
|
107
106
|
cognite/neat/rules/exporter/_rules2graphql.py,sha256=5MLWDw-7YfRwonUjqlLjxhgZOYzkU_GnyKuOvxAEEh4,6215
|
|
108
107
|
cognite/neat/rules/exporter/_rules2ontology.py,sha256=WD-Lu-1MPRU1HNCzQ3DjxX9XUtF8675rERGqJAFXD0s,18414
|
|
109
108
|
cognite/neat/rules/exporter/_rules2pydantic_models.py,sha256=VEjwGvleqkaSmNcve-2YKZ9Z2Np5KnX0tASphmOpZpo,28745
|
|
@@ -114,14 +113,14 @@ cognite/neat/rules/exporters/__init__.py,sha256=Gn3CjkVKHJF9Po1ZPH4wAJ-sRW9up7b2
|
|
|
114
113
|
cognite/neat/rules/exporters/_base.py,sha256=aRCzRjGDoXILkGvASev2UjVvLXrVCRTFiKgYwzJZqAo,1518
|
|
115
114
|
cognite/neat/rules/exporters/_models.py,sha256=f_RbFhoyD27z6Dsk4upYMHp7JHZRCfIvZsEzH5JSvtc,1645
|
|
116
115
|
cognite/neat/rules/exporters/_rules2dms.py,sha256=JPwaD42STCLi3N2zH8fKeKhWTNUsT-KsM3t56Pzap3A,11854
|
|
117
|
-
cognite/neat/rules/exporters/_rules2excel.py,sha256=
|
|
116
|
+
cognite/neat/rules/exporters/_rules2excel.py,sha256=JdEEutD0y80KLRAeWoo2GB-1-UtAughN8cTS8AJIjr8,13146
|
|
118
117
|
cognite/neat/rules/exporters/_rules2ontology.py,sha256=PqE62FIPWFWUkivyxzOUVFf9Nx8yVJghZBB9A_7us4Q,19922
|
|
119
118
|
cognite/neat/rules/exporters/_rules2yaml.py,sha256=HKgFlpgD7U2vWLQC_VdSvkDk19QgeVAd_J8wQ9ZgrN8,3038
|
|
120
119
|
cognite/neat/rules/exporters/_validation.py,sha256=E6nn1QicaJpDFiXrYRzwTqsJ5VULVzzeqhCBVlRxH4M,4092
|
|
121
120
|
cognite/neat/rules/importer/__init__.py,sha256=h1owL8pBPEtOmlIFAdkqAABH1A_Op5plh8C53og0uag,636
|
|
122
121
|
cognite/neat/rules/importer/_base.py,sha256=xNFBe33fwJnq3pp2dKqyKnNinwJG7liXvmEUvjtHqB8,2316
|
|
123
122
|
cognite/neat/rules/importer/_dict2rules.py,sha256=nZuF26CXG8lX_Si5krYLq-CLCKUtvmvfvsV5fky0EPk,6469
|
|
124
|
-
cognite/neat/rules/importer/_dms2rules.py,sha256=
|
|
123
|
+
cognite/neat/rules/importer/_dms2rules.py,sha256=jDvKkaztBh0TfaKbOmPsM9I7CwJEd6ZAGTnRVIcho6U,7713
|
|
125
124
|
cognite/neat/rules/importer/_graph2rules.py,sha256=yZqgmNFKWbG-QXfuumn6MgQrCWc3MMgyFj8uXcVSAOs,12097
|
|
126
125
|
cognite/neat/rules/importer/_json2rules.py,sha256=ko0sC3-xO3ne_PQn5aF0L5SsA8m48SN2bnqmBgjkylc,1610
|
|
127
126
|
cognite/neat/rules/importer/_owl2rules/__init__.py,sha256=tdGcrgtozdQyST-pTlxIa4cLBNTLvtk1nNYR4vOdFSw,63
|
|
@@ -134,7 +133,7 @@ cognite/neat/rules/importer/_xsd2rules.py,sha256=iKglhTX8u61j1GqNNKh_67qaw8-ZIam
|
|
|
134
133
|
cognite/neat/rules/importer/_yaml2rules.py,sha256=BnuEbhatcJrLMfPNgzLFwNWbwbIib-kIArcoC6aRMGI,1628
|
|
135
134
|
cognite/neat/rules/importers/__init__.py,sha256=zqNbGpvdVhYkLjWx1i9dJ3FXzYGtuQyTydUYsj-BndQ,408
|
|
136
135
|
cognite/neat/rules/importers/_base.py,sha256=qd9jg9n87lOTzNGdPp5p4taE4vncPAGwTm-1Vfbgj3s,4268
|
|
137
|
-
cognite/neat/rules/importers/_dms2rules.py,sha256=
|
|
136
|
+
cognite/neat/rules/importers/_dms2rules.py,sha256=QN7fD-bld9e8eU-xS8R0g4A8cQf1MItLOyhwbAX2yGc,10427
|
|
138
137
|
cognite/neat/rules/importers/_dtdl2rules/__init__.py,sha256=CNR-sUihs2mnR1bPMKs3j3L4ds3vFTsrl6YycExZTfU,68
|
|
139
138
|
cognite/neat/rules/importers/_dtdl2rules/_unit_lookup.py,sha256=wW4saKva61Q_i17guY0dc4OseJDQfqHy_QZBtm0OD6g,12134
|
|
140
139
|
cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py,sha256=o-n9KBWvA5Y7Fj0nW9KCnR1sz_PLtLKn2MTJPRnTcFc,12554
|
|
@@ -148,8 +147,8 @@ cognite/neat/rules/importers/_owl2rules/_owl2rules.py,sha256=xVGOo5wnoXLI3E9OUID
|
|
|
148
147
|
cognite/neat/rules/importers/_spreadsheet2rules.py,sha256=QIYTW-3Uc5ffFp3rJNyiqigWvc6x2eZTvhxqrXlGGSs,11481
|
|
149
148
|
cognite/neat/rules/importers/_yaml2rules.py,sha256=sIaYY3Zo--v1cXSu65n4ZPv47cS-5InvSbpkw3Ahov4,4198
|
|
150
149
|
cognite/neat/rules/issues/__init__.py,sha256=Ms6jgCxCezc5IgTOwCFtXQPtoVFfOvdcXj84_rs917I,563
|
|
151
|
-
cognite/neat/rules/issues/base.py,sha256=
|
|
152
|
-
cognite/neat/rules/issues/dms.py,sha256=
|
|
150
|
+
cognite/neat/rules/issues/base.py,sha256=qgoZ3qr35XLtdmkWWewf3sCYOW0sCkJrN9VpFvX-WzY,6158
|
|
151
|
+
cognite/neat/rules/issues/dms.py,sha256=O4JBbxOchv9wa9MGnoo1JGtc-H5Jo77izjEo3RaF7Ck,15314
|
|
153
152
|
cognite/neat/rules/issues/fileread.py,sha256=n-GZaULOJF_MKkBIh1maaOuGZXOvZYw7Y6fDAS0jrBI,4492
|
|
154
153
|
cognite/neat/rules/issues/formatters.py,sha256=_pSogWtfkt2JK0PZgWQffbj2On8vumFNshxOKAi5fYw,3346
|
|
155
154
|
cognite/neat/rules/issues/importing.py,sha256=GqUywhBD840Fbc4DD5L2I0oEllJ78MTjpmXogVEjihA,7493
|
|
@@ -162,11 +161,11 @@ cognite/neat/rules/models/_rules/_types/__init__.py,sha256=Px0uB5fqk-8qH-HRi0Zvg
|
|
|
162
161
|
cognite/neat/rules/models/_rules/_types/_base.py,sha256=okf8ebEcKrXf1rZQNp_cMsTS4pNKdJk2QMz_8-wi_so,16681
|
|
163
162
|
cognite/neat/rules/models/_rules/_types/_field.py,sha256=dOVAU1jWCupFVnrYYwLfI-nNUC4rv4vXHMzpiObtWiw,10295
|
|
164
163
|
cognite/neat/rules/models/_rules/_types/_value.py,sha256=ubyWmU6neyNxx17fqcciIjyB-CIpYNUuM97Xh2sVrYo,6308
|
|
165
|
-
cognite/neat/rules/models/_rules/base.py,sha256=
|
|
166
|
-
cognite/neat/rules/models/_rules/dms_architect_rules.py,sha256=
|
|
167
|
-
cognite/neat/rules/models/_rules/dms_schema.py,sha256
|
|
168
|
-
cognite/neat/rules/models/_rules/domain_rules.py,sha256=
|
|
169
|
-
cognite/neat/rules/models/_rules/information_rules.py,sha256=
|
|
164
|
+
cognite/neat/rules/models/_rules/base.py,sha256=F79FX1qCIqXlAPg0yAlpC6753dcRqoy9UB5BB3zWbDY,11025
|
|
165
|
+
cognite/neat/rules/models/_rules/dms_architect_rules.py,sha256=K5S9Us4hlaOVkqd4IzxsZXsuYv_4M3H107HWm3M_Sao,55886
|
|
166
|
+
cognite/neat/rules/models/_rules/dms_schema.py,sha256=dOzOr5iZyiOWFROo6706Z8aRvR6FSvPgRxWXSuuje3s,30418
|
|
167
|
+
cognite/neat/rules/models/_rules/domain_rules.py,sha256=HSZWk4NTfnzX_LlOdA5HAMkiHA5r4BZxRkGOWTFp79Y,2566
|
|
168
|
+
cognite/neat/rules/models/_rules/information_rules.py,sha256=sCqWt0OEPT8Ygqc7VfRwYVLcbJMOHUzDuhVWnQ4XO6A,21555
|
|
170
169
|
cognite/neat/rules/models/raw_rules.py,sha256=Y7ZVKyvLhX0s6WdwztbFiYFj4EijGEfjtBmv0ibRmmg,12368
|
|
171
170
|
cognite/neat/rules/models/rdfpath.py,sha256=kk1dqxl3n2W_vSQtpSJri2O4nCXnCDZB2lhLsLV1PN0,7344
|
|
172
171
|
cognite/neat/rules/models/rules.py,sha256=ECKrQEtoiQqtR8W-rKMAWmODpHeHhI8Qzf3TwCa3dy8,51063
|
|
@@ -181,7 +180,7 @@ cognite/neat/utils/cdf_loaders/_data_modeling.py,sha256=JQO0E1_PSibUP0JiaX4Kdljw
|
|
|
181
180
|
cognite/neat/utils/cdf_loaders/_ingestion.py,sha256=GqIJ7JsFuM4TYK_AFysEwS1e5CaVWfq5NqYPEUnId5s,6319
|
|
182
181
|
cognite/neat/utils/cdf_loaders/data_classes.py,sha256=0apspfwVlFltYOZfmk_PNknS3Z3SCxVr3kkvXH0bfPA,3844
|
|
183
182
|
cognite/neat/utils/exceptions.py,sha256=-w4cAcvcoWLf-_ZwAl7QV_NysfqtQzIOd1Ti-mpxJgM,981
|
|
184
|
-
cognite/neat/utils/spreadsheet.py,sha256=
|
|
183
|
+
cognite/neat/utils/spreadsheet.py,sha256=e9btpmZ68rrbc2h4ML44cpOyA7PQM0qCqMfxqHaKLr4,2722
|
|
185
184
|
cognite/neat/utils/text.py,sha256=4bg1_Q0lg7KsoxaDOvXrVyeY78BJN8i-27BlyDzUCls,3082
|
|
186
185
|
cognite/neat/utils/utils.py,sha256=E7CmZzPEYmfjsEiAxN9thNrCDCvw16r8PKODUgBR2Fk,12826
|
|
187
186
|
cognite/neat/utils/xml.py,sha256=ppLT3lQKVp8wOP-m8-tFY8uB2P4R76l7R_-kUtsABng,992
|
|
@@ -227,8 +226,8 @@ cognite/neat/workflows/steps_registry.py,sha256=PZVoHX4d6Vmjz6XzUFnFFWMCnrVnqkUC
|
|
|
227
226
|
cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
|
|
228
227
|
cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
|
|
229
228
|
cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
|
|
230
|
-
cognite_neat-0.
|
|
231
|
-
cognite_neat-0.
|
|
232
|
-
cognite_neat-0.
|
|
233
|
-
cognite_neat-0.
|
|
234
|
-
cognite_neat-0.
|
|
229
|
+
cognite_neat-0.73.0.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
230
|
+
cognite_neat-0.73.0.dist-info/METADATA,sha256=M-VcOy7BmI2OfbtquJljKUwpda3GrWrV2P_GkyR5MwE,9321
|
|
231
|
+
cognite_neat-0.73.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
232
|
+
cognite_neat-0.73.0.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
|
|
233
|
+
cognite_neat-0.73.0.dist-info/RECORD,,
|
cognite/neat/py.typed
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|