cognite-neat 0.121.1__py3-none-any.whl → 0.121.2__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/core/_client/_api/statistics.py +91 -0
- cognite/neat/core/_client/_api_client.py +2 -0
- cognite/neat/core/_client/data_classes/statistics.py +125 -0
- cognite/neat/core/_client/testing.py +4 -0
- cognite/neat/core/_constants.py +6 -7
- cognite/neat/core/_data_model/_constants.py +23 -16
- cognite/neat/core/_data_model/_shared.py +33 -17
- cognite/neat/core/_data_model/analysis/__init__.py +2 -2
- cognite/neat/core/_data_model/analysis/_base.py +186 -183
- cognite/neat/core/_data_model/catalog/__init__.py +1 -1
- cognite/neat/core/_data_model/exporters/__init__.py +5 -5
- cognite/neat/core/_data_model/exporters/_base.py +10 -8
- cognite/neat/core/_data_model/exporters/{_rules2dms.py → _data_model2dms.py} +22 -18
- cognite/neat/core/_data_model/exporters/{_rules2excel.py → _data_model2excel.py} +51 -51
- cognite/neat/core/_data_model/exporters/{_rules2instance_template.py → _data_model2instance_template.py} +4 -4
- cognite/neat/core/_data_model/exporters/{_rules2ontology.py → _data_model2ontology.py} +50 -50
- cognite/neat/core/_data_model/exporters/{_rules2yaml.py → _data_model2yaml.py} +21 -18
- cognite/neat/core/_data_model/importers/__init__.py +6 -6
- cognite/neat/core/_data_model/importers/_base.py +8 -6
- cognite/neat/core/_data_model/importers/_base_file_reader.py +56 -0
- cognite/neat/core/_data_model/importers/{_yaml2rules.py → _dict2data_model.py} +40 -20
- cognite/neat/core/_data_model/importers/{_dms2rules.py → _dms2data_model.py} +58 -49
- cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/dtdl_converter.py +22 -22
- cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/dtdl_importer.py +7 -7
- cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/spec.py +3 -3
- cognite/neat/core/_data_model/importers/_rdf/_base.py +9 -9
- cognite/neat/core/_data_model/importers/_rdf/_imf2rules.py +15 -15
- cognite/neat/core/_data_model/importers/_rdf/_inference2rules.py +36 -36
- cognite/neat/core/_data_model/importers/_rdf/_owl2rules.py +12 -12
- cognite/neat/core/_data_model/importers/_rdf/_shared.py +25 -25
- cognite/neat/core/_data_model/importers/{_spreadsheet2rules.py → _spreadsheet2data_model.py} +72 -12
- cognite/neat/core/_data_model/models/__init__.py +8 -8
- cognite/neat/core/_data_model/models/_base_unverified.py +1 -1
- cognite/neat/core/_data_model/models/_base_verified.py +3 -3
- cognite/neat/core/_data_model/models/_types.py +6 -6
- cognite/neat/core/_data_model/models/conceptual/__init__.py +6 -6
- cognite/neat/core/_data_model/models/conceptual/_unverified.py +20 -20
- cognite/neat/core/_data_model/models/conceptual/_validation.py +87 -77
- cognite/neat/core/_data_model/models/conceptual/_verified.py +53 -51
- cognite/neat/core/_data_model/models/data_types.py +2 -2
- cognite/neat/core/_data_model/models/entities/__init__.py +8 -8
- cognite/neat/core/_data_model/models/entities/_loaders.py +11 -10
- cognite/neat/core/_data_model/models/entities/_multi_value.py +5 -5
- cognite/neat/core/_data_model/models/entities/_single_value.py +44 -38
- cognite/neat/core/_data_model/models/entities/_types.py +9 -3
- cognite/neat/core/_data_model/models/entities/_wrapped.py +3 -3
- cognite/neat/core/_data_model/models/mapping/_classic2core.py +12 -9
- cognite/neat/core/_data_model/models/physical/__init__.py +40 -0
- cognite/neat/core/_data_model/models/{dms → physical}/_exporter.py +71 -52
- cognite/neat/core/_data_model/models/{dms/_rules_input.py → physical/_unverified.py} +48 -39
- cognite/neat/core/_data_model/models/{dms → physical}/_validation.py +13 -11
- cognite/neat/core/_data_model/models/{dms/_rules.py → physical/_verified.py} +68 -60
- cognite/neat/core/_data_model/transformers/__init__.py +27 -23
- cognite/neat/core/_data_model/transformers/_base.py +26 -19
- cognite/neat/core/_data_model/transformers/_converters.py +703 -618
- cognite/neat/core/_data_model/transformers/_mapping.py +74 -55
- cognite/neat/core/_data_model/transformers/_verification.py +63 -54
- cognite/neat/core/_instances/extractors/_base.py +1 -1
- cognite/neat/core/_instances/extractors/_classic_cdf/_classic.py +8 -8
- cognite/neat/core/_instances/extractors/_dms_graph.py +42 -34
- cognite/neat/core/_instances/extractors/_mock_graph_generator.py +98 -95
- cognite/neat/core/_instances/loaders/_base.py +2 -2
- cognite/neat/core/_instances/loaders/_rdf2dms.py +6 -6
- cognite/neat/core/_instances/transformers/_base.py +7 -4
- cognite/neat/core/_instances/transformers/_value_type.py +2 -6
- cognite/neat/core/_issues/_base.py +4 -4
- cognite/neat/core/_issues/errors/__init__.py +2 -2
- cognite/neat/core/_issues/errors/_wrapper.py +2 -2
- cognite/neat/core/_issues/warnings/_models.py +4 -4
- cognite/neat/core/_store/__init__.py +3 -3
- cognite/neat/core/_store/{_rules_store.py → _data_model.py} +119 -112
- cognite/neat/core/_store/{_graph_store.py → _instance.py} +3 -4
- cognite/neat/core/_store/_provenance.py +2 -2
- cognite/neat/core/_store/exceptions.py +2 -2
- cognite/neat/core/_utils/rdf_.py +14 -0
- cognite/neat/core/_utils/text.py +1 -1
- cognite/neat/session/_base.py +22 -20
- cognite/neat/session/_drop.py +2 -2
- cognite/neat/session/_inspect.py +5 -5
- cognite/neat/session/_mapping.py +8 -6
- cognite/neat/session/_read.py +2 -2
- cognite/neat/session/_set.py +3 -3
- cognite/neat/session/_show.py +11 -11
- cognite/neat/session/_state.py +13 -13
- cognite/neat/session/_subset.py +12 -9
- cognite/neat/session/_template.py +13 -13
- cognite/neat/session/_to.py +17 -17
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.121.2.dist-info}/METADATA +1 -1
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.121.2.dist-info}/RECORD +95 -93
- cognite/neat/core/_data_model/exporters/_validation.py +0 -14
- cognite/neat/core/_data_model/models/dms/__init__.py +0 -32
- /cognite/neat/core/_data_model/catalog/{info-rules-imf.xlsx → conceptual-imf-data-model.xlsx} +0 -0
- /cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/__init__.py +0 -0
- /cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/_unit_lookup.py +0 -0
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.121.2.dist-info}/WHEEL +0 -0
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.121.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,7 +18,7 @@ from cognite.neat.core._data_model.models._base_verified import (
|
|
|
18
18
|
SheetRow,
|
|
19
19
|
)
|
|
20
20
|
from cognite.neat.core._data_model.models._types import (
|
|
21
|
-
|
|
21
|
+
ConceptEntityType,
|
|
22
22
|
ConceptualPropertyType,
|
|
23
23
|
MultiValueTypeType,
|
|
24
24
|
URIRefType,
|
|
@@ -27,15 +27,15 @@ from cognite.neat.core._data_model.models._types import (
|
|
|
27
27
|
# NeatIdType,
|
|
28
28
|
from cognite.neat.core._data_model.models.data_types import DataType
|
|
29
29
|
from cognite.neat.core._data_model.models.entities import (
|
|
30
|
-
ClassEntity,
|
|
31
30
|
ClassEntityList,
|
|
32
|
-
|
|
31
|
+
ConceptEntity,
|
|
32
|
+
ConceptualEntity,
|
|
33
33
|
UnknownEntity,
|
|
34
34
|
)
|
|
35
35
|
from cognite.neat.core._issues.errors import PropertyDefinitionError
|
|
36
36
|
|
|
37
37
|
if TYPE_CHECKING:
|
|
38
|
-
from cognite.neat.core._data_model.models import
|
|
38
|
+
from cognite.neat.core._data_model.models import PhysicalDataModel
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class ConceptualMetadata(BaseVerifiedMetadata):
|
|
@@ -52,18 +52,19 @@ def _get_metadata(context: Any) -> ConceptualMetadata | None:
|
|
|
52
52
|
return None
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
class
|
|
55
|
+
class Concept(SheetRow):
|
|
56
56
|
"""
|
|
57
|
-
|
|
57
|
+
Concept is a category of things that share a common set of attributes and relationships.
|
|
58
58
|
|
|
59
59
|
Args:
|
|
60
|
-
|
|
60
|
+
concept: An ID of the concept.
|
|
61
61
|
description: A description of the class.
|
|
62
62
|
implements: Which classes the current class implements.
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
alias="
|
|
65
|
+
concept: ConceptEntityType = Field(
|
|
66
|
+
alias="Concept",
|
|
67
|
+
description="Concept id being defined, use strongly advise `PascalCase` usage.",
|
|
67
68
|
)
|
|
68
69
|
name: str | None = Field(alias="Name", default=None, description="Human readable name of the class.")
|
|
69
70
|
description: str | None = Field(alias="Description", default=None, description="Short description of the class.")
|
|
@@ -83,11 +84,11 @@ class ConceptualClass(SheetRow):
|
|
|
83
84
|
)
|
|
84
85
|
|
|
85
86
|
def _identifier(self) -> tuple[Hashable, ...]:
|
|
86
|
-
return (self.
|
|
87
|
+
return (self.concept,)
|
|
87
88
|
|
|
88
|
-
@field_serializer("
|
|
89
|
+
@field_serializer("concept", when_used="unless-none")
|
|
89
90
|
def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
|
|
90
|
-
if (metadata := _get_metadata(info.context)) and isinstance(value,
|
|
91
|
+
if (metadata := _get_metadata(info.context)) and isinstance(value, ConceptualEntity):
|
|
91
92
|
return value.dump(prefix=metadata.prefix, version=metadata.version)
|
|
92
93
|
return str(value)
|
|
93
94
|
|
|
@@ -96,22 +97,22 @@ class ConceptualClass(SheetRow):
|
|
|
96
97
|
if isinstance(value, list) and (metadata := _get_metadata(info.context)):
|
|
97
98
|
return ",".join(
|
|
98
99
|
(
|
|
99
|
-
|
|
100
|
-
if isinstance(
|
|
101
|
-
else str(
|
|
100
|
+
concept.dump(prefix=metadata.prefix, version=metadata.version)
|
|
101
|
+
if isinstance(concept, ConceptualEntity)
|
|
102
|
+
else str(concept)
|
|
102
103
|
)
|
|
103
|
-
for
|
|
104
|
+
for concept in value
|
|
104
105
|
)
|
|
105
106
|
return ",".join(str(value) for value in value)
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
class ConceptualProperty(SheetRow):
|
|
109
110
|
"""
|
|
110
|
-
A property is a characteristic of a
|
|
111
|
-
or a relationship to another
|
|
111
|
+
A property is a characteristic of a concept. It is a named attribute of a concept
|
|
112
|
+
that describes a range of values or a relationship to another concept.
|
|
112
113
|
|
|
113
114
|
Args:
|
|
114
|
-
|
|
115
|
+
concept: Concept ID to which property belongs
|
|
115
116
|
property_: Property ID of the property
|
|
116
117
|
name: Property name.
|
|
117
118
|
value_type: Type of value property will hold (data or link to another class)
|
|
@@ -122,8 +123,9 @@ class ConceptualProperty(SheetRow):
|
|
|
122
123
|
knowledge graph. Defaults to None (no transformation)
|
|
123
124
|
"""
|
|
124
125
|
|
|
125
|
-
|
|
126
|
-
alias="
|
|
126
|
+
concept: ConceptEntityType = Field(
|
|
127
|
+
alias="Concept",
|
|
128
|
+
description="Concept id that the property is defined for, strongly advise `PascalCase` usage.",
|
|
127
129
|
)
|
|
128
130
|
property_: ConceptualPropertyType = Field(
|
|
129
131
|
alias="Property",
|
|
@@ -131,7 +133,7 @@ class ConceptualProperty(SheetRow):
|
|
|
131
133
|
)
|
|
132
134
|
name: str | None = Field(alias="Name", default=None, description="Human readable name of the property.")
|
|
133
135
|
description: str | None = Field(alias="Description", default=None, description="Short description of the property.")
|
|
134
|
-
value_type: DataType |
|
|
136
|
+
value_type: DataType | ConceptEntityType | MultiValueTypeType | UnknownEntity = Field(
|
|
135
137
|
alias="Value Type",
|
|
136
138
|
union_mode="left_to_right",
|
|
137
139
|
description="Value type that the property can hold. It takes either subset of XSD type or a class defined.",
|
|
@@ -169,7 +171,7 @@ class ConceptualProperty(SheetRow):
|
|
|
169
171
|
)
|
|
170
172
|
|
|
171
173
|
def _identifier(self) -> tuple[Hashable, ...]:
|
|
172
|
-
return self.
|
|
174
|
+
return self.concept, self.property_
|
|
173
175
|
|
|
174
176
|
@field_validator("max_count", mode="before")
|
|
175
177
|
def parse_max_count(cls, value: int | float | None) -> int | float | None:
|
|
@@ -201,7 +203,7 @@ class ConceptualProperty(SheetRow):
|
|
|
201
203
|
# this value_type.python does not seems correct. Need to check this further
|
|
202
204
|
except Exception:
|
|
203
205
|
raise PropertyDefinitionError(
|
|
204
|
-
self.
|
|
206
|
+
self.concept,
|
|
205
207
|
"Class",
|
|
206
208
|
self.property_,
|
|
207
209
|
f"Default value {self.default} is not of type {self.value_type.python}", # type: ignore
|
|
@@ -220,9 +222,9 @@ class ConceptualProperty(SheetRow):
|
|
|
220
222
|
return None
|
|
221
223
|
return value
|
|
222
224
|
|
|
223
|
-
@field_serializer("
|
|
225
|
+
@field_serializer("concept", "value_type", when_used="unless-none")
|
|
224
226
|
def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
|
|
225
|
-
if (metadata := _get_metadata(info.context)) and isinstance(value,
|
|
227
|
+
if (metadata := _get_metadata(info.context)) and isinstance(value, ConceptualEntity):
|
|
226
228
|
return value.dump(prefix=metadata.prefix, version=metadata.version)
|
|
227
229
|
return str(value)
|
|
228
230
|
|
|
@@ -231,7 +233,7 @@ class ConceptualProperty(SheetRow):
|
|
|
231
233
|
"""Type of property based on value type. Either data (attribute) or object (edge) property."""
|
|
232
234
|
if isinstance(self.value_type, DataType):
|
|
233
235
|
return EntityTypes.data_property
|
|
234
|
-
elif isinstance(self.value_type,
|
|
236
|
+
elif isinstance(self.value_type, ConceptEntity):
|
|
235
237
|
return EntityTypes.object_property
|
|
236
238
|
else:
|
|
237
239
|
return EntityTypes.undefined
|
|
@@ -240,7 +242,7 @@ class ConceptualProperty(SheetRow):
|
|
|
240
242
|
class ConceptualDataModel(BaseVerifiedDataModel):
|
|
241
243
|
metadata: ConceptualMetadata = Field(alias="Metadata", description="Metadata for the conceptual data model")
|
|
242
244
|
properties: SheetList[ConceptualProperty] = Field(alias="Properties", description="List of properties")
|
|
243
|
-
|
|
245
|
+
concepts: SheetList[Concept] = Field(alias="Concepts", description="List of concepts")
|
|
244
246
|
prefixes: dict[str, Namespace] = Field(
|
|
245
247
|
alias="Prefixes",
|
|
246
248
|
default_factory=get_default_prefixes_and_namespaces,
|
|
@@ -259,12 +261,12 @@ class ConceptualDataModel(BaseVerifiedDataModel):
|
|
|
259
261
|
def set_neat_id(self) -> "ConceptualDataModel":
|
|
260
262
|
namespace = self.metadata.namespace
|
|
261
263
|
|
|
262
|
-
for
|
|
263
|
-
if not
|
|
264
|
-
|
|
264
|
+
for concept in self.concepts:
|
|
265
|
+
if not concept.neatId:
|
|
266
|
+
concept.neatId = namespace[concept.concept.suffix]
|
|
265
267
|
for property_ in self.properties:
|
|
266
268
|
if not property_.neatId:
|
|
267
|
-
property_.neatId = namespace[f"{property_.
|
|
269
|
+
property_.neatId = namespace[f"{property_.concept.suffix}/{property_.property_}"]
|
|
268
270
|
|
|
269
271
|
return self
|
|
270
272
|
|
|
@@ -273,37 +275,37 @@ class ConceptualDataModel(BaseVerifiedDataModel):
|
|
|
273
275
|
|
|
274
276
|
namespace = self.metadata.namespace
|
|
275
277
|
|
|
276
|
-
for
|
|
277
|
-
|
|
278
|
+
for concept in self.concepts:
|
|
279
|
+
concept.neatId = namespace[concept.concept.suffix]
|
|
278
280
|
for property_ in self.properties:
|
|
279
|
-
property_.neatId = namespace[f"{property_.
|
|
281
|
+
property_.neatId = namespace[f"{property_.concept.suffix}/{property_.property_}"]
|
|
280
282
|
|
|
281
|
-
def sync_with_physical_data_model(self,
|
|
283
|
+
def sync_with_physical_data_model(self, physical_data_model: "PhysicalDataModel") -> None:
|
|
282
284
|
# Sync at the metadata level
|
|
283
|
-
if
|
|
284
|
-
self.metadata.physical =
|
|
285
|
+
if physical_data_model.metadata.conceptual == self.metadata.identifier:
|
|
286
|
+
self.metadata.physical = physical_data_model.metadata.identifier
|
|
285
287
|
else:
|
|
286
288
|
# if models are not linked to start with, we skip
|
|
287
289
|
return None
|
|
288
290
|
|
|
289
291
|
conceptual_properties_by_neat_id = {prop.neatId: prop for prop in self.properties}
|
|
290
|
-
physical_properties_by_neat_id = {prop.neatId: prop for prop in
|
|
292
|
+
physical_properties_by_neat_id = {prop.neatId: prop for prop in physical_data_model.properties}
|
|
291
293
|
for neat_id, prop in physical_properties_by_neat_id.items():
|
|
292
|
-
if prop.
|
|
293
|
-
conceptual_properties_by_neat_id[prop.
|
|
294
|
+
if prop.conceptual in conceptual_properties_by_neat_id:
|
|
295
|
+
conceptual_properties_by_neat_id[prop.conceptual].physical = neat_id
|
|
294
296
|
|
|
295
|
-
classes_by_neat_id = {cls.neatId: cls for cls in self.
|
|
296
|
-
views_by_neat_id = {view.neatId: view for view in
|
|
297
|
+
classes_by_neat_id = {cls.neatId: cls for cls in self.concepts}
|
|
298
|
+
views_by_neat_id = {view.neatId: view for view in physical_data_model.views}
|
|
297
299
|
for neat_id, view in views_by_neat_id.items():
|
|
298
|
-
if view.
|
|
299
|
-
classes_by_neat_id[view.
|
|
300
|
+
if view.conceptual in classes_by_neat_id:
|
|
301
|
+
classes_by_neat_id[view.conceptual].physical = neat_id
|
|
300
302
|
|
|
301
|
-
def as_dms_rules(self) -> "
|
|
303
|
+
def as_dms_rules(self) -> "PhysicalDataModel":
|
|
302
304
|
from cognite.neat.core._data_model.transformers._converters import (
|
|
303
|
-
|
|
305
|
+
_ConceptualDataModelConverter,
|
|
304
306
|
)
|
|
305
307
|
|
|
306
|
-
return
|
|
308
|
+
return _ConceptualDataModelConverter(self).as_physical_data_model()
|
|
307
309
|
|
|
308
310
|
@classmethod
|
|
309
311
|
def display_type_name(cls) -> str:
|
|
@@ -311,12 +313,12 @@ class ConceptualDataModel(BaseVerifiedDataModel):
|
|
|
311
313
|
|
|
312
314
|
def _repr_html_(self) -> str:
|
|
313
315
|
summary = {
|
|
314
|
-
"
|
|
315
|
-
"intended for": "Information Architect",
|
|
316
|
+
"level": self.metadata.level,
|
|
317
|
+
"intended for": "Domain Expert and/or Information Architect",
|
|
316
318
|
"name": self.metadata.name,
|
|
317
319
|
"external_id": self.metadata.external_id,
|
|
318
320
|
"version": self.metadata.version,
|
|
319
|
-
"
|
|
321
|
+
"concepts": len(self.concepts),
|
|
320
322
|
"properties": len(self.properties),
|
|
321
323
|
}
|
|
322
324
|
|
|
@@ -16,7 +16,7 @@ from cognite.neat.core._data_model._constants import (
|
|
|
16
16
|
SPLIT_ON_EQUAL_PATTERN,
|
|
17
17
|
)
|
|
18
18
|
from cognite.neat.core._data_model.models.entities._single_value import (
|
|
19
|
-
|
|
19
|
+
ConceptEntity,
|
|
20
20
|
UnitEntity,
|
|
21
21
|
)
|
|
22
22
|
|
|
@@ -397,7 +397,7 @@ class Enum(DataType):
|
|
|
397
397
|
|
|
398
398
|
name: typing.Literal["enum"] = "enum"
|
|
399
399
|
|
|
400
|
-
collection:
|
|
400
|
+
collection: ConceptEntity
|
|
401
401
|
unknown_value: str | None = Field(None, alias="unknownValue")
|
|
402
402
|
|
|
403
403
|
|
|
@@ -4,15 +4,15 @@ from ._multi_value import MultiValueTypeInfo
|
|
|
4
4
|
from ._single_value import (
|
|
5
5
|
AssetEntity,
|
|
6
6
|
AssetFields,
|
|
7
|
-
|
|
7
|
+
ConceptEntity,
|
|
8
|
+
ConceptualEntity,
|
|
8
9
|
ContainerEntity,
|
|
9
10
|
DataModelEntity,
|
|
10
|
-
DMSEntity,
|
|
11
11
|
DMSNodeEntity,
|
|
12
|
-
DMSUnknownEntity,
|
|
13
12
|
DMSVersionedEntity,
|
|
14
13
|
EdgeEntity,
|
|
15
|
-
|
|
14
|
+
PhysicalEntity,
|
|
15
|
+
PhysicalUnknownEntity,
|
|
16
16
|
ReferenceEntity,
|
|
17
17
|
RelationshipEntity,
|
|
18
18
|
ReverseConnectionEntity,
|
|
@@ -28,21 +28,21 @@ __all__ = [
|
|
|
28
28
|
"AssetEntity",
|
|
29
29
|
"AssetFields",
|
|
30
30
|
"CdfResourceEntityList",
|
|
31
|
-
"ClassEntity",
|
|
32
31
|
"ClassEntityList",
|
|
32
|
+
"ConceptEntity",
|
|
33
|
+
"ConceptualEntity",
|
|
33
34
|
"ContainerEntity",
|
|
34
35
|
"ContainerEntityList",
|
|
35
|
-
"DMSEntity",
|
|
36
36
|
"DMSFilter",
|
|
37
37
|
"DMSNodeEntity",
|
|
38
|
-
"DMSUnknownEntity",
|
|
39
38
|
"DMSVersionedEntity",
|
|
40
39
|
"DataModelEntity",
|
|
41
40
|
"EdgeEntity",
|
|
42
|
-
"Entity",
|
|
43
41
|
"HasDataFilter",
|
|
44
42
|
"MultiValueTypeInfo",
|
|
45
43
|
"NodeTypeFilter",
|
|
44
|
+
"PhysicalEntity",
|
|
45
|
+
"PhysicalUnknownEntity",
|
|
46
46
|
"RawFilter",
|
|
47
47
|
"ReferenceEntity",
|
|
48
48
|
"RelationshipEntity",
|
|
@@ -5,9 +5,9 @@ from cognite.neat.core._issues.errors import NeatTypeError
|
|
|
5
5
|
|
|
6
6
|
from ._multi_value import MultiValueTypeInfo
|
|
7
7
|
from ._single_value import (
|
|
8
|
-
|
|
9
|
-
DMSUnknownEntity,
|
|
8
|
+
ConceptEntity,
|
|
10
9
|
EdgeEntity,
|
|
10
|
+
PhysicalUnknownEntity,
|
|
11
11
|
ReverseConnectionEntity,
|
|
12
12
|
Unknown,
|
|
13
13
|
UnknownEntity,
|
|
@@ -16,9 +16,10 @@ from ._single_value import (
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def load_value_type(
|
|
19
|
-
raw: str | MultiValueTypeInfo | DataType |
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
raw: str | MultiValueTypeInfo | DataType | ConceptEntity | UnknownEntity,
|
|
20
|
+
default_prefix: str,
|
|
21
|
+
) -> MultiValueTypeInfo | DataType | ConceptEntity | UnknownEntity:
|
|
22
|
+
if isinstance(raw, MultiValueTypeInfo | DataType | ConceptEntity | UnknownEntity):
|
|
22
23
|
return raw
|
|
23
24
|
elif isinstance(raw, str):
|
|
24
25
|
# property holding xsd data type
|
|
@@ -36,23 +37,23 @@ def load_value_type(
|
|
|
36
37
|
|
|
37
38
|
# property holding link to class
|
|
38
39
|
else:
|
|
39
|
-
return
|
|
40
|
+
return ConceptEntity.load(raw, prefix=default_prefix)
|
|
40
41
|
else:
|
|
41
42
|
raise NeatTypeError(f"Invalid value type: {type(raw)}")
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
def load_dms_value_type(
|
|
45
|
-
raw: str | DataType | ViewEntity |
|
|
46
|
+
raw: str | DataType | ViewEntity | PhysicalUnknownEntity,
|
|
46
47
|
default_space: str,
|
|
47
48
|
default_version: str,
|
|
48
|
-
) -> DataType | ViewEntity |
|
|
49
|
-
if isinstance(raw, DataType | ViewEntity |
|
|
49
|
+
) -> DataType | ViewEntity | PhysicalUnknownEntity:
|
|
50
|
+
if isinstance(raw, DataType | ViewEntity | PhysicalUnknownEntity):
|
|
50
51
|
return raw
|
|
51
52
|
elif isinstance(raw, str):
|
|
52
53
|
if DataType.is_data_type(raw):
|
|
53
54
|
return DataType.load(raw)
|
|
54
55
|
elif raw == str(Unknown):
|
|
55
|
-
return
|
|
56
|
+
return PhysicalUnknownEntity()
|
|
56
57
|
else:
|
|
57
58
|
return ViewEntity.load(raw, space=default_space, version=default_version)
|
|
58
59
|
raise NeatTypeError(f"Invalid value type: {type(raw)}")
|
|
@@ -10,12 +10,12 @@ from cognite.neat.core._data_model._constants import EntityTypes
|
|
|
10
10
|
from cognite.neat.core._data_model.models.data_types import DataType
|
|
11
11
|
|
|
12
12
|
from ._constants import _PARSE, Undefined
|
|
13
|
-
from ._single_value import
|
|
13
|
+
from ._single_value import ConceptEntity, UnknownEntity
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class MultiValueTypeInfo(BaseModel):
|
|
17
17
|
type_: ClassVar[EntityTypes] = EntityTypes.multi_value_type
|
|
18
|
-
types: list[DataType |
|
|
18
|
+
types: list[DataType | ConceptEntity]
|
|
19
19
|
|
|
20
20
|
def __str__(self) -> str:
|
|
21
21
|
return ", ".join([str(t) for t in self.types])
|
|
@@ -57,19 +57,19 @@ class MultiValueTypeInfo(BaseModel):
|
|
|
57
57
|
else:
|
|
58
58
|
return {
|
|
59
59
|
"types": [
|
|
60
|
-
(DataType.load(type_) if DataType.is_data_type(type_) else
|
|
60
|
+
(DataType.load(type_) if DataType.is_data_type(type_) else ConceptEntity.load(type_))
|
|
61
61
|
for type_ in types
|
|
62
62
|
]
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
def set_default_prefix(self, prefix: str) -> None:
|
|
66
66
|
for type_ in self.types:
|
|
67
|
-
if isinstance(type_,
|
|
67
|
+
if isinstance(type_, ConceptEntity) and type_.prefix is Undefined:
|
|
68
68
|
type_.prefix = prefix
|
|
69
69
|
|
|
70
70
|
def is_multi_object_type(self) -> bool:
|
|
71
71
|
"""Will signalize to DMS converter to create connection to unknown Node type"""
|
|
72
|
-
return all(isinstance(t,
|
|
72
|
+
return all(isinstance(t, ConceptEntity) for t in self.types)
|
|
73
73
|
|
|
74
74
|
def is_multi_data_type(self) -> bool:
|
|
75
75
|
"""Will signalize to DMS converter to attempt to find the best data type for value"""
|
|
@@ -50,8 +50,8 @@ from ._constants import (
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
@total_ordering
|
|
53
|
-
class
|
|
54
|
-
"""Entity is a class or property in
|
|
53
|
+
class ConceptualEntity(BaseModel, extra="ignore"):
|
|
54
|
+
"""Conceptual Entity is a concept, class or property in semantics sense."""
|
|
55
55
|
|
|
56
56
|
type_: ClassVar[EntityTypes] = EntityTypes.undefined
|
|
57
57
|
prefix: str | _UndefinedType = Undefined
|
|
@@ -64,7 +64,10 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
64
64
|
@classmethod
|
|
65
65
|
@overload
|
|
66
66
|
def load(
|
|
67
|
-
cls: "type[T_Entity]",
|
|
67
|
+
cls: "type[T_Entity]",
|
|
68
|
+
data: Any,
|
|
69
|
+
strict: Literal[False] = False,
|
|
70
|
+
**defaults: Any,
|
|
68
71
|
) -> "T_Entity | UnknownEntity": ...
|
|
69
72
|
|
|
70
73
|
@classmethod
|
|
@@ -85,7 +88,7 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
85
88
|
return cls.model_validate(data)
|
|
86
89
|
|
|
87
90
|
@model_validator(mode="before")
|
|
88
|
-
def _load(cls, data: Any) -> "dict |
|
|
91
|
+
def _load(cls, data: Any) -> "dict | ConceptualEntity":
|
|
89
92
|
defaults = {}
|
|
90
93
|
if isinstance(data, dict) and _PARSE in data:
|
|
91
94
|
defaults = data.get("defaults", {})
|
|
@@ -157,7 +160,7 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
157
160
|
if isinstance(annotation, UnionType) or get_origin(annotation) is Union:
|
|
158
161
|
annotation = get_args(annotation)[0]
|
|
159
162
|
|
|
160
|
-
if inspect.isclass(annotation) and issubclass(annotation,
|
|
163
|
+
if inspect.isclass(annotation) and issubclass(annotation, ConceptualEntity): # type: ignore[arg-type]
|
|
161
164
|
extra_args[key] = annotation.load(extra_args[key], **defaults) # type: ignore[union-attr, assignment]
|
|
162
165
|
return dict(prefix=prefix, suffix=suffix, **extra_args)
|
|
163
166
|
|
|
@@ -179,12 +182,12 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
179
182
|
return self.prefix, str(self.suffix), *extra
|
|
180
183
|
|
|
181
184
|
def __lt__(self, other: object) -> bool:
|
|
182
|
-
if not isinstance(other,
|
|
185
|
+
if not isinstance(other, ConceptualEntity):
|
|
183
186
|
return NotImplemented
|
|
184
187
|
return self.as_tuple() < other.as_tuple()
|
|
185
188
|
|
|
186
189
|
def __eq__(self, other: object) -> bool:
|
|
187
|
-
if not isinstance(other,
|
|
190
|
+
if not isinstance(other, ConceptualEntity):
|
|
188
191
|
return NotImplemented
|
|
189
192
|
return self.as_tuple() == other.as_tuple()
|
|
190
193
|
|
|
@@ -207,7 +210,7 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
207
210
|
def _as_str(self, **defaults: Any) -> str:
|
|
208
211
|
# We have overwritten the serialization to str, so we need to do it manually
|
|
209
212
|
model_dump = {
|
|
210
|
-
field.alias or field_name: v.dump(**defaults) if isinstance(v,
|
|
213
|
+
field.alias or field_name: v.dump(**defaults) if isinstance(v, ConceptualEntity) else v
|
|
211
214
|
for field_name, field in self.model_fields.items()
|
|
212
215
|
if (v := getattr(self, field_name)) is not None and field_name not in {"prefix", "suffix"}
|
|
213
216
|
}
|
|
@@ -263,11 +266,11 @@ class Entity(BaseModel, extra="ignore"):
|
|
|
263
266
|
return new_entity
|
|
264
267
|
|
|
265
268
|
|
|
266
|
-
T_Entity = TypeVar("T_Entity", bound=
|
|
269
|
+
T_Entity = TypeVar("T_Entity", bound=ConceptualEntity)
|
|
267
270
|
|
|
268
271
|
|
|
269
|
-
class
|
|
270
|
-
type_: ClassVar[EntityTypes] = EntityTypes.
|
|
272
|
+
class ConceptEntity(ConceptualEntity):
|
|
273
|
+
type_: ClassVar[EntityTypes] = EntityTypes.concept
|
|
271
274
|
version: str | None = None
|
|
272
275
|
|
|
273
276
|
def as_view_entity(self, default_space: str, default_version: str) -> "ViewEntity":
|
|
@@ -283,7 +286,7 @@ class ClassEntity(Entity):
|
|
|
283
286
|
return ContainerEntity(space=space, externalId=str(self.suffix))
|
|
284
287
|
|
|
285
288
|
|
|
286
|
-
class UnknownEntity(
|
|
289
|
+
class UnknownEntity(ConceptEntity):
|
|
287
290
|
type_: ClassVar[EntityTypes] = EntityTypes.undefined
|
|
288
291
|
prefix: _UndefinedType = Undefined
|
|
289
292
|
suffix: _UnknownType = Unknown # type: ignore[assignment]
|
|
@@ -293,7 +296,7 @@ class UnknownEntity(ClassEntity):
|
|
|
293
296
|
return str(Unknown)
|
|
294
297
|
|
|
295
298
|
|
|
296
|
-
class UnitEntity(
|
|
299
|
+
class UnitEntity(ConceptualEntity):
|
|
297
300
|
type_: ClassVar[EntityTypes] = EntityTypes.unit
|
|
298
301
|
prefix: str
|
|
299
302
|
suffix: str
|
|
@@ -310,14 +313,14 @@ class AssetFields(StrEnum):
|
|
|
310
313
|
metadata = "metadata"
|
|
311
314
|
|
|
312
315
|
|
|
313
|
-
class AssetEntity(
|
|
316
|
+
class AssetEntity(ConceptualEntity):
|
|
314
317
|
type_: ClassVar[EntityTypes] = EntityTypes.asset
|
|
315
318
|
suffix: str = "Asset"
|
|
316
319
|
prefix: _UndefinedType = Undefined
|
|
317
320
|
property_: AssetFields = Field(alias="property")
|
|
318
321
|
|
|
319
322
|
|
|
320
|
-
class RelationshipEntity(
|
|
323
|
+
class RelationshipEntity(ConceptualEntity):
|
|
321
324
|
type_: ClassVar[EntityTypes] = EntityTypes.relationship
|
|
322
325
|
suffix: str = "Relationship"
|
|
323
326
|
prefix: _UndefinedType = Undefined
|
|
@@ -327,7 +330,7 @@ class RelationshipEntity(Entity):
|
|
|
327
330
|
T_ID = TypeVar("T_ID", bound=ContainerId | ViewId | DataModelId | PropertyId | NodeId | None)
|
|
328
331
|
|
|
329
332
|
|
|
330
|
-
class
|
|
333
|
+
class PhysicalEntity(ConceptualEntity, Generic[T_ID], ABC):
|
|
331
334
|
type_: ClassVar[EntityTypes] = EntityTypes.undefined
|
|
332
335
|
prefix: str = Field(alias="space")
|
|
333
336
|
suffix: str = Field(alias="externalId")
|
|
@@ -344,17 +347,20 @@ class DMSEntity(Entity, Generic[T_ID], ABC):
|
|
|
344
347
|
@classmethod
|
|
345
348
|
@overload
|
|
346
349
|
def load(
|
|
347
|
-
cls: "type[T_DMSEntity]",
|
|
348
|
-
|
|
350
|
+
cls: "type[T_DMSEntity]",
|
|
351
|
+
data: Any,
|
|
352
|
+
strict: Literal[False] = False,
|
|
353
|
+
**defaults: Any,
|
|
354
|
+
) -> "T_DMSEntity | PhysicalUnknownEntity": ...
|
|
349
355
|
|
|
350
356
|
@classmethod
|
|
351
357
|
def load(
|
|
352
358
|
cls: "type[T_DMSEntity]", data: Any, strict: bool = False, **defaults: Any
|
|
353
|
-
) -> "T_DMSEntity |
|
|
359
|
+
) -> "T_DMSEntity | PhysicalUnknownEntity": # type: ignore
|
|
354
360
|
if isinstance(data, str) and data == str(Unknown):
|
|
355
361
|
if strict:
|
|
356
362
|
raise NeatValueError(f"Failed to load entity {data!s}")
|
|
357
|
-
return
|
|
363
|
+
return PhysicalUnknownEntity.from_id(None)
|
|
358
364
|
return cast(T_DMSEntity, super().load(data, **defaults))
|
|
359
365
|
|
|
360
366
|
@property
|
|
@@ -376,8 +382,8 @@ class DMSEntity(Entity, Generic[T_ID], ABC):
|
|
|
376
382
|
def from_id(cls, id: T_ID) -> Self:
|
|
377
383
|
raise NotImplementedError("Method from_id must be implemented in subclasses")
|
|
378
384
|
|
|
379
|
-
def
|
|
380
|
-
return
|
|
385
|
+
def as_concept_entity(self) -> ConceptEntity:
|
|
386
|
+
return ConceptEntity(prefix=self.space, suffix=self.external_id)
|
|
381
387
|
|
|
382
388
|
def as_dms_compliant_entity(self) -> "Self":
|
|
383
389
|
new_entity = self.model_copy(deep=True)
|
|
@@ -385,10 +391,10 @@ class DMSEntity(Entity, Generic[T_ID], ABC):
|
|
|
385
391
|
return new_entity
|
|
386
392
|
|
|
387
393
|
|
|
388
|
-
T_DMSEntity = TypeVar("T_DMSEntity", bound=
|
|
394
|
+
T_DMSEntity = TypeVar("T_DMSEntity", bound=PhysicalEntity)
|
|
389
395
|
|
|
390
396
|
|
|
391
|
-
class ContainerEntity(
|
|
397
|
+
class ContainerEntity(PhysicalEntity[ContainerId]):
|
|
392
398
|
type_: ClassVar[EntityTypes] = EntityTypes.container
|
|
393
399
|
|
|
394
400
|
def as_id(self) -> ContainerId:
|
|
@@ -399,13 +405,13 @@ class ContainerEntity(DMSEntity[ContainerId]):
|
|
|
399
405
|
return cls(space=id.space, externalId=id.external_id)
|
|
400
406
|
|
|
401
407
|
|
|
402
|
-
class DMSVersionedEntity(
|
|
408
|
+
class DMSVersionedEntity(PhysicalEntity[T_ID], ABC):
|
|
403
409
|
version: str
|
|
404
410
|
|
|
405
|
-
def
|
|
411
|
+
def as_concept_entity(self, skip_version: bool = False) -> ConceptEntity:
|
|
406
412
|
if skip_version:
|
|
407
|
-
return
|
|
408
|
-
return
|
|
413
|
+
return ConceptEntity(prefix=self.space, suffix=self.external_id)
|
|
414
|
+
return ConceptEntity(prefix=self.space, suffix=self.external_id, version=self.version)
|
|
409
415
|
|
|
410
416
|
|
|
411
417
|
class ViewEntity(DMSVersionedEntity[ViewId]):
|
|
@@ -429,7 +435,7 @@ class ViewEntity(DMSVersionedEntity[ViewId]):
|
|
|
429
435
|
raise ValueError("Version must be specified")
|
|
430
436
|
|
|
431
437
|
|
|
432
|
-
class
|
|
438
|
+
class PhysicalUnknownEntity(PhysicalEntity[None]):
|
|
433
439
|
"""This is a special entity that represents an unknown entity.
|
|
434
440
|
|
|
435
441
|
The use case is for direct relations where the source is not known."""
|
|
@@ -442,7 +448,7 @@ class DMSUnknownEntity(DMSEntity[None]):
|
|
|
442
448
|
return None
|
|
443
449
|
|
|
444
450
|
@classmethod
|
|
445
|
-
def from_id(cls, id: None) -> "
|
|
451
|
+
def from_id(cls, id: None) -> "PhysicalUnknownEntity":
|
|
446
452
|
return cls(space=Undefined, externalId=Unknown)
|
|
447
453
|
|
|
448
454
|
@property
|
|
@@ -463,7 +469,7 @@ class DataModelEntity(DMSVersionedEntity[DataModelId]):
|
|
|
463
469
|
return cls(space=id.space, externalId=id.external_id, version=id.version)
|
|
464
470
|
|
|
465
471
|
|
|
466
|
-
class DMSNodeEntity(
|
|
472
|
+
class DMSNodeEntity(PhysicalEntity[NodeId]):
|
|
467
473
|
type_: ClassVar[EntityTypes] = EntityTypes.dms_node
|
|
468
474
|
|
|
469
475
|
def as_id(self) -> NodeId:
|
|
@@ -481,7 +487,7 @@ class DMSNodeEntity(DMSEntity[NodeId]):
|
|
|
481
487
|
return cls(space=ref.space, externalId=ref.external_id)
|
|
482
488
|
|
|
483
489
|
|
|
484
|
-
class EdgeEntity(
|
|
490
|
+
class EdgeEntity(PhysicalEntity[None]):
|
|
485
491
|
type_: ClassVar[EntityTypes] = EntityTypes.edge
|
|
486
492
|
prefix: _UndefinedType = Undefined # type: ignore[assignment]
|
|
487
493
|
suffix: Literal["edge"] = "edge"
|
|
@@ -501,21 +507,21 @@ class EdgeEntity(DMSEntity[None]):
|
|
|
501
507
|
return cls()
|
|
502
508
|
|
|
503
509
|
|
|
504
|
-
class ReverseConnectionEntity(
|
|
510
|
+
class ReverseConnectionEntity(ConceptualEntity):
|
|
505
511
|
type_: ClassVar[EntityTypes] = EntityTypes.reverse
|
|
506
512
|
prefix: _UndefinedType = Undefined
|
|
507
513
|
suffix: Literal["reverse"] = "reverse"
|
|
508
514
|
property_: str = Field(alias="property")
|
|
509
515
|
|
|
510
516
|
|
|
511
|
-
class ReferenceEntity(
|
|
517
|
+
class ReferenceEntity(ConceptEntity):
|
|
512
518
|
type_: ClassVar[EntityTypes] = EntityTypes.reference_entity
|
|
513
519
|
prefix: str
|
|
514
520
|
property_: str | None = Field(None, alias="property")
|
|
515
521
|
|
|
516
522
|
@classmethod
|
|
517
|
-
def from_entity(cls, entity:
|
|
518
|
-
if isinstance(entity,
|
|
523
|
+
def from_entity(cls, entity: ConceptualEntity, property_: str) -> "ReferenceEntity":
|
|
524
|
+
if isinstance(entity, ConceptEntity):
|
|
519
525
|
return cls(
|
|
520
526
|
prefix=str(entity.prefix),
|
|
521
527
|
suffix=entity.suffix,
|
|
@@ -541,5 +547,5 @@ class ReferenceEntity(ClassEntity):
|
|
|
541
547
|
def as_node_entity(self) -> DMSNodeEntity:
|
|
542
548
|
return DMSNodeEntity(space=self.prefix, externalId=self.suffix)
|
|
543
549
|
|
|
544
|
-
def
|
|
545
|
-
return
|
|
550
|
+
def as_concept_entity(self) -> ConceptEntity:
|
|
551
|
+
return ConceptEntity(prefix=self.prefix, suffix=self.suffix, version=self.version)
|