cognite-neat 0.101.0__py3-none-any.whl → 0.103.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/__init__.py +1 -1
- cognite/neat/_app/api/routers/crud.py +1 -1
- cognite/neat/_client/__init__.py +1 -1
- cognite/neat/_client/_api/data_modeling_loaders.py +1 -1
- cognite/neat/_client/_api/schema.py +1 -1
- cognite/neat/_constants.py +5 -1
- cognite/neat/_graph/_tracking/__init__.py +1 -1
- cognite/neat/_graph/extractors/__init__.py +8 -8
- cognite/neat/_graph/extractors/_mock_graph_generator.py +2 -3
- cognite/neat/_graph/loaders/_base.py +1 -1
- cognite/neat/_graph/loaders/_rdf2dms.py +165 -47
- cognite/neat/_graph/queries/_base.py +22 -2
- cognite/neat/_graph/queries/_shared.py +4 -4
- cognite/neat/_graph/transformers/__init__.py +25 -9
- cognite/neat/_graph/transformers/_base.py +1 -1
- cognite/neat/_graph/transformers/_iodd.py +2 -2
- cognite/neat/_graph/transformers/_prune_graph.py +98 -24
- cognite/neat/_graph/transformers/_value_type.py +196 -2
- cognite/neat/_issues/__init__.py +6 -6
- cognite/neat/_issues/_base.py +4 -4
- cognite/neat/_issues/errors/__init__.py +22 -22
- cognite/neat/_issues/formatters.py +1 -1
- cognite/neat/_issues/warnings/__init__.py +20 -18
- cognite/neat/_issues/warnings/_properties.py +7 -0
- cognite/neat/_issues/warnings/user_modeling.py +2 -2
- cognite/neat/_rules/analysis/__init__.py +1 -1
- cognite/neat/_rules/catalog/__init__.py +1 -0
- cognite/neat/_rules/catalog/hello_world_pump.xlsx +0 -0
- cognite/neat/_rules/exporters/__init__.py +5 -5
- cognite/neat/_rules/exporters/_rules2excel.py +12 -6
- cognite/neat/_rules/importers/__init__.py +4 -4
- cognite/neat/_rules/importers/_base.py +7 -3
- cognite/neat/_rules/importers/_dms2rules.py +51 -19
- cognite/neat/_rules/importers/_rdf/__init__.py +1 -1
- cognite/neat/_rules/importers/_rdf/_base.py +2 -2
- cognite/neat/_rules/models/__init__.py +5 -5
- cognite/neat/_rules/models/_base_rules.py +1 -1
- cognite/neat/_rules/models/dms/__init__.py +11 -11
- cognite/neat/_rules/models/dms/_validation.py +16 -10
- cognite/neat/_rules/models/entities/__init__.py +26 -26
- cognite/neat/_rules/models/information/__init__.py +5 -5
- cognite/neat/_rules/models/information/_rules.py +3 -3
- cognite/neat/_rules/models/mapping/_classic2core.yaml +54 -8
- cognite/neat/_rules/transformers/__init__.py +12 -12
- cognite/neat/_rules/transformers/_pipelines.py +10 -5
- cognite/neat/_session/_base.py +71 -0
- cognite/neat/_session/_collector.py +3 -1
- cognite/neat/_session/_drop.py +10 -0
- cognite/neat/_session/_inspect.py +35 -1
- cognite/neat/_session/_mapping.py +5 -0
- cognite/neat/_session/_prepare.py +196 -7
- cognite/neat/_session/_read.py +180 -20
- cognite/neat/_session/_set.py +11 -1
- cognite/neat/_session/_show.py +41 -2
- cognite/neat/_session/_to.py +58 -10
- cognite/neat/_session/engine/__init__.py +1 -1
- cognite/neat/_session/engine/_load.py +3 -1
- cognite/neat/_store/__init__.py +3 -2
- cognite/neat/_store/{_base.py → _graph_store.py} +56 -2
- cognite/neat/_store/_provenance.py +11 -1
- cognite/neat/_store/_rules_store.py +20 -0
- cognite/neat/_utils/auth.py +7 -5
- cognite/neat/_utils/reader/__init__.py +1 -1
- cognite/neat/_version.py +2 -2
- cognite/neat/_workflows/__init__.py +3 -3
- cognite/neat/_workflows/steps/lib/current/graph_extractor.py +1 -1
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +1 -1
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +2 -2
- cognite/neat/_workflows/steps/lib/io/io_steps.py +3 -3
- {cognite_neat-0.101.0.dist-info → cognite_neat-0.103.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.101.0.dist-info → cognite_neat-0.103.0.dist-info}/RECORD +74 -72
- {cognite_neat-0.101.0.dist-info → cognite_neat-0.103.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.101.0.dist-info → cognite_neat-0.103.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.101.0.dist-info → cognite_neat-0.103.0.dist-info}/entry_points.txt +0 -0
|
@@ -34,8 +34,6 @@ class ExcelExporter(BaseExporter[VerifiedRules, Workbook]):
|
|
|
34
34
|
Args:
|
|
35
35
|
styling: The styling to use for the Excel file. Defaults to "default". See below for details
|
|
36
36
|
on the different styles.
|
|
37
|
-
output_role: The role to use for the exported spreadsheet. If provided, the rules will be converted to
|
|
38
|
-
this role formate before being written to excel. If not provided, the role from the rules will be used.
|
|
39
37
|
new_model_id: The new model ID to use for the exported spreadsheet. This is only applicable if the input
|
|
40
38
|
rules have 'is_reference' set. If provided, the model ID will be used to automatically create the
|
|
41
39
|
new metadata sheet in the Excel file. The model id is expected to be a tuple of (prefix, title)
|
|
@@ -119,7 +117,11 @@ class ExcelExporter(BaseExporter[VerifiedRules, Workbook]):
|
|
|
119
117
|
|
|
120
118
|
main_header = self._main_header_by_sheet_name[sheet_name]
|
|
121
119
|
sheet.append([main_header] + [""] * (len(headers) - 1))
|
|
122
|
-
|
|
120
|
+
|
|
121
|
+
if headers[0] == "Neat ID":
|
|
122
|
+
# Move the Neat ID to the end of the columns
|
|
123
|
+
headers = headers[1:] + ["Neat ID"]
|
|
124
|
+
|
|
123
125
|
sheet.append(headers)
|
|
124
126
|
|
|
125
127
|
fill_colors = itertools.cycle(["CADCFC", "FFFFFF"])
|
|
@@ -127,6 +129,9 @@ class ExcelExporter(BaseExporter[VerifiedRules, Workbook]):
|
|
|
127
129
|
last_class: str | None = None
|
|
128
130
|
item: dict[str, Any]
|
|
129
131
|
for item in dumped_rules.get(sheet_name) or []:
|
|
132
|
+
if "Neat ID" in item:
|
|
133
|
+
# Move the Neat ID to the end of the columns
|
|
134
|
+
item["Neat ID"] = item.pop("Neat ID")
|
|
130
135
|
row = list(item.values())
|
|
131
136
|
class_ = row[0]
|
|
132
137
|
|
|
@@ -152,12 +157,13 @@ class ExcelExporter(BaseExporter[VerifiedRules, Workbook]):
|
|
|
152
157
|
# This freezes all rows above the given row
|
|
153
158
|
sheet.freeze_panes = sheet["A3"]
|
|
154
159
|
|
|
155
|
-
sheet["A1"].alignment = Alignment(horizontal="
|
|
160
|
+
sheet["A1"].alignment = Alignment(horizontal="left")
|
|
156
161
|
|
|
157
162
|
if self._styling_level > 1:
|
|
158
163
|
# Make the header row bold, larger, and colored
|
|
159
|
-
sheet
|
|
160
|
-
|
|
164
|
+
for cell, *_ in sheet.iter_cols(min_row=1, max_row=1, min_col=1, max_col=len(headers)):
|
|
165
|
+
cell.font = Font(bold=True, size=20)
|
|
166
|
+
cell.fill = PatternFill(fgColor="FFC000", patternType="solid")
|
|
161
167
|
for cell in sheet["2"]:
|
|
162
168
|
cell.font = Font(bold=True, size=14)
|
|
163
169
|
|
|
@@ -7,14 +7,14 @@ from ._yaml2rules import YAMLImporter
|
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
9
|
"BaseImporter",
|
|
10
|
-
"OWLImporter",
|
|
11
|
-
"IMFImporter",
|
|
12
10
|
"DMSImporter",
|
|
11
|
+
"DTDLImporter",
|
|
13
12
|
"ExcelImporter",
|
|
14
13
|
"GoogleSheetImporter",
|
|
15
|
-
"
|
|
16
|
-
"YAMLImporter",
|
|
14
|
+
"IMFImporter",
|
|
17
15
|
"InferenceImporter",
|
|
16
|
+
"OWLImporter",
|
|
17
|
+
"YAMLImporter",
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
RulesImporters = (
|
|
@@ -3,16 +3,18 @@ from abc import ABC, abstractmethod
|
|
|
3
3
|
from collections.abc import Iterator
|
|
4
4
|
from contextlib import contextmanager, suppress
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import Any, Generic, Literal
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Generic, Literal
|
|
7
7
|
|
|
8
8
|
from pydantic import ValidationError
|
|
9
9
|
|
|
10
10
|
from cognite.neat._constants import DEFAULT_NAMESPACE
|
|
11
11
|
from cognite.neat._issues import IssueList, NeatError, NeatWarning
|
|
12
12
|
from cognite.neat._rules._shared import ReadRules, T_InputRules
|
|
13
|
-
from cognite.neat._store._provenance import Agent as ProvenanceAgent
|
|
14
13
|
from cognite.neat._utils.auxiliary import class_html_doc
|
|
15
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from cognite.neat._store._provenance import Agent as ProvenanceAgent
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
class BaseImporter(ABC, Generic[T_InputRules]):
|
|
18
20
|
"""
|
|
@@ -48,8 +50,10 @@ class BaseImporter(ABC, Generic[T_InputRules]):
|
|
|
48
50
|
return class_html_doc(cls)
|
|
49
51
|
|
|
50
52
|
@property
|
|
51
|
-
def agent(self) -> ProvenanceAgent:
|
|
53
|
+
def agent(self) -> "ProvenanceAgent":
|
|
52
54
|
"""Provenance agent for the importer."""
|
|
55
|
+
from cognite.neat._store._provenance import Agent as ProvenanceAgent
|
|
56
|
+
|
|
53
57
|
return ProvenanceAgent(id_=DEFAULT_NAMESPACE[f"agent/{type(self).__name__}"])
|
|
54
58
|
|
|
55
59
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from collections import Counter
|
|
1
|
+
from collections import Counter, defaultdict
|
|
2
2
|
from collections.abc import Collection, Iterable, Sequence
|
|
3
3
|
from datetime import datetime, timezone
|
|
4
4
|
from pathlib import Path
|
|
@@ -222,12 +222,18 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
222
222
|
schema: DMSSchema,
|
|
223
223
|
metadata: DMSInputMetadata | None = None,
|
|
224
224
|
) -> DMSInputRules:
|
|
225
|
+
enum_by_container_property = self._create_enum_collections(schema.containers.values())
|
|
226
|
+
enum_collection_by_container_property = {
|
|
227
|
+
key: enum_list[0].collection for key, enum_list in enum_by_container_property.items() if enum_list
|
|
228
|
+
}
|
|
229
|
+
|
|
225
230
|
properties: list[DMSInputProperty] = []
|
|
226
231
|
for view_id, view in schema.views.items():
|
|
227
232
|
view_entity = ViewEntity.from_id(view_id)
|
|
228
|
-
class_entity = view_entity.as_class()
|
|
229
233
|
for prop_id, prop in (view.properties or {}).items():
|
|
230
|
-
dms_property = self._create_dms_property(
|
|
234
|
+
dms_property = self._create_dms_property(
|
|
235
|
+
prop_id, prop, view_entity, enum_collection_by_container_property
|
|
236
|
+
)
|
|
231
237
|
if dms_property is not None:
|
|
232
238
|
properties.append(dms_property)
|
|
233
239
|
|
|
@@ -237,8 +243,6 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
237
243
|
|
|
238
244
|
metadata = metadata or DMSInputMetadata.from_data_model(data_model)
|
|
239
245
|
|
|
240
|
-
enum = self._create_enum_collections(schema.containers.values())
|
|
241
|
-
|
|
242
246
|
return DMSInputRules(
|
|
243
247
|
metadata=metadata,
|
|
244
248
|
properties=properties,
|
|
@@ -248,7 +252,7 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
248
252
|
for view_id, view in schema.views.items()
|
|
249
253
|
],
|
|
250
254
|
nodes=[DMSInputNode.from_node_type(node_type) for node_type in schema.node_types.values()],
|
|
251
|
-
enum=enum,
|
|
255
|
+
enum=[enum for enum_list in enum_by_container_property.values() for enum in enum_list] or None,
|
|
252
256
|
)
|
|
253
257
|
|
|
254
258
|
@classmethod
|
|
@@ -267,7 +271,11 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
267
271
|
)
|
|
268
272
|
|
|
269
273
|
def _create_dms_property(
|
|
270
|
-
self,
|
|
274
|
+
self,
|
|
275
|
+
prop_id: str,
|
|
276
|
+
prop: ViewPropertyApply,
|
|
277
|
+
view_entity: ViewEntity,
|
|
278
|
+
enum_collection_by_container_property: dict[tuple[dm.ContainerId, str], str],
|
|
271
279
|
) -> DMSInputProperty | None:
|
|
272
280
|
if isinstance(prop, dm.MappedPropertyApply) and prop.container not in self._all_containers_by_id:
|
|
273
281
|
self.issue_list.append(
|
|
@@ -300,7 +308,7 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
300
308
|
)
|
|
301
309
|
return None
|
|
302
310
|
|
|
303
|
-
value_type = self._get_value_type(prop, view_entity, prop_id)
|
|
311
|
+
value_type = self._get_value_type(prop, view_entity, prop_id, enum_collection_by_container_property)
|
|
304
312
|
if value_type is None:
|
|
305
313
|
return None
|
|
306
314
|
|
|
@@ -347,7 +355,11 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
347
355
|
return None
|
|
348
356
|
|
|
349
357
|
def _get_value_type(
|
|
350
|
-
self,
|
|
358
|
+
self,
|
|
359
|
+
prop: ViewPropertyApply,
|
|
360
|
+
view_entity: ViewEntity,
|
|
361
|
+
prop_id: str,
|
|
362
|
+
enum_collection_by_container_property: dict[tuple[dm.ContainerId, str], str],
|
|
351
363
|
) -> DataType | ViewEntity | DMSUnknownEntity | None:
|
|
352
364
|
if isinstance(
|
|
353
365
|
prop,
|
|
@@ -367,7 +379,16 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
367
379
|
elif isinstance(container_prop.type, PropertyTypeWithUnit) and container_prop.type.unit:
|
|
368
380
|
return DataType.load(f"{container_prop.type._type}(unit={container_prop.type.unit.external_id})")
|
|
369
381
|
elif isinstance(container_prop.type, DMSEnum):
|
|
370
|
-
|
|
382
|
+
collection = enum_collection_by_container_property.get(
|
|
383
|
+
(prop.container, prop.container_property_identifier)
|
|
384
|
+
)
|
|
385
|
+
if collection is None:
|
|
386
|
+
# This should never happen
|
|
387
|
+
raise ValueError(
|
|
388
|
+
f"BUG in Neat: Enum for {prop.container}.{prop.container_property_identifier} not found."
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
return Enum(collection=ClassEntity(suffix=collection), unknownValue=container_prop.type.unknown_value)
|
|
371
392
|
else:
|
|
372
393
|
return DataType.load(container_prop.type._type)
|
|
373
394
|
else:
|
|
@@ -477,15 +498,26 @@ class DMSImporter(BaseImporter[DMSInputRules]):
|
|
|
477
498
|
return candidates[0]
|
|
478
499
|
|
|
479
500
|
@staticmethod
|
|
480
|
-
def _create_enum_collections(
|
|
481
|
-
|
|
501
|
+
def _create_enum_collections(
|
|
502
|
+
containers: Collection[dm.ContainerApply],
|
|
503
|
+
) -> dict[tuple[dm.ContainerId, str], list[DMSInputEnum]]:
|
|
504
|
+
enum_by_container_property: dict[tuple[dm.ContainerId, str], list[DMSInputEnum]] = defaultdict(list)
|
|
505
|
+
|
|
506
|
+
is_external_id_unique = len({container.external_id for container in containers}) == len(containers)
|
|
507
|
+
|
|
482
508
|
for container in containers:
|
|
509
|
+
container_id = container.as_id()
|
|
483
510
|
for prop_id, prop in container.properties.items():
|
|
484
|
-
if isinstance(prop.type, DMSEnum):
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
511
|
+
if not isinstance(prop.type, DMSEnum):
|
|
512
|
+
continue
|
|
513
|
+
if is_external_id_unique:
|
|
514
|
+
collection = f"{container.external_id}.{prop_id}"
|
|
515
|
+
else:
|
|
516
|
+
collection = f"{container.space}:{container.external_id}.{prop_id}"
|
|
517
|
+
for identifier, value in prop.type.values.items():
|
|
518
|
+
enum_by_container_property[(container_id, prop_id)].append(
|
|
519
|
+
DMSInputEnum(
|
|
520
|
+
collection=collection, value=identifier, name=value.name, description=value.description
|
|
490
521
|
)
|
|
491
|
-
|
|
522
|
+
)
|
|
523
|
+
return enum_by_container_property
|
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
from cognite.client import data_modeling as dm
|
|
5
5
|
from rdflib import Graph, Namespace, URIRef
|
|
6
6
|
|
|
7
|
-
from cognite.neat._constants import
|
|
7
|
+
from cognite.neat._constants import get_default_prefixes_and_namespaces
|
|
8
8
|
from cognite.neat._issues import IssueList
|
|
9
9
|
from cognite.neat._issues.errors import FileReadError
|
|
10
10
|
from cognite.neat._issues.errors._general import NeatValueError
|
|
@@ -97,7 +97,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
|
|
|
97
97
|
issue_list.append(FileReadError(filepath, str(e)))
|
|
98
98
|
|
|
99
99
|
# bind key namespaces
|
|
100
|
-
for prefix, namespace in
|
|
100
|
+
for prefix, namespace in get_default_prefixes_and_namespaces().items():
|
|
101
101
|
graph.bind(prefix, namespace)
|
|
102
102
|
|
|
103
103
|
return cls(
|
|
@@ -17,16 +17,16 @@ VERIFIED_RULES_BY_ROLE: dict[RoleTypes, type[InformationRules] | type[DMSRules]]
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
20
|
+
"INPUT_RULES_BY_ROLE",
|
|
20
21
|
"DMSInputRules",
|
|
21
|
-
"InformationInputRules",
|
|
22
|
-
"InformationRules",
|
|
23
22
|
"DMSRules",
|
|
24
|
-
"INPUT_RULES_BY_ROLE",
|
|
25
23
|
"DMSSchema",
|
|
24
|
+
"DataModelType",
|
|
25
|
+
"ExtensionCategory",
|
|
26
|
+
"InformationInputRules",
|
|
27
|
+
"InformationRules",
|
|
26
28
|
"RoleTypes",
|
|
27
29
|
"SchemaCompleteness",
|
|
28
|
-
"ExtensionCategory",
|
|
29
|
-
"DataModelType",
|
|
30
30
|
"SheetList",
|
|
31
31
|
"SheetRow",
|
|
32
32
|
]
|
|
@@ -149,7 +149,7 @@ class BaseMetadata(SchemaModel):
|
|
|
149
149
|
|
|
150
150
|
role: ClassVar[RoleTypes] = Field(description="Role of the person creating the data model")
|
|
151
151
|
aspect: ClassVar[DataModelAspect] = Field(description="Aspect of the data model")
|
|
152
|
-
space: SpaceType = Field(
|
|
152
|
+
space: SpaceType = Field(description="The space where the data model is defined")
|
|
153
153
|
external_id: DataModelExternalIdType = Field(
|
|
154
154
|
alias="externalId", description="External identifier for the data model"
|
|
155
155
|
)
|
|
@@ -13,20 +13,20 @@ from ._rules_input import (
|
|
|
13
13
|
from ._validation import DMSValidation
|
|
14
14
|
|
|
15
15
|
__all__ = [
|
|
16
|
-
"DMSRules",
|
|
17
|
-
"DMSSchema",
|
|
18
|
-
"DMSMetadata",
|
|
19
|
-
"DMSView",
|
|
20
|
-
"DMSProperty",
|
|
21
16
|
"DMSContainer",
|
|
22
|
-
"DMSNode",
|
|
23
17
|
"DMSEnum",
|
|
24
|
-
"DMSInputRules",
|
|
25
|
-
"DMSInputMetadata",
|
|
26
|
-
"DMSInputView",
|
|
27
|
-
"DMSInputProperty",
|
|
28
18
|
"DMSInputContainer",
|
|
29
|
-
"DMSInputNode",
|
|
30
19
|
"DMSInputEnum",
|
|
20
|
+
"DMSInputMetadata",
|
|
21
|
+
"DMSInputNode",
|
|
22
|
+
"DMSInputProperty",
|
|
23
|
+
"DMSInputRules",
|
|
24
|
+
"DMSInputView",
|
|
25
|
+
"DMSMetadata",
|
|
26
|
+
"DMSNode",
|
|
27
|
+
"DMSProperty",
|
|
28
|
+
"DMSRules",
|
|
29
|
+
"DMSSchema",
|
|
31
30
|
"DMSValidation",
|
|
31
|
+
"DMSView",
|
|
32
32
|
]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
from collections import Counter, defaultdict
|
|
3
|
+
from collections.abc import Mapping
|
|
3
4
|
from functools import lru_cache
|
|
4
5
|
from typing import ClassVar
|
|
5
6
|
|
|
@@ -115,7 +116,9 @@ class DMSValidation:
|
|
|
115
116
|
**ref_container_by_id,
|
|
116
117
|
}
|
|
117
118
|
all_views_by_id: dict[dm.ViewId, dm.ViewApply | dm.View] = {**dict(dms_schema.views.items()), **ref_view_by_id}
|
|
118
|
-
properties_by_ids = self._as_properties_by_ids(dms_schema, ref_view_by_id)
|
|
119
|
+
properties_by_ids = self._as_properties_by_ids(dms_schema.views, ref_view_by_id)
|
|
120
|
+
ref_properties_by_ids = self._as_properties_by_ids(ref_view_by_id, {})
|
|
121
|
+
all_properties_by_ids = {**ref_properties_by_ids, **properties_by_ids}
|
|
119
122
|
view_properties_by_id = self._as_view_properties_by_id(properties_by_ids)
|
|
120
123
|
parents_view_ids_by_child_id = self._parent_view_ids_by_child_id(all_views_by_id)
|
|
121
124
|
|
|
@@ -132,7 +135,9 @@ class DMSValidation:
|
|
|
132
135
|
# SDK classes validation
|
|
133
136
|
issue_list.extend(self._containers_are_proper_size(dms_schema))
|
|
134
137
|
issue_list.extend(
|
|
135
|
-
self._validate_reverse_connections(
|
|
138
|
+
self._validate_reverse_connections(
|
|
139
|
+
properties_by_ids, all_containers_by_id, parents_view_ids_by_child_id, all_properties_by_ids
|
|
140
|
+
)
|
|
136
141
|
)
|
|
137
142
|
issue_list.extend(self._validate_schema(dms_schema, all_views_by_id, all_containers_by_id))
|
|
138
143
|
issue_list.extend(self._validate_referenced_container_limits(dms_schema.views, view_properties_by_id))
|
|
@@ -140,22 +145,22 @@ class DMSValidation:
|
|
|
140
145
|
|
|
141
146
|
@staticmethod
|
|
142
147
|
def _as_properties_by_ids(
|
|
143
|
-
|
|
148
|
+
view_by_id: Mapping[dm.ViewId, dm.ViewApply] | Mapping[dm.ViewId, dm.View],
|
|
149
|
+
ref_view_by_id: dict[dm.ViewId, dm.View],
|
|
144
150
|
) -> dict[tuple[ViewId, str], ViewPropertyApply | ViewProperty]:
|
|
145
151
|
# Priority DMS schema properties.
|
|
146
152
|
# No need to do long lookups in ref_views as these already contain all ancestor properties.
|
|
147
153
|
properties_by_id: dict[tuple[ViewId, str], ViewPropertyApply | ViewProperty] = {}
|
|
148
|
-
for view in
|
|
149
|
-
view_id = view.as_id()
|
|
154
|
+
for view_id, view in view_by_id.items():
|
|
150
155
|
for prop_id, prop in (view.properties or {}).items():
|
|
151
156
|
properties_by_id[(view_id, prop_id)] = prop
|
|
152
157
|
if view.implements:
|
|
153
158
|
to_check = view.implements.copy()
|
|
154
159
|
while to_check:
|
|
155
160
|
parent_id = to_check.pop()
|
|
156
|
-
if parent_id in
|
|
157
|
-
# Priority DMS
|
|
158
|
-
parent_view =
|
|
161
|
+
if parent_id in view_by_id:
|
|
162
|
+
# Priority of the DMS schema properties
|
|
163
|
+
parent_view = view_by_id[parent_id]
|
|
159
164
|
for prop_id, prop in (parent_view.properties or {}).items():
|
|
160
165
|
if (view_id, prop_id) not in properties_by_id:
|
|
161
166
|
properties_by_id[(view_id, prop_id)] = prop
|
|
@@ -406,6 +411,7 @@ class DMSValidation:
|
|
|
406
411
|
view_property_by_property_id: dict[tuple[dm.ViewId, str], ViewPropertyApply | ViewProperty],
|
|
407
412
|
containers_by_id: dict[dm.ContainerId, dm.ContainerApply | dm.Container],
|
|
408
413
|
parents_by_view: dict[dm.ViewId, set[dm.ViewId]],
|
|
414
|
+
all_view_property_by_property_id: dict[tuple[dm.ViewId, str], ViewPropertyApply | ViewProperty],
|
|
409
415
|
) -> IssueList:
|
|
410
416
|
issue_list = IssueList()
|
|
411
417
|
# do not check for reverse connections in Cognite models
|
|
@@ -416,7 +422,7 @@ class DMSValidation:
|
|
|
416
422
|
if not isinstance(prop_, ReverseDirectRelationApply | ReverseDirectRelation):
|
|
417
423
|
continue
|
|
418
424
|
target_id = prop_.through.source, prop_.through.property
|
|
419
|
-
if target_id not in
|
|
425
|
+
if target_id not in all_view_property_by_property_id:
|
|
420
426
|
issue_list.append(
|
|
421
427
|
ReversedConnectionNotFeasibleError(
|
|
422
428
|
view_id,
|
|
@@ -430,7 +436,7 @@ class DMSValidation:
|
|
|
430
436
|
# Todo: How to handle this case? Should not happen if you created the model with Neat
|
|
431
437
|
continue
|
|
432
438
|
|
|
433
|
-
target_property =
|
|
439
|
+
target_property = all_view_property_by_property_id[(target_id[0], target_id[1])]
|
|
434
440
|
# Validate that the target is a direct relation pointing to the view_id
|
|
435
441
|
is_direct_relation = False
|
|
436
442
|
if isinstance(target_property, dm.MappedProperty) and isinstance(target_property.type, dm.DirectRelation):
|
|
@@ -25,38 +25,38 @@ from ._types import CdfResourceEntityList, ClassEntityList, ContainerEntityList,
|
|
|
25
25
|
from ._wrapped import DMSFilter, HasDataFilter, NodeTypeFilter, RawFilter, WrappedEntity
|
|
26
26
|
|
|
27
27
|
__all__ = [
|
|
28
|
-
"Entity",
|
|
29
|
-
"UnitEntity",
|
|
30
|
-
"DMSVersionedEntity",
|
|
31
|
-
"RelationshipEntity",
|
|
32
|
-
"EdgeEntity",
|
|
33
|
-
"ViewEntity",
|
|
34
|
-
"ClassEntity",
|
|
35
|
-
"ViewEntityList",
|
|
36
|
-
"UnknownEntity",
|
|
37
|
-
"Unknown",
|
|
38
|
-
"Undefined",
|
|
39
|
-
"T_Entity",
|
|
40
28
|
"AssetEntity",
|
|
41
29
|
"AssetFields",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"DMSEntity",
|
|
30
|
+
"CdfResourceEntityList",
|
|
31
|
+
"ClassEntity",
|
|
32
|
+
"ClassEntityList",
|
|
46
33
|
"ContainerEntity",
|
|
34
|
+
"ContainerEntityList",
|
|
35
|
+
"DMSEntity",
|
|
36
|
+
"DMSFilter",
|
|
37
|
+
"DMSNodeEntity",
|
|
47
38
|
"DMSUnknownEntity",
|
|
39
|
+
"DMSVersionedEntity",
|
|
48
40
|
"DataModelEntity",
|
|
41
|
+
"EdgeEntity",
|
|
42
|
+
"Entity",
|
|
43
|
+
"HasDataFilter",
|
|
44
|
+
"MultiValueTypeInfo",
|
|
45
|
+
"NodeTypeFilter",
|
|
46
|
+
"RawFilter",
|
|
49
47
|
"ReferenceEntity",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
48
|
+
"RelationshipEntity",
|
|
49
|
+
"ReverseConnectionEntity",
|
|
50
|
+
"T_Entity",
|
|
53
51
|
"URLEntity",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
52
|
+
"Undefined",
|
|
53
|
+
"UnitEntity",
|
|
54
|
+
"Unknown",
|
|
55
|
+
"UnknownEntity",
|
|
56
|
+
"ViewEntity",
|
|
57
|
+
"ViewEntityList",
|
|
57
58
|
"WrappedEntity",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"RawFilter",
|
|
59
|
+
"load_connection",
|
|
60
|
+
"load_dms_value_type",
|
|
61
|
+
"load_value_type",
|
|
62
62
|
]
|
|
@@ -8,13 +8,13 @@ from ._rules_input import (
|
|
|
8
8
|
from ._validation import InformationValidation
|
|
9
9
|
|
|
10
10
|
__all__ = [
|
|
11
|
-
"InformationRules",
|
|
12
|
-
"InformationMetadata",
|
|
13
11
|
"InformationClass",
|
|
14
|
-
"InformationProperty",
|
|
15
|
-
"InformationInputRules",
|
|
16
|
-
"InformationInputMetadata",
|
|
17
12
|
"InformationInputClass",
|
|
13
|
+
"InformationInputMetadata",
|
|
18
14
|
"InformationInputProperty",
|
|
15
|
+
"InformationInputRules",
|
|
16
|
+
"InformationMetadata",
|
|
17
|
+
"InformationProperty",
|
|
18
|
+
"InformationRules",
|
|
19
19
|
"InformationValidation",
|
|
20
20
|
]
|
|
@@ -7,7 +7,7 @@ from pydantic import Field, field_serializer, field_validator, model_validator
|
|
|
7
7
|
from pydantic_core.core_schema import SerializationInfo
|
|
8
8
|
from rdflib import Namespace, URIRef
|
|
9
9
|
|
|
10
|
-
from cognite.neat._constants import
|
|
10
|
+
from cognite.neat._constants import get_default_prefixes_and_namespaces
|
|
11
11
|
from cognite.neat._issues.errors import NeatValueError, PropertyDefinitionError
|
|
12
12
|
from cognite.neat._rules._constants import EntityTypes
|
|
13
13
|
from cognite.neat._rules.models._base_rules import (
|
|
@@ -255,7 +255,7 @@ class InformationRules(BaseRules):
|
|
|
255
255
|
classes: SheetList[InformationClass] = Field(alias="Classes", description="List of classes")
|
|
256
256
|
prefixes: dict[str, Namespace] = Field(
|
|
257
257
|
alias="Prefixes",
|
|
258
|
-
default_factory=
|
|
258
|
+
default_factory=get_default_prefixes_and_namespaces,
|
|
259
259
|
description="the definition of the prefixes that are used in the semantic data model",
|
|
260
260
|
)
|
|
261
261
|
|
|
@@ -264,7 +264,7 @@ class InformationRules(BaseRules):
|
|
|
264
264
|
if isinstance(values, dict):
|
|
265
265
|
return {key: Namespace(value) if isinstance(value, str) else value for key, value in values.items()}
|
|
266
266
|
elif values is None:
|
|
267
|
-
values =
|
|
267
|
+
values = get_default_prefixes_and_namespaces()
|
|
268
268
|
return values
|
|
269
269
|
|
|
270
270
|
def as_dms_rules(self) -> "DMSRules":
|