ivoryos 1.0.1__py3-none-any.whl → 1.0.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.

@@ -17,338 +17,257 @@
17
17
  </script>
18
18
  {% endif %}
19
19
 
20
- <div class="accordion" id="accordionPanelsStayOpenExample">
21
- <div class="accordion-item design-control">
22
- <h2 class="accordion-header">
23
- <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#runpanel" aria-expanded="true" aria-controls="runpanel">
24
- Run Panel
25
- </button>
26
- </h2>
27
- <div id="runpanel" class="accordion-collapse collapse show">
28
- <div class="accordion-body">
29
- <div class="row">
30
- {% if script['script'] or script['prep'] or script['cleanup'] %}
31
- <div class="col-lg-6 col-sm-12" id="run-panel" style="{{ 'display: none;' if pause_status else '' }}">
32
- <ul class="nav nav-tabs" id="myTabs" role="tablist">
33
- <li class="nav-item" role="presentation">
34
- <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>
35
- </li>
36
- <li class="nav-item" role="presentation">
37
- <a class="nav-link {{ 'disabled' if (not config_list or config_list|count > 5) else '' }} {{ 'active' if config_list and config_list|count < 6 else '' }}" id="tab4-tab" data-bs-toggle="tab" href="#tab4" role="tab" aria-controls="tab4" aria-selected="false">Quick config</a>
38
- </li>
39
- <li class="nav-item" role="presentation">
40
- <a class="nav-link {{ 'disabled' if not config_list else '' }} {{ 'active' if config_list|count > 5 else '' }}" id="tab2-tab" data-bs-toggle="tab" href="#tab2" role="tab" aria-controls="tab2" aria-selected="false">Excel config</a>
41
- </li>
42
-
43
- <li class="nav-item" role="presentation">
44
- <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>
45
- </li>
46
- </ul>
47
- <div class="tab-content" id="myTabsContent">
48
- <div class="tab-pane fade {{ 'show active' if not config_list else '' }}" id="tab1" role="tabpanel" aria-labelledby="tab1-tab">
49
- <p><h5>Control panel:</h5></p>
50
- <form role="form" method='POST' name="run" action="{{url_for('design.experiment_run')}}">
51
- <div class="input-group mb-3">
52
- <label class="input-group-text" for="repeat">Repeat for </label>
53
- <input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="1">
54
- <label class="input-group-text" for="repeat"> times</label>
55
- </div>
56
- {# {% if not no_deck_warning%}#}
57
- <div class="input-group mb-3">
58
- <button class="form-control" type="submit" class="btn btn-dark">Run</button>
59
- </div>
60
- {# {% endif %}#}
61
- </form>
62
- </div>
63
- <div class="tab-pane fade {{ 'show active' if config_list|count>5 else '' }}" id="tab2" role="tabpanel" aria-labelledby="tab2-tab">
64
- <div>
20
+ <div class="row">
21
+ {% if script['script'] or script['prep'] or script['cleanup'] %}
22
+ <div class="col-lg-6 col-sm-12" id="run-panel" style="{{ 'display: none;' if pause_status else '' }}">
23
+ <ul class="nav nav-tabs" id="myTabs" role="tablist">
24
+ <li class="nav-item" role="presentation">
25
+ <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>
26
+ </li>
27
+ <li class="nav-item" role="presentation">
28
+ <a class="nav-link {{ 'disabled' if not config_list else '' }} {{ 'active' if config_list else '' }}" id="tab4-tab" data-bs-toggle="tab" href="#tab4" role="tab" aria-controls="tab4" aria-selected="false">Configuration</a>
29
+ </li>
30
+ <li class="nav-item" role="presentation">
31
+ <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>
32
+ </li>
33
+ </ul>
34
+ <div class="tab-content" id="myTabsContent">
35
+ <div class="tab-pane fade {{ 'show active' if not config_list else '' }}" id="tab1" role="tabpanel" aria-labelledby="tab1-tab">
36
+ <p><h5>Control panel:</h5></p>
37
+ <form role="form" method='POST' name="run" action="{{url_for('design.experiment_run')}}">
38
+ <div class="input-group mb-3">
39
+ <label class="input-group-text" for="repeat">Repeat for </label>
40
+ <input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="1">
41
+ <label class="input-group-text" for="repeat"> times</label>
42
+ </div>
43
+ <div class="input-group mb-3">
44
+ <button class="form-control" type="submit" class="btn btn-dark">Run</button>
45
+ </div>
46
+ </form>
47
+ </div>
65
48
 
66
- </div>
67
- <p><h5>Control with csv config:</h5></p>
68
- <p>Current configure: {{ filename }}</p>
69
- <div>
70
-
71
- <form name="filenameForm" id="filenameForm" method="GET" action="{{ url_for('design.experiment_run') }}" enctype="multipart/form-data">
72
- <div class="input-group mb-3">
73
- <select class="form-select" name="filename" id="filenameSelect" required onchange="document.getElementById('filenameForm').submit();">
74
- <option selected disabled hidden style="overflow-wrap: break-word;" value=""> -- choose config file --</option>
75
- {% for config_file in config_file_list %}
76
- <option {{'selected' if filename == config_file else '' }} style="overflow-wrap: break-word;" value="{{config_file}}">{{config_file}}</option>
77
- {% endfor %}
78
- </select>
79
- </div>
80
- </form>
81
-
82
- {# <li><a class="dropdown-item" data-bs-toggle="collapse" href="#preview" role="button" aria-expanded="false">Preview config</a></li>#}
83
- <a class="btn btn-primary" href="{{ url_for('design.download', filetype='configure') }}" >empty config</a>
84
- <a class="btn btn-primary" data-bs-toggle="collapse" href="#loadfile" role="button" aria-expanded="false">Upload .csv</a>
85
-
86
-
87
- <div class="collapse" id="loadfile" name="loadfile">
88
- <form method="POST" id="loadFile" name="loadFile" action="{{ url_for('design.upload') }}" enctype="multipart/form-data">
89
- <div class="input-group">
90
- <input class="form-control" name="file" type="file" required="required" onchange="document.getElementById('loadFile').submit();">
91
- <input class="btn btn-secondary" type="submit" value="Upload .csv">
92
- </div>
93
- </form>
94
- </div>
95
- </div>
49
+ {#TODO#}
50
+ <div class="tab-pane fade {{ 'show active' if config_list else '' }}" id="tab4" role="tabpanel" aria-labelledby="tab4-tab">
51
+ <!-- File Management Section -->
52
+ <div class="card mb-4">
53
+ <div class="card-header d-flex justify-content-between align-items-center">
54
+ <h6 class="mb-0"><i class="bi bi-file-earmark-text"></i> Configuration File</h6>
55
+ <small class="text-muted">
56
+ <a href="{{ url_for('design.download', filetype='configure') }}">
57
+ <i class="bi bi-download"></i> Download Empty Template
58
+ </a>
59
+ </small>
60
+ </div>
61
+ <div class="card-body">
62
+ <div class="row g-3">
63
+ <!-- File Selection -->
64
+ <div class="col-md-6">
65
+ <form name="filenameForm" id="filenameForm" method="GET" action="{{ url_for('design.experiment_run') }}" enctype="multipart/form-data">
66
+ <div class="input-group">
67
+ <label class="input-group-text"><i class="bi bi-folder2-open"></i></label>
68
+ <select class="form-select" name="filename" id="filenameSelect" onchange="document.getElementById('filenameForm').submit();">
69
+ <option {{ 'selected' if not filename else '' }} value="">-- Select existing file --</option>
70
+ {% for config_file in config_file_list %}
71
+ <option {{ 'selected' if filename == config_file else '' }} value="{{ config_file }}">{{ config_file }}</option>
72
+ {% endfor %}
73
+ </select>
74
+ </div>
75
+ </form>
76
+ </div>
96
77
 
97
- {#Preview config file#}
98
- <div id="preview">
99
- {% if config_preview %}
100
- <table class="table table-hover">
101
- <thead>
102
- <tr>
103
- {% for line in config_preview[0].keys() %}
104
- <th scope="col">{{ line }}</th>
105
- {% endfor %}
106
- </tr>
107
- </thead>
108
- <tbody>
109
- {% for line in config_preview %}
110
- <tr>
111
- {% for i in line.values() %}
112
- <td>{{ i }}</td>
113
- {% endfor %}
114
- </tr>
115
- {% endfor %}
116
- </tbody>
117
- </table>
118
- {% else %}
119
- Empty config file
120
- {% endif %}
121
- </div>
122
- {% if filename and not no_deck_warning %}
123
- <form role="form" method='POST' name="run" action="{{ url_for('design.experiment_run')}}">
124
- <div class="form-group">
125
- <button type="submit" class="btn btn-success">Run</button>
126
- </div>
127
- </form>
128
- {% endif %}
129
- </div>
78
+ <!-- File Upload -->
79
+ <div class="col-md-6">
80
+ <form method="POST" id="loadFile" name="loadFile" action="{{ url_for('design.upload') }}" enctype="multipart/form-data">
81
+ <div class="input-group">
82
+ <input class="form-control" name="file" type="file" accept=".csv" required="required" onchange="document.getElementById('loadFile').submit();">
83
+ </div>
84
+ </form>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ <!-- Configuration Table -->
130
90
 
131
- <div class="tab-pane fade" id="tab3" role="tabpanel" aria-labelledby="tab3-tab">
132
- <form method="POST" name="bo" action="{{ url_for('design.experiment_run') }}">
133
- <div class="container py-2">
134
-
135
- <!-- Parameters -->
136
- <h6 class="fw-bold mt-2 mb-1">Parameters</h6>
137
- {% for config in config_list %}
138
- <div class="row align-items-center mb-2">
139
- <div class="col-3 col-form-label-sm">
140
- {{ config }}:
141
- </div>
142
- <div class="col-6">
143
- <select class="form-select form-select-sm" id="{{config}}_type" name="{{config}}_type">
144
- <option selected value="range">range</option>
145
- <option value="choice">choice</option>
146
- <option value="fixed">fixed</option>
147
- </select>
148
- </div>
149
- <div class="col-3">
150
- <input type="text" class="form-control form-control-sm" id="{{config}}_value" name="{{config}}_value" placeholder="1, 2, 3">
151
- </div>
152
- </div>
153
- {% endfor %}
154
-
155
- <!-- Objective -->
156
- <h6 class="fw-bold mt-3 mb-1">Objectives</h6>
157
- {% for objective in return_list %}
158
- <div class="row align-items-center mb-2">
159
- <div class="col-3 col-form-label-sm">
160
- {{ objective }}:
161
- </div>
162
- <div class="col-6">
163
- <select class="form-select form-select-sm" id="{{objective}}_min" name="{{objective}}_min">
164
- <option selected>minimize</option>
165
- <option>maximize</option>
166
- <option>none</option>
167
- </select>
168
- </div>
169
- </div>
170
- {% endfor %}
171
-
172
- <h6 class="fw-bold mt-3 mb-1">Budget</h6>
173
-
174
- <div class="input-group mb-3">
175
- <label class="input-group-text" for="repeat">Max iteration </label>
176
- <input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="25">
177
- </div>
178
- {% if not no_deck_warning%}
179
- <div class="input-group mb-3">
180
- <button class="form-control" type="submit" name="bo">Run</button>
181
- </div>
182
- {% endif %}
183
- </div>
184
- </form>
185
- </div>
186
91
 
187
92
 
188
- <div class="tab-pane fade {{ 'show active' if config_list and config_list|count<=5 else '' }}" id="tab4" role="tabpanel" aria-labelledby="tab4-tab">
189
- {# <p><h5>Control panel:</h5></p>#}
190
- <div>
191
- <form method="POST" name="online-config" id="online-config" action="{{url_for('design.experiment_run')}}">
192
- <table id="dataInputTable" class="table table-striped">
193
- <thead class="thead-dark">
194
- <tr>
195
- {% for column in config_list %}
196
- <th>{{ column }}</th>
197
- {% endfor %}
198
- </tr>
199
- </thead>
200
- <tbody>
201
- </tbody>
202
- </table>
203
- <div class="d-flex align-items-center gap-2">
204
- <div class="btn btn-light" onclick="addRow()">
205
- <i class="bi bi-plus-circle"></i>
206
- </div>
207
- <button type="submit" name="online-config" class="form-control">Run</button>
208
- </div>
209
- </form>
210
- </div>
211
- </div>
212
- </div>
213
- </div>
214
- {% else %}
215
- <div class="col-lg-6 col-sm-12" id="placeholder-panel">
93
+ <div class="card mb-4">
94
+ <div class="card-header position-relative">
95
+ <div class="position-absolute top-50 end-0 translate-middle-y me-3">
96
+ <span id="saveStatus" class="badge bg-success" style="display: none;">
97
+ <i class="bi bi-check-circle"></i> Auto-saved
98
+ </span>
99
+ <span id="modifiedStatus" class="badge bg-warning" style="display: none;">
100
+ <i class="bi bi-pencil"></i> Modified
101
+ </span>
102
+ </div>
103
+ </div>
104
+ <div class="card-body p-0">
105
+ <form method="POST" name="online-config" id="online-config" action="{{url_for('design.experiment_run')}}">
106
+ <div class="table-responsive">
107
+ <table id="dataInputTable" class="table table-striped table-hover mb-0">
108
+ <thead class="table-dark">
109
+ <tr>
110
+ <th style="width: 40px;">#</th>
111
+ {% for column in config_list %}
112
+ <th>{{ column }}</th>
113
+ {% endfor %}
114
+ <th></th>
115
+ </tr>
116
+ </thead>
117
+ <tbody id="tableBody">
118
+ </tbody>
119
+ </table>
120
+ </div>
121
+ <div class="card-footer">
122
+ <div class="d-flex justify-content-between align-items-center">
123
+ <div class="d-flex gap-2">
124
+ <button type="button" class="btn btn-success" onclick="addRow()">
125
+ <i class="bi bi-plus-circle"></i> Add Row
126
+ </button>
127
+ <button type="button" class="btn btn-warning" onclick="clearAllRows()">
128
+ <i class="bi bi-trash"></i> Clear All
129
+ </button>
130
+ <button type="button" class="btn btn-info" onclick="resetToFile()" id="resetToFileBtn" style="display: none;">
131
+ <i class="bi bi-arrow-clockwise"></i> Reset to File
132
+ </button>
133
+ </div>
134
+ <button type="submit" name="online-config" class="btn btn-primary btn-lg">
135
+ <i class="bi bi-play-circle"></i> Run
136
+ </button>
137
+ </div>
138
+ </div>
139
+ </form>
140
+ </div>
141
+ <!-- Config Preview (if loaded from file) -->
142
+ {% if config_preview %}
143
+ <div class="alert alert-info">
144
+ <small><i class="bi bi-info-circle"></i> {{ config_preview|length }} rows loaded from {{ filename }}</small>
145
+ </div>
146
+ {% endif %}
147
+ </div>
148
+ </div>
216
149
 
217
- </div>
218
- {% endif %}
219
-
220
- <div class="col-lg-6 col-sm-12" id="code-panel" style="{{ '' if pause_status else 'display: none;'}}">
221
- <p>
222
- <h5>Progress:</h5>
223
- {% if "prep" in line_collection.keys() %} {% set stype = "cleanup" %}
224
- {% set stype = "prep" %}
225
-
226
- <h6>Preparation:</h6>
227
- {% for code in line_collection["prep"] %}
228
- <pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
229
- {% endfor %}
230
- {% endif %}
231
- {% if "script" in line_collection.keys() %}
232
- {% set stype = "script" %}
233
-
234
- <h6>Experiment:</h6>
235
- {% for code in line_collection["script"] %}
236
- <pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
237
- {% endfor %}
238
- {% endif %}
239
- {% if "cleanup" in line_collection.keys() %}
240
- {% set stype = "cleanup" %}
241
- <h6>Cleanup:</h6>
242
- {% for code in line_collection["cleanup"] %}
243
- <pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
244
- {% endfor %}
245
- {% endif %}
246
- </p>
247
- </div>
248
- <div class="col-lg-6 col-sm-12 logging-panel">
249
- <p>
250
- <div class="p d-flex justify-content-between align-items-center">
251
- <h5>Progress:</h5>
252
- <div class="d-flex gap-2 ms-auto">
253
- <button id="pause-resume" class="btn btn-info text-white" data-bs-toggle="tooltip" title="Pause execution">
254
- {% if pause_status %}
255
- <i class="bi bi-play-circle"></i> <!-- Icon for Pause -->
256
- {% else %}
257
- <i class="bi bi-pause-circle"></i> <!
258
- {% endif %}
259
- </button>
260
- <button id="abort-current" class="btn btn-danger text-white" data-bs-toggle="tooltip" title="Stop execution after current step">
261
- <i class="bi bi-stop-circle"></i> <!-- Icon for Stop After This Step -->
262
- </button>
263
- <button id="abort-pending" class="btn btn-warning text-white" data-bs-toggle="tooltip" title="Stop execution after current iteration">
264
- <i class="bi bi-hourglass-split"></i> <!-- Icon for Stop After This Iteration -->
265
- </button>
150
+ <div class="tab-pane fade" id="tab3" role="tabpanel" aria-labelledby="tab3-tab">
151
+ <form method="POST" name="bo" action="{{ url_for('design.experiment_run') }}">
152
+ <div class="container py-2">
153
+ <!-- Parameters -->
154
+ <h6 class="fw-bold mt-2 mb-1">Parameters</h6>
155
+ {% for config in config_list %}
156
+ <div class="row align-items-center mb-2">
157
+ <div class="col-3 col-form-label-sm">
158
+ {{ config }}:
159
+ </div>
160
+ <div class="col-6">
161
+ <select class="form-select form-select-sm" id="{{config}}_type" name="{{config}}_type">
162
+ <option selected value="range">range</option>
163
+ <option value="choice">choice</option>
164
+ <option value="fixed">fixed</option>
165
+ </select>
166
+ </div>
167
+ <div class="col-3">
168
+ <input type="text" class="form-control form-control-sm" id="{{config}}_value" name="{{config}}_value" placeholder="1, 2, 3">
266
169
  </div>
267
170
  </div>
268
- <div class="text-muted mt-2">
269
- <small><strong>Note:</strong> The current step cannot be paused or stopped until it completes. </small>
171
+ {% endfor %}
172
+ <!-- Objective -->
173
+ <h6 class="fw-bold mt-3 mb-1">Objectives</h6>
174
+ {% for objective in return_list %}
175
+ <div class="row align-items-center mb-2">
176
+ <div class="col-3 col-form-label-sm">
177
+ {{ objective }}:
178
+ </div>
179
+ <div class="col-6">
180
+ <select class="form-select form-select-sm" id="{{objective}}_min" name="{{objective}}_min">
181
+ <option selected>minimize</option>
182
+ <option>maximize</option>
183
+ <option>none</option>
184
+ </select>
185
+ </div>
270
186
  </div>
271
-
272
- <div class="progress" role="progressbar" aria-label="Animated striped example" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100">
273
- <div id="progress-bar-inner" class="progress-bar progress-bar-striped progress-bar-animated"></div>
187
+ {% endfor %}
188
+ <h6 class="fw-bold mt-3 mb-1">Budget</h6>
189
+ <div class="input-group mb-3">
190
+ <label class="input-group-text" for="repeat">Max iteration </label>
191
+ <input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="25">
274
192
  </div>
275
- <p><h5>Log:</h5></p>
276
- <div id="logging-panel"></div>
193
+ {% if not no_deck_warning%}
194
+ <div class="input-group mb-3">
195
+ <button class="form-control" type="submit" name="bo">Run</button>
196
+ </div>
197
+ {% endif %}
277
198
  </div>
278
- </div>
279
-
199
+ </form>
200
+ </div>
280
201
  </div>
281
202
  </div>
282
- </div>
283
- {# <div class="accordion-item design-control">#}
284
- {# <h2 class="accordion-header">#}
285
- {# <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#data" aria-expanded="false" aria-controls="script">#}
286
- {# Download experiment data csv#}
287
- {# </button>#}
288
- {# </h2>#}
289
- {# <div id="data" class="accordion-collapse collapse">#}
290
- {# <div class="accordion-body">#}
291
- {# {% if session["most_recent_result"] %}#}
292
- {# <p><a href="{{ url_for('design.download_results', filename=session["most_recent_result"]) }}">Download the latest data <i class="bi bi-download"></i></a></p>#}
293
- {# {% endif %}#}
294
- {# <p>#}
295
- {# <h5>All data files</h5>#}
296
- {# {% for datafile in data_list %}#}
297
- {# <div>#}
298
- {# {{ datafile }} <a href="{{ url_for('design.download_results', filename=datafile) }} "><i class="bi bi-download"></i></a>#}
299
- {# </div>#}
300
- {# {% endfor %}#}
301
- {# </p>#}
302
- {# </div>#}
303
- {# </div>#}
304
- {# </div>#}
305
- <div class="accordion-item design-control">
306
- <h2 class="accordion-header">
307
- <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#script" aria-expanded="false" aria-controls="script">
308
- Script
309
- </button>
310
- </h2>
311
- <div id="script" class="accordion-collapse collapse">
312
- <div class="accordion-body">
313
- {% set labels = {
314
- 'prep': 'Preparation:',
315
- 'script': 'Experiment:',
316
- 'cleanup': 'Clean up:'
317
- } %}
318
- {% for key, buttons in design_buttons.items() %}
319
- {% if buttons %}
320
- <h6>
321
- {{labels[key]}}
322
- </h6>
323
- <ul>
324
- {% for button in buttons %}
325
- <li id="{{ button['id'] }}" style="list-style-type: none;">
326
- <button type="button" class="btn btn-light" style="{{ button['style'] }}">{{ button['label'] }}</button>
327
- </li>
328
- {% endfor %}
329
- </ul>
330
- {% endif %}
331
- {% endfor %}
332
- </div>
203
+ {% else %}
204
+ <div class="col-lg-6 col-sm-12" id="placeholder-panel">
333
205
  </div>
334
- </div>
335
- <div class="accordion-item design-control">
336
- <h2 class="accordion-header">
337
- <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#python" aria-expanded="false" aria-controls="python">
338
- Python Script
339
- </button>
340
- </h2>
341
- <div id="python" class="accordion-collapse collapse">
342
- <div class="accordion-body">
343
- {% for stype, script in dot_py.items() %}
344
- <pre><code class="python" >{{script}}</code></pre>
206
+ {% endif %}
207
+
208
+ <div class="col-lg-6 col-sm-12" id="code-panel" style="{{ '' if pause_status else 'display: none;'}}">
209
+ <p>
210
+ <h5>Progress:</h5>
211
+ {% if "prep" in line_collection.keys() %}
212
+ {% set stype = "prep" %}
213
+ <h6>Preparation:</h6>
214
+ {% for code in line_collection["prep"] %}
215
+ <pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
216
+ {% endfor %}
217
+ {% endif %}
218
+ {% if "script" in line_collection.keys() %}
219
+ {% set stype = "script" %}
220
+ <h6>Experiment:</h6>
221
+ {% for code in line_collection["script"] %}
222
+ <pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
223
+ {% endfor %}
224
+ {% endif %}
225
+ {% if "cleanup" in line_collection.keys() %}
226
+ {% set stype = "cleanup" %}
227
+ <h6>Cleanup:</h6>
228
+ {% for code in line_collection["cleanup"] %}
229
+ <pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
345
230
  {% endfor %}
346
- <a href="{{ url_for('design.download', filetype='python') }}">Download <i class="bi bi-download"></i></a>
231
+ {% endif %}
232
+ </p>
233
+ </div>
234
+ <div class="col-lg-6 col-sm-12 logging-panel">
235
+ <p>
236
+ <div class="p d-flex justify-content-between align-items-center">
237
+ <h5>Progress:</h5>
238
+ <div class="d-flex gap-2 ms-auto">
239
+ <button id="pause-resume" class="btn btn-info text-white" data-bs-toggle="tooltip" title="Pause execution">
240
+ {% if pause_status %}
241
+ <i class="bi bi-play-circle"></i>
242
+ {% else %}
243
+ <i class="bi bi-pause-circle"></i>
244
+ {% endif %}
245
+ </button>
246
+ <button id="abort-current" class="btn btn-danger text-white" data-bs-toggle="tooltip" title="Stop execution after current step">
247
+ <i class="bi bi-stop-circle"></i>
248
+ </button>
249
+ <button id="abort-pending" class="btn btn-warning text-white" data-bs-toggle="tooltip" title="Stop execution after current iteration">
250
+ <i class="bi bi-hourglass-split"></i>
251
+ </button>
252
+ </div>
253
+ </div>
254
+ <div class="text-muted mt-2">
255
+ <small><strong>Note:</strong> The current step cannot be paused or stopped until it completes. </small>
347
256
  </div>
257
+
258
+ <div class="progress" role="progressbar" aria-label="Animated striped example" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100">
259
+ <div id="progress-bar-inner" class="progress-bar progress-bar-striped progress-bar-animated"></div>
348
260
  </div>
261
+ <p><h5>Log:</h5></p>
262
+ <div id="logging-panel"></div>
349
263
  </div>
264
+
350
265
  </div>
351
266
 
267
+
268
+
269
+
270
+
352
271
  <!-- Error Modal -->
353
272
  <div class="modal fade" id="error-modal" tabindex="-1" aria-labelledby="errorModalLabel" aria-hidden="true">
354
273
  <div class="modal-dialog">
@@ -370,26 +289,270 @@
370
289
  </div>
371
290
  </div>
372
291
 
373
-
374
292
  <script src="{{ url_for('static', filename='js/socket_handler.js') }}"></script>
375
293
  <script>
376
- var rowCount = 0; // Initialize row count
377
- function addRow() {
378
- rowCount++; // Increment row count each time a row is added
379
- var table = document.getElementById("dataInputTable");
380
- var newRow = table.insertRow(-1); // Adds a row at the end of the table
381
- {% for column, type in config_type_list.items() %}
382
- var cell = newRow.insertCell(-1);
383
- cell.innerHTML = '<input type="text" class="form-control" name="{{ column }}[' + rowCount + ']" placeholder={{ type }}>';
294
+ var rowCount = 0;
295
+ var configColumns = [
296
+ {% for column in config_list %}
297
+ '{{ column }}'{{ ',' if not loop.last else '' }}
298
+ {% endfor %}
299
+ ];
300
+ var configTypes = {
301
+ {% for column, type in config_type_list.items() %}
302
+ '{{ column }}': '{{ type }}'{{ ',' if not loop.last else '' }}
384
303
  {% endfor %}
304
+ };
305
+
306
+ // State management
307
+ var originalFileData = null;
308
+ var isModifiedFromFile = false;
309
+ var saveTimeout = null;
310
+ var lastSavedData = null;
311
+
312
+ function addRow(data = null, skipSave = false) {
313
+ rowCount++;
314
+ var tableBody = document.getElementById("tableBody");
315
+ var newRow = tableBody.insertRow(-1);
316
+
317
+ // Row number cell
318
+ var rowNumCell = newRow.insertCell(-1);
319
+ rowNumCell.innerHTML = '<span class="badge bg-secondary">' + rowCount + '</span>';
320
+
321
+ // Data cells
322
+ configColumns.forEach(function(column, index) {
323
+ var cell = newRow.insertCell(-1);
324
+ var value = data && data[column] ? data[column] : '';
325
+ var placeholder = configTypes[column] || 'value';
326
+ cell.innerHTML = '<input type="text" class="form-control form-control-sm" name="' +
327
+ column + '[' + rowCount + ']" value="' + value + '" placeholder="' + placeholder +
328
+ '" oninput="onInputChange()" onchange="onInputChange()">';
329
+ });
330
+
331
+ // Action cell
332
+ var actionCell = newRow.insertCell(-1);
333
+ actionCell.innerHTML = '<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeRow(this)" title="Remove row">' +
334
+ '<i class="bi bi-trash"></i></button>';
335
+
336
+ if (!skipSave) {
337
+ markAsModified();
338
+ debouncedSave();
339
+ }
385
340
  }
386
- </script>
387
- <script>
388
- // Initially add 5 rows when the page loads
389
- document.addEventListener("DOMContentLoaded", function() {
390
- for (let i = 0; i < 5; i++) {
391
- addRow();
341
+
342
+ function removeRow(button) {
343
+ var row = button.closest('tr');
344
+ row.remove();
345
+ updateRowNumbers();
346
+ markAsModified();
347
+ debouncedSave();
348
+ }
349
+
350
+ function updateRowNumbers() {
351
+ var tableBody = document.getElementById("tableBody");
352
+ var rows = tableBody.getElementsByTagName('tr');
353
+ for (var i = 0; i < rows.length; i++) {
354
+ var badge = rows[i].querySelector('.badge');
355
+ if (badge) {
356
+ badge.textContent = i + 1;
357
+ }
358
+ }
359
+ }
360
+
361
+ function clearAllRows() {
362
+ if (confirm('Are you sure you want to clear all rows?')) {
363
+ var tableBody = document.getElementById("tableBody");
364
+ tableBody.innerHTML = '';
365
+ rowCount = 0;
366
+ markAsModified();
367
+ clearSavedData();
368
+ // Add 5 empty rows by default
369
+ for (let i = 0; i < 5; i++) {
370
+ addRow(null, true);
371
+ }
372
+ debouncedSave();
373
+ }
374
+ }
375
+
376
+ function resetToFile() {
377
+ if (originalFileData && confirm('Reset to original file data? This will lose all manual changes.')) {
378
+ loadDataFromSource(originalFileData, false);
379
+ isModifiedFromFile = false;
380
+ updateStatusIndicators();
381
+ debouncedSave();
382
+ }
383
+ }
384
+
385
+ function onInputChange() {
386
+ markAsModified();
387
+ debouncedSave();
388
+ }
389
+
390
+ function markAsModified() {
391
+ if (originalFileData) {
392
+ isModifiedFromFile = true;
393
+ updateStatusIndicators();
394
+ }
395
+ }
396
+
397
+ function updateStatusIndicators() {
398
+ var modifiedStatus = document.getElementById('modifiedStatus');
399
+ var resetBtn = document.getElementById('resetToFileBtn');
400
+
401
+ if (isModifiedFromFile && originalFileData) {
402
+ modifiedStatus.style.display = 'inline-block';
403
+ resetBtn.style.display = 'inline-block';
404
+ } else {
405
+ modifiedStatus.style.display = 'none';
406
+ resetBtn.style.display = 'none';
407
+ }
408
+ }
409
+
410
+ function showSaveStatus() {
411
+ var saveStatus = document.getElementById('saveStatus');
412
+ saveStatus.style.display = 'inline-block';
413
+ setTimeout(function() {
414
+ saveStatus.style.display = 'none';
415
+ }, 2000);
416
+ }
417
+
418
+ function debouncedSave() {
419
+ clearTimeout(saveTimeout);
420
+ saveTimeout = setTimeout(function() {
421
+ saveFormData();
422
+ showSaveStatus();
423
+ }, 1000); // Save 1 second after user stops typing
424
+ }
425
+
426
+ function saveFormData() {
427
+ var formData = getCurrentFormData();
428
+ try {
429
+ sessionStorage.setItem('configFormData', JSON.stringify(formData));
430
+ sessionStorage.setItem('configModified', isModifiedFromFile.toString());
431
+ lastSavedData = formData;
432
+ } catch (e) {
433
+ console.warn('Could not save form data to sessionStorage:', e);
434
+ }
435
+ }
436
+
437
+ function getCurrentFormData() {
438
+ var tableBody = document.getElementById("tableBody");
439
+ var rows = tableBody.getElementsByTagName('tr');
440
+ var data = [];
441
+
442
+ for (var i = 0; i < rows.length; i++) {
443
+ var inputs = rows[i].getElementsByTagName('input');
444
+ var rowData = {};
445
+ var hasData = false;
446
+
447
+ for (var j = 0; j < inputs.length; j++) {
448
+ var input = inputs[j];
449
+ var name = input.name;
450
+ if (name) {
451
+ var columnName = name.substring(0, name.indexOf('['));
452
+ rowData[columnName] = input.value;
453
+ if (input.value.trim() !== '') {
454
+ hasData = true;
455
+ }
456
+ }
457
+ }
458
+
459
+ if (hasData) {
460
+ data.push(rowData);
461
+ }
462
+ }
463
+
464
+ return data;
465
+ }
466
+
467
+ function loadSavedData() {
468
+ try {
469
+ var savedData = sessionStorage.getItem('configFormData');
470
+ var savedModified = sessionStorage.getItem('configModified');
471
+
472
+ if (savedData) {
473
+ var parsedData = JSON.parse(savedData);
474
+ isModifiedFromFile = savedModified === 'true';
475
+ return parsedData;
476
+ }
477
+ } catch (e) {
478
+ console.warn('Could not load saved form data:', e);
479
+ }
480
+ return null;
481
+ }
482
+
483
+ function clearSavedData() {
484
+ try {
485
+ sessionStorage.removeItem('configFormData');
486
+ sessionStorage.removeItem('configModified');
487
+ } catch (e) {
488
+ console.warn('Could not clear saved data:', e);
489
+ }
490
+ }
491
+
492
+ function loadDataFromSource(data, isFromFile = false) {
493
+ // Clear existing rows
494
+ var tableBody = document.getElementById("tableBody");
495
+ tableBody.innerHTML = '';
496
+ rowCount = 0;
497
+
498
+ // Add rows with data
499
+ data.forEach(function(rowData) {
500
+ addRow(rowData, true);
501
+ });
502
+
503
+ // Add a few empty rows for additional input
504
+ for (let i = 0; i < 3; i++) {
505
+ addRow(null, true);
392
506
  }
507
+
508
+ if (isFromFile) {
509
+ originalFileData = JSON.parse(JSON.stringify(data)); // Deep copy
510
+ isModifiedFromFile = false;
511
+ clearSavedData(); // Clear saved data when loading from file
512
+ }
513
+
514
+ updateStatusIndicators();
515
+ }
516
+
517
+ function loadConfigData() {
518
+ // Check for saved form data first
519
+ var savedData = loadSavedData();
520
+
521
+ {% if config_preview %}
522
+ var fileData = {{ config_preview | tojson | safe }};
523
+ originalFileData = JSON.parse(JSON.stringify(fileData)); // Deep copy
524
+
525
+ if (savedData && savedData.length > 0) {
526
+ // Load saved data if available
527
+ loadDataFromSource(savedData, false);
528
+ console.log('Loaded saved form data');
529
+ } else {
530
+ // Load from file
531
+ loadDataFromSource(fileData, true);
532
+ console.log('Loaded file data');
533
+ }
534
+ {% else %}
535
+ if (savedData && savedData.length > 0) {
536
+ // Load saved data
537
+ loadDataFromSource(savedData, false);
538
+ console.log('Loaded saved form data');
539
+ } else {
540
+ // Add default empty rows
541
+ for (let i = 0; i < 5; i++) {
542
+ addRow(null, true);
543
+ }
544
+ }
545
+ {% endif %}
546
+ }
547
+
548
+ // Handle page unload
549
+ window.addEventListener('beforeunload', function() {
550
+ saveFormData();
551
+ });
552
+
553
+ // Initialize table when page loads
554
+ document.addEventListener("DOMContentLoaded", function() {
555
+ loadConfigData();
393
556
  });
394
557
  </script>
395
- {% endblock %}
558
+ {% endblock %}