cognite-neat 0.76.3__py3-none-any.whl → 0.77.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_version.py +1 -1
- cognite/neat/rules/exporters/_rules2dms.py +13 -8
- cognite/neat/rules/importers/_dms2rules.py +72 -39
- cognite/neat/rules/models/dms/_exporter.py +25 -27
- cognite/neat/rules/models/dms/_rules.py +4 -6
- cognite/neat/rules/models/dms/_schema.py +186 -72
- cognite/neat/rules/models/dms/_validation.py +9 -9
- cognite/neat/rules/models/wrapped_entities.py +34 -2
- cognite/neat/utils/cdf_classes.py +181 -0
- cognite/neat/workflows/steps/lib/current/rules_validator.py +3 -3
- {cognite_neat-0.76.3.dist-info → cognite_neat-0.77.1.dist-info}/METADATA +1 -1
- {cognite_neat-0.76.3.dist-info → cognite_neat-0.77.1.dist-info}/RECORD +15 -14
- {cognite_neat-0.76.3.dist-info → cognite_neat-0.77.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.76.3.dist-info → cognite_neat-0.77.1.dist-info}/WHEEL +0 -0
- {cognite_neat-0.76.3.dist-info → cognite_neat-0.77.1.dist-info}/entry_points.txt +0 -0
cognite/neat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.77.1"
|
|
@@ -5,7 +5,14 @@ from typing import Literal, TypeAlias, cast
|
|
|
5
5
|
|
|
6
6
|
from cognite.client import CogniteClient
|
|
7
7
|
from cognite.client.data_classes._base import CogniteResource, CogniteResourceList
|
|
8
|
-
from cognite.client.data_classes.data_modeling import
|
|
8
|
+
from cognite.client.data_classes.data_modeling import (
|
|
9
|
+
ContainerApplyList,
|
|
10
|
+
DataModelApply,
|
|
11
|
+
DataModelApplyList,
|
|
12
|
+
DataModelId,
|
|
13
|
+
SpaceApplyList,
|
|
14
|
+
ViewApplyList,
|
|
15
|
+
)
|
|
9
16
|
from cognite.client.exceptions import CogniteAPIError
|
|
10
17
|
|
|
11
18
|
from cognite.neat.rules import issues
|
|
@@ -114,9 +121,7 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
114
121
|
dms_rules = rules.as_dms_architect_rules()
|
|
115
122
|
else:
|
|
116
123
|
raise ValueError(f"{type(rules).__name__} cannot be exported to DMS")
|
|
117
|
-
return dms_rules.as_schema(
|
|
118
|
-
include_ref=True, include_pipeline=self.export_pipeline, instance_space=self.instance_space
|
|
119
|
-
)
|
|
124
|
+
return dms_rules.as_schema(include_pipeline=self.export_pipeline, instance_space=self.instance_space)
|
|
120
125
|
|
|
121
126
|
def delete_from_cdf(self, rules: Rules, client: CogniteClient, dry_run: bool = False) -> Iterable[UploadResult]:
|
|
122
127
|
schema, to_export = self._prepare_schema_and_exporters(rules, client)
|
|
@@ -266,13 +271,13 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
266
271
|
schema = self.export(rules)
|
|
267
272
|
to_export: list[tuple[CogniteResourceList, ResourceLoader]] = []
|
|
268
273
|
if self.export_components.intersection({"all", "spaces"}):
|
|
269
|
-
to_export.append((schema.spaces, SpaceLoader(client)))
|
|
274
|
+
to_export.append((SpaceApplyList(schema.spaces.values()), SpaceLoader(client)))
|
|
270
275
|
if self.export_components.intersection({"all", "containers"}):
|
|
271
|
-
to_export.append((schema.containers, ContainerLoader(client)))
|
|
276
|
+
to_export.append((ContainerApplyList(schema.containers.values()), ContainerLoader(client)))
|
|
272
277
|
if self.export_components.intersection({"all", "views"}):
|
|
273
|
-
to_export.append((schema.views, ViewLoader(client, self.existing_handling)))
|
|
278
|
+
to_export.append((ViewApplyList(schema.views.values()), ViewLoader(client, self.existing_handling)))
|
|
274
279
|
if self.export_components.intersection({"all", "data_models"}):
|
|
275
|
-
to_export.append((schema.
|
|
280
|
+
to_export.append((DataModelApplyList([schema.data_model]), DataModelLoader(client)))
|
|
276
281
|
if isinstance(schema, PipelineSchema):
|
|
277
282
|
to_export.append((schema.databases, RawDatabaseLoader(client)))
|
|
278
283
|
to_export.append((schema.raw_tables, RawTableLoader(client)))
|
|
@@ -6,7 +6,7 @@ from typing import Any, Literal, cast, overload
|
|
|
6
6
|
|
|
7
7
|
from cognite.client import CogniteClient
|
|
8
8
|
from cognite.client import data_modeling as dm
|
|
9
|
-
from cognite.client.data_classes.data_modeling import DataModelIdentifier
|
|
9
|
+
from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier
|
|
10
10
|
from cognite.client.data_classes.data_modeling.containers import BTreeIndex, InvertedIndex
|
|
11
11
|
from cognite.client.data_classes.data_modeling.views import (
|
|
12
12
|
MultiEdgeConnectionApply,
|
|
@@ -39,7 +39,6 @@ from cognite.neat.rules.models.dms import (
|
|
|
39
39
|
from cognite.neat.rules.models.entities import (
|
|
40
40
|
ClassEntity,
|
|
41
41
|
ContainerEntity,
|
|
42
|
-
DataModelEntity,
|
|
43
42
|
DMSUnknownEntity,
|
|
44
43
|
ViewEntity,
|
|
45
44
|
ViewPropertyEntity,
|
|
@@ -52,56 +51,92 @@ class DMSImporter(BaseImporter):
|
|
|
52
51
|
schema: DMSSchema,
|
|
53
52
|
read_issues: Sequence[ValidationIssue] | None = None,
|
|
54
53
|
metadata: DMSMetadata | None = None,
|
|
54
|
+
ref_metadata: DMSMetadata | None = None,
|
|
55
55
|
):
|
|
56
56
|
# Calling this root schema to distinguish it from
|
|
57
57
|
# * User Schema
|
|
58
58
|
# * Reference Schema
|
|
59
59
|
self.root_schema = schema
|
|
60
60
|
self.metadata = metadata
|
|
61
|
+
self.ref_metadata = ref_metadata
|
|
61
62
|
self.issue_list = IssueList(read_issues)
|
|
62
|
-
self._all_containers_by_id =
|
|
63
|
+
self._all_containers_by_id = schema.containers.copy()
|
|
63
64
|
if self.root_schema.reference:
|
|
64
|
-
self._all_containers_by_id.update(
|
|
65
|
-
{container.as_id(): container for container in self.root_schema.reference.containers}
|
|
66
|
-
)
|
|
65
|
+
self._all_containers_by_id.update(self.root_schema.reference.containers)
|
|
67
66
|
|
|
68
67
|
@classmethod
|
|
69
|
-
def from_data_model_id(
|
|
68
|
+
def from_data_model_id(
|
|
69
|
+
cls,
|
|
70
|
+
client: CogniteClient,
|
|
71
|
+
data_model_id: DataModelIdentifier,
|
|
72
|
+
reference_model_id: DataModelIdentifier | None = None,
|
|
73
|
+
) -> "DMSImporter":
|
|
70
74
|
"""Create a DMSImporter ready to convert the given data model to rules.
|
|
71
75
|
|
|
72
76
|
Args:
|
|
73
77
|
client: Instantiated CogniteClient to retrieve data model.
|
|
78
|
+
reference_model_id: The reference data model to retrieve. This is the data model that
|
|
79
|
+
the given data model is built on top of, typically, an enterprise data model.
|
|
74
80
|
data_model_id: Data Model to retrieve.
|
|
75
81
|
|
|
76
82
|
Returns:
|
|
77
83
|
DMSImporter: DMSImporter instance
|
|
78
84
|
"""
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
data_model_ids = [data_model_id, reference_model_id] if reference_model_id else [data_model_id]
|
|
86
|
+
data_models = client.data_modeling.data_models.retrieve(data_model_ids, inline_views=True)
|
|
87
|
+
|
|
88
|
+
user_models = cls._find_model_in_list(data_models, data_model_id)
|
|
89
|
+
if len(user_models) == 0:
|
|
81
90
|
return cls(DMSSchema(), [issues.importing.NoDataModelError(f"Data model {data_model_id} not found")])
|
|
82
|
-
|
|
91
|
+
user_model = user_models.latest_version()
|
|
92
|
+
|
|
93
|
+
if reference_model_id:
|
|
94
|
+
ref_models = cls._find_model_in_list(data_models, reference_model_id)
|
|
95
|
+
if len(ref_models) == 0:
|
|
96
|
+
return cls(
|
|
97
|
+
DMSSchema(), [issues.importing.NoDataModelError(f"Data model {reference_model_id} not found")]
|
|
98
|
+
)
|
|
99
|
+
ref_model: dm.DataModel[dm.View] | None = ref_models.latest_version()
|
|
100
|
+
else:
|
|
101
|
+
ref_model = None
|
|
83
102
|
|
|
84
103
|
try:
|
|
85
|
-
schema = DMSSchema.from_data_model(client,
|
|
104
|
+
schema = DMSSchema.from_data_model(client, user_model, ref_model)
|
|
86
105
|
except Exception as e:
|
|
87
106
|
return cls(DMSSchema(), [issues.importing.APIError(str(e))])
|
|
88
107
|
|
|
89
|
-
|
|
90
|
-
|
|
108
|
+
metadata = cls._create_metadata_from_model(user_model)
|
|
109
|
+
ref_metadata = cls._create_metadata_from_model(ref_model) if ref_model else None
|
|
91
110
|
|
|
92
|
-
|
|
111
|
+
return cls(schema, [], metadata, ref_metadata)
|
|
93
112
|
|
|
94
|
-
|
|
113
|
+
@classmethod
|
|
114
|
+
def _find_model_in_list(
|
|
115
|
+
cls, data_models: dm.DataModelList[dm.View], model_id: DataModelIdentifier
|
|
116
|
+
) -> dm.DataModelList[dm.View]:
|
|
117
|
+
identifier = DataModelId.load(model_id)
|
|
118
|
+
return dm.DataModelList[dm.View](
|
|
119
|
+
[
|
|
120
|
+
model
|
|
121
|
+
for model in data_models
|
|
122
|
+
if (model.space, model.external_id) == (identifier.space, identifier.external_id)
|
|
123
|
+
]
|
|
124
|
+
)
|
|
95
125
|
|
|
96
126
|
@classmethod
|
|
97
127
|
def _create_metadata_from_model(
|
|
98
128
|
cls,
|
|
99
129
|
model: dm.DataModel[dm.View] | dm.DataModelApply,
|
|
100
|
-
created: datetime | None = None,
|
|
101
|
-
updated: datetime | None = None,
|
|
102
130
|
) -> DMSMetadata:
|
|
103
131
|
description, creator = DMSMetadata._get_description_and_creator(model.description)
|
|
104
|
-
|
|
132
|
+
|
|
133
|
+
if isinstance(model, dm.DataModel):
|
|
134
|
+
created = ms_to_datetime(model.created_time)
|
|
135
|
+
updated = ms_to_datetime(model.last_updated_time)
|
|
136
|
+
else:
|
|
137
|
+
now = datetime.now().replace(microsecond=0)
|
|
138
|
+
created = now
|
|
139
|
+
updated = now
|
|
105
140
|
return DMSMetadata(
|
|
106
141
|
schema_=SchemaCompleteness.complete,
|
|
107
142
|
extension=ExtensionCategory.addition,
|
|
@@ -109,8 +144,8 @@ class DMSImporter(BaseImporter):
|
|
|
109
144
|
external_id=model.external_id,
|
|
110
145
|
name=model.name or model.external_id,
|
|
111
146
|
version=model.version or "0.1.0",
|
|
112
|
-
updated=updated
|
|
113
|
-
created=created
|
|
147
|
+
updated=updated,
|
|
148
|
+
created=created,
|
|
114
149
|
creator=creator,
|
|
115
150
|
description=description,
|
|
116
151
|
)
|
|
@@ -147,28 +182,33 @@ class DMSImporter(BaseImporter):
|
|
|
147
182
|
# In case there were errors during the import, the to_rules method will return None
|
|
148
183
|
return self._return_or_raise(self.issue_list, errors)
|
|
149
184
|
|
|
150
|
-
if
|
|
185
|
+
if not self.root_schema.data_model:
|
|
151
186
|
self.issue_list.append(issues.importing.NoDataModelError("No data model found."))
|
|
152
187
|
return self._return_or_raise(self.issue_list, errors)
|
|
153
|
-
|
|
188
|
+
model = self.root_schema.data_model
|
|
154
189
|
with _handle_issues(
|
|
155
190
|
self.issue_list,
|
|
156
191
|
) as future:
|
|
157
192
|
schema_completeness = SchemaCompleteness.complete
|
|
158
193
|
data_model_type = DataModelType.enterprise
|
|
159
194
|
reference: DMSRules | None = None
|
|
160
|
-
if ref_schema := self.root_schema.reference:
|
|
195
|
+
if (ref_schema := self.root_schema.reference) and (ref_model := ref_schema.data_model):
|
|
161
196
|
# Reference should always be an enterprise model.
|
|
162
197
|
reference = DMSRules(
|
|
163
198
|
**self._create_rule_components(
|
|
164
|
-
|
|
199
|
+
ref_model,
|
|
200
|
+
ref_schema,
|
|
201
|
+
self.ref_metadata or self._create_default_metadata(list(ref_schema.views.values())),
|
|
202
|
+
DataModelType.enterprise,
|
|
165
203
|
)
|
|
166
204
|
)
|
|
167
205
|
schema_completeness = SchemaCompleteness.extended
|
|
168
206
|
data_model_type = DataModelType.solution
|
|
169
207
|
|
|
170
208
|
user_rules = DMSRules(
|
|
171
|
-
**self._create_rule_components(
|
|
209
|
+
**self._create_rule_components(
|
|
210
|
+
model, self.root_schema, self.metadata, data_model_type, schema_completeness
|
|
211
|
+
),
|
|
172
212
|
reference=reference,
|
|
173
213
|
)
|
|
174
214
|
|
|
@@ -179,24 +219,14 @@ class DMSImporter(BaseImporter):
|
|
|
179
219
|
|
|
180
220
|
def _create_rule_components(
|
|
181
221
|
self,
|
|
222
|
+
data_model: dm.DataModelApply,
|
|
182
223
|
schema: DMSSchema,
|
|
183
224
|
metadata: DMSMetadata | None = None,
|
|
184
225
|
data_model_type: DataModelType | None = None,
|
|
185
226
|
schema_completeness: SchemaCompleteness | None = None,
|
|
186
227
|
) -> dict[str, Any]:
|
|
187
|
-
if len(schema.data_models) > 2:
|
|
188
|
-
# Creating a DataModelEntity to convert the data model id to a string.
|
|
189
|
-
self.issue_list.append(
|
|
190
|
-
issues.importing.MultipleDataModelsWarning(
|
|
191
|
-
[str(DataModelEntity.from_id(model.as_id())) for model in schema.data_models]
|
|
192
|
-
)
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
data_model = schema.data_models[0]
|
|
196
|
-
|
|
197
228
|
properties = SheetList[DMSProperty]()
|
|
198
|
-
for view in schema.views:
|
|
199
|
-
view_id = view.as_id()
|
|
229
|
+
for view_id, view in schema.views.items():
|
|
200
230
|
view_entity = ViewEntity.from_id(view_id)
|
|
201
231
|
class_entity = view_entity.as_class()
|
|
202
232
|
for prop_id, prop in (view.properties or {}).items():
|
|
@@ -217,10 +247,13 @@ class DMSImporter(BaseImporter):
|
|
|
217
247
|
metadata=metadata,
|
|
218
248
|
properties=properties,
|
|
219
249
|
containers=SheetList[DMSContainer](
|
|
220
|
-
data=[DMSContainer.from_container(container) for container in schema.containers]
|
|
250
|
+
data=[DMSContainer.from_container(container) for container in schema.containers.values()]
|
|
221
251
|
),
|
|
222
252
|
views=SheetList[DMSView](
|
|
223
|
-
data=[
|
|
253
|
+
data=[
|
|
254
|
+
DMSView.from_view(view, in_model=view_id in data_model_view_ids)
|
|
255
|
+
for view_id, view in schema.views.items()
|
|
256
|
+
]
|
|
224
257
|
),
|
|
225
258
|
)
|
|
226
259
|
|
|
@@ -22,6 +22,7 @@ from cognite.neat.rules.models.entities import (
|
|
|
22
22
|
ViewPropertyEntity,
|
|
23
23
|
)
|
|
24
24
|
from cognite.neat.rules.models.wrapped_entities import DMSFilter, HasDataFilter, NodeTypeFilter
|
|
25
|
+
from cognite.neat.utils.cdf_classes import ContainerApplyDict, NodeApplyDict, SpaceApplyDict, ViewApplyDict
|
|
25
26
|
|
|
26
27
|
from ._rules import DMSMetadata, DMSProperty, DMSRules, DMSView
|
|
27
28
|
from ._schema import DMSSchema, PipelineSchema
|
|
@@ -42,18 +43,18 @@ class _DMSExporter:
|
|
|
42
43
|
def __init__(
|
|
43
44
|
self,
|
|
44
45
|
rules: DMSRules,
|
|
45
|
-
include_ref: bool = True,
|
|
46
46
|
include_pipeline: bool = False,
|
|
47
47
|
instance_space: str | None = None,
|
|
48
48
|
):
|
|
49
|
-
self.include_ref = include_ref
|
|
50
49
|
self.include_pipeline = include_pipeline
|
|
51
50
|
self.instance_space = instance_space
|
|
52
51
|
self.rules = rules
|
|
53
52
|
self._ref_schema = rules.reference.as_schema() if rules.reference else None
|
|
54
53
|
if self._ref_schema:
|
|
55
54
|
# We skip version as that will always be missing in the reference
|
|
56
|
-
self._ref_views_by_id = {
|
|
55
|
+
self._ref_views_by_id = {
|
|
56
|
+
dm.ViewId(view.space, view.external_id): view for view in self._ref_schema.views.values()
|
|
57
|
+
}
|
|
57
58
|
else:
|
|
58
59
|
self._ref_views_by_id = {}
|
|
59
60
|
|
|
@@ -67,7 +68,7 @@ class _DMSExporter:
|
|
|
67
68
|
views_not_in_model = {view.view.as_id() for view in rules.views if not view.in_model}
|
|
68
69
|
data_model = rules.metadata.as_data_model()
|
|
69
70
|
data_model.views = sorted(
|
|
70
|
-
[view_id for view_id in views.
|
|
71
|
+
[view_id for view_id in views.keys() if view_id not in views_not_in_model],
|
|
71
72
|
key=lambda v: v.as_tuple(), # type: ignore[union-attr]
|
|
72
73
|
)
|
|
73
74
|
|
|
@@ -75,7 +76,7 @@ class _DMSExporter:
|
|
|
75
76
|
|
|
76
77
|
output = DMSSchema(
|
|
77
78
|
spaces=spaces,
|
|
78
|
-
|
|
79
|
+
data_model=data_model,
|
|
79
80
|
views=views,
|
|
80
81
|
containers=containers,
|
|
81
82
|
node_types=node_types,
|
|
@@ -91,31 +92,30 @@ class _DMSExporter:
|
|
|
91
92
|
def _create_spaces(
|
|
92
93
|
self,
|
|
93
94
|
metadata: DMSMetadata,
|
|
94
|
-
containers:
|
|
95
|
-
views:
|
|
95
|
+
containers: ContainerApplyDict,
|
|
96
|
+
views: ViewApplyDict,
|
|
96
97
|
data_model: dm.DataModelApply,
|
|
97
|
-
) ->
|
|
98
|
-
used_spaces = {container.space for container in containers} | {view.space for view in views}
|
|
98
|
+
) -> SpaceApplyDict:
|
|
99
|
+
used_spaces = {container.space for container in containers.values()} | {view.space for view in views.values()}
|
|
99
100
|
if len(used_spaces) == 1:
|
|
100
101
|
# We skip the default space and only use this space for the data model
|
|
101
102
|
data_model.space = used_spaces.pop()
|
|
102
|
-
spaces =
|
|
103
|
+
spaces = SpaceApplyDict([dm.SpaceApply(space=data_model.space)])
|
|
103
104
|
else:
|
|
104
105
|
used_spaces.add(metadata.space)
|
|
105
|
-
spaces =
|
|
106
|
-
if self.instance_space and self.instance_space not in
|
|
107
|
-
spaces.
|
|
106
|
+
spaces = SpaceApplyDict([dm.SpaceApply(space=space) for space in used_spaces])
|
|
107
|
+
if self.instance_space and self.instance_space not in spaces:
|
|
108
|
+
spaces[self.instance_space] = dm.SpaceApply(space=self.instance_space, name=self.instance_space)
|
|
108
109
|
return spaces
|
|
109
110
|
|
|
110
111
|
def _create_views_with_node_types(
|
|
111
112
|
self,
|
|
112
113
|
view_properties_by_id: dict[dm.ViewId, list[DMSProperty]],
|
|
113
|
-
) -> tuple[
|
|
114
|
-
views =
|
|
114
|
+
) -> tuple[ViewApplyDict, NodeApplyDict]:
|
|
115
|
+
views = ViewApplyDict([dms_view.as_view() for dms_view in self.rules.views])
|
|
115
116
|
dms_view_by_id = {dms_view.view.as_id(): dms_view for dms_view in self.rules.views}
|
|
116
117
|
|
|
117
|
-
for view in views:
|
|
118
|
-
view_id = view.as_id()
|
|
118
|
+
for view_id, view in views.items():
|
|
119
119
|
view.properties = {}
|
|
120
120
|
if not (view_properties := view_properties_by_id.get(view_id)):
|
|
121
121
|
continue
|
|
@@ -126,14 +126,13 @@ class _DMSExporter:
|
|
|
126
126
|
|
|
127
127
|
data_model_type = self.rules.metadata.data_model_type
|
|
128
128
|
unique_node_types: set[dm.NodeId] = set()
|
|
129
|
-
parent_views = {parent for view in views for parent in view.implements or []}
|
|
130
|
-
for view in views:
|
|
131
|
-
dms_view = dms_view_by_id.get(
|
|
132
|
-
dms_properties = view_properties_by_id.get(
|
|
129
|
+
parent_views = {parent for view in views.values() for parent in view.implements or []}
|
|
130
|
+
for view_id, view in views.items():
|
|
131
|
+
dms_view = dms_view_by_id.get(view_id)
|
|
132
|
+
dms_properties = view_properties_by_id.get(view_id, [])
|
|
133
133
|
view_filter = self._create_view_filter(view, dms_view, data_model_type, dms_properties)
|
|
134
134
|
|
|
135
135
|
view.filter = view_filter.as_dms_filter()
|
|
136
|
-
|
|
137
136
|
if isinstance(view_filter, NodeTypeFilter):
|
|
138
137
|
unique_node_types.update(view_filter.nodes)
|
|
139
138
|
if view.as_id() in parent_views:
|
|
@@ -153,7 +152,7 @@ class _DMSExporter:
|
|
|
153
152
|
issues.dms.HasDataFilterOnViewWithReferencesWarning(view.as_id(), list(references)), stacklevel=2
|
|
154
153
|
)
|
|
155
154
|
|
|
156
|
-
return views,
|
|
155
|
+
return views, NodeApplyDict(
|
|
157
156
|
[dm.NodeApply(space=node.space, external_id=node.external_id) for node in unique_node_types]
|
|
158
157
|
)
|
|
159
158
|
|
|
@@ -176,7 +175,7 @@ class _DMSExporter:
|
|
|
176
175
|
def _create_containers(
|
|
177
176
|
self,
|
|
178
177
|
container_properties_by_id: dict[dm.ContainerId, list[DMSProperty]],
|
|
179
|
-
) ->
|
|
178
|
+
) -> ContainerApplyDict:
|
|
180
179
|
containers = dm.ContainerApplyList(
|
|
181
180
|
[dms_container.as_container() for dms_container in self.rules.containers or []]
|
|
182
181
|
)
|
|
@@ -231,9 +230,7 @@ class _DMSExporter:
|
|
|
231
230
|
for name, const in container.constraints.items()
|
|
232
231
|
if not (isinstance(const, dm.RequiresConstraint) and const.require in container_to_drop)
|
|
233
232
|
}
|
|
234
|
-
return
|
|
235
|
-
[container for container in containers if container.as_id() not in container_to_drop]
|
|
236
|
-
)
|
|
233
|
+
return ContainerApplyDict([container for container in containers if container.as_id() not in container_to_drop])
|
|
237
234
|
|
|
238
235
|
def _gather_properties(self) -> tuple[dict[dm.ContainerId, list[DMSProperty]], dict[dm.ViewId, list[DMSProperty]]]:
|
|
239
236
|
container_properties_by_id: dict[dm.ContainerId, list[DMSProperty]] = defaultdict(list)
|
|
@@ -256,6 +253,7 @@ class _DMSExporter:
|
|
|
256
253
|
dms_properties: list[DMSProperty],
|
|
257
254
|
) -> DMSFilter:
|
|
258
255
|
selected_filter_name = (dms_view and dms_view.filter_ and dms_view.filter_.name) or ""
|
|
256
|
+
|
|
259
257
|
if dms_view and dms_view.filter_ and not dms_view.filter_.is_empty:
|
|
260
258
|
# Has Explicit Filter, use it
|
|
261
259
|
return dms_view.filter_
|
|
@@ -41,7 +41,7 @@ from cognite.neat.rules.models.entities import (
|
|
|
41
41
|
ViewEntityList,
|
|
42
42
|
ViewPropertyEntity,
|
|
43
43
|
)
|
|
44
|
-
from cognite.neat.rules.models.wrapped_entities import HasDataFilter, NodeTypeFilter
|
|
44
|
+
from cognite.neat.rules.models.wrapped_entities import HasDataFilter, NodeTypeFilter, RawFilter
|
|
45
45
|
|
|
46
46
|
from ._schema import DMSSchema
|
|
47
47
|
|
|
@@ -257,7 +257,7 @@ class DMSView(SheetEntity):
|
|
|
257
257
|
description: str | None = Field(alias="Description", default=None)
|
|
258
258
|
implements: ViewEntityList | None = Field(None, alias="Implements")
|
|
259
259
|
reference: URLEntity | ReferenceEntity | None = Field(alias="Reference", default=None, union_mode="left_to_right")
|
|
260
|
-
filter_: HasDataFilter | NodeTypeFilter | None = Field(None, alias="Filter")
|
|
260
|
+
filter_: HasDataFilter | NodeTypeFilter | RawFilter | None = Field(None, alias="Filter")
|
|
261
261
|
in_model: bool = Field(True, alias="In Model")
|
|
262
262
|
class_: ClassEntity = Field(alias="Class (linage)")
|
|
263
263
|
|
|
@@ -345,12 +345,10 @@ class DMSRules(BaseRules):
|
|
|
345
345
|
space, version = self.metadata.space, self.metadata.version
|
|
346
346
|
return _DMSRulesSerializer(info, space, version).clean(dumped)
|
|
347
347
|
|
|
348
|
-
def as_schema(
|
|
349
|
-
self, include_ref: bool = False, include_pipeline: bool = False, instance_space: str | None = None
|
|
350
|
-
) -> DMSSchema:
|
|
348
|
+
def as_schema(self, include_pipeline: bool = False, instance_space: str | None = None) -> DMSSchema:
|
|
351
349
|
from ._exporter import _DMSExporter
|
|
352
350
|
|
|
353
|
-
return _DMSExporter(self,
|
|
351
|
+
return _DMSExporter(self, include_pipeline, instance_space).to_schema()
|
|
354
352
|
|
|
355
353
|
def as_information_architect_rules(self) -> "InformationRules":
|
|
356
354
|
from ._converter import _DMSRulesConverter
|
|
@@ -30,6 +30,13 @@ from cognite.neat.rules.issues.dms import (
|
|
|
30
30
|
MissingViewError,
|
|
31
31
|
)
|
|
32
32
|
from cognite.neat.rules.models.data_types import _DATA_TYPE_BY_DMS_TYPE
|
|
33
|
+
from cognite.neat.utils.cdf_classes import (
|
|
34
|
+
CogniteResourceDict,
|
|
35
|
+
ContainerApplyDict,
|
|
36
|
+
NodeApplyDict,
|
|
37
|
+
SpaceApplyDict,
|
|
38
|
+
ViewApplyDict,
|
|
39
|
+
)
|
|
33
40
|
from cognite.neat.utils.cdf_loaders import ViewLoader
|
|
34
41
|
from cognite.neat.utils.cdf_loaders.data_classes import RawTableWrite, RawTableWriteList
|
|
35
42
|
from cognite.neat.utils.text import to_camel
|
|
@@ -43,12 +50,12 @@ else:
|
|
|
43
50
|
|
|
44
51
|
@dataclass
|
|
45
52
|
class DMSSchema:
|
|
46
|
-
|
|
47
|
-
spaces:
|
|
48
|
-
views:
|
|
49
|
-
containers:
|
|
50
|
-
node_types:
|
|
51
|
-
# The last schema is the previous version of the data model. In the case, extension=
|
|
53
|
+
data_model: dm.DataModelApply | None = None
|
|
54
|
+
spaces: SpaceApplyDict = field(default_factory=SpaceApplyDict)
|
|
55
|
+
views: ViewApplyDict = field(default_factory=ViewApplyDict)
|
|
56
|
+
containers: ContainerApplyDict = field(default_factory=ContainerApplyDict)
|
|
57
|
+
node_types: NodeApplyDict = field(default_factory=NodeApplyDict)
|
|
58
|
+
# The last schema is the previous version of the data model. In the case, extension=addition, this
|
|
52
59
|
# should not be modified.
|
|
53
60
|
last: "DMSSchema | None" = None
|
|
54
61
|
# Reference is typically the Enterprise model, while this is the solution model.
|
|
@@ -57,29 +64,28 @@ class DMSSchema:
|
|
|
57
64
|
_FIELD_NAME_BY_RESOURCE_TYPE: ClassVar[dict[str, str]] = {
|
|
58
65
|
"container": "containers",
|
|
59
66
|
"view": "views",
|
|
60
|
-
"datamodel": "
|
|
67
|
+
"datamodel": "data_model",
|
|
61
68
|
"space": "spaces",
|
|
62
69
|
"node": "node_types",
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
def _get_mapped_container_from_view(self, view_id: dm.ViewId) -> set[dm.ContainerId]:
|
|
66
73
|
# index all views, including ones from reference
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
74
|
+
view_by_id = self.views.copy()
|
|
75
|
+
if self.reference:
|
|
76
|
+
view_by_id.update(self.reference.views)
|
|
71
77
|
|
|
72
|
-
if view_id not in
|
|
78
|
+
if view_id not in view_by_id:
|
|
73
79
|
raise ValueError(f"View {view_id} not found")
|
|
74
80
|
|
|
75
|
-
indexed_implemented_views = {id_: view.implements for id_, view in
|
|
81
|
+
indexed_implemented_views = {id_: view.implements for id_, view in view_by_id.items()}
|
|
76
82
|
view_inheritance = get_inheritance_path(view_id, indexed_implemented_views)
|
|
77
83
|
|
|
78
|
-
directly_referenced_containers =
|
|
84
|
+
directly_referenced_containers = view_by_id[view_id].referenced_containers()
|
|
79
85
|
inherited_referenced_containers = set()
|
|
80
86
|
|
|
81
87
|
for view_id in view_inheritance:
|
|
82
|
-
if implemented_view :=
|
|
88
|
+
if implemented_view := view_by_id.get(view_id):
|
|
83
89
|
inherited_referenced_containers |= implemented_view.referenced_containers()
|
|
84
90
|
else:
|
|
85
91
|
raise IncompleteSchemaError(missing_component=view_id).as_exception()
|
|
@@ -95,18 +101,49 @@ class DMSSchema:
|
|
|
95
101
|
return cls.from_data_model(client, data_model)
|
|
96
102
|
|
|
97
103
|
@classmethod
|
|
98
|
-
def from_data_model(
|
|
104
|
+
def from_data_model(
|
|
105
|
+
cls,
|
|
106
|
+
client: CogniteClient,
|
|
107
|
+
data_model: dm.DataModel[dm.View],
|
|
108
|
+
reference_model: dm.DataModel[dm.View] | None = None,
|
|
109
|
+
) -> "DMSSchema":
|
|
110
|
+
"""Create a schema from a data model.
|
|
111
|
+
|
|
112
|
+
If a reference model is provided, the schema will include a reference schema. To determine which views,
|
|
113
|
+
and containers to put in the reference schema, the following rule is applied:
|
|
114
|
+
|
|
115
|
+
If a view or container space is different from the data model space,
|
|
116
|
+
it will be included in the reference schema.*
|
|
117
|
+
|
|
118
|
+
*One exception to this rule is if a view is directly referenced by the data model. In this case, the view will
|
|
119
|
+
be included in the data model schema, even if the space is different.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
client: The Cognite client used for retrieving components referenced by the data model.
|
|
123
|
+
data_model: The data model to create the schema from.
|
|
124
|
+
reference_model: (Optional) The reference model to include in the schema.
|
|
125
|
+
This is typically the Enterprise model.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
DMSSchema: The schema created from the data model.
|
|
129
|
+
"""
|
|
99
130
|
views = dm.ViewList(data_model.views)
|
|
131
|
+
|
|
132
|
+
data_model_write = data_model.as_write()
|
|
133
|
+
data_model_write.views = list(views.as_ids())
|
|
134
|
+
|
|
135
|
+
if reference_model:
|
|
136
|
+
views.extend(reference_model.views)
|
|
137
|
+
|
|
100
138
|
container_ids = views.referenced_containers()
|
|
101
139
|
containers = client.data_modeling.containers.retrieve(list(container_ids))
|
|
102
140
|
cls._append_referenced_containers(client, containers)
|
|
103
141
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
data_model_write.views = list(views.as_write())
|
|
142
|
+
space_ids = [data_model.space, reference_model.space] if reference_model else [data_model.space]
|
|
143
|
+
space_read = client.data_modeling.spaces.retrieve(space_ids)
|
|
144
|
+
if len(space_read) != len(space_ids):
|
|
145
|
+
raise ValueError(f"Space(s) {space_read} not found")
|
|
146
|
+
space_write = space_read.as_write()
|
|
110
147
|
|
|
111
148
|
view_loader = ViewLoader(client)
|
|
112
149
|
# We need to include parent views in the schema to make sure that the schema is valid.
|
|
@@ -119,13 +156,51 @@ class DMSSchema:
|
|
|
119
156
|
# as the read format contains all properties from all parents, while the write formate should not contain
|
|
120
157
|
# properties from any parents.
|
|
121
158
|
# The ViewLoader as_write method looks up parents and remove properties from them.
|
|
122
|
-
view_write =
|
|
159
|
+
view_write = ViewApplyDict([view_loader.as_write(view) for view in views])
|
|
160
|
+
|
|
161
|
+
container_write = ContainerApplyDict(containers.as_write())
|
|
162
|
+
user_space = data_model.space
|
|
163
|
+
if reference_model:
|
|
164
|
+
user_model_view_ids = set(data_model_write.views)
|
|
165
|
+
ref_model_write = reference_model.as_write()
|
|
166
|
+
ref_model_write.views = [view.as_id() for view in reference_model.views]
|
|
167
|
+
|
|
168
|
+
ref_views = ViewApplyDict(
|
|
169
|
+
[
|
|
170
|
+
view
|
|
171
|
+
for view_id, view in view_write.items()
|
|
172
|
+
if (view.space != user_space) or (view_id not in user_model_view_ids)
|
|
173
|
+
]
|
|
174
|
+
)
|
|
175
|
+
view_write = ViewApplyDict(
|
|
176
|
+
[
|
|
177
|
+
view
|
|
178
|
+
for view_id, view in view_write.items()
|
|
179
|
+
if view.space == user_space or view_id in user_model_view_ids
|
|
180
|
+
]
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
ref_containers = ContainerApplyDict(
|
|
184
|
+
[container for container in container_write.values() if container.space != user_space]
|
|
185
|
+
)
|
|
186
|
+
container_write = ContainerApplyDict(
|
|
187
|
+
[container for container in container_write.values() if container.space == user_space]
|
|
188
|
+
)
|
|
123
189
|
|
|
190
|
+
ref_schema: DMSSchema | None = cls(
|
|
191
|
+
spaces=SpaceApplyDict([s for s in space_write if s.space != user_space]),
|
|
192
|
+
data_model=ref_model_write,
|
|
193
|
+
views=ref_views,
|
|
194
|
+
containers=ref_containers,
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
ref_schema = None
|
|
124
198
|
return cls(
|
|
125
|
-
spaces=
|
|
126
|
-
|
|
199
|
+
spaces=SpaceApplyDict([s for s in space_write if s.space == user_space]),
|
|
200
|
+
data_model=data_model_write,
|
|
127
201
|
views=view_write,
|
|
128
|
-
containers=
|
|
202
|
+
containers=container_write,
|
|
203
|
+
reference=ref_schema,
|
|
129
204
|
)
|
|
130
205
|
|
|
131
206
|
@classmethod
|
|
@@ -183,33 +258,32 @@ class DMSSchema:
|
|
|
183
258
|
data_models = path_dir / "data_models"
|
|
184
259
|
data_models.mkdir(parents=True, exist_ok=True)
|
|
185
260
|
if "spaces" not in exclude_set:
|
|
186
|
-
for space in self.spaces:
|
|
261
|
+
for space in self.spaces.values():
|
|
187
262
|
(data_models / f"{space.space}.space.yaml").write_text(
|
|
188
263
|
space.dump_yaml(), newline=new_line, encoding=encoding
|
|
189
264
|
)
|
|
190
|
-
if "data_models" not in exclude_set:
|
|
191
|
-
|
|
192
|
-
(
|
|
193
|
-
|
|
194
|
-
)
|
|
265
|
+
if "data_models" not in exclude_set and self.data_model:
|
|
266
|
+
(data_models / f"{self.data_model.external_id}.datamodel.yaml").write_text(
|
|
267
|
+
self.data_model.dump_yaml(), newline=new_line, encoding=encoding
|
|
268
|
+
)
|
|
195
269
|
if "views" not in exclude_set and self.views:
|
|
196
270
|
view_dir = data_models / "views"
|
|
197
271
|
view_dir.mkdir(parents=True, exist_ok=True)
|
|
198
|
-
for view in self.views:
|
|
272
|
+
for view in self.views.values():
|
|
199
273
|
(view_dir / f"{view.external_id}.view.yaml").write_text(
|
|
200
274
|
view.dump_yaml(), newline=new_line, encoding=encoding
|
|
201
275
|
)
|
|
202
276
|
if "containers" not in exclude_set and self.containers:
|
|
203
277
|
container_dir = data_models / "containers"
|
|
204
278
|
container_dir.mkdir(parents=True, exist_ok=True)
|
|
205
|
-
for container in self.containers:
|
|
279
|
+
for container in self.containers.values():
|
|
206
280
|
(container_dir / f"{container.external_id}.container.yaml").write_text(
|
|
207
281
|
container.dump_yaml(), newline=new_line, encoding=encoding
|
|
208
282
|
)
|
|
209
283
|
if "node_types" not in exclude_set and self.node_types:
|
|
210
284
|
node_dir = data_models / "nodes"
|
|
211
285
|
node_dir.mkdir(parents=True, exist_ok=True)
|
|
212
|
-
for node in self.node_types:
|
|
286
|
+
for node in self.node_types.values():
|
|
213
287
|
(node_dir / f"{node.external_id}.node.yaml").write_text(
|
|
214
288
|
node.dump_yaml(), newline=new_line, encoding=encoding
|
|
215
289
|
)
|
|
@@ -263,21 +337,22 @@ class DMSSchema:
|
|
|
263
337
|
exclude_set = exclude or set()
|
|
264
338
|
with zipfile.ZipFile(zip_file, "w") as zip_ref:
|
|
265
339
|
if "spaces" not in exclude_set:
|
|
266
|
-
for space in self.spaces:
|
|
340
|
+
for space in self.spaces.values():
|
|
267
341
|
zip_ref.writestr(f"data_models/{space.space}.space.yaml", space.dump_yaml())
|
|
268
|
-
if "data_models" not in exclude_set:
|
|
269
|
-
|
|
270
|
-
|
|
342
|
+
if "data_models" not in exclude_set and self.data_model:
|
|
343
|
+
zip_ref.writestr(
|
|
344
|
+
f"data_models/{self.data_model.external_id}.datamodel.yaml", self.data_model.dump_yaml()
|
|
345
|
+
)
|
|
271
346
|
if "views" not in exclude_set:
|
|
272
|
-
for view in self.views:
|
|
347
|
+
for view in self.views.values():
|
|
273
348
|
zip_ref.writestr(f"data_models/views/{view.external_id}.view.yaml", view.dump_yaml())
|
|
274
349
|
if "containers" not in exclude_set:
|
|
275
|
-
for container in self.containers:
|
|
350
|
+
for container in self.containers.values():
|
|
276
351
|
zip_ref.writestr(
|
|
277
352
|
f"data_models/containers{container.external_id}.container.yaml", container.dump_yaml()
|
|
278
353
|
)
|
|
279
354
|
if "node_types" not in exclude_set:
|
|
280
|
-
for node in self.node_types:
|
|
355
|
+
for node in self.node_types.values():
|
|
281
356
|
zip_ref.writestr(f"data_models/nodes/{node.external_id}.node.yaml", node.dump_yaml())
|
|
282
357
|
|
|
283
358
|
@classmethod
|
|
@@ -308,10 +383,30 @@ class DMSSchema:
|
|
|
308
383
|
loaded: dict[str, Any] = {}
|
|
309
384
|
for attr in fields(cls):
|
|
310
385
|
if items := data_dict.get(attr.name) or data_dict.get(to_camel(attr.name)):
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
386
|
+
if attr.name == "data_model":
|
|
387
|
+
if isinstance(items, list) and len(items) > 1:
|
|
388
|
+
warnings.warn(
|
|
389
|
+
issues.importing.MultipleDataModelsWarning(
|
|
390
|
+
[item.get("externalId", "Unknown") for item in items]
|
|
391
|
+
),
|
|
392
|
+
stacklevel=2,
|
|
393
|
+
)
|
|
394
|
+
item = items[0] if isinstance(items, list) else items
|
|
395
|
+
try:
|
|
396
|
+
loaded[attr.name] = dm.DataModelApply.load(item)
|
|
397
|
+
except Exception as e:
|
|
398
|
+
data_model_file = context.get(attr.name, [Path("UNKNOWN")])[0]
|
|
399
|
+
warnings.warn(
|
|
400
|
+
issues.fileread.FailedLoadWarning(data_model_file, dm.DataModelApply.__name__, str(e)),
|
|
401
|
+
stacklevel=2,
|
|
402
|
+
)
|
|
403
|
+
else:
|
|
404
|
+
try:
|
|
405
|
+
loaded[attr.name] = attr.type.load(items)
|
|
406
|
+
except Exception as e:
|
|
407
|
+
loaded[attr.name] = cls._load_individual_resources(
|
|
408
|
+
items, attr, str(e), context.get(attr.name, [])
|
|
409
|
+
)
|
|
315
410
|
return cls(**loaded)
|
|
316
411
|
|
|
317
412
|
@classmethod
|
|
@@ -353,9 +448,16 @@ class DMSSchema:
|
|
|
353
448
|
cls_fields = sorted(fields(self), key=lambda f: f.name) if sort else fields(self)
|
|
354
449
|
for attr in cls_fields:
|
|
355
450
|
if items := getattr(self, attr.name):
|
|
356
|
-
items = sorted(items, key=self._to_sortable_identifier) if sort else items
|
|
357
451
|
key = to_camel(attr.name) if camel_case else attr.name
|
|
358
|
-
|
|
452
|
+
if isinstance(items, CogniteResourceDict):
|
|
453
|
+
if sort:
|
|
454
|
+
output[key] = [
|
|
455
|
+
item.dump(camel_case) for item in sorted(items.values(), key=self._to_sortable_identifier)
|
|
456
|
+
]
|
|
457
|
+
else:
|
|
458
|
+
output[key] = items.dump(camel_case)
|
|
459
|
+
else:
|
|
460
|
+
output[key] = items.dump(camel_case=camel_case)
|
|
359
461
|
return output
|
|
360
462
|
|
|
361
463
|
@classmethod
|
|
@@ -376,19 +478,19 @@ class DMSSchema:
|
|
|
376
478
|
|
|
377
479
|
def validate(self) -> list[DMSSchemaError]:
|
|
378
480
|
errors: set[DMSSchemaError] = set()
|
|
379
|
-
defined_spaces =
|
|
380
|
-
defined_containers =
|
|
381
|
-
defined_views =
|
|
481
|
+
defined_spaces = self.spaces.copy()
|
|
482
|
+
defined_containers = self.containers.copy()
|
|
483
|
+
defined_views = self.views.copy()
|
|
382
484
|
if self.reference:
|
|
383
|
-
defined_spaces |=
|
|
384
|
-
defined_containers |=
|
|
385
|
-
defined_views |=
|
|
485
|
+
defined_spaces |= self.reference.spaces
|
|
486
|
+
defined_containers |= self.reference.containers
|
|
487
|
+
defined_views |= self.reference.views
|
|
386
488
|
|
|
387
|
-
for container in self.containers:
|
|
489
|
+
for container in self.containers.values():
|
|
388
490
|
if container.space not in defined_spaces:
|
|
389
491
|
errors.add(MissingSpaceError(space=container.space, referred_by=container.as_id()))
|
|
390
492
|
|
|
391
|
-
for view in self.views:
|
|
493
|
+
for view in self.views.values():
|
|
392
494
|
view_id = view.as_id()
|
|
393
495
|
if view.space not in defined_spaces:
|
|
394
496
|
errors.add(MissingSpaceError(space=view.space, referred_by=view_id))
|
|
@@ -458,7 +560,8 @@ class DMSSchema:
|
|
|
458
560
|
)
|
|
459
561
|
)
|
|
460
562
|
|
|
461
|
-
|
|
563
|
+
if self.data_model:
|
|
564
|
+
model = self.data_model
|
|
462
565
|
if model.space not in defined_spaces:
|
|
463
566
|
errors.add(MissingSpaceError(space=model.space, referred_by=model.as_id()))
|
|
464
567
|
|
|
@@ -506,16 +609,27 @@ class DMSSchema:
|
|
|
506
609
|
)
|
|
507
610
|
return None
|
|
508
611
|
|
|
509
|
-
def referenced_spaces(self) -> set[str]:
|
|
510
|
-
|
|
511
|
-
referenced_spaces |= {view.space for view in self.views}
|
|
512
|
-
referenced_spaces |= {container.space for view in self.views for container in view.referenced_containers()}
|
|
513
|
-
referenced_spaces |= {parent.space for view in self.views for parent in view.implements or []}
|
|
514
|
-
referenced_spaces |= {node.space for node in self.node_types}
|
|
515
|
-
referenced_spaces |= {model.space for model in self.data_models}
|
|
516
|
-
referenced_spaces |= {view.space for model in self.data_models for view in model.views or []}
|
|
517
|
-
referenced_spaces |= {s.space for s in self.spaces}
|
|
612
|
+
def referenced_spaces(self, include_indirect_references: bool = True) -> set[str]:
|
|
613
|
+
"""Get the spaces referenced by the schema.
|
|
518
614
|
|
|
615
|
+
Args:
|
|
616
|
+
include_indirect_references (bool): If True, the spaces referenced by as view.implements, and
|
|
617
|
+
view.referenced_containers will be included in the output.
|
|
618
|
+
Returns:
|
|
619
|
+
set[str]: The spaces referenced by the schema.
|
|
620
|
+
"""
|
|
621
|
+
referenced_spaces = {view.space for view in self.views.values()}
|
|
622
|
+
referenced_spaces |= {container.space for container in self.containers.values()}
|
|
623
|
+
if include_indirect_references:
|
|
624
|
+
referenced_spaces |= {
|
|
625
|
+
container.space for view in self.views.values() for container in view.referenced_containers()
|
|
626
|
+
}
|
|
627
|
+
referenced_spaces |= {parent.space for view in self.views.values() for parent in view.implements or []}
|
|
628
|
+
referenced_spaces |= {node.space for node in self.node_types.values()}
|
|
629
|
+
if self.data_model:
|
|
630
|
+
referenced_spaces |= {self.data_model.space}
|
|
631
|
+
referenced_spaces |= {view.space for view in self.data_model.views or []}
|
|
632
|
+
referenced_spaces |= {s.space for s in self.spaces.values()}
|
|
519
633
|
return referenced_spaces
|
|
520
634
|
|
|
521
635
|
|
|
@@ -625,19 +739,19 @@ class PipelineSchema(DMSSchema):
|
|
|
625
739
|
|
|
626
740
|
@classmethod
|
|
627
741
|
def from_dms(cls, schema: DMSSchema, instance_space: str | None = None) -> "PipelineSchema":
|
|
628
|
-
if not schema.
|
|
742
|
+
if not schema.data_model:
|
|
629
743
|
raise ValueError("PipelineSchema must contain at least one data model")
|
|
630
|
-
first_data_model = schema.
|
|
744
|
+
first_data_model = schema.data_model
|
|
631
745
|
# The database name is limited to 32 characters
|
|
632
746
|
database_name = first_data_model.external_id[:32]
|
|
633
747
|
instance_space = instance_space or first_data_model.space
|
|
634
748
|
database = DatabaseWrite(name=database_name)
|
|
635
|
-
parent_views = {parent for view in schema.views for parent in view.implements or []}
|
|
636
|
-
container_by_id =
|
|
749
|
+
parent_views = {parent for view in schema.views.values() for parent in view.implements or []}
|
|
750
|
+
container_by_id = schema.containers.copy()
|
|
637
751
|
|
|
638
752
|
transformations = TransformationWriteList([])
|
|
639
753
|
raw_tables = RawTableWriteList([])
|
|
640
|
-
for view in schema.views:
|
|
754
|
+
for view in schema.views.values():
|
|
641
755
|
if view.as_id() in parent_views:
|
|
642
756
|
# Skipping parents as they do not have their own data
|
|
643
757
|
continue
|
|
@@ -666,7 +780,7 @@ class PipelineSchema(DMSSchema):
|
|
|
666
780
|
|
|
667
781
|
return cls(
|
|
668
782
|
spaces=schema.spaces,
|
|
669
|
-
|
|
783
|
+
data_model=schema.data_model,
|
|
670
784
|
views=schema.views,
|
|
671
785
|
containers=schema.containers,
|
|
672
786
|
transformations=transformations,
|
|
@@ -164,10 +164,10 @@ class DMSPostValidation:
|
|
|
164
164
|
# Everything is allowed
|
|
165
165
|
return None
|
|
166
166
|
# Is an extension of an existing model.
|
|
167
|
-
user_schema = self.rules.as_schema(
|
|
167
|
+
user_schema = self.rules.as_schema()
|
|
168
168
|
ref_schema = self.rules.reference.as_schema()
|
|
169
|
-
new_containers =
|
|
170
|
-
existing_containers =
|
|
169
|
+
new_containers = user_schema.containers.copy()
|
|
170
|
+
existing_containers = ref_schema.containers.copy()
|
|
171
171
|
|
|
172
172
|
for container_id, container in new_containers.items():
|
|
173
173
|
existing_container = existing_containers.get(container_id)
|
|
@@ -193,8 +193,8 @@ class DMSPostValidation:
|
|
|
193
193
|
# Reshape allows changes to views
|
|
194
194
|
return None
|
|
195
195
|
|
|
196
|
-
new_views =
|
|
197
|
-
existing_views =
|
|
196
|
+
new_views = user_schema.views.copy()
|
|
197
|
+
existing_views = ref_schema.views.copy()
|
|
198
198
|
for view_id, view in new_views.items():
|
|
199
199
|
existing_view = existing_views.get(view_id)
|
|
200
200
|
if not existing_view or existing_view == view:
|
|
@@ -219,13 +219,13 @@ class DMSPostValidation:
|
|
|
219
219
|
|
|
220
220
|
dms_schema = self.rules.as_schema()
|
|
221
221
|
|
|
222
|
-
for view in dms_schema.views:
|
|
223
|
-
mapped_containers = dms_schema._get_mapped_container_from_view(
|
|
222
|
+
for view_id, view in dms_schema.views.items():
|
|
223
|
+
mapped_containers = dms_schema._get_mapped_container_from_view(view_id)
|
|
224
224
|
|
|
225
225
|
if mapped_containers and len(mapped_containers) > 10:
|
|
226
226
|
self.issue_list.append(
|
|
227
227
|
issues.dms.ViewMapsToTooManyContainersWarning(
|
|
228
|
-
view_id=
|
|
228
|
+
view_id=view_id,
|
|
229
229
|
container_ids=mapped_containers,
|
|
230
230
|
)
|
|
231
231
|
)
|
|
@@ -236,7 +236,7 @@ class DMSPostValidation:
|
|
|
236
236
|
):
|
|
237
237
|
self.issue_list.append(
|
|
238
238
|
issues.dms.HasDataFilterAppliedToTooManyContainersWarning(
|
|
239
|
-
view_id=
|
|
239
|
+
view_id=view_id,
|
|
240
240
|
container_ids=mapped_containers,
|
|
241
241
|
)
|
|
242
242
|
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
1
3
|
from abc import ABC, abstractmethod
|
|
2
4
|
from collections.abc import Collection
|
|
3
5
|
from functools import total_ordering
|
|
@@ -37,8 +39,19 @@ class WrappedEntity(BaseModel, ABC):
|
|
|
37
39
|
def _parse(cls, data: str) -> dict:
|
|
38
40
|
if data.casefold() == cls.name.casefold():
|
|
39
41
|
return {"inner": None}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
|
|
43
|
+
# raw filter case:
|
|
44
|
+
if cls.__name__ == "RawFilter":
|
|
45
|
+
if match := re.search(r"rawFilter\(([\s\S]*?)\)", data):
|
|
46
|
+
return {"filter": match.group(1), "inner": None}
|
|
47
|
+
else:
|
|
48
|
+
raise ValueError(f"Cannot parse {cls.name} from {data}. Ill formatted raw filter.")
|
|
49
|
+
|
|
50
|
+
# nodeType and hasData case:
|
|
51
|
+
elif inner := data[len(cls.name) :].removeprefix("(").removesuffix(")"):
|
|
52
|
+
return {"inner": [cls._inner_cls.load(entry.strip()) for entry in inner.split(",")]}
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError(f"Cannot parse {cls.name} from {data}")
|
|
42
55
|
|
|
43
56
|
@model_serializer(when_used="unless-none", return_type=str)
|
|
44
57
|
def as_str(self) -> str:
|
|
@@ -164,3 +177,22 @@ class HasDataFilter(DMSFilter):
|
|
|
164
177
|
# Sorting to ensure deterministic order
|
|
165
178
|
containers=sorted(containers, key=lambda container: container.as_tuple()) # type: ignore[union-attr]
|
|
166
179
|
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class RawFilter(DMSFilter):
|
|
183
|
+
name: ClassVar[str] = "rawFilter"
|
|
184
|
+
filter: str
|
|
185
|
+
inner: None = None # type: ignore[assignment]
|
|
186
|
+
|
|
187
|
+
def as_dms_filter(self) -> dm.Filter: # type: ignore[override]
|
|
188
|
+
try:
|
|
189
|
+
return dm.Filter.load(json.loads(self.filter))
|
|
190
|
+
except json.JSONDecodeError as e:
|
|
191
|
+
raise ValueError(f"Error loading raw filter: {e}") from e
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def is_empty(self) -> bool:
|
|
195
|
+
return self.filter is None
|
|
196
|
+
|
|
197
|
+
def __repr__(self) -> str:
|
|
198
|
+
return self.filter
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import (
|
|
3
|
+
ItemsView,
|
|
4
|
+
Iterable,
|
|
5
|
+
Iterator,
|
|
6
|
+
KeysView,
|
|
7
|
+
Mapping,
|
|
8
|
+
MutableMapping,
|
|
9
|
+
ValuesView,
|
|
10
|
+
)
|
|
11
|
+
from typing import Any, TypeVar, cast, final
|
|
12
|
+
|
|
13
|
+
import pandas as pd
|
|
14
|
+
import yaml
|
|
15
|
+
from cognite.client.data_classes._base import T_CogniteResource
|
|
16
|
+
from cognite.client.data_classes.data_modeling import (
|
|
17
|
+
ContainerApply,
|
|
18
|
+
ContainerId,
|
|
19
|
+
DataModelApply,
|
|
20
|
+
DataModelId,
|
|
21
|
+
NodeApply,
|
|
22
|
+
NodeId,
|
|
23
|
+
SpaceApply,
|
|
24
|
+
ViewApply,
|
|
25
|
+
ViewId,
|
|
26
|
+
)
|
|
27
|
+
from cognite.client.utils._auxiliary import load_yaml_or_json
|
|
28
|
+
from cognite.client.utils._pandas_helpers import (
|
|
29
|
+
convert_nullable_int_cols,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
T_ID = TypeVar("T_ID")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Inheriting from dict as we are extending it,
|
|
36
|
+
# ref https://stackoverflow.com/questions/7148419/subclass-dict-userdict-dict-or-abc
|
|
37
|
+
class CogniteResourceDict(dict, MutableMapping[T_ID, T_CogniteResource], ABC):
|
|
38
|
+
"""CogniteResource stored in a mapping structure.
|
|
39
|
+
|
|
40
|
+
The serialization format of the CognitiveResourceDict is a list of dicts, where each dict
|
|
41
|
+
represents a CognitiveResource.
|
|
42
|
+
|
|
43
|
+
This means that the serialization methods .dump() and .load() return a list of dicts and
|
|
44
|
+
expects a list of dicts respectively.
|
|
45
|
+
|
|
46
|
+
In addition, the init method is slightly abused compared to a regular dict by allowing the input to be a
|
|
47
|
+
list of CognitiveResources.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
_RESOURCE: type[T_CogniteResource]
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
items: Iterable[T_CogniteResource]
|
|
55
|
+
| Iterable[tuple[T_ID, T_CogniteResource]]
|
|
56
|
+
| Mapping[T_ID, T_CogniteResource]
|
|
57
|
+
| None = None,
|
|
58
|
+
) -> None:
|
|
59
|
+
if isinstance(items, Mapping):
|
|
60
|
+
super().__init__(items)
|
|
61
|
+
elif isinstance(items, Iterable):
|
|
62
|
+
super().__init__(item if isinstance(item, tuple) else (self._as_id(item), item) for item in items) # type: ignore[arg-type]
|
|
63
|
+
else:
|
|
64
|
+
super().__init__()
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def _as_id(cls, resource: T_CogniteResource) -> T_ID:
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
|
|
71
|
+
def dump(self, camel_case: bool = True) -> list[dict[str, Any]]:
|
|
72
|
+
return [value.dump(camel_case) for value in self.values()]
|
|
73
|
+
|
|
74
|
+
def dump_yaml(self) -> str:
|
|
75
|
+
return yaml.dump(self.dump(camel_case=True), sort_keys=False)
|
|
76
|
+
|
|
77
|
+
def to_pandas(self, camel_case: bool = False) -> pd.DataFrame:
|
|
78
|
+
df = pd.DataFrame(self.dump(camel_case=camel_case))
|
|
79
|
+
df = convert_nullable_int_cols(df)
|
|
80
|
+
return df
|
|
81
|
+
|
|
82
|
+
def _repr_html_(self) -> str:
|
|
83
|
+
# Pretty print the dataframe in Jupyter
|
|
84
|
+
return self.to_pandas()._repr_html_() # type: ignore[operator]
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
@final
|
|
88
|
+
def load(cls: "type[T_CogniteResourceDict]", resource: Iterable[dict[str, Any]] | str) -> "T_CogniteResourceDict":
|
|
89
|
+
"""Load a resource from a YAML/JSON string or iterable of dict."""
|
|
90
|
+
if isinstance(resource, str):
|
|
91
|
+
resource = load_yaml_or_json(resource)
|
|
92
|
+
|
|
93
|
+
if isinstance(resource, Iterable):
|
|
94
|
+
return cls._load(cast(Iterable, resource))
|
|
95
|
+
|
|
96
|
+
raise TypeError(f"Resource must be json or yaml str, or iterable of dicts, not {type(resource)}")
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def _load(
|
|
100
|
+
cls: "type[T_CogniteResourceDict]",
|
|
101
|
+
resource_list: Iterable[dict[str, Any]],
|
|
102
|
+
) -> "T_CogniteResourceDict":
|
|
103
|
+
resources = (cls._RESOURCE._load(resource) for resource in resource_list)
|
|
104
|
+
return cls({cls._as_id(resource): resource for resource in resources}) # type: ignore[abstract]
|
|
105
|
+
|
|
106
|
+
# The below methods are included to make better type hints in the IDE
|
|
107
|
+
def __getitem__(self, k: T_ID) -> T_CogniteResource:
|
|
108
|
+
return super().__getitem__(k)
|
|
109
|
+
|
|
110
|
+
def __setitem__(self, k: T_ID, v: T_CogniteResource) -> None:
|
|
111
|
+
super().__setitem__(k, v)
|
|
112
|
+
|
|
113
|
+
def __delitem__(self, k: T_ID) -> None:
|
|
114
|
+
super().__delitem__(k)
|
|
115
|
+
|
|
116
|
+
def __iter__(self) -> Iterator[T_ID]:
|
|
117
|
+
return super().__iter__()
|
|
118
|
+
|
|
119
|
+
def keys(self) -> KeysView[T_ID]: # type: ignore[override]
|
|
120
|
+
return super().keys()
|
|
121
|
+
|
|
122
|
+
def values(self) -> ValuesView[T_CogniteResource]: # type: ignore[override]
|
|
123
|
+
return super().values()
|
|
124
|
+
|
|
125
|
+
def items(self) -> ItemsView[T_ID, T_CogniteResource]: # type: ignore[override]
|
|
126
|
+
return super().items()
|
|
127
|
+
|
|
128
|
+
def get(self, __key: T_ID, __default: Any = ...) -> T_CogniteResource:
|
|
129
|
+
return super().get(__key, __default)
|
|
130
|
+
|
|
131
|
+
def pop(self, __key: T_ID, __default: Any = ...) -> T_CogniteResource:
|
|
132
|
+
return super().pop(__key, __default)
|
|
133
|
+
|
|
134
|
+
def popitem(self) -> tuple[T_ID, T_CogniteResource]:
|
|
135
|
+
return super().popitem()
|
|
136
|
+
|
|
137
|
+
def copy(self) -> "CogniteResourceDict[T_ID, T_CogniteResource]":
|
|
138
|
+
return cast(CogniteResourceDict[T_ID, T_CogniteResource], super().copy())
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
T_CogniteResourceDict = TypeVar("T_CogniteResourceDict", bound=CogniteResourceDict)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class ViewApplyDict(CogniteResourceDict[ViewId, ViewApply]):
|
|
145
|
+
_RESOURCE = ViewApply
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def _as_id(cls, resource: ViewApply) -> ViewId:
|
|
149
|
+
return resource.as_id()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class SpaceApplyDict(CogniteResourceDict[str, SpaceApply]):
|
|
153
|
+
_RESOURCE = SpaceApply
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
def _as_id(cls, resource: SpaceApply) -> str:
|
|
157
|
+
return resource.space
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ContainerApplyDict(CogniteResourceDict[ContainerId, ContainerApply]):
|
|
161
|
+
_RESOURCE = ContainerApply
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def _as_id(cls, resource: ContainerApply) -> ContainerId:
|
|
165
|
+
return resource.as_id()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class DataModelApplyDict(CogniteResourceDict[DataModelId, DataModelApply]):
|
|
169
|
+
_RESOURCE = DataModelApply
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def _as_id(cls, resource: DataModelApply) -> DataModelId:
|
|
173
|
+
return resource.as_id()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class NodeApplyDict(CogniteResourceDict[NodeId, NodeApply]):
|
|
177
|
+
_RESOURCE = NodeApply
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def _as_id(cls, resource: NodeApply) -> NodeId:
|
|
181
|
+
return resource.as_id()
|
|
@@ -78,9 +78,9 @@ class ValidateRulesAgainstCDF(Step):
|
|
|
78
78
|
f"and {len(retrieved_views)} views from CDF."
|
|
79
79
|
)
|
|
80
80
|
|
|
81
|
-
schema.spaces.
|
|
82
|
-
schema.containers.
|
|
83
|
-
schema.views.
|
|
81
|
+
schema.spaces.update({space.space: space for space in retrieved_spaces})
|
|
82
|
+
schema.containers.update({container.as_id(): container for container in retrieved_containers})
|
|
83
|
+
schema.views.update({view.as_id(): view for view in retrieved_views})
|
|
84
84
|
|
|
85
85
|
errors = schema.validate()
|
|
86
86
|
if errors:
|
|
@@ -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=KAGTP0ql_FPeQE1WIVlTa-PVmeudJNxVGVpT_IPdAEg,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=2U5M6M252swvQPQyooA1EBzFUZNtcTmuSaywfJDgckM,4232
|
|
@@ -165,14 +165,14 @@ cognite/neat/rules/exceptions.py,sha256=YLnsbXXJdDSr_szQoioEtOdqDV8PR7RdQjpMP2SW
|
|
|
165
165
|
cognite/neat/rules/exporters/__init__.py,sha256=Gn3CjkVKHJF9Po1ZPH4wAJ-sRW9up7b2CpXm-eReV3Q,413
|
|
166
166
|
cognite/neat/rules/exporters/_base.py,sha256=m63iw8xjlZbZAxGL8mn7pjGf1pW3rVv8C20_RSiu4t0,1511
|
|
167
167
|
cognite/neat/rules/exporters/_models.py,sha256=vRd0P_YsrZ1eaAGGHfdTeFunaqHdaa0ZtnWiVZBR1nc,1976
|
|
168
|
-
cognite/neat/rules/exporters/_rules2dms.py,sha256=
|
|
168
|
+
cognite/neat/rules/exporters/_rules2dms.py,sha256=US2IO4YTJSF_CDzau1dTpXyeHntJWVSPkMCQoUoKeC0,13688
|
|
169
169
|
cognite/neat/rules/exporters/_rules2excel.py,sha256=K3D_AC6UZ-cG9ZFkqFvuDiMTdBC9ZUW9_IkkY9KsYW0,14934
|
|
170
170
|
cognite/neat/rules/exporters/_rules2ontology.py,sha256=NWS3cn2927LQqW_PdQ-92OLIlmIKGNk7xh5yOMAyj94,20120
|
|
171
171
|
cognite/neat/rules/exporters/_rules2yaml.py,sha256=sOSdnTJ5mXuyAJECdNnNsX6oLvgETptkpgPUQbK0n2w,3026
|
|
172
172
|
cognite/neat/rules/exporters/_validation.py,sha256=OlKIyf4nhSDehJwFHDQ8Zdf6HpNfW7dSe2s67eywHu4,4078
|
|
173
173
|
cognite/neat/rules/importers/__init__.py,sha256=zqNbGpvdVhYkLjWx1i9dJ3FXzYGtuQyTydUYsj-BndQ,408
|
|
174
174
|
cognite/neat/rules/importers/_base.py,sha256=GUiJrYwJ25thI71iS9hCeP_iSZ0Vv8ou3z6MfD07FAk,4274
|
|
175
|
-
cognite/neat/rules/importers/_dms2rules.py,sha256=
|
|
175
|
+
cognite/neat/rules/importers/_dms2rules.py,sha256=UxAatq8WL0rYz4wcopY0wHcBE4JTf--FzflhMxymypY,18274
|
|
176
176
|
cognite/neat/rules/importers/_dtdl2rules/__init__.py,sha256=CNR-sUihs2mnR1bPMKs3j3L4ds3vFTsrl6YycExZTfU,68
|
|
177
177
|
cognite/neat/rules/importers/_dtdl2rules/_unit_lookup.py,sha256=wW4saKva61Q_i17guY0dc4OseJDQfqHy_QZBtm0OD6g,12134
|
|
178
178
|
cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py,sha256=ysmWUxZ0npwrTB0uiH5jA0v37sfCwowGaYk17IyxPUU,12663
|
|
@@ -202,21 +202,22 @@ cognite/neat/rules/models/_types/_field.py,sha256=74WfCSVbTubpK4n4VsysQqCch6VI8I
|
|
|
202
202
|
cognite/neat/rules/models/data_types.py,sha256=lanwkhwG8iHKfjYfia4v2SBTJrMeXOsqaVkVEP2QMXs,6078
|
|
203
203
|
cognite/neat/rules/models/dms/__init__.py,sha256=Wzyqzz2ZIjpUbDg04CMuuIAw-f2A02DayNeqO9R-2Hw,491
|
|
204
204
|
cognite/neat/rules/models/dms/_converter.py,sha256=x3u3jLnkknozoXXoAXXOWFHCsppqUwSvWv9wMOJ2F1Y,5706
|
|
205
|
-
cognite/neat/rules/models/dms/_exporter.py,sha256=
|
|
206
|
-
cognite/neat/rules/models/dms/_rules.py,sha256=
|
|
205
|
+
cognite/neat/rules/models/dms/_exporter.py,sha256=WTokr7gyjrpxW212kZpFfvd7zw09RRVPjAsxe_aizFo,18903
|
|
206
|
+
cognite/neat/rules/models/dms/_rules.py,sha256=ZLdJLdAJGX1l5t1hChEic9TOHdculbWRhiLbUluZRRQ,15568
|
|
207
207
|
cognite/neat/rules/models/dms/_rules_input.py,sha256=qfInh3JYf7XGEghxPOtIj7GY0f5_aVvnYeUBmfGV9mk,13620
|
|
208
|
-
cognite/neat/rules/models/dms/_schema.py,sha256=
|
|
208
|
+
cognite/neat/rules/models/dms/_schema.py,sha256=6lJSLPoZAjFnbfFl6dmAJR0xlAsQKi_Obcxp8Lp_t-0,41973
|
|
209
209
|
cognite/neat/rules/models/dms/_serializer.py,sha256=Zulj__rnaVNtrbGJPkn4dYMfMXWYyRmtNPR2Yb5zYW0,6668
|
|
210
|
-
cognite/neat/rules/models/dms/_validation.py,sha256=
|
|
210
|
+
cognite/neat/rules/models/dms/_validation.py,sha256=rctt6ImSK5-fHhReH-BXbMlYu_EkTRYYE5c_JS-EQgM,14141
|
|
211
211
|
cognite/neat/rules/models/domain.py,sha256=13OhG-XavE5ipU2ICaYaUhz60volkuVfbJrsp0PhaUU,2993
|
|
212
212
|
cognite/neat/rules/models/entities.py,sha256=iBG84Jr1qQ7PvkMJUJzJ1oWApeONb1IACixdJSztUhk,16395
|
|
213
213
|
cognite/neat/rules/models/information/__init__.py,sha256=KvbYxVk38qReGbGTrU_Y3P3Gz6Bfghk5lHSKs8DlTOI,195
|
|
214
214
|
cognite/neat/rules/models/information/_converter.py,sha256=jzaIk7Q2CeU3TIGULEINwUNNyhWu-VdOW646EjH_FrI,7964
|
|
215
215
|
cognite/neat/rules/models/information/_rules.py,sha256=YE7X8MsPQv-AVtl4vYtQW99moT45sYk2dI2DDS1YRO0,15546
|
|
216
|
-
cognite/neat/rules/models/wrapped_entities.py,sha256=
|
|
216
|
+
cognite/neat/rules/models/wrapped_entities.py,sha256=ThhjnNNrpgz0HeORIQ8Q894trxP73P7T_TuZj6qH2CU,7157
|
|
217
217
|
cognite/neat/utils/__init__.py,sha256=l5Nyqhqo25bcQXCOb_lk01cr-UXsG8cczz_y_I0u6bg,68
|
|
218
218
|
cognite/neat/utils/auxiliary.py,sha256=E2-YtddzScvN7l7j0kNYIMlfqIUT9NWMqLpcJYPK4rY,309
|
|
219
219
|
cognite/neat/utils/cdf.py,sha256=dTg8wnm2916yhWT_2Jg9_PlauHCbmnuNgmpqrGU8eO0,711
|
|
220
|
+
cognite/neat/utils/cdf_classes.py,sha256=NEmz5UprBlqfqZnqJkRk5xjSpzazwHbhcWsMH_GNxP8,5831
|
|
220
221
|
cognite/neat/utils/cdf_loaders/__init__.py,sha256=s2aPR5XLo6WZ0ybstAJlcGFYkA7CyHW1XO-NYpL0V6o,483
|
|
221
222
|
cognite/neat/utils/cdf_loaders/_base.py,sha256=j85HINIx4poGD-0dN3JPHTqjOS1XhCxv7G9s1gC1GBo,2057
|
|
222
223
|
cognite/neat/utils/cdf_loaders/_data_modeling.py,sha256=RoIj2cL_j3vP6e4BlCExZU8d96p3dLitU9nsIIyesLc,11336
|
|
@@ -255,7 +256,7 @@ cognite/neat/workflows/steps/lib/current/graph_loader.py,sha256=HfGg1HRZhbV58TFu
|
|
|
255
256
|
cognite/neat/workflows/steps/lib/current/graph_store.py,sha256=r7VTxdaz8jJQU7FJbnRDMxvEYbSAZFNMABhPyfNwiFk,6295
|
|
256
257
|
cognite/neat/workflows/steps/lib/current/rules_exporter.py,sha256=wUQAZXWBCqWXe0241QSREtnNTii_tSmOkeiSPwNQRjk,22898
|
|
257
258
|
cognite/neat/workflows/steps/lib/current/rules_importer.py,sha256=yDq06cvxLvEpSnTXTjwhxDie_MzHa3wO1A4cbKnrH6c,10338
|
|
258
|
-
cognite/neat/workflows/steps/lib/current/rules_validator.py,sha256=
|
|
259
|
+
cognite/neat/workflows/steps/lib/current/rules_validator.py,sha256=LwF9lXlnuPOxDSsOMMTHBi2BHc5rk08IF5zahS9yQuw,4844
|
|
259
260
|
cognite/neat/workflows/steps/lib/io/__init__.py,sha256=k7IPbIq3ey19oRc5sA_15F99-O6dxzqbm1LihGRRo5A,32
|
|
260
261
|
cognite/neat/workflows/steps/lib/io/io_steps.py,sha256=QAGypoi1vP32BRiIgBZ0B4qsbFMcwhzpRiVUUnWysLA,16874
|
|
261
262
|
cognite/neat/workflows/steps/lib/legacy/__init__.py,sha256=725aFzVqhE0tbVOAW70zWXTGKFiYImVupRZ4C5_IkUo,274
|
|
@@ -271,8 +272,8 @@ cognite/neat/workflows/steps_registry.py,sha256=fkTX14ZA7_gkUYfWIlx7A1XbCidvqR23
|
|
|
271
272
|
cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
|
|
272
273
|
cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
|
|
273
274
|
cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
|
|
274
|
-
cognite_neat-0.
|
|
275
|
-
cognite_neat-0.
|
|
276
|
-
cognite_neat-0.
|
|
277
|
-
cognite_neat-0.
|
|
278
|
-
cognite_neat-0.
|
|
275
|
+
cognite_neat-0.77.1.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
276
|
+
cognite_neat-0.77.1.dist-info/METADATA,sha256=CXlHgF0btEegP5yLRqUsR40q8fIh3zaMU9nAefHikpQ,9316
|
|
277
|
+
cognite_neat-0.77.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
278
|
+
cognite_neat-0.77.1.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
|
|
279
|
+
cognite_neat-0.77.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|