cognite-neat 0.97.3__py3-none-any.whl → 0.99.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_client/__init__.py +4 -0
- cognite/neat/_client/_api/data_modeling_loaders.py +512 -0
- cognite/neat/_client/_api/schema.py +50 -0
- cognite/neat/_client/_api_client.py +17 -0
- cognite/neat/_client/data_classes/__init__.py +0 -0
- cognite/neat/{_utils/cdf/data_classes.py → _client/data_classes/data_modeling.py} +8 -135
- cognite/neat/{_rules/models/dms/_schema.py → _client/data_classes/schema.py} +32 -281
- cognite/neat/_graph/_shared.py +14 -15
- cognite/neat/_graph/extractors/_classic_cdf/_assets.py +14 -154
- cognite/neat/_graph/extractors/_classic_cdf/_base.py +154 -7
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +23 -12
- cognite/neat/_graph/extractors/_classic_cdf/_data_sets.py +17 -92
- cognite/neat/_graph/extractors/_classic_cdf/_events.py +13 -162
- cognite/neat/_graph/extractors/_classic_cdf/_files.py +15 -179
- cognite/neat/_graph/extractors/_classic_cdf/_labels.py +32 -100
- cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +27 -178
- cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +14 -139
- cognite/neat/_graph/extractors/_classic_cdf/_timeseries.py +15 -173
- cognite/neat/_graph/extractors/_rdf_file.py +6 -7
- cognite/neat/_graph/loaders/__init__.py +1 -2
- cognite/neat/_graph/queries/_base.py +17 -1
- cognite/neat/_graph/transformers/_classic_cdf.py +50 -134
- cognite/neat/_graph/transformers/_prune_graph.py +1 -1
- cognite/neat/_graph/transformers/_rdfpath.py +1 -1
- cognite/neat/_issues/warnings/__init__.py +6 -0
- cognite/neat/_issues/warnings/_external.py +8 -0
- cognite/neat/_issues/warnings/_models.py +9 -0
- cognite/neat/_issues/warnings/_properties.py +16 -0
- cognite/neat/_rules/_constants.py +7 -6
- cognite/neat/_rules/_shared.py +3 -8
- cognite/neat/_rules/analysis/__init__.py +1 -2
- cognite/neat/_rules/analysis/_base.py +10 -27
- cognite/neat/_rules/analysis/_dms.py +4 -10
- cognite/neat/_rules/analysis/_information.py +2 -10
- cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
- cognite/neat/_rules/exporters/_base.py +3 -4
- cognite/neat/_rules/exporters/_rules2dms.py +29 -40
- cognite/neat/_rules/exporters/_rules2excel.py +15 -72
- cognite/neat/_rules/exporters/_rules2ontology.py +4 -4
- cognite/neat/_rules/importers/_base.py +3 -4
- cognite/neat/_rules/importers/_dms2rules.py +21 -45
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py +1 -7
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +7 -10
- cognite/neat/_rules/importers/_rdf/_base.py +17 -29
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +5 -10
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +55 -51
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +5 -8
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_shared.py +25 -140
- cognite/neat/_rules/importers/_spreadsheet2rules.py +10 -41
- cognite/neat/_rules/models/__init__.py +3 -17
- cognite/neat/_rules/models/_base_rules.py +118 -62
- cognite/neat/_rules/models/dms/__init__.py +2 -2
- cognite/neat/_rules/models/dms/_exporter.py +20 -178
- cognite/neat/_rules/models/dms/_rules.py +65 -128
- cognite/neat/_rules/models/dms/_rules_input.py +72 -56
- cognite/neat/_rules/models/dms/_validation.py +16 -109
- cognite/neat/_rules/models/entities/_single_value.py +32 -4
- cognite/neat/_rules/models/information/_rules.py +19 -122
- cognite/neat/_rules/models/information/_rules_input.py +32 -41
- cognite/neat/_rules/models/information/_validation.py +34 -102
- cognite/neat/_rules/models/mapping/__init__.py +2 -3
- cognite/neat/_rules/models/mapping/_classic2core.py +36 -146
- cognite/neat/_rules/models/mapping/_classic2core.yaml +339 -0
- cognite/neat/_rules/transformers/__init__.py +3 -6
- cognite/neat/_rules/transformers/_converters.py +128 -206
- cognite/neat/_rules/transformers/_mapping.py +105 -34
- cognite/neat/_rules/transformers/_verification.py +5 -16
- cognite/neat/_session/_base.py +83 -21
- cognite/neat/_session/_collector.py +126 -0
- cognite/neat/_session/_drop.py +35 -0
- cognite/neat/_session/_inspect.py +22 -10
- cognite/neat/_session/_mapping.py +39 -0
- cognite/neat/_session/_prepare.py +222 -27
- cognite/neat/_session/_read.py +109 -19
- cognite/neat/_session/_set.py +2 -2
- cognite/neat/_session/_show.py +11 -11
- cognite/neat/_session/_to.py +27 -14
- cognite/neat/_session/exceptions.py +20 -3
- cognite/neat/_store/_base.py +27 -24
- cognite/neat/_store/_provenance.py +2 -2
- cognite/neat/_utils/auxiliary.py +19 -0
- cognite/neat/_utils/rdf_.py +28 -1
- cognite/neat/_version.py +1 -1
- cognite/neat/_workflows/steps/data_contracts.py +2 -10
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +14 -49
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +4 -1
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +5 -9
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/METADATA +4 -3
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/RECORD +97 -100
- cognite/neat/_graph/loaders/_rdf2asset.py +0 -416
- cognite/neat/_rules/analysis/_asset.py +0 -173
- cognite/neat/_rules/models/asset/__init__.py +0 -13
- cognite/neat/_rules/models/asset/_rules.py +0 -109
- cognite/neat/_rules/models/asset/_rules_input.py +0 -101
- cognite/neat/_rules/models/asset/_validation.py +0 -45
- cognite/neat/_rules/models/domain.py +0 -136
- cognite/neat/_rules/models/mapping/_base.py +0 -131
- cognite/neat/_utils/cdf/loaders/__init__.py +0 -25
- cognite/neat/_utils/cdf/loaders/_base.py +0 -54
- cognite/neat/_utils/cdf/loaders/_data_modeling.py +0 -339
- cognite/neat/_utils/cdf/loaders/_ingestion.py +0 -167
- /cognite/neat/{_utils/cdf → _client/_api}/__init__.py +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
import pandas as pd
|
|
6
6
|
from rdflib import Namespace, URIRef
|
|
@@ -25,19 +25,16 @@ from ._rules import (
|
|
|
25
25
|
|
|
26
26
|
@dataclass
|
|
27
27
|
class InformationInputMetadata(InputComponent[InformationMetadata]):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
namespace: str
|
|
28
|
+
space: str
|
|
29
|
+
external_id: str
|
|
31
30
|
version: str
|
|
32
31
|
creator: str
|
|
33
|
-
data_model_type: Literal["solution", "enterprise"] = "enterprise"
|
|
34
|
-
extension: Literal["addition", "reshape", "rebuild"] = "addition"
|
|
35
32
|
name: str | None = None
|
|
36
33
|
description: str | None = None
|
|
37
34
|
created: datetime | str | None = None
|
|
38
35
|
updated: datetime | str | None = None
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
physical: str | None = None
|
|
37
|
+
conceptual: str | None = None
|
|
41
38
|
|
|
42
39
|
@classmethod
|
|
43
40
|
def _get_verified_cls(cls) -> type[InformationMetadata]:
|
|
@@ -51,6 +48,25 @@ class InformationInputMetadata(InputComponent[InformationMetadata]):
|
|
|
51
48
|
output["updated"] = datetime.now()
|
|
52
49
|
return output
|
|
53
50
|
|
|
51
|
+
@property
|
|
52
|
+
def prefix(self) -> str:
|
|
53
|
+
return self.space
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def identifier(self) -> URIRef:
|
|
57
|
+
"""Globally unique identifier for the data model.
|
|
58
|
+
|
|
59
|
+
!!! note
|
|
60
|
+
Unlike namespace, the identifier does not end with "/" or "#".
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
return DEFAULT_NAMESPACE[f"data-model/unverified/logical/{self.space}/{self.external_id}/{self.version}"]
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def namespace(self) -> Namespace:
|
|
67
|
+
"""Namespace for the data model used for the entities in the data model."""
|
|
68
|
+
return Namespace(f"{self.identifier}/")
|
|
69
|
+
|
|
54
70
|
|
|
55
71
|
@dataclass
|
|
56
72
|
class InformationInputProperty(InputComponent[InformationProperty]):
|
|
@@ -59,12 +75,9 @@ class InformationInputProperty(InputComponent[InformationProperty]):
|
|
|
59
75
|
value_type: DataType | ClassEntity | MultiValueTypeInfo | UnknownEntity | str
|
|
60
76
|
name: str | None = None
|
|
61
77
|
description: str | None = None
|
|
62
|
-
comment: str | None = None
|
|
63
78
|
min_count: int | None = None
|
|
64
79
|
max_count: int | float | None = None
|
|
65
80
|
default: Any | None = None
|
|
66
|
-
reference: str | None = None
|
|
67
|
-
match_type: str | None = None
|
|
68
81
|
transformation: str | None = None
|
|
69
82
|
# Only used internally
|
|
70
83
|
inherited: bool = False
|
|
@@ -85,10 +98,7 @@ class InformationInputClass(InputComponent[InformationClass]):
|
|
|
85
98
|
class_: ClassEntity | str
|
|
86
99
|
name: str | None = None
|
|
87
100
|
description: str | None = None
|
|
88
|
-
|
|
89
|
-
parent: str | list[ClassEntity] | None = None
|
|
90
|
-
reference: str | None = None
|
|
91
|
-
match_type: str | None = None
|
|
101
|
+
implements: str | list[ClassEntity] | None = None
|
|
92
102
|
|
|
93
103
|
@classmethod
|
|
94
104
|
def _get_verified_cls(cls) -> type[InformationClass]:
|
|
@@ -101,12 +111,12 @@ class InformationInputClass(InputComponent[InformationClass]):
|
|
|
101
111
|
def dump(self, default_prefix: str, **kwargs) -> dict[str, Any]: # type: ignore[override]
|
|
102
112
|
output = super().dump()
|
|
103
113
|
parent: list[ClassEntity] | None = None
|
|
104
|
-
if isinstance(self.
|
|
105
|
-
parent = [ClassEntity.load(parent, prefix=default_prefix) for parent in self.
|
|
106
|
-
elif isinstance(self.
|
|
107
|
-
parent = [ClassEntity.load(parent_, prefix=default_prefix) for parent_ in self.
|
|
114
|
+
if isinstance(self.implements, str):
|
|
115
|
+
parent = [ClassEntity.load(parent, prefix=default_prefix) for parent in self.implements.split(",")]
|
|
116
|
+
elif isinstance(self.implements, list):
|
|
117
|
+
parent = [ClassEntity.load(parent_, prefix=default_prefix) for parent_ in self.implements]
|
|
108
118
|
output["Class"] = ClassEntity.load(self.class_, prefix=default_prefix)
|
|
109
|
-
output["
|
|
119
|
+
output["Implements"] = parent
|
|
110
120
|
return output
|
|
111
121
|
|
|
112
122
|
|
|
@@ -116,8 +126,6 @@ class InformationInputRules(InputRules[InformationRules]):
|
|
|
116
126
|
properties: list[InformationInputProperty] = field(default_factory=list)
|
|
117
127
|
classes: list[InformationInputClass] = field(default_factory=list)
|
|
118
128
|
prefixes: dict[str, Namespace] | None = None
|
|
119
|
-
last: "InformationInputRules | None" = None
|
|
120
|
-
reference: "InformationInputRules | None" = None
|
|
121
129
|
|
|
122
130
|
@classmethod
|
|
123
131
|
def _get_verified_cls(cls) -> type[InformationRules]:
|
|
@@ -125,26 +133,12 @@ class InformationInputRules(InputRules[InformationRules]):
|
|
|
125
133
|
|
|
126
134
|
def dump(self) -> dict[str, Any]:
|
|
127
135
|
default_prefix = self.metadata.prefix
|
|
128
|
-
reference: dict[str, Any] | None = None
|
|
129
|
-
if isinstance(self.reference, InformationInputRules):
|
|
130
|
-
reference = self.reference.dump()
|
|
131
|
-
elif isinstance(self.reference, InformationRules):
|
|
132
|
-
# We need to load through the InformationRulesInput to set the correct default space and version
|
|
133
|
-
reference = InformationInputRules.load(self.reference.model_dump()).dump()
|
|
134
|
-
last: dict[str, Any] | None = None
|
|
135
|
-
if isinstance(self.last, InformationInputRules):
|
|
136
|
-
last = self.last.dump()
|
|
137
|
-
elif isinstance(self.last, InformationRules):
|
|
138
|
-
# We need to load through the InformationRulesInput to set the correct default space and version
|
|
139
|
-
last = InformationInputRules.load(self.last.model_dump()).dump()
|
|
140
136
|
|
|
141
137
|
return dict(
|
|
142
138
|
Metadata=self.metadata.dump(),
|
|
143
139
|
Properties=[prop.dump(default_prefix) for prop in self.properties],
|
|
144
140
|
Classes=[class_.dump(default_prefix) for class_ in self.classes],
|
|
145
141
|
Prefixes=self.prefixes,
|
|
146
|
-
Last=last,
|
|
147
|
-
Reference=reference,
|
|
148
142
|
)
|
|
149
143
|
|
|
150
144
|
def _repr_html_(self) -> str:
|
|
@@ -152,14 +146,11 @@ class InformationInputRules(InputRules[InformationRules]):
|
|
|
152
146
|
"type": "Logical Data Model",
|
|
153
147
|
"intended for": "Information Architect",
|
|
154
148
|
"name": self.metadata.name,
|
|
155
|
-
"external_id": self.metadata.
|
|
149
|
+
"external_id": self.metadata.external_id,
|
|
150
|
+
"space": self.metadata.space,
|
|
156
151
|
"version": self.metadata.version,
|
|
157
152
|
"classes": len(self.classes),
|
|
158
153
|
"properties": len(self.properties),
|
|
159
154
|
}
|
|
160
155
|
|
|
161
156
|
return pd.DataFrame([summary]).T.rename(columns={0: ""})._repr_html_() # type: ignore
|
|
162
|
-
|
|
163
|
-
@property
|
|
164
|
-
def id_(self) -> URIRef:
|
|
165
|
-
return DEFAULT_NAMESPACE[f"data-model/unverified/info/{self.metadata.prefix}/{self.metadata.version}"]
|
|
@@ -4,10 +4,9 @@ from typing import cast
|
|
|
4
4
|
|
|
5
5
|
from cognite.neat._issues import IssueList
|
|
6
6
|
from cognite.neat._issues.errors import NeatValueError, ResourceNotDefinedError
|
|
7
|
+
from cognite.neat._issues.warnings._models import UndefinedClassWarning
|
|
7
8
|
from cognite.neat._rules._constants import EntityTypes
|
|
8
|
-
from cognite.neat._rules.models._base_rules import DataModelType, SchemaCompleteness
|
|
9
9
|
from cognite.neat._rules.models.entities import ClassEntity, UnknownEntity
|
|
10
|
-
from cognite.neat._utils.rdf_ import get_inheritance_path
|
|
11
10
|
|
|
12
11
|
from ._rules import InformationRules
|
|
13
12
|
|
|
@@ -24,72 +23,60 @@ class InformationPostValidation:
|
|
|
24
23
|
self.issue_list = IssueList()
|
|
25
24
|
|
|
26
25
|
def validate(self) -> IssueList:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if self.metadata.data_model_type == DataModelType.solution and not self.rules.reference:
|
|
31
|
-
raise ValueError("Reference data model is missing")
|
|
32
|
-
|
|
33
|
-
if self.metadata.schema_ == SchemaCompleteness.extended and not self.rules.last:
|
|
34
|
-
raise ValueError("Last version is missing")
|
|
35
|
-
|
|
36
|
-
self._dangling_classes()
|
|
37
|
-
self._referenced_parent_classes_exist()
|
|
26
|
+
self._namespaces_reassigned()
|
|
27
|
+
self._classes_without_properties()
|
|
28
|
+
self._parent_class_defined()
|
|
38
29
|
self._referenced_classes_exist()
|
|
39
30
|
self._referenced_value_types_exist()
|
|
40
|
-
self._namespaces_reassigned()
|
|
41
31
|
|
|
42
32
|
return self.issue_list
|
|
43
33
|
|
|
44
|
-
def
|
|
34
|
+
def _classes_without_properties(self) -> None:
|
|
45
35
|
# needs to be complete for this validation to pass
|
|
46
36
|
defined_classes = {class_.class_ for class_ in self.classes}
|
|
47
37
|
referred_classes = {property_.class_ for property_ in self.properties}
|
|
48
38
|
class_parent_pairs = self._class_parent_pairs()
|
|
49
|
-
dangling_classes = set()
|
|
50
39
|
|
|
51
40
|
if classes_without_properties := defined_classes.difference(referred_classes):
|
|
52
41
|
for class_ in classes_without_properties:
|
|
53
|
-
# USE CASE: class has no direct properties and no parents
|
|
54
|
-
if class_ not in class_parent_pairs:
|
|
55
|
-
dangling_classes.add(class_)
|
|
56
42
|
# USE CASE: class has no direct properties and no parents with properties
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
# and it is a class in the prefix of data model, as long as it is in the
|
|
44
|
+
# same prefix, meaning same space
|
|
45
|
+
if not class_parent_pairs[class_] and class_.prefix == self.metadata.prefix:
|
|
46
|
+
self.issue_list.append(
|
|
47
|
+
ResourceNotDefinedError[ClassEntity](
|
|
48
|
+
resource_type="class",
|
|
49
|
+
identifier=class_,
|
|
50
|
+
location="Classes sheet",
|
|
51
|
+
)
|
|
52
|
+
)
|
|
66
53
|
|
|
67
|
-
def
|
|
68
|
-
|
|
54
|
+
def _parent_class_defined(self) -> None:
|
|
55
|
+
"""This is a validation to check if the parent class of a class is defined in the classes sheet."""
|
|
69
56
|
class_parent_pairs = self._class_parent_pairs()
|
|
70
57
|
classes = set(class_parent_pairs.keys())
|
|
71
58
|
parents = set(itertools.chain.from_iterable(class_parent_pairs.values()))
|
|
72
59
|
|
|
73
60
|
if undefined_parents := parents.difference(classes):
|
|
74
61
|
for parent in undefined_parents:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
62
|
+
if parent.prefix != self.metadata.prefix:
|
|
63
|
+
self.issue_list.append(UndefinedClassWarning(class_id=str(parent)))
|
|
64
|
+
else:
|
|
65
|
+
self.issue_list.append(
|
|
66
|
+
ResourceNotDefinedError[ClassEntity](
|
|
67
|
+
resource_type="class",
|
|
68
|
+
identifier=parent,
|
|
69
|
+
location="Classes sheet",
|
|
70
|
+
)
|
|
81
71
|
)
|
|
82
|
-
)
|
|
83
72
|
|
|
84
73
|
def _referenced_classes_exist(self) -> None:
|
|
85
74
|
# needs to be complete for this validation to pass
|
|
86
75
|
defined_classes = {class_.class_ for class_ in self.classes}
|
|
87
|
-
|
|
76
|
+
classes_with_explicit_properties = {property_.class_ for property_ in self.properties}
|
|
88
77
|
|
|
89
78
|
# USE CASE: models are complete
|
|
90
|
-
if
|
|
91
|
-
missing_classes := referred_classes.difference(defined_classes)
|
|
92
|
-
):
|
|
79
|
+
if missing_classes := classes_with_explicit_properties.difference(defined_classes):
|
|
93
80
|
for class_ in missing_classes:
|
|
94
81
|
self.issue_list.append(
|
|
95
82
|
ResourceNotDefinedError[ClassEntity](
|
|
@@ -99,19 +86,6 @@ class InformationPostValidation:
|
|
|
99
86
|
)
|
|
100
87
|
)
|
|
101
88
|
|
|
102
|
-
# USE CASE: models are extended (user + last = complete)
|
|
103
|
-
if self.metadata.schema_ == SchemaCompleteness.extended:
|
|
104
|
-
defined_classes |= {class_.class_ for class_ in cast(InformationRules, self.rules.last).classes}
|
|
105
|
-
if missing_classes := referred_classes.difference(defined_classes):
|
|
106
|
-
for class_ in missing_classes:
|
|
107
|
-
self.issue_list.append(
|
|
108
|
-
ResourceNotDefinedError[ClassEntity](
|
|
109
|
-
resource_type="class",
|
|
110
|
-
identifier=class_,
|
|
111
|
-
location="Classes sheet",
|
|
112
|
-
)
|
|
113
|
-
)
|
|
114
|
-
|
|
115
89
|
def _referenced_value_types_exist(self) -> None:
|
|
116
90
|
# adding UnknownEntity to the set of defined classes to handle the case where a property references an unknown
|
|
117
91
|
defined_classes = {class_.class_ for class_ in self.classes} | {UnknownEntity()}
|
|
@@ -121,10 +95,7 @@ class InformationPostValidation:
|
|
|
121
95
|
if property_.type_ == EntityTypes.object_property
|
|
122
96
|
}
|
|
123
97
|
|
|
124
|
-
|
|
125
|
-
if self.metadata.schema_ == SchemaCompleteness.complete and (
|
|
126
|
-
missing_value_types := referred_object_types.difference(defined_classes)
|
|
127
|
-
):
|
|
98
|
+
if missing_value_types := referred_object_types.difference(defined_classes):
|
|
128
99
|
# Todo: include row and column number
|
|
129
100
|
for missing in missing_value_types:
|
|
130
101
|
self.issue_list.append(
|
|
@@ -135,56 +106,17 @@ class InformationPostValidation:
|
|
|
135
106
|
)
|
|
136
107
|
)
|
|
137
108
|
|
|
138
|
-
# USE CASE: models are extended (user + last = complete)
|
|
139
|
-
if self.metadata.schema_ == SchemaCompleteness.extended:
|
|
140
|
-
defined_classes |= {class_.class_ for class_ in cast(InformationRules, self.rules.last).classes}
|
|
141
|
-
if missing_value_types := referred_object_types.difference(defined_classes):
|
|
142
|
-
# Todo: include row and column number
|
|
143
|
-
for missing in missing_value_types:
|
|
144
|
-
self.issue_list.append(
|
|
145
|
-
ResourceNotDefinedError(
|
|
146
|
-
resource_type="class",
|
|
147
|
-
identifier=cast(ClassEntity, missing),
|
|
148
|
-
location="Classes sheet",
|
|
149
|
-
)
|
|
150
|
-
)
|
|
151
|
-
|
|
152
109
|
def _class_parent_pairs(self) -> dict[ClassEntity, list[ClassEntity]]:
|
|
153
|
-
|
|
154
|
-
|
|
110
|
+
class_parent_pairs: dict[ClassEntity, list[ClassEntity]] = {}
|
|
155
111
|
classes = self.rules.model_copy(deep=True).classes
|
|
156
112
|
|
|
157
|
-
# USE CASE: Solution model being extended (user + last + reference = complete)
|
|
158
|
-
if (
|
|
159
|
-
self.metadata.schema_ == SchemaCompleteness.extended
|
|
160
|
-
and self.metadata.data_model_type == DataModelType.solution
|
|
161
|
-
):
|
|
162
|
-
classes += (
|
|
163
|
-
cast(InformationRules, self.rules.last).model_copy(deep=True).classes
|
|
164
|
-
+ cast(InformationRules, self.rules.reference).model_copy(deep=True).classes
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
# USE CASE: Solution model being created from scratch (user + reference = complete)
|
|
168
|
-
elif (
|
|
169
|
-
self.metadata.schema_ == SchemaCompleteness.complete
|
|
170
|
-
and self.metadata.data_model_type == DataModelType.solution
|
|
171
|
-
):
|
|
172
|
-
classes += cast(InformationRules, self.rules.reference).model_copy(deep=True).classes
|
|
173
|
-
|
|
174
|
-
# USE CASE: Enterprise model being extended (user + last = complete)
|
|
175
|
-
elif (
|
|
176
|
-
self.metadata.schema_ == SchemaCompleteness.extended
|
|
177
|
-
and self.metadata.data_model_type == DataModelType.enterprise
|
|
178
|
-
):
|
|
179
|
-
classes += cast(InformationRules, self.rules.last).model_copy(deep=True).classes
|
|
180
|
-
|
|
181
113
|
for class_ in classes:
|
|
182
|
-
|
|
183
|
-
if class_.
|
|
114
|
+
class_parent_pairs[class_.class_] = []
|
|
115
|
+
if class_.implements is None:
|
|
184
116
|
continue
|
|
185
|
-
|
|
117
|
+
class_parent_pairs[class_.class_].extend(class_.implements)
|
|
186
118
|
|
|
187
|
-
return
|
|
119
|
+
return class_parent_pairs
|
|
188
120
|
|
|
189
121
|
def _namespaces_reassigned(self) -> None:
|
|
190
122
|
prefixes = self.rules.prefixes.copy()
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from ._classic2core import create_classic_to_core_mapping
|
|
1
|
+
from ._classic2core import load_classic_to_core_mapping
|
|
3
2
|
|
|
4
|
-
__all__ = ["
|
|
3
|
+
__all__ = ["load_classic_to_core_mapping"]
|
|
@@ -1,150 +1,40 @@
|
|
|
1
|
-
from
|
|
1
|
+
from functools import lru_cache
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
|
|
4
|
+
import yaml
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
from cognite.neat._issues.errors import NeatValueError
|
|
7
|
+
from cognite.neat._rules._shared import ReadRules
|
|
8
|
+
from cognite.neat._rules.models.dms import DMSInputRules, DMSRules
|
|
6
9
|
|
|
10
|
+
_CLASSIC_TO_CORE_MAPPING = Path(__file__).resolve().parent / "_classic2core.yaml"
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
"destination": {"Class": "cdf_cdm:CogniteAsset", "Property": "tag"},
|
|
39
|
-
"source": {"Class": "classic:Asset", "Property": "labels"},
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"destination": {"Class": "cdf_cdm:DataProduct", "Property": "externalId"},
|
|
43
|
-
"source": {"Class": "classic:DataSet", "Property": "externalId"},
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"destination": {"Class": "cdf_cdm:DataProduct", "Property": "name"},
|
|
47
|
-
"source": {"Class": "classic:DataSet", "Property": "name"},
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"destination": {"Class": "cdf_cdm:DataProduct", "Property": "description"},
|
|
51
|
-
"source": {"Class": "classic:DataSet", "Property": "description"},
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"destination": {"Class": "cdf_cdm:DataProduct", "Property": "metadata"},
|
|
55
|
-
"source": {"Class": "classic:DataSet", "Property": "metadata"},
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"destination": {"Class": "cdf_cdm:DataProduct", "Property": "writeProtected"},
|
|
59
|
-
"source": {"Class": "classic:DataSet", "Property": "writeProtected"},
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"destination": {"Class": "cdf_cdm:CogniteActivity", "Property": "externalId"},
|
|
63
|
-
"source": {"Class": "classic:Event", "Property": "externalId"},
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"destination": {"Class": "cdf_cdm:CogniteSchedulable", "Property": "startTime"},
|
|
67
|
-
"source": {"Class": "classic:Event", "Property": "startTime"},
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
"destination": {"Class": "cdf_cdm:CogniteSchedulable", "Property": "endTime"},
|
|
71
|
-
"source": {"Class": "classic:Event", "Property": "endTime"},
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"destination": {"Class": "cdf_cdm:CogniteActivity", "Property": "description"},
|
|
75
|
-
"source": {"Class": "classic:Event", "Property": "description"},
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
"destination": {"Class": "cdf_cdm:CogniteActivity", "Property": "assets"},
|
|
79
|
-
"source": {"Class": "classic:Event", "Property": "assetIds"},
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"destination": {"Class": "cdf_cdm:CogniteSourceSystem", "Property": "name"},
|
|
83
|
-
"source": {"Class": "classic:Event", "Property": "source"},
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"destination": {"Class": "cdf_cdm:CogniteFile", "Property": "externalId"},
|
|
87
|
-
"source": {"Class": "classic:File", "Property": "externalId"},
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"destination": {"Class": "cdf_cdm:CogniteFile", "Property": "name"},
|
|
91
|
-
"source": {"Class": "classic:File", "Property": "name"},
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
"destination": {"Class": "cdf_cdm:CogniteFile", "Property": "directory"},
|
|
95
|
-
"source": {"Class": "classic:File", "Property": "directory"},
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"destination": {"Class": "cdf_cdm:CogniteSourceSystem", "Property": "name"},
|
|
99
|
-
"source": {"Class": "classic:File", "Property": "source"},
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"destination": {"Class": "cdf_cdm:CogniteFile", "Property": "mimeType"},
|
|
103
|
-
"source": {"Class": "classic:File", "Property": "mimeType"},
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
"destination": {"Class": "cdf_cdm:CogniteFile", "Property": "assets"},
|
|
107
|
-
"source": {"Class": "classic:File", "Property": "assetIds"},
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
"destination": {"Class": "cdf_cdm:CogniteFile", "Property": "sourceCreatedTime"},
|
|
111
|
-
"source": {"Class": "classic:File", "Property": "sourceCreatedTime"},
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
"destination": {"Class": "cdf_cdm:CogniteFile", "Property": "sourceUpdatedTime"},
|
|
115
|
-
"source": {"Class": "classic:File", "Property": "sourceModifiedTime"},
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "externalId"},
|
|
119
|
-
"source": {"Class": "classic:TimeSeries", "Property": "externalId"},
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "name"},
|
|
123
|
-
"source": {"Class": "classic:TimeSeries", "Property": "name"},
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "type"},
|
|
127
|
-
"source": {"Class": "classic:TimeSeries", "Property": "isString"},
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "sourceUnit"},
|
|
131
|
-
"source": {"Class": "classic:TimeSeries", "Property": "unit"},
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "unit"},
|
|
135
|
-
"source": {"Class": "classic:TimeSeries", "Property": "unitExternalId"},
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "assets"},
|
|
139
|
-
"source": {"Class": "classic:TimeSeries", "Property": "assetId"},
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "isStep"},
|
|
143
|
-
"source": {"Class": "classic:TimeSeries", "Property": "isStep"},
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
"destination": {"Class": "cdf_cdm:CogniteTimeSeries", "Property": "description"},
|
|
147
|
-
"source": {"Class": "classic:TimeSeries", "Property": "description"},
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
}
|
|
12
|
+
|
|
13
|
+
@lru_cache(maxsize=1)
|
|
14
|
+
def _read_source_file() -> str:
|
|
15
|
+
return _CLASSIC_TO_CORE_MAPPING.read_text()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_classic_to_core_mapping(org_name: str, source_space: str, source_version: str) -> DMSRules:
|
|
19
|
+
if not org_name:
|
|
20
|
+
raise NeatValueError("Organization name must be provided.")
|
|
21
|
+
|
|
22
|
+
from cognite.neat._rules.importers import YAMLImporter
|
|
23
|
+
from cognite.neat._rules.transformers import VerifyDMSRules
|
|
24
|
+
|
|
25
|
+
raw_str = _read_source_file().replace("Classic", org_name)
|
|
26
|
+
|
|
27
|
+
loaded = yaml.safe_load(raw_str)
|
|
28
|
+
loaded["metadata"]["space"] = source_space
|
|
29
|
+
loaded["metadata"]["version"] = source_version
|
|
30
|
+
|
|
31
|
+
read: ReadRules[DMSInputRules] = YAMLImporter(loaded).to_rules()
|
|
32
|
+
if not isinstance(read.rules, DMSInputRules):
|
|
33
|
+
raise NeatValueError(f"Expected DMS rules, but got {type(read.rules).__name__}")
|
|
34
|
+
|
|
35
|
+
verified = VerifyDMSRules(errors="raise", post_validate=False).transform(read)
|
|
36
|
+
|
|
37
|
+
if verified.rules is None:
|
|
38
|
+
raise NeatValueError("Failed to verify the rules.")
|
|
39
|
+
|
|
40
|
+
return verified.rules
|