ivoryos 1.0.9__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 (107) hide show
  1. docs/source/conf.py +84 -0
  2. ivoryos/__init__.py +17 -207
  3. ivoryos/app.py +154 -0
  4. ivoryos/config.py +1 -0
  5. ivoryos/optimizer/ax_optimizer.py +191 -0
  6. ivoryos/optimizer/base_optimizer.py +84 -0
  7. ivoryos/optimizer/baybe_optimizer.py +193 -0
  8. ivoryos/optimizer/nimo_optimizer.py +173 -0
  9. ivoryos/optimizer/registry.py +11 -0
  10. ivoryos/routes/auth/auth.py +43 -14
  11. ivoryos/routes/auth/templates/change_password.html +32 -0
  12. ivoryos/routes/control/control.py +101 -366
  13. ivoryos/routes/control/control_file.py +33 -0
  14. ivoryos/routes/control/control_new_device.py +152 -0
  15. ivoryos/routes/control/templates/controllers.html +193 -0
  16. ivoryos/routes/control/templates/controllers_new.html +112 -0
  17. ivoryos/routes/control/utils.py +40 -0
  18. ivoryos/routes/data/data.py +197 -0
  19. ivoryos/routes/data/templates/components/step_card.html +78 -0
  20. ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +14 -8
  21. ivoryos/routes/data/templates/workflow_view.html +360 -0
  22. ivoryos/routes/design/__init__.py +4 -0
  23. ivoryos/routes/design/design.py +348 -657
  24. ivoryos/routes/design/design_file.py +68 -0
  25. ivoryos/routes/design/design_step.py +171 -0
  26. ivoryos/routes/design/templates/components/action_form.html +53 -0
  27. ivoryos/routes/design/templates/components/actions_panel.html +25 -0
  28. ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
  29. ivoryos/routes/design/templates/components/canvas.html +5 -0
  30. ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
  31. ivoryos/routes/design/templates/components/canvas_header.html +75 -0
  32. ivoryos/routes/design/templates/components/canvas_main.html +39 -0
  33. ivoryos/routes/design/templates/components/deck_selector.html +10 -0
  34. ivoryos/routes/design/templates/components/edit_action_form.html +53 -0
  35. ivoryos/routes/design/templates/components/info_modal.html +318 -0
  36. ivoryos/routes/design/templates/components/instruments_panel.html +88 -0
  37. ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
  38. ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
  39. ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
  40. ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
  41. ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
  42. ivoryos/routes/design/templates/components/modals.html +6 -0
  43. ivoryos/routes/design/templates/components/python_code_overlay.html +56 -0
  44. ivoryos/routes/design/templates/components/sidebar.html +15 -0
  45. ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
  46. ivoryos/routes/design/templates/experiment_builder.html +44 -0
  47. ivoryos/routes/execute/__init__.py +0 -0
  48. ivoryos/routes/execute/execute.py +377 -0
  49. ivoryos/routes/execute/execute_file.py +78 -0
  50. ivoryos/routes/execute/templates/components/error_modal.html +20 -0
  51. ivoryos/routes/execute/templates/components/logging_panel.html +56 -0
  52. ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
  53. ivoryos/routes/execute/templates/components/run_panel.html +9 -0
  54. ivoryos/routes/execute/templates/components/run_tabs.html +60 -0
  55. ivoryos/routes/execute/templates/components/tab_bayesian.html +520 -0
  56. ivoryos/routes/execute/templates/components/tab_configuration.html +383 -0
  57. ivoryos/routes/execute/templates/components/tab_repeat.html +18 -0
  58. ivoryos/routes/execute/templates/experiment_run.html +30 -0
  59. ivoryos/routes/library/__init__.py +0 -0
  60. ivoryos/routes/library/library.py +157 -0
  61. ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +32 -23
  62. ivoryos/routes/main/main.py +31 -3
  63. ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
  64. ivoryos/server.py +180 -0
  65. ivoryos/socket_handlers.py +52 -0
  66. ivoryos/static/ivoryos_logo.png +0 -0
  67. ivoryos/static/js/action_handlers.js +384 -0
  68. ivoryos/static/js/db_delete.js +23 -0
  69. ivoryos/static/js/script_metadata.js +39 -0
  70. ivoryos/static/js/socket_handler.js +40 -5
  71. ivoryos/static/js/sortable_design.js +107 -56
  72. ivoryos/static/js/ui_state.js +114 -0
  73. ivoryos/templates/base.html +67 -8
  74. ivoryos/utils/bo_campaign.py +180 -3
  75. ivoryos/utils/client_proxy.py +267 -36
  76. ivoryos/utils/db_models.py +300 -65
  77. ivoryos/utils/decorators.py +34 -0
  78. ivoryos/utils/form.py +63 -29
  79. ivoryos/utils/global_config.py +34 -1
  80. ivoryos/utils/nest_script.py +314 -0
  81. ivoryos/utils/py_to_json.py +295 -0
  82. ivoryos/utils/script_runner.py +599 -165
  83. ivoryos/utils/serilize.py +201 -0
  84. ivoryos/utils/task_runner.py +71 -21
  85. ivoryos/utils/utils.py +50 -6
  86. ivoryos/version.py +1 -1
  87. ivoryos-1.4.4.dist-info/METADATA +263 -0
  88. ivoryos-1.4.4.dist-info/RECORD +119 -0
  89. {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/WHEEL +1 -1
  90. {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/top_level.txt +1 -0
  91. tests/unit/test_type_conversion.py +42 -0
  92. tests/unit/test_util.py +3 -0
  93. ivoryos/routes/control/templates/control/controllers.html +0 -78
  94. ivoryos/routes/control/templates/control/controllers_home.html +0 -55
  95. ivoryos/routes/control/templates/control/controllers_new.html +0 -89
  96. ivoryos/routes/database/database.py +0 -306
  97. ivoryos/routes/database/templates/database/step_card.html +0 -7
  98. ivoryos/routes/database/templates/database/workflow_view.html +0 -130
  99. ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
  100. ivoryos/routes/design/templates/design/experiment_run.html +0 -558
  101. ivoryos-1.0.9.dist-info/METADATA +0 -218
  102. ivoryos-1.0.9.dist-info/RECORD +0 -61
  103. /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
  104. /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
  105. /ivoryos/routes/{database → data}/__init__.py +0 -0
  106. /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
  107. {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,360 @@
1
+ {% extends 'base.html' %}
2
+
3
+ {% block title %}IvoryOS | Experiment Results{% endblock %}
4
+
5
+ {% block body %}
6
+ <style>
7
+ .vis-time-axis .vis-text.vis-minor,
8
+ .vis-time-axis .vis-text.vis-major {
9
+ color: #666;
10
+ }
11
+ .vis-item.stop {
12
+ background-color: #dc3545;
13
+ color: white;
14
+ border: none;
15
+ font-weight: bold;
16
+ }
17
+ .vis-item.prep {
18
+ background-color: #17a2b8;
19
+ border-color: #138496;
20
+ }
21
+ .vis-item.script {
22
+ background-color: #28a745;
23
+ border-color: #1e7e34;
24
+ }
25
+ .vis-item.cleanup {
26
+ background-color: #ffc107;
27
+ border-color: #d39e00;
28
+ color: #212529;
29
+ }
30
+ #visualization {
31
+ border: 1px solid #dee2e6;
32
+ border-radius: 0.375rem;
33
+ background-color: #fff;
34
+ min-height: 200px;
35
+ }
36
+
37
+ .section-header {
38
+ border-bottom: 2px solid #dee2e6;
39
+ padding-bottom: 0.5rem;
40
+ margin-bottom: 1rem;
41
+ color: #495057;
42
+ }
43
+ .loading-spinner {
44
+ display: none;
45
+ }
46
+ </style>
47
+
48
+
49
+ <div class="timeline-section" style="margin-bottom: 2rem;">
50
+ <h3 class="section-header">
51
+ <i class="fas fa-clock me-2"></i>Execution Timeline
52
+ </h3>
53
+ <div class="alert alert-info" role="alert">
54
+ <i class="fas fa-info-circle me-2"></i>
55
+ <strong>Tip:</strong> Click on timeline items to navigate to detailed views. Use Ctrl+scroll to zoom.
56
+ </div>
57
+ <div id="visualization"></div>
58
+ </div>
59
+
60
+ <!-- Phase Output Plot Section -->
61
+
62
+ <div class="data-section" style="margin-bottom: 2rem;">
63
+ <h3 class="section-header">
64
+ <div class="col-md-6">
65
+ <label for="output-select" class="form-label">Select Data Type:</label>
66
+ <select id="output-select" class="form-select">
67
+ <option value="">Loading data types...</option>
68
+ </select>
69
+ </div>
70
+ </h3>
71
+
72
+ <div class="plot-controls">
73
+ <div>
74
+ <div class="col-md-6 text-md-end">
75
+ <div class="loading-spinner">
76
+ <div class="spinner-border spinner-border-sm text-primary me-2" role="status">
77
+ <span class="visually-hidden">Loading...</span>
78
+ </div>
79
+ Loading plot data...
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ <div id="phase-plot" style="height:450px;" class="border rounded bg-white shadow-sm"></div>
85
+ </div>
86
+
87
+ <!-- Workflow Details Section -->
88
+
89
+ <div>
90
+ <h2 class="section-header">
91
+ <i class="fas fa-project-diagram me-2"></i>Workflow: {{ workflow.name }}
92
+ </h2>
93
+
94
+ <!-- Prep Phase -->
95
+ {% if grouped.prep %}
96
+ <div class="mb-4">
97
+ <h4 class="text-info mb-3">
98
+ <i class="fas fa-tools me-2"></i>Prep
99
+ </h4>
100
+ <div class="row">
101
+ {% for phase in grouped.prep %}
102
+ <div class="col-lg-6 mb-3">
103
+ {% include "components/step_card.html" %}
104
+ </div>
105
+ {% endfor %}
106
+ </div>
107
+ </div>
108
+ {% endif %}
109
+
110
+ <!-- Script Iterations -->
111
+ {% for repeat_index, phase_list in grouped.script.items()|sort %}
112
+ <div class="mb-4" id="card-iter{{ repeat_index }}">
113
+ <h4 class="text-success mb-3">
114
+ <i class="fas fa-redo me-2"></i>Iteration {{ repeat_index }}
115
+ </h4>
116
+ <div class="row">
117
+ {% for phase in phase_list %}
118
+ <div class="col-lg-6 mb-3">
119
+ {% include "components/step_card.html" %}
120
+ </div>
121
+ {% endfor %}
122
+ </div>
123
+ </div>
124
+ {% endfor %}
125
+
126
+ <!-- Cleanup Phase -->
127
+ {% if grouped.cleanup %}
128
+ <div>
129
+ <h4 class="text-warning mb-3">
130
+ <i class="fas fa-broom me-2"></i>Cleanup
131
+ </h4>
132
+ <div class="row">
133
+ {% for phase in grouped.cleanup %}
134
+ <div class="col-lg-6 mb-3">
135
+ {% include "components/step_card.html" %}
136
+ </div>
137
+ {% endfor %}
138
+ </div>
139
+ </div>
140
+ {% endif %}
141
+ </div>
142
+
143
+
144
+
145
+ <!-- External Dependencies -->
146
+ <script src="https://unpkg.com/vis-timeline@latest/standalone/umd/vis-timeline-graph2d.min.js"></script>
147
+ <link href="https://unpkg.com/vis-timeline@latest/styles/vis-timeline-graph2d.min.css" rel="stylesheet"/>
148
+ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
149
+
150
+ <script type="text/javascript">
151
+ document.addEventListener('DOMContentLoaded', function() {
152
+ // ---------------- Timeline Setup ----------------
153
+ const container = document.getElementById('visualization');
154
+ const groups = [
155
+ { id: 'all', content: 'Workflow Execution' }
156
+ ];
157
+
158
+ const items = [
159
+ {% if grouped.prep %}
160
+ {
161
+ id: 'prep',
162
+ content: 'Prep',
163
+ start: '{{ grouped.prep[0].start_time }}',
164
+ end: '{{ grouped.prep[-1].end_time }}',
165
+ className: 'prep',
166
+ group: 'all',
167
+ },
168
+ {% endif %}
169
+
170
+ {% for repeat_index, step_list in grouped.script.items()|sort %}
171
+ {
172
+ id: 'iter{{ repeat_index }}',
173
+ content: 'Iteration {{ repeat_index }}',
174
+ start: '{{ step_list[0].start_time }}',
175
+ end: '{{ step_list[-1].end_time }}',
176
+ className: 'script',
177
+ group: 'all',
178
+ },
179
+ {% for step in step_list %}
180
+ {% if step.method_name == "stop" %}
181
+ {
182
+ id: 'stop-{{ step.id }}',
183
+ content: '🛑 Stop',
184
+ start: '{{ step.start_time }}',
185
+ type: 'point',
186
+ className: 'stop',
187
+ group: 'all',
188
+ title: 'Stop event at {{ step.start_time }}'
189
+ },
190
+ {% endif %}
191
+ {% endfor %}
192
+ {% endfor %}
193
+
194
+ {% if grouped.cleanup %}
195
+ {
196
+ id: 'cleanup',
197
+ content: 'Cleanup ',
198
+ start: '{{ grouped.cleanup[0].start_time }}',
199
+ end: '{{ grouped.cleanup[-1].end_time }}',
200
+ className: 'cleanup',
201
+ group: 'all',
202
+ },
203
+ {% endif %}
204
+ ];
205
+
206
+ var timeline = new vis.Timeline(container, items, groups, {
207
+ clickToUse: true,
208
+ stack: false, // keep items from overlapping vertically
209
+ horizontalScroll: true,
210
+ zoomKey: 'ctrlKey'
211
+ });
212
+
213
+ timeline.on('select', function (props) {
214
+ const id = props.items[0];
215
+ if (id && id.startsWith('iter')) {
216
+ const card = document.getElementById('card-' + id);
217
+ if (card) {
218
+ const yOffset = -80;
219
+ const y = card.getBoundingClientRect().top + window.pageYOffset + yOffset;
220
+ window.scrollTo({ top: y, behavior: 'smooth' });
221
+ }
222
+ }
223
+ });
224
+
225
+ // ---------------- Phase Data Plot ----------------
226
+ const loadingSpinner = document.querySelector('.loading-spinner');
227
+ const select = document.getElementById('output-select');
228
+
229
+ loadingSpinner.style.display = 'block';
230
+
231
+
232
+ fetch("{{ url_for('data.workflow_phase_data', workflow_id=workflow.id) }}")
233
+ .then(res => {
234
+ if (!res.ok) {
235
+ throw new Error(`HTTP error! status: ${res.status}`);
236
+ }
237
+ return res.json();
238
+ })
239
+ .then(data => {
240
+ loadingSpinner.style.display = 'none';
241
+
242
+ const repeatKeys = Object.keys(data).sort((a, b) => a - b);
243
+ const dataSection = document.querySelector('.data-section'); // Get the entire section
244
+
245
+ if (!repeatKeys.length) {
246
+ // Hide the entire data section if no data
247
+ dataSection.style.display = 'none';
248
+ return;
249
+ }
250
+
251
+ const allKeys = new Set();
252
+ repeatKeys.forEach(k => {
253
+ Object.keys(data[k]).forEach(key => allKeys.add(key));
254
+ });
255
+
256
+ // If no keys found, also hide the section
257
+ if (allKeys.size === 0) {
258
+ dataSection.style.display = 'none';
259
+ return;
260
+ }
261
+
262
+ // Show the data section since we have data
263
+ dataSection.style.display = 'block';
264
+
265
+ // Clear and populate select options
266
+ select.innerHTML = '';
267
+ allKeys.forEach(k => {
268
+ const option = new Option(k, k);
269
+ select.appendChild(option);
270
+ });
271
+
272
+ function plotData(selectedKey) {
273
+ const x = [];
274
+ const y = [];
275
+
276
+ repeatKeys.forEach(repeat_index => {
277
+ const arr = data[repeat_index][selectedKey];
278
+ if (arr && arr.length) {
279
+ const batchCount = arr.length;
280
+
281
+ arr.forEach((d, batchIdx) => {
282
+ // Compute fractional x for batch indexing: e.g. 1.1, 1.2, 1.3
283
+ const xVal = parseFloat(repeat_index) + (batchIdx + 1) / (batchCount + 1);
284
+
285
+ if (typeof d === 'object' && d.x !== undefined && d.y !== undefined) {
286
+ x.push(xVal);
287
+ y.push(d.y);
288
+ } else if (typeof d === 'number') {
289
+ x.push(xVal);
290
+ y.push(d);
291
+ }
292
+ });
293
+ }
294
+ });
295
+
296
+ const trace = {
297
+ x: x,
298
+ y: y,
299
+ mode: 'markers',
300
+ name: selectedKey,
301
+ marker: { size: 8 },
302
+ line: { shape: 'linear', width: 1 },
303
+ };
304
+
305
+ const layout = {
306
+ xaxis: {
307
+ title: 'Trial (Batch Sub-index)',
308
+ gridcolor: '#e9ecef',
309
+ tickvals: repeatKeys.map(k => parseFloat(k)),
310
+ ticktext: repeatKeys.map(k => `Trial ${k}`),
311
+ },
312
+ yaxis: {
313
+ title: selectedKey,
314
+ gridcolor: '#e9ecef'
315
+ },
316
+ plot_bgcolor: '#ffffff',
317
+ paper_bgcolor: '#ffffff',
318
+ margin: { t: 60, r: 40, b: 60, l: 80 }
319
+ };
320
+
321
+ const config = {
322
+ responsive: true,
323
+ displayModeBar: true,
324
+ modeBarButtonsToRemove: ['lasso2d', 'select2d']
325
+ };
326
+
327
+ Plotly.newPlot('phase-plot', [trace], layout, config);
328
+ }
329
+
330
+ select.addEventListener('change', e => {
331
+ if (e.target.value) {
332
+ plotData(e.target.value);
333
+ }
334
+ });
335
+
336
+ // Plot first available data type
337
+ if (allKeys.size > 0) {
338
+ plotData([...allKeys][0]);
339
+ }
340
+ })
341
+ .catch(error => {
342
+ loadingSpinner.style.display = 'none';
343
+ console.error('Error loading phase data:', error);
344
+
345
+ const dataSection = document.querySelector('.data-section');
346
+ // Hide the section on error as well
347
+ dataSection.style.display = 'none';
348
+
349
+ // Optionally, you could show an error message instead:
350
+ // dataSection.innerHTML = `
351
+ // <div class="alert alert-danger m-3" role="alert">
352
+ // <i class="fas fa-exclamation-triangle me-2"></i>
353
+ // <strong>Error:</strong> Unable to load phase data. ${error.message}
354
+ // </div>
355
+ // `;
356
+ });
357
+ });
358
+ </script>
359
+
360
+ {% endblock %}
@@ -0,0 +1,4 @@
1
+ from .design import design
2
+ # from ivoryos.routes.execute.socket_handlers import socketio
3
+
4
+ __all__ = ['design', ]