cognite-neat 0.108.0__py3-none-any.whl → 0.109.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/_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 +20 -6
- 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.1.dist-info}/METADATA +1 -1
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/RECORD +44 -42
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/WHEEL +0 -0
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,36 +1,19 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
2
|
-
from typing import Any
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
|
-
from cognite.client.data_classes.data_modeling import DataModelIdentifier
|
|
5
4
|
from rdflib import URIRef
|
|
6
5
|
|
|
7
|
-
from cognite.neat._constants import (
|
|
8
|
-
get_default_prefixes_and_namespaces,
|
|
9
|
-
)
|
|
10
6
|
from cognite.neat._graph.transformers import (
|
|
11
|
-
AttachPropertyFromTargetToSource,
|
|
12
7
|
ConnectionToLiteral,
|
|
13
8
|
ConvertLiteral,
|
|
14
9
|
LiteralToEntity,
|
|
15
|
-
PruneDeadEndEdges,
|
|
16
|
-
PruneInstancesOfUnknownType,
|
|
17
|
-
PruneTypes,
|
|
18
10
|
RelationshipAsEdgeTransformer,
|
|
19
|
-
Transformers,
|
|
20
11
|
)
|
|
21
12
|
from cognite.neat._graph.transformers._rdfpath import MakeConnectionOnExactMatch
|
|
22
13
|
from cognite.neat._issues import IssueList
|
|
23
14
|
from cognite.neat._issues.errors import NeatValueError
|
|
24
|
-
from cognite.neat._rules.models.dms import DMSValidation
|
|
25
15
|
from cognite.neat._rules.transformers import (
|
|
26
|
-
IncludeReferenced,
|
|
27
16
|
PrefixEntities,
|
|
28
|
-
ReduceCogniteModel,
|
|
29
|
-
RulesTransformer,
|
|
30
|
-
ToCompliantEntities,
|
|
31
|
-
ToDataProductModel,
|
|
32
|
-
ToEnterpriseModel,
|
|
33
|
-
ToSolutionModel,
|
|
34
17
|
)
|
|
35
18
|
from cognite.neat._utils.text import humanize_collection
|
|
36
19
|
|
|
@@ -59,91 +42,6 @@ class InstancePrepareAPI:
|
|
|
59
42
|
self._state = state
|
|
60
43
|
self._verbose = verbose
|
|
61
44
|
|
|
62
|
-
def dexpi(self) -> None:
|
|
63
|
-
"""Prepares extracted DEXPI graph for further usage in CDF
|
|
64
|
-
|
|
65
|
-
!!! note "This method bundles several graph transformers which"
|
|
66
|
-
- attach values of generic attributes to nodes
|
|
67
|
-
- create associations between nodes
|
|
68
|
-
- remove unused generic attributes
|
|
69
|
-
- remove associations between nodes that do not exist in the extracted graph
|
|
70
|
-
- remove edges to nodes that do not exist in the extracted graph
|
|
71
|
-
|
|
72
|
-
and therefore safeguard CDF from a bad graph
|
|
73
|
-
|
|
74
|
-
Example:
|
|
75
|
-
Apply Dexpi specific transformations:
|
|
76
|
-
```python
|
|
77
|
-
neat.prepare.instances.dexpi()
|
|
78
|
-
```
|
|
79
|
-
"""
|
|
80
|
-
|
|
81
|
-
DEXPI = get_default_prefixes_and_namespaces()["dexpi"]
|
|
82
|
-
|
|
83
|
-
transformers = [
|
|
84
|
-
# Remove any instance which type is unknown
|
|
85
|
-
PruneInstancesOfUnknownType(),
|
|
86
|
-
# Directly connect generic attributes
|
|
87
|
-
AttachPropertyFromTargetToSource(
|
|
88
|
-
target_property=DEXPI.Value,
|
|
89
|
-
target_property_holding_new_property=DEXPI.Name,
|
|
90
|
-
target_node_type=DEXPI.GenericAttribute,
|
|
91
|
-
delete_target_node=True,
|
|
92
|
-
),
|
|
93
|
-
# Directly connect associations
|
|
94
|
-
AttachPropertyFromTargetToSource(
|
|
95
|
-
target_property=DEXPI.ItemID,
|
|
96
|
-
target_property_holding_new_property=DEXPI.Type,
|
|
97
|
-
target_node_type=DEXPI.Association,
|
|
98
|
-
delete_target_node=True,
|
|
99
|
-
),
|
|
100
|
-
# Remove unused generic attributes and associations
|
|
101
|
-
PruneTypes([DEXPI.GenericAttribute, DEXPI.Association]),
|
|
102
|
-
# Remove edges to nodes that do not exist in the extracted graph
|
|
103
|
-
PruneDeadEndEdges(),
|
|
104
|
-
]
|
|
105
|
-
|
|
106
|
-
for transformer in transformers:
|
|
107
|
-
self._state.instances.store.transform(cast(Transformers, transformer))
|
|
108
|
-
|
|
109
|
-
def aml(self) -> None:
|
|
110
|
-
"""Prepares extracted AutomationML graph for further usage in CDF
|
|
111
|
-
|
|
112
|
-
!!! note "This method bundles several graph transformers which"
|
|
113
|
-
- attach values of attributes to nodes
|
|
114
|
-
- remove unused attributes
|
|
115
|
-
- remove edges to nodes that do not exist in the extracted graph
|
|
116
|
-
|
|
117
|
-
and therefore safeguard CDF from a bad graph
|
|
118
|
-
|
|
119
|
-
Example:
|
|
120
|
-
Apply AML specific transformations:
|
|
121
|
-
```python
|
|
122
|
-
neat.prepare.instances.aml()
|
|
123
|
-
```
|
|
124
|
-
"""
|
|
125
|
-
|
|
126
|
-
AML = get_default_prefixes_and_namespaces()["aml"]
|
|
127
|
-
|
|
128
|
-
transformers = [
|
|
129
|
-
# Remove any instance which type is unknown
|
|
130
|
-
PruneInstancesOfUnknownType(),
|
|
131
|
-
# Directly connect generic attributes
|
|
132
|
-
AttachPropertyFromTargetToSource(
|
|
133
|
-
target_property=AML.Value,
|
|
134
|
-
target_property_holding_new_property=AML.Name,
|
|
135
|
-
target_node_type=AML.Attribute,
|
|
136
|
-
delete_target_node=True,
|
|
137
|
-
),
|
|
138
|
-
# Prune unused attributes
|
|
139
|
-
PruneTypes([AML.Attribute]),
|
|
140
|
-
# # Remove edges to nodes that do not exist in the extracted graph
|
|
141
|
-
PruneDeadEndEdges(),
|
|
142
|
-
]
|
|
143
|
-
|
|
144
|
-
for transformer in transformers:
|
|
145
|
-
self._state.instances.store.transform(cast(Transformers, transformer))
|
|
146
|
-
|
|
147
45
|
def make_connection_on_exact_match(
|
|
148
46
|
self,
|
|
149
47
|
source: tuple[str, str],
|
|
@@ -357,10 +255,6 @@ class DataModelPrepareAPI:
|
|
|
357
255
|
self._state = state
|
|
358
256
|
self._verbose = verbose
|
|
359
257
|
|
|
360
|
-
def cdf_compliant_external_ids(self) -> IssueList:
|
|
361
|
-
"""Convert data model component external ids to CDF compliant entities."""
|
|
362
|
-
return self._state.rule_transform(ToCompliantEntities())
|
|
363
|
-
|
|
364
258
|
def prefix(self, prefix: str) -> IssueList:
|
|
365
259
|
"""Prefix all views in the data model with the given prefix.
|
|
366
260
|
|
|
@@ -368,143 +262,5 @@ class DataModelPrepareAPI:
|
|
|
368
262
|
prefix: The prefix to add to the views in the data model.
|
|
369
263
|
|
|
370
264
|
"""
|
|
371
|
-
return self._state.rule_transform(PrefixEntities(prefix))
|
|
372
|
-
|
|
373
|
-
def to_enterprise(
|
|
374
|
-
self,
|
|
375
|
-
data_model_id: DataModelIdentifier,
|
|
376
|
-
org_name: str = "My",
|
|
377
|
-
dummy_property: str = "GUID",
|
|
378
|
-
move_connections: bool = False,
|
|
379
|
-
) -> IssueList:
|
|
380
|
-
"""Uses the current data model as a basis to create enterprise data model
|
|
381
|
-
|
|
382
|
-
Args:
|
|
383
|
-
data_model_id: The enterprise data model id that is being created
|
|
384
|
-
org_name: Organization name to use for the views in the enterprise data model.
|
|
385
|
-
dummy_property: The dummy property to use as placeholder for the views in the new data model.
|
|
386
|
-
move_connections: If True, the connections will be moved to the new data model.
|
|
387
|
-
|
|
388
|
-
!!! note "Enterprise Data Model Creation"
|
|
389
|
-
|
|
390
|
-
Always create an enterprise data model from a Cognite Data Model as this will
|
|
391
|
-
assure all the Cognite Data Fusion applications to run smoothly, such as
|
|
392
|
-
- Search
|
|
393
|
-
- Atlas AI
|
|
394
|
-
- ...
|
|
395
|
-
|
|
396
|
-
!!! note "Move Connections"
|
|
397
|
-
|
|
398
|
-
If you want to move the connections to the new data model, set the move_connections
|
|
399
|
-
to True. This will move the connections to the new data model and use new model
|
|
400
|
-
views as the source and target views.
|
|
401
|
-
|
|
402
|
-
"""
|
|
403
|
-
return self._state.rule_transform(
|
|
404
|
-
ToEnterpriseModel(
|
|
405
|
-
new_model_id=data_model_id,
|
|
406
|
-
org_name=org_name,
|
|
407
|
-
dummy_property=dummy_property,
|
|
408
|
-
move_connections=move_connections,
|
|
409
|
-
)
|
|
410
|
-
)
|
|
411
265
|
|
|
412
|
-
|
|
413
|
-
self,
|
|
414
|
-
data_model_id: DataModelIdentifier,
|
|
415
|
-
org_name: str = "My",
|
|
416
|
-
mode: Literal["read", "write"] = "read",
|
|
417
|
-
dummy_property: str = "GUID",
|
|
418
|
-
) -> IssueList:
|
|
419
|
-
"""Uses the current data model as a basis to create solution data model
|
|
420
|
-
|
|
421
|
-
Args:
|
|
422
|
-
data_model_id: The solution data model id that is being created.
|
|
423
|
-
org_name: Organization name to use for the views in the new data model.
|
|
424
|
-
mode: The mode of the solution data model. Can be either "read" or "write".
|
|
425
|
-
dummy_property: The dummy property to use as placeholder for the views in the new data model.
|
|
426
|
-
|
|
427
|
-
!!! note "Solution Data Model Mode"
|
|
428
|
-
|
|
429
|
-
The read-only solution model will only be able to read from the existing containers
|
|
430
|
-
from the enterprise data model, therefore the solution data model will not have
|
|
431
|
-
containers in the solution data model space. Meaning the solution data model views
|
|
432
|
-
will be read-only.
|
|
433
|
-
|
|
434
|
-
The write mode will have additional containers in the solution data model space,
|
|
435
|
-
allowing in addition to reading through the solution model views, also writing to
|
|
436
|
-
the containers in the solution data model space.
|
|
437
|
-
|
|
438
|
-
"""
|
|
439
|
-
return self._state.rule_transform(
|
|
440
|
-
ToSolutionModel(
|
|
441
|
-
new_model_id=data_model_id,
|
|
442
|
-
org_name=org_name,
|
|
443
|
-
mode=mode,
|
|
444
|
-
dummy_property=dummy_property,
|
|
445
|
-
)
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
def to_data_product(
|
|
449
|
-
self,
|
|
450
|
-
data_model_id: DataModelIdentifier,
|
|
451
|
-
org_name: str = "",
|
|
452
|
-
include: Literal["same-space", "all"] = "same-space",
|
|
453
|
-
) -> None:
|
|
454
|
-
"""Uses the current data model as a basis to create data product data model.
|
|
455
|
-
|
|
456
|
-
A data product model is a data model that ONLY maps to containers and do not use implements. This is
|
|
457
|
-
typically used for defining the data in a data product.
|
|
458
|
-
|
|
459
|
-
Args:
|
|
460
|
-
data_model_id: The data product data model id that is being created.
|
|
461
|
-
org_name: Organization name used as prefix if the model is building on top of a Cognite Data Model.
|
|
462
|
-
include: The views to include in the data product data model. Can be either "same-space" or "all".
|
|
463
|
-
If you set same-space, only the properties of the views in the same space as the data model
|
|
464
|
-
will be included.
|
|
465
|
-
"""
|
|
466
|
-
|
|
467
|
-
view_ids, container_ids = DMSValidation(
|
|
468
|
-
self._state.rule_store.last_verified_dms_rules
|
|
469
|
-
).imported_views_and_containers_ids()
|
|
470
|
-
transformers: list[RulesTransformer] = []
|
|
471
|
-
client = self._state.client
|
|
472
|
-
if (view_ids or container_ids) and client is None:
|
|
473
|
-
raise NeatSessionError(
|
|
474
|
-
"No client provided. You are referencing unknown views and containers in your data model, "
|
|
475
|
-
"NEAT needs a client to lookup the definitions. "
|
|
476
|
-
"Please set the client in the session, NeatSession(client=client)."
|
|
477
|
-
)
|
|
478
|
-
elif (view_ids or container_ids) and client:
|
|
479
|
-
transformers.append(IncludeReferenced(client, include_properties=True))
|
|
480
|
-
|
|
481
|
-
transformers.append(
|
|
482
|
-
ToDataProductModel(
|
|
483
|
-
new_model_id=data_model_id,
|
|
484
|
-
org_name=org_name,
|
|
485
|
-
include=include,
|
|
486
|
-
)
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
self._state.rule_transform(*transformers)
|
|
490
|
-
|
|
491
|
-
def reduce(self, drop: Collection[Literal["3D", "Annotation", "BaseViews"] | str]) -> IssueList:
|
|
492
|
-
"""This is a special method that allow you to drop parts of the data model.
|
|
493
|
-
This only applies to Cognite Data Models.
|
|
494
|
-
|
|
495
|
-
Args:
|
|
496
|
-
drop: What to drop from the data model. The values 3D, Annotation, and BaseViews are special values that
|
|
497
|
-
drops multiple views at once. You can also pass externalIds of views to drop individual views.
|
|
498
|
-
|
|
499
|
-
"""
|
|
500
|
-
return self._state.rule_transform(ReduceCogniteModel(drop))
|
|
501
|
-
|
|
502
|
-
def include_referenced(self) -> IssueList:
|
|
503
|
-
"""Include referenced views and containers in the data model."""
|
|
504
|
-
if self._state.client is None:
|
|
505
|
-
raise NeatSessionError(
|
|
506
|
-
"No client provided. You are referencing unknown views and containers in your data model, "
|
|
507
|
-
"NEAT needs a client to lookup the definitions. "
|
|
508
|
-
"Please set the client in the session, NeatSession(client=client)."
|
|
509
|
-
)
|
|
510
|
-
return self._state.rule_transform(IncludeReferenced(self._state.client))
|
|
266
|
+
return self._state.rule_transform(PrefixEntities(prefix)) # type: ignore[arg-type]
|
cognite/neat/_session/_read.py
CHANGED
|
@@ -4,10 +4,23 @@ from cognite.client.data_classes.data_modeling import DataModelId, DataModelIden
|
|
|
4
4
|
from cognite.client.utils.useful_types import SequenceNotStr
|
|
5
5
|
|
|
6
6
|
from cognite.neat._client import NeatClient
|
|
7
|
-
from cognite.neat._constants import
|
|
7
|
+
from cognite.neat._constants import (
|
|
8
|
+
CLASSIC_CDF_NAMESPACE,
|
|
9
|
+
get_default_prefixes_and_namespaces,
|
|
10
|
+
)
|
|
8
11
|
from cognite.neat._graph import examples as instances_examples
|
|
9
12
|
from cognite.neat._graph import extractors
|
|
10
|
-
from cognite.neat._graph.transformers import
|
|
13
|
+
from cognite.neat._graph.transformers import (
|
|
14
|
+
ConvertLiteral,
|
|
15
|
+
LiteralToEntity,
|
|
16
|
+
Transformers,
|
|
17
|
+
)
|
|
18
|
+
from cognite.neat._graph.transformers._prune_graph import (
|
|
19
|
+
AttachPropertyFromTargetToSource,
|
|
20
|
+
PruneDeadEndEdges,
|
|
21
|
+
PruneInstancesOfUnknownType,
|
|
22
|
+
PruneTypes,
|
|
23
|
+
)
|
|
11
24
|
from cognite.neat._issues import IssueList
|
|
12
25
|
from cognite.neat._issues.errors import NeatValueError
|
|
13
26
|
from cognite.neat._issues.warnings import MissingCogniteClientWarning
|
|
@@ -387,7 +400,7 @@ class XMLReadAPI(BaseReadAPI):
|
|
|
387
400
|
raise NeatValueError("Only support XML files of DEXPI format at the moment.")
|
|
388
401
|
|
|
389
402
|
def dexpi(self, io: Any) -> None:
|
|
390
|
-
"""Reads a DEXPI file into the NeatSession.
|
|
403
|
+
"""Reads a DEXPI file into the NeatSession and executes set of predefined transformations.
|
|
391
404
|
|
|
392
405
|
Args:
|
|
393
406
|
io: file path or url to the DEXPI file
|
|
@@ -396,6 +409,13 @@ class XMLReadAPI(BaseReadAPI):
|
|
|
396
409
|
```python
|
|
397
410
|
neat.read.xml.dexpi("url_or_path_to_dexpi_file")
|
|
398
411
|
```
|
|
412
|
+
|
|
413
|
+
!!! note "This method bundles several graph transformers which"
|
|
414
|
+
- attach values of generic attributes to nodes
|
|
415
|
+
- create associations between nodes
|
|
416
|
+
- remove unused generic attributes
|
|
417
|
+
- remove associations between nodes that do not exist in the extracted graph
|
|
418
|
+
- remove edges to nodes that do not exist in the extracted graph
|
|
399
419
|
"""
|
|
400
420
|
path = NeatReader.create(io).materialize_path()
|
|
401
421
|
engine = import_engine()
|
|
@@ -404,8 +424,36 @@ class XMLReadAPI(BaseReadAPI):
|
|
|
404
424
|
extractor = engine.create_extractor()
|
|
405
425
|
self._state.instances.store.write(extractor)
|
|
406
426
|
|
|
427
|
+
DEXPI = get_default_prefixes_and_namespaces()["dexpi"]
|
|
428
|
+
|
|
429
|
+
transformers = [
|
|
430
|
+
# Remove any instance which type is unknown
|
|
431
|
+
PruneInstancesOfUnknownType(),
|
|
432
|
+
# Directly connect generic attributes
|
|
433
|
+
AttachPropertyFromTargetToSource(
|
|
434
|
+
target_property=DEXPI.Value,
|
|
435
|
+
target_property_holding_new_property=DEXPI.Name,
|
|
436
|
+
target_node_type=DEXPI.GenericAttribute,
|
|
437
|
+
delete_target_node=True,
|
|
438
|
+
),
|
|
439
|
+
# Directly connect associations
|
|
440
|
+
AttachPropertyFromTargetToSource(
|
|
441
|
+
target_property=DEXPI.ItemID,
|
|
442
|
+
target_property_holding_new_property=DEXPI.Type,
|
|
443
|
+
target_node_type=DEXPI.Association,
|
|
444
|
+
delete_target_node=True,
|
|
445
|
+
),
|
|
446
|
+
# Remove unused generic attributes and associations
|
|
447
|
+
PruneTypes([DEXPI.GenericAttribute, DEXPI.Association]),
|
|
448
|
+
# Remove edges to nodes that do not exist in the extracted graph
|
|
449
|
+
PruneDeadEndEdges(),
|
|
450
|
+
]
|
|
451
|
+
|
|
452
|
+
for transformer in transformers:
|
|
453
|
+
self._state.instances.store.transform(cast(Transformers, transformer))
|
|
454
|
+
|
|
407
455
|
def aml(self, io: Any):
|
|
408
|
-
"""Reads an AML file into NeatSession.
|
|
456
|
+
"""Reads an AML file into NeatSession and executes a set of predefined transformations.
|
|
409
457
|
|
|
410
458
|
Args:
|
|
411
459
|
io: file path or url to the AML file
|
|
@@ -414,6 +462,11 @@ class XMLReadAPI(BaseReadAPI):
|
|
|
414
462
|
```python
|
|
415
463
|
neat.read.xml.aml("url_or_path_to_aml_file")
|
|
416
464
|
```
|
|
465
|
+
|
|
466
|
+
!!! note "This method bundles several graph transformers which"
|
|
467
|
+
- attach values of attributes to nodes
|
|
468
|
+
- remove unused attributes
|
|
469
|
+
- remove edges to nodes that do not exist in the extracted graph
|
|
417
470
|
"""
|
|
418
471
|
path = NeatReader.create(io).materialize_path()
|
|
419
472
|
engine = import_engine()
|
|
@@ -422,6 +475,27 @@ class XMLReadAPI(BaseReadAPI):
|
|
|
422
475
|
extractor = engine.create_extractor()
|
|
423
476
|
self._state.instances.store.write(extractor)
|
|
424
477
|
|
|
478
|
+
AML = get_default_prefixes_and_namespaces()["aml"]
|
|
479
|
+
|
|
480
|
+
transformers = [
|
|
481
|
+
# Remove any instance which type is unknown
|
|
482
|
+
PruneInstancesOfUnknownType(),
|
|
483
|
+
# Directly connect generic attributes
|
|
484
|
+
AttachPropertyFromTargetToSource(
|
|
485
|
+
target_property=AML.Value,
|
|
486
|
+
target_property_holding_new_property=AML.Name,
|
|
487
|
+
target_node_type=AML.Attribute,
|
|
488
|
+
delete_target_node=True,
|
|
489
|
+
),
|
|
490
|
+
# Prune unused attributes
|
|
491
|
+
PruneTypes([AML.Attribute]),
|
|
492
|
+
# # Remove edges to nodes that do not exist in the extracted graph
|
|
493
|
+
PruneDeadEndEdges(),
|
|
494
|
+
]
|
|
495
|
+
|
|
496
|
+
for transformer in transformers:
|
|
497
|
+
self._state.instances.store.transform(cast(Transformers, transformer))
|
|
498
|
+
|
|
425
499
|
|
|
426
500
|
@session_class_wrapper
|
|
427
501
|
class RDFReadAPI(BaseReadAPI):
|
cognite/neat/_session/_set.py
CHANGED
|
@@ -3,7 +3,7 @@ from cognite.client import data_modeling as dm
|
|
|
3
3
|
|
|
4
4
|
from cognite.neat._client import NeatClient
|
|
5
5
|
from cognite.neat._constants import COGNITE_MODELS
|
|
6
|
-
from cognite.neat._graph.transformers import
|
|
6
|
+
from cognite.neat._graph.transformers import SetType
|
|
7
7
|
from cognite.neat._issues import IssueList
|
|
8
8
|
from cognite.neat._issues.errors import NeatValueError
|
|
9
9
|
from cognite.neat._rules.models import DMSRules
|
|
@@ -21,6 +21,7 @@ class SetAPI:
|
|
|
21
21
|
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
22
22
|
self._state = state
|
|
23
23
|
self._verbose = verbose
|
|
24
|
+
self.instances = SetInstances(state, verbose)
|
|
24
25
|
|
|
25
26
|
def data_model_id(self, new_model_id: dm.DataModelId | tuple[str, str, str]) -> IssueList:
|
|
26
27
|
"""Sets the data model ID of the latest verified data model. Set the data model id as a tuple of strings
|
|
@@ -32,7 +33,9 @@ class SetAPI:
|
|
|
32
33
|
neat.set.data_model_id(("my_data_model_space", "My_Data_Model", "v1"))
|
|
33
34
|
```
|
|
34
35
|
"""
|
|
35
|
-
|
|
36
|
+
if self._state.rule_store.empty:
|
|
37
|
+
raise NeatSessionError("No rules to set the data model ID.")
|
|
38
|
+
rules = self._state.rule_store.provenance[-1].target_entity.dms
|
|
36
39
|
if isinstance(rules, DMSRules):
|
|
37
40
|
if rules.metadata.as_data_model_id() in COGNITE_MODELS:
|
|
38
41
|
raise NeatSessionError(
|
|
@@ -48,26 +51,45 @@ class SetAPI:
|
|
|
48
51
|
print(f"Client set to {self._state.client.config.project} CDF project.")
|
|
49
52
|
return None
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
|
|
55
|
+
@session_class_wrapper
|
|
56
|
+
class SetInstances:
|
|
57
|
+
"""Used to change instances"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
60
|
+
self._state = state
|
|
61
|
+
self._verbose = verbose
|
|
62
|
+
|
|
63
|
+
def type_using_property(self, current_type: str, property_type: str, drop_property: bool = True) -> None:
|
|
64
|
+
"""Replaces the type of all instances with the value of a property.
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
All Assets have a property `assetCategory` that we want to use as the type of all asset instances.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
neat.set.instances.replace_type("Asset", "assetCategory")
|
|
71
|
+
```
|
|
72
|
+
"""
|
|
73
|
+
type_uri = self._state.instances.store.queries.type_uri(current_type)
|
|
74
|
+
property_uri = self._state.instances.store.queries.property_uri(property_type)
|
|
55
75
|
|
|
56
76
|
if not type_uri:
|
|
57
|
-
raise NeatValueError(f"Type {
|
|
77
|
+
raise NeatValueError(f"Type {current_type} does not exist in the graph.")
|
|
58
78
|
elif len(type_uri) > 1:
|
|
59
|
-
raise NeatValueError(
|
|
79
|
+
raise NeatValueError(
|
|
80
|
+
f"{current_type} has multiple ids found in the graph: {humanize_collection(type_uri)}."
|
|
81
|
+
)
|
|
60
82
|
|
|
61
83
|
if not property_uri:
|
|
62
|
-
raise NeatValueError(f"Property {
|
|
84
|
+
raise NeatValueError(f"Property {property_type} does not exist in the graph.")
|
|
63
85
|
elif len(type_uri) > 1:
|
|
64
86
|
raise NeatValueError(
|
|
65
|
-
f"{
|
|
87
|
+
f"{property_type} has multiple ids found in the graph: {humanize_collection(property_uri)}."
|
|
66
88
|
)
|
|
67
89
|
|
|
68
90
|
if not self._state.instances.store.queries.type_with_property(type_uri[0], property_uri[0]):
|
|
69
|
-
raise NeatValueError(f"Property {
|
|
91
|
+
raise NeatValueError(f"Property {property_type} is not defined for type {current_type}.")
|
|
70
92
|
|
|
71
|
-
self._state.instances.store.transform(
|
|
93
|
+
self._state.instances.store.transform(SetType(type_uri[0], property_uri[0], drop_property))
|
|
72
94
|
|
|
73
95
|
return None
|
cognite/neat/_session/_show.py
CHANGED
|
@@ -98,22 +98,15 @@ class ShowDataModelAPI(ShowBaseAPI):
|
|
|
98
98
|
self.implements = ShowDataModelImplementsAPI(self._state)
|
|
99
99
|
|
|
100
100
|
def __call__(self) -> Any:
|
|
101
|
-
if
|
|
102
|
-
raise NeatSessionError(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
rules = self._state.rule_store.last_verified_rule
|
|
101
|
+
if self._state.rule_store.empty:
|
|
102
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to read a new data model.")
|
|
103
|
+
last_target = self._state.rule_store.provenance[-1].target_entity
|
|
104
|
+
rules = last_target.dms or last_target.information
|
|
107
105
|
|
|
108
|
-
if
|
|
109
|
-
di_graph = self._generate_dms_di_graph(
|
|
110
|
-
elif isinstance(rules, InformationRules):
|
|
111
|
-
di_graph = self._generate_info_di_graph(rules)
|
|
106
|
+
if last_target.dms is not None:
|
|
107
|
+
di_graph = self._generate_dms_di_graph(last_target.dms)
|
|
112
108
|
else:
|
|
113
|
-
|
|
114
|
-
raise NeatSessionError(
|
|
115
|
-
f"Unsupported type {type(rules)}. Make sure you have either information or DMS rules."
|
|
116
|
-
)
|
|
109
|
+
di_graph = self._generate_info_di_graph(last_target.information)
|
|
117
110
|
identifier = to_directory_compatible(str(rules.metadata.identifier))
|
|
118
111
|
name = f"{identifier}.html"
|
|
119
112
|
|
|
@@ -187,22 +180,16 @@ class ShowDataModelImplementsAPI(ShowBaseAPI):
|
|
|
187
180
|
self._state = state
|
|
188
181
|
|
|
189
182
|
def __call__(self) -> Any:
|
|
190
|
-
if
|
|
191
|
-
raise NeatSessionError(
|
|
192
|
-
"No verified data model available. Try using [bold].verify()[/bold] to verify data model."
|
|
193
|
-
)
|
|
183
|
+
if self._state.rule_store.empty:
|
|
184
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to read a data model.")
|
|
194
185
|
|
|
195
|
-
|
|
186
|
+
last_target = self._state.rule_store.provenance[-1].target_entity
|
|
187
|
+
rules = last_target.dms or last_target.information
|
|
196
188
|
|
|
197
|
-
if
|
|
198
|
-
di_graph = self._generate_dms_di_graph(
|
|
199
|
-
elif isinstance(rules, InformationRules):
|
|
200
|
-
di_graph = self._generate_info_di_graph(rules)
|
|
189
|
+
if last_target.dms is not None:
|
|
190
|
+
di_graph = self._generate_dms_di_graph(last_target.dms)
|
|
201
191
|
else:
|
|
202
|
-
|
|
203
|
-
raise NeatSessionError(
|
|
204
|
-
f"Unsupported type {type(rules)}. Make sure you have either information or DMS rules."
|
|
205
|
-
)
|
|
192
|
+
di_graph = self._generate_info_di_graph(last_target.information)
|
|
206
193
|
identifier = to_directory_compatible(str(rules.metadata.identifier))
|
|
207
194
|
name = f"{identifier}_implements.html"
|
|
208
195
|
return self._generate_visualization(di_graph, name)
|
|
@@ -325,20 +312,6 @@ class ShowDataModelProvenanceAPI(ShowBaseAPI):
|
|
|
325
312
|
)
|
|
326
313
|
di_graph.add_edge(source_shorten, export_id, label="exported", color="grey")
|
|
327
314
|
|
|
328
|
-
for pruned_lists in self._state.rule_store.pruned_by_source_entity_id.values():
|
|
329
|
-
for prune_path in pruned_lists:
|
|
330
|
-
for change in prune_path:
|
|
331
|
-
source = uri_display_name(change.source_entity.id_)
|
|
332
|
-
target = uri_display_name(change.target_entity.id_)
|
|
333
|
-
di_graph.add_node(
|
|
334
|
-
target,
|
|
335
|
-
label=target,
|
|
336
|
-
type="Pruned",
|
|
337
|
-
title="Pruned",
|
|
338
|
-
color=hex_colored_types["Pruned"],
|
|
339
|
-
)
|
|
340
|
-
di_graph.add_edge(source, target, label="pruned", color="grey")
|
|
341
|
-
|
|
342
315
|
return di_graph
|
|
343
316
|
|
|
344
317
|
|