ivoryos 0.1.19__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.
- ivoryos/__init__.py +8 -11
- ivoryos/routes/database/database.py +2 -0
- ivoryos/routes/database/templates/database/experiment_database.html +3 -1
- ivoryos/routes/design/design.py +61 -17
- ivoryos/routes/design/templates/design/experiment_builder.html +84 -94
- ivoryos/routes/design/templates/design/experiment_run.html +39 -3
- ivoryos/static/js/socket_handler.js +69 -16
- ivoryos/static/js/sortable_design.js +102 -33
- ivoryos/static/style.css +9 -0
- ivoryos/templates/base.html +3 -3
- ivoryos/utils/db_models.py +44 -13
- ivoryos/utils/form.py +50 -2
- ivoryos/utils/global_config.py +10 -0
- ivoryos/utils/script_runner.py +71 -43
- ivoryos/utils/utils.py +1 -1
- ivoryos/version.py +1 -1
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/METADATA +6 -7
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/RECORD +21 -21
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/LICENSE +0 -0
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/WHEEL +0 -0
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/top_level.txt +0 -0
ivoryos/utils/script_runner.py
CHANGED
|
@@ -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,11 +36,18 @@ 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):
|
|
44
|
+
"""Resets the stop event"""
|
|
38
45
|
self.stop_pending_event.clear()
|
|
39
46
|
self.stop_current_event.clear()
|
|
47
|
+
self.pause_event.set()
|
|
40
48
|
|
|
41
49
|
def abort_pending(self):
|
|
50
|
+
"""Abort the pending iteration after the current is finished"""
|
|
42
51
|
self.stop_pending_event.set()
|
|
43
52
|
# print("Stop pending tasks")
|
|
44
53
|
|
|
@@ -66,7 +75,7 @@ class ScriptRunner:
|
|
|
66
75
|
thread.start()
|
|
67
76
|
return thread
|
|
68
77
|
|
|
69
|
-
def
|
|
78
|
+
def exex_steps(self, script, section_name, logger, socketio, **kwargs):
|
|
70
79
|
"""
|
|
71
80
|
Executes a function defined in a string line by line.
|
|
72
81
|
|
|
@@ -74,71 +83,89 @@ class ScriptRunner:
|
|
|
74
83
|
:param kwargs: Arguments to pass to the function
|
|
75
84
|
:return: The final result of the function execution
|
|
76
85
|
"""
|
|
86
|
+
_func_str = script.compile()
|
|
87
|
+
step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
|
|
77
88
|
global deck
|
|
78
89
|
if deck is None:
|
|
79
90
|
deck = global_config.deck
|
|
80
91
|
# func_str = script.compile()
|
|
81
92
|
# Parse function body from string
|
|
82
|
-
|
|
83
|
-
func_def = next(node for node in module.body if isinstance(node, ast.FunctionDef))
|
|
84
|
-
|
|
85
|
-
# Extract function body as source lines
|
|
86
|
-
lines = [ast.unparse(node) for node in func_def.body if not isinstance(node, ast.Return)]
|
|
93
|
+
temp_connections = global_config.defined_variables
|
|
87
94
|
# Prepare execution environment
|
|
88
|
-
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)
|
|
89
98
|
exec_locals = {} # Local execution scope
|
|
90
99
|
|
|
91
100
|
# Define function arguments manually in exec_locals
|
|
92
101
|
exec_locals.update(kwargs)
|
|
102
|
+
index = 0
|
|
93
103
|
|
|
94
104
|
# Execute each line dynamically
|
|
95
|
-
|
|
105
|
+
while index < len(step_list):
|
|
96
106
|
if self.stop_current_event.is_set():
|
|
97
107
|
logger.info(f'Stopping execution during {section_name}')
|
|
98
108
|
break
|
|
109
|
+
line = step_list[index]
|
|
99
110
|
logger.info(f"Executing: {line}") # Debugging output
|
|
100
|
-
|
|
111
|
+
socketio.emit('execution', {'section': f"{section_name}-{index}"})
|
|
112
|
+
# self._emit_progress(socketio, 100)
|
|
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
|
-
def _run_with_stop_check(self, script: Script, repeat_count, run_name, logger, socketio, config, bo_args,
|
|
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
|
-
|
|
130
|
+
# _func_str = script.compile()
|
|
131
|
+
# step_list_dict: dict = script.convert_to_lines(_func_str)
|
|
109
132
|
self._emit_progress(socketio, 1)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
self.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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')
|
|
136
163
|
if self.stop_pending_event.is_set():
|
|
137
164
|
logger.info(f"Stopping execution during {section_name} section.")
|
|
138
165
|
return
|
|
139
|
-
self.
|
|
166
|
+
self.exex_steps(script, section_name, logger, socketio)
|
|
140
167
|
|
|
141
|
-
def _run_config_section(self, config, arg_type, output_list,
|
|
168
|
+
def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio):
|
|
142
169
|
compiled = True
|
|
143
170
|
for i in config:
|
|
144
171
|
try:
|
|
@@ -158,12 +185,13 @@ class ScriptRunner:
|
|
|
158
185
|
self._emit_progress(socketio, progress)
|
|
159
186
|
# fname = f"{run_name}_script"
|
|
160
187
|
# function = self.globals_dict[fname]
|
|
161
|
-
output = self.
|
|
188
|
+
output = self.exex_steps(script, "script", logger, socketio, **kwargs)
|
|
162
189
|
if output:
|
|
163
190
|
# kwargs.update(output)
|
|
164
191
|
output_list.append(output)
|
|
165
192
|
|
|
166
|
-
def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list,
|
|
193
|
+
def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list,
|
|
194
|
+
logger, socketio):
|
|
167
195
|
if bo_args:
|
|
168
196
|
logger.info('Initializing optimizer...')
|
|
169
197
|
ax_client = utils.ax_initiation(bo_args, arg_types)
|
|
@@ -180,7 +208,7 @@ class ScriptRunner:
|
|
|
180
208
|
logger.info(f'Output value: {parameters}')
|
|
181
209
|
# fname = f"{run_name}_script"
|
|
182
210
|
# function = self.globals_dict[fname]
|
|
183
|
-
output = self.
|
|
211
|
+
output = self.exex_steps(script, "script", logger, socketio, **parameters)
|
|
184
212
|
|
|
185
213
|
_output = {key: value for key, value in output.items() if key in return_list}
|
|
186
214
|
ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
|
|
@@ -191,7 +219,7 @@ class ScriptRunner:
|
|
|
191
219
|
else:
|
|
192
220
|
# fname = f"{run_name}_script"
|
|
193
221
|
# function = self.globals_dict[fname]
|
|
194
|
-
output = self.
|
|
222
|
+
output = self.exex_steps(script, "script", logger, socketio)
|
|
195
223
|
|
|
196
224
|
if output:
|
|
197
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=
|
|
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.
|
|
1
|
+
__version__ = "0.1.21"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.21
|
|
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
|
|
@@ -21,6 +21,9 @@ Requires-Dist: python-dotenv
|
|
|
21
21
|
[](https://ivoryos.readthedocs.io/en/latest/?badge=latest)
|
|
22
22
|
[](https://pypi.org/project/ivoryos/)
|
|
23
23
|

|
|
24
|
+
[](https://youtu.be/dFfJv9I2-1g)
|
|
25
|
+
[](https://www.researchsquare.com/article/rs-5307798/v1)
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|

|
|
26
29
|
# ivoryOS: interoperable Web UI for self-driving laboratories (SDLs)
|
|
@@ -32,6 +35,7 @@ Requires-Dist: python-dotenv
|
|
|
32
35
|
- [Installation](#installation)
|
|
33
36
|
- [Instructions for use](#instructions-for-use)
|
|
34
37
|
- [Demo](#demo)
|
|
38
|
+
- [Roadmap](#roadmap)
|
|
35
39
|
- [License](#license)
|
|
36
40
|
|
|
37
41
|
## Description
|
|
@@ -160,15 +164,10 @@ When you run the application for the first time, it will automatically create th
|
|
|
160
164
|
- **`ivoryos.db`**: Database file that stores application data locally.
|
|
161
165
|
|
|
162
166
|
|
|
163
|
-
### Demo video
|
|
164
|
-
Intro + Tutorial + Demo with PurPOSE platform
|
|
165
|
-
https://youtu.be/dFfJv9I2-1g
|
|
166
|
-
|
|
167
|
-
|
|
168
167
|
## Roadmap
|
|
169
168
|
|
|
170
169
|
- [x] Allow plugin pages ✅
|
|
171
|
-
- [
|
|
170
|
+
- [x] pause, resume, abort current and pending workflows ✅
|
|
172
171
|
- [ ] snapshot version control
|
|
173
172
|
- [ ] dropdown input
|
|
174
173
|
- [ ] show line number option
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
ivoryos/__init__.py,sha256=
|
|
1
|
+
ivoryos/__init__.py,sha256=bx8EuJ5ObYwuwlW3KnWQwXG-AdWWyi-F0kaOLe7WeUo,5978
|
|
2
2
|
ivoryos/config.py,sha256=3FPBYTIBhQTKDvsEoR8ZeTmg65D-CSFEdGmOuIL4pSI,1311
|
|
3
|
-
ivoryos/version.py,sha256=
|
|
3
|
+
ivoryos/version.py,sha256=qEmNtjnOwhDYQ0cHPPtUkUaghzD2xl0thJEznl4giYw,23
|
|
4
4
|
ivoryos/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
ivoryos/routes/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
ivoryos/routes/auth/auth.py,sha256=7CdXjGAr1B_xsmwweakTWOoROgsOJf0MNTzlMP_5Nus,3240
|
|
@@ -12,35 +12,35 @@ ivoryos/routes/control/templates/control/controllers.html,sha256=iIp0h6WA68gQj9O
|
|
|
12
12
|
ivoryos/routes/control/templates/control/controllers_home.html,sha256=xFnBXTWMlyi2naMSsCEqFlX-Z1HJJDDtzITPvq-3nng,2733
|
|
13
13
|
ivoryos/routes/control/templates/control/controllers_new.html,sha256=uOQo9kYmwX2jk3KZDkMUF_ylfNUIs_oIWb_kk_MMVDM,4921
|
|
14
14
|
ivoryos/routes/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
ivoryos/routes/database/database.py,sha256=
|
|
16
|
-
ivoryos/routes/database/templates/database/experiment_database.html,sha256=
|
|
15
|
+
ivoryos/routes/database/database.py,sha256=h4b5r1K5W3psqze0v2QF_jX0ADppy8XdFBQLJb41LOU,5808
|
|
16
|
+
ivoryos/routes/database/templates/database/experiment_database.html,sha256=GeZjx220PdAt_dVN6kigsaNzvaruPlrSSii1cka56NE,3530
|
|
17
17
|
ivoryos/routes/design/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
ivoryos/routes/design/design.py,sha256=
|
|
19
|
-
ivoryos/routes/design/templates/design/experiment_builder.html,sha256
|
|
20
|
-
ivoryos/routes/design/templates/design/experiment_run.html,sha256=
|
|
18
|
+
ivoryos/routes/design/design.py,sha256=rIy6LCMw6ck4bxuPqoxIV6F1vZxY8aZ3Au5pm74dfr8,23934
|
|
19
|
+
ivoryos/routes/design/templates/design/experiment_builder.html,sha256=2cT25URI6NoRDKuU6xl8Vsv4pqyQhhx-Fu8JOfPIP8k,27110
|
|
20
|
+
ivoryos/routes/design/templates/design/experiment_run.html,sha256=t4Pq6uIltm3sq5cDb4zQa9i98iOQFNumkSa24vzCZrY,26001
|
|
21
21
|
ivoryos/routes/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
ivoryos/routes/main/main.py,sha256=yuVJzXAob1kc1dfflkTBIZQ0tdf6kChfuq-uQlN1e9Q,957
|
|
23
23
|
ivoryos/routes/main/templates/main/help.html,sha256=IOktMEsOPk0SCiMBXZ4mpffClERAyX8W82fel71M3M0,9370
|
|
24
24
|
ivoryos/routes/main/templates/main/home.html,sha256=ujM0YC0yrHhCfkuprNnZZZd8XEEguS_6NjrY5ktfctg,3356
|
|
25
25
|
ivoryos/static/favicon.ico,sha256=RhlrPtfITOkzC9BjP1UB1V5L9Oyp6NwNtWeMcGOnpyc,15406
|
|
26
26
|
ivoryos/static/logo.webp,sha256=lXgfQR-4mHTH83k7VV9iB54-oC2ipe6uZvbwdOnLETc,14974
|
|
27
|
-
ivoryos/static/style.css,sha256=
|
|
27
|
+
ivoryos/static/style.css,sha256=zQVx35A5g6JMJ-K84-6fSKtzXGjp_p5ZVG6KLHPM2IE,4021
|
|
28
28
|
ivoryos/static/gui_annotation/Slide1.png,sha256=Lm4gdOkUF5HIUFaB94tl6koQVkzpitKj43GXV_XYMMc,121727
|
|
29
29
|
ivoryos/static/gui_annotation/Slide2.PNG,sha256=z3wQ9oVgg4JTWVLQGKK_KhtepRHUYP1e05XUWGT2A0I,118761
|
|
30
30
|
ivoryos/static/js/overlay.js,sha256=dPxop19es0E0ZUSY3d_4exIk7CJuQEnlW5uTt5fZfzI,483
|
|
31
|
-
ivoryos/static/js/socket_handler.js,sha256=
|
|
31
|
+
ivoryos/static/js/socket_handler.js,sha256=fJ_SKCfS2ZejnECPKZuD8DTEPhBxmm0m-yCvxf2UtIQ,4885
|
|
32
32
|
ivoryos/static/js/sortable_card.js,sha256=ifmlGe3yy0U_KzMphV4ClRhK2DLOvkELYMlq1vECuac,807
|
|
33
|
-
ivoryos/static/js/sortable_design.js,sha256=
|
|
34
|
-
ivoryos/templates/base.html,sha256=
|
|
33
|
+
ivoryos/static/js/sortable_design.js,sha256=wwpKfIzZGDxfX3moNz0cvPvm9YyHmopZK3wmkUdnBiw,4333
|
|
34
|
+
ivoryos/templates/base.html,sha256=OM4D_1A_RawOLBRhiCbKVn7a1UgPxji0BZlnCURl5WM,8325
|
|
35
35
|
ivoryos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
ivoryos/utils/db_models.py,sha256=
|
|
37
|
-
ivoryos/utils/form.py,sha256=
|
|
38
|
-
ivoryos/utils/global_config.py,sha256=
|
|
36
|
+
ivoryos/utils/db_models.py,sha256=LG78TSc9VAEELH9pgTVRRpmyEsCjtxkQOM176N0HAw8,25179
|
|
37
|
+
ivoryos/utils/form.py,sha256=Eqrg4G44hoG6TizxTEP4DNd-rEoyN77t9Y7cB1WdrcQ,19205
|
|
38
|
+
ivoryos/utils/global_config.py,sha256=P0xs_33bZfNQ-D71lCkq7HJyT4ngQWPqUKnkoMrmM8c,1908
|
|
39
39
|
ivoryos/utils/llm_agent.py,sha256=DTf-AF56vy1Em1fUKagl5NURKittmNoxTKIw1PlyC2o,6413
|
|
40
|
-
ivoryos/utils/script_runner.py,sha256=
|
|
41
|
-
ivoryos/utils/utils.py,sha256=
|
|
42
|
-
ivoryos-0.1.
|
|
43
|
-
ivoryos-0.1.
|
|
44
|
-
ivoryos-0.1.
|
|
45
|
-
ivoryos-0.1.
|
|
46
|
-
ivoryos-0.1.
|
|
40
|
+
ivoryos/utils/script_runner.py,sha256=1B6iImRvJ--VSD036p5YSjl76yxMRqDa1OCoRi9ZMM4,10304
|
|
41
|
+
ivoryos/utils/utils.py,sha256=SWVlMjpcUnQ2hgVasXwKugX781eiq78Dv6oZa21lg5Q,14071
|
|
42
|
+
ivoryos-0.1.21.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
|
|
43
|
+
ivoryos-0.1.21.dist-info/METADATA,sha256=3eE6xsyC4fYuUeXA5OsAhWbdrRPCGXaxAFbTTLbCCNA,6697
|
|
44
|
+
ivoryos-0.1.21.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
45
|
+
ivoryos-0.1.21.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
|
|
46
|
+
ivoryos-0.1.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|