cognite-neat 0.88.2__py3-none-any.whl → 0.89.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_version.py +1 -1
- cognite/neat/constants.py +3 -0
- cognite/neat/graph/__init__.py +0 -3
- cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
- cognite/neat/graph/loaders/_base.py +3 -3
- cognite/neat/graph/loaders/_rdf2asset.py +24 -25
- cognite/neat/graph/loaders/_rdf2dms.py +20 -15
- cognite/neat/issues/__init__.py +1 -3
- cognite/neat/issues/_base.py +261 -71
- cognite/neat/issues/errors/__init__.py +73 -0
- cognite/neat/issues/errors/_external.py +67 -0
- cognite/neat/issues/errors/_general.py +35 -0
- cognite/neat/issues/errors/_properties.py +62 -0
- cognite/neat/issues/errors/_resources.py +111 -0
- cognite/neat/issues/errors/_workflow.py +36 -0
- cognite/neat/issues/formatters.py +1 -1
- cognite/neat/issues/warnings/__init__.py +66 -0
- cognite/neat/issues/warnings/_external.py +40 -0
- cognite/neat/issues/warnings/_general.py +29 -0
- cognite/neat/issues/warnings/_models.py +92 -0
- cognite/neat/issues/warnings/_properties.py +44 -0
- cognite/neat/issues/warnings/_resources.py +55 -0
- cognite/neat/issues/warnings/user_modeling.py +113 -0
- cognite/neat/rules/_shared.py +53 -2
- cognite/neat/rules/analysis/_base.py +1 -1
- cognite/neat/rules/exporters/_base.py +7 -18
- cognite/neat/rules/exporters/_rules2dms.py +17 -20
- cognite/neat/rules/exporters/_rules2excel.py +9 -16
- cognite/neat/rules/exporters/_rules2ontology.py +77 -64
- cognite/neat/rules/exporters/_rules2yaml.py +6 -9
- cognite/neat/rules/exporters/_validation.py +11 -96
- cognite/neat/rules/importers/_base.py +9 -58
- cognite/neat/rules/importers/_dms2rules.py +188 -135
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +48 -35
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +36 -45
- cognite/neat/rules/importers/_dtdl2rules/spec.py +7 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +8 -4
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +3 -3
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +18 -11
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +12 -19
- cognite/neat/rules/importers/_rdf/_inference2rules.py +14 -37
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
- cognite/neat/rules/importers/_rdf/_shared.py +4 -4
- cognite/neat/rules/importers/_spreadsheet2rules.py +46 -97
- cognite/neat/rules/importers/_yaml2rules.py +32 -58
- cognite/neat/rules/models/__init__.py +21 -5
- cognite/neat/rules/models/_base_input.py +162 -0
- cognite/neat/rules/models/{_base.py → _base_rules.py} +1 -12
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/{_types/_field.py → _types.py} +5 -10
- cognite/neat/rules/models/asset/__init__.py +5 -2
- cognite/neat/rules/models/asset/_rules.py +3 -23
- cognite/neat/rules/models/asset/_rules_input.py +40 -115
- cognite/neat/rules/models/asset/_validation.py +14 -10
- cognite/neat/rules/models/data_types.py +150 -44
- cognite/neat/rules/models/dms/__init__.py +19 -7
- cognite/neat/rules/models/dms/_exporter.py +102 -34
- cognite/neat/rules/models/dms/_rules.py +65 -162
- cognite/neat/rules/models/dms/_rules_input.py +186 -254
- cognite/neat/rules/models/dms/_schema.py +87 -78
- cognite/neat/rules/models/dms/_serializer.py +44 -3
- cognite/neat/rules/models/dms/_validation.py +106 -68
- cognite/neat/rules/models/domain.py +52 -1
- cognite/neat/rules/models/entities/__init__.py +63 -0
- cognite/neat/rules/models/entities/_constants.py +73 -0
- cognite/neat/rules/models/entities/_loaders.py +76 -0
- cognite/neat/rules/models/entities/_multi_value.py +67 -0
- cognite/neat/rules/models/{entities.py → entities/_single_value.py} +74 -232
- cognite/neat/rules/models/entities/_types.py +86 -0
- cognite/neat/rules/models/{wrapped_entities.py → entities/_wrapped.py} +1 -1
- cognite/neat/rules/models/information/__init__.py +10 -2
- cognite/neat/rules/models/information/_rules.py +10 -22
- cognite/neat/rules/models/information/_rules_input.py +57 -204
- cognite/neat/rules/models/information/_validation.py +48 -25
- cognite/neat/rules/transformers/__init__.py +21 -0
- cognite/neat/rules/transformers/_base.py +81 -0
- cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +217 -21
- cognite/neat/rules/transformers/_map_onto.py +97 -0
- cognite/neat/rules/transformers/_pipelines.py +61 -0
- cognite/neat/rules/transformers/_verification.py +136 -0
- cognite/neat/{graph/stores → store}/_provenance.py +10 -1
- cognite/neat/utils/auxiliary.py +2 -35
- cognite/neat/utils/cdf/data_classes.py +20 -0
- cognite/neat/utils/regex_patterns.py +6 -0
- cognite/neat/utils/text.py +17 -0
- cognite/neat/workflows/base.py +4 -4
- cognite/neat/workflows/cdf_store.py +3 -3
- cognite/neat/workflows/steps/data_contracts.py +1 -1
- cognite/neat/workflows/steps/lib/current/graph_extractor.py +3 -3
- cognite/neat/workflows/steps/lib/current/graph_loader.py +2 -2
- cognite/neat/workflows/steps/lib/current/graph_store.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_exporter.py +116 -47
- cognite/neat/workflows/steps/lib/current/rules_importer.py +30 -28
- cognite/neat/workflows/steps/lib/current/rules_validator.py +5 -6
- cognite/neat/workflows/steps/lib/io/io_steps.py +5 -5
- cognite/neat/workflows/steps_registry.py +4 -5
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +105 -106
- cognite/neat/exceptions.py +0 -145
- cognite/neat/graph/exceptions.py +0 -90
- cognite/neat/issues/errors/external.py +0 -21
- cognite/neat/issues/errors/properties.py +0 -75
- cognite/neat/issues/errors/resources.py +0 -123
- cognite/neat/issues/errors/schema.py +0 -0
- cognite/neat/issues/neat_warnings/__init__.py +0 -2
- cognite/neat/issues/neat_warnings/identifier.py +0 -27
- cognite/neat/issues/neat_warnings/models.py +0 -22
- cognite/neat/issues/neat_warnings/properties.py +0 -77
- cognite/neat/issues/neat_warnings/resources.py +0 -125
- cognite/neat/rules/issues/__init__.py +0 -22
- cognite/neat/rules/issues/base.py +0 -63
- cognite/neat/rules/issues/dms.py +0 -549
- cognite/neat/rules/issues/fileread.py +0 -197
- cognite/neat/rules/issues/ontology.py +0 -298
- cognite/neat/rules/issues/spreadsheet.py +0 -563
- cognite/neat/rules/issues/spreadsheet_file.py +0 -151
- cognite/neat/rules/issues/tables.py +0 -72
- cognite/neat/rules/models/_constants.py +0 -1
- cognite/neat/rules/models/_types/__init__.py +0 -19
- cognite/neat/rules/models/asset/_converter.py +0 -4
- cognite/neat/rules/models/dms/_converter.py +0 -145
- cognite/neat/workflows/_exceptions.py +0 -41
- /cognite/neat/{graph/stores → store}/__init__.py +0 -0
- /cognite/neat/{graph/stores → store}/_base.py +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from collections import Counter
|
|
2
|
-
from collections.abc import Sequence
|
|
2
|
+
from collections.abc import Collection, Sequence
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Literal, cast
|
|
6
6
|
|
|
7
7
|
from cognite.client import CogniteClient
|
|
8
8
|
from cognite.client import data_modeling as dm
|
|
9
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
|
-
from cognite.client.data_classes.data_modeling.data_types import
|
|
11
|
+
from cognite.client.data_classes.data_modeling.data_types import Enum as DMSEnum
|
|
12
|
+
from cognite.client.data_classes.data_modeling.data_types import ListablePropertyType, PropertyTypeWithUnit
|
|
12
13
|
from cognite.client.data_classes.data_modeling.views import (
|
|
13
14
|
MultiEdgeConnectionApply,
|
|
14
15
|
MultiReverseDirectRelationApply,
|
|
@@ -19,40 +20,42 @@ from cognite.client.data_classes.data_modeling.views import (
|
|
|
19
20
|
from cognite.client.utils import ms_to_datetime
|
|
20
21
|
|
|
21
22
|
from cognite.neat.issues import IssueList, NeatIssue
|
|
22
|
-
from cognite.neat.issues.errors
|
|
23
|
-
from cognite.neat.issues.
|
|
23
|
+
from cognite.neat.issues.errors import FileTypeUnexpectedError, ResourceMissingIdentifierError, ResourceRetrievalError
|
|
24
|
+
from cognite.neat.issues.warnings import (
|
|
25
|
+
PropertyNotFoundWarning,
|
|
24
26
|
PropertyTypeNotSupportedWarning,
|
|
25
|
-
|
|
27
|
+
ResourceNotFoundWarning,
|
|
28
|
+
ResourcesDuplicatedWarning,
|
|
26
29
|
)
|
|
27
|
-
from cognite.neat.
|
|
28
|
-
from cognite.neat.rules import
|
|
29
|
-
from cognite.neat.rules.importers._base import BaseImporter, Rules, _handle_issues
|
|
30
|
+
from cognite.neat.rules._shared import ReadRules
|
|
31
|
+
from cognite.neat.rules.importers._base import BaseImporter, _handle_issues
|
|
30
32
|
from cognite.neat.rules.models import (
|
|
31
33
|
DataModelType,
|
|
32
|
-
|
|
34
|
+
DMSInputRules,
|
|
33
35
|
DMSSchema,
|
|
34
|
-
ExtensionCategory,
|
|
35
|
-
RoleTypes,
|
|
36
36
|
SchemaCompleteness,
|
|
37
|
-
SheetList,
|
|
38
37
|
)
|
|
39
|
-
from cognite.neat.rules.models.data_types import DataType
|
|
38
|
+
from cognite.neat.rules.models.data_types import DataType, Enum
|
|
40
39
|
from cognite.neat.rules.models.dms import (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
DMSInputContainer,
|
|
41
|
+
DMSInputEnum,
|
|
42
|
+
DMSInputMetadata,
|
|
43
|
+
DMSInputNode,
|
|
44
|
+
DMSInputProperty,
|
|
45
|
+
DMSInputView,
|
|
45
46
|
)
|
|
46
47
|
from cognite.neat.rules.models.entities import (
|
|
47
48
|
ClassEntity,
|
|
48
49
|
ContainerEntity,
|
|
50
|
+
DMSNodeEntity,
|
|
49
51
|
DMSUnknownEntity,
|
|
52
|
+
EdgeEntity,
|
|
53
|
+
ReverseConnectionEntity,
|
|
50
54
|
ViewEntity,
|
|
51
|
-
ViewPropertyEntity,
|
|
52
55
|
)
|
|
53
56
|
|
|
54
57
|
|
|
55
|
-
class DMSImporter(BaseImporter):
|
|
58
|
+
class DMSImporter(BaseImporter[DMSInputRules]):
|
|
56
59
|
"""Imports a Data Model from Cognite Data Fusion.
|
|
57
60
|
|
|
58
61
|
Args:
|
|
@@ -67,8 +70,8 @@ class DMSImporter(BaseImporter):
|
|
|
67
70
|
self,
|
|
68
71
|
schema: DMSSchema,
|
|
69
72
|
read_issues: Sequence[NeatIssue] | None = None,
|
|
70
|
-
metadata:
|
|
71
|
-
ref_metadata:
|
|
73
|
+
metadata: DMSInputMetadata | None = None,
|
|
74
|
+
ref_metadata: DMSInputMetadata | None = None,
|
|
72
75
|
):
|
|
73
76
|
# Calling this root schema to distinguish it from
|
|
74
77
|
# * User Schema
|
|
@@ -78,10 +81,10 @@ class DMSImporter(BaseImporter):
|
|
|
78
81
|
self.ref_metadata = ref_metadata
|
|
79
82
|
self.issue_list = IssueList(read_issues)
|
|
80
83
|
self._all_containers_by_id = schema.containers.copy()
|
|
81
|
-
self.
|
|
82
|
-
if
|
|
83
|
-
self._all_containers_by_id.update(
|
|
84
|
-
self.
|
|
84
|
+
self._all_views_by_id = schema.views.copy()
|
|
85
|
+
if schema.reference:
|
|
86
|
+
self._all_containers_by_id.update(schema.reference.containers.items())
|
|
87
|
+
self._all_views_by_id.update(schema.reference.views.items())
|
|
85
88
|
|
|
86
89
|
@classmethod
|
|
87
90
|
def from_data_model_id(
|
|
@@ -109,9 +112,9 @@ class DMSImporter(BaseImporter):
|
|
|
109
112
|
return cls(
|
|
110
113
|
DMSSchema(),
|
|
111
114
|
[
|
|
112
|
-
|
|
115
|
+
ResourceRetrievalError(
|
|
113
116
|
dm.DataModelId.load(reference_model_id), # type: ignore[arg-type]
|
|
114
|
-
"
|
|
117
|
+
"data model",
|
|
115
118
|
"Data Model is missing in CDF",
|
|
116
119
|
)
|
|
117
120
|
],
|
|
@@ -124,8 +127,8 @@ class DMSImporter(BaseImporter):
|
|
|
124
127
|
return cls(
|
|
125
128
|
DMSSchema(),
|
|
126
129
|
[
|
|
127
|
-
|
|
128
|
-
dm.DataModelId.load(reference_model_id), "
|
|
130
|
+
ResourceRetrievalError(
|
|
131
|
+
dm.DataModelId.load(reference_model_id), "data model", "Data Model is missing in CDF"
|
|
129
132
|
)
|
|
130
133
|
],
|
|
131
134
|
)
|
|
@@ -163,8 +166,8 @@ class DMSImporter(BaseImporter):
|
|
|
163
166
|
cls,
|
|
164
167
|
model: dm.DataModel[dm.View] | dm.DataModelApply,
|
|
165
168
|
has_reference: bool = False,
|
|
166
|
-
) ->
|
|
167
|
-
description, creator =
|
|
169
|
+
) -> DMSInputMetadata:
|
|
170
|
+
description, creator = DMSInputMetadata._get_description_and_creator(model.description)
|
|
168
171
|
|
|
169
172
|
if isinstance(model, dm.DataModel):
|
|
170
173
|
created = ms_to_datetime(model.created_time)
|
|
@@ -173,17 +176,17 @@ class DMSImporter(BaseImporter):
|
|
|
173
176
|
now = datetime.now().replace(microsecond=0)
|
|
174
177
|
created = now
|
|
175
178
|
updated = now
|
|
176
|
-
return
|
|
177
|
-
schema_=
|
|
178
|
-
data_model_type=
|
|
179
|
-
extension=
|
|
179
|
+
return DMSInputMetadata(
|
|
180
|
+
schema_="complete",
|
|
181
|
+
data_model_type="solution" if has_reference else "enterprise",
|
|
182
|
+
extension="addition",
|
|
180
183
|
space=model.space,
|
|
181
184
|
external_id=model.external_id,
|
|
182
185
|
name=model.name or model.external_id,
|
|
183
186
|
version=model.version or "0.1.0",
|
|
184
187
|
updated=updated,
|
|
185
188
|
created=created,
|
|
186
|
-
creator=creator,
|
|
189
|
+
creator=",".join(creator),
|
|
187
190
|
description=description,
|
|
188
191
|
)
|
|
189
192
|
|
|
@@ -198,77 +201,58 @@ class DMSImporter(BaseImporter):
|
|
|
198
201
|
@classmethod
|
|
199
202
|
def from_zip_file(cls, zip_file: str | Path) -> "DMSImporter":
|
|
200
203
|
if Path(zip_file).suffix != ".zip":
|
|
201
|
-
return cls(DMSSchema(), [
|
|
204
|
+
return cls(DMSSchema(), [FileTypeUnexpectedError(Path(zip_file), frozenset([".zip"]))])
|
|
202
205
|
issue_list = IssueList()
|
|
203
206
|
with _handle_issues(issue_list) as _:
|
|
204
207
|
schema = DMSSchema.from_zip(zip_file)
|
|
205
208
|
return cls(schema, issue_list)
|
|
206
209
|
|
|
207
|
-
|
|
208
|
-
def to_rules(self, errors: Literal["raise"], role: RoleTypes | None = None) -> Rules: ...
|
|
209
|
-
|
|
210
|
-
@overload
|
|
211
|
-
def to_rules(
|
|
212
|
-
self, errors: Literal["continue"] = "continue", role: RoleTypes | None = None
|
|
213
|
-
) -> tuple[Rules | None, IssueList]: ...
|
|
214
|
-
|
|
215
|
-
def to_rules(
|
|
216
|
-
self, errors: Literal["raise", "continue"] = "continue", role: RoleTypes | None = None
|
|
217
|
-
) -> tuple[Rules | None, IssueList] | Rules:
|
|
210
|
+
def to_rules(self) -> ReadRules[DMSInputRules]:
|
|
218
211
|
if self.issue_list.has_errors:
|
|
219
212
|
# In case there were errors during the import, the to_rules method will return None
|
|
220
|
-
return
|
|
213
|
+
return ReadRules(None, self.issue_list, {})
|
|
221
214
|
|
|
222
215
|
if not self.root_schema.data_model:
|
|
223
|
-
self.issue_list.append(
|
|
224
|
-
return
|
|
216
|
+
self.issue_list.append(ResourceMissingIdentifierError("data model", type(self.root_schema).__name__))
|
|
217
|
+
return ReadRules(None, self.issue_list, {})
|
|
218
|
+
|
|
225
219
|
model = self.root_schema.data_model
|
|
226
|
-
with _handle_issues(
|
|
227
|
-
self.issue_list,
|
|
228
|
-
) as future:
|
|
229
|
-
schema_completeness = SchemaCompleteness.complete
|
|
230
|
-
data_model_type = DataModelType.enterprise
|
|
231
|
-
reference: DMSRules | None = None
|
|
232
|
-
if (ref_schema := self.root_schema.reference) and (ref_model := ref_schema.data_model):
|
|
233
|
-
# Reference should always be an enterprise model.
|
|
234
|
-
reference = DMSRules(
|
|
235
|
-
**self._create_rule_components(
|
|
236
|
-
ref_model,
|
|
237
|
-
ref_schema,
|
|
238
|
-
self.ref_metadata
|
|
239
|
-
or self._create_default_metadata(list(ref_schema.views.values()), is_ref=True),
|
|
240
|
-
DataModelType.enterprise,
|
|
241
|
-
)
|
|
242
|
-
)
|
|
243
|
-
data_model_type = DataModelType.solution
|
|
244
|
-
|
|
245
|
-
user_rules = DMSRules(
|
|
246
|
-
**self._create_rule_components(
|
|
247
|
-
model,
|
|
248
|
-
self.root_schema,
|
|
249
|
-
self.metadata,
|
|
250
|
-
data_model_type,
|
|
251
|
-
schema_completeness,
|
|
252
|
-
has_reference=reference is not None,
|
|
253
|
-
),
|
|
254
|
-
reference=reference,
|
|
255
|
-
)
|
|
256
220
|
|
|
257
|
-
|
|
258
|
-
|
|
221
|
+
schema_completeness = SchemaCompleteness.complete
|
|
222
|
+
data_model_type = DataModelType.enterprise
|
|
223
|
+
reference: DMSInputRules | None = None
|
|
224
|
+
if (ref_schema := self.root_schema.reference) and (ref_model := ref_schema.data_model):
|
|
225
|
+
# Reference should always be an enterprise model.
|
|
226
|
+
reference = self._create_rule_components(
|
|
227
|
+
ref_model,
|
|
228
|
+
ref_schema,
|
|
229
|
+
self.ref_metadata or self._create_default_metadata(list(ref_schema.views.values()), is_ref=True),
|
|
230
|
+
DataModelType.enterprise,
|
|
231
|
+
)
|
|
232
|
+
data_model_type = DataModelType.solution
|
|
233
|
+
|
|
234
|
+
user_rules = self._create_rule_components(
|
|
235
|
+
model,
|
|
236
|
+
self.root_schema,
|
|
237
|
+
self.metadata,
|
|
238
|
+
data_model_type,
|
|
239
|
+
schema_completeness,
|
|
240
|
+
has_reference=reference is not None,
|
|
241
|
+
)
|
|
242
|
+
user_rules.reference = reference
|
|
259
243
|
|
|
260
|
-
return
|
|
244
|
+
return ReadRules(user_rules, self.issue_list, {})
|
|
261
245
|
|
|
262
246
|
def _create_rule_components(
|
|
263
247
|
self,
|
|
264
248
|
data_model: dm.DataModelApply,
|
|
265
249
|
schema: DMSSchema,
|
|
266
|
-
metadata:
|
|
250
|
+
metadata: DMSInputMetadata | None = None,
|
|
267
251
|
data_model_type: DataModelType | None = None,
|
|
268
252
|
schema_completeness: SchemaCompleteness | None = None,
|
|
269
253
|
has_reference: bool = False,
|
|
270
|
-
) ->
|
|
271
|
-
properties =
|
|
254
|
+
) -> DMSInputRules:
|
|
255
|
+
properties: list[DMSInputProperty] = []
|
|
272
256
|
for view_id, view in schema.views.items():
|
|
273
257
|
view_entity = ViewEntity.from_id(view_id)
|
|
274
258
|
class_entity = view_entity.as_class()
|
|
@@ -281,51 +265,54 @@ class DMSImporter(BaseImporter):
|
|
|
281
265
|
view.as_id() if isinstance(view, dm.View | dm.ViewApply) else view for view in data_model.views or []
|
|
282
266
|
}
|
|
283
267
|
|
|
284
|
-
metadata = metadata or
|
|
268
|
+
metadata = metadata or DMSInputMetadata.from_data_model(data_model, has_reference)
|
|
285
269
|
if data_model_type is not None:
|
|
286
|
-
metadata.data_model_type = data_model_type
|
|
270
|
+
metadata.data_model_type = str(data_model_type) # type: ignore[assignment]
|
|
287
271
|
if schema_completeness is not None:
|
|
288
|
-
metadata.schema_ = schema_completeness
|
|
289
|
-
|
|
272
|
+
metadata.schema_ = str(schema_completeness) # type: ignore[assignment]
|
|
273
|
+
|
|
274
|
+
enum = self._create_enum_collections(schema.containers.values())
|
|
275
|
+
|
|
276
|
+
return DMSInputRules(
|
|
290
277
|
metadata=metadata,
|
|
291
278
|
properties=properties,
|
|
292
|
-
containers=
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
]
|
|
300
|
-
),
|
|
279
|
+
containers=[DMSInputContainer.from_container(container) for container in schema.containers.values()],
|
|
280
|
+
views=[
|
|
281
|
+
DMSInputView.from_view(view, in_model=view_id in data_model_view_ids)
|
|
282
|
+
for view_id, view in schema.views.items()
|
|
283
|
+
],
|
|
284
|
+
nodes=[DMSInputNode.from_node_type(node_type) for node_type in schema.node_types.values()],
|
|
285
|
+
enum=enum,
|
|
301
286
|
)
|
|
302
287
|
|
|
303
288
|
@classmethod
|
|
304
|
-
def _create_default_metadata(
|
|
289
|
+
def _create_default_metadata(
|
|
290
|
+
cls, views: Sequence[dm.View | dm.ViewApply], is_ref: bool = False
|
|
291
|
+
) -> DMSInputMetadata:
|
|
305
292
|
now = datetime.now().replace(microsecond=0)
|
|
306
293
|
space = Counter(view.space for view in views).most_common(1)[0][0]
|
|
307
|
-
return
|
|
308
|
-
schema_=
|
|
309
|
-
extension=
|
|
310
|
-
data_model_type=
|
|
294
|
+
return DMSInputMetadata(
|
|
295
|
+
schema_="complete",
|
|
296
|
+
extension="addition",
|
|
297
|
+
data_model_type="enterprise" if is_ref else "solution",
|
|
311
298
|
space=space,
|
|
312
299
|
external_id="Unknown",
|
|
313
300
|
version="0.1.0",
|
|
314
|
-
creator=
|
|
301
|
+
creator="Unknown",
|
|
315
302
|
created=now,
|
|
316
303
|
updated=now,
|
|
317
304
|
)
|
|
318
305
|
|
|
319
306
|
def _create_dms_property(
|
|
320
307
|
self, prop_id: str, prop: ViewPropertyApply, view_entity: ViewEntity, class_entity: ClassEntity
|
|
321
|
-
) ->
|
|
308
|
+
) -> DMSInputProperty | None:
|
|
322
309
|
if isinstance(prop, dm.MappedPropertyApply) and prop.container not in self._all_containers_by_id:
|
|
323
310
|
self.issue_list.append(
|
|
324
|
-
|
|
311
|
+
ResourceNotFoundWarning[dm.ContainerId, dm.PropertyId](
|
|
325
312
|
dm.ContainerId.load(prop.container),
|
|
326
|
-
"
|
|
313
|
+
"container",
|
|
327
314
|
view_entity.to_property_id(prop_id),
|
|
328
|
-
"
|
|
315
|
+
"view property",
|
|
329
316
|
)
|
|
330
317
|
)
|
|
331
318
|
return None
|
|
@@ -334,9 +321,7 @@ class DMSImporter(BaseImporter):
|
|
|
334
321
|
and prop.container_property_identifier not in self._all_containers_by_id[prop.container].properties
|
|
335
322
|
):
|
|
336
323
|
self.issue_list.append(
|
|
337
|
-
|
|
338
|
-
prop.container, "Container", view_entity.as_id(), "View", prop_id
|
|
339
|
-
),
|
|
324
|
+
PropertyNotFoundWarning(prop.container, "container", prop_id, view_entity.as_id(), "view"),
|
|
340
325
|
)
|
|
341
326
|
return None
|
|
342
327
|
if not isinstance(
|
|
@@ -348,7 +333,7 @@ class DMSImporter(BaseImporter):
|
|
|
348
333
|
| MultiReverseDirectRelationApply,
|
|
349
334
|
):
|
|
350
335
|
self.issue_list.append(
|
|
351
|
-
PropertyTypeNotSupportedWarning[dm.ViewId](view_entity.as_id(), "
|
|
336
|
+
PropertyTypeNotSupportedWarning[dm.ViewId](view_entity.as_id(), "view", prop_id, type(prop).__name__)
|
|
352
337
|
)
|
|
353
338
|
return None
|
|
354
339
|
|
|
@@ -356,20 +341,22 @@ class DMSImporter(BaseImporter):
|
|
|
356
341
|
if value_type is None:
|
|
357
342
|
return None
|
|
358
343
|
|
|
359
|
-
return
|
|
360
|
-
class_=class_entity,
|
|
344
|
+
return DMSInputProperty(
|
|
345
|
+
class_=str(class_entity),
|
|
361
346
|
property_=prop_id,
|
|
362
347
|
description=prop.description,
|
|
363
348
|
name=prop.name,
|
|
364
|
-
connection=self.
|
|
365
|
-
value_type=value_type,
|
|
349
|
+
connection=self._get_connection_type(prop_id, prop, view_entity.as_id()),
|
|
350
|
+
value_type=str(value_type),
|
|
366
351
|
is_list=self._get_is_list(prop),
|
|
367
352
|
nullable=self._get_nullable(prop),
|
|
368
353
|
immutable=self._get_immutable(prop),
|
|
369
354
|
default=self._get_default(prop),
|
|
370
|
-
container=ContainerEntity.from_id(prop.container)
|
|
355
|
+
container=str(ContainerEntity.from_id(prop.container))
|
|
356
|
+
if isinstance(prop, dm.MappedPropertyApply)
|
|
357
|
+
else None,
|
|
371
358
|
container_property=prop.container_property_identifier if isinstance(prop, dm.MappedPropertyApply) else None,
|
|
372
|
-
view=view_entity,
|
|
359
|
+
view=str(view_entity),
|
|
373
360
|
view_property=prop_id,
|
|
374
361
|
index=self._get_index(prop, prop_id),
|
|
375
362
|
constraint=self._get_constraint(prop, prop_id),
|
|
@@ -379,13 +366,22 @@ class DMSImporter(BaseImporter):
|
|
|
379
366
|
"""This method assumes you have already checked that the container with property exists."""
|
|
380
367
|
return self._all_containers_by_id[prop.container].properties[prop.container_property_identifier]
|
|
381
368
|
|
|
382
|
-
def
|
|
369
|
+
def _get_connection_type(
|
|
370
|
+
self, prop_id: str, prop: ViewPropertyApply, view_id: dm.ViewId
|
|
371
|
+
) -> Literal["direct"] | ReverseConnectionEntity | EdgeEntity | None:
|
|
383
372
|
if isinstance(prop, SingleEdgeConnectionApply | MultiEdgeConnectionApply) and prop.direction == "outwards":
|
|
384
|
-
|
|
373
|
+
properties = ViewEntity.from_id(prop.edge_source) if prop.edge_source is not None else None
|
|
374
|
+
return EdgeEntity(properties=properties, type=DMSNodeEntity.from_reference(prop.type), direction="outwards")
|
|
385
375
|
elif isinstance(prop, SingleEdgeConnectionApply | MultiEdgeConnectionApply) and prop.direction == "inwards":
|
|
386
|
-
|
|
376
|
+
if reverse_prop := self._find_reverse_edge(prop_id, prop, view_id):
|
|
377
|
+
return ReverseConnectionEntity(property=reverse_prop)
|
|
378
|
+
else:
|
|
379
|
+
properties = ViewEntity.from_id(prop.source) if prop.edge_source is not None else None
|
|
380
|
+
return EdgeEntity(
|
|
381
|
+
properties=properties, type=DMSNodeEntity.from_reference(prop.type), direction="inwards"
|
|
382
|
+
)
|
|
387
383
|
elif isinstance(prop, SingleReverseDirectRelationApply | MultiReverseDirectRelationApply):
|
|
388
|
-
return
|
|
384
|
+
return ReverseConnectionEntity(property=prop.through.property)
|
|
389
385
|
elif isinstance(prop, dm.MappedPropertyApply) and isinstance(
|
|
390
386
|
self._container_prop_unsafe(prop).type, dm.DirectRelation
|
|
391
387
|
):
|
|
@@ -395,26 +391,31 @@ class DMSImporter(BaseImporter):
|
|
|
395
391
|
|
|
396
392
|
def _get_value_type(
|
|
397
393
|
self, prop: ViewPropertyApply, view_entity: ViewEntity, prop_id
|
|
398
|
-
) -> DataType | ViewEntity |
|
|
399
|
-
if isinstance(
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
394
|
+
) -> DataType | ViewEntity | DMSUnknownEntity | None:
|
|
395
|
+
if isinstance(
|
|
396
|
+
prop,
|
|
397
|
+
SingleEdgeConnectionApply
|
|
398
|
+
| MultiEdgeConnectionApply
|
|
399
|
+
| SingleReverseDirectRelationApply
|
|
400
|
+
| MultiReverseDirectRelationApply,
|
|
401
|
+
):
|
|
404
402
|
return ViewEntity.from_id(prop.source)
|
|
405
403
|
elif isinstance(prop, dm.MappedPropertyApply):
|
|
406
404
|
container_prop = self._container_prop_unsafe(cast(dm.MappedPropertyApply, prop))
|
|
407
405
|
if isinstance(container_prop.type, dm.DirectRelation):
|
|
408
|
-
if prop.source is None or prop.source not in self.
|
|
409
|
-
# The warning is issued when the DMS Rules are created.
|
|
406
|
+
if prop.source is None or prop.source not in self._all_views_by_id:
|
|
410
407
|
return DMSUnknownEntity()
|
|
411
408
|
else:
|
|
412
409
|
return ViewEntity.from_id(prop.source)
|
|
410
|
+
elif isinstance(container_prop.type, PropertyTypeWithUnit) and container_prop.type.unit:
|
|
411
|
+
return DataType.load(f"{container_prop.type._type}(unit={container_prop.type.unit.external_id})")
|
|
412
|
+
elif isinstance(container_prop.type, DMSEnum):
|
|
413
|
+
return Enum(collection=ClassEntity(suffix=prop_id), unknownValue=container_prop.type.unknown_value)
|
|
413
414
|
else:
|
|
414
415
|
return DataType.load(container_prop.type._type)
|
|
415
416
|
else:
|
|
416
417
|
self.issue_list.append(
|
|
417
|
-
PropertyTypeNotSupportedWarning[dm.ViewId](view_entity.as_id(), "
|
|
418
|
+
PropertyTypeNotSupportedWarning[dm.ViewId](view_entity.as_id(), "view", prop_id, type(prop).__name__)
|
|
418
419
|
)
|
|
419
420
|
return None
|
|
420
421
|
|
|
@@ -475,7 +476,59 @@ class DMSImporter(BaseImporter):
|
|
|
475
476
|
else:
|
|
476
477
|
self.issue_list.append(
|
|
477
478
|
PropertyTypeNotSupportedWarning[dm.ContainerId](
|
|
478
|
-
prop.container, "
|
|
479
|
+
prop.container, "container", prop_id, type(constraint_obj).__name__
|
|
479
480
|
)
|
|
480
481
|
)
|
|
481
482
|
return unique_constraints or None
|
|
483
|
+
|
|
484
|
+
def _find_reverse_edge(
|
|
485
|
+
self, prop_id: str, prop: SingleEdgeConnectionApply | MultiEdgeConnectionApply, view_id: dm.ViewId
|
|
486
|
+
) -> str | None:
|
|
487
|
+
if prop.source not in self._all_views_by_id:
|
|
488
|
+
return None
|
|
489
|
+
view = self._all_views_by_id[prop.source]
|
|
490
|
+
candidates = []
|
|
491
|
+
for prop_name, reverse_prop in (view.properties or {}).items():
|
|
492
|
+
if isinstance(reverse_prop, SingleEdgeConnectionApply | MultiEdgeConnectionApply):
|
|
493
|
+
if (
|
|
494
|
+
reverse_prop.type == prop.type
|
|
495
|
+
and reverse_prop.source == view_id
|
|
496
|
+
and reverse_prop.direction != prop.direction
|
|
497
|
+
):
|
|
498
|
+
candidates.append(prop_name)
|
|
499
|
+
if len(candidates) == 0:
|
|
500
|
+
self.issue_list.append(
|
|
501
|
+
PropertyNotFoundWarning(
|
|
502
|
+
prop.source,
|
|
503
|
+
"view property",
|
|
504
|
+
f"reverse edge of {prop_id}",
|
|
505
|
+
dm.PropertyId(view_id, prop_id),
|
|
506
|
+
"view property",
|
|
507
|
+
)
|
|
508
|
+
)
|
|
509
|
+
return None
|
|
510
|
+
if len(candidates) > 1:
|
|
511
|
+
self.issue_list.append(
|
|
512
|
+
ResourcesDuplicatedWarning(
|
|
513
|
+
frozenset(dm.PropertyId(view.as_id(), candidate) for candidate in candidates),
|
|
514
|
+
"view property",
|
|
515
|
+
default_action="Multiple reverse edges found for "
|
|
516
|
+
f"{dm.PropertyId(view_id, prop_id)!r}. Will use {candidates[0]}",
|
|
517
|
+
)
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
return candidates[0]
|
|
521
|
+
|
|
522
|
+
@staticmethod
|
|
523
|
+
def _create_enum_collections(containers: Collection[dm.ContainerApply]) -> list[DMSInputEnum] | None:
|
|
524
|
+
enum_collections: list[DMSInputEnum] = []
|
|
525
|
+
for container in containers:
|
|
526
|
+
for prop_id, prop in container.properties.items():
|
|
527
|
+
if isinstance(prop.type, DMSEnum):
|
|
528
|
+
for identifier, value in prop.type.values.items():
|
|
529
|
+
enum_collections.append(
|
|
530
|
+
DMSInputEnum(
|
|
531
|
+
collection=prop_id, value=identifier, name=value.name, description=value.description
|
|
532
|
+
)
|
|
533
|
+
)
|
|
534
|
+
return enum_collections
|