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,29 @@
|
|
|
1
|
+
import dash_bootstrap_components as dbc
|
|
2
|
+
|
|
3
|
+
from algomancy_gui.componentids import (
|
|
4
|
+
SCENARIO_DELETE_MODAL,
|
|
5
|
+
SCENARIO_CANCEL_DELETE_BUTTON,
|
|
6
|
+
SCENARIO_CONFIRM_DELETE_BUTTON,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def delete_confirmation_modal():
|
|
11
|
+
# Delete confirmation modal
|
|
12
|
+
return dbc.Modal(
|
|
13
|
+
[
|
|
14
|
+
dbc.ModalHeader("Confirm Deletion"),
|
|
15
|
+
dbc.ModalBody("Are you sure you want to delete this scenario?"),
|
|
16
|
+
dbc.ModalFooter(
|
|
17
|
+
[
|
|
18
|
+
dbc.Button(
|
|
19
|
+
"Cancel", id=SCENARIO_CANCEL_DELETE_BUTTON, color="secondary"
|
|
20
|
+
),
|
|
21
|
+
dbc.Button(
|
|
22
|
+
"Delete", id=SCENARIO_CONFIRM_DELETE_BUTTON, color="danger"
|
|
23
|
+
),
|
|
24
|
+
]
|
|
25
|
+
),
|
|
26
|
+
],
|
|
27
|
+
id=SCENARIO_DELETE_MODAL,
|
|
28
|
+
is_open=False,
|
|
29
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from dash import html, dcc, get_app
|
|
2
|
+
import dash_bootstrap_components as dbc
|
|
3
|
+
|
|
4
|
+
from algomancy_gui.componentids import (
|
|
5
|
+
SCENARIO_TAG_INPUT,
|
|
6
|
+
SCENARIO_DATA_INPUT,
|
|
7
|
+
SCENARIO_ALGO_INPUT,
|
|
8
|
+
SCENARIO_NEW_BUTTON,
|
|
9
|
+
SCENARIO_CREATE_STATUS,
|
|
10
|
+
SCENARIO_CREATOR_MODAL,
|
|
11
|
+
)
|
|
12
|
+
from algomancy_gui.managergetters import get_scenario_manager
|
|
13
|
+
from algomancy_gui.scenario_page.new_scenario_parameters_window import (
|
|
14
|
+
create_algo_parameters_window,
|
|
15
|
+
)
|
|
16
|
+
from algomancy_scenario import ScenarioManager
|
|
17
|
+
from algomancy_gui.stylingconfigurator import StylingConfigurator
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def new_scenario_creator(session_id: str):
|
|
21
|
+
sm: ScenarioManager = get_scenario_manager(get_app().server, session_id)
|
|
22
|
+
sc: StylingConfigurator = get_app().server.styling_config
|
|
23
|
+
|
|
24
|
+
# Modal for creating a new scenario
|
|
25
|
+
return dbc.Modal(
|
|
26
|
+
[
|
|
27
|
+
dbc.ModalHeader(dbc.ModalTitle("Create New Scenario"), close_button=False),
|
|
28
|
+
dbc.ModalBody(
|
|
29
|
+
[
|
|
30
|
+
dbc.Row(
|
|
31
|
+
[
|
|
32
|
+
dbc.Col(
|
|
33
|
+
dbc.Input(
|
|
34
|
+
id=SCENARIO_TAG_INPUT, placeholder="Scenario tag"
|
|
35
|
+
),
|
|
36
|
+
width=12,
|
|
37
|
+
),
|
|
38
|
+
],
|
|
39
|
+
className="mb-2",
|
|
40
|
+
),
|
|
41
|
+
dbc.Row(
|
|
42
|
+
[
|
|
43
|
+
dbc.Col(
|
|
44
|
+
dcc.Dropdown(
|
|
45
|
+
id=SCENARIO_DATA_INPUT,
|
|
46
|
+
options=[
|
|
47
|
+
{"label": ds, "value": ds}
|
|
48
|
+
for ds in sm.get_data_keys()
|
|
49
|
+
],
|
|
50
|
+
placeholder="Select dataset",
|
|
51
|
+
),
|
|
52
|
+
width=12,
|
|
53
|
+
),
|
|
54
|
+
],
|
|
55
|
+
className="mb-2",
|
|
56
|
+
),
|
|
57
|
+
dbc.Row(
|
|
58
|
+
[
|
|
59
|
+
dbc.Col(
|
|
60
|
+
dcc.Dropdown(
|
|
61
|
+
id=SCENARIO_ALGO_INPUT,
|
|
62
|
+
options=[
|
|
63
|
+
{"label": algo, "value": algo}
|
|
64
|
+
for algo in sm.available_algorithms
|
|
65
|
+
],
|
|
66
|
+
placeholder="Select algorithm",
|
|
67
|
+
),
|
|
68
|
+
width=12,
|
|
69
|
+
),
|
|
70
|
+
],
|
|
71
|
+
className="mb-2",
|
|
72
|
+
),
|
|
73
|
+
dbc.Row(
|
|
74
|
+
[
|
|
75
|
+
dbc.Col(create_algo_parameters_window(), width=12),
|
|
76
|
+
],
|
|
77
|
+
className="mb-3",
|
|
78
|
+
),
|
|
79
|
+
html.Div(id=SCENARIO_CREATE_STATUS, className="mt-2"),
|
|
80
|
+
]
|
|
81
|
+
),
|
|
82
|
+
dbc.ModalFooter(
|
|
83
|
+
[
|
|
84
|
+
dbc.Button(
|
|
85
|
+
"Create",
|
|
86
|
+
id=SCENARIO_NEW_BUTTON,
|
|
87
|
+
class_name="new-scenario-confirm-button",
|
|
88
|
+
),
|
|
89
|
+
dbc.Button(
|
|
90
|
+
"Cancel",
|
|
91
|
+
id=f"{SCENARIO_CREATOR_MODAL}-cancel",
|
|
92
|
+
class_name="new-scenario-cancel-button ms-auto",
|
|
93
|
+
),
|
|
94
|
+
]
|
|
95
|
+
),
|
|
96
|
+
],
|
|
97
|
+
id=SCENARIO_CREATOR_MODAL,
|
|
98
|
+
is_open=False,
|
|
99
|
+
centered=True,
|
|
100
|
+
class_name="themed-modal",
|
|
101
|
+
style=sc.initiate_theme_colors(),
|
|
102
|
+
keyboard=False,
|
|
103
|
+
backdrop="static",
|
|
104
|
+
)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from algomancy_gui.managergetters import get_manager
|
|
2
|
+
from algomancy_gui.sessionmanager import SessionManager
|
|
3
|
+
from algomancy_gui.settingsmanager import SettingsManager
|
|
4
|
+
import dash_bootstrap_components as dbc
|
|
5
|
+
from dash import html, get_app, dcc
|
|
6
|
+
from typing import Dict
|
|
7
|
+
|
|
8
|
+
from algomancy_scenario import ScenarioManager
|
|
9
|
+
from algomancy_utils.baseparameterset import (
|
|
10
|
+
TypedParameter,
|
|
11
|
+
ParameterType,
|
|
12
|
+
BaseParameterSet,
|
|
13
|
+
)
|
|
14
|
+
from algomancy_gui.componentids import (
|
|
15
|
+
ALGO_PARAMS_ENTRY_CARD,
|
|
16
|
+
ALGO_PARAMS_ENTRY_TAB,
|
|
17
|
+
ALGO_PARAMS_UPLOAD_TAB,
|
|
18
|
+
ALGO_PARAMS_WINDOW_ID,
|
|
19
|
+
ALGO_PARAM_INPUT,
|
|
20
|
+
ALGO_PARAM_DATE_INPUT,
|
|
21
|
+
ALGO_PARAM_INTERVAL_INPUT,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def prettify_label(key):
|
|
26
|
+
# Converts 'batch_size' -> 'Batch Size'
|
|
27
|
+
return key.replace("_", " ").title()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_parameter_input_component(param: TypedParameter, param_name: str):
|
|
31
|
+
typ = param.parameter_type
|
|
32
|
+
match typ:
|
|
33
|
+
case ParameterType.STRING:
|
|
34
|
+
return dbc.Input(
|
|
35
|
+
id={"type": ALGO_PARAM_INPUT, "param": param_name}, type="text"
|
|
36
|
+
)
|
|
37
|
+
case ParameterType.INTEGER:
|
|
38
|
+
return dbc.Input(
|
|
39
|
+
id={"type": ALGO_PARAM_INPUT, "param": param_name}, type="number"
|
|
40
|
+
)
|
|
41
|
+
case ParameterType.FLOAT:
|
|
42
|
+
return dbc.Input(
|
|
43
|
+
id={"type": ALGO_PARAM_INPUT, "param": param_name}, type="number"
|
|
44
|
+
)
|
|
45
|
+
case ParameterType.BOOLEAN:
|
|
46
|
+
return dbc.Checklist(
|
|
47
|
+
options=[{"label": "On", "value": True}],
|
|
48
|
+
id={"type": ALGO_PARAM_INPUT, "param": param_name},
|
|
49
|
+
switch=True,
|
|
50
|
+
)
|
|
51
|
+
case ParameterType.ENUM:
|
|
52
|
+
return dcc.Dropdown(
|
|
53
|
+
id={"type": ALGO_PARAM_INPUT, "param": param_name},
|
|
54
|
+
options=[
|
|
55
|
+
{"label": prettify_label(opt), "value": opt}
|
|
56
|
+
for opt in param.choices
|
|
57
|
+
],
|
|
58
|
+
)
|
|
59
|
+
case ParameterType.MULTI_ENUM:
|
|
60
|
+
return dcc.Dropdown(
|
|
61
|
+
id={"type": ALGO_PARAM_INPUT, "param": param_name},
|
|
62
|
+
options=[
|
|
63
|
+
{"label": prettify_label(opt), "value": opt}
|
|
64
|
+
for opt in param.choices
|
|
65
|
+
],
|
|
66
|
+
multi=True,
|
|
67
|
+
)
|
|
68
|
+
case ParameterType.TIME:
|
|
69
|
+
return dcc.DatePickerSingle(
|
|
70
|
+
id={"type": ALGO_PARAM_DATE_INPUT, "param": param_name},
|
|
71
|
+
date=param.default,
|
|
72
|
+
)
|
|
73
|
+
case ParameterType.INTERVAL:
|
|
74
|
+
return dcc.DatePickerRange(
|
|
75
|
+
id={"type": ALGO_PARAM_INTERVAL_INPUT, "param": param_name},
|
|
76
|
+
start_date=param.default_start,
|
|
77
|
+
end_date=param.default_end,
|
|
78
|
+
)
|
|
79
|
+
case _:
|
|
80
|
+
raise ValueError(f"Unsupported parameter type: {typ}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def create_input_group(param_dict: Dict[str, TypedParameter]):
|
|
84
|
+
"""
|
|
85
|
+
Given a dictionary of parameter names and Python types,
|
|
86
|
+
returns a list of input groups, each with a neat label and input.
|
|
87
|
+
"""
|
|
88
|
+
form_groups = []
|
|
89
|
+
for param_name, param in param_dict.items():
|
|
90
|
+
label = prettify_label(param_name)
|
|
91
|
+
|
|
92
|
+
html_id = f"{ALGO_PARAM_INPUT}-{param_name}"
|
|
93
|
+
|
|
94
|
+
component = create_parameter_input_component(param, param_name)
|
|
95
|
+
|
|
96
|
+
form_groups.append(
|
|
97
|
+
html.Div(
|
|
98
|
+
[
|
|
99
|
+
dbc.Label(label, html_for=html_id, className="d-block"),
|
|
100
|
+
html.Div(
|
|
101
|
+
component,
|
|
102
|
+
className="",
|
|
103
|
+
style={"display": "block", "width": "100%"},
|
|
104
|
+
),
|
|
105
|
+
],
|
|
106
|
+
className="mb-3",
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return form_groups
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def create_algo_parameters_entry_card_body(template_name: str) -> dbc.CardBody:
|
|
114
|
+
session_manager: SessionManager | ScenarioManager = get_manager(get_app().server)
|
|
115
|
+
algo_params: BaseParameterSet = session_manager.get_algorithm_parameters(
|
|
116
|
+
template_name
|
|
117
|
+
)
|
|
118
|
+
assert algo_params.has_inputs(), "No parameters found for algorithm template."
|
|
119
|
+
input_group = create_input_group(algo_params.get_parameters())
|
|
120
|
+
|
|
121
|
+
return dbc.CardBody(
|
|
122
|
+
input_group,
|
|
123
|
+
style={
|
|
124
|
+
"maxHeight": "60vh", # or e.g. "420px"
|
|
125
|
+
"overflowY": "auto",
|
|
126
|
+
"overflowX": "hidden",
|
|
127
|
+
},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def create_algo_parameters_window() -> dbc.Collapse:
|
|
132
|
+
tabs = []
|
|
133
|
+
|
|
134
|
+
param_entry_card = dbc.Card(id=ALGO_PARAMS_ENTRY_CARD, class_name="mt-3")
|
|
135
|
+
tabs.append(
|
|
136
|
+
dbc.Tab(param_entry_card, label="Fill in", tab_id=ALGO_PARAMS_ENTRY_TAB)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
settings: SettingsManager = get_app().server.settings
|
|
140
|
+
if settings.allow_param_upload_by_file:
|
|
141
|
+
param_upload_card = dbc.Card(
|
|
142
|
+
dbc.CardBody(html.Strong("TO DO: file uploader.")), class_name="mt-3"
|
|
143
|
+
)
|
|
144
|
+
tabs.append(
|
|
145
|
+
dbc.Tab(param_upload_card, label="Upload", tab_id=ALGO_PARAMS_UPLOAD_TAB)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
window = dbc.Collapse(
|
|
149
|
+
children=[dbc.Tabs(tabs)],
|
|
150
|
+
id=ALGO_PARAMS_WINDOW_ID,
|
|
151
|
+
is_open=False,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return window
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
scenario_badge.py - Scenario Status Badge Component
|
|
3
|
+
|
|
4
|
+
This module defines a function for creating styled status badges for scenarios,
|
|
5
|
+
providing visual indicators of a scenario's current status.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import dash_bootstrap_components as dbc
|
|
9
|
+
|
|
10
|
+
from algomancy_scenario import ScenarioStatus
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def status_badge(status):
|
|
14
|
+
"""
|
|
15
|
+
Transforms a given status value into a styled badge with the appropriate color coding,
|
|
16
|
+
enhancing the display of statuses for visual purposes.
|
|
17
|
+
|
|
18
|
+
:param status: The current status of a scenario, which must be one of the predefined
|
|
19
|
+
values in the ScenarioStatus enumeration.
|
|
20
|
+
:type status: ScenarioStatus
|
|
21
|
+
:return: A Dash Bootstrap Component (dbc) Badge element styled according to the status value.
|
|
22
|
+
:rtype: Dbc.Badge
|
|
23
|
+
"""
|
|
24
|
+
color_map = {
|
|
25
|
+
ScenarioStatus.CREATED: "secondary",
|
|
26
|
+
ScenarioStatus.QUEUED: "info",
|
|
27
|
+
ScenarioStatus.PROCESSING: "warning",
|
|
28
|
+
ScenarioStatus.COMPLETE: "success",
|
|
29
|
+
ScenarioStatus.FAILED: "danger",
|
|
30
|
+
}
|
|
31
|
+
return dbc.Badge(
|
|
32
|
+
status.capitalize(),
|
|
33
|
+
color=color_map.get(status, "dark"),
|
|
34
|
+
className="ms-1",
|
|
35
|
+
pill=True,
|
|
36
|
+
)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
scenario_cards.py - Scenario Card Components
|
|
3
|
+
|
|
4
|
+
This module defines functions for creating and styling scenario cards for the scenario page.
|
|
5
|
+
These cards display scenario information and provide buttons for processing and deleting scenarios.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dash import html, dcc
|
|
9
|
+
import dash_bootstrap_components as dbc
|
|
10
|
+
|
|
11
|
+
from algomancy_scenario.scenariomanager import ScenarioManager, Scenario
|
|
12
|
+
from algomancy_scenario import ScenarioStatus
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from .scenario_badge import status_badge
|
|
16
|
+
from ..componentids import (
|
|
17
|
+
SCENARIO_PROCESS_BUTTON,
|
|
18
|
+
SCENARIO_DELETE_BUTTON,
|
|
19
|
+
SCENARIO_CARD,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def hidden_card():
|
|
24
|
+
dummy_scenario = Scenario("dummy", None, None, None)
|
|
25
|
+
return scenario_card(dummy_scenario, is_hidden=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def scenario_card(s: Scenario, is_hidden: bool = False):
|
|
29
|
+
# Determine process button appearance based on scenario status
|
|
30
|
+
if s.status == ScenarioStatus.CREATED:
|
|
31
|
+
status = "created"
|
|
32
|
+
btn_text = "Process"
|
|
33
|
+
elif s.status in (ScenarioStatus.QUEUED, ScenarioStatus.PROCESSING):
|
|
34
|
+
status = "queued"
|
|
35
|
+
btn_text = "Cancel"
|
|
36
|
+
elif s.status in (ScenarioStatus.COMPLETE, ScenarioStatus.FAILED):
|
|
37
|
+
status = "completed"
|
|
38
|
+
btn_text = "Refresh"
|
|
39
|
+
else: # never
|
|
40
|
+
status = "standard"
|
|
41
|
+
btn_text = "Process"
|
|
42
|
+
|
|
43
|
+
return html.Div(
|
|
44
|
+
[
|
|
45
|
+
# Top row: Scenario tag
|
|
46
|
+
dbc.Row([dbc.Col(html.P(html.Strong(s.tag), className="mb-1"), width=12)]),
|
|
47
|
+
# Bottom row: Badge and buttons
|
|
48
|
+
dbc.Row(
|
|
49
|
+
[
|
|
50
|
+
dbc.Col(
|
|
51
|
+
# Left: Badge
|
|
52
|
+
html.Div(
|
|
53
|
+
[
|
|
54
|
+
html.Div(
|
|
55
|
+
status_badge(s.status),
|
|
56
|
+
id={
|
|
57
|
+
"type": "SCENARIO_STATUS_BADGE_INNER",
|
|
58
|
+
"index": s.id,
|
|
59
|
+
},
|
|
60
|
+
),
|
|
61
|
+
dcc.Store(
|
|
62
|
+
id={
|
|
63
|
+
"type": "scenario-status-badge-store",
|
|
64
|
+
"index": s.id,
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
],
|
|
68
|
+
className="d-flex align-items-center",
|
|
69
|
+
),
|
|
70
|
+
width=6,
|
|
71
|
+
),
|
|
72
|
+
# Right: Buttons
|
|
73
|
+
dbc.Col(
|
|
74
|
+
dbc.ButtonGroup(
|
|
75
|
+
[
|
|
76
|
+
dbc.Button(
|
|
77
|
+
btn_text,
|
|
78
|
+
id={"type": SCENARIO_PROCESS_BUTTON, "index": s.id},
|
|
79
|
+
size="sm",
|
|
80
|
+
n_clicks=0,
|
|
81
|
+
class_name="scenario-multi-btn-" + status,
|
|
82
|
+
),
|
|
83
|
+
dbc.Button(
|
|
84
|
+
"Delete",
|
|
85
|
+
id={"type": SCENARIO_DELETE_BUTTON, "index": s.id},
|
|
86
|
+
size="sm",
|
|
87
|
+
n_clicks=0,
|
|
88
|
+
class_name="scenario-delete-btn-" + status,
|
|
89
|
+
),
|
|
90
|
+
]
|
|
91
|
+
),
|
|
92
|
+
width=6,
|
|
93
|
+
className="d-flex align-items-center justify-content-end",
|
|
94
|
+
),
|
|
95
|
+
]
|
|
96
|
+
),
|
|
97
|
+
],
|
|
98
|
+
id={"type": SCENARIO_CARD, "index": s.id},
|
|
99
|
+
n_clicks=0,
|
|
100
|
+
className="scenario-card" if not is_hidden else "scenario-card hidden",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def scenario_cards(scenario_manager: ScenarioManager, selected_id=None):
|
|
105
|
+
"""
|
|
106
|
+
Creates a list of scenario cards for display.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
scenario_manager: The scenario manager containing the scenarios to display
|
|
110
|
+
selected_id: ID of the currently selected scenario, if any
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
list: A list of HTML Div components representing scenario cards
|
|
114
|
+
"""
|
|
115
|
+
cards = []
|
|
116
|
+
for scenario in scenario_manager.list_scenarios():
|
|
117
|
+
card = scenario_card(scenario)
|
|
118
|
+
cards.append(card)
|
|
119
|
+
return cards
|