ivoryos 1.2.5__py3-none-any.whl → 1.4.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.
Files changed (75) hide show
  1. docs/source/conf.py +84 -0
  2. ivoryos/__init__.py +16 -246
  3. ivoryos/app.py +154 -0
  4. ivoryos/optimizer/ax_optimizer.py +55 -28
  5. ivoryos/optimizer/base_optimizer.py +20 -1
  6. ivoryos/optimizer/baybe_optimizer.py +27 -17
  7. ivoryos/optimizer/nimo_optimizer.py +173 -0
  8. ivoryos/optimizer/registry.py +3 -1
  9. ivoryos/routes/auth/auth.py +35 -8
  10. ivoryos/routes/auth/templates/change_password.html +32 -0
  11. ivoryos/routes/control/control.py +58 -28
  12. ivoryos/routes/control/control_file.py +12 -15
  13. ivoryos/routes/control/control_new_device.py +21 -11
  14. ivoryos/routes/control/templates/controllers.html +27 -0
  15. ivoryos/routes/control/utils.py +2 -0
  16. ivoryos/routes/data/data.py +110 -44
  17. ivoryos/routes/data/templates/components/step_card.html +78 -13
  18. ivoryos/routes/data/templates/workflow_view.html +343 -113
  19. ivoryos/routes/design/design.py +59 -10
  20. ivoryos/routes/design/design_file.py +3 -3
  21. ivoryos/routes/design/design_step.py +43 -17
  22. ivoryos/routes/design/templates/components/action_form.html +2 -2
  23. ivoryos/routes/design/templates/components/canvas_main.html +6 -1
  24. ivoryos/routes/design/templates/components/edit_action_form.html +18 -3
  25. ivoryos/routes/design/templates/components/info_modal.html +318 -0
  26. ivoryos/routes/design/templates/components/instruments_panel.html +23 -1
  27. ivoryos/routes/design/templates/components/python_code_overlay.html +27 -10
  28. ivoryos/routes/design/templates/experiment_builder.html +3 -0
  29. ivoryos/routes/execute/execute.py +82 -22
  30. ivoryos/routes/execute/templates/components/logging_panel.html +50 -25
  31. ivoryos/routes/execute/templates/components/run_tabs.html +45 -2
  32. ivoryos/routes/execute/templates/components/tab_bayesian.html +447 -325
  33. ivoryos/routes/execute/templates/components/tab_configuration.html +303 -18
  34. ivoryos/routes/execute/templates/components/tab_repeat.html +6 -2
  35. ivoryos/routes/execute/templates/experiment_run.html +0 -264
  36. ivoryos/routes/library/library.py +9 -11
  37. ivoryos/routes/main/main.py +30 -2
  38. ivoryos/server.py +180 -0
  39. ivoryos/socket_handlers.py +1 -1
  40. ivoryos/static/ivoryos_logo.png +0 -0
  41. ivoryos/static/js/action_handlers.js +259 -88
  42. ivoryos/static/js/socket_handler.js +40 -5
  43. ivoryos/static/js/sortable_design.js +29 -11
  44. ivoryos/templates/base.html +61 -2
  45. ivoryos/utils/bo_campaign.py +18 -17
  46. ivoryos/utils/client_proxy.py +267 -36
  47. ivoryos/utils/db_models.py +286 -60
  48. ivoryos/utils/decorators.py +34 -0
  49. ivoryos/utils/form.py +52 -19
  50. ivoryos/utils/global_config.py +21 -0
  51. ivoryos/utils/nest_script.py +314 -0
  52. ivoryos/utils/py_to_json.py +80 -10
  53. ivoryos/utils/script_runner.py +573 -189
  54. ivoryos/utils/task_runner.py +69 -22
  55. ivoryos/utils/utils.py +48 -5
  56. ivoryos/version.py +1 -1
  57. {ivoryos-1.2.5.dist-info → ivoryos-1.4.4.dist-info}/METADATA +109 -47
  58. ivoryos-1.4.4.dist-info/RECORD +119 -0
  59. ivoryos-1.4.4.dist-info/top_level.txt +3 -0
  60. tests/__init__.py +0 -0
  61. tests/conftest.py +133 -0
  62. tests/integration/__init__.py +0 -0
  63. tests/integration/test_route_auth.py +80 -0
  64. tests/integration/test_route_control.py +94 -0
  65. tests/integration/test_route_database.py +61 -0
  66. tests/integration/test_route_design.py +36 -0
  67. tests/integration/test_route_main.py +35 -0
  68. tests/integration/test_sockets.py +26 -0
  69. tests/unit/test_type_conversion.py +42 -0
  70. tests/unit/test_util.py +3 -0
  71. ivoryos/routes/api/api.py +0 -56
  72. ivoryos-1.2.5.dist-info/RECORD +0 -100
  73. ivoryos-1.2.5.dist-info/top_level.txt +0 -1
  74. {ivoryos-1.2.5.dist-info → ivoryos-1.4.4.dist-info}/WHEEL +0 -0
  75. {ivoryos-1.2.5.dist-info → ivoryos-1.4.4.dist-info}/licenses/LICENSE +0 -0
@@ -68,24 +68,35 @@
68
68
  </tbody>
69
69
  </table>
70
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>
71
+
72
+ <div class="card-footer">
73
+ <div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
74
+ <!-- Left side: Action buttons -->
75
+ <div class="d-flex gap-2 flex-wrap">
76
+ <button type="button" class="btn btn-success" onclick="addRow()">
77
+ <i class="bi bi-plus-circle"></i> Add Row
78
+ </button>
79
+ <button type="button" class="btn btn-warning" onclick="clearAllRows()">
80
+ <i class="bi bi-trash"></i> Clear All
81
+ </button>
82
+ <button type="button" class="btn btn-info" onclick="resetToFile()" id="resetToFileBtn" style="display: none;">
83
+ <i class="bi bi-arrow-clockwise"></i> Reset to File
84
+ </button>
85
+ </div>
86
+
87
+ <!-- Right side: Batch size and Run button -->
88
+ <div class="d-flex gap-2 align-items-center flex-wrap">
89
+ <div class="input-group" style="width: auto;">
90
+ <label class="input-group-text" for="batch_size">Batch size</label>
91
+ <input class="form-control" type="number" id="batch_size" name="batch_size" min="1" max="1000" value="1" style="width: 80px;">
92
+ </div>
93
+ <button type="submit" name="online-config" class="btn btn-primary btn-lg">
94
+ <i class="bi bi-play-circle"></i> Run
95
+ </button>
96
+ </div>
97
+ </div>
83
98
  </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>
99
+
89
100
  </form>
90
101
  </div>
91
102
  <!-- Config Preview (if loaded from file) -->
@@ -95,4 +106,278 @@
95
106
  </div>
96
107
  {% endif %}
97
108
  </div>
98
- </div>
109
+ </div>
110
+ <script>
111
+ var rowCount = 0;
112
+ var configColumns = [
113
+ {% for column in config_list %}
114
+ '{{ column }}'{{ ',' if not loop.last else '' }}
115
+ {% endfor %}
116
+ ];
117
+ var configTypes = {
118
+ {% for column, type in config_type_list.items() %}
119
+ '{{ column }}': '{{ type }}'{{ ',' if not loop.last else '' }}
120
+ {% endfor %}
121
+ };
122
+
123
+ // State management
124
+ var originalFileData = null;
125
+ var isModifiedFromFile = false;
126
+ var saveTimeout = null;
127
+ var lastSavedData = null;
128
+
129
+ function addRow(data = null, skipSave = false) {
130
+ rowCount++;
131
+ var tableBody = document.getElementById("tableBody");
132
+ var newRow = tableBody.insertRow(-1);
133
+
134
+ // Row number cell
135
+ var rowNumCell = newRow.insertCell(-1);
136
+ rowNumCell.innerHTML = '<span class="badge bg-secondary">' + rowCount + '</span>';
137
+
138
+ // Data cells
139
+ configColumns.forEach(function(column, index) {
140
+ var cell = newRow.insertCell(-1);
141
+ var value = data && data[column] ? data[column] : '';
142
+ var placeholder = configTypes[column] || 'value';
143
+ cell.innerHTML = '<input type="text" class="form-control form-control-sm" name="' +
144
+ column + '[' + rowCount + ']" value="' + value + '" placeholder="' + placeholder +
145
+ '" oninput="onInputChange()" onchange="onInputChange()">';
146
+ });
147
+
148
+ // Action cell
149
+ var actionCell = newRow.insertCell(-1);
150
+ actionCell.innerHTML = '<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeRow(this)" title="Remove row">' +
151
+ '<i class="bi bi-trash"></i></button>';
152
+
153
+ if (!skipSave) {
154
+ markAsModified();
155
+ debouncedSave();
156
+ }
157
+ }
158
+
159
+ function removeRow(button) {
160
+ var row = button.closest('tr');
161
+ row.remove();
162
+ updateRowNumbers();
163
+ markAsModified();
164
+ debouncedSave();
165
+ }
166
+
167
+ function updateRowNumbers() {
168
+ var tableBody = document.getElementById("tableBody");
169
+ var rows = tableBody.getElementsByTagName('tr');
170
+ for (var i = 0; i < rows.length; i++) {
171
+ var badge = rows[i].querySelector('.badge');
172
+ if (badge) {
173
+ badge.textContent = i + 1;
174
+ }
175
+ }
176
+ }
177
+
178
+ function clearAllRows() {
179
+ if (confirm('Are you sure you want to clear all rows?')) {
180
+ var tableBody = document.getElementById("tableBody");
181
+ tableBody.innerHTML = '';
182
+ rowCount = 0;
183
+ markAsModified();
184
+ clearSavedData();
185
+ // Add 5 empty rows by default
186
+ for (let i = 0; i < 5; i++) {
187
+ addRow(null, true);
188
+ }
189
+ debouncedSave();
190
+ }
191
+ }
192
+
193
+ function resetToFile() {
194
+ if (originalFileData && confirm('Reset to original file data? This will lose all manual changes.')) {
195
+ loadDataFromSource(originalFileData, false);
196
+ isModifiedFromFile = false;
197
+ updateStatusIndicators();
198
+ debouncedSave();
199
+ }
200
+ }
201
+
202
+ function onInputChange() {
203
+ markAsModified();
204
+ debouncedSave();
205
+ }
206
+
207
+ function markAsModified() {
208
+ if (originalFileData) {
209
+ isModifiedFromFile = true;
210
+ updateStatusIndicators();
211
+ }
212
+ }
213
+
214
+ function updateStatusIndicators() {
215
+ var modifiedStatus = document.getElementById('modifiedStatus');
216
+ var resetBtn = document.getElementById('resetToFileBtn');
217
+
218
+ if (isModifiedFromFile && originalFileData) {
219
+ modifiedStatus.style.display = 'inline-block';
220
+ resetBtn.style.display = 'inline-block';
221
+ } else {
222
+ modifiedStatus.style.display = 'none';
223
+ resetBtn.style.display = 'none';
224
+ }
225
+ }
226
+
227
+ function showSaveStatus() {
228
+ var saveStatus = document.getElementById('saveStatus');
229
+ saveStatus.style.display = 'inline-block';
230
+ setTimeout(function() {
231
+ saveStatus.style.display = 'none';
232
+ }, 2000);
233
+ }
234
+
235
+ function debouncedSave() {
236
+ clearTimeout(saveTimeout);
237
+ saveTimeout = setTimeout(function() {
238
+ saveFormData();
239
+ showSaveStatus();
240
+ }, 1000); // Save 1 second after user stops typing
241
+ }
242
+
243
+ function saveFormData() {
244
+ var formData = getCurrentFormData();
245
+ try {
246
+ sessionStorage.setItem('configFormData', JSON.stringify(formData));
247
+ sessionStorage.setItem('configModified', isModifiedFromFile.toString());
248
+ lastSavedData = formData;
249
+ console.log('Tab2: Saved form data', formData.length, 'rows');
250
+ } catch (e) {
251
+ console.warn('Could not save form data to sessionStorage:', e);
252
+ }
253
+ }
254
+
255
+ function getCurrentFormData() {
256
+ var tableBody = document.getElementById("tableBody");
257
+ var rows = tableBody.getElementsByTagName('tr');
258
+ var data = [];
259
+
260
+ for (var i = 0; i < rows.length; i++) {
261
+ var inputs = rows[i].getElementsByTagName('input');
262
+ var rowData = {};
263
+ var hasData = false;
264
+
265
+ for (var j = 0; j < inputs.length; j++) {
266
+ var input = inputs[j];
267
+ var name = input.name;
268
+ if (name) {
269
+ var columnName = name.substring(0, name.indexOf('['));
270
+ rowData[columnName] = input.value;
271
+ if (input.value.trim() !== '') {
272
+ hasData = true;
273
+ }
274
+ }
275
+ }
276
+
277
+ if (hasData) {
278
+ data.push(rowData);
279
+ }
280
+ }
281
+
282
+ return data;
283
+ }
284
+
285
+ function loadSavedData() {
286
+ try {
287
+ var savedData = sessionStorage.getItem('configFormData');
288
+ var savedModified = sessionStorage.getItem('configModified');
289
+
290
+ if (savedData) {
291
+ var parsedData = JSON.parse(savedData);
292
+ isModifiedFromFile = savedModified === 'true';
293
+ console.log('Tab2: Loaded saved data', parsedData.length, 'rows');
294
+ return parsedData;
295
+ }
296
+ } catch (e) {
297
+ console.warn('Could not load saved form data:', e);
298
+ }
299
+ return null;
300
+ }
301
+
302
+ function clearSavedData() {
303
+ try {
304
+ sessionStorage.removeItem('configFormData');
305
+ sessionStorage.removeItem('configModified');
306
+ console.log('Tab2: Cleared saved data');
307
+ } catch (e) {
308
+ console.warn('Could not clear saved data:', e);
309
+ }
310
+ }
311
+
312
+ function loadDataFromSource(data, isFromFile = false) {
313
+ // Clear existing rows
314
+ var tableBody = document.getElementById("tableBody");
315
+ tableBody.innerHTML = '';
316
+ rowCount = 0;
317
+
318
+ // Add rows with data
319
+ data.forEach(function(rowData) {
320
+ addRow(rowData, true);
321
+ });
322
+
323
+ // Add a few empty rows for additional input
324
+ for (let i = 0; i < 3; i++) {
325
+ addRow(null, true);
326
+ }
327
+
328
+ if (isFromFile) {
329
+ originalFileData = JSON.parse(JSON.stringify(data)); // Deep copy
330
+ isModifiedFromFile = false;
331
+ clearSavedData(); // Clear saved data when loading from file
332
+ }
333
+
334
+ updateStatusIndicators();
335
+ }
336
+
337
+ function loadConfigData() {
338
+ // Check for saved form data first
339
+ var savedData = loadSavedData();
340
+
341
+ {% if config_preview %}
342
+ var fileData = {{ config_preview | tojson | safe }};
343
+ originalFileData = JSON.parse(JSON.stringify(fileData)); // Deep copy
344
+
345
+ if (savedData && savedData.length > 0) {
346
+ // Load saved data if available
347
+ loadDataFromSource(savedData, false);
348
+ console.log('Tab2: Loaded saved form data');
349
+ } else {
350
+ // Load from file
351
+ loadDataFromSource(fileData, true);
352
+ console.log('Tab2: Loaded file data');
353
+ }
354
+ {% else %}
355
+ if (savedData && savedData.length > 0) {
356
+ // Load saved data
357
+ loadDataFromSource(savedData, false);
358
+ console.log('Tab2: Loaded saved form data');
359
+ } else {
360
+ // Add default empty rows
361
+ for (let i = 0; i < 5; i++) {
362
+ addRow(null, true);
363
+ }
364
+ }
365
+ {% endif %}
366
+ }
367
+
368
+ // Handle page unload
369
+ window.addEventListener('beforeunload', function() {
370
+ saveFormData();
371
+ });
372
+
373
+ // Initialize table when page loads
374
+ document.addEventListener("DOMContentLoaded", function() {
375
+ loadConfigData();
376
+ });
377
+
378
+ // ============================================
379
+ // EXPOSE FUNCTIONS GLOBALLY FOR COORDINATION
380
+ // ============================================
381
+ window.saveFormData = saveFormData;
382
+ window.loadConfigData = loadConfigData;
383
+ </script>
@@ -1,7 +1,11 @@
1
1
  {# Repeat tab component #}
2
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>
3
+ <p></p>
4
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="batch_size">Batch size </label>
7
+ <input class="form-control" type="number" id="batch_size" name="batch_size" min="1" max="1000" value="1">
8
+ </div>
5
9
  <div class="input-group mb-3">
6
10
  <label class="input-group-text" for="repeat">Repeat for </label>
7
11
  <input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="1">
@@ -11,4 +15,4 @@
11
15
  <button class="form-control" type="submit" class="btn btn-dark">Run</button>
12
16
  </div>
13
17
  </form>
14
- </div>
18
+ </div>
@@ -26,269 +26,5 @@
26
26
  {% include 'components/error_modal.html' %}
27
27
 
28
28
  <script src="{{ url_for('static', filename='js/socket_handler.js') }}"></script>
29
- <script>
30
- var rowCount = 0;
31
- var configColumns = [
32
- {% for column in config_list %}
33
- '{{ column }}'{{ ',' if not loop.last else '' }}
34
- {% endfor %}
35
- ];
36
- var configTypes = {
37
- {% for column, type in config_type_list.items() %}
38
- '{{ column }}': '{{ type }}'{{ ',' if not loop.last else '' }}
39
- {% endfor %}
40
- };
41
29
 
42
- // State management
43
- var originalFileData = null;
44
- var isModifiedFromFile = false;
45
- var saveTimeout = null;
46
- var lastSavedData = null;
47
-
48
- function addRow(data = null, skipSave = false) {
49
- rowCount++;
50
- var tableBody = document.getElementById("tableBody");
51
- var newRow = tableBody.insertRow(-1);
52
-
53
- // Row number cell
54
- var rowNumCell = newRow.insertCell(-1);
55
- rowNumCell.innerHTML = '<span class="badge bg-secondary">' + rowCount + '</span>';
56
-
57
- // Data cells
58
- configColumns.forEach(function(column, index) {
59
- var cell = newRow.insertCell(-1);
60
- var value = data && data[column] ? data[column] : '';
61
- var placeholder = configTypes[column] || 'value';
62
- cell.innerHTML = '<input type="text" class="form-control form-control-sm" name="' +
63
- column + '[' + rowCount + ']" value="' + value + '" placeholder="' + placeholder +
64
- '" oninput="onInputChange()" onchange="onInputChange()">';
65
- });
66
-
67
- // Action cell
68
- var actionCell = newRow.insertCell(-1);
69
- actionCell.innerHTML = '<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeRow(this)" title="Remove row">' +
70
- '<i class="bi bi-trash"></i></button>';
71
-
72
- if (!skipSave) {
73
- markAsModified();
74
- debouncedSave();
75
- }
76
- }
77
-
78
- function removeRow(button) {
79
- var row = button.closest('tr');
80
- row.remove();
81
- updateRowNumbers();
82
- markAsModified();
83
- debouncedSave();
84
- }
85
-
86
- function updateRowNumbers() {
87
- var tableBody = document.getElementById("tableBody");
88
- var rows = tableBody.getElementsByTagName('tr');
89
- for (var i = 0; i < rows.length; i++) {
90
- var badge = rows[i].querySelector('.badge');
91
- if (badge) {
92
- badge.textContent = i + 1;
93
- }
94
- }
95
- }
96
-
97
- function clearAllRows() {
98
- if (confirm('Are you sure you want to clear all rows?')) {
99
- var tableBody = document.getElementById("tableBody");
100
- tableBody.innerHTML = '';
101
- rowCount = 0;
102
- markAsModified();
103
- clearSavedData();
104
- // Add 5 empty rows by default
105
- for (let i = 0; i < 5; i++) {
106
- addRow(null, true);
107
- }
108
- debouncedSave();
109
- }
110
- }
111
-
112
- function resetToFile() {
113
- if (originalFileData && confirm('Reset to original file data? This will lose all manual changes.')) {
114
- loadDataFromSource(originalFileData, false);
115
- isModifiedFromFile = false;
116
- updateStatusIndicators();
117
- debouncedSave();
118
- }
119
- }
120
-
121
- function onInputChange() {
122
- markAsModified();
123
- debouncedSave();
124
- }
125
-
126
- function markAsModified() {
127
- if (originalFileData) {
128
- isModifiedFromFile = true;
129
- updateStatusIndicators();
130
- }
131
- }
132
-
133
- function updateStatusIndicators() {
134
- var modifiedStatus = document.getElementById('modifiedStatus');
135
- var resetBtn = document.getElementById('resetToFileBtn');
136
-
137
- if (isModifiedFromFile && originalFileData) {
138
- modifiedStatus.style.display = 'inline-block';
139
- resetBtn.style.display = 'inline-block';
140
- } else {
141
- modifiedStatus.style.display = 'none';
142
- resetBtn.style.display = 'none';
143
- }
144
- }
145
-
146
- function showSaveStatus() {
147
- var saveStatus = document.getElementById('saveStatus');
148
- saveStatus.style.display = 'inline-block';
149
- setTimeout(function() {
150
- saveStatus.style.display = 'none';
151
- }, 2000);
152
- }
153
-
154
- function debouncedSave() {
155
- clearTimeout(saveTimeout);
156
- saveTimeout = setTimeout(function() {
157
- saveFormData();
158
- showSaveStatus();
159
- }, 1000); // Save 1 second after user stops typing
160
- }
161
-
162
- function saveFormData() {
163
- var formData = getCurrentFormData();
164
- try {
165
- sessionStorage.setItem('configFormData', JSON.stringify(formData));
166
- sessionStorage.setItem('configModified', isModifiedFromFile.toString());
167
- lastSavedData = formData;
168
- } catch (e) {
169
- console.warn('Could not save form data to sessionStorage:', e);
170
- }
171
- }
172
-
173
- function getCurrentFormData() {
174
- var tableBody = document.getElementById("tableBody");
175
- var rows = tableBody.getElementsByTagName('tr');
176
- var data = [];
177
-
178
- for (var i = 0; i < rows.length; i++) {
179
- var inputs = rows[i].getElementsByTagName('input');
180
- var rowData = {};
181
- var hasData = false;
182
-
183
- for (var j = 0; j < inputs.length; j++) {
184
- var input = inputs[j];
185
- var name = input.name;
186
- if (name) {
187
- var columnName = name.substring(0, name.indexOf('['));
188
- rowData[columnName] = input.value;
189
- if (input.value.trim() !== '') {
190
- hasData = true;
191
- }
192
- }
193
- }
194
-
195
- if (hasData) {
196
- data.push(rowData);
197
- }
198
- }
199
-
200
- return data;
201
- }
202
-
203
- function loadSavedData() {
204
- try {
205
- var savedData = sessionStorage.getItem('configFormData');
206
- var savedModified = sessionStorage.getItem('configModified');
207
-
208
- if (savedData) {
209
- var parsedData = JSON.parse(savedData);
210
- isModifiedFromFile = savedModified === 'true';
211
- return parsedData;
212
- }
213
- } catch (e) {
214
- console.warn('Could not load saved form data:', e);
215
- }
216
- return null;
217
- }
218
-
219
- function clearSavedData() {
220
- try {
221
- sessionStorage.removeItem('configFormData');
222
- sessionStorage.removeItem('configModified');
223
- } catch (e) {
224
- console.warn('Could not clear saved data:', e);
225
- }
226
- }
227
-
228
- function loadDataFromSource(data, isFromFile = false) {
229
- // Clear existing rows
230
- var tableBody = document.getElementById("tableBody");
231
- tableBody.innerHTML = '';
232
- rowCount = 0;
233
-
234
- // Add rows with data
235
- data.forEach(function(rowData) {
236
- addRow(rowData, true);
237
- });
238
-
239
- // Add a few empty rows for additional input
240
- for (let i = 0; i < 3; i++) {
241
- addRow(null, true);
242
- }
243
-
244
- if (isFromFile) {
245
- originalFileData = JSON.parse(JSON.stringify(data)); // Deep copy
246
- isModifiedFromFile = false;
247
- clearSavedData(); // Clear saved data when loading from file
248
- }
249
-
250
- updateStatusIndicators();
251
- }
252
-
253
- function loadConfigData() {
254
- // Check for saved form data first
255
- var savedData = loadSavedData();
256
-
257
- {% if config_preview %}
258
- var fileData = {{ config_preview | tojson | safe }};
259
- originalFileData = JSON.parse(JSON.stringify(fileData)); // Deep copy
260
-
261
- if (savedData && savedData.length > 0) {
262
- // Load saved data if available
263
- loadDataFromSource(savedData, false);
264
- console.log('Loaded saved form data');
265
- } else {
266
- // Load from file
267
- loadDataFromSource(fileData, true);
268
- console.log('Loaded file data');
269
- }
270
- {% else %}
271
- if (savedData && savedData.length > 0) {
272
- // Load saved data
273
- loadDataFromSource(savedData, false);
274
- console.log('Loaded saved form data');
275
- } else {
276
- // Add default empty rows
277
- for (let i = 0; i < 5; i++) {
278
- addRow(null, true);
279
- }
280
- }
281
- {% endif %}
282
- }
283
-
284
- // Handle page unload
285
- window.addEventListener('beforeunload', function() {
286
- saveFormData();
287
- });
288
-
289
- // Initialize table when page loads
290
- document.addEventListener("DOMContentLoaded", function() {
291
- loadConfigData();
292
- });
293
- </script>
294
30
  {% endblock %}
@@ -13,25 +13,25 @@ library = Blueprint('library', __name__, template_folder='templates')
13
13
  def workflow_script(script_name:str):
14
14
  # todo: split this into two routes, one for GET and POST, another for DELETE
15
15
  """
16
- .. :quickref: Workflow Script Database; get, post, delete workflow script
16
+ .. :quickref: Workflow Script Database; get, post, delete a workflow script
17
17
 
18
- .. http:get:: /<string:script_name>
18
+ .. http:get:: /library/<string: script_name>
19
19
 
20
20
  :param script_name: script name
21
21
  :type script_name: str
22
- :status 302: redirect to :http:get:`/ivoryos/design/script/`
22
+ :status 302: redirect to :http:get:`/ivoryos/draft`
23
23
 
24
- .. http:post:: /<string:script_name>
24
+ .. http:post:: /library/<string: script_name>
25
25
 
26
26
  :param script_name: script name
27
27
  :type script_name: str
28
28
  :status 200: json response with success status
29
29
 
30
- .. http:delete:: /<string:script_name>
30
+ .. http:delete:: /library/<string: script_name>
31
31
 
32
32
  :param script_name: script name
33
33
  :type script_name: str
34
- :status 302: redirect to :http:get:`/ivoryos/design/script/`
34
+ :status 302: redirect to :http:get:`/ivoryos/draft`
35
35
 
36
36
  """
37
37
  row = Script.query.get(script_name)
@@ -89,10 +89,8 @@ def load_from_database():
89
89
 
90
90
  backend control through http requests
91
91
 
92
- .. http:get:: /designs/get/<deck_name>
92
+ .. http:get:: /library
93
93
 
94
- :param deck_name: filter for deck name
95
- :type deck_name: str
96
94
 
97
95
  """
98
96
  session.pop('edit_action', None) # reset cache
@@ -132,10 +130,10 @@ def save_as():
132
130
 
133
131
  save the current workflow script as
134
132
 
135
- .. http:post:: /library/save_as
133
+ .. http:post:: /library
136
134
 
137
135
  : form run_name: new workflow name
138
- :status 302: redirect to :http:get:`/ivoryos/design/script/`
136
+ :status 302: redirect to :http:get:`/ivoryos/draft`
139
137
 
140
138
  """
141
139
  if request.method == "POST":