cognite-neat 0.98.0__py3-none-any.whl → 0.99.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_client/__init__.py +4 -0
- cognite/neat/_client/_api/data_modeling_loaders.py +585 -0
- cognite/neat/_client/_api/schema.py +111 -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/_client/data_classes/schema.py +495 -0
- cognite/neat/_constants.py +27 -4
- 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 +25 -14
- 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/_rdf2dms.py +2 -2
- cognite/neat/_graph/queries/_base.py +17 -1
- cognite/neat/_graph/transformers/_classic_cdf.py +74 -147
- cognite/neat/_graph/transformers/_prune_graph.py +1 -1
- cognite/neat/_graph/transformers/_rdfpath.py +1 -1
- cognite/neat/_issues/_base.py +26 -17
- cognite/neat/_issues/errors/__init__.py +4 -2
- cognite/neat/_issues/errors/_external.py +7 -0
- cognite/neat/_issues/errors/_properties.py +2 -7
- cognite/neat/_issues/errors/_resources.py +1 -1
- cognite/neat/_issues/warnings/__init__.py +8 -0
- cognite/neat/_issues/warnings/_external.py +16 -0
- cognite/neat/_issues/warnings/_properties.py +16 -0
- cognite/neat/_issues/warnings/_resources.py +26 -2
- cognite/neat/_issues/warnings/user_modeling.py +4 -4
- cognite/neat/_rules/_constants.py +8 -11
- cognite/neat/_rules/analysis/_base.py +8 -4
- cognite/neat/_rules/exporters/_base.py +3 -4
- cognite/neat/_rules/exporters/_rules2dms.py +33 -46
- cognite/neat/_rules/importers/__init__.py +1 -3
- cognite/neat/_rules/importers/_base.py +1 -1
- cognite/neat/_rules/importers/_dms2rules.py +6 -29
- cognite/neat/_rules/importers/_rdf/__init__.py +5 -0
- cognite/neat/_rules/importers/_rdf/_base.py +34 -11
- cognite/neat/_rules/importers/_rdf/_imf2rules.py +91 -0
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +43 -35
- cognite/neat/_rules/importers/_rdf/_owl2rules.py +80 -0
- cognite/neat/_rules/importers/_rdf/_shared.py +138 -441
- cognite/neat/_rules/models/__init__.py +1 -1
- cognite/neat/_rules/models/_base_rules.py +22 -12
- cognite/neat/_rules/models/dms/__init__.py +4 -2
- cognite/neat/_rules/models/dms/_exporter.py +45 -48
- cognite/neat/_rules/models/dms/_rules.py +20 -17
- cognite/neat/_rules/models/dms/_rules_input.py +52 -8
- cognite/neat/_rules/models/dms/_validation.py +391 -119
- cognite/neat/_rules/models/entities/_single_value.py +32 -4
- cognite/neat/_rules/models/information/__init__.py +2 -0
- cognite/neat/_rules/models/information/_rules.py +0 -67
- cognite/neat/_rules/models/information/_validation.py +9 -9
- 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 +343 -0
- cognite/neat/_rules/transformers/__init__.py +2 -2
- cognite/neat/_rules/transformers/_converters.py +110 -11
- cognite/neat/_rules/transformers/_mapping.py +105 -30
- cognite/neat/_rules/transformers/_pipelines.py +1 -1
- cognite/neat/_rules/transformers/_verification.py +31 -3
- cognite/neat/_session/_base.py +24 -8
- cognite/neat/_session/_drop.py +35 -0
- cognite/neat/_session/_inspect.py +17 -5
- cognite/neat/_session/_mapping.py +39 -0
- cognite/neat/_session/_prepare.py +219 -23
- cognite/neat/_session/_read.py +49 -12
- cognite/neat/_session/_to.py +8 -5
- cognite/neat/_session/exceptions.py +4 -0
- cognite/neat/_store/_base.py +27 -24
- cognite/neat/_utils/rdf_.py +34 -5
- cognite/neat/_version.py +1 -1
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +5 -88
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +3 -14
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +6 -7
- {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/METADATA +3 -3
- {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/RECORD +87 -92
- cognite/neat/_rules/importers/_rdf/_imf2rules/__init__.py +0 -3
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +0 -86
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +0 -29
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +0 -130
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2rules.py +0 -154
- cognite/neat/_rules/importers/_rdf/_owl2rules/__init__.py +0 -3
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +0 -58
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +0 -65
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +0 -59
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2rules.py +0 -39
- cognite/neat/_rules/models/dms/_schema.py +0 -1101
- 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.98.0.dist-info → cognite_neat-0.99.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/WHEEL +0 -0
- {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from abc import ABC
|
|
2
3
|
from collections import defaultdict
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
from typing import Any, ClassVar, Literal
|
|
3
6
|
|
|
7
|
+
from cognite.neat._issues.errors import NeatValueError
|
|
8
|
+
from cognite.neat._issues.warnings import NeatValueWarning, PropertyOverwritingWarning
|
|
4
9
|
from cognite.neat._rules._shared import JustRules, OutRules
|
|
5
|
-
from cognite.neat._rules.models import DMSRules,
|
|
6
|
-
from cognite.neat._rules.models.
|
|
7
|
-
from cognite.neat._rules.models.dms import DMSProperty
|
|
8
|
-
from cognite.neat._rules.models.entities import
|
|
9
|
-
from cognite.neat._rules.models.information import InformationClass
|
|
10
|
-
from cognite.neat._rules.models.mapping import RuleMapping
|
|
10
|
+
from cognite.neat._rules.models import DMSRules, SheetList
|
|
11
|
+
from cognite.neat._rules.models.data_types import Enum
|
|
12
|
+
from cognite.neat._rules.models.dms import DMSEnum, DMSProperty, DMSView
|
|
13
|
+
from cognite.neat._rules.models.entities import ViewEntity
|
|
11
14
|
|
|
12
15
|
from ._base import RulesTransformer
|
|
13
16
|
|
|
@@ -96,7 +99,7 @@ class MapOneToOne(MapOntoTransformers):
|
|
|
96
99
|
return JustRules(solution)
|
|
97
100
|
|
|
98
101
|
|
|
99
|
-
class RuleMapper(RulesTransformer[
|
|
102
|
+
class RuleMapper(RulesTransformer[DMSRules, DMSRules]):
|
|
100
103
|
"""Maps properties and classes using the given mapping.
|
|
101
104
|
|
|
102
105
|
**Note**: This transformer mutates the input rules.
|
|
@@ -106,30 +109,102 @@ class RuleMapper(RulesTransformer[InformationRules, InformationRules]):
|
|
|
106
109
|
|
|
107
110
|
"""
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
_mapping_fields: ClassVar[frozenset[str]] = frozenset(
|
|
113
|
+
["connection", "value_type", "nullable", "immutable", "is_list", "default", "index", "constraint"]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def __init__(self, mapping: DMSRules, data_type_conflict: Literal["overwrite"] = "overwrite") -> None:
|
|
110
117
|
self.mapping = mapping
|
|
118
|
+
self.data_type_conflict = data_type_conflict
|
|
119
|
+
|
|
120
|
+
@cached_property
|
|
121
|
+
def _view_by_entity_id(self) -> dict[str, DMSView]:
|
|
122
|
+
return {view.view.external_id: view for view in self.mapping.views}
|
|
111
123
|
|
|
112
|
-
|
|
124
|
+
@cached_property
|
|
125
|
+
def _property_by_view_property(self) -> dict[tuple[str, str], DMSProperty]:
|
|
126
|
+
return {(prop.view.external_id, prop.view_property): prop for prop in self.mapping.properties}
|
|
127
|
+
|
|
128
|
+
def transform(self, rules: DMSRules | OutRules[DMSRules]) -> JustRules[DMSRules]:
|
|
129
|
+
if self.data_type_conflict != "overwrite":
|
|
130
|
+
raise NeatValueError(f"Invalid data_type_conflict: {self.data_type_conflict}")
|
|
113
131
|
input_rules = self._to_rules(rules)
|
|
132
|
+
new_rules = input_rules.model_copy(deep=True)
|
|
133
|
+
new_rules.metadata.version += "_mapped"
|
|
114
134
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
for view in new_rules.views:
|
|
136
|
+
if mapping_view := self._view_by_entity_id.get(view.view.external_id):
|
|
137
|
+
view.implements = mapping_view.implements
|
|
138
|
+
|
|
139
|
+
for prop in new_rules.properties:
|
|
140
|
+
key = (prop.view.external_id, prop.view_property)
|
|
141
|
+
if key not in self._property_by_view_property:
|
|
142
|
+
continue
|
|
143
|
+
mapping_prop = self._property_by_view_property[key]
|
|
144
|
+
to_overwrite, conflicts = self._find_overwrites(prop, mapping_prop)
|
|
145
|
+
if conflicts and self.data_type_conflict == "overwrite":
|
|
146
|
+
warnings.warn(
|
|
147
|
+
PropertyOverwritingWarning(prop.view.as_id(), "view", prop.view_property, tuple(conflicts)),
|
|
148
|
+
stacklevel=2,
|
|
149
|
+
)
|
|
150
|
+
elif conflicts:
|
|
151
|
+
raise NeatValueError(f"Conflicting properties for {prop.view}.{prop.view_property}: {conflicts}")
|
|
152
|
+
for field_name, value in to_overwrite.items():
|
|
153
|
+
setattr(prop, field_name, value)
|
|
154
|
+
prop.container = mapping_prop.container
|
|
155
|
+
prop.container_property = mapping_prop.container_property
|
|
156
|
+
|
|
157
|
+
# Add missing views used as value types
|
|
158
|
+
existing_views = {view.view for view in new_rules.views}
|
|
159
|
+
new_value_types = {
|
|
160
|
+
prop.value_type
|
|
161
|
+
for prop in new_rules.properties
|
|
162
|
+
if isinstance(prop.value_type, ViewEntity) and prop.value_type not in existing_views
|
|
163
|
+
}
|
|
164
|
+
for new_value_type in new_value_types:
|
|
165
|
+
if mapping_view := self._view_by_entity_id.get(new_value_type.external_id):
|
|
166
|
+
new_rules.views.append(mapping_view)
|
|
167
|
+
else:
|
|
168
|
+
warnings.warn(NeatValueWarning(f"View {new_value_type} not found in mapping"), stacklevel=2)
|
|
169
|
+
|
|
170
|
+
# Add missing enums
|
|
171
|
+
existing_enum_collections = {item.collection for item in new_rules.enum or []}
|
|
172
|
+
new_enums = {
|
|
173
|
+
prop.value_type.collection
|
|
174
|
+
for prop in new_rules.properties
|
|
175
|
+
if isinstance(prop.value_type, Enum) and prop.value_type.collection not in existing_enum_collections
|
|
176
|
+
}
|
|
177
|
+
if new_enums:
|
|
178
|
+
new_rules.enum = new_rules.enum or SheetList[DMSEnum]([])
|
|
179
|
+
for item in self.mapping.enum or []:
|
|
180
|
+
if item.collection in new_enums:
|
|
181
|
+
new_rules.enum.append(item)
|
|
182
|
+
|
|
183
|
+
return JustRules(new_rules)
|
|
184
|
+
|
|
185
|
+
def _find_overwrites(self, prop: DMSProperty, mapping_prop: DMSProperty) -> tuple[dict[str, Any], list[str]]:
|
|
186
|
+
"""Finds the properties that need to be overwritten and returns them.
|
|
187
|
+
|
|
188
|
+
In addition, conflicting properties are returned. Note that overwriting properties that are
|
|
189
|
+
originally None is not considered a conflict. Thus, you can have properties to overwrite but no
|
|
190
|
+
conflicts.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
prop: The property to compare.
|
|
194
|
+
mapping_prop: The property to compare against.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
A tuple with the properties to overwrite and the conflicting properties.
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
to_overwrite: dict[str, Any] = {}
|
|
201
|
+
conflicts: list[str] = []
|
|
202
|
+
for field_name in self._mapping_fields:
|
|
203
|
+
mapping_value = getattr(mapping_prop, field_name)
|
|
204
|
+
source_value = getattr(prop, field_name)
|
|
205
|
+
if mapping_value != source_value:
|
|
206
|
+
to_overwrite[field_name] = mapping_value
|
|
207
|
+
if source_value is not None:
|
|
208
|
+
# These are used for warnings so we use the alias to make it more readable for the user
|
|
209
|
+
conflicts.append(mapping_prop.model_fields[field_name].alias or field_name)
|
|
210
|
+
return to_overwrite, conflicts
|
|
@@ -23,7 +23,7 @@ class ImporterPipeline(RulesPipeline[InputRules, VerifiedRules]):
|
|
|
23
23
|
|
|
24
24
|
@classmethod
|
|
25
25
|
def _create_pipeline(cls, importer: BaseImporter[InputRules], role: RoleTypes | None = None) -> "ImporterPipeline":
|
|
26
|
-
items: list[RulesTransformer] = [VerifyAnyRules(errors="continue")]
|
|
26
|
+
items: list[RulesTransformer] = [VerifyAnyRules(errors="continue", validate=True)]
|
|
27
27
|
if role is not None:
|
|
28
28
|
out_cls = VERIFIED_RULES_BY_ROLE[role]
|
|
29
29
|
items.append(ConvertToRules(out_cls))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
2
|
from typing import Any, Literal
|
|
3
3
|
|
|
4
|
-
from cognite.neat._issues import IssueList, NeatError, NeatWarning, catch_issues
|
|
4
|
+
from cognite.neat._issues import IssueList, MultiValueError, NeatError, NeatWarning, catch_issues
|
|
5
5
|
from cognite.neat._issues.errors import NeatTypeError
|
|
6
6
|
from cognite.neat._rules._shared import (
|
|
7
7
|
InputRules,
|
|
@@ -18,6 +18,8 @@ from cognite.neat._rules.models import (
|
|
|
18
18
|
InformationInputRules,
|
|
19
19
|
InformationRules,
|
|
20
20
|
)
|
|
21
|
+
from cognite.neat._rules.models.dms import DMSValidation
|
|
22
|
+
from cognite.neat._rules.models.information import InformationValidation
|
|
21
23
|
|
|
22
24
|
from ._base import RulesTransformer
|
|
23
25
|
|
|
@@ -26,9 +28,11 @@ class VerificationTransformer(RulesTransformer[T_InputRules, T_VerifiedRules], A
|
|
|
26
28
|
"""Base class for all verification transformers."""
|
|
27
29
|
|
|
28
30
|
_rules_cls: type[T_VerifiedRules]
|
|
31
|
+
_validation_cls: type
|
|
29
32
|
|
|
30
|
-
def __init__(self, errors: Literal["raise", "continue"]) -> None:
|
|
33
|
+
def __init__(self, errors: Literal["raise", "continue"], validate: bool = True) -> None:
|
|
31
34
|
self.errors = errors
|
|
35
|
+
self.validate = validate
|
|
32
36
|
|
|
33
37
|
def transform(self, rules: T_InputRules | OutRules[T_InputRules]) -> MaybeRules[T_VerifiedRules]:
|
|
34
38
|
issues = IssueList()
|
|
@@ -39,7 +43,18 @@ class VerificationTransformer(RulesTransformer[T_InputRules, T_VerifiedRules], A
|
|
|
39
43
|
verified_rules: T_VerifiedRules | None = None
|
|
40
44
|
with catch_issues(issues, NeatError, NeatWarning, error_args) as future:
|
|
41
45
|
rules_cls = self._get_rules_cls(in_)
|
|
42
|
-
|
|
46
|
+
dumped = in_.dump()
|
|
47
|
+
verified_rules = rules_cls.model_validate(dumped) # type: ignore[assignment]
|
|
48
|
+
if self.validate:
|
|
49
|
+
validation_cls = self._get_validation_cls(verified_rules) # type: ignore[arg-type]
|
|
50
|
+
validation_issues = validation_cls(verified_rules).validate()
|
|
51
|
+
# We need to trigger warnings are raise exceptions such that they are caught by the context manager
|
|
52
|
+
# and processed with the read context
|
|
53
|
+
if validation_issues.warnings:
|
|
54
|
+
validation_issues.trigger_warnings()
|
|
55
|
+
if validation_issues.has_errors:
|
|
56
|
+
verified_rules = None
|
|
57
|
+
raise MultiValueError(validation_issues.errors)
|
|
43
58
|
|
|
44
59
|
if (future.result == "failure" or issues.has_errors or verified_rules is None) and self.errors == "raise":
|
|
45
60
|
raise issues.as_errors()
|
|
@@ -51,17 +66,22 @@ class VerificationTransformer(RulesTransformer[T_InputRules, T_VerifiedRules], A
|
|
|
51
66
|
def _get_rules_cls(self, in_: T_InputRules) -> type[T_VerifiedRules]:
|
|
52
67
|
return self._rules_cls
|
|
53
68
|
|
|
69
|
+
def _get_validation_cls(self, rules: T_VerifiedRules) -> type:
|
|
70
|
+
return self._validation_cls
|
|
71
|
+
|
|
54
72
|
|
|
55
73
|
class VerifyDMSRules(VerificationTransformer[DMSInputRules, DMSRules]):
|
|
56
74
|
"""Class to verify DMS rules."""
|
|
57
75
|
|
|
58
76
|
_rules_cls = DMSRules
|
|
77
|
+
_validation_cls = DMSValidation
|
|
59
78
|
|
|
60
79
|
|
|
61
80
|
class VerifyInformationRules(VerificationTransformer[InformationInputRules, InformationRules]):
|
|
62
81
|
"""Class to verify Information rules."""
|
|
63
82
|
|
|
64
83
|
_rules_cls = InformationRules
|
|
84
|
+
_validation_cls = InformationValidation
|
|
65
85
|
|
|
66
86
|
|
|
67
87
|
class VerifyAnyRules(VerificationTransformer[InputRules, VerifiedRules]):
|
|
@@ -74,3 +94,11 @@ class VerifyAnyRules(VerificationTransformer[InputRules, VerifiedRules]):
|
|
|
74
94
|
return DMSRules
|
|
75
95
|
else:
|
|
76
96
|
raise NeatTypeError(f"Unsupported rules type: {type(in_)}")
|
|
97
|
+
|
|
98
|
+
def _get_validation_cls(self, rules: T_VerifiedRules) -> type:
|
|
99
|
+
if isinstance(rules, InformationRules):
|
|
100
|
+
return InformationValidation
|
|
101
|
+
elif isinstance(rules, DMSRules):
|
|
102
|
+
return DMSValidation
|
|
103
|
+
else:
|
|
104
|
+
raise NeatTypeError(f"Unsupported rules type: {type(rules)}")
|
cognite/neat/_session/_base.py
CHANGED
|
@@ -5,11 +5,14 @@ from cognite.client import CogniteClient
|
|
|
5
5
|
from cognite.client import data_modeling as dm
|
|
6
6
|
|
|
7
7
|
from cognite.neat import _version
|
|
8
|
+
from cognite.neat._client import NeatClient
|
|
8
9
|
from cognite.neat._issues import IssueList, catch_issues
|
|
9
10
|
from cognite.neat._issues.errors import RegexViolationError
|
|
10
11
|
from cognite.neat._rules import importers
|
|
11
12
|
from cognite.neat._rules._shared import ReadRules, VerifiedRules
|
|
12
13
|
from cognite.neat._rules.models import DMSRules
|
|
14
|
+
from cognite.neat._rules.models.dms import DMSValidation
|
|
15
|
+
from cognite.neat._rules.models.information import InformationValidation
|
|
13
16
|
from cognite.neat._rules.models.information._rules import InformationRules
|
|
14
17
|
from cognite.neat._rules.models.information._rules_input import InformationInputRules
|
|
15
18
|
from cognite.neat._rules.transformers import ConvertToRules, VerifyAnyRules
|
|
@@ -18,10 +21,11 @@ from cognite.neat._store._provenance import (
|
|
|
18
21
|
INSTANCES_ENTITY,
|
|
19
22
|
Change,
|
|
20
23
|
)
|
|
21
|
-
from cognite.neat._utils.auth import _CLIENT_NAME
|
|
22
24
|
|
|
23
25
|
from ._collector import _COLLECTOR, Collector
|
|
26
|
+
from ._drop import DropAPI
|
|
24
27
|
from ._inspect import InspectAPI
|
|
28
|
+
from ._mapping import MappingAPI
|
|
25
29
|
from ._prepare import PrepareAPI
|
|
26
30
|
from ._read import ReadAPI
|
|
27
31
|
from ._set import SetAPI
|
|
@@ -41,19 +45,19 @@ class NeatSession:
|
|
|
41
45
|
verbose: bool = True,
|
|
42
46
|
load_engine: Literal["newest", "cache", "skip"] = "cache",
|
|
43
47
|
) -> None:
|
|
44
|
-
self._client = client
|
|
48
|
+
self._client = NeatClient(client) if client else None
|
|
45
49
|
self._verbose = verbose
|
|
46
50
|
self._state = SessionState(store_type=storage)
|
|
47
|
-
self.read = ReadAPI(self._state,
|
|
48
|
-
self.to = ToAPI(self._state,
|
|
49
|
-
self.prepare = PrepareAPI(self._state, verbose)
|
|
51
|
+
self.read = ReadAPI(self._state, self._client, verbose)
|
|
52
|
+
self.to = ToAPI(self._state, self._client, verbose)
|
|
53
|
+
self.prepare = PrepareAPI(self._client, self._state, verbose)
|
|
50
54
|
self.show = ShowAPI(self._state)
|
|
51
55
|
self.set = SetAPI(self._state, verbose)
|
|
52
56
|
self.inspect = InspectAPI(self._state)
|
|
57
|
+
self.mapping = MappingAPI(self._state)
|
|
58
|
+
self.drop = DropAPI(self._state)
|
|
53
59
|
self.opt = OptAPI()
|
|
54
60
|
self.opt._display()
|
|
55
|
-
if self._client is not None and self._client._config is not None:
|
|
56
|
-
self._client._config.client_name = _CLIENT_NAME
|
|
57
61
|
if load_engine != "skip" and (engine_version := load_neat_engine(client, load_engine)):
|
|
58
62
|
print(f"Neat Engine {engine_version} loaded.")
|
|
59
63
|
|
|
@@ -63,9 +67,21 @@ class NeatSession:
|
|
|
63
67
|
|
|
64
68
|
def verify(self) -> IssueList:
|
|
65
69
|
source_id, last_unverified_rule = self._state.data_model.last_unverified_rule
|
|
66
|
-
transformer = VerifyAnyRules("continue")
|
|
70
|
+
transformer = VerifyAnyRules("continue", validate=False)
|
|
67
71
|
start = datetime.now(timezone.utc)
|
|
68
72
|
output = transformer.try_transform(last_unverified_rule)
|
|
73
|
+
if isinstance(output.rules, DMSRules):
|
|
74
|
+
issues = DMSValidation(output.rules, self._client).validate()
|
|
75
|
+
elif isinstance(output.rules, InformationRules):
|
|
76
|
+
issues = InformationValidation(output.rules).validate()
|
|
77
|
+
else:
|
|
78
|
+
raise NeatSessionError("Unsupported rule type")
|
|
79
|
+
if issues.has_errors:
|
|
80
|
+
# This is up for discussion, but I think we should not return rules that
|
|
81
|
+
# only pass the verification but not the validation.
|
|
82
|
+
output.rules = None
|
|
83
|
+
output.issues.extend(issues)
|
|
84
|
+
|
|
69
85
|
end = datetime.now(timezone.utc)
|
|
70
86
|
|
|
71
87
|
if output.rules:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from rdflib import URIRef
|
|
2
|
+
|
|
3
|
+
from ._state import SessionState
|
|
4
|
+
from .exceptions import session_class_wrapper
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from rich import print
|
|
8
|
+
except ImportError:
|
|
9
|
+
...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@session_class_wrapper
|
|
13
|
+
class DropAPI:
|
|
14
|
+
def __init__(self, state: SessionState):
|
|
15
|
+
self._state = state
|
|
16
|
+
|
|
17
|
+
def instances(self, type: str | list[str]) -> None:
|
|
18
|
+
"""Drop instances from the session.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
type: The type of instances to drop.
|
|
22
|
+
"""
|
|
23
|
+
type_list = type if isinstance(type, list) else [type]
|
|
24
|
+
uri_type_type = dict((v, k) for k, v in self._state.instances.store.queries.types.items())
|
|
25
|
+
selected_uri_by_type: dict[URIRef, str] = {}
|
|
26
|
+
for type_item in type_list:
|
|
27
|
+
if type_item not in uri_type_type:
|
|
28
|
+
print(f"Type {type_item} not found.")
|
|
29
|
+
selected_uri_by_type[uri_type_type[type_item]] = type_item
|
|
30
|
+
|
|
31
|
+
result = self._state.instances.store.queries.drop_types(list(selected_uri_by_type.keys()))
|
|
32
|
+
|
|
33
|
+
for type_uri, count in result.items():
|
|
34
|
+
print(f"Dropped {count} instances of type {selected_uri_by_type[type_uri]}")
|
|
35
|
+
return None
|
|
@@ -11,6 +11,13 @@ from cognite.neat._utils.upload import UploadResult, UploadResultCore, UploadRes
|
|
|
11
11
|
from ._state import SessionState
|
|
12
12
|
from .exceptions import session_class_wrapper
|
|
13
13
|
|
|
14
|
+
try:
|
|
15
|
+
from rich.markdown import Markdown as RichMarkdown
|
|
16
|
+
|
|
17
|
+
RICH_AVAILABLE = True
|
|
18
|
+
except ImportError:
|
|
19
|
+
RICH_AVAILABLE = False
|
|
20
|
+
|
|
14
21
|
|
|
15
22
|
@session_class_wrapper
|
|
16
23
|
class InspectAPI:
|
|
@@ -61,14 +68,19 @@ class InspectIssues:
|
|
|
61
68
|
closest_match = set(difflib.get_close_matches(search, unique_types))
|
|
62
69
|
issues = IssueList([issue for issue in issues if type(issue).__name__ in closest_match])
|
|
63
70
|
|
|
71
|
+
issue_str = "\n".join(
|
|
72
|
+
[f" * **{type(issue).__name__}**: {issue.as_message(include_type=False)}" for issue in issues]
|
|
73
|
+
)
|
|
74
|
+
markdown_str = f"### {len(issues)} issues found\n\n{issue_str}"
|
|
75
|
+
|
|
64
76
|
if IN_NOTEBOOK:
|
|
65
77
|
from IPython.display import Markdown, display
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
display(Markdown(markdown_str))
|
|
80
|
+
elif RICH_AVAILABLE:
|
|
81
|
+
from rich import print
|
|
82
|
+
|
|
83
|
+
print(RichMarkdown(markdown_str))
|
|
72
84
|
|
|
73
85
|
if return_dataframe:
|
|
74
86
|
return issues.to_pandas()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
|
|
3
|
+
from cognite.neat._rules.models.mapping import load_classic_to_core_mapping
|
|
4
|
+
from cognite.neat._rules.transformers import RuleMapper
|
|
5
|
+
from cognite.neat._store._provenance import Change
|
|
6
|
+
|
|
7
|
+
from ._state import SessionState
|
|
8
|
+
from .exceptions import session_class_wrapper
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@session_class_wrapper
|
|
12
|
+
class MappingAPI:
|
|
13
|
+
def __init__(self, state: SessionState):
|
|
14
|
+
self._state = state
|
|
15
|
+
|
|
16
|
+
def classic_to_core(self, org_name: str) -> None:
|
|
17
|
+
"""Map classic types to core types.
|
|
18
|
+
|
|
19
|
+
Note this automatically creates an extended CogniteCore model.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
source_id, rules = self._state.data_model.last_verified_dms_rules
|
|
23
|
+
|
|
24
|
+
start = datetime.now(timezone.utc)
|
|
25
|
+
transformer = RuleMapper(load_classic_to_core_mapping(org_name, rules.metadata.space, rules.metadata.version))
|
|
26
|
+
output = transformer.transform(rules)
|
|
27
|
+
end = datetime.now(timezone.utc)
|
|
28
|
+
|
|
29
|
+
change = Change.from_rules_activity(
|
|
30
|
+
output,
|
|
31
|
+
transformer.agent,
|
|
32
|
+
start,
|
|
33
|
+
end,
|
|
34
|
+
"Mapping classic to core",
|
|
35
|
+
self._state.data_model.provenance.source_entity(source_id)
|
|
36
|
+
or self._state.data_model.provenance.target_entity(source_id),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
self._state.data_model.write(output.rules, change)
|