cognite-neat 0.95.0__py3-none-any.whl → 0.96.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 +26 -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/transformers/__init__.py +2 -0
- cognite/neat/_rules/transformers/_converters.py +206 -37
- cognite/neat/_session/_base.py +44 -9
- cognite/neat/_session/_inspect.py +89 -0
- cognite/neat/_session/_prepare.py +62 -8
- cognite/neat/_session/_read.py +27 -4
- cognite/neat/_session/_set.py +23 -0
- cognite/neat/_session/_show.py +160 -201
- cognite/neat/_session/_state.py +24 -4
- cognite/neat/_store/_base.py +11 -8
- cognite/neat/_utils/collection_.py +4 -0
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.95.0.dist-info → cognite_neat-0.96.1.dist-info}/METADATA +3 -5
- {cognite_neat-0.95.0.dist-info → cognite_neat-0.96.1.dist-info}/RECORD +22 -20
- {cognite_neat-0.95.0.dist-info → cognite_neat-0.96.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.95.0.dist-info → cognite_neat-0.96.1.dist-info}/WHEEL +0 -0
- {cognite_neat-0.95.0.dist-info → cognite_neat-0.96.1.dist-info}/entry_points.txt +0 -0
cognite/neat/_session/_show.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
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
|
|
6
|
+
from IPython.display import HTML, display
|
|
7
|
+
from pyvis.network import Network as PyVisNetwork # type: ignore
|
|
7
8
|
|
|
9
|
+
from cognite.neat._constants import IN_NOTEBOOK, IN_PYODIDE
|
|
8
10
|
from cognite.neat._rules._constants import EntityTypes
|
|
9
11
|
from cognite.neat._rules.models.dms._rules import DMSRules
|
|
10
12
|
from cognite.neat._rules.models.entities._single_value import ClassEntity, ViewEntity
|
|
@@ -25,254 +27,211 @@ class ShowAPI:
|
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
@intercept_session_exceptions
|
|
28
|
-
class
|
|
30
|
+
class ShowBaseAPI:
|
|
29
31
|
def __init__(self, state: SessionState) -> None:
|
|
30
32
|
self._state = state
|
|
31
33
|
|
|
32
|
-
def
|
|
33
|
-
if not
|
|
34
|
-
raise NeatSessionError("
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
print("Max of 100 nodes and edges are displayed, which are randomly selected.")
|
|
45
|
-
|
|
46
|
-
return display(widget)
|
|
47
|
-
|
|
48
|
-
def _generate_instance_di_graph_and_types(self) -> tuple[nx.DiGraph, set[str]]:
|
|
49
|
-
query = """
|
|
50
|
-
SELECT ?s ?p ?o ?ts ?to WHERE {
|
|
51
|
-
?s ?p ?o .
|
|
52
|
-
FILTER(isIRI(?o)) # Example filter to check if ?o is an IRI (object type)
|
|
53
|
-
FILTER(BOUND(?o))
|
|
54
|
-
FILTER(?p != rdf:type)
|
|
55
|
-
|
|
56
|
-
?s a ?ts .
|
|
57
|
-
?o a ?to .
|
|
58
|
-
}
|
|
59
|
-
LIMIT 100
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
NxGraph = nx.DiGraph()
|
|
63
|
-
|
|
64
|
-
types = set()
|
|
65
|
-
|
|
66
|
-
for ( # type: ignore
|
|
67
|
-
subject,
|
|
68
|
-
property_,
|
|
69
|
-
object,
|
|
70
|
-
subject_type,
|
|
71
|
-
object_type,
|
|
72
|
-
) in self._state.store.graph.query(query):
|
|
73
|
-
subject = remove_namespace_from_uri(subject)
|
|
74
|
-
property_ = remove_namespace_from_uri(property_)
|
|
75
|
-
object = remove_namespace_from_uri(object)
|
|
76
|
-
subject_type = remove_namespace_from_uri(subject_type)
|
|
77
|
-
object_type = remove_namespace_from_uri(object_type)
|
|
78
|
-
|
|
79
|
-
NxGraph.add_node(subject, label=subject, type=subject_type)
|
|
80
|
-
NxGraph.add_node(object, label=object, type=object_type)
|
|
81
|
-
NxGraph.add_edge(subject, object, label=property_)
|
|
82
|
-
|
|
83
|
-
types.add(subject_type)
|
|
84
|
-
types.add(object_type)
|
|
85
|
-
|
|
86
|
-
return NxGraph, types
|
|
87
|
-
|
|
88
|
-
def _generate_cytoscape_widget_style(self, types: set[str]) -> list[dict]:
|
|
89
|
-
widget_style = [
|
|
90
|
-
{
|
|
91
|
-
"selector": "edge",
|
|
92
|
-
"style": {
|
|
93
|
-
"width": 1,
|
|
94
|
-
"target-arrow-shape": "triangle",
|
|
95
|
-
"curve-style": "bezier",
|
|
96
|
-
"label": "data(label)",
|
|
97
|
-
"font-size": "8px",
|
|
98
|
-
"line-color": "black",
|
|
99
|
-
"target-arrow-color": "black",
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
]
|
|
103
|
-
|
|
104
|
-
colors = self._generate_hex_colors(len(types))
|
|
105
|
-
|
|
106
|
-
for i, type_ in enumerate(types):
|
|
107
|
-
widget_style.append(self._generate_node_cytoscape_style(type_, colors[i]))
|
|
108
|
-
|
|
109
|
-
return widget_style
|
|
110
|
-
|
|
111
|
-
@staticmethod
|
|
112
|
-
def _generate_hex_colors(n: int) -> list[str]:
|
|
113
|
-
"""Generate a list of N random HEX color codes."""
|
|
114
|
-
random.seed(42) # Set a seed for deterministic behavior
|
|
115
|
-
hex_colors = []
|
|
116
|
-
for _ in range(n):
|
|
117
|
-
color = f"#{random.randint(0, 0xFFFFFF):06x}"
|
|
118
|
-
hex_colors.append(color)
|
|
119
|
-
return hex_colors
|
|
34
|
+
def _generate_visualization(self, di_graph: nx.DiGraph, name: str) -> Any:
|
|
35
|
+
if not IN_NOTEBOOK:
|
|
36
|
+
raise NeatSessionError("Visualization is only available in Jupyter notebooks!")
|
|
37
|
+
|
|
38
|
+
net = PyVisNetwork(
|
|
39
|
+
notebook=IN_NOTEBOOK,
|
|
40
|
+
cdn_resources="remote",
|
|
41
|
+
directed=True,
|
|
42
|
+
height="750px",
|
|
43
|
+
width="100%",
|
|
44
|
+
select_menu=IN_NOTEBOOK,
|
|
45
|
+
)
|
|
120
46
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"width": "mapData(score, 0, 1, 10, 50)",
|
|
130
|
-
"height": "mapData(score, 0, 1, 10, 50)",
|
|
131
|
-
},
|
|
132
|
-
}
|
|
47
|
+
# Change the plotting layout
|
|
48
|
+
net.repulsion(
|
|
49
|
+
node_distance=100,
|
|
50
|
+
central_gravity=0.3,
|
|
51
|
+
spring_length=200,
|
|
52
|
+
spring_strength=0.05,
|
|
53
|
+
damping=0.09,
|
|
54
|
+
)
|
|
133
55
|
|
|
134
|
-
|
|
135
|
-
|
|
56
|
+
net.from_nx(di_graph)
|
|
57
|
+
if IN_PYODIDE:
|
|
58
|
+
net.write_html(name)
|
|
59
|
+
return display(HTML(name))
|
|
136
60
|
|
|
137
|
-
|
|
61
|
+
else:
|
|
62
|
+
return net.show(name)
|
|
138
63
|
|
|
139
64
|
|
|
140
65
|
@intercept_session_exceptions
|
|
141
|
-
class ShowDataModelAPI:
|
|
66
|
+
class ShowDataModelAPI(ShowBaseAPI):
|
|
142
67
|
def __init__(self, state: SessionState) -> None:
|
|
68
|
+
super().__init__(state)
|
|
143
69
|
self._state = state
|
|
144
70
|
|
|
145
71
|
def __call__(self) -> Any:
|
|
146
|
-
if not self._state.
|
|
72
|
+
if not self._state.has_verified_rules:
|
|
147
73
|
raise NeatSessionError(
|
|
148
74
|
"No verified data model available. Try using [bold].verify()[/bold] to verify data model."
|
|
149
75
|
)
|
|
150
76
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
77
|
+
try:
|
|
78
|
+
di_graph = self._generate_dms_di_graph(self._state.last_verified_dms_rules)
|
|
79
|
+
name = "dms_data_model.html"
|
|
80
|
+
except NeatSessionError:
|
|
81
|
+
di_graph = self._generate_info_di_graph(self._state.last_verified_information_rules)
|
|
82
|
+
name = "information_data_model.html"
|
|
155
83
|
|
|
156
|
-
|
|
157
|
-
widget.graph.add_graph_from_networkx(NxGraph)
|
|
158
|
-
return display(widget)
|
|
84
|
+
return self._generate_visualization(di_graph, name)
|
|
159
85
|
|
|
160
|
-
def _generate_dms_di_graph(self) -> nx.DiGraph:
|
|
86
|
+
def _generate_dms_di_graph(self, rules: DMSRules) -> nx.DiGraph:
|
|
161
87
|
"""Generate a DiGraph from the last verified DMS rules."""
|
|
162
|
-
|
|
88
|
+
di_graph = nx.DiGraph()
|
|
163
89
|
|
|
164
90
|
# Add nodes and edges from Views sheet
|
|
165
|
-
for view in
|
|
91
|
+
for view in rules.views:
|
|
166
92
|
# if possible use human readable label coming from the view name
|
|
167
|
-
if not
|
|
168
|
-
|
|
93
|
+
if not di_graph.has_node(view.view.suffix):
|
|
94
|
+
di_graph.add_node(view.view.suffix, label=view.name or view.view.suffix)
|
|
169
95
|
|
|
170
96
|
# add implements as edges
|
|
171
97
|
if view.implements:
|
|
172
98
|
for implement in view.implements:
|
|
173
|
-
if not
|
|
174
|
-
|
|
99
|
+
if not di_graph.has_node(implement.suffix):
|
|
100
|
+
di_graph.add_node(implement.suffix, label=implement.suffix)
|
|
175
101
|
|
|
176
|
-
|
|
102
|
+
di_graph.add_edge(
|
|
103
|
+
view.view.suffix,
|
|
104
|
+
implement.suffix,
|
|
105
|
+
label="implements",
|
|
106
|
+
dashes=True,
|
|
107
|
+
)
|
|
177
108
|
|
|
178
109
|
# Add nodes and edges from Properties sheet
|
|
179
|
-
for prop_ in
|
|
110
|
+
for prop_ in rules.properties:
|
|
180
111
|
if prop_.connection and isinstance(prop_.value_type, ViewEntity):
|
|
181
|
-
if not
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
112
|
+
if not di_graph.has_node(prop_.view.suffix):
|
|
113
|
+
di_graph.add_node(prop_.view.suffix, label=prop_.view.suffix)
|
|
114
|
+
di_graph.add_edge(
|
|
115
|
+
prop_.view.suffix,
|
|
116
|
+
prop_.value_type.suffix,
|
|
117
|
+
label=prop_.name or prop_.property_,
|
|
118
|
+
)
|
|
186
119
|
|
|
187
|
-
return
|
|
120
|
+
return di_graph
|
|
188
121
|
|
|
189
|
-
def _generate_info_di_graph(self) -> nx.DiGraph:
|
|
190
|
-
"""Generate
|
|
122
|
+
def _generate_info_di_graph(self, rules: InformationRules) -> nx.DiGraph:
|
|
123
|
+
"""Generate DiGraph representing information data model."""
|
|
191
124
|
|
|
192
|
-
|
|
125
|
+
di_graph = nx.DiGraph()
|
|
193
126
|
|
|
194
127
|
# Add nodes and edges from Views sheet
|
|
195
|
-
for class_ in
|
|
128
|
+
for class_ in rules.classes:
|
|
196
129
|
# if possible use human readable label coming from the view name
|
|
197
|
-
if not
|
|
198
|
-
|
|
130
|
+
if not di_graph.has_node(class_.class_.suffix):
|
|
131
|
+
di_graph.add_node(
|
|
199
132
|
class_.class_.suffix,
|
|
200
133
|
label=class_.name or class_.class_.suffix,
|
|
201
134
|
)
|
|
202
135
|
|
|
203
|
-
# add
|
|
136
|
+
# add subClassOff as edges
|
|
204
137
|
if class_.parent:
|
|
205
138
|
for parent in class_.parent:
|
|
206
|
-
if not
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
139
|
+
if not di_graph.has_node(parent.suffix):
|
|
140
|
+
di_graph.add_node(parent.suffix, label=parent.suffix)
|
|
141
|
+
di_graph.add_edge(
|
|
142
|
+
class_.class_.suffix,
|
|
143
|
+
parent.suffix,
|
|
144
|
+
label="subClassOf",
|
|
145
|
+
dashes=True,
|
|
146
|
+
)
|
|
210
147
|
|
|
211
148
|
# Add nodes and edges from Properties sheet
|
|
212
|
-
for prop_ in
|
|
149
|
+
for prop_ in rules.properties:
|
|
213
150
|
if prop_.type_ == EntityTypes.object_property:
|
|
214
|
-
if not
|
|
215
|
-
|
|
151
|
+
if not di_graph.has_node(prop_.class_.suffix):
|
|
152
|
+
di_graph.add_node(prop_.class_.suffix, label=prop_.class_.suffix)
|
|
216
153
|
|
|
217
|
-
|
|
218
|
-
NxGraph.add_edge(
|
|
154
|
+
di_graph.add_edge(
|
|
219
155
|
prop_.class_.suffix,
|
|
220
156
|
cast(ClassEntity, prop_.value_type).suffix,
|
|
221
|
-
label=
|
|
157
|
+
label=prop_.name or prop_.property_,
|
|
222
158
|
)
|
|
223
159
|
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
"style": {
|
|
248
|
-
"width": 1,
|
|
249
|
-
"target-arrow-shape": "triangle",
|
|
250
|
-
"curve-style": "bezier",
|
|
251
|
-
"label": "data(label)",
|
|
252
|
-
"font-size": "8px",
|
|
253
|
-
"line-color": "black",
|
|
254
|
-
"target-arrow-color": "black",
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
"selector": 'edge[label = "subClassOf"]',
|
|
259
|
-
"style": {
|
|
260
|
-
"line-color": "grey",
|
|
261
|
-
"target-arrow-color": "grey",
|
|
262
|
-
"line-style": "dashed",
|
|
263
|
-
"font-size": "8px",
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
"selector": 'edge[label = "implements"]',
|
|
268
|
-
"style": {
|
|
269
|
-
"line-color": "grey",
|
|
270
|
-
"target-arrow-color": "grey",
|
|
271
|
-
"line-style": "dashed",
|
|
272
|
-
"font-size": "8px",
|
|
273
|
-
},
|
|
274
|
-
},
|
|
275
|
-
]
|
|
276
|
-
)
|
|
160
|
+
return di_graph
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@intercept_session_exceptions
|
|
164
|
+
class ShowInstanceAPI(ShowBaseAPI):
|
|
165
|
+
def __init__(self, state: SessionState) -> None:
|
|
166
|
+
super().__init__(state)
|
|
167
|
+
self._state = state
|
|
168
|
+
|
|
169
|
+
def __call__(self) -> Any:
|
|
170
|
+
if not self._state.store.graph:
|
|
171
|
+
raise NeatSessionError("No instances available. Try using [bold].read[/bold] to load instances.")
|
|
172
|
+
|
|
173
|
+
di_graph = self._generate_instance_di_graph_and_types()
|
|
174
|
+
return self._generate_visualization(di_graph, name="instances.html")
|
|
175
|
+
|
|
176
|
+
def _generate_instance_di_graph_and_types(self) -> nx.DiGraph:
|
|
177
|
+
query = """
|
|
178
|
+
SELECT ?s ?p ?o ?ts ?to WHERE {
|
|
179
|
+
?s ?p ?o .
|
|
180
|
+
FILTER(isIRI(?o)) # Example filter to check if ?o is an IRI (object type)
|
|
181
|
+
FILTER(BOUND(?o))
|
|
182
|
+
FILTER(?p != rdf:type)
|
|
277
183
|
|
|
278
|
-
|
|
184
|
+
?s a ?ts .
|
|
185
|
+
?o a ?to .
|
|
186
|
+
}
|
|
187
|
+
LIMIT 200
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
di_graph = nx.DiGraph()
|
|
191
|
+
|
|
192
|
+
types = [type_ for type_, _ in self._state.store.queries.summarize_instances()]
|
|
193
|
+
hex_colored_types = self._generate_hex_color_per_type(types)
|
|
194
|
+
|
|
195
|
+
for ( # type: ignore
|
|
196
|
+
subject,
|
|
197
|
+
property_,
|
|
198
|
+
object,
|
|
199
|
+
subject_type,
|
|
200
|
+
object_type,
|
|
201
|
+
) in self._state.store.graph.query(query):
|
|
202
|
+
subject = remove_namespace_from_uri(subject)
|
|
203
|
+
property_ = remove_namespace_from_uri(property_)
|
|
204
|
+
object = remove_namespace_from_uri(object)
|
|
205
|
+
subject_type = remove_namespace_from_uri(subject_type)
|
|
206
|
+
object_type = remove_namespace_from_uri(object_type)
|
|
207
|
+
|
|
208
|
+
di_graph.add_node(
|
|
209
|
+
subject,
|
|
210
|
+
label=subject,
|
|
211
|
+
type=subject_type,
|
|
212
|
+
title=subject_type,
|
|
213
|
+
color=hex_colored_types[subject_type],
|
|
214
|
+
)
|
|
215
|
+
di_graph.add_node(
|
|
216
|
+
object,
|
|
217
|
+
label=object,
|
|
218
|
+
type=object_type,
|
|
219
|
+
title=object_type,
|
|
220
|
+
color=hex_colored_types[object_type],
|
|
221
|
+
)
|
|
222
|
+
di_graph.add_edge(subject, object, label=property_, color="grey")
|
|
223
|
+
|
|
224
|
+
return di_graph
|
|
225
|
+
|
|
226
|
+
@staticmethod
|
|
227
|
+
def _generate_hex_color_per_type(types: list[str]) -> dict:
|
|
228
|
+
hex_colored_types = {}
|
|
229
|
+
random.seed(381)
|
|
230
|
+
for type_ in types:
|
|
231
|
+
hue = random.random()
|
|
232
|
+
saturation = random.uniform(0.5, 1.0)
|
|
233
|
+
lightness = random.uniform(0.4, 0.6)
|
|
234
|
+
rgb = colorsys.hls_to_rgb(hue, lightness, saturation)
|
|
235
|
+
hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}"
|
|
236
|
+
hex_colored_types[type_] = hex_color
|
|
237
|
+
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/_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.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.96.1
|
|
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.5.2)
|
|
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"
|