cognite-neat 0.97.3__py3-none-any.whl → 0.99.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_client/__init__.py +4 -0
- cognite/neat/_client/_api/data_modeling_loaders.py +512 -0
- cognite/neat/_client/_api/schema.py +50 -0
- cognite/neat/_client/_api_client.py +17 -0
- cognite/neat/_client/data_classes/__init__.py +0 -0
- cognite/neat/{_utils/cdf/data_classes.py → _client/data_classes/data_modeling.py} +8 -135
- cognite/neat/{_rules/models/dms/_schema.py → _client/data_classes/schema.py} +32 -281
- cognite/neat/_graph/_shared.py +14 -15
- cognite/neat/_graph/extractors/_classic_cdf/_assets.py +14 -154
- cognite/neat/_graph/extractors/_classic_cdf/_base.py +154 -7
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +23 -12
- cognite/neat/_graph/extractors/_classic_cdf/_data_sets.py +17 -92
- cognite/neat/_graph/extractors/_classic_cdf/_events.py +13 -162
- cognite/neat/_graph/extractors/_classic_cdf/_files.py +15 -179
- cognite/neat/_graph/extractors/_classic_cdf/_labels.py +32 -100
- cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +27 -178
- cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +14 -139
- cognite/neat/_graph/extractors/_classic_cdf/_timeseries.py +15 -173
- cognite/neat/_graph/extractors/_rdf_file.py +6 -7
- cognite/neat/_graph/loaders/__init__.py +1 -2
- cognite/neat/_graph/queries/_base.py +17 -1
- cognite/neat/_graph/transformers/_classic_cdf.py +50 -134
- cognite/neat/_graph/transformers/_prune_graph.py +1 -1
- cognite/neat/_graph/transformers/_rdfpath.py +1 -1
- cognite/neat/_issues/warnings/__init__.py +6 -0
- cognite/neat/_issues/warnings/_external.py +8 -0
- cognite/neat/_issues/warnings/_models.py +9 -0
- cognite/neat/_issues/warnings/_properties.py +16 -0
- cognite/neat/_rules/_constants.py +7 -6
- cognite/neat/_rules/_shared.py +3 -8
- cognite/neat/_rules/analysis/__init__.py +1 -2
- cognite/neat/_rules/analysis/_base.py +10 -27
- cognite/neat/_rules/analysis/_dms.py +4 -10
- cognite/neat/_rules/analysis/_information.py +2 -10
- cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
- cognite/neat/_rules/exporters/_base.py +3 -4
- cognite/neat/_rules/exporters/_rules2dms.py +29 -40
- cognite/neat/_rules/exporters/_rules2excel.py +15 -72
- cognite/neat/_rules/exporters/_rules2ontology.py +4 -4
- cognite/neat/_rules/importers/_base.py +3 -4
- cognite/neat/_rules/importers/_dms2rules.py +21 -45
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py +1 -7
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +7 -10
- cognite/neat/_rules/importers/_rdf/_base.py +17 -29
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +5 -10
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +55 -51
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +5 -8
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_shared.py +25 -140
- cognite/neat/_rules/importers/_spreadsheet2rules.py +10 -41
- cognite/neat/_rules/models/__init__.py +3 -17
- cognite/neat/_rules/models/_base_rules.py +118 -62
- cognite/neat/_rules/models/dms/__init__.py +2 -2
- cognite/neat/_rules/models/dms/_exporter.py +20 -178
- cognite/neat/_rules/models/dms/_rules.py +65 -128
- cognite/neat/_rules/models/dms/_rules_input.py +72 -56
- cognite/neat/_rules/models/dms/_validation.py +16 -109
- cognite/neat/_rules/models/entities/_single_value.py +32 -4
- cognite/neat/_rules/models/information/_rules.py +19 -122
- cognite/neat/_rules/models/information/_rules_input.py +32 -41
- cognite/neat/_rules/models/information/_validation.py +34 -102
- cognite/neat/_rules/models/mapping/__init__.py +2 -3
- cognite/neat/_rules/models/mapping/_classic2core.py +36 -146
- cognite/neat/_rules/models/mapping/_classic2core.yaml +339 -0
- cognite/neat/_rules/transformers/__init__.py +3 -6
- cognite/neat/_rules/transformers/_converters.py +128 -206
- cognite/neat/_rules/transformers/_mapping.py +105 -34
- cognite/neat/_rules/transformers/_verification.py +5 -16
- cognite/neat/_session/_base.py +83 -21
- cognite/neat/_session/_collector.py +126 -0
- cognite/neat/_session/_drop.py +35 -0
- cognite/neat/_session/_inspect.py +22 -10
- cognite/neat/_session/_mapping.py +39 -0
- cognite/neat/_session/_prepare.py +222 -27
- cognite/neat/_session/_read.py +109 -19
- cognite/neat/_session/_set.py +2 -2
- cognite/neat/_session/_show.py +11 -11
- cognite/neat/_session/_to.py +27 -14
- cognite/neat/_session/exceptions.py +20 -3
- cognite/neat/_store/_base.py +27 -24
- cognite/neat/_store/_provenance.py +2 -2
- cognite/neat/_utils/auxiliary.py +19 -0
- cognite/neat/_utils/rdf_.py +28 -1
- cognite/neat/_version.py +1 -1
- cognite/neat/_workflows/steps/data_contracts.py +2 -10
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +14 -49
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +4 -1
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +5 -9
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/METADATA +4 -3
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/RECORD +97 -100
- cognite/neat/_graph/loaders/_rdf2asset.py +0 -416
- cognite/neat/_rules/analysis/_asset.py +0 -173
- cognite/neat/_rules/models/asset/__init__.py +0 -13
- cognite/neat/_rules/models/asset/_rules.py +0 -109
- cognite/neat/_rules/models/asset/_rules_input.py +0 -101
- cognite/neat/_rules/models/asset/_validation.py +0 -45
- cognite/neat/_rules/models/domain.py +0 -136
- cognite/neat/_rules/models/mapping/_base.py +0 -131
- cognite/neat/_utils/cdf/loaders/__init__.py +0 -25
- cognite/neat/_utils/cdf/loaders/_base.py +0 -54
- cognite/neat/_utils/cdf/loaders/_data_modeling.py +0 -339
- cognite/neat/_utils/cdf/loaders/_ingestion.py +0 -167
- /cognite/neat/{_utils/cdf → _client/_api}/__init__.py +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from typing import Any, ClassVar, cast
|
|
3
|
-
|
|
4
|
-
from pydantic import Field, field_validator, model_validator
|
|
5
|
-
from rdflib import Namespace, URIRef
|
|
6
|
-
|
|
7
|
-
from cognite.neat._constants import DEFAULT_NAMESPACE, get_default_prefixes
|
|
8
|
-
from cognite.neat._rules.models._base_rules import BaseRules, RoleTypes, SheetList
|
|
9
|
-
from cognite.neat._rules.models.entities import (
|
|
10
|
-
CdfResourceEntityList,
|
|
11
|
-
ClassEntity,
|
|
12
|
-
MultiValueTypeInfo,
|
|
13
|
-
Undefined,
|
|
14
|
-
)
|
|
15
|
-
from cognite.neat._rules.models.information import (
|
|
16
|
-
InformationClass,
|
|
17
|
-
InformationMetadata,
|
|
18
|
-
InformationProperty,
|
|
19
|
-
InformationRules,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
if sys.version_info >= (3, 11):
|
|
23
|
-
from typing import Self
|
|
24
|
-
else:
|
|
25
|
-
from typing_extensions import Self
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class AssetMetadata(InformationMetadata):
|
|
29
|
-
role: ClassVar[RoleTypes] = RoleTypes.asset
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class AssetClass(InformationClass): ...
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class AssetProperty(InformationProperty):
|
|
36
|
-
"""
|
|
37
|
-
A property is a characteristic of a class. It is a named attribute of a class that describes a range of values
|
|
38
|
-
or a relationship to another class.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
class_: Class ID to which property belongs
|
|
42
|
-
property_: Property ID of the property
|
|
43
|
-
name: Property name.
|
|
44
|
-
value_type: Type of value property will hold (data or link to another class)
|
|
45
|
-
min_count: Minimum count of the property values. Defaults to 0
|
|
46
|
-
max_count: Maximum count of the property values. Defaults to None
|
|
47
|
-
default: Default value of the property
|
|
48
|
-
reference: Reference to the source of the information, HTTP URI
|
|
49
|
-
match_type: The match type of the resource being described and the source entity.
|
|
50
|
-
transformation: Actual rule for the transformation from source to target representation of
|
|
51
|
-
knowledge graph. Defaults to None (no transformation)
|
|
52
|
-
implementation: Details on how given class-property is implemented in the classic CDF
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
implementation: CdfResourceEntityList = Field(alias="Implementation")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class AssetRules(BaseRules):
|
|
59
|
-
metadata: AssetMetadata = Field(alias="Metadata")
|
|
60
|
-
properties: SheetList[AssetProperty] = Field(alias="Properties")
|
|
61
|
-
classes: SheetList[AssetClass] = Field(alias="Classes")
|
|
62
|
-
prefixes: dict[str, Namespace] = Field(default_factory=get_default_prefixes)
|
|
63
|
-
last: "AssetRules | None" = Field(None, alias="Last")
|
|
64
|
-
reference: "AssetRules | None" = Field(None, alias="Reference")
|
|
65
|
-
|
|
66
|
-
@field_validator("prefixes", mode="before")
|
|
67
|
-
def parse_str(cls, values: Any) -> Any:
|
|
68
|
-
if isinstance(values, dict):
|
|
69
|
-
return {key: Namespace(value) if isinstance(value, str) else value for key, value in values.items()}
|
|
70
|
-
return values
|
|
71
|
-
|
|
72
|
-
@model_validator(mode="after")
|
|
73
|
-
def update_entities_prefix(self) -> Self:
|
|
74
|
-
# update expected_value_types
|
|
75
|
-
for property_ in self.properties:
|
|
76
|
-
if isinstance(property_.value_type, ClassEntity) and property_.value_type.prefix is Undefined:
|
|
77
|
-
property_.value_type.prefix = self.metadata.prefix
|
|
78
|
-
|
|
79
|
-
if isinstance(property_.value_type, MultiValueTypeInfo):
|
|
80
|
-
property_.value_type.set_default_prefix(self.metadata.prefix)
|
|
81
|
-
|
|
82
|
-
if property_.class_.prefix is Undefined:
|
|
83
|
-
property_.class_.prefix = self.metadata.prefix
|
|
84
|
-
|
|
85
|
-
# update parent classes
|
|
86
|
-
for class_ in self.classes:
|
|
87
|
-
if class_.parent:
|
|
88
|
-
for parent in class_.parent:
|
|
89
|
-
if not isinstance(parent.prefix, str):
|
|
90
|
-
parent.prefix = self.metadata.prefix
|
|
91
|
-
if class_.class_.prefix is Undefined:
|
|
92
|
-
class_.class_.prefix = self.metadata.prefix
|
|
93
|
-
|
|
94
|
-
return self
|
|
95
|
-
|
|
96
|
-
@model_validator(mode="after")
|
|
97
|
-
def post_validation(self) -> "AssetRules":
|
|
98
|
-
from ._validation import AssetPostValidation
|
|
99
|
-
|
|
100
|
-
issue_list = AssetPostValidation(cast(InformationRules, self)).validate()
|
|
101
|
-
if issue_list.warnings:
|
|
102
|
-
issue_list.trigger_warnings()
|
|
103
|
-
if issue_list.has_errors:
|
|
104
|
-
raise issue_list.as_exception()
|
|
105
|
-
return self
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def id_(self) -> URIRef:
|
|
109
|
-
return DEFAULT_NAMESPACE[f"data-model/verified/asset/{self.metadata.prefix}/{self.metadata.version}"]
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
from rdflib import Namespace, URIRef
|
|
5
|
-
|
|
6
|
-
from cognite.neat._constants import DEFAULT_NAMESPACE
|
|
7
|
-
from cognite.neat._rules.models._base_input import InputComponent, InputRules
|
|
8
|
-
from cognite.neat._rules.models.data_types import DataType
|
|
9
|
-
from cognite.neat._rules.models.entities import (
|
|
10
|
-
ClassEntity,
|
|
11
|
-
MultiValueTypeInfo,
|
|
12
|
-
UnknownEntity,
|
|
13
|
-
load_value_type,
|
|
14
|
-
)
|
|
15
|
-
from cognite.neat._rules.models.information._rules_input import InformationInputClass, InformationInputMetadata
|
|
16
|
-
|
|
17
|
-
from ._rules import AssetClass, AssetMetadata, AssetProperty, AssetRules
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@dataclass
|
|
21
|
-
class AssetInputMetadata(InformationInputMetadata):
|
|
22
|
-
@classmethod
|
|
23
|
-
def _get_verified_cls(cls) -> type[AssetMetadata]:
|
|
24
|
-
return AssetMetadata
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@dataclass
|
|
28
|
-
class AssetInputProperty(InputComponent[AssetProperty]):
|
|
29
|
-
class_: ClassEntity | str
|
|
30
|
-
property_: str
|
|
31
|
-
value_type: DataType | ClassEntity | MultiValueTypeInfo | UnknownEntity | str
|
|
32
|
-
name: str | None = None
|
|
33
|
-
description: str | None = None
|
|
34
|
-
comment: str | None = None
|
|
35
|
-
min_count: int | None = None
|
|
36
|
-
max_count: int | float | None = None
|
|
37
|
-
default: Any | None = None
|
|
38
|
-
reference: str | None = None
|
|
39
|
-
match_type: str | None = None
|
|
40
|
-
transformation: str | None = None
|
|
41
|
-
implementation: str | None = None
|
|
42
|
-
# Only used internally
|
|
43
|
-
inherited: bool = False
|
|
44
|
-
|
|
45
|
-
@classmethod
|
|
46
|
-
def _get_verified_cls(cls) -> type[AssetProperty]:
|
|
47
|
-
return AssetProperty
|
|
48
|
-
|
|
49
|
-
def dump(self, default_prefix: str) -> dict[str, Any]: # type: ignore[override]
|
|
50
|
-
output = super().dump()
|
|
51
|
-
output["Class"] = ClassEntity.load(self.class_, prefix=default_prefix)
|
|
52
|
-
output["Value Type"] = load_value_type(self.value_type, default_prefix)
|
|
53
|
-
return output
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@dataclass
|
|
57
|
-
class AssetInputClass(InformationInputClass):
|
|
58
|
-
@classmethod
|
|
59
|
-
def _get_verified_cls(cls) -> type[AssetClass]:
|
|
60
|
-
return AssetClass
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@dataclass
|
|
64
|
-
class AssetInputRules(InputRules[AssetRules]):
|
|
65
|
-
metadata: AssetInputMetadata
|
|
66
|
-
properties: list[AssetInputProperty]
|
|
67
|
-
classes: list[AssetInputClass]
|
|
68
|
-
prefixes: dict[str, Namespace] | None = None
|
|
69
|
-
last: "AssetInputRules | None" = None
|
|
70
|
-
reference: "AssetInputRules | None" = None
|
|
71
|
-
|
|
72
|
-
@classmethod
|
|
73
|
-
def _get_verified_cls(cls) -> type[AssetRules]:
|
|
74
|
-
return AssetRules
|
|
75
|
-
|
|
76
|
-
def dump(self) -> dict[str, Any]:
|
|
77
|
-
default_prefix = self.metadata.prefix
|
|
78
|
-
reference: dict[str, Any] | None = None
|
|
79
|
-
if isinstance(self.reference, AssetInputRules):
|
|
80
|
-
reference = self.reference.dump()
|
|
81
|
-
elif isinstance(self.reference, AssetRules):
|
|
82
|
-
# We need to load through the AssetRulesInput to set the correct default space and version
|
|
83
|
-
reference = AssetInputRules.load(self.reference.model_dump()).dump()
|
|
84
|
-
last: dict[str, Any] | None = None
|
|
85
|
-
if isinstance(self.last, AssetInputRules):
|
|
86
|
-
last = self.last.dump()
|
|
87
|
-
elif isinstance(self.last, AssetRules):
|
|
88
|
-
# We need to load through the AssetRulesInput to set the correct default space and version
|
|
89
|
-
last = AssetInputRules.load(self.last.model_dump()).dump()
|
|
90
|
-
|
|
91
|
-
return dict(
|
|
92
|
-
Metadata=self.metadata.dump(),
|
|
93
|
-
Properties=[prop.dump(default_prefix) for prop in self.properties],
|
|
94
|
-
Classes=[class_.dump(default_prefix) for class_ in self.classes],
|
|
95
|
-
Last=last,
|
|
96
|
-
Reference=reference,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
@property
|
|
100
|
-
def id_(self) -> URIRef:
|
|
101
|
-
return DEFAULT_NAMESPACE[f"data-model/unverified/asset/{self.metadata.prefix}/{self.metadata.version}"]
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from graphlib import CycleError
|
|
2
|
-
from typing import cast
|
|
3
|
-
|
|
4
|
-
from cognite.neat._issues import IssueList
|
|
5
|
-
from cognite.neat._issues.errors import NeatValueError, PropertyDefinitionError
|
|
6
|
-
from cognite.neat._rules.models._base_rules import SheetList
|
|
7
|
-
from cognite.neat._rules.models.asset._rules import AssetProperty, AssetRules
|
|
8
|
-
from cognite.neat._rules.models.entities import AssetEntity, AssetFields, ClassEntity
|
|
9
|
-
from cognite.neat._rules.models.information._validation import InformationPostValidation
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class AssetPostValidation(InformationPostValidation):
|
|
13
|
-
def validate(self) -> IssueList:
|
|
14
|
-
self.issue_list = super().validate()
|
|
15
|
-
self._parent_property_point_to_class()
|
|
16
|
-
self._circular_dependency()
|
|
17
|
-
return self.issue_list
|
|
18
|
-
|
|
19
|
-
def _parent_property_point_to_class(self) -> None:
|
|
20
|
-
for property_ in cast(SheetList[AssetProperty], self.properties):
|
|
21
|
-
for implementation in property_.implementation:
|
|
22
|
-
if (
|
|
23
|
-
isinstance(implementation, AssetEntity)
|
|
24
|
-
and implementation.property_ == AssetFields.parentExternalId
|
|
25
|
-
and not isinstance(property_.value_type, ClassEntity)
|
|
26
|
-
):
|
|
27
|
-
self.issue_list.append(
|
|
28
|
-
PropertyDefinitionError(
|
|
29
|
-
property_.class_,
|
|
30
|
-
"class",
|
|
31
|
-
property_.property_,
|
|
32
|
-
"parentExternalId is only allowed to "
|
|
33
|
-
f"point to a Class not {type(property_.value_type).__name__}",
|
|
34
|
-
)
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
def _circular_dependency(self) -> None:
|
|
38
|
-
from cognite.neat._rules.analysis import AssetAnalysis
|
|
39
|
-
|
|
40
|
-
try:
|
|
41
|
-
_ = AssetAnalysis(cast(AssetRules, self.rules)).class_topological_sort()
|
|
42
|
-
except CycleError as error:
|
|
43
|
-
self.issue_list.append(
|
|
44
|
-
NeatValueError(f"Invalid Asset Hierarchy, circular dependency detected: {error.args[1]}")
|
|
45
|
-
)
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import math
|
|
2
|
-
from collections.abc import Hashable
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from typing import ClassVar
|
|
5
|
-
|
|
6
|
-
from pydantic import Field, field_serializer, field_validator
|
|
7
|
-
from rdflib import URIRef
|
|
8
|
-
|
|
9
|
-
from cognite.neat._constants import DEFAULT_NAMESPACE
|
|
10
|
-
from cognite.neat._rules.models.data_types import DataType
|
|
11
|
-
from cognite.neat._rules.models.entities import ClassEntity, ClassEntityList
|
|
12
|
-
|
|
13
|
-
from ._base_input import InputComponent, InputRules
|
|
14
|
-
from ._base_rules import (
|
|
15
|
-
BaseMetadata,
|
|
16
|
-
BaseRules,
|
|
17
|
-
RoleTypes,
|
|
18
|
-
SheetList,
|
|
19
|
-
SheetRow,
|
|
20
|
-
)
|
|
21
|
-
from ._types import ClassEntityType, InformationPropertyType, StrOrListType
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class DomainMetadata(BaseMetadata):
|
|
25
|
-
role: ClassVar[RoleTypes] = RoleTypes.domain_expert
|
|
26
|
-
creator: StrOrListType
|
|
27
|
-
|
|
28
|
-
def as_identifier(self) -> str:
|
|
29
|
-
return "DomainRules"
|
|
30
|
-
|
|
31
|
-
def get_prefix(self) -> str:
|
|
32
|
-
return "domain"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class DomainProperty(SheetRow):
|
|
36
|
-
class_: ClassEntityType = Field(alias="Class")
|
|
37
|
-
property_: InformationPropertyType = Field(alias="Property")
|
|
38
|
-
name: str | None = Field(alias="Name", default=None)
|
|
39
|
-
description: str | None = Field(alias="Description", default=None)
|
|
40
|
-
value_type: DataType | ClassEntity = Field(alias="Value Type")
|
|
41
|
-
min_count: int | None = Field(alias="Min Count", default=None)
|
|
42
|
-
max_count: int | float | None = Field(alias="Max Count", default=None)
|
|
43
|
-
|
|
44
|
-
def _identifier(self) -> tuple[Hashable, ...]:
|
|
45
|
-
return self.class_, self.property_
|
|
46
|
-
|
|
47
|
-
@field_serializer("max_count", when_used="json-unless-none")
|
|
48
|
-
def serialize_max_count(self, value: int | float | None) -> int | float | None | str:
|
|
49
|
-
if isinstance(value, float) and math.isinf(value):
|
|
50
|
-
return None
|
|
51
|
-
return value
|
|
52
|
-
|
|
53
|
-
@field_validator("max_count", mode="before")
|
|
54
|
-
def parse_max_count(cls, value: int | float | None) -> int | float | None:
|
|
55
|
-
if value is None:
|
|
56
|
-
return float("inf")
|
|
57
|
-
return value
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class DomainClass(SheetRow):
|
|
61
|
-
class_: ClassEntityType = Field(alias="Class")
|
|
62
|
-
name: str | None = Field(alias="Name", default=None)
|
|
63
|
-
description: str | None = Field(None, alias="Description")
|
|
64
|
-
parent: ClassEntityList | None = Field(alias="Parent Class")
|
|
65
|
-
|
|
66
|
-
def _identifier(self) -> tuple[Hashable, ...]:
|
|
67
|
-
return (self.class_,)
|
|
68
|
-
|
|
69
|
-
@field_serializer("parent", when_used="unless-none")
|
|
70
|
-
def serialize_parent(self, value: list[ClassEntity]) -> str:
|
|
71
|
-
return ",".join([str(entry) for entry in value])
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class DomainRules(BaseRules):
|
|
75
|
-
metadata: DomainMetadata = Field(alias="Metadata")
|
|
76
|
-
properties: SheetList[DomainProperty] = Field(alias="Properties")
|
|
77
|
-
classes: SheetList[DomainClass] | None = Field(None, alias="Classes")
|
|
78
|
-
last: "DomainRules | None" = Field(None, alias="Last")
|
|
79
|
-
reference: "DomainRules | None" = Field(None, alias="Reference")
|
|
80
|
-
|
|
81
|
-
@property
|
|
82
|
-
def id_(self) -> URIRef:
|
|
83
|
-
return DEFAULT_NAMESPACE["data-model/verified/domain"]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
@dataclass
|
|
87
|
-
class DomainInputMetadata(InputComponent[DomainMetadata]):
|
|
88
|
-
creator: str
|
|
89
|
-
|
|
90
|
-
@classmethod
|
|
91
|
-
def _get_verified_cls(cls) -> type[DomainMetadata]:
|
|
92
|
-
return DomainMetadata
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@dataclass
|
|
96
|
-
class DomainInputProperty(InputComponent[DomainProperty]):
|
|
97
|
-
class_: str
|
|
98
|
-
property_: str
|
|
99
|
-
value_type: str
|
|
100
|
-
name: str | None = None
|
|
101
|
-
description: str | None = None
|
|
102
|
-
min_count: int | None = None
|
|
103
|
-
max_count: int | float | None = None
|
|
104
|
-
|
|
105
|
-
@classmethod
|
|
106
|
-
def _get_verified_cls(cls) -> type[DomainProperty]:
|
|
107
|
-
return DomainProperty
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@dataclass
|
|
111
|
-
class DomainInputClass(InputComponent[DomainClass]):
|
|
112
|
-
class_: str
|
|
113
|
-
name: str | None = None
|
|
114
|
-
description: str | None = None
|
|
115
|
-
parent: list[str] | None = None
|
|
116
|
-
|
|
117
|
-
@classmethod
|
|
118
|
-
def _get_verified_cls(cls) -> type[DomainClass]:
|
|
119
|
-
return DomainClass
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
@dataclass
|
|
123
|
-
class DomainInputRules(InputRules[DomainRules]):
|
|
124
|
-
metadata: DomainInputMetadata
|
|
125
|
-
properties: list[DomainInputProperty] = field(default_factory=list)
|
|
126
|
-
classes: list[DomainInputClass] = field(default_factory=list)
|
|
127
|
-
last: "DomainInputRules | None" = None
|
|
128
|
-
reference: "DomainInputRules | None" = None
|
|
129
|
-
|
|
130
|
-
@classmethod
|
|
131
|
-
def _get_verified_cls(cls) -> type[DomainRules]:
|
|
132
|
-
return DomainRules
|
|
133
|
-
|
|
134
|
-
@property
|
|
135
|
-
def id_(self) -> URIRef:
|
|
136
|
-
return DEFAULT_NAMESPACE["data-model/unverified/domain"]
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
from collections import Counter, defaultdict
|
|
2
|
-
from collections.abc import Iterator, MutableSequence, Sequence
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Any, Generic, SupportsIndex, TypeVar, cast, get_args, overload
|
|
5
|
-
|
|
6
|
-
import pandas as pd
|
|
7
|
-
from pydantic import BaseModel, GetCoreSchemaHandler, field_validator
|
|
8
|
-
from pydantic_core import core_schema
|
|
9
|
-
from pydantic_core.core_schema import ValidationInfo
|
|
10
|
-
|
|
11
|
-
from cognite.neat._issues.errors import NeatValueError
|
|
12
|
-
from cognite.neat._rules.models._base_rules import ClassRef, PropertyRef
|
|
13
|
-
from cognite.neat._rules.models.entities import ClassEntity, Undefined
|
|
14
|
-
|
|
15
|
-
T_Mapping = TypeVar("T_Mapping", bound=ClassRef | PropertyRef)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Mapping(BaseModel, Generic[T_Mapping]):
|
|
19
|
-
source: T_Mapping
|
|
20
|
-
destination: T_Mapping
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class MappingList(list, MutableSequence[Mapping[T_Mapping]]):
|
|
24
|
-
@classmethod
|
|
25
|
-
def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
|
26
|
-
if args := get_args(source):
|
|
27
|
-
item_type = args[0]
|
|
28
|
-
else:
|
|
29
|
-
# Someone use SheetList without specifying the type
|
|
30
|
-
raise TypeError("SheetList must be used with a type argument, e.g., SheetList[InformationProperty]")
|
|
31
|
-
|
|
32
|
-
instance_schema = core_schema.is_instance_schema(cls)
|
|
33
|
-
sequence_row_schema = handler.generate_schema(Sequence[item_type]) # type: ignore[valid-type]
|
|
34
|
-
|
|
35
|
-
non_instance_schema = core_schema.no_info_after_validator_function(MappingList, sequence_row_schema)
|
|
36
|
-
return core_schema.union_schema([instance_schema, non_instance_schema])
|
|
37
|
-
|
|
38
|
-
def as_destination_by_source(self) -> dict[T_Mapping, T_Mapping]:
|
|
39
|
-
return {mapping.source: mapping.destination for mapping in self}
|
|
40
|
-
|
|
41
|
-
def to_pandas(self, drop_na_columns: bool = True, include: list[str] | None = None) -> pd.DataFrame:
|
|
42
|
-
"""Converts ResourceDict to pandas DataFrame."""
|
|
43
|
-
df = pd.DataFrame([entity.model_dump() for entity in self])
|
|
44
|
-
if drop_na_columns:
|
|
45
|
-
df = df.dropna(axis=1, how="all")
|
|
46
|
-
if include is not None:
|
|
47
|
-
df = df[include]
|
|
48
|
-
return df
|
|
49
|
-
|
|
50
|
-
def _repr_html_(self) -> str:
|
|
51
|
-
"""Returns HTML representation of ResourceDict."""
|
|
52
|
-
return self.to_pandas(drop_na_columns=True)._repr_html_() # type: ignore[operator]
|
|
53
|
-
|
|
54
|
-
# Implemented to get correct type hints
|
|
55
|
-
def __iter__(self) -> Iterator[Mapping[T_Mapping]]:
|
|
56
|
-
return super().__iter__()
|
|
57
|
-
|
|
58
|
-
@overload
|
|
59
|
-
def __getitem__(self, index: SupportsIndex) -> Mapping[T_Mapping]: ...
|
|
60
|
-
|
|
61
|
-
@overload
|
|
62
|
-
def __getitem__(self, index: slice) -> "MappingList[T_Mapping]": ...
|
|
63
|
-
|
|
64
|
-
def __getitem__(self, index: SupportsIndex | slice, /) -> "Mapping[T_Mapping] | MappingList[T_Mapping]":
|
|
65
|
-
if isinstance(index, slice):
|
|
66
|
-
return MappingList[T_Mapping](super().__getitem__(index))
|
|
67
|
-
return super().__getitem__(index)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class RuleMapping(BaseModel):
|
|
71
|
-
properties: MappingList[PropertyRef]
|
|
72
|
-
classes: MappingList[ClassRef]
|
|
73
|
-
|
|
74
|
-
@field_validator("properties", "classes", mode="before")
|
|
75
|
-
def as_mapping_list(cls, value: Sequence[Any], info: ValidationInfo) -> Any:
|
|
76
|
-
if isinstance(value, Sequence) and not isinstance(value, MappingList):
|
|
77
|
-
annotation = cast(type, cls.model_fields[info.field_name].annotation) # type: ignore[index]
|
|
78
|
-
ref_cls = get_args(annotation)[0]
|
|
79
|
-
return annotation([Mapping[ref_cls].model_validate(item) for item in value]) # type: ignore[valid-type, index]
|
|
80
|
-
return value
|
|
81
|
-
|
|
82
|
-
@classmethod
|
|
83
|
-
def load_spreadsheet(
|
|
84
|
-
cls, path: str | Path, source_prefix: str | None = None, destination_prefix: str | None = None
|
|
85
|
-
) -> "RuleMapping":
|
|
86
|
-
"""Loads mapping from Excel spreadsheet.
|
|
87
|
-
|
|
88
|
-
This method expects four columns in the spreadsheet. The first two columns are the source class and
|
|
89
|
-
property, and the last two columns are the destination class and property. The method will create
|
|
90
|
-
a mapping for each row in the spreadsheet.
|
|
91
|
-
|
|
92
|
-
The class mapping will be inferred from the property mappings. If a source class has multiple
|
|
93
|
-
destination classes, the most common destination class will be used.
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
path: Path to Excel spreadsheet.
|
|
97
|
-
source_prefix: Default prefix for source classes.
|
|
98
|
-
destination_prefix: Default prefix for destination classes.
|
|
99
|
-
|
|
100
|
-
Returns:
|
|
101
|
-
Mapping object.
|
|
102
|
-
|
|
103
|
-
"""
|
|
104
|
-
df = pd.read_excel(path).dropna(axis=1, how="all")
|
|
105
|
-
properties = MappingList[PropertyRef]()
|
|
106
|
-
destination_classes_by_source: dict[ClassEntity, Counter[ClassEntity]] = defaultdict(Counter)
|
|
107
|
-
for _, row in df.iterrows():
|
|
108
|
-
if len(row) < 4:
|
|
109
|
-
raise NeatValueError(f"Row {row} is not valid. Expected 4 columns, got {len(row)}")
|
|
110
|
-
|
|
111
|
-
if any(pd.isna(row.iloc[:4])):
|
|
112
|
-
continue
|
|
113
|
-
source_class, source_property, destination_class, destination_property = row.iloc[:4]
|
|
114
|
-
source_entity = ClassEntity.load(source_class, prefix=source_prefix or Undefined)
|
|
115
|
-
destination_entity = ClassEntity.load(destination_class, prefix=destination_prefix or Undefined)
|
|
116
|
-
properties.append(
|
|
117
|
-
Mapping(
|
|
118
|
-
source=PropertyRef(Class=source_entity, Property=source_property),
|
|
119
|
-
destination=PropertyRef(Class=destination_entity, Property=destination_property),
|
|
120
|
-
)
|
|
121
|
-
)
|
|
122
|
-
destination_classes_by_source[source_entity][destination_entity] += 1
|
|
123
|
-
|
|
124
|
-
classes = MappingList[ClassRef]()
|
|
125
|
-
for source_entity, destination_classes in destination_classes_by_source.items():
|
|
126
|
-
destination_entity = destination_classes.most_common(1)[0][0]
|
|
127
|
-
classes.append(
|
|
128
|
-
Mapping(source=ClassRef(Class=source_entity), destination=ClassRef(Class=destination_entity))
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
return cls(properties=properties, classes=classes)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
from ._base import ResourceLoader
|
|
2
|
-
from ._data_modeling import (
|
|
3
|
-
ContainerLoader,
|
|
4
|
-
DataModelingLoader,
|
|
5
|
-
DataModelLoader,
|
|
6
|
-
SpaceLoader,
|
|
7
|
-
ViewLoader,
|
|
8
|
-
)
|
|
9
|
-
from ._ingestion import (
|
|
10
|
-
RawDatabaseLoader,
|
|
11
|
-
RawTableLoader,
|
|
12
|
-
TransformationLoader,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
__all__ = [
|
|
16
|
-
"DataModelingLoader",
|
|
17
|
-
"ContainerLoader",
|
|
18
|
-
"DataModelLoader",
|
|
19
|
-
"ResourceLoader",
|
|
20
|
-
"SpaceLoader",
|
|
21
|
-
"ViewLoader",
|
|
22
|
-
"TransformationLoader",
|
|
23
|
-
"RawTableLoader",
|
|
24
|
-
"RawDatabaseLoader",
|
|
25
|
-
]
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
from collections.abc import Sequence
|
|
3
|
-
from typing import Generic, TypeVar
|
|
4
|
-
|
|
5
|
-
from cognite.client import CogniteClient
|
|
6
|
-
from cognite.client.data_classes._base import (
|
|
7
|
-
T_CogniteResourceList,
|
|
8
|
-
T_WritableCogniteResource,
|
|
9
|
-
T_WriteClass,
|
|
10
|
-
WriteableCogniteResourceList,
|
|
11
|
-
)
|
|
12
|
-
from cognite.client.utils.useful_types import SequenceNotStr
|
|
13
|
-
|
|
14
|
-
from cognite.neat._shared import T_ID
|
|
15
|
-
|
|
16
|
-
T_WritableCogniteResourceList = TypeVar("T_WritableCogniteResourceList", bound=WriteableCogniteResourceList)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class ResourceLoader(
|
|
20
|
-
ABC,
|
|
21
|
-
Generic[T_ID, T_WriteClass, T_WritableCogniteResource, T_CogniteResourceList, T_WritableCogniteResourceList],
|
|
22
|
-
):
|
|
23
|
-
resource_name: str
|
|
24
|
-
|
|
25
|
-
def __init__(self, client: CogniteClient) -> None:
|
|
26
|
-
self.client = client
|
|
27
|
-
|
|
28
|
-
@classmethod
|
|
29
|
-
@abstractmethod
|
|
30
|
-
def get_id(cls, item: T_WriteClass | T_WritableCogniteResource | dict | T_ID) -> T_ID:
|
|
31
|
-
raise NotImplementedError
|
|
32
|
-
|
|
33
|
-
@classmethod
|
|
34
|
-
def get_ids(cls, items: Sequence[T_WriteClass | T_WritableCogniteResource]) -> list[T_ID]:
|
|
35
|
-
return [cls.get_id(item) for item in items]
|
|
36
|
-
|
|
37
|
-
@abstractmethod
|
|
38
|
-
def create(self, items: Sequence[T_WriteClass]) -> T_WritableCogniteResourceList:
|
|
39
|
-
raise NotImplementedError
|
|
40
|
-
|
|
41
|
-
@abstractmethod
|
|
42
|
-
def retrieve(self, ids: SequenceNotStr[T_ID]) -> T_WritableCogniteResourceList:
|
|
43
|
-
raise NotImplementedError
|
|
44
|
-
|
|
45
|
-
@abstractmethod
|
|
46
|
-
def update(self, items: Sequence[T_WriteClass]) -> T_WritableCogniteResourceList:
|
|
47
|
-
raise NotImplementedError
|
|
48
|
-
|
|
49
|
-
@abstractmethod
|
|
50
|
-
def delete(self, ids: SequenceNotStr[T_ID]) -> list[T_ID]:
|
|
51
|
-
raise NotImplementedError
|
|
52
|
-
|
|
53
|
-
def are_equal(self, local: T_WriteClass, remote: T_WritableCogniteResource) -> bool:
|
|
54
|
-
return local == remote.as_write()
|