ivoryos 0.1.18__tar.gz → 0.1.19__tar.gz
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.
- {ivoryos-0.1.18/ivoryos.egg-info → ivoryos-0.1.19}/PKG-INFO +10 -1
- {ivoryos-0.1.18 → ivoryos-0.1.19}/README.md +9 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/design/design.py +21 -7
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/design/templates/design/experiment_run.html +21 -5
- ivoryos-0.1.19/ivoryos/static/js/socket_handler.js +62 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/utils/db_models.py +8 -41
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/utils/script_runner.py +85 -39
- ivoryos-0.1.19/ivoryos/version.py +1 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19/ivoryos.egg-info}/PKG-INFO +10 -1
- ivoryos-0.1.18/ivoryos/static/js/socket_handler.js +0 -34
- ivoryos-0.1.18/ivoryos/version.py +0 -1
- {ivoryos-0.1.18 → ivoryos-0.1.19}/LICENSE +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/MANIFEST.in +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/config.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/auth/auth.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/auth/templates/auth/login.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/auth/templates/auth/signup.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/control/control.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/control/templates/control/controllers.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/control/templates/control/controllers_home.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/control/templates/control/controllers_new.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/database/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/database/database.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/database/templates/database/experiment_database.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/design/templates/design/experiment_builder.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/main/main.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/main/templates/main/help.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/main/templates/main/home.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/js/sortable_design.js +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/logo.webp +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/static/style.css +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/templates/base.html +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/utils/form.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/utils/global_config.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/utils/utils.py +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos.egg-info/SOURCES.txt +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos.egg-info/requires.txt +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos.egg-info/top_level.txt +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/setup.cfg +0 -0
- {ivoryos-0.1.18 → ivoryos-0.1.19}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
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
|
|
@@ -157,6 +157,15 @@ Intro + Tutorial + Demo with PurPOSE platform
|
|
|
157
157
|
https://youtu.be/dFfJv9I2-1g
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
## Roadmap
|
|
161
|
+
|
|
162
|
+
- [x] Allow plugin pages ✅
|
|
163
|
+
- [ ] pause, resume, abort current and pending workflows
|
|
164
|
+
- [ ] snapshot version control
|
|
165
|
+
- [ ] dropdown input
|
|
166
|
+
- [ ] show line number option
|
|
167
|
+
|
|
168
|
+
|
|
160
169
|
## Authors and Acknowledgement
|
|
161
170
|
Ivory Zhang, Lucy Hao
|
|
162
171
|
|
|
@@ -145,6 +145,15 @@ Intro + Tutorial + Demo with PurPOSE platform
|
|
|
145
145
|
https://youtu.be/dFfJv9I2-1g
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
## Roadmap
|
|
149
|
+
|
|
150
|
+
- [x] Allow plugin pages ✅
|
|
151
|
+
- [ ] pause, resume, abort current and pending workflows
|
|
152
|
+
- [ ] snapshot version control
|
|
153
|
+
- [ ] dropdown input
|
|
154
|
+
- [ ] show line number option
|
|
155
|
+
|
|
156
|
+
|
|
148
157
|
## Authors and Acknowledgement
|
|
149
158
|
Ivory Zhang, Lucy Hao
|
|
150
159
|
|
|
@@ -24,10 +24,22 @@ global_config = GlobalConfig()
|
|
|
24
24
|
runner = ScriptRunner()
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
@socketio.on('
|
|
28
|
-
def
|
|
27
|
+
@socketio.on('abort_pending')
|
|
28
|
+
def handle_abort_pending():
|
|
29
|
+
runner.abort_pending()
|
|
30
|
+
socketio.emit('log', {'message': "aborted pending iterations"})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@socketio.on('abort_current')
|
|
34
|
+
def handle_abort_current():
|
|
29
35
|
runner.stop_execution()
|
|
30
|
-
socketio.emit('log', {'message': "
|
|
36
|
+
socketio.emit('log', {'message': "stopped next task"})
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@socketio.on('pause')
|
|
40
|
+
def handle_pause():
|
|
41
|
+
msg = runner.toggle_pause()
|
|
42
|
+
socketio.emit('log', {'message': msg})
|
|
31
43
|
|
|
32
44
|
|
|
33
45
|
@socketio.on('connect')
|
|
@@ -266,9 +278,10 @@ def experiment_run():
|
|
|
266
278
|
config_preview = config[1:6]
|
|
267
279
|
arg_type = config.pop(0) # first entry is types
|
|
268
280
|
try:
|
|
269
|
-
|
|
281
|
+
for key, func_str in exec_string.items():
|
|
282
|
+
exec(func_str)
|
|
270
283
|
except Exception:
|
|
271
|
-
flash("Please check syntax!!")
|
|
284
|
+
flash(f"Please check {key} syntax!!")
|
|
272
285
|
return redirect(url_for("design.experiment_builder"))
|
|
273
286
|
# runner.globals_dict.update(globals())
|
|
274
287
|
run_name = script.name if script.name else "untitled"
|
|
@@ -313,7 +326,8 @@ def experiment_run():
|
|
|
313
326
|
return render_template('experiment_run.html', script=script.script_dict, filename=filename, dot_py=exec_string,
|
|
314
327
|
return_list=return_list, config_list=config_list, config_file_list=config_file_list,
|
|
315
328
|
config_preview=config_preview, data_list=data_list, config_type_list=config_type_list,
|
|
316
|
-
no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons,
|
|
329
|
+
no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons,
|
|
330
|
+
history=deck_list)
|
|
317
331
|
|
|
318
332
|
|
|
319
333
|
@design.route("/toggle_script_type/<stype>")
|
|
@@ -547,4 +561,4 @@ def duplicate_action(id: int):
|
|
|
547
561
|
script = utils.get_script_file()
|
|
548
562
|
script.duplicate_action(id)
|
|
549
563
|
utils.post_script_file(script)
|
|
550
|
-
return redirect(back)
|
|
564
|
+
return redirect(back)
|
{ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/design/templates/design/experiment_run.html
RENAMED
|
@@ -219,10 +219,24 @@
|
|
|
219
219
|
</div>
|
|
220
220
|
<div class="col-lg-6 col-sm-12 logging-panel">
|
|
221
221
|
<p>
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
<div class="p d-flex justify-content-between align-items-center">
|
|
223
|
+
<h5>Progress:</h5>
|
|
224
|
+
<div class="d-flex gap-2 ms-auto">
|
|
225
|
+
<button id="pause-resume" class="btn btn-info text-white" data-bs-toggle="tooltip" title="Pause execution">
|
|
226
|
+
<i class="bi bi-pause-circle"></i> <!-- Icon for Pause -->
|
|
227
|
+
</button>
|
|
228
|
+
<button id="abort-current" class="btn btn-danger text-white" data-bs-toggle="tooltip" title="Stop execution after current step">
|
|
229
|
+
<i class="bi bi-stop-circle"></i> <!-- Icon for Stop After This Step -->
|
|
230
|
+
</button>
|
|
231
|
+
<button id="abort-pending" class="btn btn-warning text-white" data-bs-toggle="tooltip" title="Stop execution after current iteration">
|
|
232
|
+
<i class="bi bi-hourglass-split"></i> <!-- Icon for Stop After This Iteration -->
|
|
233
|
+
</button>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="text-muted mt-2">
|
|
237
|
+
<small><strong>Note:</strong> The current step cannot be paused or stopped until it completes. </small>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
226
240
|
</p>
|
|
227
241
|
<div class="progress" role="progressbar" aria-label="Animated striped example" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100">
|
|
228
242
|
<div id="progress-bar-inner" class="progress-bar progress-bar-striped progress-bar-animated"></div>
|
|
@@ -295,7 +309,9 @@
|
|
|
295
309
|
</h2>
|
|
296
310
|
<div id="python" class="accordion-collapse collapse">
|
|
297
311
|
<div class="accordion-body">
|
|
298
|
-
|
|
312
|
+
{% for stype, script in dot_py.items() %}
|
|
313
|
+
<pre><code class="python" >{{script}}</code></pre>
|
|
314
|
+
{% endfor %}
|
|
299
315
|
<a href="{{ url_for('design.download', filetype='python') }}">Download <i class="bi bi-download"></i></a>
|
|
300
316
|
</div>
|
|
301
317
|
</div>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
document.addEventListener("DOMContentLoaded", function() {
|
|
2
|
+
var socket = io.connect('http://' + document.domain + ':' + location.port);
|
|
3
|
+
socket.on('connect', function() {
|
|
4
|
+
console.log('Connected');
|
|
5
|
+
});
|
|
6
|
+
socket.on('progress', function(data) {
|
|
7
|
+
var progress = data.progress;
|
|
8
|
+
console.log(progress);
|
|
9
|
+
// Update the progress bar's width and appearance
|
|
10
|
+
var progressBar = document.getElementById('progress-bar-inner');
|
|
11
|
+
progressBar.style.width = progress + '%';
|
|
12
|
+
progressBar.setAttribute('aria-valuenow', progress);
|
|
13
|
+
|
|
14
|
+
if (progress === 100) {
|
|
15
|
+
// Remove animation and set green color when 100% is reached
|
|
16
|
+
progressBar.classList.remove('progress-bar-animated');
|
|
17
|
+
progressBar.classList.add('bg-success'); // Bootstrap class for green color
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
socket.on('log', function(data) {
|
|
21
|
+
var logMessage = data.message;
|
|
22
|
+
console.log(logMessage);
|
|
23
|
+
$('#logging-panel').append(logMessage + "<br>");
|
|
24
|
+
$('#logging-panel').scrollTop($('#logging-panel')[0].scrollHeight);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
document.getElementById('abort-pending').addEventListener('click', function() {
|
|
28
|
+
var confirmation = confirm("Are you sure you want to stop after this iteration?");
|
|
29
|
+
if (confirmation) {
|
|
30
|
+
socket.emit('abort_pending');
|
|
31
|
+
console.log('Abort action sent to server.');
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
document.getElementById('abort-current').addEventListener('click', function() {
|
|
35
|
+
var confirmation = confirm("Are you sure you want to stop after this step?");
|
|
36
|
+
if (confirmation) {
|
|
37
|
+
socket.emit('abort_current');
|
|
38
|
+
console.log('Stop action sent to server.');
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
document.getElementById('pause-resume').addEventListener('click', function() {
|
|
43
|
+
|
|
44
|
+
socket.emit('pause');
|
|
45
|
+
console.log('Pause/Resume is toggled.');
|
|
46
|
+
var button = this;
|
|
47
|
+
var icon = button.querySelector("i");
|
|
48
|
+
|
|
49
|
+
// Toggle between Pause and Resume
|
|
50
|
+
if (icon.classList.contains("bi-pause-circle")) {
|
|
51
|
+
icon.classList.remove("bi-pause-circle");
|
|
52
|
+
icon.classList.add("bi-play-circle");
|
|
53
|
+
button.innerHTML = '<i class="bi bi-play-circle"></i>';
|
|
54
|
+
button.setAttribute("title", "Resume execution");
|
|
55
|
+
} else {
|
|
56
|
+
icon.classList.remove("bi-play-circle");
|
|
57
|
+
icon.classList.add("bi-pause-circle");
|
|
58
|
+
button.innerHTML = '<i class="bi bi-pause-circle"></i>';
|
|
59
|
+
button.setAttribute("title", "Pause execution");
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -398,35 +398,16 @@ class Script(db.Model):
|
|
|
398
398
|
self.sort_actions()
|
|
399
399
|
run_name = self.name if self.name else "untitled"
|
|
400
400
|
run_name = self.validate_function_name(run_name)
|
|
401
|
-
|
|
401
|
+
exec_str_collection = {}
|
|
402
402
|
|
|
403
403
|
for i in self.stypes:
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
404
|
+
func_str = self._generate_function_header(run_name, i) + self._generate_function_body(i)
|
|
405
|
+
exec_str_collection[i] = func_str
|
|
407
406
|
if script_path:
|
|
408
|
-
self._write_to_file(script_path, run_name,
|
|
407
|
+
self._write_to_file(script_path, run_name, exec_str_collection)
|
|
409
408
|
|
|
410
|
-
return
|
|
409
|
+
return exec_str_collection
|
|
411
410
|
|
|
412
|
-
def compile_steps(self, script_path=None):
|
|
413
|
-
"""
|
|
414
|
-
Compile the current script to steps.
|
|
415
|
-
:return: {"prep":[], "script":[], "cleanup":[],}.
|
|
416
|
-
"""
|
|
417
|
-
self.sort_actions()
|
|
418
|
-
run_name = self.name if self.name else "untitled"
|
|
419
|
-
run_name = self.validate_function_name(run_name)
|
|
420
|
-
exec_string = ''
|
|
421
|
-
steps = {}
|
|
422
|
-
for i in self.stypes:
|
|
423
|
-
# exec_string += self._generate_function_header(run_name, i)
|
|
424
|
-
exec_string += self._generate_function_body(i)
|
|
425
|
-
|
|
426
|
-
if script_path:
|
|
427
|
-
self._write_to_file(script_path, run_name, exec_string)
|
|
428
|
-
|
|
429
|
-
return exec_string
|
|
430
411
|
|
|
431
412
|
|
|
432
413
|
@staticmethod
|
|
@@ -447,7 +428,7 @@ class Script(db.Model):
|
|
|
447
428
|
configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
|
|
448
429
|
config_type.items()]
|
|
449
430
|
|
|
450
|
-
function_header = f"
|
|
431
|
+
function_header = f"def {run_name}_{stype}("
|
|
451
432
|
|
|
452
433
|
if stype == "script":
|
|
453
434
|
function_header += ", ".join(configure)
|
|
@@ -471,21 +452,6 @@ class Script(db.Model):
|
|
|
471
452
|
body += self.indent(indent_unit) + return_str
|
|
472
453
|
return body
|
|
473
454
|
|
|
474
|
-
# def _generate_function_body(self, stype):
|
|
475
|
-
# """
|
|
476
|
-
# Generate the function body for each type in stypes.
|
|
477
|
-
# """
|
|
478
|
-
# steps = []
|
|
479
|
-
# indent_unit = 1
|
|
480
|
-
#
|
|
481
|
-
# for index, action in enumerate(self.script_dict[stype]):
|
|
482
|
-
# text, indent_unit = self._process_action(indent_unit, action, index, stype)
|
|
483
|
-
# body += text
|
|
484
|
-
# return_str, return_list = self.config_return()
|
|
485
|
-
# if return_list and stype == "script":
|
|
486
|
-
# body += self.indent(indent_unit) + return_str
|
|
487
|
-
# return body
|
|
488
|
-
|
|
489
455
|
def _process_action(self, indent_unit, action, index, stype):
|
|
490
456
|
"""
|
|
491
457
|
Process each action within the script dictionary.
|
|
@@ -631,7 +597,8 @@ class Script(db.Model):
|
|
|
631
597
|
else:
|
|
632
598
|
s.write("deck = None")
|
|
633
599
|
s.write("\nimport time")
|
|
634
|
-
|
|
600
|
+
for i in exec_string.values():
|
|
601
|
+
s.write(f"\n\n\n{i}")
|
|
635
602
|
|
|
636
603
|
|
|
637
604
|
if __name__ == "__main__":
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ast
|
|
1
2
|
import os
|
|
2
3
|
import csv
|
|
3
4
|
import threading
|
|
@@ -17,25 +18,40 @@ class ScriptRunner:
|
|
|
17
18
|
if globals_dict is None:
|
|
18
19
|
globals_dict = globals()
|
|
19
20
|
self.globals_dict = globals_dict
|
|
20
|
-
|
|
21
|
-
self.
|
|
21
|
+
self.pause_event = threading.Event() # A threading event to manage pause/resume
|
|
22
|
+
self.pause_event.set()
|
|
23
|
+
self.stop_pending_event = threading.Event()
|
|
24
|
+
self.stop_current_event = threading.Event()
|
|
22
25
|
self.is_running = False
|
|
23
26
|
self.lock = threading.Lock()
|
|
24
27
|
|
|
28
|
+
def toggle_pause(self):
|
|
29
|
+
"""Toggles between pausing and resuming the script"""
|
|
30
|
+
if self.pause_event.is_set():
|
|
31
|
+
self.pause_event.clear() # Pause the script
|
|
32
|
+
return "Paused"
|
|
33
|
+
else:
|
|
34
|
+
self.pause_event.set() # Resume the script
|
|
35
|
+
return "Resumed"
|
|
36
|
+
|
|
25
37
|
def reset_stop_event(self):
|
|
26
|
-
self.
|
|
38
|
+
self.stop_pending_event.clear()
|
|
39
|
+
self.stop_current_event.clear()
|
|
27
40
|
|
|
28
|
-
def
|
|
29
|
-
self.
|
|
41
|
+
def abort_pending(self):
|
|
42
|
+
self.stop_pending_event.set()
|
|
30
43
|
# print("Stop pending tasks")
|
|
31
44
|
|
|
45
|
+
def stop_execution(self):
|
|
46
|
+
"""Force stop everything, including ongoing tasks."""
|
|
47
|
+
self.stop_current_event.set()
|
|
48
|
+
self.abort_pending()
|
|
49
|
+
|
|
32
50
|
def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
|
|
33
51
|
output_path=""):
|
|
34
52
|
global deck
|
|
35
53
|
if deck is None:
|
|
36
54
|
deck = global_config.deck
|
|
37
|
-
exec_string = script.compile()
|
|
38
|
-
exec(exec_string)
|
|
39
55
|
time.sleep(1)
|
|
40
56
|
with self.lock:
|
|
41
57
|
if self.is_running:
|
|
@@ -50,24 +66,61 @@ class ScriptRunner:
|
|
|
50
66
|
thread.start()
|
|
51
67
|
return thread
|
|
52
68
|
|
|
69
|
+
def execute_function_line_by_line(self, script, section_name, logger, **kwargs):
|
|
70
|
+
"""
|
|
71
|
+
Executes a function defined in a string line by line.
|
|
72
|
+
|
|
73
|
+
:param func_str: The function as a string
|
|
74
|
+
:param kwargs: Arguments to pass to the function
|
|
75
|
+
:return: The final result of the function execution
|
|
76
|
+
"""
|
|
77
|
+
global deck
|
|
78
|
+
if deck is None:
|
|
79
|
+
deck = global_config.deck
|
|
80
|
+
# func_str = script.compile()
|
|
81
|
+
# Parse function body from string
|
|
82
|
+
module = ast.parse(script)
|
|
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)]
|
|
87
|
+
# Prepare execution environment
|
|
88
|
+
exec_globals = {"deck": deck} # Add required global objects
|
|
89
|
+
exec_locals = {} # Local execution scope
|
|
90
|
+
|
|
91
|
+
# Define function arguments manually in exec_locals
|
|
92
|
+
exec_locals.update(kwargs)
|
|
93
|
+
|
|
94
|
+
# Execute each line dynamically
|
|
95
|
+
for line in lines:
|
|
96
|
+
if self.stop_current_event.is_set():
|
|
97
|
+
logger.info(f'Stopping execution during {section_name}')
|
|
98
|
+
break
|
|
99
|
+
logger.info(f"Executing: {line}") # Debugging output
|
|
100
|
+
exec(line, exec_globals, exec_locals)
|
|
101
|
+
self.pause_event.wait()
|
|
102
|
+
|
|
103
|
+
return exec_locals # Return the 'results' variable
|
|
104
|
+
|
|
53
105
|
def _run_with_stop_check(self, script: Script, repeat_count, run_name, logger, socketio, config, bo_args,
|
|
54
106
|
output_path):
|
|
55
107
|
time.sleep(1)
|
|
108
|
+
func_str = script.compile()
|
|
56
109
|
self._emit_progress(socketio, 1)
|
|
57
110
|
try:
|
|
58
111
|
# Run "prep" section once
|
|
59
112
|
script_dict = script.script_dict
|
|
60
|
-
self._run_actions(script_dict.get("prep", []),
|
|
113
|
+
self._run_actions(script_dict.get("prep", []), func_str.get("prep", ''), section_name="prep", logger=logger)
|
|
61
114
|
output_list = []
|
|
62
115
|
_, arg_type = script.config("script")
|
|
63
116
|
_, return_list = script.config_return()
|
|
64
117
|
# Run "script" section multiple times
|
|
65
118
|
if repeat_count:
|
|
66
|
-
self._run_repeat_section(repeat_count, arg_type, bo_args, output_list,
|
|
119
|
+
self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, func_str.get("script", ''), run_name, return_list, logger, socketio)
|
|
67
120
|
elif config:
|
|
68
|
-
self._run_config_section(config, arg_type, output_list,
|
|
121
|
+
self._run_config_section(config, arg_type, output_list, func_str.get("script", ''), run_name, logger, socketio)
|
|
69
122
|
# Run "cleanup" section once
|
|
70
|
-
self._run_actions(script_dict.get("cleanup", []),
|
|
123
|
+
self._run_actions(script_dict.get("cleanup", []), func_str.get("cleanup", ''), section_name="cleanup", logger=logger)
|
|
71
124
|
# Save results if necessary
|
|
72
125
|
if output_list:
|
|
73
126
|
self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
|
|
@@ -78,21 +131,14 @@ class ScriptRunner:
|
|
|
78
131
|
self.is_running = False # Reset the running flag when done
|
|
79
132
|
self._emit_progress(socketio, 100)
|
|
80
133
|
|
|
81
|
-
def _run_actions(self, actions, section_name="",
|
|
134
|
+
def _run_actions(self, actions, func_str, section_name="", logger=None):
|
|
82
135
|
logger.info(f'Executing {section_name} steps') if actions else logger.info(f'No {section_name} steps')
|
|
83
|
-
if self.
|
|
136
|
+
if self.stop_pending_event.is_set():
|
|
84
137
|
logger.info(f"Stopping execution during {section_name} section.")
|
|
85
138
|
return
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# break
|
|
90
|
-
# logger.info(f'Executing {action.get("action", "")} action')
|
|
91
|
-
fname = f"{run_name}_{section_name}"
|
|
92
|
-
function = self.globals_dict[fname]
|
|
93
|
-
function()
|
|
94
|
-
|
|
95
|
-
def _run_config_section(self, config, arg_type, output_list, return_list, run_name, logger, socketio):
|
|
139
|
+
self.execute_function_line_by_line(func_str, section_name, logger)
|
|
140
|
+
|
|
141
|
+
def _run_config_section(self, config, arg_type, output_list, func_str, run_name, logger, socketio):
|
|
96
142
|
compiled = True
|
|
97
143
|
for i in config:
|
|
98
144
|
try:
|
|
@@ -104,25 +150,25 @@ class ScriptRunner:
|
|
|
104
150
|
if compiled:
|
|
105
151
|
for i, kwargs in enumerate(config):
|
|
106
152
|
kwargs = dict(kwargs)
|
|
107
|
-
if self.
|
|
153
|
+
if self.stop_pending_event.is_set():
|
|
108
154
|
logger.info(f'Stopping execution during {run_name}: {i + 1}/{len(config)}')
|
|
109
155
|
break
|
|
110
156
|
logger.info(f'Executing {i + 1} of {len(config)} with kwargs = {kwargs}')
|
|
111
157
|
progress = (i + 1) * 100 / len(config)
|
|
112
158
|
self._emit_progress(socketio, progress)
|
|
113
|
-
fname = f"{run_name}_script"
|
|
114
|
-
function = self.globals_dict[fname]
|
|
115
|
-
output =
|
|
159
|
+
# fname = f"{run_name}_script"
|
|
160
|
+
# function = self.globals_dict[fname]
|
|
161
|
+
output = self.execute_function_line_by_line(func_str, "script", logger, **kwargs)
|
|
116
162
|
if output:
|
|
117
|
-
kwargs.update(output)
|
|
118
|
-
output_list.append(
|
|
163
|
+
# kwargs.update(output)
|
|
164
|
+
output_list.append(output)
|
|
119
165
|
|
|
120
|
-
def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list,
|
|
166
|
+
def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, func_str, run_name, return_list, logger, socketio):
|
|
121
167
|
if bo_args:
|
|
122
168
|
logger.info('Initializing optimizer...')
|
|
123
169
|
ax_client = utils.ax_initiation(bo_args, arg_types)
|
|
124
170
|
for i in range(int(repeat_count)):
|
|
125
|
-
if self.
|
|
171
|
+
if self.stop_pending_event.is_set():
|
|
126
172
|
logger.info(f'Stopping execution during {run_name}: {i + 1}/{int(repeat_count)}')
|
|
127
173
|
break
|
|
128
174
|
logger.info(f'Executing {run_name} experiment: {i + 1}/{int(repeat_count)}')
|
|
@@ -132,20 +178,20 @@ class ScriptRunner:
|
|
|
132
178
|
try:
|
|
133
179
|
parameters, trial_index = ax_client.get_next_trial()
|
|
134
180
|
logger.info(f'Output value: {parameters}')
|
|
135
|
-
fname = f"{run_name}_script"
|
|
136
|
-
function = self.globals_dict[fname]
|
|
137
|
-
output =
|
|
138
|
-
|
|
139
|
-
_output = output.
|
|
181
|
+
# fname = f"{run_name}_script"
|
|
182
|
+
# function = self.globals_dict[fname]
|
|
183
|
+
output = self.execute_function_line_by_line(func_str, "script", logger, **parameters)
|
|
184
|
+
|
|
185
|
+
_output = {key: value for key, value in output.items() if key in return_list}
|
|
140
186
|
ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
|
|
141
187
|
output.update(parameters)
|
|
142
188
|
except Exception as e:
|
|
143
189
|
logger.info(f'Optimization error: {e}')
|
|
144
190
|
break
|
|
145
191
|
else:
|
|
146
|
-
fname = f"{run_name}_script"
|
|
147
|
-
function = self.globals_dict[fname]
|
|
148
|
-
output =
|
|
192
|
+
# fname = f"{run_name}_script"
|
|
193
|
+
# function = self.globals_dict[fname]
|
|
194
|
+
output = self.execute_function_line_by_line(func_str, "script", logger)
|
|
149
195
|
|
|
150
196
|
if output:
|
|
151
197
|
output_list.append(output)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.19"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
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
|
|
@@ -157,6 +157,15 @@ Intro + Tutorial + Demo with PurPOSE platform
|
|
|
157
157
|
https://youtu.be/dFfJv9I2-1g
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
## Roadmap
|
|
161
|
+
|
|
162
|
+
- [x] Allow plugin pages ✅
|
|
163
|
+
- [ ] pause, resume, abort current and pending workflows
|
|
164
|
+
- [ ] snapshot version control
|
|
165
|
+
- [ ] dropdown input
|
|
166
|
+
- [ ] show line number option
|
|
167
|
+
|
|
168
|
+
|
|
160
169
|
## Authors and Acknowledgement
|
|
161
170
|
Ivory Zhang, Lucy Hao
|
|
162
171
|
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
document.addEventListener("DOMContentLoaded", function() {
|
|
2
|
-
var socket = io.connect('http://' + document.domain + ':' + location.port);
|
|
3
|
-
socket.on('connect', function() {
|
|
4
|
-
console.log('Connected');
|
|
5
|
-
});
|
|
6
|
-
socket.on('progress', function(data) {
|
|
7
|
-
var progress = data.progress;
|
|
8
|
-
console.log(progress);
|
|
9
|
-
// Update the progress bar's width and appearance
|
|
10
|
-
var progressBar = document.getElementById('progress-bar-inner');
|
|
11
|
-
progressBar.style.width = progress + '%';
|
|
12
|
-
progressBar.setAttribute('aria-valuenow', progress);
|
|
13
|
-
|
|
14
|
-
if (progress === 100) {
|
|
15
|
-
// Remove animation and set green color when 100% is reached
|
|
16
|
-
progressBar.classList.remove('progress-bar-animated');
|
|
17
|
-
progressBar.classList.add('bg-success'); // Bootstrap class for green color
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
socket.on('log', function(data) {
|
|
21
|
-
var logMessage = data.message;
|
|
22
|
-
console.log(logMessage);
|
|
23
|
-
$('#logging-panel').append(logMessage + "<br>");
|
|
24
|
-
$('#logging-panel').scrollTop($('#logging-panel')[0].scrollHeight);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
document.getElementById('abort').addEventListener('click', function() {
|
|
28
|
-
var confirmation = confirm("Are you sure you want to abort pending actions?");
|
|
29
|
-
if (confirmation) {
|
|
30
|
-
socket.emit('abort_action');
|
|
31
|
-
console.log('Abort action sent to server.');
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.18"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/control/templates/control/controllers_home.html
RENAMED
|
File without changes
|
{ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/control/templates/control/controllers_new.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ivoryos-0.1.18 → ivoryos-0.1.19}/ivoryos/routes/design/templates/design/experiment_builder.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|