cognite-neat 0.94.0__py3-none-any.whl → 0.96.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 +16 -0
- cognite/neat/_issues/_base.py +24 -8
- cognite/neat/_issues/errors/_resources.py +2 -2
- cognite/neat/_issues/warnings/_models.py +20 -2
- cognite/neat/_rules/exporters/_rules2excel.py +8 -2
- cognite/neat/_rules/models/dms/_rules.py +16 -0
- cognite/neat/_rules/models/dms/_rules_input.py +16 -0
- cognite/neat/_rules/models/information/_rules.py +14 -0
- cognite/neat/_rules/models/information/_rules_input.py +14 -0
- cognite/neat/_rules/transformers/__init__.py +2 -0
- cognite/neat/_rules/transformers/_converters.py +206 -37
- cognite/neat/_session/_base.py +14 -7
- cognite/neat/_session/_inspect.py +89 -0
- cognite/neat/_session/_prepare.py +65 -8
- cognite/neat/_session/_read.py +33 -4
- cognite/neat/_session/_set.py +23 -0
- cognite/neat/_session/_show.py +156 -202
- cognite/neat/_session/_state.py +24 -4
- cognite/neat/_session/_to.py +3 -0
- cognite/neat/_session/exceptions.py +9 -6
- cognite/neat/_store/_base.py +11 -8
- cognite/neat/_utils/collection_.py +4 -0
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.94.0.dist-info → cognite_neat-0.96.0.dist-info}/METADATA +3 -5
- {cognite_neat-0.94.0.dist-info → cognite_neat-0.96.0.dist-info}/RECORD +28 -26
- {cognite_neat-0.94.0.dist-info → cognite_neat-0.96.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.94.0.dist-info → cognite_neat-0.96.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.94.0.dist-info → cognite_neat-0.96.0.dist-info}/entry_points.txt +0 -0
cognite/neat/_session/_show.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import colorsys
|
|
1
2
|
import random
|
|
2
3
|
from typing import Any, cast
|
|
3
4
|
|
|
4
5
|
import networkx as nx
|
|
5
|
-
from
|
|
6
|
-
from IPython.display import display
|
|
6
|
+
from pyvis.network import Network as PyVisNetwork # type: ignore
|
|
7
7
|
|
|
8
|
+
from cognite.neat._constants import IN_NOTEBOOK
|
|
8
9
|
from cognite.neat._rules._constants import EntityTypes
|
|
9
10
|
from cognite.neat._rules.models.dms._rules import DMSRules
|
|
10
11
|
from cognite.neat._rules.models.entities._single_value import ClassEntity, ViewEntity
|
|
@@ -13,8 +14,10 @@ from cognite.neat._session.exceptions import NeatSessionError
|
|
|
13
14
|
from cognite.neat._utils.rdf_ import remove_namespace_from_uri
|
|
14
15
|
|
|
15
16
|
from ._state import SessionState
|
|
17
|
+
from .exceptions import intercept_session_exceptions
|
|
16
18
|
|
|
17
19
|
|
|
20
|
+
@intercept_session_exceptions
|
|
18
21
|
class ShowAPI:
|
|
19
22
|
def __init__(self, state: SessionState) -> None:
|
|
20
23
|
self._state = state
|
|
@@ -22,253 +25,204 @@ class ShowAPI:
|
|
|
22
25
|
self.instances = ShowInstanceAPI(self._state)
|
|
23
26
|
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
@intercept_session_exceptions
|
|
29
|
+
class ShowBaseAPI:
|
|
26
30
|
def __init__(self, state: SessionState) -> None:
|
|
27
31
|
self._state = state
|
|
28
32
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
widget.set_style(widget_style)
|
|
39
|
-
|
|
40
|
-
widget.graph.add_graph_from_networkx(NxGraph)
|
|
41
|
-
print("Max of 100 nodes and edges are displayed, which are randomly selected.")
|
|
42
|
-
|
|
43
|
-
return display(widget)
|
|
44
|
-
|
|
45
|
-
def _generate_instance_di_graph_and_types(self) -> tuple[nx.DiGraph, set[str]]:
|
|
46
|
-
query = """
|
|
47
|
-
SELECT ?s ?p ?o ?ts ?to WHERE {
|
|
48
|
-
?s ?p ?o .
|
|
49
|
-
FILTER(isIRI(?o)) # Example filter to check if ?o is an IRI (object type)
|
|
50
|
-
FILTER(BOUND(?o))
|
|
51
|
-
FILTER(?p != rdf:type)
|
|
52
|
-
|
|
53
|
-
?s a ?ts .
|
|
54
|
-
?o a ?to .
|
|
55
|
-
}
|
|
56
|
-
LIMIT 100
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
NxGraph = nx.DiGraph()
|
|
60
|
-
|
|
61
|
-
types = set()
|
|
62
|
-
|
|
63
|
-
for ( # type: ignore
|
|
64
|
-
subject,
|
|
65
|
-
property_,
|
|
66
|
-
object,
|
|
67
|
-
subject_type,
|
|
68
|
-
object_type,
|
|
69
|
-
) in self._state.store.graph.query(query):
|
|
70
|
-
subject = remove_namespace_from_uri(subject)
|
|
71
|
-
property_ = remove_namespace_from_uri(property_)
|
|
72
|
-
object = remove_namespace_from_uri(object)
|
|
73
|
-
subject_type = remove_namespace_from_uri(subject_type)
|
|
74
|
-
object_type = remove_namespace_from_uri(object_type)
|
|
75
|
-
|
|
76
|
-
NxGraph.add_node(subject, label=subject, type=subject_type)
|
|
77
|
-
NxGraph.add_node(object, label=object, type=object_type)
|
|
78
|
-
NxGraph.add_edge(subject, object, label=property_)
|
|
79
|
-
|
|
80
|
-
types.add(subject_type)
|
|
81
|
-
types.add(object_type)
|
|
82
|
-
|
|
83
|
-
return NxGraph, types
|
|
84
|
-
|
|
85
|
-
def _generate_cytoscape_widget_style(self, types: set[str]) -> list[dict]:
|
|
86
|
-
widget_style = [
|
|
87
|
-
{
|
|
88
|
-
"selector": "edge",
|
|
89
|
-
"style": {
|
|
90
|
-
"width": 1,
|
|
91
|
-
"target-arrow-shape": "triangle",
|
|
92
|
-
"curve-style": "bezier",
|
|
93
|
-
"label": "data(label)",
|
|
94
|
-
"font-size": "8px",
|
|
95
|
-
"line-color": "black",
|
|
96
|
-
"target-arrow-color": "black",
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
colors = self._generate_hex_colors(len(types))
|
|
102
|
-
|
|
103
|
-
for i, type_ in enumerate(types):
|
|
104
|
-
widget_style.append(self._generate_node_cytoscape_style(type_, colors[i]))
|
|
105
|
-
|
|
106
|
-
return widget_style
|
|
107
|
-
|
|
108
|
-
@staticmethod
|
|
109
|
-
def _generate_hex_colors(n: int) -> list[str]:
|
|
110
|
-
"""Generate a list of N random HEX color codes."""
|
|
111
|
-
random.seed(42) # Set a seed for deterministic behavior
|
|
112
|
-
hex_colors = []
|
|
113
|
-
for _ in range(n):
|
|
114
|
-
color = f"#{random.randint(0, 0xFFFFFF):06x}"
|
|
115
|
-
hex_colors.append(color)
|
|
116
|
-
return hex_colors
|
|
117
|
-
|
|
118
|
-
@staticmethod
|
|
119
|
-
def _generate_node_cytoscape_style(type_: str, color: str) -> dict:
|
|
120
|
-
template = {
|
|
121
|
-
"css": {
|
|
122
|
-
"content": "data(label)",
|
|
123
|
-
"text-valign": "center",
|
|
124
|
-
"color": "black",
|
|
125
|
-
"font-size": "10px",
|
|
126
|
-
"width": "mapData(score, 0, 1, 10, 50)",
|
|
127
|
-
"height": "mapData(score, 0, 1, 10, 50)",
|
|
128
|
-
},
|
|
129
|
-
}
|
|
33
|
+
def _generate_visualization(self, di_graph: nx.DiGraph, name: str) -> Any:
|
|
34
|
+
net = PyVisNetwork(
|
|
35
|
+
notebook=IN_NOTEBOOK,
|
|
36
|
+
cdn_resources="remote",
|
|
37
|
+
directed=True,
|
|
38
|
+
height="750px",
|
|
39
|
+
width="100%",
|
|
40
|
+
select_menu=IN_NOTEBOOK,
|
|
41
|
+
)
|
|
130
42
|
|
|
131
|
-
|
|
132
|
-
|
|
43
|
+
# Change the plotting layout
|
|
44
|
+
net.repulsion(
|
|
45
|
+
node_distance=100,
|
|
46
|
+
central_gravity=0.3,
|
|
47
|
+
spring_length=200,
|
|
48
|
+
spring_strength=0.05,
|
|
49
|
+
damping=0.09,
|
|
50
|
+
)
|
|
133
51
|
|
|
134
|
-
|
|
52
|
+
net.from_nx(di_graph)
|
|
53
|
+
return net.show(name)
|
|
135
54
|
|
|
136
55
|
|
|
137
|
-
|
|
56
|
+
@intercept_session_exceptions
|
|
57
|
+
class ShowDataModelAPI(ShowBaseAPI):
|
|
138
58
|
def __init__(self, state: SessionState) -> None:
|
|
59
|
+
super().__init__(state)
|
|
139
60
|
self._state = state
|
|
140
61
|
|
|
141
62
|
def __call__(self) -> Any:
|
|
142
|
-
if not self._state.
|
|
63
|
+
if not self._state.has_verified_rules:
|
|
143
64
|
raise NeatSessionError(
|
|
144
65
|
"No verified data model available. Try using [bold].verify()[/bold] to verify data model."
|
|
145
66
|
)
|
|
146
67
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
68
|
+
try:
|
|
69
|
+
di_graph = self._generate_dms_di_graph(self._state.last_verified_dms_rules)
|
|
70
|
+
name = "dms_data_model.html"
|
|
71
|
+
except NeatSessionError:
|
|
72
|
+
di_graph = self._generate_info_di_graph(self._state.last_verified_information_rules)
|
|
73
|
+
name = "information_data_model.html"
|
|
151
74
|
|
|
152
|
-
|
|
153
|
-
widget.graph.add_graph_from_networkx(NxGraph)
|
|
154
|
-
return display(widget)
|
|
75
|
+
return self._generate_visualization(di_graph, name)
|
|
155
76
|
|
|
156
|
-
def _generate_dms_di_graph(self) -> nx.DiGraph:
|
|
77
|
+
def _generate_dms_di_graph(self, rules: DMSRules) -> nx.DiGraph:
|
|
157
78
|
"""Generate a DiGraph from the last verified DMS rules."""
|
|
158
|
-
|
|
79
|
+
di_graph = nx.DiGraph()
|
|
159
80
|
|
|
160
81
|
# Add nodes and edges from Views sheet
|
|
161
|
-
for view in
|
|
82
|
+
for view in rules.views:
|
|
162
83
|
# if possible use human readable label coming from the view name
|
|
163
|
-
if not
|
|
164
|
-
|
|
84
|
+
if not di_graph.has_node(view.view.suffix):
|
|
85
|
+
di_graph.add_node(view.view.suffix, label=view.name or view.view.suffix)
|
|
165
86
|
|
|
166
87
|
# add implements as edges
|
|
167
88
|
if view.implements:
|
|
168
89
|
for implement in view.implements:
|
|
169
|
-
if not
|
|
170
|
-
|
|
90
|
+
if not di_graph.has_node(implement.suffix):
|
|
91
|
+
di_graph.add_node(implement.suffix, label=implement.suffix)
|
|
171
92
|
|
|
172
|
-
|
|
93
|
+
di_graph.add_edge(
|
|
94
|
+
view.view.suffix,
|
|
95
|
+
implement.suffix,
|
|
96
|
+
label="implements",
|
|
97
|
+
dashes=True,
|
|
98
|
+
)
|
|
173
99
|
|
|
174
100
|
# Add nodes and edges from Properties sheet
|
|
175
|
-
for prop_ in
|
|
101
|
+
for prop_ in rules.properties:
|
|
176
102
|
if prop_.connection and isinstance(prop_.value_type, ViewEntity):
|
|
177
|
-
if not
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
103
|
+
if not di_graph.has_node(prop_.view.suffix):
|
|
104
|
+
di_graph.add_node(prop_.view.suffix, label=prop_.view.suffix)
|
|
105
|
+
di_graph.add_edge(
|
|
106
|
+
prop_.view.suffix,
|
|
107
|
+
prop_.value_type.suffix,
|
|
108
|
+
label=prop_.name or prop_.property_,
|
|
109
|
+
)
|
|
182
110
|
|
|
183
|
-
return
|
|
111
|
+
return di_graph
|
|
184
112
|
|
|
185
|
-
def _generate_info_di_graph(self) -> nx.DiGraph:
|
|
186
|
-
"""Generate
|
|
113
|
+
def _generate_info_di_graph(self, rules: InformationRules) -> nx.DiGraph:
|
|
114
|
+
"""Generate DiGraph representing information data model."""
|
|
187
115
|
|
|
188
|
-
|
|
116
|
+
di_graph = nx.DiGraph()
|
|
189
117
|
|
|
190
118
|
# Add nodes and edges from Views sheet
|
|
191
|
-
for class_ in
|
|
119
|
+
for class_ in rules.classes:
|
|
192
120
|
# if possible use human readable label coming from the view name
|
|
193
|
-
if not
|
|
194
|
-
|
|
121
|
+
if not di_graph.has_node(class_.class_.suffix):
|
|
122
|
+
di_graph.add_node(
|
|
195
123
|
class_.class_.suffix,
|
|
196
124
|
label=class_.name or class_.class_.suffix,
|
|
197
125
|
)
|
|
198
126
|
|
|
199
|
-
# add
|
|
127
|
+
# add subClassOff as edges
|
|
200
128
|
if class_.parent:
|
|
201
129
|
for parent in class_.parent:
|
|
202
|
-
if not
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
130
|
+
if not di_graph.has_node(parent.suffix):
|
|
131
|
+
di_graph.add_node(parent.suffix, label=parent.suffix)
|
|
132
|
+
di_graph.add_edge(
|
|
133
|
+
class_.class_.suffix,
|
|
134
|
+
parent.suffix,
|
|
135
|
+
label="subClassOf",
|
|
136
|
+
dashes=True,
|
|
137
|
+
)
|
|
206
138
|
|
|
207
139
|
# Add nodes and edges from Properties sheet
|
|
208
|
-
for prop_ in
|
|
140
|
+
for prop_ in rules.properties:
|
|
209
141
|
if prop_.type_ == EntityTypes.object_property:
|
|
210
|
-
if not
|
|
211
|
-
|
|
142
|
+
if not di_graph.has_node(prop_.class_.suffix):
|
|
143
|
+
di_graph.add_node(prop_.class_.suffix, label=prop_.class_.suffix)
|
|
212
144
|
|
|
213
|
-
|
|
214
|
-
NxGraph.add_edge(
|
|
145
|
+
di_graph.add_edge(
|
|
215
146
|
prop_.class_.suffix,
|
|
216
147
|
cast(ClassEntity, prop_.value_type).suffix,
|
|
217
|
-
label=
|
|
148
|
+
label=prop_.name or prop_.property_,
|
|
218
149
|
)
|
|
219
150
|
|
|
220
|
-
return
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
"style": {
|
|
244
|
-
"width": 1,
|
|
245
|
-
"target-arrow-shape": "triangle",
|
|
246
|
-
"curve-style": "bezier",
|
|
247
|
-
"label": "data(label)",
|
|
248
|
-
"font-size": "8px",
|
|
249
|
-
"line-color": "black",
|
|
250
|
-
"target-arrow-color": "black",
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
"selector": 'edge[label = "subClassOf"]',
|
|
255
|
-
"style": {
|
|
256
|
-
"line-color": "grey",
|
|
257
|
-
"target-arrow-color": "grey",
|
|
258
|
-
"line-style": "dashed",
|
|
259
|
-
"font-size": "8px",
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
"selector": 'edge[label = "implements"]',
|
|
264
|
-
"style": {
|
|
265
|
-
"line-color": "grey",
|
|
266
|
-
"target-arrow-color": "grey",
|
|
267
|
-
"line-style": "dashed",
|
|
268
|
-
"font-size": "8px",
|
|
269
|
-
},
|
|
270
|
-
},
|
|
271
|
-
]
|
|
272
|
-
)
|
|
151
|
+
return di_graph
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@intercept_session_exceptions
|
|
155
|
+
class ShowInstanceAPI(ShowBaseAPI):
|
|
156
|
+
def __init__(self, state: SessionState) -> None:
|
|
157
|
+
super().__init__(state)
|
|
158
|
+
self._state = state
|
|
159
|
+
|
|
160
|
+
def __call__(self) -> Any:
|
|
161
|
+
if not self._state.store.graph:
|
|
162
|
+
raise NeatSessionError("No instances available. Try using [bold].read[/bold] to load instances.")
|
|
163
|
+
|
|
164
|
+
di_graph = self._generate_instance_di_graph_and_types()
|
|
165
|
+
return self._generate_visualization(di_graph, name="instances.html")
|
|
166
|
+
|
|
167
|
+
def _generate_instance_di_graph_and_types(self) -> nx.DiGraph:
|
|
168
|
+
query = """
|
|
169
|
+
SELECT ?s ?p ?o ?ts ?to WHERE {
|
|
170
|
+
?s ?p ?o .
|
|
171
|
+
FILTER(isIRI(?o)) # Example filter to check if ?o is an IRI (object type)
|
|
172
|
+
FILTER(BOUND(?o))
|
|
173
|
+
FILTER(?p != rdf:type)
|
|
273
174
|
|
|
274
|
-
|
|
175
|
+
?s a ?ts .
|
|
176
|
+
?o a ?to .
|
|
177
|
+
}
|
|
178
|
+
LIMIT 200
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
di_graph = nx.DiGraph()
|
|
182
|
+
|
|
183
|
+
types = [type_ for type_, _ in self._state.store.queries.summarize_instances()]
|
|
184
|
+
hex_colored_types = self._generate_hex_color_per_type(types)
|
|
185
|
+
|
|
186
|
+
for ( # type: ignore
|
|
187
|
+
subject,
|
|
188
|
+
property_,
|
|
189
|
+
object,
|
|
190
|
+
subject_type,
|
|
191
|
+
object_type,
|
|
192
|
+
) in self._state.store.graph.query(query):
|
|
193
|
+
subject = remove_namespace_from_uri(subject)
|
|
194
|
+
property_ = remove_namespace_from_uri(property_)
|
|
195
|
+
object = remove_namespace_from_uri(object)
|
|
196
|
+
subject_type = remove_namespace_from_uri(subject_type)
|
|
197
|
+
object_type = remove_namespace_from_uri(object_type)
|
|
198
|
+
|
|
199
|
+
di_graph.add_node(
|
|
200
|
+
subject,
|
|
201
|
+
label=subject,
|
|
202
|
+
type=subject_type,
|
|
203
|
+
title=subject_type,
|
|
204
|
+
color=hex_colored_types[subject_type],
|
|
205
|
+
)
|
|
206
|
+
di_graph.add_node(
|
|
207
|
+
object,
|
|
208
|
+
label=object,
|
|
209
|
+
type=object_type,
|
|
210
|
+
title=object_type,
|
|
211
|
+
color=hex_colored_types[object_type],
|
|
212
|
+
)
|
|
213
|
+
di_graph.add_edge(subject, object, label=property_, color="grey")
|
|
214
|
+
|
|
215
|
+
return di_graph
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def _generate_hex_color_per_type(types: list[str]) -> dict:
|
|
219
|
+
hex_colored_types = {}
|
|
220
|
+
random.seed(381)
|
|
221
|
+
for type_ in types:
|
|
222
|
+
hue = random.random()
|
|
223
|
+
saturation = random.uniform(0.5, 1.0)
|
|
224
|
+
lightness = random.uniform(0.4, 0.6)
|
|
225
|
+
rgb = colorsys.hls_to_rgb(hue, lightness, saturation)
|
|
226
|
+
hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}"
|
|
227
|
+
hex_colored_types[type_] = hex_color
|
|
228
|
+
return hex_colored_types
|
cognite/neat/_session/_state.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from typing import Literal, cast
|
|
3
3
|
|
|
4
|
+
from cognite.neat._issues import IssueList
|
|
4
5
|
from cognite.neat._rules._shared import ReadRules, VerifiedRules
|
|
5
6
|
from cognite.neat._rules.models.dms._rules import DMSRules
|
|
6
7
|
from cognite.neat._rules.models.information._rules import InformationRules
|
|
@@ -15,6 +16,7 @@ class SessionState:
|
|
|
15
16
|
store_type: Literal["memory", "oxigraph"]
|
|
16
17
|
input_rules: list[ReadRules] = field(default_factory=list)
|
|
17
18
|
verified_rules: list[VerifiedRules] = field(default_factory=list)
|
|
19
|
+
issue_lists: list[IssueList] = field(default_factory=list)
|
|
18
20
|
_store: NeatGraphStore | None = field(init=False, default=None)
|
|
19
21
|
|
|
20
22
|
@property
|
|
@@ -49,21 +51,39 @@ class SessionState:
|
|
|
49
51
|
return self.verified_rules[-1]
|
|
50
52
|
|
|
51
53
|
@property
|
|
52
|
-
def last_verified_dms_rules(self) -> DMSRules
|
|
54
|
+
def last_verified_dms_rules(self) -> DMSRules:
|
|
53
55
|
if self.verified_rules:
|
|
54
56
|
for rules in self.verified_rules[::-1]:
|
|
55
57
|
if isinstance(rules, DMSRules):
|
|
56
58
|
return rules
|
|
57
|
-
|
|
59
|
+
|
|
60
|
+
raise NeatSessionError(
|
|
61
|
+
'No verified DMS data model. Try using [bold].convert("DMS")[/bold]'
|
|
62
|
+
" to convert verified information model to verified DMS model."
|
|
63
|
+
)
|
|
58
64
|
|
|
59
65
|
@property
|
|
60
|
-
def last_verified_information_rules(self) -> InformationRules
|
|
66
|
+
def last_verified_information_rules(self) -> InformationRules:
|
|
61
67
|
if self.verified_rules:
|
|
62
68
|
for rules in self.verified_rules[::-1]:
|
|
63
69
|
if isinstance(rules, InformationRules):
|
|
64
70
|
return rules
|
|
65
|
-
|
|
71
|
+
|
|
72
|
+
raise NeatSessionError(
|
|
73
|
+
"No verified information data model. Try using [bold].verify()[/bold]"
|
|
74
|
+
" to convert unverified information model to verified information model."
|
|
75
|
+
)
|
|
66
76
|
|
|
67
77
|
@property
|
|
68
78
|
def has_store(self) -> bool:
|
|
69
79
|
return self._store is not None
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def has_verified_rules(self) -> bool:
|
|
83
|
+
return bool(self.verified_rules)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def last_issues(self) -> IssueList:
|
|
87
|
+
if not self.issue_lists:
|
|
88
|
+
raise NeatSessionError("No issues available. Try using [bold].verify()[/bold] to verify a data model.")
|
|
89
|
+
return self.issue_lists[-1]
|
cognite/neat/_session/_to.py
CHANGED
|
@@ -8,8 +8,10 @@ from cognite.neat._rules import exporters
|
|
|
8
8
|
from cognite.neat._session._wizard import space_wizard
|
|
9
9
|
|
|
10
10
|
from ._state import SessionState
|
|
11
|
+
from .exceptions import intercept_session_exceptions
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
@intercept_session_exceptions
|
|
13
15
|
class ToAPI:
|
|
14
16
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
15
17
|
self._state = state
|
|
@@ -39,6 +41,7 @@ class ToAPI:
|
|
|
39
41
|
return None
|
|
40
42
|
|
|
41
43
|
|
|
44
|
+
@intercept_session_exceptions
|
|
42
45
|
class CDFToAPI:
|
|
43
46
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
44
47
|
self._client = client
|
|
@@ -23,6 +23,8 @@ def _intercept_session_exceptions(func: Callable):
|
|
|
23
23
|
return func(*args, **kwargs)
|
|
24
24
|
except NeatSessionError as e:
|
|
25
25
|
action = func.__name__
|
|
26
|
+
if action == "__call__":
|
|
27
|
+
action = func.__qualname__.removesuffix(".__call__").removesuffix("API")
|
|
26
28
|
print(f"{_PREFIX} Cannot {action}: {e}")
|
|
27
29
|
|
|
28
30
|
return wrapper
|
|
@@ -33,10 +35,11 @@ def intercept_session_exceptions(cls: type):
|
|
|
33
35
|
while to_check:
|
|
34
36
|
cls = to_check.pop()
|
|
35
37
|
for attr_name in dir(cls):
|
|
36
|
-
if
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if attr_name.startswith("_") and not attr_name == "__call__":
|
|
39
|
+
continue
|
|
40
|
+
attr = getattr(cls, attr_name)
|
|
41
|
+
if callable(attr):
|
|
42
|
+
setattr(cls, attr_name, _intercept_session_exceptions(attr))
|
|
43
|
+
elif isinstance(attr, type):
|
|
44
|
+
to_check.append(attr)
|
|
42
45
|
return cls
|
cognite/neat/_store/_base.py
CHANGED
|
@@ -367,20 +367,23 @@ class NeatGraphStore:
|
|
|
367
367
|
def _shorten_summary(self, summary: pd.DataFrame) -> pd.DataFrame:
|
|
368
368
|
"""Shorten summary to top 5 types by occurrence."""
|
|
369
369
|
top_5_rows = summary.head(5)
|
|
370
|
-
last_row = summary.tail(1)
|
|
371
370
|
|
|
372
371
|
indexes = [
|
|
373
372
|
*top_5_rows.index.tolist(),
|
|
374
|
-
"...",
|
|
375
|
-
*last_row.index.tolist(),
|
|
376
373
|
]
|
|
374
|
+
data = [
|
|
375
|
+
top_5_rows,
|
|
376
|
+
]
|
|
377
|
+
if len(summary) > 6:
|
|
378
|
+
last_row = summary.tail(1)
|
|
379
|
+
indexes += [
|
|
380
|
+
"...",
|
|
381
|
+
*last_row.index.tolist(),
|
|
382
|
+
]
|
|
383
|
+
data.extend([pd.DataFrame([["..."] * summary.shape[1]], columns=summary.columns), last_row])
|
|
377
384
|
|
|
378
385
|
shorter_summary = pd.concat(
|
|
379
|
-
|
|
380
|
-
top_5_rows,
|
|
381
|
-
pd.DataFrame([["..."] * summary.shape[1]], columns=summary.columns),
|
|
382
|
-
last_row,
|
|
383
|
-
],
|
|
386
|
+
data,
|
|
384
387
|
ignore_index=True,
|
|
385
388
|
)
|
|
386
389
|
shorter_summary.index = cast(Index, indexes)
|
|
@@ -17,3 +17,7 @@ def most_occurring_element(list_of_elements: list[T_Element]) -> T_Element:
|
|
|
17
17
|
def chunker(sequence: Sequence[T_Element], chunk_size: int) -> Iterable[Sequence[T_Element]]:
|
|
18
18
|
for i in range(0, len(sequence), chunk_size):
|
|
19
19
|
yield sequence[i : i + chunk_size]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def remove_list_elements(input_list: list, elements_to_remove: list) -> list:
|
|
23
|
+
return [element for element in input_list if element not in elements_to_remove]
|
cognite/neat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.96.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.96.0
|
|
4
4
|
Summary: Knowledge graph transformation
|
|
5
5
|
Home-page: https://cognite-neat.readthedocs-hosted.com/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -17,7 +17,6 @@ Provides-Extra: all
|
|
|
17
17
|
Provides-Extra: docs
|
|
18
18
|
Provides-Extra: google
|
|
19
19
|
Provides-Extra: graphql
|
|
20
|
-
Provides-Extra: jupyter
|
|
21
20
|
Provides-Extra: oxi
|
|
22
21
|
Provides-Extra: service
|
|
23
22
|
Requires-Dist: PyYAML
|
|
@@ -28,9 +27,7 @@ Requires-Dist: fastapi (>=0,<1) ; extra == "service" or extra == "all"
|
|
|
28
27
|
Requires-Dist: google-api-python-client ; extra == "google"
|
|
29
28
|
Requires-Dist: google-auth-oauthlib ; extra == "google"
|
|
30
29
|
Requires-Dist: gspread ; extra == "google"
|
|
31
|
-
Requires-Dist: ipycytoscape (>=1.3.3,<2.0.0)
|
|
32
30
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0) ; extra == "graphql" or extra == "all"
|
|
33
|
-
Requires-Dist: matplotlib (>=3.9.2,<4.0.0)
|
|
34
31
|
Requires-Dist: mkdocs ; extra == "docs"
|
|
35
32
|
Requires-Dist: mkdocs-autorefs (>=0.5.0,<0.6.0) ; extra == "docs"
|
|
36
33
|
Requires-Dist: mkdocs-git-authors-plugin ; extra == "docs"
|
|
@@ -49,9 +46,10 @@ Requires-Dist: pydantic (>=2,<3)
|
|
|
49
46
|
Requires-Dist: pymdown-extensions ; extra == "docs"
|
|
50
47
|
Requires-Dist: pyoxigraph (==0.3.19) ; extra == "oxi" or extra == "all"
|
|
51
48
|
Requires-Dist: python-multipart (==0.0.9) ; extra == "service" or extra == "all"
|
|
49
|
+
Requires-Dist: pyvis (>=0.3.2,<0.4.0)
|
|
52
50
|
Requires-Dist: rdflib
|
|
53
51
|
Requires-Dist: requests
|
|
54
|
-
Requires-Dist: rich[jupyter] (>=13.7.1,<14.0.0)
|
|
52
|
+
Requires-Dist: rich[jupyter] (>=13.7.1,<14.0.0)
|
|
55
53
|
Requires-Dist: schedule (>=1,<2) ; extra == "service" or extra == "all"
|
|
56
54
|
Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
|
|
57
55
|
Requires-Dist: typing_extensions (>=4.8,<5.0) ; python_version < "3.11"
|