cognite-neat 0.123.20__py3-none-any.whl → 0.123.22__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_version.py +1 -1
- cognite/neat/session/_session/_data_model/__init__.py +3 -0
- cognite/neat/session/_session/{_data_model.py → _data_model/_read.py} +63 -24
- cognite/neat/session/_session/_data_model/_routes.py +43 -0
- cognite/neat/session/_session/_data_model/_show.py +147 -0
- {cognite_neat-0.123.20.dist-info → cognite_neat-0.123.22.dist-info}/METADATA +1 -1
- {cognite_neat-0.123.20.dist-info → cognite_neat-0.123.22.dist-info}/RECORD +9 -6
- {cognite_neat-0.123.20.dist-info → cognite_neat-0.123.22.dist-info}/WHEEL +0 -0
- {cognite_neat-0.123.20.dist-info → cognite_neat-0.123.22.dist-info}/licenses/LICENSE +0 -0
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.123.
|
|
1
|
+
__version__ = "0.123.22"
|
|
2
2
|
__engine__ = "^2.0.4"
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
from typing import Any, Literal
|
|
1
|
+
from typing import Any, Literal, cast
|
|
2
2
|
from zipfile import Path
|
|
3
3
|
|
|
4
|
+
from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier
|
|
5
|
+
|
|
6
|
+
from cognite.neat.core._client._api_client import NeatClient
|
|
4
7
|
from cognite.neat.core._data_model import importers
|
|
5
8
|
from cognite.neat.core._data_model.importers._base import BaseImporter
|
|
6
9
|
from cognite.neat.core._issues._base import IssueList
|
|
@@ -12,14 +15,7 @@ from cognite.neat.plugins.data_model.importers._base import DataModelImporterPlu
|
|
|
12
15
|
from cognite.neat.session._state import SessionState
|
|
13
16
|
from cognite.neat.session.exceptions import NeatSessionError, session_class_wrapper
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
@session_class_wrapper
|
|
17
|
-
class DataModelAPI:
|
|
18
|
-
"""API for managing data models in NEAT session."""
|
|
19
|
-
|
|
20
|
-
def __init__(self, state: SessionState) -> None:
|
|
21
|
-
self._state = state
|
|
22
|
-
self.read = ReadAPI(state)
|
|
18
|
+
InternalReaderName = Literal["excel", "cdf", "ontology", "yaml"]
|
|
23
19
|
|
|
24
20
|
|
|
25
21
|
@session_class_wrapper
|
|
@@ -27,28 +23,43 @@ class ReadAPI:
|
|
|
27
23
|
def __init__(self, state: SessionState) -> None:
|
|
28
24
|
self._state = state
|
|
29
25
|
|
|
30
|
-
def __call__(self, name: str, io: str | Path, **kwargs: Any) -> IssueList:
|
|
31
|
-
"""Provides access to
|
|
26
|
+
def __call__(self, name: str, io: str | Path | DataModelIdentifier, **kwargs: Any) -> IssueList:
|
|
27
|
+
"""Provides access to internal data model readers and external data model
|
|
28
|
+
reader plugins.
|
|
32
29
|
|
|
33
30
|
Args:
|
|
34
|
-
name (str): The name of format (e.g. Excel)
|
|
35
|
-
io (str | Path | None): The input/output interface for the
|
|
36
|
-
**kwargs (Any): Additional keyword arguments for the
|
|
31
|
+
name (str): The name of format (e.g. Excel) reader is handling.
|
|
32
|
+
io (str | Path | | DataModelIdentifier | None): The input/output interface for the reader.
|
|
33
|
+
**kwargs (Any): Additional keyword arguments for the reader.
|
|
34
|
+
|
|
35
|
+
!!! note "io"
|
|
36
|
+
The `io` parameter can be a file path, sting, or a DataModelIdentifier
|
|
37
|
+
depending on the reader's requirements.
|
|
37
38
|
|
|
38
39
|
!!! note "kwargs"
|
|
39
|
-
Users must consult the documentation of the
|
|
40
|
+
Users must consult the documentation of the reader to understand
|
|
40
41
|
what keyword arguments are supported.
|
|
41
42
|
"""
|
|
42
43
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
# Clean the input name once before matching.
|
|
45
|
+
clean_name: InternalReaderName | str = name.strip().lower()
|
|
46
|
+
|
|
47
|
+
# The match statement cleanly handles each case.
|
|
48
|
+
match clean_name:
|
|
49
|
+
case "excel":
|
|
50
|
+
return self.excel(cast(str | Path, io), **kwargs)
|
|
51
|
+
|
|
52
|
+
case "cdf":
|
|
53
|
+
return self.cdf(cast(DataModelIdentifier, io))
|
|
54
|
+
|
|
55
|
+
case "ontology":
|
|
56
|
+
return self.ontology(cast(str | Path, io))
|
|
57
|
+
|
|
58
|
+
case "yaml":
|
|
59
|
+
return self.yaml(cast(str | Path, io), **kwargs)
|
|
60
|
+
|
|
61
|
+
case _: # The wildcard '_' acts as the default 'else' case.
|
|
62
|
+
return self._plugin(name, cast(str | Path, io), **kwargs)
|
|
52
63
|
|
|
53
64
|
def _plugin(self, name: str, io: str | Path, **kwargs: Any) -> IssueList:
|
|
54
65
|
"""Provides access to the external plugins for data model importing.
|
|
@@ -82,6 +93,34 @@ class ReadAPI:
|
|
|
82
93
|
|
|
83
94
|
return self._state.data_model_import(plugin().configure(io=path, **kwargs))
|
|
84
95
|
|
|
96
|
+
def cdf(self, io: DataModelIdentifier) -> IssueList:
|
|
97
|
+
"""Reads a Data Model from CDF to the knowledge graph.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
io: Tuple of strings with the id of a CDF Data Model.
|
|
101
|
+
Notation as follows (<name_of_space>, <name_of_data_model>, <data_model_version>)
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
```python
|
|
105
|
+
neat.read.cdf.data_model(("example_data_model_space", "EXAMPLE_DATA_MODEL", "v1"))
|
|
106
|
+
```
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
data_model_id = DataModelId.load(io)
|
|
110
|
+
|
|
111
|
+
if not data_model_id.version:
|
|
112
|
+
raise NeatSessionError("Data model version is required to read a data model.")
|
|
113
|
+
|
|
114
|
+
self._state._raise_exception_if_condition_not_met(
|
|
115
|
+
"Read data model from CDF",
|
|
116
|
+
empty_data_model_store_required=True,
|
|
117
|
+
client_required=True,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return self._state.data_model_import(
|
|
121
|
+
importers.DMSImporter.from_data_model_id(cast(NeatClient, self._state.client), data_model_id)
|
|
122
|
+
)
|
|
123
|
+
|
|
85
124
|
def excel(self, io: str | Path, *, enable_manual_edit: bool = False) -> IssueList:
|
|
86
125
|
"""Reads a Neat Excel Data Model to the data model store.
|
|
87
126
|
The data model spreadsheets may contain conceptual or physical data model definitions.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from cognite.neat.core._data_model.models.conceptual._verified import ConceptualDataModel
|
|
2
|
+
from cognite.neat.core._data_model.models.physical._verified import PhysicalDataModel
|
|
3
|
+
from cognite.neat.session._session._data_model._read import ReadAPI
|
|
4
|
+
from cognite.neat.session._session._data_model._show import ShowAPI
|
|
5
|
+
from cognite.neat.session._state import SessionState
|
|
6
|
+
from cognite.neat.session.exceptions import session_class_wrapper
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@session_class_wrapper
|
|
10
|
+
class DataModelAPI:
|
|
11
|
+
"""API for managing data models in NEAT session."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, state: SessionState) -> None:
|
|
14
|
+
self._state = state
|
|
15
|
+
self.read = ReadAPI(state)
|
|
16
|
+
self.show = ShowAPI(state)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def physical(self) -> PhysicalDataModel | None:
|
|
20
|
+
"""Access to the physical data model level."""
|
|
21
|
+
return self._state.data_model_store.try_get_last_physical_data_model
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def conceptual(self) -> ConceptualDataModel | None:
|
|
25
|
+
"""Access to the conceptual data model level."""
|
|
26
|
+
return self._state.data_model_store.try_get_last_conceptual_data_model
|
|
27
|
+
|
|
28
|
+
def _repr_html_(self) -> str:
|
|
29
|
+
if self._state.data_model_store.empty:
|
|
30
|
+
return (
|
|
31
|
+
"<strong>No data model</strong>. Get started by reading data model with the <em>.read</em> attribute."
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
output = []
|
|
35
|
+
|
|
36
|
+
if self._state.data_model_store.provenance:
|
|
37
|
+
if self.physical:
|
|
38
|
+
html = self.physical._repr_html_()
|
|
39
|
+
if self.conceptual:
|
|
40
|
+
html = self.conceptual._repr_html_()
|
|
41
|
+
output.append(f"<H2>Data Model</H2><br />{html}")
|
|
42
|
+
|
|
43
|
+
return "<br />".join(output)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import networkx as nx
|
|
4
|
+
from IPython.display import HTML, display
|
|
5
|
+
from pyvis.network import Network as PyVisNetwork # type: ignore
|
|
6
|
+
|
|
7
|
+
from cognite.neat.core._constants import IN_NOTEBOOK, IN_PYODIDE
|
|
8
|
+
from cognite.neat.core._data_model.analysis._base import DataModelAnalysis
|
|
9
|
+
from cognite.neat.core._utils.io_ import to_directory_compatible
|
|
10
|
+
from cognite.neat.core._utils.rdf_ import uri_display_name
|
|
11
|
+
from cognite.neat.session._show import _generate_hex_color_per_type
|
|
12
|
+
from cognite.neat.session._state import SessionState
|
|
13
|
+
from cognite.neat.session.exceptions import NeatSessionError, session_class_wrapper
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@session_class_wrapper
|
|
17
|
+
class ShowAPI:
|
|
18
|
+
def __init__(self, state: SessionState) -> None:
|
|
19
|
+
self._state = state
|
|
20
|
+
|
|
21
|
+
def __call__(self) -> Any:
|
|
22
|
+
"""Generates a visualization of the data model without implements."""
|
|
23
|
+
if self._state.data_model_store.empty:
|
|
24
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to read a data model.")
|
|
25
|
+
|
|
26
|
+
last_target = self._state.data_model_store.provenance[-1].target_entity
|
|
27
|
+
data_model = last_target.physical or last_target.conceptual
|
|
28
|
+
analysis = DataModelAnalysis(physical=last_target.physical, conceptual=last_target.conceptual)
|
|
29
|
+
|
|
30
|
+
if last_target.physical is not None:
|
|
31
|
+
di_graph = analysis._physical_di_graph(format="data-model")
|
|
32
|
+
else:
|
|
33
|
+
di_graph = analysis._conceptual_di_graph(format="data-model")
|
|
34
|
+
|
|
35
|
+
identifier = to_directory_compatible(str(data_model.metadata.identifier))
|
|
36
|
+
name = f"{identifier}.html"
|
|
37
|
+
return self._generate_visualization(di_graph, name)
|
|
38
|
+
|
|
39
|
+
def implements(self) -> Any:
|
|
40
|
+
"""Generates a visualization of implements of the data model concepts, showing
|
|
41
|
+
the inheritance between the concepts in the data model."""
|
|
42
|
+
if self._state.data_model_store.empty:
|
|
43
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to read a data model.")
|
|
44
|
+
|
|
45
|
+
last_target = self._state.data_model_store.provenance[-1].target_entity
|
|
46
|
+
data_model = last_target.physical or last_target.conceptual
|
|
47
|
+
analysis = DataModelAnalysis(physical=last_target.physical, conceptual=last_target.conceptual)
|
|
48
|
+
|
|
49
|
+
if last_target.physical is not None:
|
|
50
|
+
di_graph = analysis._physical_di_graph(format="implements")
|
|
51
|
+
else:
|
|
52
|
+
di_graph = analysis._conceptual_di_graph(format="implements")
|
|
53
|
+
identifier = to_directory_compatible(str(data_model.metadata.identifier))
|
|
54
|
+
name = f"{identifier}_implements.html"
|
|
55
|
+
return self._generate_visualization(di_graph, name)
|
|
56
|
+
|
|
57
|
+
def provenance(self) -> Any:
|
|
58
|
+
if not self._state.data_model_store.provenance:
|
|
59
|
+
raise NeatSessionError("No data model available. Try using [bold].read[/bold] to load data model.")
|
|
60
|
+
|
|
61
|
+
di_graph = self._generate_provenance_di_graph()
|
|
62
|
+
unique_hash = self._state.data_model_store.calculate_provenance_hash(shorten=True)
|
|
63
|
+
return self._generate_visualization(di_graph, name=f"data_model_provenance_{unique_hash}.html")
|
|
64
|
+
|
|
65
|
+
def _generate_visualization(self, di_graph: nx.DiGraph, name: str) -> Any:
|
|
66
|
+
if not IN_NOTEBOOK:
|
|
67
|
+
raise NeatSessionError("Visualization is only available in Jupyter notebooks!")
|
|
68
|
+
|
|
69
|
+
net = PyVisNetwork(
|
|
70
|
+
notebook=IN_NOTEBOOK,
|
|
71
|
+
cdn_resources="remote",
|
|
72
|
+
directed=True,
|
|
73
|
+
height="750px",
|
|
74
|
+
width="100%",
|
|
75
|
+
select_menu=IN_NOTEBOOK,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Change the plotting layout
|
|
79
|
+
net.repulsion(
|
|
80
|
+
node_distance=100,
|
|
81
|
+
central_gravity=0.3,
|
|
82
|
+
spring_length=200,
|
|
83
|
+
spring_strength=0.05,
|
|
84
|
+
damping=0.09,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
net.from_nx(di_graph)
|
|
88
|
+
if IN_PYODIDE:
|
|
89
|
+
net.write_html(name)
|
|
90
|
+
return display(HTML(name))
|
|
91
|
+
|
|
92
|
+
else:
|
|
93
|
+
return net.show(name)
|
|
94
|
+
|
|
95
|
+
def _generate_provenance_di_graph(self) -> nx.DiGraph:
|
|
96
|
+
di_graph = nx.DiGraph()
|
|
97
|
+
hex_colored_types = _generate_hex_color_per_type(["Agent", "Entity", "Activity", "Export", "Pruned"])
|
|
98
|
+
|
|
99
|
+
for change in self._state.data_model_store.provenance:
|
|
100
|
+
source = uri_display_name(change.source_entity.id_)
|
|
101
|
+
target = uri_display_name(change.target_entity.id_)
|
|
102
|
+
agent = uri_display_name(change.agent.id_)
|
|
103
|
+
|
|
104
|
+
di_graph.add_node(
|
|
105
|
+
source,
|
|
106
|
+
label=source,
|
|
107
|
+
type="Entity",
|
|
108
|
+
title="Entity",
|
|
109
|
+
color=hex_colored_types["Entity"],
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
di_graph.add_node(
|
|
113
|
+
target,
|
|
114
|
+
label=target,
|
|
115
|
+
type="Entity",
|
|
116
|
+
title="Entity",
|
|
117
|
+
color=hex_colored_types["Entity"],
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
di_graph.add_node(
|
|
121
|
+
agent,
|
|
122
|
+
label=agent,
|
|
123
|
+
type="Agent",
|
|
124
|
+
title="Agent",
|
|
125
|
+
color=hex_colored_types["Agent"],
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
di_graph.add_edge(source, agent, label="used", color="grey")
|
|
129
|
+
di_graph.add_edge(agent, target, label="generated", color="grey")
|
|
130
|
+
|
|
131
|
+
for (
|
|
132
|
+
source_id,
|
|
133
|
+
exports,
|
|
134
|
+
) in self._state.data_model_store.exports_by_source_entity_id.items():
|
|
135
|
+
source_shorten = uri_display_name(source_id)
|
|
136
|
+
for export in exports:
|
|
137
|
+
export_id = uri_display_name(export.target_entity.id_)
|
|
138
|
+
di_graph.add_node(
|
|
139
|
+
export_id,
|
|
140
|
+
label=export_id,
|
|
141
|
+
type="Export",
|
|
142
|
+
title="Export",
|
|
143
|
+
color=hex_colored_types["Export"],
|
|
144
|
+
)
|
|
145
|
+
di_graph.add_edge(source_shorten, export_id, label="exported", color="grey")
|
|
146
|
+
|
|
147
|
+
return di_graph
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 0.123.
|
|
3
|
+
Version: 0.123.22
|
|
4
4
|
Summary: Knowledge graph transformation
|
|
5
5
|
Project-URL: Documentation, https://cognite-neat.readthedocs-hosted.com/
|
|
6
6
|
Project-URL: Homepage, https://cognite-neat.readthedocs-hosted.com/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cognite/neat/__init__.py,sha256=12StS1dzH9_MElqxGvLWrNsxCJl9Hv8A2a9D0E5OD_U,193
|
|
2
|
-
cognite/neat/_version.py,sha256=
|
|
2
|
+
cognite/neat/_version.py,sha256=F_PTAPIzLmGDm4RPxIsNpijbHtxGJrALannZQ5hHcUY,47
|
|
3
3
|
cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
cognite/neat/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
cognite/neat/core/_config.py,sha256=WT1BS8uADcFvGoUYOOfwFOVq_VBl472TisdoA3wLick,280
|
|
@@ -185,13 +185,16 @@ cognite/neat/session/_to.py,sha256=_R-UB3iEIQoa12kTD7tuSrRDdbySQXQg_mzbn5t-7bg,1
|
|
|
185
185
|
cognite/neat/session/_wizard.py,sha256=hARNNzD5Zfkk_V147rIjOLVvrFaqzXGXWhZuH1NJG3M,1486
|
|
186
186
|
cognite/neat/session/exceptions.py,sha256=z5jxwfVTXDCCFZKTTYVIaksNKqb9CMa2tyIZgyNL3Us,3475
|
|
187
187
|
cognite/neat/session/_session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
188
|
-
cognite/neat/session/_session/_data_model.py,sha256=
|
|
188
|
+
cognite/neat/session/_session/_data_model/__init__.py,sha256=417QF6wm3r-bLTmhYGXL_5XnEFShCklLCoGg3YBOIqY,62
|
|
189
|
+
cognite/neat/session/_session/_data_model/_read.py,sha256=B_mVOt_9uKzG5vbrZG2Uf5D0FQGjcCDEdfAhIp2veUI,7902
|
|
190
|
+
cognite/neat/session/_session/_data_model/_routes.py,sha256=wF83vLtmgyGCM2zxnHq0m3-sPQXQamObc58u2kSN7nQ,1666
|
|
191
|
+
cognite/neat/session/_session/_data_model/_show.py,sha256=yX4BTIeBzcCcllfJvGm8g4qy13heFmcJtpXPixI8T2o,5835
|
|
189
192
|
cognite/neat/session/_state/README.md,sha256=o6N7EL98lgyWffw8IoEUf2KG5uSKveD5__TW45YzVjA,902
|
|
190
193
|
cognite/neat/session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4dvca6n48bu_bI,120
|
|
191
194
|
cognite/neat/session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
|
|
192
195
|
cognite/neat/session/engine/_interface.py,sha256=3W-cYr493c_mW3P5O6MKN1xEQg3cA7NHR_ev3zdF9Vk,533
|
|
193
196
|
cognite/neat/session/engine/_load.py,sha256=g52uYakQM03VqHt_RDHtpHso1-mFFifH5M4T2ScuH8A,5198
|
|
194
|
-
cognite_neat-0.123.
|
|
195
|
-
cognite_neat-0.123.
|
|
196
|
-
cognite_neat-0.123.
|
|
197
|
-
cognite_neat-0.123.
|
|
197
|
+
cognite_neat-0.123.22.dist-info/METADATA,sha256=cIvYIW2976HqHJSdiseJuSEG3om5ldv2Z-csEbZQt6Y,9172
|
|
198
|
+
cognite_neat-0.123.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
199
|
+
cognite_neat-0.123.22.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
200
|
+
cognite_neat-0.123.22.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|