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
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# This file is intentionally left empty to make the directory a Python package.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
from dash import (
|
|
2
|
+
Output,
|
|
3
|
+
Input,
|
|
4
|
+
State,
|
|
5
|
+
callback,
|
|
6
|
+
get_app,
|
|
7
|
+
html,
|
|
8
|
+
dcc,
|
|
9
|
+
callback_context,
|
|
10
|
+
no_update,
|
|
11
|
+
)
|
|
12
|
+
import dash_bootstrap_components as dbc
|
|
13
|
+
|
|
14
|
+
from .sessions import create_new_session_window
|
|
15
|
+
from ..componentids import (
|
|
16
|
+
ADMIN_NEW_SESSION,
|
|
17
|
+
ACTIVE_SESSION,
|
|
18
|
+
ADMIN_SELECT_SESSION,
|
|
19
|
+
ADMIN_LOG_WINDOW,
|
|
20
|
+
ADMIN_LOG_INTERVAL,
|
|
21
|
+
ADMIN_LOG_FILTER,
|
|
22
|
+
ADMIN_PAGE,
|
|
23
|
+
ADMIN_COPY_SESSION,
|
|
24
|
+
SESSION_CREATOR_MODAL,
|
|
25
|
+
NEW_SESSION_BUTTON,
|
|
26
|
+
NEW_SESSION_NAME,
|
|
27
|
+
HOW_TO_CREATE_NEW_SESSION,
|
|
28
|
+
)
|
|
29
|
+
from algomancy_utils.logger import Logger, MessageStatus
|
|
30
|
+
from algomancy_gui.managergetters import get_manager
|
|
31
|
+
from ..sessionmanager import SessionManager
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def admin_page():
|
|
35
|
+
"""Returns the HTML page layout which the callbacks use to create the page."""
|
|
36
|
+
return html.Div(id=ADMIN_PAGE)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def admin_header():
|
|
40
|
+
"""Creates the header for the admin page."""
|
|
41
|
+
return [
|
|
42
|
+
html.H1("Admin"),
|
|
43
|
+
html.P(
|
|
44
|
+
"This is where settings are managed and an overview of the jobs is provided."
|
|
45
|
+
),
|
|
46
|
+
html.Hr(),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def admin_sessions(session_id):
|
|
51
|
+
"""Creates a page-section where sessions can be selected and created."""
|
|
52
|
+
|
|
53
|
+
if not get_app().server.use_sessions:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
session_manager: SessionManager = get_app().server.session_manager
|
|
57
|
+
sessions = session_manager.sessions_names
|
|
58
|
+
|
|
59
|
+
return [
|
|
60
|
+
html.H3("Sessions"),
|
|
61
|
+
dcc.Store(
|
|
62
|
+
id=HOW_TO_CREATE_NEW_SESSION,
|
|
63
|
+
data=False,
|
|
64
|
+
),
|
|
65
|
+
dbc.Row(
|
|
66
|
+
[
|
|
67
|
+
dbc.Col(
|
|
68
|
+
[
|
|
69
|
+
html.Label("Select session:"),
|
|
70
|
+
dcc.Dropdown(
|
|
71
|
+
id=ADMIN_SELECT_SESSION,
|
|
72
|
+
options=[{"label": s, "value": s} for s in sessions],
|
|
73
|
+
value=session_id,
|
|
74
|
+
clearable=False,
|
|
75
|
+
),
|
|
76
|
+
],
|
|
77
|
+
width="auto",
|
|
78
|
+
className="d-flex flex-column justify-content-end",
|
|
79
|
+
style={"minWidth": "250px"},
|
|
80
|
+
),
|
|
81
|
+
# New Session button
|
|
82
|
+
dbc.Col(
|
|
83
|
+
dbc.Button(
|
|
84
|
+
"New Session",
|
|
85
|
+
id=ADMIN_NEW_SESSION,
|
|
86
|
+
className="ms-2 w-100",
|
|
87
|
+
style={
|
|
88
|
+
"backgroundColor": "var(--theme-secondary)",
|
|
89
|
+
"color": "var(--text-selected)",
|
|
90
|
+
"border": "none",
|
|
91
|
+
"height": "38px",
|
|
92
|
+
},
|
|
93
|
+
),
|
|
94
|
+
width="auto",
|
|
95
|
+
className="d-flex align-items-end",
|
|
96
|
+
),
|
|
97
|
+
# Copy Session button
|
|
98
|
+
dbc.Col(
|
|
99
|
+
dbc.Button(
|
|
100
|
+
"Copy Session",
|
|
101
|
+
id=ADMIN_COPY_SESSION,
|
|
102
|
+
className="ms-2 w-100",
|
|
103
|
+
style={
|
|
104
|
+
"backgroundColor": "var(--theme-secondary)",
|
|
105
|
+
"color": "var(--text-selected)",
|
|
106
|
+
"border": "none",
|
|
107
|
+
"height": "38px%",
|
|
108
|
+
},
|
|
109
|
+
),
|
|
110
|
+
width="auto",
|
|
111
|
+
className="d-flex align-items-end",
|
|
112
|
+
),
|
|
113
|
+
],
|
|
114
|
+
className="g-1",
|
|
115
|
+
),
|
|
116
|
+
html.Hr(),
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def admin_system_logs():
|
|
121
|
+
"""Creates a page-section where system logs are displayed."""
|
|
122
|
+
return [
|
|
123
|
+
html.H3("System Logs"),
|
|
124
|
+
html.P("This window displays logging messages from the scenario manager."),
|
|
125
|
+
# Log filter dropdown
|
|
126
|
+
dbc.Row(
|
|
127
|
+
[
|
|
128
|
+
dbc.Col(
|
|
129
|
+
[
|
|
130
|
+
html.Label("Filter by status:"),
|
|
131
|
+
dcc.Dropdown(
|
|
132
|
+
id=ADMIN_LOG_FILTER,
|
|
133
|
+
options=[
|
|
134
|
+
{"label": "All", "value": "ALL"},
|
|
135
|
+
{"label": "Info", "value": "INFO"},
|
|
136
|
+
{"label": "Success", "value": "SUCCESS"},
|
|
137
|
+
{"label": "Warning", "value": "WARNING"},
|
|
138
|
+
{"label": "Error", "value": "ERROR"},
|
|
139
|
+
],
|
|
140
|
+
value="ALL",
|
|
141
|
+
clearable=False,
|
|
142
|
+
),
|
|
143
|
+
],
|
|
144
|
+
width=3,
|
|
145
|
+
)
|
|
146
|
+
],
|
|
147
|
+
className="mb-3",
|
|
148
|
+
),
|
|
149
|
+
# Scrollable log window
|
|
150
|
+
dbc.Card(
|
|
151
|
+
[
|
|
152
|
+
dbc.CardBody(
|
|
153
|
+
[html.Div(id=ADMIN_LOG_WINDOW, className="admin-log-window")]
|
|
154
|
+
)
|
|
155
|
+
],
|
|
156
|
+
className="admin-log-card mb-4",
|
|
157
|
+
),
|
|
158
|
+
# Interval for updating logs
|
|
159
|
+
dcc.Interval(
|
|
160
|
+
id=ADMIN_LOG_INTERVAL,
|
|
161
|
+
interval=2000, # 2 seconds
|
|
162
|
+
n_intervals=0,
|
|
163
|
+
),
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@callback(
|
|
168
|
+
Output(ADMIN_PAGE, "children"),
|
|
169
|
+
Input(ACTIVE_SESSION, "data"),
|
|
170
|
+
)
|
|
171
|
+
def create_admin_page(session_id):
|
|
172
|
+
"""
|
|
173
|
+
Creates the admin page layout.
|
|
174
|
+
|
|
175
|
+
from ..componentids import ADMIN_LOG_WINDOW, ADMIN_LOG_FILTER, ADMIN_LOG_INTERVAL
|
|
176
|
+
from algomancy_utils import MessageStatus
|
|
177
|
+
This page provides settings management, an overview of system jobs,
|
|
178
|
+
and a scrollable window displaying logging messages from the scenario_manager.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
List: to fill the Dash HTML component representing the admin page
|
|
182
|
+
"""
|
|
183
|
+
admin_content = (
|
|
184
|
+
admin_header()
|
|
185
|
+
+ admin_sessions(session_id)
|
|
186
|
+
+ admin_system_logs()
|
|
187
|
+
+ [create_new_session_window()]
|
|
188
|
+
)
|
|
189
|
+
return admin_content
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@callback(
|
|
193
|
+
Output(ACTIVE_SESSION, "data"),
|
|
194
|
+
Input(ADMIN_SELECT_SESSION, "value"),
|
|
195
|
+
)
|
|
196
|
+
def load_session(session_id):
|
|
197
|
+
"""Updates the active session when a new session is selected using the dropdown."""
|
|
198
|
+
return session_id
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@callback(
|
|
202
|
+
Output(ADMIN_LOG_WINDOW, "children"),
|
|
203
|
+
[Input(ADMIN_LOG_INTERVAL, "n_intervals"), Input(ADMIN_LOG_FILTER, "value")],
|
|
204
|
+
)
|
|
205
|
+
def update_log_window(n_intervals, filter_value):
|
|
206
|
+
"""
|
|
207
|
+
Updates the log window with messages from the session_manager's logger.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
n_intervals (int): Number of intervals elapsed (from dcc.Interval)
|
|
211
|
+
filter_value (str): Selected filter value for log messages
|
|
212
|
+
session_id (str): ID of active session
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
list: List of HTML components representing log messages
|
|
216
|
+
"""
|
|
217
|
+
# Get the scenario manager
|
|
218
|
+
|
|
219
|
+
manager = get_manager(get_app().server)
|
|
220
|
+
|
|
221
|
+
# Get the logger from the session manager
|
|
222
|
+
logger: Logger = manager.logger
|
|
223
|
+
|
|
224
|
+
# Get logs based on filter
|
|
225
|
+
if filter_value == "ALL":
|
|
226
|
+
logs = logger.get_logs()
|
|
227
|
+
else:
|
|
228
|
+
# Convert string filter value to MessageStatus enum
|
|
229
|
+
status_filter = MessageStatus[filter_value]
|
|
230
|
+
logs = logger.get_logs(status_filter=status_filter)
|
|
231
|
+
|
|
232
|
+
# Format logs for display
|
|
233
|
+
log_components = []
|
|
234
|
+
|
|
235
|
+
for log in logs:
|
|
236
|
+
# Determine style based on log status
|
|
237
|
+
style = {
|
|
238
|
+
"padding": "5px",
|
|
239
|
+
"borderBottom": "1px solid #ddd",
|
|
240
|
+
"fontSize": "0.9em",
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
# Add color based on status
|
|
244
|
+
if log.status == MessageStatus.INFO:
|
|
245
|
+
style["color"] = "#0d6efd" # blue
|
|
246
|
+
elif log.status == MessageStatus.SUCCESS:
|
|
247
|
+
style["color"] = "#198754" # green
|
|
248
|
+
elif log.status == MessageStatus.WARNING:
|
|
249
|
+
style["color"] = "#fd7e14" # orange
|
|
250
|
+
elif log.status == MessageStatus.ERROR:
|
|
251
|
+
style["color"] = "#dc3545" # red
|
|
252
|
+
|
|
253
|
+
# Create log entry component
|
|
254
|
+
log_entry = html.Div(
|
|
255
|
+
f"[{log.timestamp.strftime('%Y-%m-%d %H:%M:%S')}] {log.status.name}: {log.message}",
|
|
256
|
+
style=style,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
log_components.append(log_entry)
|
|
260
|
+
|
|
261
|
+
# Reverse to show newest logs at the top
|
|
262
|
+
log_components.reverse()
|
|
263
|
+
|
|
264
|
+
return log_components
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@callback(
|
|
268
|
+
Output(NEW_SESSION_BUTTON, "disabled"),
|
|
269
|
+
Output(f"{NEW_SESSION_BUTTON}-tooltip-container", "children"),
|
|
270
|
+
Input(NEW_SESSION_NAME, "value"),
|
|
271
|
+
)
|
|
272
|
+
def validate_session_name(session_name: str):
|
|
273
|
+
"""
|
|
274
|
+
Validates the session name before creating a new session.
|
|
275
|
+
The new session button is disabled if the session name is invalid.
|
|
276
|
+
A name is considered invalid if it is empty or already exists.
|
|
277
|
+
A tooltip is displayed if the session name is invalid with a short explanation.
|
|
278
|
+
"""
|
|
279
|
+
if not get_app().server.use_sessions:
|
|
280
|
+
return no_update, no_update
|
|
281
|
+
existing_names = get_app().server.session_manager.sessions_names
|
|
282
|
+
|
|
283
|
+
if not session_name:
|
|
284
|
+
tooltip = dbc.Tooltip(
|
|
285
|
+
"Session name cannot be empty.",
|
|
286
|
+
target=f"{NEW_SESSION_BUTTON}-wrapper",
|
|
287
|
+
placement="top",
|
|
288
|
+
id=f"{NEW_SESSION_BUTTON}-tooltip",
|
|
289
|
+
)
|
|
290
|
+
return True, tooltip
|
|
291
|
+
|
|
292
|
+
if session_name in existing_names:
|
|
293
|
+
tooltip = dbc.Tooltip(
|
|
294
|
+
"Session name already exists.",
|
|
295
|
+
target=f"{NEW_SESSION_BUTTON}-wrapper",
|
|
296
|
+
placement="top",
|
|
297
|
+
id=f"{NEW_SESSION_BUTTON}-tooltip",
|
|
298
|
+
)
|
|
299
|
+
return True, tooltip
|
|
300
|
+
|
|
301
|
+
# valid -> enable button and remove tooltip from DOM
|
|
302
|
+
return False, None
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@callback(
|
|
306
|
+
[
|
|
307
|
+
Output(SESSION_CREATOR_MODAL, "is_open"),
|
|
308
|
+
Output(NEW_SESSION_NAME, "value"),
|
|
309
|
+
Output(HOW_TO_CREATE_NEW_SESSION, "data"),
|
|
310
|
+
Output(ADMIN_SELECT_SESSION, "value"),
|
|
311
|
+
],
|
|
312
|
+
[
|
|
313
|
+
Input(ADMIN_NEW_SESSION, "n_clicks"),
|
|
314
|
+
Input(ADMIN_COPY_SESSION, "n_clicks"),
|
|
315
|
+
Input(NEW_SESSION_BUTTON, "n_clicks"),
|
|
316
|
+
Input(f"{NEW_SESSION_BUTTON}-cancel", "n_clicks"),
|
|
317
|
+
],
|
|
318
|
+
[
|
|
319
|
+
State(NEW_SESSION_NAME, "value"),
|
|
320
|
+
State(SESSION_CREATOR_MODAL, "is_open"),
|
|
321
|
+
State(ACTIVE_SESSION, "data"),
|
|
322
|
+
State(HOW_TO_CREATE_NEW_SESSION, "data"),
|
|
323
|
+
],
|
|
324
|
+
prevent_initial_call=True,
|
|
325
|
+
)
|
|
326
|
+
def toggle_session_creator_modal(
|
|
327
|
+
open_new_click,
|
|
328
|
+
open_copy_click,
|
|
329
|
+
confirm_clicked,
|
|
330
|
+
cancel_click,
|
|
331
|
+
new_session_name: str,
|
|
332
|
+
is_open: bool,
|
|
333
|
+
session_id: str,
|
|
334
|
+
copy_session: bool,
|
|
335
|
+
):
|
|
336
|
+
"""
|
|
337
|
+
Handles all buttons that have to do with creating a new session.
|
|
338
|
+
This is the opening of the model via the new or copy session buttons,
|
|
339
|
+
the creation of the new session, and the closing of the modal.
|
|
340
|
+
|
|
341
|
+
Coping a session and creating a new session opens the same modal.
|
|
342
|
+
Therefore, the information of the button which is clicked is stored.
|
|
343
|
+
"""
|
|
344
|
+
ctx = callback_context
|
|
345
|
+
if not ctx.triggered:
|
|
346
|
+
return no_update, no_update, no_update, no_update
|
|
347
|
+
triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]
|
|
348
|
+
if triggered_id == ADMIN_NEW_SESSION and not is_open:
|
|
349
|
+
return True, "", False, no_update
|
|
350
|
+
if triggered_id == ADMIN_COPY_SESSION and not is_open:
|
|
351
|
+
return True, "", True, no_update
|
|
352
|
+
if triggered_id == NEW_SESSION_BUTTON and is_open:
|
|
353
|
+
session_manager: SessionManager = get_app().server.session_manager
|
|
354
|
+
if copy_session:
|
|
355
|
+
session_manager.copy_session(session_id, new_session_name)
|
|
356
|
+
else:
|
|
357
|
+
session_manager.create_new_session(new_session_name)
|
|
358
|
+
|
|
359
|
+
return False, "", no_update, new_session_name
|
|
360
|
+
if triggered_id == f"{NEW_SESSION_BUTTON}-cancel":
|
|
361
|
+
return False, "", no_update, no_update
|
|
362
|
+
return is_open, "", no_update, no_update
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import dash_bootstrap_components as dbc
|
|
2
|
+
from dash import get_app, html
|
|
3
|
+
|
|
4
|
+
from ..componentids import (
|
|
5
|
+
NEW_SESSION_BUTTON,
|
|
6
|
+
SESSION_CREATOR_MODAL,
|
|
7
|
+
NEW_SESSION_NAME,
|
|
8
|
+
)
|
|
9
|
+
from ..stylingconfigurator import StylingConfigurator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_new_session_window() -> dbc.Modal:
|
|
13
|
+
"""Creates the modal for creating a new session.
|
|
14
|
+
|
|
15
|
+
Coping a session and creating a new session opens the same modal.
|
|
16
|
+
Therefore, the information of the button which is clicked is stored.
|
|
17
|
+
"""
|
|
18
|
+
sc: StylingConfigurator = get_app().server.styling_config
|
|
19
|
+
window = dbc.Modal(
|
|
20
|
+
[
|
|
21
|
+
dbc.ModalHeader(dbc.ModalTitle("Create New Session"), close_button=False),
|
|
22
|
+
dbc.ModalBody(
|
|
23
|
+
[
|
|
24
|
+
dbc.Label("Session name:"),
|
|
25
|
+
dbc.Input(id=NEW_SESSION_NAME, placeholder="Session name"),
|
|
26
|
+
]
|
|
27
|
+
),
|
|
28
|
+
dbc.ModalFooter(
|
|
29
|
+
[
|
|
30
|
+
html.Div(
|
|
31
|
+
dbc.Button(
|
|
32
|
+
"Create",
|
|
33
|
+
id=NEW_SESSION_BUTTON,
|
|
34
|
+
class_name="new-session-confirm-button",
|
|
35
|
+
),
|
|
36
|
+
id=f"{NEW_SESSION_BUTTON}-wrapper",
|
|
37
|
+
style={"display": "inline-block"},
|
|
38
|
+
),
|
|
39
|
+
html.Div(id=f"{NEW_SESSION_BUTTON}-tooltip-container"),
|
|
40
|
+
dbc.Button(
|
|
41
|
+
"Cancel",
|
|
42
|
+
id=f"{NEW_SESSION_BUTTON}-cancel",
|
|
43
|
+
class_name="new-session-cancel-button ms-auto",
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
),
|
|
47
|
+
],
|
|
48
|
+
id=SESSION_CREATOR_MODAL,
|
|
49
|
+
is_open=False,
|
|
50
|
+
centered=True,
|
|
51
|
+
class_name="themed-modal",
|
|
52
|
+
style=sc.initiate_theme_colors(),
|
|
53
|
+
keyboard=False,
|
|
54
|
+
backdrop="static",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return window
|