cognite-neat 0.108.0__py3-none-any.whl → 0.109.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/_constants.py +1 -1
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +8 -4
- cognite/neat/_graph/queries/_base.py +4 -0
- cognite/neat/_graph/transformers/__init__.py +3 -3
- cognite/neat/_graph/transformers/_base.py +4 -4
- cognite/neat/_graph/transformers/_classic_cdf.py +13 -13
- cognite/neat/_graph/transformers/_prune_graph.py +3 -3
- cognite/neat/_graph/transformers/_rdfpath.py +3 -4
- cognite/neat/_graph/transformers/_value_type.py +23 -16
- cognite/neat/_issues/errors/__init__.py +2 -0
- cognite/neat/_issues/errors/_external.py +8 -0
- cognite/neat/_issues/warnings/_resources.py +1 -1
- cognite/neat/_rules/exporters/_rules2yaml.py +1 -1
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +179 -118
- cognite/neat/_rules/models/_base_rules.py +9 -8
- cognite/neat/_rules/models/dms/_exporter.py +5 -4
- cognite/neat/_rules/transformers/__init__.py +4 -3
- cognite/neat/_rules/transformers/_base.py +6 -1
- cognite/neat/_rules/transformers/_converters.py +436 -361
- cognite/neat/_rules/transformers/_mapping.py +4 -4
- cognite/neat/_session/_base.py +71 -69
- cognite/neat/_session/_create.py +133 -0
- cognite/neat/_session/_drop.py +55 -1
- cognite/neat/_session/_fix.py +28 -0
- cognite/neat/_session/_inspect.py +19 -5
- cognite/neat/_session/_mapping.py +8 -8
- cognite/neat/_session/_prepare.py +3 -247
- cognite/neat/_session/_read.py +78 -4
- cognite/neat/_session/_set.py +34 -12
- cognite/neat/_session/_show.py +14 -41
- cognite/neat/_session/_state.py +48 -51
- cognite/neat/_session/_to.py +7 -3
- cognite/neat/_session/exceptions.py +7 -1
- cognite/neat/_store/_graph_store.py +14 -13
- cognite/neat/_store/_provenance.py +36 -20
- cognite/neat/_store/_rules_store.py +172 -293
- cognite/neat/_store/exceptions.py +40 -4
- cognite/neat/_utils/auth.py +4 -2
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/RECORD +44 -42
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/entry_points.txt +0 -0
|
@@ -13,10 +13,10 @@ from cognite.neat._rules.models.data_types import Enum
|
|
|
13
13
|
from cognite.neat._rules.models.dms import DMSContainer, DMSEnum, DMSProperty
|
|
14
14
|
from cognite.neat._rules.models.entities import ClassEntity, ContainerEntity, ViewEntity
|
|
15
15
|
|
|
16
|
-
from ._base import
|
|
16
|
+
from ._base import VerifiedRulesTransformer
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class MapOntoTransformers(
|
|
19
|
+
class MapOntoTransformers(VerifiedRulesTransformer[DMSRules, DMSRules], ABC):
|
|
20
20
|
"""Base class for transformers that map one rule onto another."""
|
|
21
21
|
|
|
22
22
|
...
|
|
@@ -100,7 +100,7 @@ class MapOneToOne(MapOntoTransformers):
|
|
|
100
100
|
return solution
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
class RuleMapper(
|
|
103
|
+
class RuleMapper(VerifiedRulesTransformer[DMSRules, DMSRules]):
|
|
104
104
|
"""Maps properties and classes using the given mapping.
|
|
105
105
|
|
|
106
106
|
Args:
|
|
@@ -232,7 +232,7 @@ class RuleMapper(RulesTransformer[DMSRules, DMSRules]):
|
|
|
232
232
|
return f"Mapping to {self.mapping.metadata.as_data_model_id()!r}."
|
|
233
233
|
|
|
234
234
|
|
|
235
|
-
class AsParentPropertyId(
|
|
235
|
+
class AsParentPropertyId(VerifiedRulesTransformer[DMSRules, DMSRules]):
|
|
236
236
|
"""Looks up all view properties that map to the same container property,
|
|
237
237
|
and changes the child view property id to match the parent property id.
|
|
238
238
|
"""
|
cognite/neat/_session/_base.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from pathlib import Path
|
|
1
2
|
from typing import Literal
|
|
2
3
|
|
|
3
4
|
from cognite.client import CogniteClient
|
|
@@ -9,22 +10,21 @@ from cognite.neat._issues import IssueList
|
|
|
9
10
|
from cognite.neat._issues.errors import RegexViolationError
|
|
10
11
|
from cognite.neat._issues.errors._general import NeatImportError
|
|
11
12
|
from cognite.neat._rules import importers
|
|
12
|
-
from cognite.neat._rules.models
|
|
13
|
+
from cognite.neat._rules.models import DMSRules
|
|
13
14
|
from cognite.neat._rules.models.information._rules import InformationRules
|
|
14
15
|
from cognite.neat._rules.transformers import (
|
|
15
|
-
ConversionTransformer,
|
|
16
|
-
ConvertToRules,
|
|
17
16
|
InformationToDMS,
|
|
18
17
|
MergeDMSRules,
|
|
19
18
|
MergeInformationRules,
|
|
20
|
-
VerifyAnyRules,
|
|
21
19
|
VerifyInformationRules,
|
|
22
20
|
)
|
|
23
|
-
from cognite.neat._store._rules_store import
|
|
21
|
+
from cognite.neat._store._rules_store import RulesEntity
|
|
24
22
|
from cognite.neat._utils.auxiliary import local_import
|
|
25
23
|
|
|
26
24
|
from ._collector import _COLLECTOR, Collector
|
|
25
|
+
from ._create import CreateAPI
|
|
27
26
|
from ._drop import DropAPI
|
|
27
|
+
from ._fix import FixAPI
|
|
28
28
|
from ._inspect import InspectAPI
|
|
29
29
|
from ._mapping import MappingAPI
|
|
30
30
|
from ._prepare import PrepareAPI
|
|
@@ -34,7 +34,7 @@ from ._show import ShowAPI
|
|
|
34
34
|
from ._state import SessionState
|
|
35
35
|
from ._to import ToAPI
|
|
36
36
|
from .engine import load_neat_engine
|
|
37
|
-
from .exceptions import
|
|
37
|
+
from .exceptions import session_class_wrapper
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
@session_class_wrapper
|
|
@@ -81,22 +81,26 @@ class NeatSession:
|
|
|
81
81
|
self,
|
|
82
82
|
client: CogniteClient | None = None,
|
|
83
83
|
storage: Literal["memory", "oxigraph"] | None = None,
|
|
84
|
+
storage_path: str | None = None,
|
|
84
85
|
verbose: bool = True,
|
|
85
86
|
load_engine: Literal["newest", "cache", "skip"] = "cache",
|
|
86
87
|
) -> None:
|
|
87
88
|
self._verbose = verbose
|
|
88
89
|
self._state = SessionState(
|
|
89
90
|
store_type=storage or self._select_most_performant_store(),
|
|
91
|
+
storage_path=Path(storage_path) if storage_path else None,
|
|
90
92
|
client=NeatClient(client) if client else None,
|
|
91
93
|
)
|
|
92
94
|
self.read = ReadAPI(self._state, verbose)
|
|
93
95
|
self.to = ToAPI(self._state, verbose)
|
|
96
|
+
self.fix = FixAPI(self._state, verbose)
|
|
94
97
|
self.prepare = PrepareAPI(self._state, verbose)
|
|
95
98
|
self.show = ShowAPI(self._state)
|
|
96
99
|
self.set = SetAPI(self._state, verbose)
|
|
97
100
|
self.inspect = InspectAPI(self._state)
|
|
98
101
|
self.mapping = MappingAPI(self._state)
|
|
99
102
|
self.drop = DropAPI(self._state)
|
|
103
|
+
self.create = CreateAPI(self._state)
|
|
100
104
|
self.opt = OptAPI()
|
|
101
105
|
self.opt._display()
|
|
102
106
|
if load_engine != "skip" and (engine_version := load_neat_engine(client, load_engine)):
|
|
@@ -141,46 +145,27 @@ class NeatSession:
|
|
|
141
145
|
neat.verify()
|
|
142
146
|
```
|
|
143
147
|
"""
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if not issues.has_errors:
|
|
147
|
-
rules = self._state.rule_store.last_verified_rule
|
|
148
|
-
if isinstance(rules, InformationRules):
|
|
149
|
-
self._state.instances.store.add_rules(rules)
|
|
148
|
+
print("This action has no effect. Neat no longer supports unverified data models.")
|
|
149
|
+
return IssueList()
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
print("You can inspect the issues with the .inspect.issues(...) method.")
|
|
153
|
-
return issues
|
|
154
|
-
|
|
155
|
-
def convert(self, target: Literal["dms", "information"]) -> IssueList:
|
|
151
|
+
def convert(self, reserved_properties: Literal["error", "warning"] = "warning") -> IssueList:
|
|
156
152
|
"""Converts the last verified data model to the target type.
|
|
157
153
|
|
|
158
154
|
Args:
|
|
159
|
-
|
|
155
|
+
reserved_properties: What to do with reserved properties. Can be "error" or "warning".
|
|
160
156
|
|
|
161
157
|
Example:
|
|
162
158
|
Convert to DMS rules
|
|
163
159
|
```python
|
|
164
|
-
neat.convert(
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
Example:
|
|
168
|
-
Convert to Information rules
|
|
169
|
-
```python
|
|
170
|
-
neat.convert(target="information")
|
|
160
|
+
neat.convert()
|
|
171
161
|
```
|
|
172
162
|
"""
|
|
173
|
-
converter
|
|
174
|
-
|
|
175
|
-
converter = InformationToDMS()
|
|
176
|
-
elif target == "information":
|
|
177
|
-
converter = ConvertToRules(InformationRules)
|
|
178
|
-
else:
|
|
179
|
-
raise NeatSessionError(f"Target {target} not supported.")
|
|
163
|
+
converter = InformationToDMS(reserved_properties=reserved_properties)
|
|
164
|
+
|
|
180
165
|
issues = self._state.rule_transform(converter)
|
|
181
166
|
|
|
182
167
|
if self._verbose and not issues.has_errors:
|
|
183
|
-
print(
|
|
168
|
+
print("Rules converted to dms.")
|
|
184
169
|
else:
|
|
185
170
|
print("Conversion failed.")
|
|
186
171
|
if issues:
|
|
@@ -197,13 +182,11 @@ class NeatSession:
|
|
|
197
182
|
"NeatInferredDataModel",
|
|
198
183
|
"v1",
|
|
199
184
|
),
|
|
200
|
-
max_number_of_instance: int = 100,
|
|
201
185
|
) -> IssueList:
|
|
202
186
|
"""Data model inference from instances.
|
|
203
187
|
|
|
204
188
|
Args:
|
|
205
189
|
model_id: The ID of the inferred data model.
|
|
206
|
-
max_number_of_instance: The maximum number of instances to use for inference.
|
|
207
190
|
|
|
208
191
|
Example:
|
|
209
192
|
Infer a data model after reading a source file
|
|
@@ -214,6 +197,12 @@ class NeatSession:
|
|
|
214
197
|
neat.infer()
|
|
215
198
|
```
|
|
216
199
|
"""
|
|
200
|
+
return self._infer_subclasses(model_id)
|
|
201
|
+
|
|
202
|
+
def _previous_inference(
|
|
203
|
+
self, model_id: dm.DataModelId | tuple[str, str, str], max_number_of_instance: int = 100
|
|
204
|
+
) -> IssueList:
|
|
205
|
+
# Temporary keeping the old inference method in case we need to revert back
|
|
217
206
|
model_id = dm.DataModelId.load(model_id)
|
|
218
207
|
importer = importers.InferenceImporter.from_graph_store(
|
|
219
208
|
store=self._state.instances.store,
|
|
@@ -222,57 +211,70 @@ class NeatSession:
|
|
|
222
211
|
)
|
|
223
212
|
return self._state.rule_import(importer)
|
|
224
213
|
|
|
225
|
-
def _infer_subclasses(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
214
|
+
def _infer_subclasses(
|
|
215
|
+
self,
|
|
216
|
+
model_id: dm.DataModelId | tuple[str, str, str] = (
|
|
217
|
+
"neat_space",
|
|
218
|
+
"NeatInferredDataModel",
|
|
219
|
+
"v1",
|
|
220
|
+
),
|
|
221
|
+
) -> IssueList:
|
|
222
|
+
"""Infer data model from instances."""
|
|
223
|
+
last_entity: RulesEntity | None = None
|
|
224
|
+
if self._state.rule_store.provenance:
|
|
225
|
+
last_entity = self._state.rule_store.provenance[-1].target_entity
|
|
226
|
+
|
|
227
|
+
# Note that this importer behaves as a transformer in the rule store when there is an existing rules.
|
|
228
|
+
# We are essentially transforming the last entity's information rules into a new set of information rules.
|
|
229
229
|
importer = importers.SubclassInferenceImporter(
|
|
230
|
-
issue_list=
|
|
230
|
+
issue_list=IssueList(),
|
|
231
231
|
graph=self._state.instances.store.graph(),
|
|
232
|
-
rules=
|
|
233
|
-
|
|
232
|
+
rules=last_entity.information if last_entity is not None else None,
|
|
233
|
+
data_model_id=dm.DataModelId.load(model_id) if last_entity is None else None,
|
|
234
234
|
)
|
|
235
235
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
object.__setattr__(change.target_entity, "result", new_information_rules)
|
|
248
|
-
break
|
|
236
|
+
def action() -> tuple[InformationRules, DMSRules | None]:
|
|
237
|
+
unverified_information = importer.to_rules()
|
|
238
|
+
extra_info = VerifyInformationRules().transform(unverified_information)
|
|
239
|
+
if not last_entity:
|
|
240
|
+
return extra_info, None
|
|
241
|
+
merged_info = MergeInformationRules(extra_info).transform(last_entity.information)
|
|
242
|
+
if not last_entity.dms:
|
|
243
|
+
return merged_info, None
|
|
244
|
+
extra_dms = InformationToDMS(reserved_properties="warning").transform(extra_info)
|
|
245
|
+
merged_dms = MergeDMSRules(extra_dms).transform(last_entity.dms)
|
|
246
|
+
return merged_info, merged_dms
|
|
249
247
|
|
|
250
|
-
|
|
251
|
-
return self._state.rule_transform(MergeDMSRules(dms_rules))
|
|
248
|
+
return self._state.rule_store.do_activity(action, importer)
|
|
252
249
|
|
|
253
250
|
def _repr_html_(self) -> str:
|
|
254
251
|
state = self._state
|
|
255
|
-
if
|
|
256
|
-
not state.instances.has_store
|
|
257
|
-
and not state.rule_store.has_unverified_rules
|
|
258
|
-
and not state.rule_store.has_verified_rules
|
|
259
|
-
):
|
|
252
|
+
if state.instances.empty and state.rule_store.empty:
|
|
260
253
|
return "<strong>Empty session</strong>. Get started by reading something with the <em>.read</em> attribute."
|
|
261
254
|
|
|
262
255
|
output = []
|
|
263
256
|
|
|
264
|
-
if state.rule_store.
|
|
265
|
-
|
|
266
|
-
|
|
257
|
+
if state.rule_store.provenance:
|
|
258
|
+
last_entity = state.rule_store.provenance[-1].target_entity
|
|
259
|
+
if last_entity.dms:
|
|
260
|
+
html = last_entity.dms._repr_html_()
|
|
261
|
+
else:
|
|
262
|
+
html = last_entity.information._repr_html_()
|
|
263
|
+
output.append(f"<H2>Data Model</H2><br />{html}") # type: ignore
|
|
267
264
|
|
|
268
|
-
if state.
|
|
269
|
-
output.append(f"<H2>Verified Data Model</H2><br />{state.rule_store.last_verified_rule._repr_html_()}") # type: ignore
|
|
270
|
-
|
|
271
|
-
if state.instances.has_store:
|
|
265
|
+
if not state.instances.empty:
|
|
272
266
|
output.append(f"<H2>Instances</H2> {state.instances.store._repr_html_()}")
|
|
273
267
|
|
|
274
268
|
return "<br />".join(output)
|
|
275
269
|
|
|
270
|
+
def close(self) -> None:
|
|
271
|
+
"""Close the session and release resources."""
|
|
272
|
+
self._state.instances.store.dataset.close()
|
|
273
|
+
|
|
274
|
+
def __del__(self) -> None:
|
|
275
|
+
"""Called by garbage collector"""
|
|
276
|
+
self.close()
|
|
277
|
+
|
|
276
278
|
|
|
277
279
|
@session_class_wrapper
|
|
278
280
|
class OptAPI:
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from cognite.client.data_classes.data_modeling import DataModelIdentifier
|
|
4
|
+
|
|
5
|
+
from cognite.neat._issues import IssueList
|
|
6
|
+
from cognite.neat._rules.models.dms import DMSValidation
|
|
7
|
+
from cognite.neat._rules.transformers import (
|
|
8
|
+
IncludeReferenced,
|
|
9
|
+
ToDataProductModel,
|
|
10
|
+
ToEnterpriseModel,
|
|
11
|
+
ToSolutionModel,
|
|
12
|
+
VerifiedRulesTransformer,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from ._state import SessionState
|
|
16
|
+
from .exceptions import NeatSessionError, session_class_wrapper
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@session_class_wrapper
|
|
20
|
+
class CreateAPI:
|
|
21
|
+
"""
|
|
22
|
+
Create new data model based on the given data.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, state: SessionState):
|
|
26
|
+
self._state = state
|
|
27
|
+
|
|
28
|
+
def enterprise_model(
|
|
29
|
+
self,
|
|
30
|
+
data_model_id: DataModelIdentifier,
|
|
31
|
+
org_name: str = "My",
|
|
32
|
+
dummy_property: str = "GUID",
|
|
33
|
+
) -> IssueList:
|
|
34
|
+
"""Uses the current data model as a basis to create enterprise data model
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
data_model_id: The enterprise data model id that is being created
|
|
38
|
+
org_name: Organization name to use for the views in the enterprise data model.
|
|
39
|
+
dummy_property: The dummy property to use as placeholder for the views in the new data model.
|
|
40
|
+
|
|
41
|
+
!!! note "Enterprise Data Model Creation"
|
|
42
|
+
|
|
43
|
+
Always create an enterprise data model from a Cognite Data Model as this will
|
|
44
|
+
assure all the Cognite Data Fusion applications to run smoothly, such as
|
|
45
|
+
- Search
|
|
46
|
+
- Atlas AI
|
|
47
|
+
- ...
|
|
48
|
+
|
|
49
|
+
!!! note "Move Connections"
|
|
50
|
+
|
|
51
|
+
If you want to move the connections to the new data model, set the move_connections
|
|
52
|
+
to True. This will move the connections to the new data model and use new model
|
|
53
|
+
views as the source and target views.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
return self._state.rule_transform(
|
|
57
|
+
ToEnterpriseModel(
|
|
58
|
+
new_model_id=data_model_id,
|
|
59
|
+
org_name=org_name,
|
|
60
|
+
dummy_property=dummy_property,
|
|
61
|
+
move_connections=True,
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def solution_model(
|
|
66
|
+
self,
|
|
67
|
+
data_model_id: DataModelIdentifier,
|
|
68
|
+
direct_property: str = "enterprise",
|
|
69
|
+
view_prefix: str = "Enterprise",
|
|
70
|
+
) -> IssueList:
|
|
71
|
+
"""Uses the current data model as a basis to create solution data model
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
data_model_id: The solution data model id that is being created.
|
|
75
|
+
direct_property: The property to use for the direct connection between the views in the solution data model
|
|
76
|
+
and the enterprise data model.
|
|
77
|
+
view_prefix: The prefix to use for the views in the enterprise data model.
|
|
78
|
+
|
|
79
|
+
!!! note "Solution Data Model Mode"
|
|
80
|
+
|
|
81
|
+
The read-only solution model will only be able to read from the existing containers
|
|
82
|
+
from the enterprise data model, therefore the solution data model will not have
|
|
83
|
+
containers in the solution data model space. Meaning the solution data model views
|
|
84
|
+
will be read-only.
|
|
85
|
+
|
|
86
|
+
The write mode will have additional containers in the solution data model space,
|
|
87
|
+
allowing in addition to read through the solution model views, also writing to
|
|
88
|
+
the containers in the solution data model space.
|
|
89
|
+
|
|
90
|
+
"""
|
|
91
|
+
return self._state.rule_transform(
|
|
92
|
+
ToSolutionModel(
|
|
93
|
+
new_model_id=data_model_id,
|
|
94
|
+
properties="connection",
|
|
95
|
+
direct_property=direct_property,
|
|
96
|
+
view_prefix=view_prefix,
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def data_product_model(
|
|
101
|
+
self,
|
|
102
|
+
data_model_id: DataModelIdentifier,
|
|
103
|
+
include: Literal["same-space", "all"] = "same-space",
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Uses the current data model as a basis to create data product data model.
|
|
106
|
+
|
|
107
|
+
A data product model is a data model that ONLY maps to containers and do not use implements. This is
|
|
108
|
+
typically used for defining the data in a data product.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
data_model_id: The data product data model id that is being created.
|
|
112
|
+
include: The views to include in the data product data model. Can be either "same-space" or "all".
|
|
113
|
+
If you set same-space, only the properties of the views in the same space as the data model
|
|
114
|
+
will be included.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
view_ids, container_ids = DMSValidation(
|
|
118
|
+
self._state.rule_store.last_verified_dms_rules
|
|
119
|
+
).imported_views_and_containers_ids()
|
|
120
|
+
transformers: list[VerifiedRulesTransformer] = []
|
|
121
|
+
client = self._state.client
|
|
122
|
+
if (view_ids or container_ids) and client is None:
|
|
123
|
+
raise NeatSessionError(
|
|
124
|
+
"No client provided. You are referencing unknown views and containers in your data model, "
|
|
125
|
+
"NEAT needs a client to lookup the definitions. "
|
|
126
|
+
"Please set the client in the session, NeatSession(client=client)."
|
|
127
|
+
)
|
|
128
|
+
elif (view_ids or container_ids) and client:
|
|
129
|
+
transformers.append(IncludeReferenced(client, include_properties=True))
|
|
130
|
+
|
|
131
|
+
transformers.append(ToDataProductModel(new_model_id=data_model_id, include=include))
|
|
132
|
+
|
|
133
|
+
self._state.rule_transform(*transformers)
|
cognite/neat/_session/_drop.py
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
from collections.abc import Collection
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from cognite.client.utils.useful_types import SequenceNotStr
|
|
1
6
|
from rdflib import URIRef
|
|
2
7
|
|
|
8
|
+
from cognite.neat._constants import COGNITE_MODELS
|
|
9
|
+
from cognite.neat._issues import IssueList
|
|
10
|
+
from cognite.neat._rules.transformers import DropModelViews
|
|
11
|
+
|
|
3
12
|
from ._state import SessionState
|
|
4
|
-
from .exceptions import session_class_wrapper
|
|
13
|
+
from .exceptions import NeatSessionError, session_class_wrapper
|
|
5
14
|
|
|
6
15
|
try:
|
|
7
16
|
from rich import print
|
|
@@ -17,6 +26,7 @@ class DropAPI:
|
|
|
17
26
|
|
|
18
27
|
def __init__(self, state: SessionState):
|
|
19
28
|
self._state = state
|
|
29
|
+
self.data_model = DropDataModelAPI(state)
|
|
20
30
|
|
|
21
31
|
def instances(self, type: str | list[str]) -> None:
|
|
22
32
|
"""Drop instances from the session.
|
|
@@ -47,3 +57,47 @@ class DropAPI:
|
|
|
47
57
|
for type_uri, count in result.items():
|
|
48
58
|
print(f"Dropped {count} instances of type {selected_uri_by_type[type_uri]}")
|
|
49
59
|
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@session_class_wrapper
|
|
63
|
+
class DropDataModelAPI:
|
|
64
|
+
def __init__(self, state: SessionState):
|
|
65
|
+
self._state = state
|
|
66
|
+
|
|
67
|
+
def views(
|
|
68
|
+
self,
|
|
69
|
+
view_external_id: str | SequenceNotStr[str] | None = None,
|
|
70
|
+
group: Literal["3D", "Annotation", "BaseViews"]
|
|
71
|
+
| Collection[Literal["3D", "Annotation", "BaseViews"]]
|
|
72
|
+
| None = None,
|
|
73
|
+
) -> IssueList:
|
|
74
|
+
"""Drops views from the data model.
|
|
75
|
+
Args:
|
|
76
|
+
view_external_id: The externalId of the view to drop.
|
|
77
|
+
group: Only applies to CogniteCore model. This is a shorthand for dropping multiple views at once.
|
|
78
|
+
"""
|
|
79
|
+
if sum([view_external_id is not None, group is not None]) != 1:
|
|
80
|
+
raise NeatSessionError("Only one of view_external_id or group can be specified.")
|
|
81
|
+
last_dms = self._state.rule_store.last_verified_dms_rules
|
|
82
|
+
if group is not None and last_dms.metadata.as_data_model_id() not in COGNITE_MODELS:
|
|
83
|
+
raise NeatSessionError("Group can only be specified for CogniteCore models.")
|
|
84
|
+
if view_external_id is not None:
|
|
85
|
+
existing_views = {view.view.external_id for view in last_dms.views}
|
|
86
|
+
requested_views = {view_external_id} if isinstance(view_external_id, str) else set(view_external_id)
|
|
87
|
+
missing_views = requested_views - existing_views
|
|
88
|
+
if missing_views:
|
|
89
|
+
suggestions: list[str] = []
|
|
90
|
+
for view in missing_views:
|
|
91
|
+
suggestion = difflib.get_close_matches(view, existing_views, n=1)
|
|
92
|
+
if suggestion:
|
|
93
|
+
suggestions.append(f"{view} -> {suggestion[0]}")
|
|
94
|
+
else:
|
|
95
|
+
suggestions.append(f"{view} -> NOT FOUND")
|
|
96
|
+
raise NeatSessionError(
|
|
97
|
+
f"{len(missing_views)} view(s) not found in the data model.\nDid you mean {', '.join(suggestions)}?"
|
|
98
|
+
)
|
|
99
|
+
before = len(last_dms.views)
|
|
100
|
+
issues = self._state.rule_transform(DropModelViews(view_external_id, group))
|
|
101
|
+
after = len(self._state.rule_store.last_verified_dms_rules.views)
|
|
102
|
+
print(f"Dropped {before - after} views.")
|
|
103
|
+
return issues
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from cognite.neat._issues._base import IssueList
|
|
2
|
+
from cognite.neat._rules.transformers import (
|
|
3
|
+
ToCompliantEntities,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
from ._state import SessionState
|
|
7
|
+
from .exceptions import session_class_wrapper
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@session_class_wrapper
|
|
11
|
+
class FixAPI:
|
|
12
|
+
"""Apply variety of fix methods to data model and isntances"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
15
|
+
self._state = state
|
|
16
|
+
self._verbose = verbose
|
|
17
|
+
self.data_model = DataModelFixAPI(state, verbose)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@session_class_wrapper
|
|
21
|
+
class DataModelFixAPI:
|
|
22
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
23
|
+
self._state = state
|
|
24
|
+
self._verbose = verbose
|
|
25
|
+
|
|
26
|
+
def cdf_compliant_external_ids(self) -> IssueList:
|
|
27
|
+
"""Convert (information/logical) data model component external ids to CDF compliant entities."""
|
|
28
|
+
return self._state.rule_transform(ToCompliantEntities())
|
|
@@ -48,7 +48,6 @@ class InspectAPI:
|
|
|
48
48
|
self.issues = InspectIssues(state)
|
|
49
49
|
self.outcome = InspectOutcome(state)
|
|
50
50
|
|
|
51
|
-
@property
|
|
52
51
|
def properties(self) -> pd.DataFrame:
|
|
53
52
|
"""Returns the properties of the current data model.
|
|
54
53
|
|
|
@@ -59,7 +58,23 @@ class InspectAPI:
|
|
|
59
58
|
neat.inspect.properties
|
|
60
59
|
```
|
|
61
60
|
"""
|
|
62
|
-
|
|
61
|
+
if self._state.rule_store.empty:
|
|
62
|
+
return pd.DataFrame()
|
|
63
|
+
last_entity = self._state.rule_store.provenance[-1].target_entity
|
|
64
|
+
if last_entity.dms:
|
|
65
|
+
df = last_entity.dms.properties.to_pandas()
|
|
66
|
+
else:
|
|
67
|
+
df = last_entity.information.properties.to_pandas()
|
|
68
|
+
df.drop(columns=["neatId"], errors="ignore", inplace=True)
|
|
69
|
+
return df
|
|
70
|
+
|
|
71
|
+
def views(self) -> pd.DataFrame:
|
|
72
|
+
if self._state.rule_store.empty:
|
|
73
|
+
return pd.DataFrame()
|
|
74
|
+
last_entity = self._state.rule_store.provenance[-1].target_entity
|
|
75
|
+
if last_entity.dms is None:
|
|
76
|
+
return pd.DataFrame()
|
|
77
|
+
df = last_entity.dms.views.to_pandas()
|
|
63
78
|
df.drop(columns=["neatId"], errors="ignore", inplace=True)
|
|
64
79
|
return df
|
|
65
80
|
|
|
@@ -91,9 +106,8 @@ class InspectIssues:
|
|
|
91
106
|
return_dataframe: bool = (False if IN_NOTEBOOK else True), # type: ignore[assignment]
|
|
92
107
|
) -> pd.DataFrame | None:
|
|
93
108
|
"""Returns the issues of the current data model."""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
elif self._state.instances.store.provenance:
|
|
109
|
+
issues = self._state.rule_store.last_issues
|
|
110
|
+
if issues is None and self._state.instances.store.provenance:
|
|
97
111
|
last_change = self._state.instances.store.provenance[-1]
|
|
98
112
|
issues = last_change.target_entity.issues
|
|
99
113
|
else:
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from cognite.neat._issues import IssueList
|
|
2
|
-
from cognite.neat._rules.models import DMSRules
|
|
3
2
|
from cognite.neat._rules.models.mapping import load_classic_to_core_mapping
|
|
4
3
|
from cognite.neat._rules.transformers import (
|
|
5
4
|
AsParentPropertyId,
|
|
6
5
|
ChangeViewPrefix,
|
|
7
6
|
IncludeReferenced,
|
|
8
7
|
RuleMapper,
|
|
9
|
-
|
|
8
|
+
VerifiedRulesTransformer,
|
|
10
9
|
)
|
|
11
10
|
|
|
12
11
|
from ._state import SessionState
|
|
@@ -43,15 +42,16 @@ class DataModelMappingAPI:
|
|
|
43
42
|
neat.mapping.classic_to_core(company_prefix="WindFarmX", use_parent_property_name=True)
|
|
44
43
|
```
|
|
45
44
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
if self._state.rule_store.empty:
|
|
46
|
+
raise NeatSessionError("No rules to map")
|
|
47
|
+
last_entity = self._state.rule_store.provenance[-1].target_entity
|
|
48
|
+
if last_entity.dms is None:
|
|
49
|
+
raise NeatSessionError("Data model not converted to DMS. Try running `neat.convert('dms')` first.")
|
|
50
|
+
rules = last_entity.dms
|
|
51
51
|
if self._state.client is None:
|
|
52
52
|
raise NeatSessionError("Client is required to map classic to core")
|
|
53
53
|
|
|
54
|
-
transformers: list[
|
|
54
|
+
transformers: list[VerifiedRulesTransformer] = []
|
|
55
55
|
if company_prefix:
|
|
56
56
|
transformers.append(ChangeViewPrefix("Classic", company_prefix))
|
|
57
57
|
transformers.extend(
|