ivoryos 1.2.2__py3-none-any.whl → 1.2.4__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 ivoryos might be problematic. Click here for more details.
- ivoryos/__init__.py +18 -18
- ivoryos/routes/auth/templates/login.html +25 -0
- ivoryos/routes/auth/templates/signup.html +32 -0
- ivoryos/routes/control/templates/controllers.html +166 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/data/templates/components/step_card.html +13 -0
- ivoryos/routes/data/templates/workflow_database.html +109 -0
- ivoryos/routes/data/templates/workflow_view.html +130 -0
- ivoryos/routes/design/design.py +1 -1
- ivoryos/routes/design/templates/components/action_form.html +53 -0
- ivoryos/routes/design/templates/components/actions_panel.html +25 -0
- ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
- ivoryos/routes/design/templates/components/canvas.html +5 -0
- ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
- ivoryos/routes/design/templates/components/canvas_header.html +75 -0
- ivoryos/routes/design/templates/components/canvas_main.html +34 -0
- ivoryos/routes/design/templates/components/deck_selector.html +10 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +38 -0
- ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
- ivoryos/routes/design/templates/components/modals.html +6 -0
- ivoryos/routes/design/templates/components/python_code_overlay.html +39 -0
- ivoryos/routes/design/templates/components/sidebar.html +15 -0
- ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos/routes/design/templates/experiment_builder.html +41 -0
- ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
- ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
- ivoryos/routes/execute/templates/components/run_panel.html +9 -0
- ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +398 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
- ivoryos/routes/execute/templates/experiment_run.html +294 -0
- ivoryos/routes/library/templates/library.html +92 -0
- ivoryos/routes/main/templates/help.html +141 -0
- ivoryos/routes/main/templates/home.html +103 -0
- ivoryos/static/favicon.ico +0 -0
- ivoryos/static/gui_annotation/Slide1.png +0 -0
- ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- ivoryos/static/js/action_handlers.js +213 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/overlay.js +12 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/socket_handler.js +125 -0
- ivoryos/static/js/sortable_card.js +24 -0
- ivoryos/static/js/sortable_design.js +138 -0
- ivoryos/static/js/ui_state.js +114 -0
- ivoryos/static/logo.webp +0 -0
- ivoryos/static/style.css +211 -0
- ivoryos/templates/base.html +157 -0
- ivoryos/utils/form.py +6 -1
- ivoryos/utils/script_runner.py +46 -38
- ivoryos/version.py +1 -1
- {ivoryos-1.2.2.dist-info → ivoryos-1.2.4.dist-info}/METADATA +80 -119
- ivoryos-1.2.4.dist-info/RECORD +100 -0
- ivoryos-1.2.2.dist-info/RECORD +0 -47
- {ivoryos-1.2.2.dist-info → ivoryos-1.2.4.dist-info}/WHEEL +0 -0
- {ivoryos-1.2.2.dist-info → ivoryos-1.2.4.dist-info}/licenses/LICENSE +0 -0
- {ivoryos-1.2.2.dist-info → ivoryos-1.2.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{# Logging panel component for experiment run #}
|
|
2
|
+
<div class="col-lg-6 col-sm-12 logging-panel">
|
|
3
|
+
<p>
|
|
4
|
+
<div class="p d-flex justify-content-between align-items-center">
|
|
5
|
+
<h5>Progress:</h5>
|
|
6
|
+
<div class="d-flex gap-2 ms-auto">
|
|
7
|
+
<button id="pause-resume" class="btn btn-info text-white" data-bs-toggle="tooltip" title="Pause execution">
|
|
8
|
+
{% if pause_status %}
|
|
9
|
+
<i class="bi bi-play-circle"></i>
|
|
10
|
+
{% else %}
|
|
11
|
+
<i class="bi bi-pause-circle"></i>
|
|
12
|
+
{% endif %}
|
|
13
|
+
</button>
|
|
14
|
+
<button id="abort-current" class="btn btn-danger text-white" data-bs-toggle="tooltip" title="Stop execution after current step">
|
|
15
|
+
<i class="bi bi-stop-circle"></i>
|
|
16
|
+
</button>
|
|
17
|
+
<button id="abort-pending" class="btn btn-warning text-white" data-bs-toggle="tooltip" title="Stop execution after current iteration">
|
|
18
|
+
<i class="bi bi-hourglass-split"></i>
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="text-muted mt-2">
|
|
23
|
+
<small><strong>Note:</strong> The current step cannot be paused or stopped until it completes. </small>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="progress" role="progressbar" aria-label="Animated striped example" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100">
|
|
27
|
+
<div id="progress-bar-inner" class="progress-bar progress-bar-striped progress-bar-animated"></div>
|
|
28
|
+
</div>
|
|
29
|
+
<p><h5>Log:</h5></p>
|
|
30
|
+
<div id="logging-panel"></div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{# Progress panel component for experiment run #}
|
|
2
|
+
<div class="col-lg-6 col-sm-12" id="code-panel" style="{{ '' if pause_status else 'display: none;'}}">
|
|
3
|
+
<p>
|
|
4
|
+
<h5>Progress:</h5>
|
|
5
|
+
{% if "prep" in line_collection.keys() %}
|
|
6
|
+
{% set stype = "prep" %}
|
|
7
|
+
<h6>Preparation:</h6>
|
|
8
|
+
{% for code in line_collection["prep"] %}
|
|
9
|
+
<pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
|
|
10
|
+
{% endfor %}
|
|
11
|
+
{% endif %}
|
|
12
|
+
{% if "script" in line_collection.keys() %}
|
|
13
|
+
{% set stype = "script" %}
|
|
14
|
+
<h6>Experiment:</h6>
|
|
15
|
+
{% for code in line_collection["script"] %}
|
|
16
|
+
<pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
|
|
17
|
+
{% endfor %}
|
|
18
|
+
{% endif %}
|
|
19
|
+
{% if "cleanup" in line_collection.keys() %}
|
|
20
|
+
{% set stype = "cleanup" %}
|
|
21
|
+
<h6>Cleanup:</h6>
|
|
22
|
+
{% for code in line_collection["cleanup"] %}
|
|
23
|
+
<pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
{% endif %}
|
|
26
|
+
</p>
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{# Run panel component for experiment run #}
|
|
2
|
+
{% if script['script'] or script['prep'] or script['cleanup'] %}
|
|
3
|
+
<div class="col-lg-6 col-sm-12" id="run-panel" style="{{ 'display: none;' if pause_status else '' }}">
|
|
4
|
+
{% include 'components/run_tabs.html' %}
|
|
5
|
+
</div>
|
|
6
|
+
{% else %}
|
|
7
|
+
<div class="col-lg-6 col-sm-12" id="placeholder-panel">
|
|
8
|
+
</div>
|
|
9
|
+
{% endif %}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{# Run tabs component for experiment run #}
|
|
2
|
+
<ul class="nav nav-tabs" id="myTabs" role="tablist">
|
|
3
|
+
<li class="nav-item" role="presentation">
|
|
4
|
+
<a class="nav-link {{ 'disabled' if config_list else '' }} {{ 'active' if not config_list else '' }}" id="tab1-tab" data-bs-toggle="tab" href="#tab1" role="tab" aria-controls="tab1" aria-selected="false">Repeat</a>
|
|
5
|
+
</li>
|
|
6
|
+
<li class="nav-item" role="presentation">
|
|
7
|
+
<a class="nav-link {{ 'disabled' if not config_list else '' }} {{ 'active' if config_list else '' }}" id="tab2-tab" data-bs-toggle="tab" href="#tab2" role="tab" aria-controls="tab2" aria-selected="false">Configuration</a>
|
|
8
|
+
</li>
|
|
9
|
+
<li class="nav-item" role="presentation">
|
|
10
|
+
<a class="nav-link {{ 'disabled' if not config_list or not return_list else '' }}" id="tab3-tab" data-bs-toggle="tab" href="#tab3" role="tab" aria-controls="tab3" aria-selected="false">Bayesian Optimization</a>
|
|
11
|
+
</li>
|
|
12
|
+
</ul>
|
|
13
|
+
<div class="tab-content" id="myTabsContent">
|
|
14
|
+
{% include 'components/tab_repeat.html' %}
|
|
15
|
+
{% include 'components/tab_configuration.html' %}
|
|
16
|
+
{% include 'components/tab_bayesian.html' %}
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
{# Bayesian optimization tab component #}
|
|
2
|
+
|
|
3
|
+
<div class="tab-pane fade" id="tab3" role="tabpanel" aria-labelledby="tab3-tab">
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
{# <div class="row align-items-center mb-3">#}
|
|
7
|
+
{# <div class="col-6">#}
|
|
8
|
+
{# <form method="POST" id="loadHistory" name="loadHistory" action="{{ url_for('execute.execute_files.upload_history') }}" enctype="multipart/form-data">#}
|
|
9
|
+
{# <div class="input-group">#}
|
|
10
|
+
{# <input class="form-control" name="historyfile" id="historyfile" type="file" accept=".csv" onchange="var f=document.getElementById('loadHistory'); if(f) f.submit();"> </div>#}
|
|
11
|
+
{# </form>#}
|
|
12
|
+
{# </div>#}
|
|
13
|
+
{# </div>#}
|
|
14
|
+
<h6 class="fw-bold mt-2 mb-1">Load Previous Data</h6>
|
|
15
|
+
|
|
16
|
+
<form method="POST" name="bo" action="{{ url_for('execute.run_bo') }}">
|
|
17
|
+
<div class="container py-2">
|
|
18
|
+
|
|
19
|
+
<!-- Data Loading Section -->
|
|
20
|
+
<div class="input-group mb-3">
|
|
21
|
+
<label class="input-group-text"><i class="bi bi-folder2-open"></i></label>
|
|
22
|
+
<select class="form-select" id="existing_data" name="existing_data">
|
|
23
|
+
<option value="">Load existing data...</option>
|
|
24
|
+
{% for data in data_list %}
|
|
25
|
+
<option value="{{ data }}">{{ data }} </option>
|
|
26
|
+
{% endfor %}
|
|
27
|
+
</select>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- Data preview section -->
|
|
31
|
+
<div class="row mb-3" id="data_preview_section" style="display: none;">
|
|
32
|
+
<div class="col-12">
|
|
33
|
+
<div class="card">
|
|
34
|
+
<div class="card-header py-2">
|
|
35
|
+
<small class="fw-bold">Data Preview</small>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="card-body py-2">
|
|
38
|
+
<div id="data_preview_content">
|
|
39
|
+
<small class="text-muted">Select a data source to preview</small>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<hr class="my-3">
|
|
47
|
+
<!-- Optimizer Selection -->
|
|
48
|
+
<div class="input-group mb-3">
|
|
49
|
+
<label class="input-group-text"><i class="bi bi-gear"></i></label>
|
|
50
|
+
<select class="form-select" id="optimizer_type" name="optimizer_type" onchange="updateOptimizerInfo()">
|
|
51
|
+
<option value="">Select optimizer...</option>
|
|
52
|
+
{% for optimizer_name, optimizer_info in optimizer_schema.items() %}
|
|
53
|
+
<option value="{{ optimizer_name }}"
|
|
54
|
+
data-multiobjective="{{ optimizer_info.multiple_objectives }}"
|
|
55
|
+
data-parameter-types="{{ optimizer_info.parameter_types|join(',') }}"
|
|
56
|
+
data-optimizer-config="{{ optimizer_info.optimizer_config|tojson|e }}">
|
|
57
|
+
{{ optimizer_name }} {% if optimizer_info.multiple_objectives %}(Multi-objective supported){% endif %}
|
|
58
|
+
</option>
|
|
59
|
+
{% endfor %}
|
|
60
|
+
</select>
|
|
61
|
+
</div>
|
|
62
|
+
<!-- Tabs Navigation -->
|
|
63
|
+
<ul class="nav nav-tabs" id="configTabs" role="tablist">
|
|
64
|
+
<li class="nav-item" role="presentation">
|
|
65
|
+
<button class="nav-link active" id="parameters-tab" data-bs-toggle="tab" data-bs-target="#parameters" type="button" role="tab" aria-controls="parameters" aria-selected="true">
|
|
66
|
+
Parameters
|
|
67
|
+
</button>
|
|
68
|
+
</li>
|
|
69
|
+
<li class="nav-item" role="presentation">
|
|
70
|
+
<button class="nav-link" id="advanced-tab" data-bs-toggle="tab" data-bs-target="#advanced" type="button" role="tab" aria-controls="advanced" aria-selected="false">
|
|
71
|
+
Advanced Settings
|
|
72
|
+
</button>
|
|
73
|
+
</li>
|
|
74
|
+
</ul>
|
|
75
|
+
|
|
76
|
+
<!-- Tab Content -->
|
|
77
|
+
<div class="tab-content" id="configTabContent">
|
|
78
|
+
<!-- Parameters Tab -->
|
|
79
|
+
<div class="tab-pane fade show active" id="parameters" role="tabpanel" aria-labelledby="parameters-tab">
|
|
80
|
+
<div class="py-3">
|
|
81
|
+
<h6 class="fw-bold mt-2 mb-1">Parameters</h6>
|
|
82
|
+
<div id="parameters_container">
|
|
83
|
+
{% for config in config_list %}
|
|
84
|
+
<div class="row align-items-center mb-2 parameter-row">
|
|
85
|
+
<div class="col-3 col-form-label-sm">
|
|
86
|
+
{{ config }}:
|
|
87
|
+
</div>
|
|
88
|
+
<div class="col-3">
|
|
89
|
+
<select class="form-select form-select-sm parameter-type" id="{{config}}_type" name="{{config}}_type" onchange="updateParameterInputs(this)">
|
|
90
|
+
<!-- Options will be populated by JavaScript -->
|
|
91
|
+
</select>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="col-6 parameter-inputs">
|
|
94
|
+
<input type="text" class="form-control form-control-sm single-input" id="{{config}}_value" name="{{config}}_value" placeholder="1, 2, 3">
|
|
95
|
+
<div class="range-inputs" style="display: none;">
|
|
96
|
+
<div class="row">
|
|
97
|
+
<div class="col-6">
|
|
98
|
+
<input type="text" class="form-control form-control-sm" id="{{config}}_min" name="{{config}}_min" placeholder="Min value">
|
|
99
|
+
</div>
|
|
100
|
+
<div class="col-6">
|
|
101
|
+
<input type="text" class="form-control form-control-sm" id="{{config}}_max" name="{{config}}_max" placeholder="Max value">
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
{% endfor %}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<!-- Objectives -->
|
|
111
|
+
<h6 class="fw-bold mt-3 mb-1">Objectives</h6>
|
|
112
|
+
{% for objective in return_list %}
|
|
113
|
+
<div class="row align-items-center mb-2">
|
|
114
|
+
<div class="col-3 col-form-label-sm">
|
|
115
|
+
{{ objective }}:
|
|
116
|
+
</div>
|
|
117
|
+
<div class="col-6">
|
|
118
|
+
<select class="form-select form-select-sm" id="{{objective}}_min" name="{{objective}}_min">
|
|
119
|
+
<option selected>minimize</option>
|
|
120
|
+
<option>maximize</option>
|
|
121
|
+
<option>none</option>
|
|
122
|
+
</select>
|
|
123
|
+
</div>
|
|
124
|
+
{% if not return_list|length == 1 %}
|
|
125
|
+
<div class="col-3">
|
|
126
|
+
<input class="form-control" type="number" id="{{objective}}_weight" name="{{objective}}_weight" min="1" max="1000" value="1">
|
|
127
|
+
</div>
|
|
128
|
+
{% endif %}
|
|
129
|
+
</div>
|
|
130
|
+
{% endfor %}
|
|
131
|
+
|
|
132
|
+
<!-- Budget -->
|
|
133
|
+
<h6 class="fw-bold mt-3 mb-1">Budget</h6>
|
|
134
|
+
<div class="input-group mb-3">
|
|
135
|
+
<label class="input-group-text" for="repeat">Max iteration </label>
|
|
136
|
+
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="25">
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<!-- Advanced Settings Tab -->
|
|
142
|
+
<div class="tab-pane fade" id="advanced" role="tabpanel" aria-labelledby="advanced-tab">
|
|
143
|
+
<div class="py-3">
|
|
144
|
+
<h6 class="fw-bold mt-2 mb-1">Optimizer Configuration</h6>
|
|
145
|
+
<div id="optimizer_config_container" style="display: none;">
|
|
146
|
+
|
|
147
|
+
<!-- Step 1 Configuration -->
|
|
148
|
+
<div class="card mb-3">
|
|
149
|
+
<div class="card-header py-2">
|
|
150
|
+
<small class="fw-bold">Step 1 Configuration</small>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="card-body py-2">
|
|
153
|
+
<div class="row align-items-center mb-2">
|
|
154
|
+
<div class="col-3 col-form-label-sm">
|
|
155
|
+
Model:
|
|
156
|
+
</div>
|
|
157
|
+
<div class="col-6">
|
|
158
|
+
<select class="form-select form-select-sm" id="step1_model" name="step1_model">
|
|
159
|
+
<!-- Options will be populated by JavaScript -->
|
|
160
|
+
</select>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
<div class="row align-items-center mb-2" id="step1_num_samples_row">
|
|
164
|
+
<div class="col-3 col-form-label-sm">
|
|
165
|
+
Num Samples:
|
|
166
|
+
</div>
|
|
167
|
+
<div class="col-6">
|
|
168
|
+
<input type="number" class="form-control form-control-sm" id="step1_num_samples" name="step1_num_samples" min="1" value="">
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<!-- Step 2 Configuration -->
|
|
175
|
+
<div class="card mb-3">
|
|
176
|
+
<div class="card-header py-2">
|
|
177
|
+
<small class="fw-bold">Step 2 Configuration</small>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="card-body py-2">
|
|
180
|
+
<div class="row align-items-center mb-2">
|
|
181
|
+
<div class="col-3 col-form-label-sm">
|
|
182
|
+
Model:
|
|
183
|
+
</div>
|
|
184
|
+
<div class="col-6">
|
|
185
|
+
<select class="form-select form-select-sm" id="step2_model" name="step2_model">
|
|
186
|
+
<!-- Options will be populated by JavaScript -->
|
|
187
|
+
</select>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
<div id="optimizer_config_placeholder">
|
|
194
|
+
<small class="text-muted">Select an optimizer to configure advanced settings</small>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
{% if not no_deck_warning%}
|
|
201
|
+
<div class="input-group mb-3 mt-3">
|
|
202
|
+
<button class="form-control" type="submit" name="bo">Run</button>
|
|
203
|
+
</div>
|
|
204
|
+
{% endif %}
|
|
205
|
+
</div>
|
|
206
|
+
</form>
|
|
207
|
+
</div>
|
|
208
|
+
<script>
|
|
209
|
+
|
|
210
|
+
const optimizerSchema = {{ optimizer_schema|tojson }};
|
|
211
|
+
|
|
212
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
213
|
+
const dataSelect = document.getElementById('existing_data');
|
|
214
|
+
const previewSection = document.getElementById('data_preview_section');
|
|
215
|
+
const previewContent = document.getElementById('data_preview_content');
|
|
216
|
+
|
|
217
|
+
// Data preview functionality
|
|
218
|
+
dataSelect.addEventListener('change', function() {
|
|
219
|
+
const filename = dataSelect.value;
|
|
220
|
+
if (!filename) {
|
|
221
|
+
previewSection.style.display = 'none';
|
|
222
|
+
previewContent.innerHTML = '<small class="text-muted">Select a data source to preview</small>';
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
fetch('{{ url_for("execute.data_preview", filename="FILENAME") }}'.replace('FILENAME', encodeURIComponent(filename)))
|
|
226
|
+
.then(response => {
|
|
227
|
+
if (!response.ok) throw new Error('Network response was not ok');
|
|
228
|
+
return response.json();
|
|
229
|
+
})
|
|
230
|
+
.then(data => {
|
|
231
|
+
previewSection.style.display = '';
|
|
232
|
+
if (!data.rows || data.rows.length === 0) {
|
|
233
|
+
previewContent.innerHTML = '<small class="text-muted">No data found in file.</small>';
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
let html = '<table class="table table-sm table-bordered mb-0"><thead><tr>';
|
|
237
|
+
data.columns.forEach(col => html += `<th>${col}</th>`);
|
|
238
|
+
html += '</tr></thead><tbody>';
|
|
239
|
+
data.rows.forEach(row => {
|
|
240
|
+
html += '<tr>';
|
|
241
|
+
data.columns.forEach(col => html += `<td>${row[col] || ''}</td>`);
|
|
242
|
+
html += '</tr>';
|
|
243
|
+
});
|
|
244
|
+
html += '</tbody></table>';
|
|
245
|
+
previewContent.innerHTML = html;
|
|
246
|
+
})
|
|
247
|
+
.catch(() => {
|
|
248
|
+
previewSection.style.display = '';
|
|
249
|
+
previewContent.innerHTML = '<small class="text-danger">Failed to load preview.</small>';
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
function updateOptimizerInfo() {
|
|
255
|
+
const optimizerSelect = document.getElementById('optimizer_type');
|
|
256
|
+
const selectedOption = optimizerSelect.selectedOptions[0];
|
|
257
|
+
|
|
258
|
+
if (!selectedOption || !selectedOption.value) {
|
|
259
|
+
// Hide optimizer config and reset parameter types
|
|
260
|
+
document.getElementById('optimizer_config_container').style.display = 'none';
|
|
261
|
+
document.getElementById('optimizer_config_placeholder').style.display = 'block';
|
|
262
|
+
resetParameterTypes();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Update parameter types
|
|
267
|
+
const parameterTypes = selectedOption.dataset.parameterTypes.split(',');
|
|
268
|
+
updateParameterTypeOptions(parameterTypes);
|
|
269
|
+
|
|
270
|
+
// Update optimizer config - use the stored schema data instead
|
|
271
|
+
const optimizerName = selectedOption.value;
|
|
272
|
+
if (optimizerSchema[optimizerName] && optimizerSchema[optimizerName].optimizer_config) {
|
|
273
|
+
updateOptimizerConfig(optimizerSchema[optimizerName].optimizer_config);
|
|
274
|
+
} else {
|
|
275
|
+
document.getElementById('optimizer_config_container').style.display = 'none';
|
|
276
|
+
document.getElementById('optimizer_config_placeholder').style.display = 'block';
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function updateParameterTypeOptions(availableTypes) {
|
|
281
|
+
const parameterTypeSelects = document.querySelectorAll('.parameter-type');
|
|
282
|
+
|
|
283
|
+
parameterTypeSelects.forEach(select => {
|
|
284
|
+
// Clear existing options
|
|
285
|
+
select.innerHTML = '';
|
|
286
|
+
|
|
287
|
+
// Add available options based on optimizer
|
|
288
|
+
availableTypes.forEach(type => {
|
|
289
|
+
const option = document.createElement('option');
|
|
290
|
+
option.value = type;
|
|
291
|
+
option.textContent = type;
|
|
292
|
+
if (type === 'range') {
|
|
293
|
+
option.selected = true;
|
|
294
|
+
}
|
|
295
|
+
select.appendChild(option);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Update inputs for the current selection
|
|
299
|
+
updateParameterInputs(select);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function resetParameterTypes() {
|
|
304
|
+
const parameterTypeSelects = document.querySelectorAll('.parameter-type');
|
|
305
|
+
|
|
306
|
+
parameterTypeSelects.forEach(select => {
|
|
307
|
+
select.innerHTML = '';
|
|
308
|
+
const defaultTypes = ['range', 'choice', 'fixed'];
|
|
309
|
+
defaultTypes.forEach(type => {
|
|
310
|
+
const option = document.createElement('option');
|
|
311
|
+
option.value = type;
|
|
312
|
+
option.textContent = type;
|
|
313
|
+
if (type === 'range') {
|
|
314
|
+
option.selected = true;
|
|
315
|
+
}
|
|
316
|
+
select.appendChild(option);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
updateParameterInputs(select);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function updateParameterInputs(selectElement) {
|
|
324
|
+
const parameterRow = selectElement.closest('.parameter-row');
|
|
325
|
+
const singleInput = parameterRow.querySelector('.single-input');
|
|
326
|
+
const rangeInputs = parameterRow.querySelector('.range-inputs');
|
|
327
|
+
|
|
328
|
+
if (selectElement.value === 'range') {
|
|
329
|
+
singleInput.style.display = 'none';
|
|
330
|
+
rangeInputs.style.display = 'block';
|
|
331
|
+
} else {
|
|
332
|
+
singleInput.style.display = 'block';
|
|
333
|
+
rangeInputs.style.display = 'none';
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function updateOptimizerConfig(config) {
|
|
338
|
+
const container = document.getElementById('optimizer_config_container');
|
|
339
|
+
const placeholder = document.getElementById('optimizer_config_placeholder');
|
|
340
|
+
|
|
341
|
+
console.log('Updating optimizer config:', config); // Debug log
|
|
342
|
+
|
|
343
|
+
if (!config || !config.step_1) {
|
|
344
|
+
container.style.display = 'none';
|
|
345
|
+
placeholder.style.display = 'block';
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
placeholder.style.display = 'none';
|
|
350
|
+
container.style.display = 'block';
|
|
351
|
+
|
|
352
|
+
// Update Step 1
|
|
353
|
+
const step1ModelSelect = document.getElementById('step1_model');
|
|
354
|
+
if (step1ModelSelect && config.step_1.model) {
|
|
355
|
+
step1ModelSelect.innerHTML = '';
|
|
356
|
+
config.step_1.model.forEach(model => {
|
|
357
|
+
const option = document.createElement('option');
|
|
358
|
+
option.value = model;
|
|
359
|
+
option.textContent = model;
|
|
360
|
+
step1ModelSelect.appendChild(option);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Update Step 1 num_samples if exists
|
|
365
|
+
const step1NumSamplesRow = document.getElementById('step1_num_samples_row');
|
|
366
|
+
const step1NumSamplesInput = document.getElementById('step1_num_samples');
|
|
367
|
+
if (step1NumSamplesRow && step1NumSamplesInput) {
|
|
368
|
+
if (config.step_1.num_samples !== undefined) {
|
|
369
|
+
step1NumSamplesRow.style.display = '';
|
|
370
|
+
step1NumSamplesInput.value = config.step_1.num_samples;
|
|
371
|
+
} else {
|
|
372
|
+
step1NumSamplesRow.style.display = 'none';
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Update Step 2
|
|
377
|
+
if (config.step_2) {
|
|
378
|
+
const step2ModelSelect = document.getElementById('step2_model');
|
|
379
|
+
if (step2ModelSelect && config.step_2.model) {
|
|
380
|
+
step2ModelSelect.innerHTML = '';
|
|
381
|
+
config.step_2.model.forEach(model => {
|
|
382
|
+
const option = document.createElement('option');
|
|
383
|
+
option.value = model;
|
|
384
|
+
option.textContent = model;
|
|
385
|
+
step2ModelSelect.appendChild(option);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Initialize parameter inputs on page load
|
|
392
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
393
|
+
const parameterTypeSelects = document.querySelectorAll('.parameter-type');
|
|
394
|
+
parameterTypeSelects.forEach(select => {
|
|
395
|
+
updateParameterInputs(select);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
</script>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{# Configuration tab component #}
|
|
2
|
+
<div class="tab-pane fade {{ 'show active' if config_list else '' }}" id="tab2" role="tabpanel" aria-labelledby="tab2-tab">
|
|
3
|
+
<!-- File Management Section -->
|
|
4
|
+
<div class="card mb-4">
|
|
5
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
6
|
+
<h6 class="mb-0"><i class="bi bi-file-earmark-text"></i> Configuration File</h6>
|
|
7
|
+
<small class="text-muted">
|
|
8
|
+
<a href="{{ url_for('execute.execute_files.download_empty_config', filetype='configure') }}">
|
|
9
|
+
<i class="bi bi-download"></i> Download Empty Template
|
|
10
|
+
</a>
|
|
11
|
+
</small>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="card-body">
|
|
14
|
+
<div class="row g-3">
|
|
15
|
+
<!-- File Selection -->
|
|
16
|
+
<div class="col-md-6">
|
|
17
|
+
<form name="filenameForm" id="filenameForm" method="GET" action="{{ url_for('execute.experiment_run') }}" enctype="multipart/form-data">
|
|
18
|
+
<div class="input-group">
|
|
19
|
+
<label class="input-group-text"><i class="bi bi-folder2-open"></i></label>
|
|
20
|
+
<select class="form-select" name="filename" id="filenameSelect" onchange="document.getElementById('filenameForm').submit();">
|
|
21
|
+
<option {{ 'selected' if not filename else '' }} value="">-- Select existing file --</option>
|
|
22
|
+
{% for config_file in config_file_list %}
|
|
23
|
+
<option {{ 'selected' if filename == config_file else '' }} value="{{ config_file }}">{{ config_file }}</option>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</select>
|
|
26
|
+
</div>
|
|
27
|
+
</form>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- File Upload -->
|
|
31
|
+
<div class="col-md-6">
|
|
32
|
+
<form method="POST" id="loadFile" name="loadFile" action="{{ url_for('execute.execute_files.upload') }}" enctype="multipart/form-data">
|
|
33
|
+
<div class="input-group">
|
|
34
|
+
<input class="form-control" name="file" type="file" accept=".csv" required="required" onchange="document.getElementById('loadFile').submit();">
|
|
35
|
+
</div>
|
|
36
|
+
</form>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Configuration Table -->
|
|
43
|
+
<div class="card mb-4">
|
|
44
|
+
<div class="card-header position-relative">
|
|
45
|
+
<div class="position-absolute top-50 end-0 translate-middle-y me-3">
|
|
46
|
+
<span id="saveStatus" class="badge bg-success" style="display: none;">
|
|
47
|
+
<i class="bi bi-check-circle"></i> Auto-saved
|
|
48
|
+
</span>
|
|
49
|
+
<span id="modifiedStatus" class="badge bg-warning" style="display: none;">
|
|
50
|
+
<i class="bi bi-pencil"></i> Modified
|
|
51
|
+
</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="card-body p-0">
|
|
55
|
+
<form method="POST" name="online-config" id="online-config" action="{{url_for('execute.experiment_run')}}">
|
|
56
|
+
<div class="table-responsive">
|
|
57
|
+
<table id="dataInputTable" class="table table-striped table-hover mb-0">
|
|
58
|
+
<thead class="table-dark">
|
|
59
|
+
<tr>
|
|
60
|
+
<th style="width: 40px;">#</th>
|
|
61
|
+
{% for column in config_list %}
|
|
62
|
+
<th>{{ column }}</th>
|
|
63
|
+
{% endfor %}
|
|
64
|
+
<th></th>
|
|
65
|
+
</tr>
|
|
66
|
+
</thead>
|
|
67
|
+
<tbody id="tableBody">
|
|
68
|
+
</tbody>
|
|
69
|
+
</table>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="card-footer">
|
|
72
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
73
|
+
<div class="d-flex gap-2">
|
|
74
|
+
<button type="button" class="btn btn-success" onclick="addRow()">
|
|
75
|
+
<i class="bi bi-plus-circle"></i> Add Row
|
|
76
|
+
</button>
|
|
77
|
+
<button type="button" class="btn btn-warning" onclick="clearAllRows()">
|
|
78
|
+
<i class="bi bi-trash"></i> Clear All
|
|
79
|
+
</button>
|
|
80
|
+
<button type="button" class="btn btn-info" onclick="resetToFile()" id="resetToFileBtn" style="display: none;">
|
|
81
|
+
<i class="bi bi-arrow-clockwise"></i> Reset to File
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
<button type="submit" name="online-config" class="btn btn-primary btn-lg">
|
|
85
|
+
<i class="bi bi-play-circle"></i> Run
|
|
86
|
+
</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</form>
|
|
90
|
+
</div>
|
|
91
|
+
<!-- Config Preview (if loaded from file) -->
|
|
92
|
+
{% if config_preview %}
|
|
93
|
+
<div class="alert alert-info">
|
|
94
|
+
<small><i class="bi bi-info-circle"></i> {{ config_preview|length }} rows loaded from {{ filename }}</small>
|
|
95
|
+
</div>
|
|
96
|
+
{% endif %}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{# Repeat tab component #}
|
|
2
|
+
<div class="tab-pane fade {{ 'show active' if not config_list else '' }}" id="tab1" role="tabpanel" aria-labelledby="tab1-tab">
|
|
3
|
+
<p><h5>Control panel:</h5></p>
|
|
4
|
+
<form role="form" method='POST' name="run" action="{{url_for('execute.experiment_run')}}">
|
|
5
|
+
<div class="input-group mb-3">
|
|
6
|
+
<label class="input-group-text" for="repeat">Repeat for </label>
|
|
7
|
+
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="1">
|
|
8
|
+
<label class="input-group-text" for="repeat"> times</label>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="input-group mb-3">
|
|
11
|
+
<button class="form-control" type="submit" class="btn btn-dark">Run</button>
|
|
12
|
+
</div>
|
|
13
|
+
</form>
|
|
14
|
+
</div>
|