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

@@ -1,70 +1,103 @@
1
1
  {% extends 'base.html' %}
2
- {% block title %}IvoryOS | Welcome {% endblock %}
3
-
2
+ {% block title %}IvoryOS | Welcome{% endblock %}
4
3
 
5
4
  {% block body %}
6
- <div class="p-4" >
7
- <h1 style=" font-size: 3rem;font-weight: bold;color: #343a40;margin-bottom: 40px;">
5
+ <div class="p-4">
6
+ <h1 class="mb-4" style="font-size: 3rem; font-weight: bold; color: #343a40;">
8
7
  Welcome
9
8
  </h1>
10
- <p>Version: {{ version }}</p>
9
+ <p class="mb-5">Version: {{ version }}</p>
10
+
11
+ {% if enable_design %}
12
+ <!-- Workflow Design Section -->
13
+ <h4 class="mb-3">Workflow Design</h4>
11
14
  <div class="row">
12
- {% if enable_design %}
13
- <div class="col-lg-6 mb-4 d-flex align-items-stretch">
14
- <div class="card rounded shadow-sm flex-fill">
15
- <div class="card-body">
16
- <h5 class="card-title">Browse designs</h5>
17
- <p class="card-text">Browse all workflows saved in the database.</p>
18
- <a href="{{ url_for('database.load_from_database') }}" class="stretched-link"></a>
19
- </div>
15
+ <div class="col-lg-6 mb-3 d-flex align-items-stretch">
16
+ <div class="card rounded shadow-sm flex-fill">
17
+ <div class="card-body">
18
+ <h5 class="card-title">
19
+ <i class="bi bi-folder2-open me-2"></i>Browse designs
20
+ </h5>
21
+ <p class="card-text">View all saved workflows from the database.</p>
22
+ <a href="{{ url_for('database.load_from_database') }}" class="stretched-link"></a>
20
23
  </div>
21
24
  </div>
22
- <div class="col-lg-6 mb-4 d-flex align-items-stretch">
23
- <div class="card rounded shadow-sm flex-fill">
24
- <div class="card-body">
25
- <h5 class="card-title">Edit designs</h5>
26
- <p class="card-text">Build your workflow from current deck functions.</p>
27
- <a href="{{ url_for('design.experiment_builder') }}" class="stretched-link"></a>
28
- </div>
25
+ </div>
26
+ <div class="col-lg-6 mb-3 d-flex align-items-stretch">
27
+ <div class="card rounded shadow-sm flex-fill">
28
+ <div class="card-body">
29
+ <h5 class="card-title">
30
+ <i class="bi bi-pencil-square me-2"></i>Edit designs
31
+ </h5>
32
+ <p class="card-text">Create or modify workflows using available functions.</p>
33
+ <a href="{{ url_for('design.experiment_builder') }}" class="stretched-link"></a>
29
34
  </div>
30
35
  </div>
31
- {% endif %}
36
+ </div>
32
37
  </div>
38
+ {% endif %}
33
39
 
34
- <br><br><br>
35
- {% if not off_line %}
36
- <div class="row">
37
- {% if enable_design %}
38
- <div class="col-lg-6 mb-4 d-flex align-items-stretch">
39
- <div class="card rounded shadow-sm flex-fill">
40
- <div class="card-body">
41
- <h5 class="card-title">Run experiment</h5>
42
- <p class="card-text">Execute workflows with repeat times or variable configs. </p>
43
- <a href="{{ url_for('design.experiment_run') }}" class="stretched-link"></a>
44
- </div>
40
+ <!-- Workflow Control and Monitor Section -->
41
+ <h4 class="mt-5 mb-3">Workflow Control & Monitoring</h4>
42
+ <div class="row">
43
+ <!-- Always visible: Experiment data -->
44
+ <div class="col-lg-6 mb-3 d-flex align-items-stretch">
45
+ <div class="card rounded shadow-sm flex-fill">
46
+ <div class="card-body">
47
+ <h5 class="card-title">
48
+ <i class="bi bi-graph-up-arrow me-2"></i>Experiment data
49
+ </h5>
50
+ <p class="card-text">Browse workflow logs and output data.</p>
51
+ <a href="{{ url_for('database.list_workflows') }}" class="stretched-link"></a>
45
52
  </div>
46
53
  </div>
47
- {% endif %}
54
+ </div>
48
55
 
49
- <div class="col-lg-6 mb-4 d-flex align-items-stretch">
50
- <div class="card rounded shadow-sm flex-fill">
51
- <div class="card-body">
52
- <h5 class="card-title">Control device</h5>
53
- <p class="card-text">Browse and control instruments of current platform</p>
54
- <a href="{{ url_for('control.deck_controllers') }}" class="stretched-link"></a>
55
- </div>
56
+ <!-- Conditionally visible: Run current workflow -->
57
+ {% if not off_line %}
58
+ <div class="col-lg-6 mb-3 d-flex align-items-stretch">
59
+ <div class="card rounded shadow-sm flex-fill">
60
+ <div class="card-body">
61
+ <h5 class="card-title">
62
+ <i class="bi bi-play-circle me-2"></i>Run current workflow
63
+ </h5>
64
+ <p class="card-text">Execute workflows with configurable parameters.</p>
65
+ <a href="{{ url_for('design.experiment_run') }}" class="stretched-link"></a>
56
66
  </div>
57
67
  </div>
58
- <div class="col-lg-6 mb-4 d-flex align-items-stretch">
59
- <div class="card rounded shadow-sm flex-fill">
60
- <div class="card-body">
61
- <h5 class="card-title">Connect new device</h5>
62
- <p class="card-text">Connect new hardware: for simple/temporary configurations.</p>
63
- <a href="{{ url_for('control.controllers_home') }}" class="stretched-link"></a>
64
- </div>
68
+ </div>
69
+ {% endif %}
70
+ </div>
71
+
72
+
73
+
74
+ {% if not off_line %}
75
+ <!-- Direct Control Section -->
76
+ <h4 class="mt-5 mb-3">Direct Control</h4>
77
+ <div class="row">
78
+ <div class="col-lg-6 mb-3 d-flex align-items-stretch">
79
+ <div class="card rounded shadow-sm flex-fill">
80
+ <div class="card-body">
81
+ <h5 class="card-title">
82
+ <i class="bi bi-toggle-on me-2"></i>Direct control
83
+ </h5>
84
+ <p class="card-text">Manually control individual components.</p>
85
+ <a href="{{ url_for('control.deck_controllers') }}" class="stretched-link"></a>
65
86
  </div>
66
87
  </div>
67
88
  </div>
89
+ <div class="col-lg-6 mb-3 d-flex align-items-stretch">
90
+ <div class="card rounded shadow-sm flex-fill">
91
+ <div class="card-body">
92
+ <h5 class="card-title">
93
+ <i class="bi bi-usb-plug me-2"></i>Connect a new device
94
+ </h5>
95
+ <p class="card-text">Add new hardware temporarily or for testing purposes.</p>
96
+ <a href="{{ url_for('control.controllers_home') }}" class="stretched-link"></a>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
68
101
  {% endif %}
69
102
  </div>
70
103
  {% endblock %}
@@ -46,14 +46,17 @@
46
46
  <li class="nav-item">
47
47
  <a class="nav-link" href="{{ url_for('design.experiment_run') }}">Compile/Run</a>
48
48
  </li>
49
+ <li class="nav-item">
50
+ <a class="nav-link" href="{{ url_for('database.list_workflows') }}">Data</a>
51
+ </li>
49
52
  {% endif %}
50
53
 
51
54
  <li class="nav-item">
52
55
  <a class="nav-link" href="{{ url_for('control.deck_controllers') }}">Devices</a></li>
53
56
  </li>
54
- <li class="nav-item">
55
- <a class="nav-link" href="{{ url_for('control.controllers_home') }}">Temp Devices</a></li>
56
- </li>
57
+ {# <li class="nav-item">#}
58
+ {# <a class="nav-link" href="{{ url_for('control.controllers_home') }}">Temp Devices</a></li>#}
59
+ {# </li>#}
57
60
  {# <li class="nav-item">#}
58
61
  {# <a class="nav-link" href="{{ url_for('main.help_info') }}">About</a>#}
59
62
  {# </li>#}
@@ -0,0 +1,57 @@
1
+ # import argparse
2
+ import os
3
+
4
+ # import requests
5
+
6
+ # session = requests.Session()
7
+
8
+
9
+ # Function to create class and methods dynamically
10
+ def create_function(url, class_name, functions):
11
+ class_template = f'class {class_name.capitalize()}:\n url = "{url}ivoryos/backend_control/deck.{class_name}"\n'
12
+
13
+ for function_name, details in functions.items():
14
+ signature = details['signature']
15
+ docstring = details.get('docstring', '')
16
+
17
+ # Creating the function definition
18
+ method = f' def {function_name}{signature}:\n'
19
+ if docstring:
20
+ method += f' """{docstring}"""\n'
21
+
22
+ # Generating the session.post code for sending data
23
+ method += ' return session.post(self.url, data={'
24
+ method += f'"hidden_name": "{function_name}"'
25
+
26
+ # Extracting the parameters from the signature string for the data payload
27
+ param_str = signature[6:-1] # Remove the "(self" and final ")"
28
+ params = [param.strip() for param in param_str.split(',')] if param_str else []
29
+
30
+ for param in params:
31
+ param_name = param.split(':')[0].strip() # Split on ':' and get parameter name
32
+ method += f', "{param_name}": {param_name}'
33
+
34
+ method += '}).json()\n'
35
+ class_template += method + '\n'
36
+
37
+ return class_template
38
+
39
+ # Function to export the generated classes to a Python script
40
+ def export_to_python(class_definitions, path):
41
+ with open(os.path.join(path, "generated_proxy.py"), 'w') as f:
42
+ # Writing the imports at the top of the script
43
+ f.write('import requests\n\n')
44
+ f.write('session = requests.Session()\n\n')
45
+
46
+ # Writing each class definition to the file
47
+ for class_name, class_def in class_definitions.items():
48
+ f.write(class_def)
49
+ f.write('\n')
50
+
51
+ # Creating instances of the dynamically generated classes
52
+ for class_name in class_definitions.keys():
53
+ instance_name = class_name.lower() # Using lowercase for instance names
54
+ f.write(f'{instance_name} = {class_name.capitalize()}()\n')
55
+
56
+
57
+
@@ -44,9 +44,12 @@ class Script(db.Model):
44
44
  id_order = db.Column(JSONType, nullable=True)
45
45
  editing_type = db.Column(db.String(50), nullable=True)
46
46
  author = db.Column(db.String(50), nullable=False)
47
+ # registered = db.Column(db.Boolean, nullable=True, default=False)
47
48
 
48
49
  def __init__(self, name=None, deck=None, status=None, script_dict: dict = None, id_order: dict = None,
49
- time_created=None, last_modified=None, editing_type=None, author: str = None):
50
+ time_created=None, last_modified=None, editing_type=None, author: str = None,
51
+ registered:bool=False,
52
+ ):
50
53
  if script_dict is None:
51
54
  script_dict = {"prep": [], "script": [], "cleanup": []}
52
55
  elif type(script_dict) is not dict:
@@ -73,6 +76,7 @@ class Script(db.Model):
73
76
  self.id_order = id_order
74
77
  self.editing_type = editing_type
75
78
  self.author = author
79
+ # self.r = registered
76
80
 
77
81
  def as_dict(self):
78
82
  dict = self.__dict__
@@ -631,6 +635,44 @@ class Script(db.Model):
631
635
  for i in exec_string.values():
632
636
  s.write(f"\n\n\n{i}")
633
637
 
638
+ class WorkflowRun(db.Model):
639
+ __tablename__ = 'workflow_runs'
640
+
641
+ id = db.Column(db.Integer, primary_key=True)
642
+ name = db.Column(db.String(128), nullable=False)
643
+ platform = db.Column(db.String(128), nullable=False)
644
+ start_time = db.Column(db.DateTime, default=datetime.now())
645
+ end_time = db.Column(db.DateTime)
646
+ data_path = db.Column(db.String(256))
647
+ steps = db.relationship(
648
+ 'WorkflowStep',
649
+ backref='workflow_runs',
650
+ cascade='all, delete-orphan',
651
+ passive_deletes=True
652
+ )
653
+ def as_dict(self):
654
+ dict = self.__dict__
655
+ dict.pop('_sa_instance_state', None)
656
+ return dict
657
+
658
+ class WorkflowStep(db.Model):
659
+ __tablename__ = 'workflow_steps'
660
+
661
+ id = db.Column(db.Integer, primary_key=True)
662
+ workflow_id = db.Column(db.Integer, db.ForeignKey('workflow_runs.id', ondelete='CASCADE'), nullable=False)
663
+
664
+ phase = db.Column(db.String(64), nullable=False) # 'prep', 'main', 'cleanup'
665
+ repeat_index = db.Column(db.Integer, default=0) # Only applies to 'main' phase
666
+ step_index = db.Column(db.Integer, default=0)
667
+ method_name = db.Column(db.String(128), nullable=False)
668
+ start_time = db.Column(db.DateTime)
669
+ end_time = db.Column(db.DateTime)
670
+ run_error = db.Column(db.Boolean, default=False)
671
+
672
+ def as_dict(self):
673
+ dict = self.__dict__
674
+ dict.pop('_sa_instance_state', None)
675
+ return dict
634
676
 
635
677
  if __name__ == "__main__":
636
678
  a = Script()
@@ -162,7 +162,7 @@ can you also help find the default value you can't find the info from my request
162
162
 
163
163
  if __name__ == "__main__":
164
164
  from pprint import pprint
165
- from example.sdl_example.abstract_sdl import deck
165
+ from example.abstract_sdl_example.abstract_sdl import deck
166
166
 
167
167
  from utils import parse_functions
168
168
 
@@ -6,12 +6,14 @@ import time
6
6
  from datetime import datetime
7
7
 
8
8
  from ivoryos.utils import utils
9
- from ivoryos.utils.db_models import Script
9
+ from ivoryos.utils.db_models import Script, WorkflowRun, WorkflowStep, db
10
10
  from ivoryos.utils.global_config import GlobalConfig
11
11
 
12
12
  global_config = GlobalConfig()
13
13
  global deck
14
14
  deck = None
15
+ # global deck, registered_workflows
16
+ # deck, registered_workflows = None, None
15
17
 
16
18
  class ScriptRunner:
17
19
  def __init__(self, globals_dict=None):
@@ -57,7 +59,8 @@ class ScriptRunner:
57
59
  self.abort_pending()
58
60
 
59
61
  def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
60
- output_path=""):
62
+ output_path="", current_app=None):
63
+ # Get run.id
61
64
  global deck
62
65
  if deck is None:
63
66
  deck = global_config.deck
@@ -71,14 +74,13 @@ class ScriptRunner:
71
74
  self.reset_stop_event()
72
75
 
73
76
  thread = threading.Thread(target=self._run_with_stop_check,
74
- args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path))
77
+ args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path, current_app))
75
78
  thread.start()
76
79
  return thread
77
80
 
78
- def exex_steps(self, script, section_name, logger, socketio, **kwargs):
81
+ def exec_steps(self, script, section_name, logger, socketio, run_id, i_progress, **kwargs):
79
82
  """
80
- Executes a function defined in a string line by line.
81
-
83
+ Executes a function defined in a string line by line
82
84
  :param func_str: The function as a string
83
85
  :param kwargs: Arguments to pass to the function
84
86
  :return: The final result of the function execution
@@ -86,8 +88,15 @@ class ScriptRunner:
86
88
  _func_str = script.compile()
87
89
  step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
88
90
  global deck
91
+ # global deck, registered_workflows
89
92
  if deck is None:
90
93
  deck = global_config.deck
94
+ # if registered_workflows is None:
95
+ # registered_workflows = global_config.registered_workflows
96
+
97
+ # for i, line in enumerate(step_list):
98
+ # if line.startswith("registered_workflows"):
99
+ #
91
100
  # func_str = script.compile()
92
101
  # Parse function body from string
93
102
  temp_connections = global_config.defined_variables
@@ -105,27 +114,56 @@ class ScriptRunner:
105
114
  while index < len(step_list):
106
115
  if self.stop_current_event.is_set():
107
116
  logger.info(f'Stopping execution during {section_name}')
117
+ step = WorkflowStep(
118
+ workflow_id=run_id,
119
+ phase=section_name,
120
+ repeat_index=i_progress,
121
+ step_index=index,
122
+ method_name="stop",
123
+ start_time=datetime.now(),
124
+ end_time=datetime.now(),
125
+ run_error=False,
126
+ )
127
+ db.session.add(step)
108
128
  break
109
129
  line = step_list[index]
110
- logger.info(f"Executing: {line}") # Debugging output
130
+ method_name = line.strip().split("(")[0] if "(" in line else line.strip()
131
+ start_time = datetime.now()
132
+ step = WorkflowStep(
133
+ workflow_id=run_id,
134
+ phase=section_name,
135
+ repeat_index=i_progress,
136
+ step_index=index,
137
+ method_name=method_name,
138
+ start_time=start_time,
139
+ )
140
+ logger.info(f"Executing: {line}")
111
141
  socketio.emit('execution', {'section': f"{section_name}-{index}"})
112
142
  # self._emit_progress(socketio, 100)
143
+ # if line.startswith("registered_workflows"):
144
+ # line = line.replace("registered_workflows.", "")
113
145
  try:
114
146
  exec(line, exec_globals, exec_locals)
147
+ step.run_error = False
115
148
  except Exception as e:
116
149
  logger.error(f"Error during script execution: {e}")
117
- socketio.emit('error', {'message': e.__str__()})
150
+ socketio.emit('error', {'message': str(e)})
151
+
152
+ step.run_error = True
118
153
  self.toggle_pause()
154
+ step.end_time = datetime.now()
119
155
  self.pause_event.wait()
120
156
 
121
157
  # todo update script during the run
122
158
  # _func_str = script.compile()
123
159
  # step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
160
+ db.session.add(step)
161
+ db.session.commit()
124
162
  index += 1
125
163
  return exec_locals # Return the 'results' variable
126
164
 
127
165
  def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
128
- output_path):
166
+ output_path, current_app):
129
167
  time.sleep(1)
130
168
  # _func_str = script.compile()
131
169
  # step_list_dict: dict = script.convert_to_lines(_func_str)
@@ -133,39 +171,49 @@ class ScriptRunner:
133
171
 
134
172
  # Run "prep" section once
135
173
  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)
174
+ with current_app.app_context():
175
+
176
+ run = WorkflowRun(name=script.name or "untitled", platform=script.deck,start_time=datetime.now())
177
+ db.session.add(run)
178
+ db.session.flush()
179
+
180
+ self._run_actions(script, section_name="prep", logger=logger, socketio=socketio, run_id=run.id)
181
+ output_list = []
182
+ _, arg_type = script.config("script")
183
+ _, return_list = script.config_return()
184
+
185
+ # Run "script" section multiple times
186
+ if repeat_count:
187
+ self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
188
+ run_name, return_list, logger, socketio, run_id=run.id)
189
+ elif config:
190
+ self._run_config_section(config, arg_type, output_list, script, run_name, logger,
191
+ socketio, run_id=run.id)
192
+
193
+ # Run "cleanup" section once
194
+ self._run_actions(script, section_name="cleanup", logger=logger, socketio=socketio,run_id=run.id)
195
+ # Reset the running flag when done
196
+ with self.lock:
197
+ self.is_running = False
198
+ # Save results if necessary
199
+ filename = None
200
+ if output_list:
201
+ filename = self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
202
+ self._emit_progress(socketio, 100)
203
+ run.end_time = datetime.now()
204
+ run.data_path = filename
205
+ db.session.commit()
158
206
 
159
- def _run_actions(self, script, section_name="", logger=None, socketio=None):
207
+ def _run_actions(self, script, section_name="", logger=None, socketio=None, run_id=None):
160
208
  _func_str = script.compile()
161
209
  step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
162
210
  logger.info(f'Executing {section_name} steps') if step_list else logger.info(f'No {section_name} steps')
163
211
  if self.stop_pending_event.is_set():
164
212
  logger.info(f"Stopping execution during {section_name} section.")
165
213
  return
166
- self.exex_steps(script, section_name, logger, socketio)
214
+ self.exec_steps(script, section_name, logger, socketio, run_id=run_id, i_progress=0)
167
215
 
168
- def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio):
216
+ def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio, run_id):
169
217
  compiled = True
170
218
  for i in config:
171
219
  try:
@@ -185,22 +233,22 @@ class ScriptRunner:
185
233
  self._emit_progress(socketio, progress)
186
234
  # fname = f"{run_name}_script"
187
235
  # function = self.globals_dict[fname]
188
- output = self.exex_steps(script, "script", logger, socketio, **kwargs)
236
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i, **kwargs)
189
237
  if output:
190
238
  # kwargs.update(output)
191
239
  output_list.append(output)
192
240
 
193
241
  def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list,
194
- logger, socketio):
242
+ logger, socketio, run_id):
195
243
  if bo_args:
196
244
  logger.info('Initializing optimizer...')
197
245
  ax_client = utils.ax_initiation(bo_args, arg_types)
198
- for i in range(int(repeat_count)):
246
+ for i_progress in range(int(repeat_count)):
199
247
  if self.stop_pending_event.is_set():
200
- logger.info(f'Stopping execution during {run_name}: {i + 1}/{int(repeat_count)}')
248
+ logger.info(f'Stopping execution during {run_name}: {i_progress + 1}/{int(repeat_count)}')
201
249
  break
202
- logger.info(f'Executing {run_name} experiment: {i + 1}/{int(repeat_count)}')
203
- progress = (i + 1) * 100 / int(repeat_count) - 0.1
250
+ logger.info(f'Executing {run_name} experiment: {i_progress + 1}/{int(repeat_count)}')
251
+ progress = (i_progress + 1) * 100 / int(repeat_count) - 0.1
204
252
  self._emit_progress(socketio, progress)
205
253
  if bo_args:
206
254
  try:
@@ -208,7 +256,7 @@ class ScriptRunner:
208
256
  logger.info(f'Output value: {parameters}')
209
257
  # fname = f"{run_name}_script"
210
258
  # function = self.globals_dict[fname]
211
- output = self.exex_steps(script, "script", logger, socketio, **parameters)
259
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress, **parameters)
212
260
 
213
261
  _output = {key: value for key, value in output.items() if key in return_list}
214
262
  ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
@@ -219,7 +267,7 @@ class ScriptRunner:
219
267
  else:
220
268
  # fname = f"{run_name}_script"
221
269
  # function = self.globals_dict[fname]
222
- output = self.exex_steps(script, "script", logger, socketio)
270
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress)
223
271
 
224
272
  if output:
225
273
  output_list.append(output)
@@ -237,6 +285,7 @@ class ScriptRunner:
237
285
  writer.writeheader()
238
286
  writer.writerows(output_list)
239
287
  logger.info(f'Results saved to {file_path}')
288
+ return filename
240
289
 
241
290
  @staticmethod
242
291
  def _emit_progress(socketio, progress):
ivoryos/utils/utils.py CHANGED
@@ -407,3 +407,26 @@ def check_config_duplicate(config):
407
407
  """
408
408
  hashable_data = [tuple(sorted(d.items())) for d in config]
409
409
  return any(count > 1 for count in Counter(hashable_data).values())
410
+
411
+
412
+ def get_method_from_workflow(function_string, func_name="workflow"):
413
+ """Creates a function from a string and assigns it a new name."""
414
+
415
+ namespace = {}
416
+ exec(function_string, globals(), namespace) # Execute the string in a safe namespace
417
+ # func_name = next(iter(namespace))
418
+ # Get the function name dynamically
419
+ return namespace[func_name]
420
+
421
+
422
+ # def load_workflows(script):
423
+ #
424
+ # class RegisteredWorkflows:
425
+ # pass
426
+ # deck_name = script.deck
427
+ # workflows = Script.query.filter(Script.deck == deck_name, Script.name != script.name, Script.registered==True).all()
428
+ # for workflow in workflows:
429
+ # compiled_strs = workflow.compile().get('script', "")
430
+ # method = get_method_from_workflow(compiled_strs, func_name=workflow.name)
431
+ # setattr(RegisteredWorkflows, workflow.name, staticmethod(method))
432
+ # global_config.registered_workflows = RegisteredWorkflows()
ivoryos/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.21"
1
+ __version__ = "0.1.22"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 0.1.21
3
+ Version: 0.1.22
4
4
  Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
5
5
  Home-page: https://gitlab.com/heingroup/ivoryos
6
6
  Author: Ivory Zhang
@@ -36,7 +36,7 @@ Requires-Dist: python-dotenv
36
36
  - [Instructions for use](#instructions-for-use)
37
37
  - [Demo](#demo)
38
38
  - [Roadmap](#roadmap)
39
- - [License](#license)
39
+
40
40
 
41
41
  ## Description
42
42
  Granting SDLs flexibility and modularity makes it almost impossible to design a UI, yet it's a necessity for allowing more people to interact with it (democratisation).
@@ -93,6 +93,10 @@ Create an account and login (local database)
93
93
  - **Database**: manage workflows in _Library_ tab.
94
94
  - **Info page**: additional info in _About_ tab.
95
95
 
96
+ [//]: # (![Discord]&#40;https://img.shields.io/discord/1313641159356059770&#41;)
97
+
98
+ [//]: # (![PyPI - Downloads]&#40;https://img.shields.io/pypi/dm/ivoryos&#41;)
99
+
96
100
 
97
101
  ### Additional settings
98
102
  #### AI assistant
@@ -133,7 +137,7 @@ After one successful connection, a blueprint will be automatically saved and mad
133
137
  ivoryos.run()
134
138
  ```
135
139
  ## Demo
136
- In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
140
+ In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
137
141
  addresses will be available on terminal.
138
142
  ```Python
139
143
  ivoryos.run(__name__)