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
|
@@ -3,11 +3,11 @@ from typing import Any, ClassVar, cast
|
|
|
3
3
|
|
|
4
4
|
from cognite.client import data_modeling as dm
|
|
5
5
|
|
|
6
|
+
from cognite.neat._client.data_classes.schema import DMSSchema
|
|
6
7
|
from cognite.neat._constants import COGNITE_MODELS, DMS_CONTAINER_PROPERTY_SIZE_LIMIT
|
|
7
8
|
from cognite.neat._issues import IssueList, NeatError, NeatIssue, NeatIssueList
|
|
8
9
|
from cognite.neat._issues.errors import (
|
|
9
10
|
PropertyDefinitionDuplicatedError,
|
|
10
|
-
ResourceChangedError,
|
|
11
11
|
ResourceNotDefinedError,
|
|
12
12
|
)
|
|
13
13
|
from cognite.neat._issues.errors._properties import ReversedConnectionNotFeasibleError
|
|
@@ -20,7 +20,7 @@ from cognite.neat._issues.warnings.user_modeling import (
|
|
|
20
20
|
NotNeatSupportedFilterWarning,
|
|
21
21
|
ViewPropertyLimitWarning,
|
|
22
22
|
)
|
|
23
|
-
from cognite.neat._rules.
|
|
23
|
+
from cognite.neat._rules.analysis import DMSAnalysis
|
|
24
24
|
from cognite.neat._rules.models.data_types import DataType
|
|
25
25
|
from cognite.neat._rules.models.entities import ContainerEntity, RawFilter
|
|
26
26
|
from cognite.neat._rules.models.entities._single_value import (
|
|
@@ -29,7 +29,6 @@ from cognite.neat._rules.models.entities._single_value import (
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
from ._rules import DMSProperty, DMSRules
|
|
32
|
-
from ._schema import DMSSchema
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
class DMSPostValidation:
|
|
@@ -47,6 +46,7 @@ class DMSPostValidation:
|
|
|
47
46
|
self.containers = rules.containers
|
|
48
47
|
self.views = rules.views
|
|
49
48
|
self.issue_list = IssueList()
|
|
49
|
+
self.probe = DMSAnalysis(rules)
|
|
50
50
|
|
|
51
51
|
def validate(self) -> NeatIssueList:
|
|
52
52
|
self._validate_raw_filter()
|
|
@@ -55,12 +55,7 @@ class DMSPostValidation:
|
|
|
55
55
|
self._validate_reverse_connections()
|
|
56
56
|
|
|
57
57
|
self._referenced_views_and_containers_are_existing_and_proper_size()
|
|
58
|
-
if self.metadata.schema_ is SchemaCompleteness.extended:
|
|
59
|
-
self._validate_extension()
|
|
60
|
-
if self.metadata.schema_ is SchemaCompleteness.partial:
|
|
61
|
-
return self.issue_list
|
|
62
58
|
dms_schema = self.rules.as_schema()
|
|
63
|
-
self.issue_list.extend(dms_schema.validate())
|
|
64
59
|
self._validate_performance(dms_schema)
|
|
65
60
|
return self.issue_list
|
|
66
61
|
|
|
@@ -160,9 +155,10 @@ class DMSPostValidation:
|
|
|
160
155
|
self.issue_list.extend(errors)
|
|
161
156
|
|
|
162
157
|
def _referenced_views_and_containers_are_existing_and_proper_size(self) -> None:
|
|
158
|
+
# TODO: Split this method and keep only validation that should be independent of
|
|
159
|
+
# whether view and/or container exist in the pydantic model instance
|
|
160
|
+
# other validation should be done through NeatSession.verify()
|
|
163
161
|
defined_views = {view.view.as_id() for view in self.views}
|
|
164
|
-
if self.metadata.schema_ is SchemaCompleteness.extended and self.rules.last:
|
|
165
|
-
defined_views |= {view.view.as_id() for view in self.rules.last.views}
|
|
166
162
|
|
|
167
163
|
property_count_by_view: dict[dm.ViewId, int] = defaultdict(int)
|
|
168
164
|
errors: list[NeatIssue] = []
|
|
@@ -170,7 +166,7 @@ class DMSPostValidation:
|
|
|
170
166
|
view_id = prop.view.as_id()
|
|
171
167
|
if view_id not in defined_views:
|
|
172
168
|
errors.append(
|
|
173
|
-
ResourceNotDefinedError
|
|
169
|
+
ResourceNotDefinedError(
|
|
174
170
|
identifier=view_id,
|
|
175
171
|
resource_type="view",
|
|
176
172
|
location="Views Sheet",
|
|
@@ -184,103 +180,9 @@ class DMSPostValidation:
|
|
|
184
180
|
for view_id, count in property_count_by_view.items():
|
|
185
181
|
if count > DMS_CONTAINER_PROPERTY_SIZE_LIMIT:
|
|
186
182
|
errors.append(ViewPropertyLimitWarning(view_id, count))
|
|
187
|
-
if self.metadata.schema_ is SchemaCompleteness.complete:
|
|
188
|
-
defined_containers = {container.container.as_id() for container in self.containers or []}
|
|
189
|
-
if self.metadata.data_model_type == DataModelType.solution and self.rules.reference:
|
|
190
|
-
defined_containers |= {
|
|
191
|
-
container.container.as_id() for container in self.rules.reference.containers or []
|
|
192
|
-
}
|
|
193
183
|
|
|
194
|
-
for prop_no, prop in enumerate(self.properties):
|
|
195
|
-
if prop.container and (container_id := prop.container.as_id()) not in defined_containers:
|
|
196
|
-
errors.append(
|
|
197
|
-
ResourceNotDefinedError(
|
|
198
|
-
identifier=container_id,
|
|
199
|
-
resource_type="container",
|
|
200
|
-
location="Containers Sheet",
|
|
201
|
-
column_name="Container",
|
|
202
|
-
row_number=prop_no,
|
|
203
|
-
sheet_name="Properties",
|
|
204
|
-
)
|
|
205
|
-
)
|
|
206
|
-
for _container_no, container in enumerate(self.containers or []):
|
|
207
|
-
for constraint_no, constraint in enumerate(container.constraint or []):
|
|
208
|
-
if constraint.as_id() not in defined_containers:
|
|
209
|
-
errors.append(
|
|
210
|
-
ResourceNotDefinedError(
|
|
211
|
-
identifier=constraint.as_id(),
|
|
212
|
-
resource_type="container",
|
|
213
|
-
location="Containers Sheet",
|
|
214
|
-
column_name="Constraint",
|
|
215
|
-
row_number=constraint_no,
|
|
216
|
-
sheet_name="Properties",
|
|
217
|
-
)
|
|
218
|
-
)
|
|
219
184
|
self.issue_list.extend(errors)
|
|
220
185
|
|
|
221
|
-
def _validate_extension(self) -> None:
|
|
222
|
-
if self.metadata.schema_ is not SchemaCompleteness.extended:
|
|
223
|
-
return None
|
|
224
|
-
if not self.rules.last:
|
|
225
|
-
raise ValueError("The schema is set to 'extended', but no last rules are provided to validate against")
|
|
226
|
-
if self.metadata.extension is ExtensionCategory.rebuild:
|
|
227
|
-
# Everything is allowed
|
|
228
|
-
return None
|
|
229
|
-
user_schema = self.rules.as_schema()
|
|
230
|
-
new_containers = user_schema.containers.copy()
|
|
231
|
-
|
|
232
|
-
last_schema = self.rules.last.as_schema()
|
|
233
|
-
existing_containers = last_schema.containers.copy()
|
|
234
|
-
|
|
235
|
-
for container_id, container in new_containers.items():
|
|
236
|
-
existing_container = existing_containers.get(container_id)
|
|
237
|
-
if not existing_container or existing_container == container:
|
|
238
|
-
# No problem
|
|
239
|
-
continue
|
|
240
|
-
new_dumped = container.dump()
|
|
241
|
-
existing_dumped = existing_container.dump()
|
|
242
|
-
changed_attributes, changed_properties = self._changed_attributes_and_properties(
|
|
243
|
-
new_dumped, existing_dumped
|
|
244
|
-
)
|
|
245
|
-
self.issue_list.append(
|
|
246
|
-
ResourceChangedError(
|
|
247
|
-
container_id,
|
|
248
|
-
"container",
|
|
249
|
-
changed_properties=frozenset(changed_properties),
|
|
250
|
-
changed_attributes=frozenset(changed_attributes),
|
|
251
|
-
)
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
if self.metadata.extension is ExtensionCategory.reshape:
|
|
255
|
-
# Reshape allows changes to views
|
|
256
|
-
return None
|
|
257
|
-
|
|
258
|
-
new_views = user_schema.views.copy()
|
|
259
|
-
existing_views = last_schema.views.copy()
|
|
260
|
-
for view_id, view in new_views.items():
|
|
261
|
-
existing_view = existing_views.get(view_id)
|
|
262
|
-
if not existing_view or existing_view == view:
|
|
263
|
-
# No problem
|
|
264
|
-
continue
|
|
265
|
-
changed_attributes, changed_properties = self._changed_attributes_and_properties(
|
|
266
|
-
view.dump(), existing_view.dump()
|
|
267
|
-
)
|
|
268
|
-
existing_properties = existing_view.properties or {}
|
|
269
|
-
changed_properties = [prop for prop in changed_properties if prop in existing_properties]
|
|
270
|
-
changed_attributes = [attr for attr in changed_attributes if attr not in self.changeable_view_attributes]
|
|
271
|
-
|
|
272
|
-
if not changed_attributes and not changed_properties:
|
|
273
|
-
# Only added new properties, no problem
|
|
274
|
-
continue
|
|
275
|
-
self.issue_list.append(
|
|
276
|
-
ResourceChangedError(
|
|
277
|
-
view_id,
|
|
278
|
-
"view",
|
|
279
|
-
changed_properties=frozenset(changed_properties),
|
|
280
|
-
changed_attributes=frozenset(changed_attributes),
|
|
281
|
-
)
|
|
282
|
-
)
|
|
283
|
-
|
|
284
186
|
def _validate_performance(self, dms_schema: DMSSchema) -> None:
|
|
285
187
|
for view_id, view in dms_schema.views.items():
|
|
286
188
|
mapped_containers = dms_schema._get_mapped_container_from_view(view_id)
|
|
@@ -320,7 +222,7 @@ class DMSPostValidation:
|
|
|
320
222
|
UndefinedViewWarning(
|
|
321
223
|
str(prop_.view),
|
|
322
224
|
str(prop_.value_type),
|
|
323
|
-
prop_.
|
|
225
|
+
prop_.view_property,
|
|
324
226
|
)
|
|
325
227
|
)
|
|
326
228
|
|
|
@@ -329,7 +231,12 @@ class DMSPostValidation:
|
|
|
329
231
|
if self.metadata.as_data_model_id() in COGNITE_MODELS:
|
|
330
232
|
return None
|
|
331
233
|
|
|
332
|
-
properties_by_ids = {
|
|
234
|
+
properties_by_ids = {
|
|
235
|
+
f"{prop_.view!s}.{prop_.view_property}": prop_
|
|
236
|
+
for properties in self.probe.classes_with_properties(True, True).values()
|
|
237
|
+
for prop_ in properties
|
|
238
|
+
}
|
|
239
|
+
|
|
333
240
|
reversed_by_ids = {
|
|
334
241
|
id_: prop_
|
|
335
242
|
for id_, prop_ in properties_by_ids.items()
|
|
@@ -343,7 +250,7 @@ class DMSPostValidation:
|
|
|
343
250
|
ReversedConnectionNotFeasibleError(
|
|
344
251
|
id_,
|
|
345
252
|
"reversed connection",
|
|
346
|
-
prop_.
|
|
253
|
+
prop_.view_property,
|
|
347
254
|
str(prop_.view),
|
|
348
255
|
str(prop_.value_type),
|
|
349
256
|
cast(ReverseConnectionEntity, prop_.connection).property_,
|
|
@@ -355,7 +262,7 @@ class DMSPostValidation:
|
|
|
355
262
|
ReversedConnectionNotFeasibleError(
|
|
356
263
|
id_,
|
|
357
264
|
"view property",
|
|
358
|
-
prop_.
|
|
265
|
+
prop_.view_property,
|
|
359
266
|
str(prop_.view),
|
|
360
267
|
str(prop_.value_type),
|
|
361
268
|
cast(ReverseConnectionEntity, prop_.connection).property_,
|
|
@@ -3,7 +3,7 @@ import sys
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from functools import total_ordering
|
|
5
5
|
from types import UnionType
|
|
6
|
-
from typing import Any, ClassVar, Generic, Literal, TypeVar, Union, cast, get_args, get_origin
|
|
6
|
+
from typing import Any, ClassVar, Generic, Literal, TypeVar, Union, cast, get_args, get_origin, overload
|
|
7
7
|
|
|
8
8
|
from cognite.client.data_classes.data_modeling import DirectRelationReference
|
|
9
9
|
from cognite.client.data_classes.data_modeling.data_types import UnitReference
|
|
@@ -22,6 +22,7 @@ from pydantic import (
|
|
|
22
22
|
model_validator,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
+
from cognite.neat._issues.errors import NeatValueError
|
|
25
26
|
from cognite.neat._utils.text import replace_non_alphanumeric_with_underscore
|
|
26
27
|
|
|
27
28
|
if sys.version_info >= (3, 11):
|
|
@@ -56,14 +57,29 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
56
57
|
suffix: str
|
|
57
58
|
|
|
58
59
|
@classmethod
|
|
59
|
-
|
|
60
|
+
@overload
|
|
61
|
+
def load(cls: "type[T_Entity]", data: Any, strict: Literal[True], **defaults) -> "T_Entity": ...
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
@overload
|
|
65
|
+
def load(
|
|
66
|
+
cls: "type[T_Entity]", data: Any, strict: Literal[False] = False, **defaults
|
|
67
|
+
) -> "T_Entity | UnknownEntity": ...
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def load(cls: "type[T_Entity]", data: Any, strict: bool = False, **defaults) -> "T_Entity | UnknownEntity":
|
|
60
71
|
if isinstance(data, cls):
|
|
61
72
|
return data
|
|
62
73
|
elif isinstance(data, str) and data == str(Unknown):
|
|
74
|
+
if strict:
|
|
75
|
+
raise NeatValueError(f"Failed to load entity {data!s}")
|
|
63
76
|
return UnknownEntity(prefix=Undefined, suffix=Unknown)
|
|
64
77
|
if defaults and isinstance(defaults, dict):
|
|
65
78
|
# This is a trick to pass in default values
|
|
66
|
-
|
|
79
|
+
try:
|
|
80
|
+
return cls.model_validate({_PARSE: data, "defaults": defaults})
|
|
81
|
+
except ValueError:
|
|
82
|
+
raise
|
|
67
83
|
else:
|
|
68
84
|
return cls.model_validate(data)
|
|
69
85
|
|
|
@@ -291,9 +307,21 @@ class DMSEntity(Entity, Generic[T_ID], ABC):
|
|
|
291
307
|
defaults["prefix"] = defaults.pop("space")
|
|
292
308
|
return super().dump(**defaults)
|
|
293
309
|
|
|
310
|
+
@classmethod # type: ignore[override]
|
|
311
|
+
@overload
|
|
312
|
+
def load(cls: "type[T_DMSEntity]", data: Any, strict: Literal[True], **defaults) -> "T_DMSEntity": ...
|
|
313
|
+
|
|
314
|
+
@classmethod
|
|
315
|
+
@overload
|
|
316
|
+
def load(
|
|
317
|
+
cls: "type[T_DMSEntity]", data: Any, strict: Literal[False] = False, **defaults
|
|
318
|
+
) -> "T_DMSEntity | DMSUnknownEntity": ...
|
|
319
|
+
|
|
294
320
|
@classmethod
|
|
295
|
-
def load(cls: "type[T_DMSEntity]", data: Any, **defaults) -> "T_DMSEntity | DMSUnknownEntity": # type: ignore[override]
|
|
321
|
+
def load(cls: "type[T_DMSEntity]", data: Any, strict: bool = False, **defaults) -> "T_DMSEntity | DMSUnknownEntity": # type: ignore[override]
|
|
296
322
|
if isinstance(data, str) and data == str(Unknown):
|
|
323
|
+
if strict:
|
|
324
|
+
raise NeatValueError(f"Failed to load entity {data!s}")
|
|
297
325
|
return DMSUnknownEntity.from_id(None)
|
|
298
326
|
return cast(T_DMSEntity, super().load(data, **defaults))
|
|
299
327
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import math
|
|
2
2
|
import sys
|
|
3
3
|
from collections.abc import Hashable
|
|
4
|
-
from datetime import datetime
|
|
5
4
|
from typing import TYPE_CHECKING, Any, ClassVar
|
|
6
5
|
|
|
7
6
|
import pandas as pd
|
|
@@ -9,20 +8,14 @@ from pydantic import Field, field_serializer, field_validator, model_validator
|
|
|
9
8
|
from pydantic_core.core_schema import SerializationInfo
|
|
10
9
|
from rdflib import Namespace, URIRef
|
|
11
10
|
|
|
12
|
-
from cognite.neat._constants import
|
|
11
|
+
from cognite.neat._constants import get_default_prefixes
|
|
13
12
|
from cognite.neat._issues.errors import NeatValueError, PropertyDefinitionError
|
|
14
13
|
from cognite.neat._rules._constants import EntityTypes
|
|
15
14
|
from cognite.neat._rules.models._base_rules import (
|
|
16
15
|
BaseMetadata,
|
|
17
16
|
BaseRules,
|
|
18
|
-
|
|
19
|
-
DataModelType,
|
|
20
|
-
ExtensionCategory,
|
|
21
|
-
ExtensionCategoryType,
|
|
22
|
-
MatchType,
|
|
23
|
-
PropertyRef,
|
|
17
|
+
DataModelAspect,
|
|
24
18
|
RoleTypes,
|
|
25
|
-
SchemaCompleteness,
|
|
26
19
|
SheetList,
|
|
27
20
|
SheetRow,
|
|
28
21
|
)
|
|
@@ -35,10 +28,6 @@ from cognite.neat._rules.models._types import (
|
|
|
35
28
|
ClassEntityType,
|
|
36
29
|
InformationPropertyType,
|
|
37
30
|
MultiValueTypeType,
|
|
38
|
-
NamespaceType,
|
|
39
|
-
PrefixType,
|
|
40
|
-
StrListType,
|
|
41
|
-
VersionType,
|
|
42
31
|
)
|
|
43
32
|
from cognite.neat._rules.models.data_types import DataType
|
|
44
33
|
from cognite.neat._rules.models.entities import (
|
|
@@ -46,10 +35,8 @@ from cognite.neat._rules.models.entities import (
|
|
|
46
35
|
ClassEntityList,
|
|
47
36
|
Entity,
|
|
48
37
|
MultiValueTypeInfo,
|
|
49
|
-
ReferenceEntity,
|
|
50
38
|
Undefined,
|
|
51
39
|
UnknownEntity,
|
|
52
|
-
URLEntity,
|
|
53
40
|
)
|
|
54
41
|
|
|
55
42
|
if TYPE_CHECKING:
|
|
@@ -64,62 +51,11 @@ else:
|
|
|
64
51
|
|
|
65
52
|
class InformationMetadata(BaseMetadata):
|
|
66
53
|
role: ClassVar[RoleTypes] = RoleTypes.information
|
|
67
|
-
|
|
68
|
-
schema_: SchemaCompleteness = Field(SchemaCompleteness.partial, alias="schema")
|
|
69
|
-
extension: ExtensionCategoryType | None = ExtensionCategory.addition
|
|
70
|
-
|
|
71
|
-
prefix: PrefixType
|
|
72
|
-
namespace: NamespaceType
|
|
73
|
-
|
|
74
|
-
name: str = Field(
|
|
75
|
-
alias="title",
|
|
76
|
-
description="Human readable name of the data model",
|
|
77
|
-
min_length=1,
|
|
78
|
-
max_length=255,
|
|
79
|
-
)
|
|
80
|
-
description: str | None = Field(None, min_length=1, max_length=1024)
|
|
81
|
-
version: VersionType
|
|
82
|
-
|
|
83
|
-
created: datetime = Field(
|
|
84
|
-
description=("Date of the data model creation"),
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
updated: datetime = Field(
|
|
88
|
-
description=("Date of the data model update"),
|
|
89
|
-
)
|
|
90
|
-
creator: StrListType = Field(
|
|
91
|
-
description=(
|
|
92
|
-
"List of contributors to the data model creation, "
|
|
93
|
-
"typically information architects are considered as contributors."
|
|
94
|
-
),
|
|
95
|
-
)
|
|
96
|
-
license: str | None = None
|
|
97
|
-
rights: str | None = None
|
|
98
|
-
|
|
99
|
-
@model_validator(mode="after")
|
|
100
|
-
def extension_none_but_schema_extend(self) -> Self:
|
|
101
|
-
if self.extension is None:
|
|
102
|
-
self.extension = ExtensionCategory.addition
|
|
103
|
-
return self
|
|
104
|
-
return self
|
|
105
|
-
|
|
106
|
-
@field_validator("schema_", mode="plain")
|
|
107
|
-
def as_enum_schema(cls, value: str) -> SchemaCompleteness:
|
|
108
|
-
return SchemaCompleteness(value.strip())
|
|
109
|
-
|
|
110
|
-
@field_validator("extension", mode="plain")
|
|
111
|
-
def as_enum_extension(cls, value: str) -> ExtensionCategory:
|
|
112
|
-
return ExtensionCategory(value.strip())
|
|
54
|
+
aspect: ClassVar[DataModelAspect] = DataModelAspect.logical
|
|
113
55
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def as_identifier(self) -> str:
|
|
119
|
-
return f"{self.prefix}:{self.name}"
|
|
120
|
-
|
|
121
|
-
def get_prefix(self) -> str:
|
|
122
|
-
return self.prefix
|
|
56
|
+
# Linking to Conceptual and Physical data model aspects
|
|
57
|
+
physical: URIRef | None = Field(None, description="Link to the logical data model aspect")
|
|
58
|
+
conceptual: URIRef | None = Field(None, description="Link to the logical data model aspect")
|
|
123
59
|
|
|
124
60
|
|
|
125
61
|
def _get_metadata(context: Any) -> InformationMetadata | None:
|
|
@@ -135,48 +71,36 @@ class InformationClass(SheetRow):
|
|
|
135
71
|
Args:
|
|
136
72
|
class_: The class ID of the class.
|
|
137
73
|
description: A description of the class.
|
|
138
|
-
|
|
139
|
-
reference: Reference of the source of the information for given resource
|
|
140
|
-
match_type: The match type of the resource being described and the source entity.
|
|
74
|
+
implements: Which classes the current class implements.
|
|
141
75
|
"""
|
|
142
76
|
|
|
143
77
|
class_: ClassEntityType = Field(alias="Class")
|
|
144
78
|
name: str | None = Field(alias="Name", default=None)
|
|
145
79
|
description: str | None = Field(alias="Description", default=None)
|
|
146
|
-
|
|
147
|
-
reference: URLEntity | ReferenceEntity | None = Field(alias="Reference", default=None, union_mode="left_to_right")
|
|
148
|
-
match_type: MatchType | None = Field(alias="Match Type", default=None)
|
|
149
|
-
comment: str | None = Field(alias="Comment", default=None)
|
|
80
|
+
implements: ClassEntityList | None = Field(alias="Implements", default=None)
|
|
150
81
|
|
|
151
82
|
def _identifier(self) -> tuple[Hashable, ...]:
|
|
152
83
|
return (self.class_,)
|
|
153
84
|
|
|
154
|
-
@field_serializer("reference", when_used="always")
|
|
155
|
-
def set_reference(self, value: Any, info: SerializationInfo) -> str | None:
|
|
156
|
-
if isinstance(info.context, dict) and info.context.get("as_reference") is True:
|
|
157
|
-
return self.class_.dump()
|
|
158
|
-
return str(value) if value is not None else None
|
|
159
|
-
|
|
160
85
|
@field_serializer("class_", when_used="unless-none")
|
|
161
86
|
def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
|
|
162
87
|
if (metadata := _get_metadata(info.context)) and isinstance(value, Entity):
|
|
163
88
|
return value.dump(prefix=metadata.prefix, version=metadata.version)
|
|
164
89
|
return str(value)
|
|
165
90
|
|
|
166
|
-
@field_serializer("
|
|
91
|
+
@field_serializer("implements", when_used="unless-none")
|
|
167
92
|
def remove_default_prefixes(self, value: Any, info: SerializationInfo) -> str:
|
|
168
93
|
if isinstance(value, list) and (metadata := _get_metadata(info.context)):
|
|
169
94
|
return ",".join(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
95
|
+
(
|
|
96
|
+
class_.dump(prefix=metadata.prefix, version=metadata.version)
|
|
97
|
+
if isinstance(class_, Entity)
|
|
98
|
+
else str(class_)
|
|
99
|
+
)
|
|
100
|
+
for class_ in value
|
|
174
101
|
)
|
|
175
102
|
return ",".join(str(value) for value in value)
|
|
176
103
|
|
|
177
|
-
def as_reference(self) -> ClassRef:
|
|
178
|
-
return ClassRef(Class=self.class_)
|
|
179
|
-
|
|
180
104
|
|
|
181
105
|
class InformationProperty(SheetRow):
|
|
182
106
|
"""
|
|
@@ -191,8 +115,6 @@ class InformationProperty(SheetRow):
|
|
|
191
115
|
min_count: Minimum count of the property values. Defaults to 0
|
|
192
116
|
max_count: Maximum count of the property values. Defaults to None
|
|
193
117
|
default: Default value of the property
|
|
194
|
-
reference: Reference to the source of the information, HTTP URI
|
|
195
|
-
match_type: The match type of the resource being described and the source entity.
|
|
196
118
|
transformation: Actual rule for the transformation from source to target representation of
|
|
197
119
|
knowledge graph. Defaults to None (no transformation)
|
|
198
120
|
"""
|
|
@@ -207,10 +129,7 @@ class InformationProperty(SheetRow):
|
|
|
207
129
|
min_count: int | None = Field(alias="Min Count", default=None)
|
|
208
130
|
max_count: int | float | None = Field(alias="Max Count", default=None)
|
|
209
131
|
default: Any | None = Field(alias="Default", default=None)
|
|
210
|
-
reference: URLEntity | ReferenceEntity | None = Field(alias="Reference", default=None, union_mode="left_to_right")
|
|
211
|
-
match_type: MatchType | None = Field(alias="Match Type", default=None)
|
|
212
132
|
transformation: RDFPath | None = Field(alias="Transformation", default=None)
|
|
213
|
-
comment: str | None = Field(alias="Comment", default=None)
|
|
214
133
|
inherited: bool = Field(
|
|
215
134
|
default=False,
|
|
216
135
|
exclude=True,
|
|
@@ -281,19 +200,6 @@ class InformationProperty(SheetRow):
|
|
|
281
200
|
return None
|
|
282
201
|
return value
|
|
283
202
|
|
|
284
|
-
@field_serializer("reference", when_used="always")
|
|
285
|
-
def set_reference(self, value: Any, info: SerializationInfo) -> str | None:
|
|
286
|
-
# When rules as dumped as reference, we set the reference to the class
|
|
287
|
-
if isinstance(info.context, dict) and info.context.get("as_reference") is True:
|
|
288
|
-
return str(
|
|
289
|
-
ReferenceEntity(
|
|
290
|
-
prefix=str(self.class_.prefix),
|
|
291
|
-
suffix=self.class_.suffix,
|
|
292
|
-
property=self.property_,
|
|
293
|
-
)
|
|
294
|
-
)
|
|
295
|
-
return str(value) if value is not None else None
|
|
296
|
-
|
|
297
203
|
@field_serializer("class_", "value_type", when_used="unless-none")
|
|
298
204
|
def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
|
|
299
205
|
if (metadata := _get_metadata(info.context)) and isinstance(value, Entity):
|
|
@@ -322,17 +228,12 @@ class InformationProperty(SheetRow):
|
|
|
322
228
|
isinstance(self.max_count, int | float) and self.max_count > 1
|
|
323
229
|
)
|
|
324
230
|
|
|
325
|
-
def as_reference(self) -> PropertyRef:
|
|
326
|
-
return PropertyRef(Class=self.class_, Property=self.property_)
|
|
327
|
-
|
|
328
231
|
|
|
329
232
|
class InformationRules(BaseRules):
|
|
330
233
|
metadata: InformationMetadata = Field(alias="Metadata")
|
|
331
234
|
properties: SheetList[InformationProperty] = Field(alias="Properties")
|
|
332
235
|
classes: SheetList[InformationClass] = Field(alias="Classes")
|
|
333
236
|
prefixes: dict[str, Namespace] = Field(default_factory=get_default_prefixes, alias="Prefixes")
|
|
334
|
-
last: "InformationRules | None" = Field(None, alias="Last")
|
|
335
|
-
reference: "InformationRules | None" = Field(None, alias="Reference")
|
|
336
237
|
|
|
337
238
|
@field_validator("prefixes", mode="before")
|
|
338
239
|
def parse_str(cls, values: Any) -> Any:
|
|
@@ -355,10 +256,10 @@ class InformationRules(BaseRules):
|
|
|
355
256
|
if property_.class_.prefix is Undefined:
|
|
356
257
|
property_.class_.prefix = self.metadata.prefix
|
|
357
258
|
|
|
358
|
-
# update
|
|
259
|
+
# update implements
|
|
359
260
|
for class_ in self.classes:
|
|
360
|
-
if class_.
|
|
361
|
-
for parent in class_.
|
|
261
|
+
if class_.implements:
|
|
262
|
+
for parent in class_.implements:
|
|
362
263
|
if not isinstance(parent.prefix, str):
|
|
363
264
|
parent.prefix = self.metadata.prefix
|
|
364
265
|
if class_.class_.prefix is Undefined:
|
|
@@ -387,14 +288,10 @@ class InformationRules(BaseRules):
|
|
|
387
288
|
"type": "Logical Data Model",
|
|
388
289
|
"intended for": "Information Architect",
|
|
389
290
|
"name": self.metadata.name,
|
|
390
|
-
"external_id": self.metadata.
|
|
291
|
+
"external_id": self.metadata.external_id,
|
|
391
292
|
"version": self.metadata.version,
|
|
392
293
|
"classes": len(self.classes),
|
|
393
294
|
"properties": len(self.properties),
|
|
394
295
|
}
|
|
395
296
|
|
|
396
297
|
return pd.DataFrame([summary]).T.rename(columns={0: ""})._repr_html_() # type: ignore
|
|
397
|
-
|
|
398
|
-
@property
|
|
399
|
-
def id_(self) -> URIRef:
|
|
400
|
-
return DEFAULT_NAMESPACE[f"data-model/verified/info/{self.metadata.prefix}/{self.metadata.version}"]
|