ivoryos 0.1.20__py3-none-any.whl → 0.1.21__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.
@@ -27,6 +27,57 @@ document.addEventListener("DOMContentLoaded", function() {
27
27
  }, 1000); // Small delay to let users see the completion
28
28
  }
29
29
  });
30
+
31
+ socket.on('error', function(errorData) {
32
+ console.error("Error received:", errorData);
33
+ var progressBar = document.getElementById('progress-bar-inner');
34
+
35
+ progressBar.classList.remove('bg-success');
36
+ progressBar.classList.add('bg-danger'); // Red color for error
37
+ // Show error modal
38
+ var errorModal = new bootstrap.Modal(document.getElementById('error-modal'));
39
+ document.getElementById('error-message').innerText = "An error occurred: " + errorData.message;
40
+ errorModal.show();
41
+
42
+ });
43
+
44
+ // Handle Pause/Resume Button
45
+ document.getElementById('pause-resume').addEventListener('click', function() {
46
+ socket.emit('pause');
47
+ console.log('Pause/Resume is toggled.');
48
+ var button = this;
49
+ var icon = button.querySelector("i");
50
+
51
+ // Toggle Pause and Resume
52
+ if (icon.classList.contains("bi-pause-circle")) {
53
+ icon.classList.remove("bi-pause-circle");
54
+ icon.classList.add("bi-play-circle");
55
+ button.innerHTML = '<i class="bi bi-play-circle"></i>';
56
+ button.setAttribute("title", "Resume execution");
57
+ } else {
58
+ icon.classList.remove("bi-play-circle");
59
+ icon.classList.add("bi-pause-circle");
60
+ button.innerHTML = '<i class="bi bi-pause-circle"></i>';
61
+ button.setAttribute("title", "Pause execution");
62
+ }
63
+ });
64
+
65
+ // Handle Modal Buttons
66
+ document.getElementById('continue-btn').addEventListener('click', function() {
67
+ socket.emit('pause'); // Resume execution
68
+ console.log("Execution resumed.");
69
+ });
70
+
71
+ document.getElementById('stop-btn').addEventListener('click', function() {
72
+ socket.emit('pause'); // Resume execution
73
+ socket.emit('abort_current'); // Stop execution
74
+ console.log("Execution stopped.");
75
+
76
+ // Reset UI back to initial state
77
+ document.getElementById("code-panel").style.display = "none";
78
+ document.getElementById("run-panel").style.display = "block";
79
+ });
80
+
30
81
  socket.on('log', function(data) {
31
82
  var logMessage = data.message;
32
83
  console.log(logMessage);
@@ -49,24 +100,16 @@ document.addEventListener("DOMContentLoaded", function() {
49
100
  }
50
101
  });
51
102
 
52
- document.getElementById('pause-resume').addEventListener('click', function() {
103
+ socket.on('execution', function(data) {
104
+ // Remove highlighting from all lines
105
+ document.querySelectorAll('pre code').forEach(el => el.style.backgroundColor = '');
53
106
 
54
- socket.emit('pause');
55
- console.log('Pause/Resume is toggled.');
56
- var button = this;
57
- var icon = button.querySelector("i");
107
+ // Get the currently executing line and highlight it
108
+ let executingLine = document.getElementById(data.section);
109
+ if (executingLine) {
110
+ executingLine.style.backgroundColor = '#cce5ff'; // Highlight
111
+ executingLine.style.transition = 'background-color 0.3s ease-in-out';
58
112
 
59
- // Toggle between Pause and Resume
60
- if (icon.classList.contains("bi-pause-circle")) {
61
- icon.classList.remove("bi-pause-circle");
62
- icon.classList.add("bi-play-circle");
63
- button.innerHTML = '<i class="bi bi-play-circle"></i>';
64
- button.setAttribute("title", "Resume execution");
65
- } else {
66
- icon.classList.remove("bi-play-circle");
67
- icon.classList.add("bi-pause-circle");
68
- button.innerHTML = '<i class="bi bi-pause-circle"></i>';
69
- button.setAttribute("title", "Pause execution");
70
113
  }
71
114
  });
72
115
  });
@@ -1,36 +1,105 @@
1
- $(document).ready(function(){
2
- function slideout(){
3
- setTimeout(function(){
4
- $("#response").slideUp("slow", function () {});
5
- }, 2000);
6
- }
1
+ $(document).ready(function () {
2
+ let dropTargetId = ""; // Store the ID of the drop target
3
+
4
+ $("#list ul").sortable({
5
+ cancel: ".unsortable",
6
+ opacity: 0.8,
7
+ cursor: "move",
8
+ placeholder: "drop-placeholder",
9
+ update: function () {
10
+ var item_order = [];
11
+ $("ul.reorder li").each(function () {
12
+ item_order.push($(this).attr("id"));
13
+ });
14
+ var order_string = "order=" + item_order.join(",");
15
+
16
+ $.ajax({
17
+ method: "POST",
18
+ url: updateListUrl,
19
+ data: order_string,
20
+ cache: false,
21
+ success: function (data) {
22
+ $("#response").html(data);
23
+ $("#response").slideDown("slow");
24
+ slideout();
25
+ }
26
+ });
27
+ }
28
+ });
29
+
30
+ // 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
34
+ event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
35
+ event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
36
+
37
+ $(this).addClass("dragging");
38
+ });
39
+
40
+
41
+ $("#list ul, .canvas").on("dragover", function (event) {
42
+ event.preventDefault();
43
+ let $target = $(event.target).closest("li");
44
+
45
+ // If we're over a valid <li> element in the list
46
+ 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
51
+ } 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
54
+ } else {
55
+ dropTargetId = ""; // Append placeholder to canvas
56
+ }
57
+ });
7
58
 
8
- $("#response").hide();
9
-
10
- $(function() {
11
- $("#list ul").sortable({
12
- cancel: ".unsortable",
13
- opacity: 0.8,
14
- cursor: 'move',
15
- update: function() {
16
- var item_order = [];
17
- $('ul.reorder li').each(function() {
18
- item_order.push($(this).attr("id"));
19
- });
20
- var order_string = 'order=' + item_order.join(',');
21
-
22
- $.ajax({
23
- method: "POST",
24
- url: updateListUrl, // updateListUrl should be set dynamically in your HTML template
25
- data: order_string,
26
- cache: false,
27
- success: function(data){
28
- $("#response").html(data);
29
- $("#response").slideDown('slow');
30
- slideout();
31
- }
32
- });
33
- }
34
- });
59
+ $("#list ul, .canvas").on("dragleave", function () {
60
+ $(".drop-placeholder").remove(); // Remove placeholder on leave
35
61
  });
62
+
63
+ $("#list ul, .canvas").on("drop", function (event) {
64
+ event.preventDefault();
65
+
66
+ var actionName = event.originalEvent.dataTransfer.getData("action");
67
+ var actionId = event.originalEvent.dataTransfer.getData("id");
68
+ var formHtml = event.originalEvent.dataTransfer.getData("form"); // Retrieve form HTML
69
+ let listLength = $("ul.reorder li").length;
70
+ dropTargetId = dropTargetId || listLength + 1; // Assign a "last" ID or unique identifier
71
+ $(".drop-placeholder").remove();
72
+ // Trigger the modal with the appropriate action
73
+ triggerModal(formHtml, actionName, actionId, dropTargetId);
74
+
75
+ });
76
+
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
81
+
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);
88
+
89
+ // Insert before the submit button
90
+ $form.find("button[type='submit']").before($hiddenInput);
91
+
92
+ $("#modalFormFields").empty().append($form.children());
93
+ $("#dropModal").modal("show"); // Show modal
94
+
95
+ // Store and display drop target ID in the modal
96
+ $("#modalDropTarget").text(dropTargetId || "N/A");
97
+
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
+ }
36
105
  });
ivoryos/static/style.css CHANGED
@@ -199,4 +199,13 @@ hr.vertical {
199
199
  color: white;
200
200
  font-size: 24px;
201
201
  padding-top: 20%;
202
+ }
203
+ .drop-placeholder {
204
+ height: 2px !important; /* Keep it very thin */
205
+ min-height: 2px !important;
206
+ margin: 0 !important;
207
+ padding: 0 !important;
208
+ background: rgba(0, 0, 0, 0.2); /* Slight visibility */
209
+ border-radius: 2px;
210
+ list-style: none; /* Remove any default list styling */
202
211
  }
@@ -54,9 +54,9 @@
54
54
  <li class="nav-item">
55
55
  <a class="nav-link" href="{{ url_for('control.controllers_home') }}">Temp Devices</a></li>
56
56
  </li>
57
- <li class="nav-item">
58
- <a class="nav-link" href="{{ url_for('main.help_info') }}">About</a>
59
- </li>
57
+ {# <li class="nav-item">#}
58
+ {# <a class="nav-link" href="{{ url_for('main.help_info') }}">About</a>#}
59
+ {# </li>#}
60
60
  {% if plugins %}
61
61
  {% for plugin in plugins %}
62
62
  <li class="nav-item">
@@ -196,28 +196,37 @@ class Script(db.Model):
196
196
  for i in self.stypes:
197
197
  self._sort(i)
198
198
 
199
- def add_action(self, action: dict):
199
+ def add_action(self, action: dict, insert_position=None):
200
200
  current_len = len(self.currently_editing_script)
201
201
  action_to_add = action.copy()
202
202
  action_to_add['id'] = current_len + 1
203
203
  action_to_add['uuid'] = uuid.uuid4().fields[-1]
204
204
  self.currently_editing_script.append(action_to_add)
205
- self.currently_editing_order.append(str(current_len + 1))
205
+ self._insert_action(insert_position, current_len)
206
206
  self.update_time_stamp()
207
207
 
208
- def add_variable(self, statement, variable, type):
208
+ def add_variable(self, statement, variable, type, insert_position=None):
209
209
  variable = self.validate_function_name(variable)
210
210
  convert_type = getattr(builtins, type)
211
211
  statement = convert_type(statement)
212
212
  current_len = len(self.currently_editing_script)
213
213
  uid = uuid.uuid4().fields[-1]
214
- action_list = [{"id": current_len + 1, "instrument": 'variable', "action": variable,
214
+ action = {"id": current_len + 1, "instrument": 'variable', "action": variable,
215
215
  "args": {"statement": 'None' if statement == '' else statement}, "return": '', "uuid": uid,
216
- "arg_types": {"statement": type}}]
217
- self.currently_editing_script.extend(action_list)
218
- self.currently_editing_order.extend([str(current_len + i + 1) for i in range(len(action_list))])
216
+ "arg_types": {"statement": type}}
217
+ self.currently_editing_script.append(action)
218
+ self._insert_action(insert_position, current_len)
219
219
  self.update_time_stamp()
220
220
 
221
+ def _insert_action(self, insert_position, current_len, action_len:int=1):
222
+
223
+ if insert_position is None:
224
+ self.currently_editing_order.extend([str(current_len + i + 1) for i in range(action_len)])
225
+ else:
226
+ index = int(insert_position) - 1
227
+ self.currently_editing_order[index:index] = [str(current_len + i + 1) for i in range(action_len)]
228
+ self.sort_actions()
229
+
221
230
  def get_added_variables(self):
222
231
  added_variables: Dict[str, str] = {action["action"]: action["arg_types"]["statement"] for action in
223
232
  self.currently_editing_script if action["instrument"] == "variable"}
@@ -251,7 +260,7 @@ class Script(db.Model):
251
260
  kwargs[key] = f"#{self.validate_function_name(value[1:])}"
252
261
  return kwargs
253
262
 
254
- def add_logic_action(self, logic_type: str, statement):
263
+ def add_logic_action(self, logic_type: str, statement, insert_position=None):
255
264
  current_len = len(self.currently_editing_script)
256
265
  uid = uuid.uuid4().fields[-1]
257
266
  logic_dict = {
@@ -291,7 +300,7 @@ class Script(db.Model):
291
300
  }
292
301
  action_list = logic_dict[logic_type]
293
302
  self.currently_editing_script.extend(action_list)
294
- self.currently_editing_order.extend([str(current_len + i + 1) for i in range(len(action_list))])
303
+ self._insert_action(insert_position, current_len, len(action_list))
295
304
  self.update_time_stamp()
296
305
 
297
306
  def delete_action(self, id: int):
@@ -446,7 +455,8 @@ class Script(db.Model):
446
455
  configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
447
456
  config_type.items()]
448
457
 
449
- function_header = f"def {run_name}_{stype}("
458
+ script_type = f"_{stype}" if stype != "script" else ""
459
+ function_header = f"def {run_name}{script_type}("
450
460
 
451
461
  if stype == "script":
452
462
  function_header += ", ".join(configure)
@@ -492,6 +502,9 @@ class Script(db.Model):
492
502
  return f"{self.indent(indent_unit)}time.sleep({statement})", indent_unit
493
503
  elif instrument == 'repeat':
494
504
  return self._process_repeat(indent_unit, action_name, statement, next_action)
505
+ #todo
506
+ # elif instrument == 'registered_workflows':
507
+ # return inspect.getsource(my_function)
495
508
  else:
496
509
  return self._process_instrument_action(indent_unit, instrument, action_name, args, save_data)
497
510
 
ivoryos/utils/form.py CHANGED
@@ -7,6 +7,10 @@ from flask_wtf import FlaskForm
7
7
  from wtforms import StringField, FloatField, HiddenField, BooleanField, IntegerField
8
8
  import inspect
9
9
 
10
+ from ivoryos.utils.db_models import Script
11
+ from ivoryos.utils.global_config import GlobalConfig
12
+
13
+ global_config = GlobalConfig()
10
14
 
11
15
  def find_variable(data, script):
12
16
  """
@@ -345,6 +349,13 @@ def create_form_from_action(action: dict, script=None, design=True):
345
349
  setattr(DynamicForm, 'return', return_value)
346
350
  return DynamicForm()
347
351
 
352
+ def create_all_builtin_forms(script):
353
+ all_builtin_forms = {}
354
+ for logic_name in ['if', 'while', 'variable', 'wait', 'repeat']:
355
+ # signature = info.get('signature', {})
356
+ form_class = create_builtin_form(logic_name, script)
357
+ all_builtin_forms[logic_name] = form_class()
358
+ return all_builtin_forms
348
359
 
349
360
  def create_builtin_form(logic_type, script):
350
361
  """
@@ -379,14 +390,51 @@ def create_builtin_form(logic_type, script):
379
390
  render_kw=render_kwargs)
380
391
  type_field = SelectField(
381
392
  'Select Input Type',
382
- choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String')],
393
+ choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String'), ('bool', 'Boolean')],
383
394
  default='str' # Optional default value
384
395
  )
385
396
  setattr(BuiltinFunctionForm, "variable", variable_field)
386
397
  setattr(BuiltinFunctionForm, "type", type_field)
387
398
  hidden_field = HiddenField(name=f'builtin_name', render_kw={"value": f'{logic_type}'})
388
399
  setattr(BuiltinFunctionForm, "builtin_name", hidden_field)
389
- return BuiltinFunctionForm()
400
+ return BuiltinFunctionForm
401
+
402
+
403
+ def get_method_from_workflow(function_string):
404
+ """Creates a function from a string and assigns it a new name."""
405
+
406
+ namespace = {}
407
+ exec(function_string, globals(), namespace) # Execute the string in a safe namespace
408
+ func_name = next(iter(namespace))
409
+ # Get the function name dynamically
410
+ return namespace[func_name]
411
+
412
+
413
+ def create_workflow_forms(script, autofill: bool = False, design: bool = False):
414
+ workflow_forms = {}
415
+ functions = {}
416
+ class RegisteredWorkflows:
417
+ pass
418
+
419
+ deck_name = script.deck
420
+ workflows = Script.query.filter(Script.deck==deck_name, Script.name != script.name).all()
421
+ for workflow in workflows:
422
+ compiled_strs = workflow.compile().get('script', "")
423
+ method = get_method_from_workflow(compiled_strs)
424
+ functions[workflow.name] = dict(signature=inspect.signature(method), docstring=inspect.getdoc(method))
425
+ setattr(RegisteredWorkflows, workflow.name, method)
426
+
427
+ form_class = create_form_for_method(method, autofill, script, design)
428
+
429
+ hidden_method_name = HiddenField(name=f'hidden_name', description="",
430
+ render_kw={"value": f'{workflow.name}'})
431
+ if design:
432
+ return_value = StringField(label='Save value as', render_kw={"placeholder": "Optional"})
433
+ setattr(form_class, 'return', return_value)
434
+ setattr(form_class, 'workflow_name', hidden_method_name)
435
+ workflow_forms[workflow.name] = form_class()
436
+ global_config.registered_workflows = RegisteredWorkflows
437
+ return workflow_forms, functions
390
438
 
391
439
 
392
440
  def create_action_button(script, stype=None):
@@ -8,6 +8,7 @@ class GlobalConfig:
8
8
  if cls._instance is None:
9
9
  cls._instance = super(GlobalConfig, cls).__new__(cls, *args, **kwargs)
10
10
  cls._instance._deck = None
11
+ cls._instance._registered_workflows = None
11
12
  cls._instance._agent = None
12
13
  cls._instance._defined_variables = {}
13
14
  cls._instance._api_variables = set()
@@ -24,6 +25,15 @@ class GlobalConfig:
24
25
  if self._deck is None:
25
26
  self._deck = value
26
27
 
28
+ @property
29
+ def registered_workflows(self):
30
+ return self._registered_workflows
31
+
32
+ @registered_workflows.setter
33
+ def registered_workflows(self, value):
34
+ if self._registered_workflows is None:
35
+ self._registered_workflows = value
36
+
27
37
 
28
38
  @property
29
39
  def deck_snapshot(self):
@@ -24,9 +24,11 @@ class ScriptRunner:
24
24
  self.stop_current_event = threading.Event()
25
25
  self.is_running = False
26
26
  self.lock = threading.Lock()
27
+ self.paused = False
27
28
 
28
29
  def toggle_pause(self):
29
30
  """Toggles between pausing and resuming the script"""
31
+ self.paused = not self.paused
30
32
  if self.pause_event.is_set():
31
33
  self.pause_event.clear() # Pause the script
32
34
  return "Paused"
@@ -34,10 +36,15 @@ class ScriptRunner:
34
36
  self.pause_event.set() # Resume the script
35
37
  return "Resumed"
36
38
 
39
+ def pause_status(self):
40
+ """Toggles between pausing and resuming the script"""
41
+ return self.paused
42
+
37
43
  def reset_stop_event(self):
38
44
  """Resets the stop event"""
39
45
  self.stop_pending_event.clear()
40
46
  self.stop_current_event.clear()
47
+ self.pause_event.set()
41
48
 
42
49
  def abort_pending(self):
43
50
  """Abort the pending iteration after the current is finished"""
@@ -68,7 +75,7 @@ class ScriptRunner:
68
75
  thread.start()
69
76
  return thread
70
77
 
71
- def execute_function_line_by_line(self, lines:list, section_name, logger, socketio, **kwargs):
78
+ def exex_steps(self, script, section_name, logger, socketio, **kwargs):
72
79
  """
73
80
  Executes a function defined in a string line by line.
74
81
 
@@ -76,73 +83,89 @@ class ScriptRunner:
76
83
  :param kwargs: Arguments to pass to the function
77
84
  :return: The final result of the function execution
78
85
  """
86
+ _func_str = script.compile()
87
+ step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
79
88
  global deck
80
89
  if deck is None:
81
90
  deck = global_config.deck
82
91
  # func_str = script.compile()
83
92
  # Parse function body from string
84
-
93
+ temp_connections = global_config.defined_variables
85
94
  # Prepare execution environment
86
- exec_globals = {"deck": deck} # Add required global objects
95
+ exec_globals = {"deck": deck, "time":time} # Add required global objects
96
+ # exec_globals = {"deck": deck, "time": time, "registered_workflows":registered_workflows} # Add required global objects
97
+ exec_globals.update(temp_connections)
87
98
  exec_locals = {} # Local execution scope
88
99
 
89
100
  # Define function arguments manually in exec_locals
90
101
  exec_locals.update(kwargs)
102
+ index = 0
91
103
 
92
104
  # Execute each line dynamically
93
- for index, line in enumerate(lines):
105
+ while index < len(step_list):
94
106
  if self.stop_current_event.is_set():
95
107
  logger.info(f'Stopping execution during {section_name}')
96
108
  break
109
+ line = step_list[index]
97
110
  logger.info(f"Executing: {line}") # Debugging output
98
111
  socketio.emit('execution', {'section': f"{section_name}-{index}"})
99
112
  # self._emit_progress(socketio, 100)
100
- exec(line, exec_globals, exec_locals)
113
+ try:
114
+ exec(line, exec_globals, exec_locals)
115
+ except Exception as e:
116
+ logger.error(f"Error during script execution: {e}")
117
+ socketio.emit('error', {'message': e.__str__()})
118
+ self.toggle_pause()
101
119
  self.pause_event.wait()
102
120
 
121
+ # todo update script during the run
122
+ # _func_str = script.compile()
123
+ # step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
124
+ index += 1
103
125
  return exec_locals # Return the 'results' variable
104
126
 
105
127
  def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
106
128
  output_path):
107
129
  time.sleep(1)
108
- func_str = script.compile()
109
- func_str_lines = script.convert_to_lines(func_str)
130
+ # _func_str = script.compile()
131
+ # step_list_dict: dict = script.convert_to_lines(_func_str)
110
132
  self._emit_progress(socketio, 1)
111
- try:
112
- # Run "prep" section once
113
- script_dict = script.script_dict
114
- self._run_actions(script_dict.get("prep", []), func_str_lines.get("prep", []), section_name="prep", logger=logger, socketio=socketio)
115
- output_list = []
116
- _, arg_type = script.config("script")
117
- _, return_list = script.config_return()
118
- # Run "script" section multiple times
119
- if repeat_count:
120
- self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, func_str_lines.get("script", []),
121
- run_name, return_list, logger, socketio)
122
- elif config:
123
- self._run_config_section(config, arg_type, output_list, func_str_lines.get("script", []), run_name, logger,
124
- socketio)
125
- # Run "cleanup" section once
126
- self._run_actions(script_dict.get("cleanup", []), func_str.get("cleanup", []), section_name="cleanup",
127
- logger=logger, socketio=socketio)
128
- # Save results if necessary
129
- if output_list:
130
- self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
131
- except Exception as e:
132
- logger.error(f"Error during script execution: {e}")
133
- finally:
134
- with self.lock:
135
- self.is_running = False # Reset the running flag when done
136
- self._emit_progress(socketio, 100)
137
-
138
- def _run_actions(self, actions, func_str, section_name="", logger=None, socketio=None):
139
- logger.info(f'Executing {section_name} steps') if actions else logger.info(f'No {section_name} steps')
133
+
134
+ # Run "prep" section once
135
+ script_dict = script.script_dict
136
+ self._run_actions(script, section_name="prep", logger=logger, socketio=socketio)
137
+ output_list = []
138
+ _, arg_type = script.config("script")
139
+ _, return_list = script.config_return()
140
+
141
+ # Run "script" section multiple times
142
+ if repeat_count:
143
+ self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
144
+ run_name, return_list, logger, socketio)
145
+ elif config:
146
+ self._run_config_section(config, arg_type, output_list, script, run_name, logger,
147
+ socketio)
148
+
149
+ # Run "cleanup" section once
150
+ self._run_actions(script, section_name="cleanup", logger=logger, socketio=socketio)
151
+ # Reset the running flag when done
152
+ with self.lock:
153
+ self.is_running = False
154
+ # Save results if necessary
155
+ if output_list:
156
+ self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
157
+ self._emit_progress(socketio, 100)
158
+
159
+ def _run_actions(self, script, section_name="", logger=None, socketio=None):
160
+ _func_str = script.compile()
161
+ step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
162
+ logger.info(f'Executing {section_name} steps') if step_list else logger.info(f'No {section_name} steps')
140
163
  if self.stop_pending_event.is_set():
141
164
  logger.info(f"Stopping execution during {section_name} section.")
142
165
  return
143
- self.execute_function_line_by_line(func_str, section_name, logger, socketio)
166
+ self.exex_steps(script, section_name, logger, socketio)
144
167
 
145
- def _run_config_section(self, config, arg_type, output_list, func_str, run_name, logger, socketio):
168
+ def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio):
146
169
  compiled = True
147
170
  for i in config:
148
171
  try:
@@ -162,12 +185,12 @@ class ScriptRunner:
162
185
  self._emit_progress(socketio, progress)
163
186
  # fname = f"{run_name}_script"
164
187
  # function = self.globals_dict[fname]
165
- output = self.execute_function_line_by_line(func_str, "script", logger, socketio, **kwargs)
188
+ output = self.exex_steps(script, "script", logger, socketio, **kwargs)
166
189
  if output:
167
190
  # kwargs.update(output)
168
191
  output_list.append(output)
169
192
 
170
- def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, func_str, run_name, return_list,
193
+ def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list,
171
194
  logger, socketio):
172
195
  if bo_args:
173
196
  logger.info('Initializing optimizer...')
@@ -185,7 +208,7 @@ class ScriptRunner:
185
208
  logger.info(f'Output value: {parameters}')
186
209
  # fname = f"{run_name}_script"
187
210
  # function = self.globals_dict[fname]
188
- output = self.execute_function_line_by_line(func_str, "script", logger, socketio, **parameters)
211
+ output = self.exex_steps(script, "script", logger, socketio, **parameters)
189
212
 
190
213
  _output = {key: value for key, value in output.items() if key in return_list}
191
214
  ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
@@ -196,7 +219,7 @@ class ScriptRunner:
196
219
  else:
197
220
  # fname = f"{run_name}_script"
198
221
  # function = self.globals_dict[fname]
199
- output = self.execute_function_line_by_line(func_str, "script", logger, socketio)
222
+ output = self.exex_steps(script, "script", logger, socketio)
200
223
 
201
224
  if output:
202
225
  output_list.append(output)
ivoryos/utils/utils.py CHANGED
@@ -97,7 +97,7 @@ def _inspect_class(class_object=None, debug=False):
97
97
  under_score = "_"
98
98
  if debug:
99
99
  under_score = "__"
100
- for function, method in inspect.getmembers(type(class_object), predicate=inspect.isfunction):
100
+ for function, method in inspect.getmembers(type(class_object), predicate=callable):
101
101
  if not function.startswith(under_score) and not function.isupper():
102
102
  try:
103
103
  annotation = inspect.signature(method)
ivoryos/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.20"
1
+ __version__ = "0.1.21"