algomancy-gui 0.3.16__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.
- algomancy_gui/__init__.py +0 -0
- algomancy_gui/admin_page/__init__.py +1 -0
- algomancy_gui/admin_page/admin.py +362 -0
- algomancy_gui/admin_page/sessions.py +57 -0
- algomancy_gui/appconfiguration.py +291 -0
- algomancy_gui/compare_page/__init__.py +1 -0
- algomancy_gui/compare_page/compare.py +360 -0
- algomancy_gui/compare_page/kpicard.py +236 -0
- algomancy_gui/compare_page/scenarioselector.py +99 -0
- algomancy_gui/componentids.py +177 -0
- algomancy_gui/contentregistry.py +167 -0
- algomancy_gui/cqmloader.py +58 -0
- algomancy_gui/data_page/__init__.py +1 -0
- algomancy_gui/data_page/data.py +77 -0
- algomancy_gui/data_page/datamanagementdeletemodal.py +260 -0
- algomancy_gui/data_page/datamanagementderivemodal.py +201 -0
- algomancy_gui/data_page/datamanagementdownloadmodal.py +193 -0
- algomancy_gui/data_page/datamanagementimportmodal.py +438 -0
- algomancy_gui/data_page/datamanagementsavemodal.py +191 -0
- algomancy_gui/data_page/datamanagementtopbar.py +123 -0
- algomancy_gui/data_page/datamanagementuploadmodal.py +366 -0
- algomancy_gui/data_page/dialogcallbacks.py +51 -0
- algomancy_gui/data_page/filenamematcher.py +109 -0
- algomancy_gui/defaultloader.py +36 -0
- algomancy_gui/gui_launcher.py +183 -0
- algomancy_gui/home_page/__init__.py +1 -0
- algomancy_gui/home_page/home.py +16 -0
- algomancy_gui/layout.py +199 -0
- algomancy_gui/layouthelpers.py +30 -0
- algomancy_gui/managergetters.py +28 -0
- algomancy_gui/overview_page/__init__.py +1 -0
- algomancy_gui/overview_page/overview.py +20 -0
- algomancy_gui/py.typed +0 -0
- algomancy_gui/scenario_page/__init__.py +0 -0
- algomancy_gui/scenario_page/delete_confirmation.py +29 -0
- algomancy_gui/scenario_page/new_scenario_creator.py +104 -0
- algomancy_gui/scenario_page/new_scenario_parameters_window.py +154 -0
- algomancy_gui/scenario_page/scenario_badge.py +36 -0
- algomancy_gui/scenario_page/scenario_cards.py +119 -0
- algomancy_gui/scenario_page/scenarios.py +596 -0
- algomancy_gui/sessionmanager.py +168 -0
- algomancy_gui/settingsmanager.py +43 -0
- algomancy_gui/stylingconfigurator.py +740 -0
- algomancy_gui-0.3.16.dist-info/METADATA +71 -0
- algomancy_gui-0.3.16.dist-info/RECORD +46 -0
- algomancy_gui-0.3.16.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
kpicard.py - KPI Card Component
|
|
3
|
+
|
|
4
|
+
This module defines functions for creating and formatting KPI cards that display
|
|
5
|
+
performance metrics and comparisons between scenarios.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dash import html
|
|
9
|
+
import dash_bootstrap_components as dbc
|
|
10
|
+
|
|
11
|
+
from algomancy_scenario import ImprovementDirection, BASE_KPI
|
|
12
|
+
from algomancy_utils import Measurement
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_improvement_good(better_when, left, right):
|
|
16
|
+
"""
|
|
17
|
+
Determine if the change between left and right values is positive according to the measurement direction.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
better_when: Direction in which improvement is measured (higher or lower)
|
|
21
|
+
left: Left value to compare
|
|
22
|
+
right: Right value to compare
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
bool or None: True if the change is positive, False if negative, None if can't determine
|
|
26
|
+
"""
|
|
27
|
+
if left is None or right is None:
|
|
28
|
+
return None
|
|
29
|
+
if better_when == ImprovementDirection.HIGHER:
|
|
30
|
+
return right > left
|
|
31
|
+
if better_when == ImprovementDirection.LOWER:
|
|
32
|
+
return right < left
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_delta_binary(left_kpi: BASE_KPI, right_kpi: BASE_KPI):
|
|
37
|
+
left_value = 1 if left_kpi.success else 0
|
|
38
|
+
right_value = 1 if right_kpi.success else 0
|
|
39
|
+
|
|
40
|
+
delta = right_value - left_value
|
|
41
|
+
is_good = delta > 0
|
|
42
|
+
|
|
43
|
+
if abs(delta) < 1e-10: # Handle floating point precision
|
|
44
|
+
return "No change", "-", "text-muted"
|
|
45
|
+
|
|
46
|
+
arrow = "🡅" if is_good else "🡇"
|
|
47
|
+
|
|
48
|
+
# Create delta measurement with same unit as scaled measurements
|
|
49
|
+
delta_str = f"{left_kpi.pretty()} {arrow} {right_kpi.pretty()}"
|
|
50
|
+
|
|
51
|
+
if is_good:
|
|
52
|
+
details = "Right passes but left does not"
|
|
53
|
+
else:
|
|
54
|
+
details = "Left passes but right does not"
|
|
55
|
+
|
|
56
|
+
color_class = "text-success" if is_good else "text-danger"
|
|
57
|
+
return delta_str, details, color_class
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_delta_default(left_kpi: BASE_KPI, right_kpi: BASE_KPI):
|
|
61
|
+
"""
|
|
62
|
+
Determine difference, percentage, and color between two measurements.
|
|
63
|
+
Scales left measurement and matches right to the same unit.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
left_kpi: The left measurement to compare
|
|
67
|
+
right_kpi: The right measurement to compare
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
tuple: A tuple containing (delta string, percentage string, color class)
|
|
71
|
+
"""
|
|
72
|
+
left_measurement: Measurement = left_kpi.measurement
|
|
73
|
+
right_measurement: Measurement = right_kpi.measurement
|
|
74
|
+
better_when: ImprovementDirection = left_kpi.better_when
|
|
75
|
+
|
|
76
|
+
# Handle None or uninitialized measurements
|
|
77
|
+
if (
|
|
78
|
+
left_measurement is None
|
|
79
|
+
or right_measurement is None
|
|
80
|
+
or left_measurement.value == Measurement.INITIAL_VALUE
|
|
81
|
+
or right_measurement.value == Measurement.INITIAL_VALUE
|
|
82
|
+
):
|
|
83
|
+
return "No data", "-", "text-muted"
|
|
84
|
+
|
|
85
|
+
# Scale left measurement first
|
|
86
|
+
left_scaled = left_measurement.scale()
|
|
87
|
+
|
|
88
|
+
# Match right measurement to left's scaled unit
|
|
89
|
+
try:
|
|
90
|
+
right_scaled = right_measurement.scale_to_unit(left_scaled.unit)
|
|
91
|
+
except ValueError:
|
|
92
|
+
# Units are incompatible
|
|
93
|
+
return "Incompatible units", "-", "text-warning"
|
|
94
|
+
|
|
95
|
+
# Get the actual values for comparison
|
|
96
|
+
left_value = left_scaled.value
|
|
97
|
+
right_value = right_scaled.value
|
|
98
|
+
|
|
99
|
+
delta = right_value - left_value
|
|
100
|
+
is_good = is_improvement_good(better_when, left_value, right_value)
|
|
101
|
+
|
|
102
|
+
if abs(delta) < 1e-10: # Handle floating point precision
|
|
103
|
+
return "No change", "-", "text-muted"
|
|
104
|
+
|
|
105
|
+
arrow = "🡅" if delta > 0 else "🡇"
|
|
106
|
+
|
|
107
|
+
# Create delta measurement with same unit as scaled measurements
|
|
108
|
+
delta_measurement = Measurement(left_scaled.base_measurement, abs(delta))
|
|
109
|
+
delta_str = f"{arrow} {delta_measurement.pretty()}"
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
delta_perc = (delta / left_value * 100) if abs(left_value) > 1e-10 else 0
|
|
113
|
+
except ZeroDivisionError:
|
|
114
|
+
delta_perc = 0
|
|
115
|
+
|
|
116
|
+
verdict = "better" if is_good else "worse"
|
|
117
|
+
delta_perc_str = f"Right is relatively {abs(delta_perc):.1f}% {verdict} than left"
|
|
118
|
+
|
|
119
|
+
color_class = "text-success" if is_good else "text-danger"
|
|
120
|
+
return delta_str, delta_perc_str, color_class
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_delta_infos(left_kpi: BASE_KPI, right_kpi: BASE_KPI):
|
|
124
|
+
if left_kpi.is_binary_kpi:
|
|
125
|
+
return get_delta_binary(left_kpi, right_kpi)
|
|
126
|
+
else:
|
|
127
|
+
return get_delta_default(left_kpi, right_kpi)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def kpi_card(
|
|
131
|
+
left_kpi: BASE_KPI,
|
|
132
|
+
right_kpi: BASE_KPI,
|
|
133
|
+
):
|
|
134
|
+
"""
|
|
135
|
+
Create a compact KPI comparison card without excessive height.
|
|
136
|
+
Automatically scales the left measurement and matches the right to the same unit.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
left_kpi
|
|
140
|
+
right_kpi
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
dbc.Card: A Dash Bootstrap card component displaying the KPI comparison
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
# Extract unit symbol for display
|
|
147
|
+
unit = left_kpi.get_pretty_unit()
|
|
148
|
+
|
|
149
|
+
kpi_type = str(left_kpi.better_when).capitalize().replace("_", " ") + (
|
|
150
|
+
f" {left_kpi.get_threshold_str(unit)}"
|
|
151
|
+
if left_kpi.is_binary_kpi
|
|
152
|
+
else " is better"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
header = html.Div(
|
|
156
|
+
[
|
|
157
|
+
# Left group: name + optional unit
|
|
158
|
+
html.Div(
|
|
159
|
+
[
|
|
160
|
+
html.Span(str(left_kpi.name), className="fw-bold"),
|
|
161
|
+
# html.Span(f" ({unit_symbol})", className="text-secondary ms-1") if unit_symbol else None,
|
|
162
|
+
],
|
|
163
|
+
style={"display": "flex", "alignItems": "center", "gap": "0.25rem"},
|
|
164
|
+
),
|
|
165
|
+
# Right-aligned kpi_type (same muted style as unit)
|
|
166
|
+
html.Div(
|
|
167
|
+
html.Span(kpi_type, className="text-secondary"),
|
|
168
|
+
style={
|
|
169
|
+
"display": "flex",
|
|
170
|
+
"alignItems": "center",
|
|
171
|
+
"justifyContent": "flex-end",
|
|
172
|
+
"fontSize": "0.7rem",
|
|
173
|
+
},
|
|
174
|
+
),
|
|
175
|
+
],
|
|
176
|
+
# Make the header take full width and push the right group to the far right
|
|
177
|
+
style={
|
|
178
|
+
"fontSize": "1rem",
|
|
179
|
+
"display": "flex",
|
|
180
|
+
"alignItems": "center",
|
|
181
|
+
"justifyContent": "space-between",
|
|
182
|
+
"width": "100%",
|
|
183
|
+
"marginBottom": "10px",
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
values = html.Div(
|
|
188
|
+
[
|
|
189
|
+
html.Small(
|
|
190
|
+
f"Left: {left_kpi.details(unit)}",
|
|
191
|
+
style={"flex": "1", "textAlign": "left"},
|
|
192
|
+
),
|
|
193
|
+
html.Small(
|
|
194
|
+
f"Right: {right_kpi.details(unit)}",
|
|
195
|
+
style={"flex": "1", "textAlign": "right"},
|
|
196
|
+
),
|
|
197
|
+
],
|
|
198
|
+
className="text-muted",
|
|
199
|
+
style={
|
|
200
|
+
"fontSize": "0.85rem",
|
|
201
|
+
"lineHeight": "1",
|
|
202
|
+
"marginBottom": "2px",
|
|
203
|
+
"display": "flex",
|
|
204
|
+
"width": "100%",
|
|
205
|
+
},
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
delta_str, delta_perc_str, color_class = get_delta_infos(left_kpi, right_kpi)
|
|
209
|
+
delta = html.Div(
|
|
210
|
+
[
|
|
211
|
+
# Second row: Change, centered
|
|
212
|
+
html.Div(
|
|
213
|
+
html.H2(f"{delta_str}", className=color_class),
|
|
214
|
+
style={"width": "100%", "textAlign": "center", "marginTop": "0.3em"},
|
|
215
|
+
),
|
|
216
|
+
# Third row: Change percent, centered
|
|
217
|
+
html.Div(
|
|
218
|
+
html.H6(f"{delta_perc_str}", className=color_class),
|
|
219
|
+
style={"width": "100%", "textAlign": "center", "marginTop": "0.0em"},
|
|
220
|
+
),
|
|
221
|
+
]
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return dbc.Card(
|
|
225
|
+
dbc.CardBody(
|
|
226
|
+
[header, values, delta], className="p-2", style={"display": "block"}
|
|
227
|
+
),
|
|
228
|
+
className="shadow-sm bg-light",
|
|
229
|
+
style={
|
|
230
|
+
"margin": "2px",
|
|
231
|
+
"borderRadius": "0.5rem",
|
|
232
|
+
"boxShadow": "0 1px 2px rgba(0,0,0,0.07)",
|
|
233
|
+
"height": "auto",
|
|
234
|
+
"display": "block",
|
|
235
|
+
},
|
|
236
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
scenarioselector.py - Scenario Selection Component
|
|
3
|
+
|
|
4
|
+
This module defines the scenario selector component for the compare dashboard page.
|
|
5
|
+
It allows users to select and compare two different scenarios side by side.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import dash_bootstrap_components as dbc
|
|
9
|
+
from dash import html, dcc, get_app
|
|
10
|
+
|
|
11
|
+
from algomancy_scenario import ScenarioManager
|
|
12
|
+
|
|
13
|
+
from ..layouthelpers import create_wrapped_content_div
|
|
14
|
+
from ..componentids import (
|
|
15
|
+
LEFT_SCENARIO_DROPDOWN,
|
|
16
|
+
RIGHT_SCENARIO_DROPDOWN,
|
|
17
|
+
LEFT_SCENARIO_OVERVIEW,
|
|
18
|
+
RIGHT_SCENARIO_OVERVIEW,
|
|
19
|
+
PERF_SBS_LEFT_COLLAPSE,
|
|
20
|
+
PERF_SBS_RIGHT_COLLAPSE,
|
|
21
|
+
)
|
|
22
|
+
from ..settingsmanager import SettingsManager
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# === Helper ===
|
|
26
|
+
def get_completed_scenarios(scenario_manager: ScenarioManager):
|
|
27
|
+
return [
|
|
28
|
+
{"label": s.tag, "value": s.id}
|
|
29
|
+
for s in scenario_manager.list_scenarios()
|
|
30
|
+
if s.is_completed()
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_side_by_side_selector(scenario_manager: ScenarioManager):
|
|
35
|
+
selector = dbc.Row(
|
|
36
|
+
[
|
|
37
|
+
dbc.Col(
|
|
38
|
+
[
|
|
39
|
+
html.Label("Left Scenario"),
|
|
40
|
+
dcc.Dropdown(
|
|
41
|
+
id=LEFT_SCENARIO_DROPDOWN,
|
|
42
|
+
options=get_completed_scenarios(scenario_manager),
|
|
43
|
+
placeholder="Select completed scenario",
|
|
44
|
+
),
|
|
45
|
+
],
|
|
46
|
+
width=6,
|
|
47
|
+
),
|
|
48
|
+
dbc.Col(
|
|
49
|
+
[
|
|
50
|
+
html.Label("Right Scenario"),
|
|
51
|
+
dcc.Dropdown(
|
|
52
|
+
id=RIGHT_SCENARIO_DROPDOWN,
|
|
53
|
+
options=get_completed_scenarios(scenario_manager),
|
|
54
|
+
placeholder="Select completed scenario",
|
|
55
|
+
),
|
|
56
|
+
],
|
|
57
|
+
width=6,
|
|
58
|
+
),
|
|
59
|
+
],
|
|
60
|
+
className="mb-4",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return selector
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def create_side_by_side_viewer():
|
|
67
|
+
settings: SettingsManager = get_app().server.settings
|
|
68
|
+
left_overview = create_wrapped_content_div(
|
|
69
|
+
html.Div(id=LEFT_SCENARIO_OVERVIEW, className="mt-3 compare-sbs-content"),
|
|
70
|
+
show_loading=settings.show_loading_on_comparepage,
|
|
71
|
+
cqm=settings.use_cqm_loader,
|
|
72
|
+
spinner_scale=1.5,
|
|
73
|
+
)
|
|
74
|
+
right_overview = create_wrapped_content_div(
|
|
75
|
+
html.Div(id=RIGHT_SCENARIO_OVERVIEW, className="mt-3"),
|
|
76
|
+
show_loading=settings.show_loading_on_comparepage,
|
|
77
|
+
cqm=settings.use_cqm_loader,
|
|
78
|
+
spinner_scale=1.5,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
viewer = dbc.Row(
|
|
82
|
+
[
|
|
83
|
+
dbc.Col(
|
|
84
|
+
[
|
|
85
|
+
dbc.Collapse(left_overview, id=PERF_SBS_LEFT_COLLAPSE),
|
|
86
|
+
],
|
|
87
|
+
width=6,
|
|
88
|
+
),
|
|
89
|
+
dbc.Col(
|
|
90
|
+
[
|
|
91
|
+
dbc.Collapse(right_overview, id=PERF_SBS_RIGHT_COLLAPSE),
|
|
92
|
+
],
|
|
93
|
+
width=6,
|
|
94
|
+
),
|
|
95
|
+
],
|
|
96
|
+
className="side-by-side-viewer",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return viewer
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""
|
|
2
|
+
componentids.py - Component ID Constants
|
|
3
|
+
|
|
4
|
+
This module defines constants for all component IDs used in the dashboard application.
|
|
5
|
+
These IDs are used for callbacks and to reference components in the Dash application.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# === Session IDs ===
|
|
9
|
+
ACTIVE_SESSION = "active-session-id"
|
|
10
|
+
|
|
11
|
+
# === Styling ===
|
|
12
|
+
GLOBAL_STYLING_STORE = "global-styling"
|
|
13
|
+
PAGE_CONTENT = "page-content"
|
|
14
|
+
|
|
15
|
+
# === Home IDs ===
|
|
16
|
+
HOME_PAGE_CONTENT = "home-page-content"
|
|
17
|
+
|
|
18
|
+
# === Data IDs ===
|
|
19
|
+
DATA_PAGE = "data-page"
|
|
20
|
+
DM_LIST_UPDATER_STORE = "dm-list-updater"
|
|
21
|
+
|
|
22
|
+
DATA_PAGE_CONTENT = "data-page-content"
|
|
23
|
+
DATA_SELECTOR_DROPDOWN = "data-selector-dropdown"
|
|
24
|
+
|
|
25
|
+
DATA_MAN_SUCCESS_ALERT = "data-man-success-alert"
|
|
26
|
+
DATA_MAN_ERROR_ALERT = "data-man-error-alert"
|
|
27
|
+
|
|
28
|
+
DM_DERIVE_OPEN_BTN = "dm-derive-open-btn"
|
|
29
|
+
DM_DERIVE_MODAL = "dm-derive-modal"
|
|
30
|
+
DM_DERIVE_MODAL_CLOSE_BTN = "dm-derive-modal-close-btn"
|
|
31
|
+
DM_DERIVE_MODAL_SUBMIT_BTN = "dm-derive-modal-submit-btn"
|
|
32
|
+
DM_DERIVE_SET_SELECTOR = "dm-derive-set-selector"
|
|
33
|
+
DM_DERIVE_SET_NAME_INPUT = "dm-derive-set-name-input"
|
|
34
|
+
|
|
35
|
+
DM_DELETE_OPEN_BUTTON = "dm-delete-open-btn"
|
|
36
|
+
DM_DELETE_SUBMIT_BUTTON = "DATA_MAN_DELETE_BTN"
|
|
37
|
+
DM_DELETE_SET_SELECTOR = "data-man-delete-selector"
|
|
38
|
+
DM_DELETE_CLOSE_BUTTON = "data-man-delete-close-btn"
|
|
39
|
+
DM_DELETE_MODAL = "data-man-delete-modal"
|
|
40
|
+
DM_DELETE_COLLAPSE = "data-man-delete-collapse"
|
|
41
|
+
DM_DELETE_CONFIRM_INPUT = "data-man-delete-confirm-input"
|
|
42
|
+
|
|
43
|
+
DM_SAVE_MODAL = "dm-save-modal"
|
|
44
|
+
DM_SAVE_OPEN_BUTTON = "dm-save-open-btn"
|
|
45
|
+
DM_SAVE_SUBMIT_BUTTON = "dm-save-submit-btn"
|
|
46
|
+
DM_SAVE_MODAL_CLOSE_BTN = "dm-save-modal-close-btn"
|
|
47
|
+
DM_SAVE_SET_SELECTOR = "dm-save-set-selector"
|
|
48
|
+
|
|
49
|
+
DM_IMPORT_MODAL = "dm-import-modal"
|
|
50
|
+
DM_IMPORT_OPEN_BUTTON = "dm-load-open-btn"
|
|
51
|
+
DM_IMPORT_UPLOADER = "dm-load-uploader"
|
|
52
|
+
DM_IMPORT_SUBMIT_BUTTON = "dm-load-submit-btn"
|
|
53
|
+
DM_IMPORT_MODAL_CLOSE_BTN = "dm-load-modal-close-btn"
|
|
54
|
+
DM_IMPORT_MODAL_FILEVIEWER_COLLAPSE = "dm-load-modal-fileviewer-collapse"
|
|
55
|
+
DM_IMPORT_MODAL_FILEVIEWER_CARD = "dm-load-modal-fileviewer-card"
|
|
56
|
+
DM_IMPORT_MODAL_NAME_INPUT = "dm-load-modal-name-input"
|
|
57
|
+
DM_IMPORT_MODAL_FILEVIEWER_ALERT = "dm-load-modal-fileviewer-alert"
|
|
58
|
+
|
|
59
|
+
DM_UPLOAD_OPEN_BUTTON = "dm-upload-open-btn"
|
|
60
|
+
DM_UPLOAD_MODAL = "dm-upload-modal"
|
|
61
|
+
DM_UPLOAD_UPLOADER = "dm-upload-uploader"
|
|
62
|
+
DM_UPLOAD_SUBMIT_BUTTON = "dm-upload-submit-btn"
|
|
63
|
+
DM_UPLOAD_MODAL_CLOSE_BTN = "dm-upload-modal-close-btn"
|
|
64
|
+
DM_UPLOAD_MODAL_FILEVIEWER_CARD = "dm-upload-modal-fileviewer-card"
|
|
65
|
+
DM_UPLOAD_MODAL_FILEVIEWER_COLLAPSE = "dm-upload-modal-fileviewer-collapse"
|
|
66
|
+
DM_UPLOAD_MODAL_FILEVIEWER_ALERT = "dm-upload-modal-fileviewer-alert"
|
|
67
|
+
DM_UPLOAD_DATA_STORE = "dm-upload-data-store"
|
|
68
|
+
DM_UPLOAD_SUCCESS_ALERT = "dm-upload-success-alert"
|
|
69
|
+
|
|
70
|
+
DM_DOWNLOAD_OPEN_BUTTON = "dm-download-open-btn"
|
|
71
|
+
DM_DOWNLOAD_MODAL = "dm-download-modal"
|
|
72
|
+
DM_DOWNLOAD_CHECKLIST = "dm-download-checklist"
|
|
73
|
+
DM_DOWNLOAD_SUBMIT_BUTTON = "dm-download-submit-btn"
|
|
74
|
+
DM_DOWNLOAD_MODAL_CLOSE_BTN = "dm-download-modal-close-btn"
|
|
75
|
+
|
|
76
|
+
# === Scenario IDs ===
|
|
77
|
+
SCENARIO_PAGE = "scenario-page"
|
|
78
|
+
|
|
79
|
+
SCENARIO_LIST = "scenario-list"
|
|
80
|
+
SCENARIO_DELETE_MODAL = "delete-modal"
|
|
81
|
+
SCENARIO_CREATE_STATUS = "create-status"
|
|
82
|
+
SCENARIO_TO_DELETE = "scenario-to-delete"
|
|
83
|
+
SCENARIO_SELECTED = "selected-scenario"
|
|
84
|
+
SCENARIO_SELECTED_ID_STORE = "selected-scenario-id"
|
|
85
|
+
|
|
86
|
+
SCENARIO_CREATOR_MODAL = "scenario-creator-modal"
|
|
87
|
+
SCENARIO_CREATOR_OPEN_BUTTON = "scenario-creator-open-btn"
|
|
88
|
+
|
|
89
|
+
SCENARIO_NEW_BUTTON = "create-scenario-button"
|
|
90
|
+
SCENARIO_PROCESS_BUTTON = "scenario-process-button"
|
|
91
|
+
SCENARIO_DELETE_BUTTON = "scenario-delete-button"
|
|
92
|
+
SCENARIO_CONFIRM_DELETE_BUTTON = "scenario-confirm-delete-button"
|
|
93
|
+
SCENARIO_CANCEL_DELETE_BUTTON = "scenario-cancel-delete-button"
|
|
94
|
+
|
|
95
|
+
SCENARIO_STATUS_UPDATE_EVENT = "scenario-status-update-event"
|
|
96
|
+
SCENARIO_STATUS_BADGE = "scenario-status-badge"
|
|
97
|
+
|
|
98
|
+
SCENARIO_CARD = "scenario-card"
|
|
99
|
+
SCENARIO_TAG_INPUT = "scenario-tag-input"
|
|
100
|
+
SCENARIO_DATA_INPUT = "scenario-data-input"
|
|
101
|
+
SCENARIO_ALGO_INPUT = "scenario-algo-input"
|
|
102
|
+
SCENARIO_PARAMS_INPUT = "scenario-params-input"
|
|
103
|
+
|
|
104
|
+
SCENARIO_STATUS_SNAPSHOT = "scenario-status-snapshot"
|
|
105
|
+
SCENARIO_STATUS_UPDATE = "scenario-status-update"
|
|
106
|
+
|
|
107
|
+
SCENARIO_ALERT = "scenario-alert"
|
|
108
|
+
|
|
109
|
+
ALGO_PARAMS_WINDOW_ID = "algo-params-window"
|
|
110
|
+
ALGO_PARAMS_ENTRY_TAB = "algo-params-entry"
|
|
111
|
+
ALGO_PARAMS_UPLOAD_TAB = "algo-params-upload"
|
|
112
|
+
ALGO_PARAMS_ENTRY_CARD = "algo-params-entry-card"
|
|
113
|
+
ALGO_PARAM_INPUT = "algo-param-input"
|
|
114
|
+
ALGO_PARAM_DATE_INPUT = "algo-param-date-input"
|
|
115
|
+
ALGO_PARAM_INTERVAL_INPUT = "algo-param-interval-input"
|
|
116
|
+
|
|
117
|
+
SCENARIO_PROG_INTERVAL = "scenario-progress-polling-interval"
|
|
118
|
+
SCENARIO_PROG_BAR = "scenario-progress-bar"
|
|
119
|
+
SCENARIO_PROG_TEXT = "scenario-progress-text"
|
|
120
|
+
SCENARIO_PROG_COLLAPSE = "scenario-progress-collapse"
|
|
121
|
+
SCENARIO_CURRENTLY_RUNNING_STORE = "scenario-currently-running"
|
|
122
|
+
|
|
123
|
+
SCENARIO_LIST_UPDATE_STORE = "scenario-list-update-store"
|
|
124
|
+
|
|
125
|
+
# === Component IDs ===
|
|
126
|
+
LEFT_SCENARIO_DROPDOWN = "left-scenario-dropdown"
|
|
127
|
+
RIGHT_SCENARIO_DROPDOWN = "right-scenario-dropdown"
|
|
128
|
+
|
|
129
|
+
LEFT_SCENARIO_OVERVIEW = "left-scenario-overview"
|
|
130
|
+
RIGHT_SCENARIO_OVERVIEW = "right-scenario-overview"
|
|
131
|
+
|
|
132
|
+
KPI_IMPROVEMENT_SECTION = "kpi-improvement-section"
|
|
133
|
+
PERF_PRIMARY_RESULTS = "perf-primary-results"
|
|
134
|
+
PERF_SECONDARY_RESULTS = "perf-secondary-results"
|
|
135
|
+
|
|
136
|
+
PERF_DETAILS_COLLAPSE = "secondary-results-collapse"
|
|
137
|
+
LEFT_SECONDARY_RESULTS = "left-secondary-results"
|
|
138
|
+
RIGHT_SECONDARY_RESULTS = "right-secondary-results"
|
|
139
|
+
|
|
140
|
+
PERF_TOGGLE_CHECKLIST_LEFT = "toggle-checklist-left"
|
|
141
|
+
PERF_TOGGLE_CHECKLIST_RIGHT = "toggle-checklist-right"
|
|
142
|
+
PERF_SBS_LEFT_COLLAPSE = "perf-sbs-left-collapse"
|
|
143
|
+
PERF_SBS_RIGHT_COLLAPSE = "perf-sbs-right-collapse"
|
|
144
|
+
PERF_KPI_COLLAPSE = "perf-kpi-collapse"
|
|
145
|
+
PERF_COMPARE_COLLAPSE = "perf-compare-collapse"
|
|
146
|
+
HOW_TO_CREATE_NEW_SESSION = "how-to-create-new-session"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# === Compare Page IDs ===
|
|
150
|
+
COMPARE_PAGE = "compare-page"
|
|
151
|
+
COMPARE_DETAIL_VIEW = "compare-detail-view"
|
|
152
|
+
|
|
153
|
+
# === Overview Page IDs ===
|
|
154
|
+
# OVERVIEW_TABLE = "overview-table"
|
|
155
|
+
# OVERVIEW_UPDATE_INTERVAL = "overview-update-interval"
|
|
156
|
+
OVERVIEW_PAGE_CONTENT = "overview-page-content"
|
|
157
|
+
|
|
158
|
+
# === Admin Page IDs ===
|
|
159
|
+
ADMIN_PAGE = "admin-page"
|
|
160
|
+
ADMIN_NEW_SESSION = "admin-new-session"
|
|
161
|
+
ADMIN_COPY_SESSION = "admin-copy-session"
|
|
162
|
+
ADMIN_SELECT_SESSION = "admin-select-session"
|
|
163
|
+
ADMIN_LOG_WINDOW = "admin-log-window"
|
|
164
|
+
ADMIN_LOG_INTERVAL = "admin-log-interval"
|
|
165
|
+
ADMIN_LOG_FILTER = "admin-log-filter"
|
|
166
|
+
NEW_SESSION_BUTTON = "new-session-button"
|
|
167
|
+
SESSION_CREATOR_MODAL = "session-creator-modal"
|
|
168
|
+
NEW_SESSION_NAME = "new-session-name"
|
|
169
|
+
|
|
170
|
+
# === Layout IDs ===
|
|
171
|
+
SIDEBAR = "sidebar"
|
|
172
|
+
SIDEBAR_TOGGLE = "sidebar-toggle"
|
|
173
|
+
SIDEBAR_COLLAPSED = "sidebar-collapsed"
|
|
174
|
+
|
|
175
|
+
# === Notification related ===
|
|
176
|
+
SCENARIO_NOTIFICATION = "scenario-notification"
|
|
177
|
+
SCENARIO_NOTIFICATION_STORE = "scenario-notification-store"
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from typing import Callable, List
|
|
2
|
+
|
|
3
|
+
from dash import html
|
|
4
|
+
|
|
5
|
+
from algomancy_data import BASE_DATA_BOUND
|
|
6
|
+
from algomancy_content.pages.page import (
|
|
7
|
+
HomePage,
|
|
8
|
+
DataPage,
|
|
9
|
+
ScenarioPage,
|
|
10
|
+
ComparePage,
|
|
11
|
+
OverviewPage,
|
|
12
|
+
)
|
|
13
|
+
from algomancy_scenario import Scenario
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ContentRegistry:
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self._home_content: Callable[[], html.Div] | None = None
|
|
19
|
+
self._data_content: Callable[[BASE_DATA_BOUND], html.Div] | None = None
|
|
20
|
+
self._scenario_content: Callable[[Scenario], html.Div] | None = None
|
|
21
|
+
self._compare_side_by_side: Callable[[Scenario, str], html.Div] | None = None
|
|
22
|
+
self._compare_compare: Callable[[Scenario, Scenario], html.Div] | None = None
|
|
23
|
+
self._compare_details: Callable[[Scenario, Scenario], html.Div] | None = None
|
|
24
|
+
self._overview_content: Callable[[List[Scenario]], html.Div] | None = None
|
|
25
|
+
|
|
26
|
+
def register_pages(
|
|
27
|
+
self,
|
|
28
|
+
home_page: HomePage,
|
|
29
|
+
data_page: DataPage,
|
|
30
|
+
scenario_page: ScenarioPage,
|
|
31
|
+
compare_page: ComparePage,
|
|
32
|
+
overview_page: OverviewPage,
|
|
33
|
+
) -> None:
|
|
34
|
+
self._register_home_page(home_page)
|
|
35
|
+
self._register_data_page(data_page)
|
|
36
|
+
self._register_scenario_page(scenario_page)
|
|
37
|
+
self._register_compare_page(compare_page)
|
|
38
|
+
self._register_overview_page(overview_page)
|
|
39
|
+
|
|
40
|
+
def _register_home_page(self, page: HomePage) -> None:
|
|
41
|
+
self._home_content = page.create_content
|
|
42
|
+
page.register_callbacks()
|
|
43
|
+
|
|
44
|
+
def _register_data_page(self, page: DataPage) -> None:
|
|
45
|
+
self._data_content = page.create_content
|
|
46
|
+
page.register_callbacks()
|
|
47
|
+
|
|
48
|
+
def _register_scenario_page(self, page: ScenarioPage) -> None:
|
|
49
|
+
self._scenario_content = page.create_content
|
|
50
|
+
page.register_callbacks()
|
|
51
|
+
|
|
52
|
+
def _register_compare_page(self, page: ComparePage) -> None:
|
|
53
|
+
self._compare_side_by_side = page.create_side_by_side_content
|
|
54
|
+
self._compare_compare = page.create_compare_section
|
|
55
|
+
self._compare_details = page.create_details_section
|
|
56
|
+
page.register_callbacks()
|
|
57
|
+
|
|
58
|
+
def _register_overview_page(self, page: OverviewPage) -> None:
|
|
59
|
+
self._overview_content = page.create_content
|
|
60
|
+
page.register_callbacks()
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def home_content(self) -> Callable[[], html.Div]:
|
|
64
|
+
if self._home_content:
|
|
65
|
+
return self._home_content
|
|
66
|
+
else:
|
|
67
|
+
|
|
68
|
+
def default_content():
|
|
69
|
+
return html.Div(
|
|
70
|
+
[
|
|
71
|
+
html.H1("Home content was not filled."),
|
|
72
|
+
]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return default_content
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def data_content(self) -> Callable[[BASE_DATA_BOUND], html.Div]:
|
|
79
|
+
if self._data_content:
|
|
80
|
+
return self._data_content
|
|
81
|
+
else:
|
|
82
|
+
|
|
83
|
+
def default_content(data: BASE_DATA_BOUND):
|
|
84
|
+
return html.Div(
|
|
85
|
+
[
|
|
86
|
+
html.H1("Data content was not filled."),
|
|
87
|
+
html.H2(f"Data source: {data.name}"),
|
|
88
|
+
]
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return default_content
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def scenario_content(self) -> Callable[[Scenario], html.Div]:
|
|
95
|
+
if self._scenario_content:
|
|
96
|
+
return self._scenario_content
|
|
97
|
+
else:
|
|
98
|
+
|
|
99
|
+
def default_content(scenario: Scenario):
|
|
100
|
+
return html.Div(
|
|
101
|
+
[
|
|
102
|
+
html.H1("Scenario content was not filled."),
|
|
103
|
+
html.H2(f"Scenario: {scenario.tag}"),
|
|
104
|
+
]
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return default_content
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def compare_side_by_side(self) -> Callable[[Scenario, str], html.Div]:
|
|
111
|
+
if self._compare_side_by_side:
|
|
112
|
+
return self._compare_side_by_side
|
|
113
|
+
else:
|
|
114
|
+
|
|
115
|
+
def default_content(scenario: Scenario, side: str):
|
|
116
|
+
return html.Div(
|
|
117
|
+
[
|
|
118
|
+
html.H1("Compare side by side content was not filled."),
|
|
119
|
+
]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return default_content
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def compare_compare(self) -> Callable[[Scenario, Scenario], html.Div]:
|
|
126
|
+
if self._compare_compare:
|
|
127
|
+
return self._compare_compare
|
|
128
|
+
else:
|
|
129
|
+
|
|
130
|
+
def default_content(scenario1: Scenario, scenario2: Scenario):
|
|
131
|
+
return html.Div(
|
|
132
|
+
[
|
|
133
|
+
html.H1("Compare content was not filled."),
|
|
134
|
+
]
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return default_content
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def compare_details(self) -> Callable[[Scenario, Scenario], html.Div]:
|
|
141
|
+
if self._compare_details:
|
|
142
|
+
return self._compare_details
|
|
143
|
+
else:
|
|
144
|
+
|
|
145
|
+
def default_content(scenario1: Scenario, scenario2: Scenario):
|
|
146
|
+
return html.Div(
|
|
147
|
+
[
|
|
148
|
+
html.H1("Compare details content was not filled."),
|
|
149
|
+
]
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return default_content
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def overview_content(self) -> Callable[[List[Scenario]], html.Div]:
|
|
156
|
+
if self._overview_content:
|
|
157
|
+
return self._overview_content
|
|
158
|
+
else:
|
|
159
|
+
|
|
160
|
+
def default_content(scenarios: List[Scenario]):
|
|
161
|
+
return html.Div(
|
|
162
|
+
[
|
|
163
|
+
html.H1("Overview content was not filled."),
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return default_content
|