arbok-inspector 0.1.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 arbok-inspector might be problematic. Click here for more details.
- arbok_inspector/__init__.py +1 -0
- arbok_inspector/analysis/analysis_base.py +29 -0
- arbok_inspector/analysis/prepare_data.py +118 -0
- arbok_inspector/classes/dim.py +26 -0
- arbok_inspector/classes/run.py +238 -0
- arbok_inspector/cli.py +4 -0
- arbok_inspector/dev.py +19 -0
- arbok_inspector/helpers/string_formaters.py +33 -0
- arbok_inspector/helpers/unit_formater.py +29 -0
- arbok_inspector/main.py +15 -0
- arbok_inspector/pages/__init__.py +2 -0
- arbok_inspector/pages/database_browser.py +159 -0
- arbok_inspector/pages/greeter.py +35 -0
- arbok_inspector/pages/run_view.py +280 -0
- arbok_inspector/state.py +56 -0
- arbok_inspector/test_main.py +65 -0
- arbok_inspector/widgets/build_xarray_grid.py +141 -0
- arbok_inspector/widgets/build_xarray_html.py +57 -0
- arbok_inspector/widgets/json_plot_settings_dialog.py +77 -0
- arbok_inspector/widgets/update_day_selecter.py +36 -0
- arbok_inspector/widgets/update_run_selecter.py +51 -0
- arbok_inspector-0.1.0.dist-info/METADATA +92 -0
- arbok_inspector-0.1.0.dist-info/RECORD +27 -0
- arbok_inspector-0.1.0.dist-info/WHEEL +5 -0
- arbok_inspector-0.1.0.dist-info/entry_points.txt +2 -0
- arbok_inspector-0.1.0.dist-info/licenses/LICENSE +21 -0
- arbok_inspector-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Module to build a grid of xarray plots for a given run."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import copy
|
|
7
|
+
import plotly.graph_objects as go
|
|
8
|
+
from nicegui import ui, app
|
|
9
|
+
|
|
10
|
+
from arbok_inspector.helpers.unit_formater import unit_formatter
|
|
11
|
+
from arbok_inspector.helpers.string_formaters import (
|
|
12
|
+
title_formater, axis_label_formater
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from arbok_inspector.classes.dim import Dim
|
|
17
|
+
from arbok_inspector.classes.run import Run
|
|
18
|
+
from plotly.graph_objs import Figure
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_xarray_grid() -> None:
|
|
22
|
+
"""
|
|
23
|
+
Build a grid of xarray plots for the given run.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
run (Run): The Run object containing the data to plot.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Figure: The Plotly Figure object containing the grid of plots.
|
|
30
|
+
"""
|
|
31
|
+
#client = await ui.context.client.connected()
|
|
32
|
+
run = app.storage.tab["run"]
|
|
33
|
+
container = app.storage.tab["placeholders"]['plots']
|
|
34
|
+
container.clear()
|
|
35
|
+
if run.dim_axis_option['x-axis'] is None:
|
|
36
|
+
ui.notify(
|
|
37
|
+
'Please select at least one dimension for the x-axis to display plots.<br>',
|
|
38
|
+
color = 'red')
|
|
39
|
+
return
|
|
40
|
+
ds = run.generate_subset()
|
|
41
|
+
print(f"Found {len(ds.dims)} dimensions to plot in subset:")
|
|
42
|
+
fig_dict = {}
|
|
43
|
+
if len(ds.dims) == 1:
|
|
44
|
+
create_1d_plot(run, ds, container)
|
|
45
|
+
elif len(ds.dims) == 2:
|
|
46
|
+
create_2d_grid(run, ds, container)
|
|
47
|
+
else:
|
|
48
|
+
ui.notify(
|
|
49
|
+
'The selected dimensions result in more than 2D data.<br>'
|
|
50
|
+
'Please select only 1 or 2 dimensions to plot)',
|
|
51
|
+
color = 'red')
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
def create_1d_plot(run: Run, ds: xr.Dataset, container: ui.Row) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Create a 1D plot for the given run and dataset.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
run: The Run object containing the data to plot.
|
|
60
|
+
ds: The xarray Dataset containing the data.
|
|
61
|
+
container: The NiceGUI container to hold the plot.
|
|
62
|
+
"""
|
|
63
|
+
print("Creating 1D plot")
|
|
64
|
+
x_dim = run.dim_axis_option['x-axis'].name
|
|
65
|
+
traces = []
|
|
66
|
+
plot_dict = copy.deepcopy(app.storage.tab["plot_dict_1D"])
|
|
67
|
+
for key in run.plot_selection:
|
|
68
|
+
da = ds[key]
|
|
69
|
+
traces.append({
|
|
70
|
+
"type": "scatter",
|
|
71
|
+
"mode": "lines+markers",
|
|
72
|
+
"name": key.replace("__", "."),
|
|
73
|
+
"x": da.coords[x_dim].values.tolist(),
|
|
74
|
+
"y": da.values.tolist(),
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
plot_dict["data"] = traces
|
|
78
|
+
plot_dict["layout"]["xaxis"]["title"]["text"] = axis_label_formater(ds, x_dim)
|
|
79
|
+
plot_dict["layout"]["title"]["text"] = title_formater(run)
|
|
80
|
+
|
|
81
|
+
with container:
|
|
82
|
+
fig = go.Figure(plot_dict)
|
|
83
|
+
ui.plotly(fig).classes('w-full').style('min-height: 400px;')
|
|
84
|
+
app.storage.tab["plot_dict_1D"] = plot_dict
|
|
85
|
+
|
|
86
|
+
def create_2d_grid(run, ds, container) -> dict:
|
|
87
|
+
"""
|
|
88
|
+
Create a grid of 2D plots for the given run and dataset.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
run: The Run object containing the data to plot.
|
|
92
|
+
ds: The xarray Dataset containing the data.
|
|
93
|
+
container: The NiceGUI container to hold the plots.
|
|
94
|
+
"""
|
|
95
|
+
print("Creating 2D grid of plots")
|
|
96
|
+
if not all([run.dim_axis_option[axis]is not None for axis in ['x-axis', 'y-axis']]):
|
|
97
|
+
ui.notify(
|
|
98
|
+
'Please select both x-axis and y-axis dimensions to display 2D plots.<br>'
|
|
99
|
+
f'x: {run.dim_axis_option["x-axis"]}<br>'
|
|
100
|
+
f'y: {run.dim_axis_option["y-axis"]}',
|
|
101
|
+
color = 'red')
|
|
102
|
+
return
|
|
103
|
+
keys = run.plot_selection
|
|
104
|
+
num_plots = len(keys)
|
|
105
|
+
num_columns = int(min([run.plots_per_column, len(keys)]))
|
|
106
|
+
num_rows = math.ceil(num_plots / num_columns)
|
|
107
|
+
pretty_keys = [key.replace("__", ".") for key in keys]
|
|
108
|
+
|
|
109
|
+
x_dim = run.dim_axis_option['x-axis'].name
|
|
110
|
+
y_dim = run.dim_axis_option['y-axis'].name
|
|
111
|
+
plot_dict = copy.deepcopy(app.storage.tab["plot_dict_2D"])
|
|
112
|
+
plot_dict["layout"]["xaxis"]["title"]["text"] = axis_label_formater(ds, x_dim)
|
|
113
|
+
plot_dict["layout"]["yaxis"]["title"]["text"] = axis_label_formater(ds, y_dim)
|
|
114
|
+
plot_idx = 0
|
|
115
|
+
def create_2d_plot(plot_idx):
|
|
116
|
+
key = keys[plot_idx]
|
|
117
|
+
da = ds[key]
|
|
118
|
+
if x_dim != da.dims[1]:
|
|
119
|
+
da = da.transpose(y_dim, x_dim)
|
|
120
|
+
plot_dict["data"][0]["z"] = da.values.tolist()
|
|
121
|
+
plot_dict["data"][0]["x"] = da.coords[x_dim].values.tolist()
|
|
122
|
+
plot_dict["data"][0]["y"] = da.coords[y_dim].values.tolist()
|
|
123
|
+
plot_dict["layout"]["title"]["text"] = (
|
|
124
|
+
f"<b>{pretty_keys[plot_idx]}</b><br>{title_formater(run)}")
|
|
125
|
+
return go.Figure(plot_dict)
|
|
126
|
+
|
|
127
|
+
with container:
|
|
128
|
+
with ui.column().classes('w-full'):
|
|
129
|
+
for row in range(num_rows):
|
|
130
|
+
with ui.row().classes('w-full justify-start flex-wrap'):
|
|
131
|
+
for col in range(num_columns):
|
|
132
|
+
if plot_idx >= num_plots:
|
|
133
|
+
break
|
|
134
|
+
fig = create_2d_plot(plot_idx)
|
|
135
|
+
width_percent = 100 / num_columns - 2
|
|
136
|
+
with ui.column().style(
|
|
137
|
+
f"width: {width_percent}%; box-sizing: border-box;"
|
|
138
|
+
):
|
|
139
|
+
ui.plotly(fig).classes('w-full').style('min-height: 300px;')
|
|
140
|
+
plot_idx += 1
|
|
141
|
+
app.storage.tab["plot_dict_2D"] = plot_dict
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Build and display xarray dataset HTML with dark theme.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from nicegui import ui, app
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from arbok_inspector.classes.run import Run
|
|
11
|
+
from xarray import Dataset
|
|
12
|
+
|
|
13
|
+
def build_xarray_html():
|
|
14
|
+
"""Display the xarray dataset in a dark-themed style."""
|
|
15
|
+
run: Run = app.storage.tab["run"]
|
|
16
|
+
ds: Dataset = run.full_data_set
|
|
17
|
+
with ui.column().classes('w-full'):
|
|
18
|
+
ui.html('''
|
|
19
|
+
<style>
|
|
20
|
+
/* Wrap styles to only apply inside this container */
|
|
21
|
+
.xarray-dark-wrapper {
|
|
22
|
+
background-color: #343535; /* Tailwind gray-800 */
|
|
23
|
+
color: #ffffff;
|
|
24
|
+
padding: 1rem;
|
|
25
|
+
border-radius: 0.5rem;
|
|
26
|
+
overflow-x: auto;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.xarray-dark-wrapper th,
|
|
30
|
+
.xarray-dark-wrapper td {
|
|
31
|
+
color: #d1d5db; /* Tailwind gray-300 */
|
|
32
|
+
background-color: transparent;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.xarray-dark-wrapper .xr-var-name {
|
|
36
|
+
color: #93c5fd !important; /* Tailwind blue-300 */
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.xarray-dark-wrapper .xr-var-dims,
|
|
40
|
+
.xarray-dark-wrapper .xr-var-data {
|
|
41
|
+
color: #d1d5db !important; /* Light gray */
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Optional: override any inline black text */
|
|
45
|
+
.xarray-dark-wrapper * {
|
|
46
|
+
color: inherit !important;
|
|
47
|
+
background-color: transparent !important;
|
|
48
|
+
}
|
|
49
|
+
</style>
|
|
50
|
+
''')
|
|
51
|
+
|
|
52
|
+
# Wrap the dataset HTML in a div with that class
|
|
53
|
+
ui.html(f'''
|
|
54
|
+
<div class="xarray-dark-wrapper">
|
|
55
|
+
{ds._repr_html_()}
|
|
56
|
+
</div>
|
|
57
|
+
''')
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dialog for editing JSON plot settings.
|
|
3
|
+
"""
|
|
4
|
+
import copy
|
|
5
|
+
import json
|
|
6
|
+
import importlib.resources as resources
|
|
7
|
+
|
|
8
|
+
from nicegui import app, ui
|
|
9
|
+
|
|
10
|
+
from arbok_inspector.widgets.build_xarray_grid import build_xarray_grid
|
|
11
|
+
|
|
12
|
+
class JsonPlotSettingsDialog:
|
|
13
|
+
"""
|
|
14
|
+
Dialog for editing JSON plot settings.
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, dimension: str):
|
|
17
|
+
self.dimension = dimension
|
|
18
|
+
self.json_editor = None
|
|
19
|
+
self.dialog = self.build_plot_settings_dialog()
|
|
20
|
+
|
|
21
|
+
def build_plot_settings_dialog(self):
|
|
22
|
+
"""
|
|
23
|
+
Build the dialog for plot settings.
|
|
24
|
+
"""
|
|
25
|
+
plot_dict = app.storage.tab[self.dimension]
|
|
26
|
+
|
|
27
|
+
with ui.dialog() as dialog, ui.card():
|
|
28
|
+
with ui.column().classes('w_full h-screen'):
|
|
29
|
+
ui.label('Plot Settings')
|
|
30
|
+
self.json_editor = ui.json_editor(
|
|
31
|
+
properties = {"content": {"json": plot_dict}},
|
|
32
|
+
).classes("jse-theme-dark")
|
|
33
|
+
with ui.row().classes('w-full justify-end'):
|
|
34
|
+
ui.button(
|
|
35
|
+
text = 'Apply',
|
|
36
|
+
on_click=lambda: self.set_editor_data(),
|
|
37
|
+
color='green'
|
|
38
|
+
)
|
|
39
|
+
ui.button(
|
|
40
|
+
text = 'Reset',
|
|
41
|
+
on_click=lambda: self.reset_plot_settings(),
|
|
42
|
+
color='blue'
|
|
43
|
+
)
|
|
44
|
+
ui.button(
|
|
45
|
+
text = 'Close',
|
|
46
|
+
color = 'red',
|
|
47
|
+
on_click=dialog.close
|
|
48
|
+
)
|
|
49
|
+
return dialog
|
|
50
|
+
|
|
51
|
+
def open(self):
|
|
52
|
+
"""Open the dialog."""
|
|
53
|
+
print("Opening dialog and setting json data")
|
|
54
|
+
self.dialog.open()
|
|
55
|
+
self.json_editor.properties['content']['json'] = copy.deepcopy(
|
|
56
|
+
app.storage.tab[self.dimension])
|
|
57
|
+
self.json_editor.update()
|
|
58
|
+
|
|
59
|
+
async def set_editor_data(self):
|
|
60
|
+
"""Sets json data from the JSON editor to the app storage and rebuilds plots."""
|
|
61
|
+
json_data = await self.json_editor.run_editor_method('get')
|
|
62
|
+
json_data = json_data["json"]
|
|
63
|
+
app.storage.tab[self.dimension] = json_data
|
|
64
|
+
build_xarray_grid()
|
|
65
|
+
|
|
66
|
+
def reset_plot_settings(self):
|
|
67
|
+
"""Reset plot settings to defaults."""
|
|
68
|
+
if self.dimension == 'plot_dict_1D':
|
|
69
|
+
with resources.files("arbok_inspector.configurations").joinpath("1d_plot.json").open("r") as f:
|
|
70
|
+
app.storage.tab["plot_dict_1D"] = json.load(f)
|
|
71
|
+
|
|
72
|
+
elif self.dimension == 'plot_dict_2D':
|
|
73
|
+
with resources.files("arbok_inspector.configurations").joinpath("2d_plot.json").open("r") as f:
|
|
74
|
+
app.storage.tab["plot_dict_2D"] = json.load(f)
|
|
75
|
+
|
|
76
|
+
ui.notify('Reset to default settings', type='positive', position='top-right')
|
|
77
|
+
build_xarray_grid()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from nicegui import ui, app
|
|
3
|
+
|
|
4
|
+
from arbok_inspector.state import inspector
|
|
5
|
+
|
|
6
|
+
def update_day_selecter(day_grid):
|
|
7
|
+
offset_hours = app.storage.general["timezone"]
|
|
8
|
+
inspector.cursor.execute(f"""
|
|
9
|
+
SELECT
|
|
10
|
+
day,
|
|
11
|
+
MIN(run_timestamp) AS earliest_ts
|
|
12
|
+
FROM (
|
|
13
|
+
SELECT
|
|
14
|
+
run_timestamp,
|
|
15
|
+
DATE(datetime(run_timestamp, 'unixepoch', '{offset_hours} hours')) AS day
|
|
16
|
+
FROM runs
|
|
17
|
+
)
|
|
18
|
+
GROUP BY day
|
|
19
|
+
ORDER BY day;
|
|
20
|
+
""")
|
|
21
|
+
day_grid.clear()
|
|
22
|
+
rows = inspector.cursor.fetchall()
|
|
23
|
+
row_data = []
|
|
24
|
+
for day, ts in rows[::-1]:
|
|
25
|
+
row_data.append({'day': day})
|
|
26
|
+
|
|
27
|
+
day_grid.options['rowData'] = row_data
|
|
28
|
+
day_grid.update()
|
|
29
|
+
ui.notify(
|
|
30
|
+
'Day selector updated: \n'
|
|
31
|
+
f'found {len(row_data)} days',
|
|
32
|
+
type='positive',
|
|
33
|
+
multi_line=True,
|
|
34
|
+
classes='multi-line-notification',
|
|
35
|
+
position = 'top-right'
|
|
36
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
from nicegui import ui, app
|
|
3
|
+
|
|
4
|
+
from arbok_inspector.state import inspector
|
|
5
|
+
|
|
6
|
+
def update_run_selecter(run_grid, target_day, run_grid_column_defs):
|
|
7
|
+
offset_hours = app.storage.general["timezone"]
|
|
8
|
+
print(target_day)
|
|
9
|
+
print(offset_hours)
|
|
10
|
+
inspector.cursor.execute(
|
|
11
|
+
f"""
|
|
12
|
+
SELECT *
|
|
13
|
+
FROM runs
|
|
14
|
+
WHERE DATE(datetime(run_timestamp, 'unixepoch', '{offset_hours} hours')) = ?
|
|
15
|
+
ORDER BY run_timestamp;
|
|
16
|
+
""",
|
|
17
|
+
(target_day,),
|
|
18
|
+
)
|
|
19
|
+
runs = inspector.cursor.fetchall()
|
|
20
|
+
results = [dict(row) for row in runs]
|
|
21
|
+
|
|
22
|
+
run_grid_rows = []
|
|
23
|
+
columns = [x['field'] for x in run_grid_column_defs]
|
|
24
|
+
for run in results:
|
|
25
|
+
print(f'Run: {run.keys()}')
|
|
26
|
+
run_dict = {}
|
|
27
|
+
for key in columns:
|
|
28
|
+
if key in run:
|
|
29
|
+
value = run[key]
|
|
30
|
+
if 'time' in key:
|
|
31
|
+
if value is not None:
|
|
32
|
+
# Apply offset and format nicely
|
|
33
|
+
local_dt = datetime.utcfromtimestamp(value) + timedelta(hours=offset_hours)
|
|
34
|
+
value = local_dt.strftime('%H:%M:%S')
|
|
35
|
+
else:
|
|
36
|
+
value = 'N/A'
|
|
37
|
+
run_dict[key] = value
|
|
38
|
+
run_grid_rows.insert(0, run_dict)
|
|
39
|
+
|
|
40
|
+
run_grid.options['rowData'] = run_grid_rows
|
|
41
|
+
run_grid.update()
|
|
42
|
+
|
|
43
|
+
ui.notify(
|
|
44
|
+
'Run selector updated: \n'
|
|
45
|
+
f'found {len(run_grid_rows)} run(s)',
|
|
46
|
+
type='positive',
|
|
47
|
+
multi_line=True,
|
|
48
|
+
classes='multi-line-notification',
|
|
49
|
+
position = 'top-right'
|
|
50
|
+
)
|
|
51
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arbok-inspector
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: jupyterlab>=4.4.8
|
|
9
|
+
Requires-Dist: nicegui>=2.24.2
|
|
10
|
+
Requires-Dist: nodejs>=0.1.1
|
|
11
|
+
Requires-Dist: plotly>=6.3.0
|
|
12
|
+
Requires-Dist: qcodes>=0.53.0
|
|
13
|
+
Requires-Dist: xarray>=2025.9.0
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# arbok_inspector 🐍
|
|
17
|
+
arbok_inspector is an browser based inspection and visualization utility for QCoDeS measurement
|
|
18
|
+
databases.
|
|
19
|
+
It provides a lightweight GUI and CLI to browse runs and visualize data.
|
|
20
|
+
|
|
21
|
+
## Features 🔎
|
|
22
|
+
The most commonly used used tool to visualize QCoDeS databases is
|
|
23
|
+
[plottr](https://github.com/toolsforexperiments/plottr).
|
|
24
|
+
Plottr is a great tool to get started, but struggles with increasing abounts of data.
|
|
25
|
+
|
|
26
|
+
This is how arbok_inspector streamlines your data inspection:
|
|
27
|
+
- Fast browsing of measurement runs and their metadata
|
|
28
|
+
- Written in tailwind using nicegui wrapper
|
|
29
|
+
- Browser based approach ensures cross system compatibily
|
|
30
|
+
- Selected runs are opened in a new tab and run on a separate thread
|
|
31
|
+
- this avoids blocking the entire application when loading big datasets
|
|
32
|
+
- plotting backend is plotly which natively returns html
|
|
33
|
+
- plotly plot customization is declarative and can therefore be tweaked in a simple json editor without implementing each customization by hand
|
|
34
|
+
- runs are only loaded on demand
|
|
35
|
+
- startup time in plottr can be several minutes for large databases
|
|
36
|
+
- SQL queries load only the given days upon database selection, only loads respective runs once day is selected
|
|
37
|
+
|
|
38
|
+
## Installation 📲
|
|
39
|
+
|
|
40
|
+
From the project root (the folder containing `pyproject.toml`), install:
|
|
41
|
+
```bash
|
|
42
|
+
python -m pip install -e .
|
|
43
|
+
```
|
|
44
|
+
To be added to pypi (pip) soon!
|
|
45
|
+
|
|
46
|
+
Launch either from CLI:
|
|
47
|
+
```bash
|
|
48
|
+
arbok-inspector
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
... or alternatively run in editable/dev mode:
|
|
52
|
+
```bash
|
|
53
|
+
python -m arbok_inspector/dev.py
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Project layout
|
|
57
|
+
|
|
58
|
+
- `main.py` — app entrypoint and startup logic
|
|
59
|
+
- `state.py` — application state & database handling
|
|
60
|
+
- `pages/` — NiceGUI pages (database browser, run view, greeter, ...)
|
|
61
|
+
- `widgets/` — reusable UI widgets (grid builders, selectors, dialogs)
|
|
62
|
+
- `analysis/` — analysis and data-prep utilities
|
|
63
|
+
- `classes/` — small domain objects used across the app
|
|
64
|
+
- `helpers/` — formatting and utility helpers
|
|
65
|
+
|
|
66
|
+
Development & testing 🛠️
|
|
67
|
+
|
|
68
|
+
- Run tests:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pytest -q
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- Use an editable install for local development to pick up changes immediately:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
python -m pip install -e .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Contributing & help 🙌
|
|
81
|
+
|
|
82
|
+
Contributions, bug reports, and small feature requests are welcome. If you want to add a visualization or a new page, use `pages/` and `widgets/` for examples of how UI components are composed. When opening a PR, please keep changes focused and include a short description of how to exercise the change locally.
|
|
83
|
+
|
|
84
|
+
License
|
|
85
|
+
|
|
86
|
+
See the `LICENSE` file in the project root for license details.
|
|
87
|
+
|
|
88
|
+
Notes & tips
|
|
89
|
+
|
|
90
|
+
- For exact runtime dependencies check `pyproject.toml` — prefer using that manifest (and a virtual environment) for reproducible installs.
|
|
91
|
+
- If you want me to add a short walkthrough for common tasks (open a run, plot data, export CSV), tell me which task you'd like first and I can add a step-by-step example here. 📘
|
|
92
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
arbok_inspector/__init__.py,sha256=KEDfR87-199tMUCeufnGkPswwjGivDOIwF5w1_cZ6ZM,53
|
|
2
|
+
arbok_inspector/cli.py,sha256=G0gIa4ByfQTMmeXCyLjnsmke5EEXs6DYuR5eVbhszXo,96
|
|
3
|
+
arbok_inspector/dev.py,sha256=F19xar7hlR-fG1QTxGyXSwT5r80x52vZIlZDaimPc4E,413
|
|
4
|
+
arbok_inspector/main.py,sha256=oQEypBKdD5jLARiXmz2u1_Op5OIos0BTqkM--oLU3lo,300
|
|
5
|
+
arbok_inspector/state.py,sha256=FDsnqUq8_oGecTvFgQoVB0-JMgFtmgccsxPX7jYX74A,2295
|
|
6
|
+
arbok_inspector/test_main.py,sha256=brV3O2ZVyuUrqnUmZr3U-tH6VY7PQxthNiElgIax_x0,1955
|
|
7
|
+
arbok_inspector/analysis/analysis_base.py,sha256=7uChxCyoiHazQBId9DHgXZtk166fZshr-E-eF-SNw5Q,933
|
|
8
|
+
arbok_inspector/analysis/prepare_data.py,sha256=DedVxlbTNDppMlNBwt8kN4cWk0_Nnyx9J442GqG1Asw,4393
|
|
9
|
+
arbok_inspector/classes/dim.py,sha256=bPxVNn23ohZ9CVePmiqLUFABbh6iKiuN_dMAxIcqIG0,750
|
|
10
|
+
arbok_inspector/classes/run.py,sha256=i50RhllM-fXanTNRLxBdcpBCkfiqjrNvupDSv4vwg2s,9895
|
|
11
|
+
arbok_inspector/helpers/string_formaters.py,sha256=q8ldfHRlACWKBhjUpuwbDthbUBcP6q8Z9qaRLdiwxJ4,1000
|
|
12
|
+
arbok_inspector/helpers/unit_formater.py,sha256=7h77hqPoLLBil3Z1n72T1iLv3Du84Qt0iZ3B0tzZtaY,1163
|
|
13
|
+
arbok_inspector/pages/__init__.py,sha256=3ZbSQftX4VYoWU6DsAEZvXl89PLjwn89Xtxqwwxy8vc,88
|
|
14
|
+
arbok_inspector/pages/database_browser.py,sha256=22KbzJwKSIPoLnk6Li6i0WYqjL713nH4Q222XDWjCsg,6793
|
|
15
|
+
arbok_inspector/pages/greeter.py,sha256=O1QpLU9nMAOjG0kKNCVMPX00fnUlmoZzOP6Pdt_2H0g,1380
|
|
16
|
+
arbok_inspector/pages/run_view.py,sha256=yEV7k_OdgT61PemMK8l1lDlqtzYNu8-Qguer0Iw5uMk,10980
|
|
17
|
+
arbok_inspector/widgets/build_xarray_grid.py,sha256=TgFYWtFp2DqBI5neP_WqXkhmwTj6Hl6UqzIqcW_Xtsk,5409
|
|
18
|
+
arbok_inspector/widgets/build_xarray_html.py,sha256=NaS8YKidcXOPhSY-QHraHS_Hag8cCqizrtaDJ__WwH0,1679
|
|
19
|
+
arbok_inspector/widgets/json_plot_settings_dialog.py,sha256=tUjx9TqDHhKsT9IGZfw61zuzjY1BqJH00s_gtR5kzio,2913
|
|
20
|
+
arbok_inspector/widgets/update_day_selecter.py,sha256=z5HVUVnxSqtZfoaK4lzMh5J16o9RoEZJwDvnwS-ewZ8,1011
|
|
21
|
+
arbok_inspector/widgets/update_run_selecter.py,sha256=M7hvy80kXO2NA83ly3QeeMQsTfdslit--2K3CbkHrQc,1639
|
|
22
|
+
arbok_inspector-0.1.0.dist-info/licenses/LICENSE,sha256=ZEqWKVZ2qROesXQcWpU2JTdY2yk6OkWR70H5771iIGc,1070
|
|
23
|
+
arbok_inspector-0.1.0.dist-info/METADATA,sha256=ykCseeNLgTxncqeBwMUT_DPB5e1LlXle_NcaWHt4Eyc,3282
|
|
24
|
+
arbok_inspector-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
25
|
+
arbok_inspector-0.1.0.dist-info/entry_points.txt,sha256=i1gVPzcOgNNzSly4zUqJ_z38eiUrMEOCNgtH1dr55zE,64
|
|
26
|
+
arbok_inspector-0.1.0.dist-info/top_level.txt,sha256=-jLIWMq05sffOZ-lCiP8dZgVga7Upp95QsBgoE718oE,16
|
|
27
|
+
arbok_inspector-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Andreas Nickl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
arbok_inspector
|