ivoryos 1.1.0__py3-none-any.whl → 1.2.0__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.

Files changed (58) hide show
  1. ivoryos/__init__.py +12 -5
  2. ivoryos/routes/api/api.py +5 -58
  3. ivoryos/routes/control/control.py +46 -43
  4. ivoryos/routes/control/control_file.py +4 -4
  5. ivoryos/routes/control/control_new_device.py +10 -10
  6. ivoryos/routes/control/templates/controllers.html +38 -9
  7. ivoryos/routes/control/templates/controllers_new.html +1 -1
  8. ivoryos/routes/data/data.py +81 -60
  9. ivoryos/routes/data/templates/components/step_card.html +9 -3
  10. ivoryos/routes/data/templates/workflow_database.html +10 -4
  11. ivoryos/routes/design/design.py +306 -243
  12. ivoryos/routes/design/design_file.py +42 -31
  13. ivoryos/routes/design/design_step.py +132 -30
  14. ivoryos/routes/design/templates/components/action_form.html +4 -3
  15. ivoryos/routes/design/templates/components/{instrument_panel.html → actions_panel.html} +7 -5
  16. ivoryos/routes/design/templates/components/autofill_toggle.html +8 -12
  17. ivoryos/routes/design/templates/components/canvas.html +5 -14
  18. ivoryos/routes/design/templates/components/canvas_footer.html +5 -1
  19. ivoryos/routes/design/templates/components/canvas_header.html +36 -15
  20. ivoryos/routes/design/templates/components/canvas_main.html +34 -0
  21. ivoryos/routes/design/templates/components/deck_selector.html +8 -10
  22. ivoryos/routes/design/templates/components/edit_action_form.html +16 -7
  23. ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
  24. ivoryos/routes/design/templates/components/modals/drop_modal.html +3 -5
  25. ivoryos/routes/design/templates/components/modals/new_script_modal.html +1 -2
  26. ivoryos/routes/design/templates/components/modals/rename_modal.html +5 -5
  27. ivoryos/routes/design/templates/components/modals/saveas_modal.html +2 -2
  28. ivoryos/routes/design/templates/components/python_code_overlay.html +26 -4
  29. ivoryos/routes/design/templates/components/sidebar.html +12 -13
  30. ivoryos/routes/design/templates/experiment_builder.html +20 -20
  31. ivoryos/routes/execute/execute.py +157 -13
  32. ivoryos/routes/execute/execute_file.py +38 -4
  33. ivoryos/routes/execute/templates/components/tab_bayesian.html +365 -114
  34. ivoryos/routes/execute/templates/components/tab_configuration.html +1 -1
  35. ivoryos/routes/library/library.py +70 -115
  36. ivoryos/routes/library/templates/library.html +27 -19
  37. ivoryos/static/js/action_handlers.js +213 -0
  38. ivoryos/static/js/db_delete.js +23 -0
  39. ivoryos/static/js/script_metadata.js +39 -0
  40. ivoryos/static/js/sortable_design.js +89 -56
  41. ivoryos/static/js/ui_state.js +113 -0
  42. ivoryos/utils/bo_campaign.py +137 -1
  43. ivoryos/utils/db_models.py +14 -5
  44. ivoryos/utils/form.py +4 -9
  45. ivoryos/utils/global_config.py +13 -1
  46. ivoryos/utils/script_runner.py +24 -5
  47. ivoryos/utils/serilize.py +203 -0
  48. ivoryos/utils/task_runner.py +4 -1
  49. ivoryos/version.py +1 -1
  50. {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/METADATA +1 -1
  51. {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/RECORD +54 -51
  52. ivoryos/routes/design/templates/components/action_list.html +0 -15
  53. ivoryos/routes/design/templates/components/operations_panel.html +0 -43
  54. ivoryos/routes/design/templates/components/script_info.html +0 -31
  55. ivoryos/routes/design/templates/components/scripts.html +0 -50
  56. {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/LICENSE +0 -0
  57. {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/WHEEL +0 -0
  58. {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,56 @@
1
- $(document).ready(function () {
2
- let dropTargetId = ""; // Store the ID of the drop target
1
+ // Move triggerModal to global scope
2
+ function triggerModal(formHtml, actionName, actionId, dropTargetId) {
3
+ if (formHtml && formHtml.trim() !== "") {
4
+ var $form = $("<div>").html(formHtml);
5
+
6
+ var $hiddenInput = $("<input>")
7
+ .attr("type", "hidden")
8
+ .attr("name", "drop_target_id")
9
+ .attr("id", "dropTargetInput")
10
+ .val(dropTargetId);
11
+
12
+ $form.find("button[type='submit']").before($hiddenInput);
13
+
14
+ $("#modalFormFields").empty().append($form.children());
15
+
16
+ const $modal = $("#dropModal");
17
+
18
+ setTimeout(() => {
19
+ showModal($modal);
20
+ }, 0);
21
+
22
+ $("#modalDropTarget").text(dropTargetId || "N/A");
23
+ $("#modalFormFields")
24
+ .data("action-id", actionId)
25
+ .data("action-name", actionName)
26
+ .data("drop-target-id", dropTargetId);
27
+ } else {
28
+ console.error("Form HTML is undefined or empty!");
29
+ }
30
+ }
31
+
32
+ function showModal($modal) {
33
+ $modal.modal({
34
+ backdrop: 'static',
35
+ keyboard: true,
36
+ focus: true
37
+ }).modal('show');
38
+ }
3
39
 
40
+ const state = {
41
+ dropTargetId: ""
42
+ };
43
+
44
+ function initializeCanvas() {
4
45
  $("#list ul").sortable({
5
46
  cancel: ".unsortable",
6
47
  opacity: 0.8,
7
48
  cursor: "move",
8
49
  placeholder: "drop-placeholder",
9
50
  update: function () {
10
- var item_order = [];
11
- $("ul.reorder li").each(function () {
12
- item_order.push($(this).attr("id"));
13
- });
51
+ const item_order = $("ul.reorder li").map(function () {
52
+ return this.id;
53
+ }).get();
14
54
  var order_string = "order=" + item_order.join(",");
15
55
 
16
56
  $.ajax({
@@ -19,87 +59,80 @@ $(document).ready(function () {
19
59
  data: order_string,
20
60
  cache: false,
21
61
  success: function (data) {
22
- $("#response").html(data);
23
- $("#response").slideDown("slow");
24
- window.location.href = window.location.href;
62
+ // Update the canvas content with the new HTML
63
+ updateActionCanvas(data)
25
64
  }
65
+ }).fail(function (jqXHR, textStatus, errorThrown) {
66
+ console.error("Failed to update order:", textStatus, errorThrown);
26
67
  });
27
68
  }
28
69
  });
29
70
 
30
71
  // Make Entire Accordion Item Draggable
31
- $(".accordion-item").on("dragstart", function (event) {
32
- let formHtml = $(this).find(".accordion-body").html(); // Get the correct form
33
- event.originalEvent.dataTransfer.setData("form", formHtml || ""); // Store form HTML
72
+ $(".accordion-item").off("dragstart").on("dragstart", function (event) {
73
+ let formHtml = $(this).find(".accordion-body").html();
74
+ event.originalEvent.dataTransfer.setData("form", formHtml || "");
34
75
  event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
35
76
  event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
36
-
37
77
  $(this).addClass("dragging");
38
78
  });
39
79
 
40
-
41
- $("#list ul, .canvas").on("dragover", function (event) {
80
+ $("#list ul, .canvas").off("dragover").on("dragover", function (event) {
42
81
  event.preventDefault();
43
82
  let $target = $(event.target).closest("li");
44
83
 
45
- // If we're over a valid <li> element in the list
46
84
  if ($target.length) {
47
- dropTargetId = $target.attr("id") || ""; // Store the drop target ID
48
-
49
- $(".drop-placeholder").remove(); // Remove existing placeholders
50
- $("<li class='drop-placeholder'></li>").insertBefore($target); // Insert before the target element
85
+ state.dropTargetId = $target.attr("id") || "";
86
+ insertDropPlaceholder($target);
51
87
  } else if (!$("#list ul").children().length && $(this).hasClass("canvas")) {
52
- $(".drop-placeholder").remove(); // Remove any placeholder
53
- // $("#list ul").append("<li class='drop-placeholder'></li>"); // Append placeholder to canvas
88
+ $(".drop-placeholder").remove();
54
89
  } else {
55
- dropTargetId = ""; // Append placeholder to canvas
90
+ state.dropTargetId = "";
56
91
  }
57
92
  });
58
93
 
59
- $("#list ul, .canvas").on("dragleave", function () {
60
- $(".drop-placeholder").remove(); // Remove placeholder on leave
94
+ $("#list ul, .canvas").off("dragleave").on("dragleave", function () {
95
+ $(".drop-placeholder").remove();
61
96
  });
62
97
 
63
- $("#list ul, .canvas").on("drop", function (event) {
98
+ $("#list ul, .canvas").off("drop").on("drop", function (event) {
64
99
  event.preventDefault();
65
-
66
100
  var actionName = event.originalEvent.dataTransfer.getData("action");
67
101
  var actionId = event.originalEvent.dataTransfer.getData("id");
68
- var formHtml = event.originalEvent.dataTransfer.getData("form"); // Retrieve form HTML
102
+ var formHtml = event.originalEvent.dataTransfer.getData("form");
69
103
  let listLength = $("ul.reorder li").length;
70
- dropTargetId = dropTargetId || listLength + 1; // Assign a "last" ID or unique identifier
104
+ state.dropTargetId = state.dropTargetId || listLength + 1;
71
105
  $(".drop-placeholder").remove();
72
- // Trigger the modal with the appropriate action
73
- triggerModal(formHtml, actionName, actionId, dropTargetId);
74
-
106
+ document.activeElement?.blur();
107
+ triggerModal(formHtml, actionName, actionId, state.dropTargetId);
75
108
  });
109
+ }
76
110
 
77
- // Function to trigger the modal (same for both buttons and accordion items)
78
- function triggerModal(formHtml, actionName, actionId, dropTargetId) {
79
- if (formHtml && formHtml.trim() !== "") {
80
- var $form = $("<div>").html(formHtml); // Convert HTML string to jQuery object
111
+ function insertDropPlaceholder($target) {
112
+ $(".drop-placeholder").remove();
113
+ $("<li class='drop-placeholder'></li>").insertBefore($target);
114
+ }
81
115
 
82
- // Create a hidden input for the drop target ID
83
- var $hiddenInput = $("<input>")
84
- .attr("type", "hidden")
85
- .attr("name", "drop_target_id")
86
- .attr("id", "dropTargetInput")
87
- .val(dropTargetId);
116
+ // Add this function to sortable_design.js
117
+ function initializeDragHandlers() {
118
+ $(".accordion-item").off("dragstart").on("dragstart", function (event) {
119
+ let formHtml = $(this).find(".accordion-body form").prop('outerHTML');
88
120
 
89
- // Insert before the submit button
90
- $form.find("button[type='submit']").before($hiddenInput);
121
+ if (!formHtml) {
122
+ console.error("Form not found in accordion-body");
123
+ return false;
124
+ }
91
125
 
92
- $("#modalFormFields").empty().append($form.children());
93
- $("#dropModal").modal("show"); // Show modal
126
+ event.originalEvent.dataTransfer.setData("form", formHtml);
127
+ event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
128
+ event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
94
129
 
95
- // Store and display drop target ID in the modal
96
- $("#modalDropTarget").text(dropTargetId || "N/A");
130
+ $(this).addClass("dragging");
131
+ });
132
+ }
97
133
 
98
- $("#modalFormFields").data("action-id", actionId);
99
- $("#modalFormFields").data("action-name", actionName);
100
- $("#modalFormFields").data("drop-target-id", dropTargetId);
101
- } else {
102
- console.error("Form HTML is undefined or empty!");
103
- }
104
- }
105
- });
134
+ // Make sure it's called in the document ready function
135
+ $(document).ready(function () {
136
+ initializeCanvas();
137
+ initializeDragHandlers(); // Add this line
138
+ });
@@ -0,0 +1,113 @@
1
+ // Toggle visibility of line numbers
2
+ function toggleLineNumbers(save = true) {
3
+ const show = document.getElementById('toggleLineNumbers').checked;
4
+ document.querySelectorAll('.line-number').forEach(el => {
5
+ el.classList.toggle('d-none', !show);
6
+ });
7
+
8
+ if (save) {
9
+ localStorage.setItem('showLineNumbers', show ? 'true' : 'false');
10
+ }
11
+ }
12
+
13
+ // Toggle visibility of Python code overlay
14
+ function toggleCodeOverlay(state = null) {
15
+ const overlay = document.getElementById("pythonCodeOverlay");
16
+ const checkbox = document.getElementById("showPythonCodeSwitch");
17
+
18
+ const isVisible = overlay.classList.contains("show");
19
+ const newState = state !== null ? state : !isVisible;
20
+
21
+ if (newState) {
22
+ overlay.classList.add("show");
23
+ checkbox.checked = true;
24
+ } else {
25
+ overlay.classList.remove("show");
26
+ checkbox.checked = false;
27
+ }
28
+
29
+ // Save state to session via PATCH
30
+ fetch(scriptUIStateUrl, {
31
+ method: "PATCH",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify({ show_code: newState })
34
+ });
35
+ }
36
+
37
+
38
+ function setScriptPhase(stype) {
39
+ fetch(scriptUIStateUrl, {
40
+ method: "PATCH",
41
+ headers: {
42
+ "Content-Type": "application/json",
43
+ },
44
+ body: JSON.stringify({ editing_type: stype })
45
+ })
46
+ .then(res => res.json())
47
+ .then(data => {
48
+ if (data.html) {
49
+ document.getElementById("canvas-wrapper").innerHTML = data.html;
50
+ initializeCanvas(); // Reinitialize the canvas functionality
51
+ document.querySelectorAll('#pythonCodeOverlay pre code').forEach((block) => {
52
+ hljs.highlightElement(block);
53
+ });
54
+ }
55
+ })
56
+ .catch(error => console.error("Failed to update editing type", error));
57
+ }
58
+
59
+
60
+
61
+ function changeDeck(deck) {
62
+ fetch(scriptUIStateUrl, {
63
+ method: "PATCH",
64
+ headers: {
65
+ "Content-Type": "application/json",
66
+ },
67
+ body: JSON.stringify({ deck_name: deck })
68
+ })
69
+ .then(res => res.json())
70
+ .then(data => {
71
+ if (data.html) {
72
+ document.getElementById("sidebar-wrapper").innerHTML = data.html;
73
+ }
74
+ })
75
+ .catch(error => console.error("Failed to change deck", error));
76
+ }
77
+
78
+
79
+
80
+ function toggleAutoFill() {
81
+ const instrumentValue = document.querySelector('.form-check.form-switch').dataset.instrument;
82
+
83
+ fetch(scriptUIStateUrl, {
84
+ method: "PATCH",
85
+ headers: {
86
+ "Content-Type": "application/json",
87
+ },
88
+ body: JSON.stringify({
89
+ autofill: document.getElementById("autoFillCheck").checked,
90
+ instrument: instrumentValue
91
+ })
92
+ })
93
+ .then(res => res.json())
94
+ .then(data => {
95
+ if (data.html) {
96
+ document.getElementById("instrument-panel").innerHTML = data.html;
97
+ }
98
+ })
99
+ }
100
+ // Restore state on page load
101
+ document.addEventListener('DOMContentLoaded', () => {
102
+ const savedState = localStorage.getItem('showLineNumbers');
103
+ const checkbox = document.getElementById('toggleLineNumbers');
104
+
105
+ if (savedState === 'true') {
106
+ checkbox.checked = true;
107
+ }
108
+ if (checkbox) {
109
+ toggleLineNumbers(false); // don't overwrite localStorage on load
110
+ checkbox.addEventListener('change', () => toggleLineNumbers());
111
+ }
112
+
113
+ });
@@ -1,3 +1,5 @@
1
+ from typing import Dict, Any
2
+ import re
1
3
  from ivoryos.utils.utils import install_and_import
2
4
 
3
5
 
@@ -124,4 +126,138 @@ def exisitng_data_gs(data_len):
124
126
  ),
125
127
  ]
126
128
  )
127
- return gs
129
+ return gs
130
+
131
+
132
+ def parse_optimization_form(form_data: Dict[str, str]):
133
+ """
134
+ Parse dynamic form data into structured optimization configuration.
135
+
136
+ Expected form field patterns:
137
+ - Objectives: {name}_min, {name}_weight
138
+ - Parameters: {name}_type, {name}_min, {name}_max, {name}_choices, {name}_value_type
139
+ - Config: step{n}_model, step{n}_num_samples
140
+ """
141
+
142
+ objectives = []
143
+ parameters = []
144
+ config = {}
145
+
146
+ # Track processed field names to avoid duplicates
147
+ processed_objectives = set()
148
+ processed_parameters = set()
149
+
150
+ # Parse objectives
151
+ for field_name, value in form_data.items():
152
+ if field_name.endswith('_min') and value:
153
+ # Extract objective name
154
+ obj_name = field_name.replace('_min', '')
155
+ if obj_name in processed_objectives:
156
+ continue
157
+
158
+ # Check if corresponding weight exists
159
+ weight_field = f"{obj_name}_weight"
160
+ if weight_field in form_data and form_data[weight_field]:
161
+ objectives.append({
162
+ "name": obj_name,
163
+ "minimize": value == "minimize",
164
+ "weight": float(form_data[weight_field])
165
+ })
166
+ else:
167
+ objectives.append({
168
+ "name": obj_name,
169
+ "minimize": value == "minimize",
170
+ })
171
+ processed_objectives.add(obj_name)
172
+
173
+ # Parse parameters
174
+ for field_name, value in form_data.items():
175
+ if field_name.endswith('_type') and value:
176
+ # Extract parameter name
177
+ param_name = field_name.replace('_type', '')
178
+ if param_name in processed_parameters:
179
+ continue
180
+
181
+ parameter = {
182
+ "name": param_name,
183
+ "type": value
184
+ }
185
+
186
+ # Get value type (default to float)
187
+ value_type_field = f"{param_name}_value_type"
188
+ value_type = form_data.get(value_type_field, "float")
189
+ parameter["value_type"] = value_type
190
+
191
+ # Handle different parameter types
192
+ if value == "range":
193
+ min_field = f"{param_name}_min"
194
+ max_field = f"{param_name}_max"
195
+
196
+ if min_field in form_data and max_field in form_data:
197
+ min_val = form_data[min_field]
198
+ max_val = form_data[max_field]
199
+
200
+ if min_val and max_val:
201
+ # Convert based on value_type
202
+ if value_type == "int":
203
+ bounds = [int(min_val), int(max_val)]
204
+ elif value_type == "float":
205
+ bounds = [float(min_val), float(max_val)]
206
+ else: # string
207
+ bounds = [str(min_val), str(max_val)]
208
+
209
+ parameter["bounds"] = bounds
210
+
211
+ elif value == "choice":
212
+ choices_field = f"{param_name}_choices"
213
+ if choices_field in form_data and form_data[choices_field]:
214
+ # Split choices by comma and clean whitespace
215
+ choices = [choice.strip() for choice in form_data[choices_field].split(',')]
216
+
217
+ # Convert choices based on value_type
218
+ if value_type == "int":
219
+ choices = [int(choice) for choice in choices if choice.isdigit()]
220
+ elif value_type == "float":
221
+ choices = [float(choice) for choice in choices if
222
+ choice.replace('.', '').replace('-', '').isdigit()]
223
+ # For string, keep as is
224
+
225
+ parameter["bounds"] = choices
226
+
227
+ elif value == "fixed":
228
+ fixed_field = f"{param_name}_value"
229
+ if fixed_field in form_data and form_data[fixed_field]:
230
+ fixed_val = form_data[fixed_field]
231
+
232
+ # Convert based on value_type
233
+ if value_type == "int":
234
+ parameter["value"] = int(fixed_val)
235
+ elif value_type == "float":
236
+ parameter["value"] = float(fixed_val)
237
+ else:
238
+ parameter["value"] = str(fixed_val)
239
+
240
+ parameters.append(parameter)
241
+ processed_parameters.add(param_name)
242
+
243
+ # Parse configuration steps
244
+ step_pattern = re.compile(r'step(\d+)_(.+)')
245
+ steps = {}
246
+
247
+ for field_name, value in form_data.items():
248
+ match = step_pattern.match(field_name)
249
+ if match and value:
250
+ step_num = int(match.group(1))
251
+ step_attr = match.group(2)
252
+ step_key = f"step_{step_num}"
253
+
254
+ if step_key not in steps:
255
+ steps[step_key] = {}
256
+
257
+ # Convert num_samples to int if it's a number field
258
+ if step_attr == "num_samples":
259
+ steps[step_key][step_attr] = int(value)
260
+ else:
261
+ steps[step_key][step_attr] = value
262
+
263
+ return parameters, objectives, steps
@@ -265,11 +265,20 @@ class Script(db.Model):
265
265
  output_variables: Dict[str, str] = self.get_variables()
266
266
  # print(output_variables)
267
267
  for key, value in kwargs.items():
268
- if type(value) is str and value in output_variables:
269
- var_type = output_variables[value]
270
- kwargs[key] = {value: var_type}
271
- if isinstance(value, str) and value.startswith("#"):
272
- kwargs[key] = f"#{self.validate_function_name(value[1:])}"
268
+ if isinstance(value, str):
269
+ if value in output_variables:
270
+ var_type = output_variables[value]
271
+ kwargs[key] = {value: var_type}
272
+ elif value.startswith("#"):
273
+ kwargs[key] = f"#{self.validate_function_name(value[1:])}"
274
+ else:
275
+ # attempt to convert to numerical or bool value for args with no type hint
276
+ try:
277
+ converted = ast.literal_eval(value)
278
+ if isinstance(converted, (int, float, bool)):
279
+ kwargs[key] = converted
280
+ except (ValueError, SyntaxError):
281
+ pass
273
282
  return kwargs
274
283
 
275
284
  def add_logic_action(self, logic_type: str, statement, insert_position=None):
ivoryos/utils/form.py CHANGED
@@ -214,11 +214,6 @@ class FlexibleEnumField(StringField):
214
214
  f"Invalid choice: '{key}'. Must match one of {list(self.enum_class.__members__.keys())}")
215
215
 
216
216
 
217
- def format_name(name):
218
- """Converts 'example_name' to 'Example Name'."""
219
- name = name.split(".")[-1]
220
- text = ' '.join(word for word in name.split('_'))
221
- return text.capitalize()
222
217
 
223
218
  def parse_annotation(annotation):
224
219
  """
@@ -264,7 +259,7 @@ def create_form_for_method(method, autofill, script=None, design=True):
264
259
  for param in sig.parameters.values():
265
260
  if param.name == 'self':
266
261
  continue
267
- formatted_param_name = format_name(param.name)
262
+ # formatted_param_name = format_name(param.name)
268
263
 
269
264
  default_value = None
270
265
  if autofill:
@@ -277,7 +272,7 @@ def create_form_for_method(method, autofill, script=None, design=True):
277
272
  default_value = param.default
278
273
 
279
274
  field_kwargs = {
280
- "label": formatted_param_name,
275
+ "label": param.name,
281
276
  "default": default_value,
282
277
  "validators": [InputRequired()] if param.default is param.empty else [Optional()],
283
278
  **({"script": script} if (autofill or design) else {})
@@ -396,12 +391,12 @@ def create_form_from_action(action: dict, script=None, design=True):
396
391
  }
397
392
 
398
393
  for name, param_type in arg_types.items():
399
- formatted_param_name = format_name(name)
394
+ # formatted_param_name = format_name(name)
400
395
  value = args.get(name, "")
401
396
  if type(value) is dict:
402
397
  value = next(iter(value))
403
398
  field_kwargs = {
404
- "label": formatted_param_name,
399
+ "label": name,
405
400
  "default": f'{value}',
406
401
  "validators": [InputRequired()],
407
402
  **({"script": script})
@@ -15,6 +15,7 @@ class GlobalConfig:
15
15
  cls._instance._deck_snapshot = {}
16
16
  cls._instance._runner_lock = threading.Lock()
17
17
  cls._instance._runner_status = None
18
+ cls._instance._optimizers = {}
18
19
  return cls._instance
19
20
 
20
21
  @property
@@ -84,4 +85,15 @@ class GlobalConfig:
84
85
 
85
86
  @runner_status.setter
86
87
  def runner_status(self, value):
87
- self._runner_status = value
88
+ self._runner_status = value
89
+
90
+ @property
91
+ def optimizers(self):
92
+ return self._optimizers
93
+
94
+ @optimizers.setter
95
+ def optimizers(self, value):
96
+ if isinstance(value, dict):
97
+ self._optimizers = value
98
+ else:
99
+ raise ValueError("Optimizers must be a dictionary.")
@@ -62,7 +62,7 @@ class ScriptRunner:
62
62
 
63
63
 
64
64
  def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
65
- output_path="", compiled=False, current_app=None, history=None):
65
+ output_path="", compiled=False, current_app=None, history=None, optimizer=None):
66
66
  global deck
67
67
  if deck is None:
68
68
  deck = global_config.deck
@@ -82,7 +82,7 @@ class ScriptRunner:
82
82
  thread = threading.Thread(
83
83
  target=self._run_with_stop_check,
84
84
  args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path, current_app, compiled,
85
- history)
85
+ history, optimizer)
86
86
  )
87
87
  thread.start()
88
88
  return thread
@@ -183,7 +183,7 @@ class ScriptRunner:
183
183
  return exec_locals # Return the 'results' variable
184
184
 
185
185
  def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
186
- output_path, current_app, compiled, history=None):
186
+ output_path, current_app, compiled, history=None, optimizer=None):
187
187
  time.sleep(1)
188
188
  # _func_str = script.compile()
189
189
  # step_list_dict: dict = script.convert_to_lines(_func_str)
@@ -207,7 +207,7 @@ class ScriptRunner:
207
207
  if repeat_count:
208
208
  self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
209
209
  run_name, return_list, compiled, logger, socketio,
210
- history, output_path, run_id=run_id)
210
+ history, output_path, run_id=run_id, optimizer=optimizer)
211
211
  elif config:
212
212
  self._run_config_section(config, arg_type, output_list, script, run_name, logger,
213
213
  socketio, run_id=run_id, compiled=compiled)
@@ -264,7 +264,7 @@ class ScriptRunner:
264
264
  output_list.append(output)
265
265
 
266
266
  def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list, compiled,
267
- logger, socketio, history, output_path, run_id):
267
+ logger, socketio, history, output_path, run_id, optimizer=None):
268
268
  if bo_args:
269
269
  logger.info('Initializing optimizer...')
270
270
  if compiled:
@@ -283,6 +283,14 @@ class ScriptRunner:
283
283
  output_list.append(row)
284
284
  else:
285
285
  ax_client = bo_campaign.ax_init_form(bo_args, arg_types)
286
+ elif optimizer and history:
287
+ import pandas as pd
288
+ file_path = os.path.join(output_path, history)
289
+
290
+ previous_runs = pd.read_csv(file_path)
291
+ optimizer.append_existing_data(previous_runs)
292
+ for row in previous_runs:
293
+ output_list.append(row)
286
294
 
287
295
 
288
296
 
@@ -307,6 +315,17 @@ class ScriptRunner:
307
315
  except Exception as e:
308
316
  logger.info(f'Optimization error: {e}')
309
317
  break
318
+ elif optimizer:
319
+ try:
320
+ parameters = optimizer.suggest(1)
321
+ logger.info(f'Output value: {parameters}')
322
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress, **parameters)
323
+ if output:
324
+ optimizer.observe(output)
325
+ output.update(parameters)
326
+ except Exception as e:
327
+ logger.info(f'Optimization error: {e}')
328
+ break
310
329
  else:
311
330
  # fname = f"{run_name}_script"
312
331
  # function = self.globals_dict[fname]