ivoryos 1.0.1__py3-none-any.whl → 1.0.4__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.
- ivoryos/__init__.py +22 -6
- ivoryos/routes/auth/auth.py +15 -18
- ivoryos/routes/control/control.py +78 -43
- ivoryos/routes/control/templates/control/controllers_home.html +7 -7
- ivoryos/routes/database/database.py +64 -44
- ivoryos/routes/database/templates/database/{experiment_database.html → scripts_database.html} +27 -18
- ivoryos/routes/database/templates/database/{workflow_run_database.html → workflow_database.html} +27 -5
- ivoryos/routes/design/design.py +135 -79
- ivoryos/routes/design/templates/design/experiment_builder.html +100 -45
- ivoryos/routes/design/templates/design/experiment_run.html +491 -328
- ivoryos/static/js/sortable_design.js +1 -1
- ivoryos/utils/client_proxy.py +1 -1
- ivoryos/utils/form.py +30 -3
- ivoryos/utils/script_runner.py +2 -1
- ivoryos/utils/utils.py +13 -0
- ivoryos/version.py +1 -1
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/METADATA +37 -1
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/RECORD +22 -22
- /ivoryos/routes/database/templates/database/{experiment_step_view.html → workflow_view.html} +0 -0
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/LICENSE +0 -0
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/WHEEL +0 -0
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/top_level.txt +0 -0
ivoryos/routes/database/templates/database/{experiment_database.html → scripts_database.html}
RENAMED
|
@@ -35,19 +35,19 @@
|
|
|
35
35
|
</tr>
|
|
36
36
|
</thead>
|
|
37
37
|
<tbody>
|
|
38
|
-
{% for
|
|
38
|
+
{% for script in scripts %}
|
|
39
39
|
<tr>
|
|
40
|
-
<td><a href="{{ url_for('database.edit_workflow',
|
|
41
|
-
<td>{{
|
|
42
|
-
<td>{{
|
|
43
|
-
<td>{{
|
|
44
|
-
<td>{{
|
|
45
|
-
<td>{{
|
|
40
|
+
<td><a href="{{ url_for('database.edit_workflow', script_name=script.name) }}">{{ script.name }}</a></td>
|
|
41
|
+
<td>{{ script.deck }}</td>
|
|
42
|
+
<td>{{ script.status }}</td>
|
|
43
|
+
<td>{{ script.time_created }}</td>
|
|
44
|
+
<td>{{ script.last_modified }}</td>
|
|
45
|
+
<td>{{ script.author }}</td>
|
|
46
46
|
{# <td>{{ workflow.registered }}</td>#}
|
|
47
47
|
<td>
|
|
48
48
|
{#not workflow.status == "finalized" or#}
|
|
49
|
-
{% if session['user'] == 'admin' or session['user'] ==
|
|
50
|
-
<a href="{{ url_for('database.delete_workflow',
|
|
49
|
+
{% if session['user'] == 'admin' or session['user'] == script.author %}
|
|
50
|
+
<a href="{{ url_for('database.delete_workflow', script_name=script.name) }}">delete</a>
|
|
51
51
|
{% else %}
|
|
52
52
|
<a class="disabled-link">delete</a>
|
|
53
53
|
{% endif %}
|
|
@@ -57,18 +57,27 @@
|
|
|
57
57
|
</tbody>
|
|
58
58
|
</table>
|
|
59
59
|
|
|
60
|
-
{# paging#}
|
|
60
|
+
{# paging#}
|
|
61
61
|
<div class="pagination justify-content-center">
|
|
62
|
-
<div class="page-item {{ 'disabled' if not
|
|
63
|
-
<a class="page-link" href="{{ url_for('database.load_from_database', page=
|
|
62
|
+
<div class="page-item {{ 'disabled' if not scripts.has_prev else '' }}">
|
|
63
|
+
<a class="page-link" href="{{ url_for('database.load_from_database', page=scripts.prev_num) }}">Previous</a>
|
|
64
64
|
</div>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
|
|
66
|
+
{% for num in scripts.iter_pages() %}
|
|
67
|
+
{% if num %}
|
|
68
|
+
<div class="page-item {{ 'active' if num == scripts.page else '' }}">
|
|
69
|
+
<a class="page-link" href="{{ url_for('database.load_from_database', page=num) }}">{{ num }}</a>
|
|
70
|
+
</div>
|
|
71
|
+
{% else %}
|
|
72
|
+
<div class="page-item disabled">
|
|
73
|
+
<span class="page-link">…</span>
|
|
74
|
+
</div>
|
|
75
|
+
{% endif %}
|
|
69
76
|
{% endfor %}
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
|
|
78
|
+
<div class="page-item {{ 'disabled' if not scripts.has_next else '' }}">
|
|
79
|
+
<a class="page-link" href="{{ url_for('database.load_from_database', page=scripts.next_num) }}">Next</a>
|
|
72
80
|
</div>
|
|
73
81
|
</div>
|
|
82
|
+
|
|
74
83
|
{% endblock %}
|
ivoryos/routes/database/templates/database/{workflow_run_database.html → workflow_database.html}
RENAMED
|
@@ -2,11 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
{% block title %}IvoryOS | Design Database{% endblock %}
|
|
4
4
|
{% block body %}
|
|
5
|
+
<div class="div">
|
|
6
|
+
<form id="search" style="display: inline-block;float: right;" action="{{url_for('database.list_workflows',deck_name=deck_name)}}" method="GET">
|
|
7
|
+
<div class="input-group">
|
|
8
|
+
<div class="form-outline">
|
|
9
|
+
<input type="search" name="keyword" id="keyword" class="form-control" placeholder="Search workflows...">
|
|
10
|
+
</div>
|
|
11
|
+
<button type="submit" class="btn btn-primary">
|
|
12
|
+
<i class="bi bi-search"></i>
|
|
13
|
+
</button>
|
|
14
|
+
</div>
|
|
15
|
+
</form>
|
|
16
|
+
</div>
|
|
5
17
|
|
|
6
|
-
|
|
18
|
+
<table class="table table-hover" id="workflowResultLibrary">
|
|
7
19
|
<thead>
|
|
8
20
|
<tr>
|
|
9
21
|
<th scope="col">Workflow name</th>
|
|
22
|
+
<th scope="col">Workflow ID</th>
|
|
10
23
|
<th scope="col">Start time</th>
|
|
11
24
|
<th scope="col">End time</th>
|
|
12
25
|
<th scope="col">Data</th>
|
|
@@ -16,6 +29,7 @@
|
|
|
16
29
|
{% for workflow in workflows %}
|
|
17
30
|
<tr>
|
|
18
31
|
<td><a href="{{ url_for('database.get_workflow_steps', workflow_id=workflow.id) }}">{{ workflow.name }}</a></td>
|
|
32
|
+
<td>{{ workflow.id }}</td>
|
|
19
33
|
<td>{{ workflow.start_time.strftime("%Y-%m-%d %H:%M:%S") if workflow.start_time else '' }}</td>
|
|
20
34
|
<td>{{ workflow.end_time.strftime("%Y-%m-%d %H:%M:%S") if workflow.end_time else '' }}</td>
|
|
21
35
|
|
|
@@ -36,16 +50,24 @@
|
|
|
36
50
|
</tbody>
|
|
37
51
|
</table>
|
|
38
52
|
|
|
39
|
-
{# paging#}
|
|
53
|
+
{# paging#}
|
|
40
54
|
<div class="pagination justify-content-center">
|
|
41
55
|
<div class="page-item {{ 'disabled' if not workflows.has_prev else '' }}">
|
|
42
56
|
<a class="page-link" href="{{ url_for('database.list_workflows', page=workflows.prev_num) }}">Previous</a>
|
|
43
57
|
</div>
|
|
58
|
+
|
|
44
59
|
{% for num in workflows.iter_pages() %}
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
60
|
+
{% if num %}
|
|
61
|
+
<div class="page-item {{ 'active' if num == workflows.page else '' }}">
|
|
62
|
+
<a class="page-link" href="{{ url_for('database.list_workflows', page=num) }}">{{ num }}</a>
|
|
63
|
+
</div>
|
|
64
|
+
{% else %}
|
|
65
|
+
<div class="page-item disabled">
|
|
66
|
+
<span class="page-link">…</span>
|
|
67
|
+
</div>
|
|
68
|
+
{% endif %}
|
|
48
69
|
{% endfor %}
|
|
70
|
+
|
|
49
71
|
<div class="page-item {{ 'disabled' if not workflows.has_next else '' }}">
|
|
50
72
|
<a class="page-link" href="{{ url_for('database.list_workflows', page=workflows.next_num) }}">Next</a>
|
|
51
73
|
</div>
|
ivoryos/routes/design/design.py
CHANGED
|
@@ -3,6 +3,7 @@ import json
|
|
|
3
3
|
import os
|
|
4
4
|
import pickle
|
|
5
5
|
import sys
|
|
6
|
+
import time
|
|
6
7
|
|
|
7
8
|
from flask import Blueprint, redirect, url_for, flash, jsonify, send_file, request, render_template, session, \
|
|
8
9
|
current_app, g
|
|
@@ -11,7 +12,6 @@ from flask_socketio import SocketIO
|
|
|
11
12
|
from werkzeug.utils import secure_filename
|
|
12
13
|
|
|
13
14
|
from ivoryos.utils import utils
|
|
14
|
-
from ivoryos.utils.client_proxy import create_function, export_to_python
|
|
15
15
|
from ivoryos.utils.global_config import GlobalConfig
|
|
16
16
|
from ivoryos.utils.form import create_builtin_form, create_action_button, format_name, create_form_from_pseudo, \
|
|
17
17
|
create_form_from_action, create_all_builtin_forms
|
|
@@ -74,8 +74,8 @@ def handle_abort_action():
|
|
|
74
74
|
socketio.emit('log', {'message': message})
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
@design.route("/
|
|
78
|
-
@design.route("/
|
|
77
|
+
@design.route("/design/script/", methods=['GET', 'POST'])
|
|
78
|
+
@design.route("/design/script/<instrument>/", methods=['GET', 'POST'])
|
|
79
79
|
@login_required
|
|
80
80
|
def experiment_builder(instrument=None):
|
|
81
81
|
"""
|
|
@@ -86,7 +86,7 @@ def experiment_builder(instrument=None):
|
|
|
86
86
|
This route allows users to build and edit experiment workflows. Users can interact with available instruments,
|
|
87
87
|
define variables, and manage experiment scripts.
|
|
88
88
|
|
|
89
|
-
.. http:get:: /
|
|
89
|
+
.. http:get:: /design/script
|
|
90
90
|
|
|
91
91
|
Load the experiment builder interface.
|
|
92
92
|
|
|
@@ -94,7 +94,7 @@ def experiment_builder(instrument=None):
|
|
|
94
94
|
:type instrument: str
|
|
95
95
|
:status 200: Experiment builder loaded successfully.
|
|
96
96
|
|
|
97
|
-
.. http:post:: /
|
|
97
|
+
.. http:post:: /design/script
|
|
98
98
|
|
|
99
99
|
Submit form data to add or modify actions in the experiment script.
|
|
100
100
|
|
|
@@ -120,7 +120,7 @@ def experiment_builder(instrument=None):
|
|
|
120
120
|
if deck and script.deck is None:
|
|
121
121
|
script.deck = os.path.splitext(os.path.basename(deck.__file__))[
|
|
122
122
|
0] if deck.__name__ == "__main__" else deck.__name__
|
|
123
|
-
script.sort_actions()
|
|
123
|
+
# script.sort_actions()
|
|
124
124
|
|
|
125
125
|
pseudo_deck_name = session.get('pseudo_deck', '')
|
|
126
126
|
pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pseudo_deck_name)
|
|
@@ -202,11 +202,11 @@ def experiment_builder(instrument=None):
|
|
|
202
202
|
logic_type = kwargs.pop('builtin_name')
|
|
203
203
|
if 'variable' in kwargs:
|
|
204
204
|
try:
|
|
205
|
-
script.add_variable(
|
|
205
|
+
script.add_variable(insert_position=insert_position, **kwargs)
|
|
206
206
|
except ValueError:
|
|
207
207
|
flash("Invalid variable type")
|
|
208
208
|
else:
|
|
209
|
-
script.add_logic_action(logic_type=logic_type,
|
|
209
|
+
script.add_logic_action(logic_type=logic_type, insert_position=insert_position, **kwargs)
|
|
210
210
|
else:
|
|
211
211
|
flash(form.errors)
|
|
212
212
|
elif request.method == 'POST' and "workflow_name" in request.form:
|
|
@@ -240,6 +240,10 @@ def experiment_builder(instrument=None):
|
|
|
240
240
|
forms = create_form_from_pseudo(functions, autofill=autofill, script=script)
|
|
241
241
|
|
|
242
242
|
utils.post_script_file(script)
|
|
243
|
+
|
|
244
|
+
exec_string = script.python_script if script.python_script else script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
245
|
+
session['python_code'] = exec_string
|
|
246
|
+
|
|
243
247
|
design_buttons = create_action_button(script)
|
|
244
248
|
return render_template('experiment_builder.html', off_line=off_line, instrument=instrument, history=deck_list,
|
|
245
249
|
script=script, defined_variables=deck_variables,
|
|
@@ -248,17 +252,17 @@ def experiment_builder(instrument=None):
|
|
|
248
252
|
use_llm=enable_llm)
|
|
249
253
|
|
|
250
254
|
|
|
251
|
-
@design.route("/generate_code", methods=['POST'])
|
|
255
|
+
@design.route("/design/generate_code", methods=['POST'])
|
|
252
256
|
@login_required
|
|
253
257
|
def generate_code():
|
|
254
258
|
"""
|
|
255
259
|
.. :quickref: Text to Code; Generate code from user input and update the design canvas.
|
|
256
260
|
|
|
257
|
-
.. http:post:: /generate_code
|
|
261
|
+
.. http:post:: /design/generate_code
|
|
258
262
|
|
|
259
263
|
:form prompt: user's prompt
|
|
260
264
|
:status 200: and then redirects to :http:get:`/experiment/build`
|
|
261
|
-
:status 400: failed to initialize the AI agent redirects to :http:get:`/
|
|
265
|
+
:status 400: failed to initialize the AI agent redirects to :http:get:`/design/script`
|
|
262
266
|
|
|
263
267
|
"""
|
|
264
268
|
agent = global_config.agent
|
|
@@ -296,24 +300,25 @@ def generate_code():
|
|
|
296
300
|
return redirect(url_for("design.experiment_builder", instrument=instrument, use_llm=True))
|
|
297
301
|
|
|
298
302
|
|
|
299
|
-
@design.route("/
|
|
303
|
+
@design.route("/design/campaign", methods=['GET', 'POST'])
|
|
300
304
|
@login_required
|
|
301
305
|
def experiment_run():
|
|
302
306
|
"""
|
|
303
307
|
.. :quickref: Workflow Execution; Execute/iterate the workflow
|
|
304
308
|
|
|
305
|
-
.. http:get:: /
|
|
309
|
+
.. http:get:: /design/campaign
|
|
306
310
|
|
|
307
311
|
Compile the workflow and load the experiment execution interface.
|
|
308
312
|
|
|
309
|
-
.. http:post:: /
|
|
313
|
+
.. http:post:: /design/campaign
|
|
310
314
|
|
|
311
315
|
Start workflow execution
|
|
312
316
|
|
|
313
317
|
"""
|
|
314
318
|
deck = global_config.deck
|
|
315
319
|
script = utils.get_script_file()
|
|
316
|
-
|
|
320
|
+
|
|
321
|
+
# script.sort_actions() # handled in update list
|
|
317
322
|
off_line = current_app.config["OFF_LINE"]
|
|
318
323
|
deck_list = utils.import_history(os.path.join(current_app.config["OUTPUT_FOLDER"], 'deck_history.txt'))
|
|
319
324
|
# if not off_line and deck is None:
|
|
@@ -328,7 +333,7 @@ def experiment_run():
|
|
|
328
333
|
# todo
|
|
329
334
|
exec_string = script.python_script if script.python_script else script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
330
335
|
# exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
331
|
-
|
|
336
|
+
# print(exec_string)
|
|
332
337
|
except Exception as e:
|
|
333
338
|
flash(e.__str__())
|
|
334
339
|
# handle api request
|
|
@@ -345,12 +350,13 @@ def experiment_run():
|
|
|
345
350
|
if filename:
|
|
346
351
|
# config_preview = list(csv.DictReader(open(os.path.join(current_app.config['CSV_FOLDER'], filename))))
|
|
347
352
|
config = list(csv.DictReader(open(os.path.join(current_app.config['CSV_FOLDER'], filename))))
|
|
348
|
-
config_preview = config[1:
|
|
353
|
+
config_preview = config[1:]
|
|
349
354
|
arg_type = config.pop(0) # first entry is types
|
|
350
355
|
try:
|
|
351
356
|
for key, func_str in exec_string.items():
|
|
352
357
|
exec(func_str)
|
|
353
358
|
line_collection = script.convert_to_lines(exec_string)
|
|
359
|
+
|
|
354
360
|
except Exception:
|
|
355
361
|
flash(f"Please check {key} syntax!!")
|
|
356
362
|
return redirect(url_for("design.experiment_builder"))
|
|
@@ -408,6 +414,9 @@ def experiment_run():
|
|
|
408
414
|
else:
|
|
409
415
|
flash(e)
|
|
410
416
|
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
417
|
+
# wait to get a workflow ID
|
|
418
|
+
while not global_config.runner_status:
|
|
419
|
+
time.sleep(1)
|
|
411
420
|
return jsonify({"status": "task started", "task_id": global_config.runner_status.get("id")})
|
|
412
421
|
else:
|
|
413
422
|
return render_template('experiment_run.html', script=script.script_dict, filename=filename,
|
|
@@ -418,15 +427,15 @@ def experiment_run():
|
|
|
418
427
|
history=deck_list, pause_status=runner.pause_status())
|
|
419
428
|
|
|
420
429
|
|
|
421
|
-
@design.route("/
|
|
430
|
+
@design.route("/design/script/toggle/<stype>")
|
|
422
431
|
@login_required
|
|
423
432
|
def toggle_script_type(stype=None):
|
|
424
433
|
"""
|
|
425
434
|
.. :quickref: Workflow Design; toggle the experimental phase for design canvas.
|
|
426
435
|
|
|
427
|
-
.. http:get:: /
|
|
436
|
+
.. http:get:: /design/script/toggle/<stype>
|
|
428
437
|
|
|
429
|
-
:status 200: and then redirects to :http:get:`/
|
|
438
|
+
:status 200: and then redirects to :http:get:`/design/script`
|
|
430
439
|
|
|
431
440
|
"""
|
|
432
441
|
script = utils.get_script_file()
|
|
@@ -435,27 +444,37 @@ def toggle_script_type(stype=None):
|
|
|
435
444
|
return redirect(url_for('design.experiment_builder'))
|
|
436
445
|
|
|
437
446
|
|
|
438
|
-
@design.route("/updateList", methods=['
|
|
447
|
+
@design.route("/updateList", methods=['POST'])
|
|
439
448
|
@login_required
|
|
440
449
|
def update_list():
|
|
441
450
|
order = request.form['order']
|
|
442
451
|
script = utils.get_script_file()
|
|
443
452
|
script.currently_editing_order = order.split(",", len(script.currently_editing_script))
|
|
453
|
+
script.sort_actions()
|
|
454
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
444
455
|
utils.post_script_file(script)
|
|
445
|
-
|
|
456
|
+
session['python_code'] = exec_string
|
|
457
|
+
|
|
458
|
+
return jsonify({'success': True})
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@design.route("/toggle_show_code", methods=["POST"])
|
|
462
|
+
def toggle_show_code():
|
|
463
|
+
session["show_code"] = not session.get("show_code", False)
|
|
464
|
+
return redirect(request.referrer or url_for("design.experiment_builder"))
|
|
446
465
|
|
|
447
466
|
|
|
448
467
|
# --------------------handle all the import/export and download/upload--------------------------
|
|
449
|
-
@design.route("/clear")
|
|
468
|
+
@design.route("/design/clear")
|
|
450
469
|
@login_required
|
|
451
470
|
def clear():
|
|
452
471
|
"""
|
|
453
472
|
.. :quickref: Workflow Design; clear the design canvas.
|
|
454
473
|
|
|
455
|
-
.. http:get:: /clear
|
|
474
|
+
.. http:get:: /design/clear
|
|
456
475
|
|
|
457
476
|
:form prompt: user's prompt
|
|
458
|
-
:status 200: clear canvas and then redirects to :http:get:`/
|
|
477
|
+
:status 200: clear canvas and then redirects to :http:get:`/design/script`
|
|
459
478
|
"""
|
|
460
479
|
deck = global_config.deck
|
|
461
480
|
pseudo_name = session.get("pseudo_deck", "")
|
|
@@ -471,16 +490,16 @@ def clear():
|
|
|
471
490
|
return redirect(url_for("design.experiment_builder"))
|
|
472
491
|
|
|
473
492
|
|
|
474
|
-
@design.route("/
|
|
493
|
+
@design.route("/design/import/pseudo", methods=['POST'])
|
|
475
494
|
@login_required
|
|
476
495
|
def import_pseudo():
|
|
477
496
|
"""
|
|
478
497
|
.. :quickref: Workflow Design; Import pseudo deck from deck history
|
|
479
498
|
|
|
480
|
-
.. http:post:: /
|
|
499
|
+
.. http:post:: /design/import/pseudo
|
|
481
500
|
|
|
482
501
|
:form pkl_name: pseudo deck name
|
|
483
|
-
:status 302: load pseudo deck and then redirects to :http:get:`/
|
|
502
|
+
:status 302: load pseudo deck and then redirects to :http:get:`/design/script`
|
|
484
503
|
"""
|
|
485
504
|
pkl_name = request.form.get('pkl_name')
|
|
486
505
|
script = utils.get_script_file()
|
|
@@ -494,16 +513,16 @@ def import_pseudo():
|
|
|
494
513
|
return redirect(url_for("design.experiment_builder"))
|
|
495
514
|
|
|
496
515
|
|
|
497
|
-
@design.route('/uploads', methods=['POST'])
|
|
516
|
+
@design.route('/design/uploads', methods=['POST'])
|
|
498
517
|
@login_required
|
|
499
518
|
def upload():
|
|
500
519
|
"""
|
|
501
520
|
.. :quickref: Workflow Execution; upload a workflow config file (.CSV)
|
|
502
521
|
|
|
503
|
-
.. http:post:: /uploads
|
|
522
|
+
.. http:post:: /design/uploads
|
|
504
523
|
|
|
505
524
|
:form file: workflow CSV config file
|
|
506
|
-
:status 302: save csv file and then redirects to :http:get:`/
|
|
525
|
+
:status 302: save csv file and then redirects to :http:get:`/design/campaign`
|
|
507
526
|
"""
|
|
508
527
|
if request.method == "POST":
|
|
509
528
|
f = request.files['file']
|
|
@@ -519,14 +538,20 @@ def upload():
|
|
|
519
538
|
return redirect(url_for("design.experiment_run"))
|
|
520
539
|
|
|
521
540
|
|
|
522
|
-
@design.route('/
|
|
541
|
+
@design.route('/design/workflow/download/<filename>')
|
|
523
542
|
@login_required
|
|
524
543
|
def download_results(filename):
|
|
544
|
+
"""
|
|
545
|
+
.. :quickref: Workflow Design; download a workflow data file
|
|
546
|
+
|
|
547
|
+
.. http:get:: /design/workflow/download/<filename>
|
|
548
|
+
|
|
549
|
+
"""
|
|
525
550
|
filepath = os.path.join(current_app.config["DATA_FOLDER"], filename)
|
|
526
551
|
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
527
552
|
|
|
528
553
|
|
|
529
|
-
@design.route('/load_json', methods=['POST'])
|
|
554
|
+
@design.route('/design/load_json', methods=['POST'])
|
|
530
555
|
@login_required
|
|
531
556
|
def load_json():
|
|
532
557
|
"""
|
|
@@ -535,7 +560,7 @@ def load_json():
|
|
|
535
560
|
.. http:post:: /load_json
|
|
536
561
|
|
|
537
562
|
:form file: workflow design JSON file
|
|
538
|
-
:status 302: load pseudo deck and then redirects to :http:get:`/
|
|
563
|
+
:status 302: load pseudo deck and then redirects to :http:get:`/design/script`
|
|
539
564
|
"""
|
|
540
565
|
if request.method == "POST":
|
|
541
566
|
f = request.files['file']
|
|
@@ -549,9 +574,15 @@ def load_json():
|
|
|
549
574
|
return redirect(url_for("design.experiment_builder"))
|
|
550
575
|
|
|
551
576
|
|
|
552
|
-
@design.route('/download/<filetype>')
|
|
577
|
+
@design.route('/design/script/download/<filetype>')
|
|
553
578
|
@login_required
|
|
554
579
|
def download(filetype):
|
|
580
|
+
"""
|
|
581
|
+
.. :quickref: Workflow Design Ext; download a workflow design file
|
|
582
|
+
|
|
583
|
+
.. http:get:: /design/script/download/<filetype>
|
|
584
|
+
|
|
585
|
+
"""
|
|
555
586
|
script = utils.get_script_file()
|
|
556
587
|
run_name = script.name if script.name else "untitled"
|
|
557
588
|
if filetype == "configure":
|
|
@@ -569,40 +600,28 @@ def download(filetype):
|
|
|
569
600
|
outfile.write(json_object)
|
|
570
601
|
elif filetype == "python":
|
|
571
602
|
filepath = os.path.join(current_app.config["SCRIPT_FOLDER"], f"{run_name}.py")
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
class_definitions = {}
|
|
575
|
-
# Iterate through each instrument in the snapshot
|
|
576
|
-
for instrument_key, instrument_data in snapshot.items():
|
|
577
|
-
# Iterate through each function associated with the current instrument
|
|
578
|
-
for function_key, function_data in instrument_data.items():
|
|
579
|
-
# Convert the function signature to a string representation
|
|
580
|
-
function_data['signature'] = str(function_data['signature'])
|
|
581
|
-
class_name = instrument_key.split('.')[-1] # Extracting the class name from the path
|
|
582
|
-
class_definitions[class_name.capitalize()] = create_function(request.url_root, class_name, instrument_data)
|
|
583
|
-
# Export the generated class definitions to a .py script
|
|
584
|
-
export_to_python(class_definitions, current_app.config["OUTPUT_FOLDER"])
|
|
585
|
-
filepath = os.path.join(current_app.config["OUTPUT_FOLDER"], "generated_proxy.py")
|
|
603
|
+
else:
|
|
604
|
+
return "Unsupported file type", 400
|
|
586
605
|
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
587
606
|
|
|
588
607
|
|
|
589
|
-
@design.route("/edit/<uuid>", methods=['GET', 'POST'])
|
|
608
|
+
@design.route("/design/step/edit/<uuid>", methods=['GET', 'POST'])
|
|
590
609
|
@login_required
|
|
591
610
|
def edit_action(uuid: str):
|
|
592
611
|
"""
|
|
593
612
|
.. :quickref: Workflow Design; edit parameters of an action step on canvas
|
|
594
613
|
|
|
595
|
-
.. http:get:: /edit
|
|
614
|
+
.. http:get:: /design/step/edit/<uuid>
|
|
596
615
|
|
|
597
616
|
Load parameter form of an action step
|
|
598
617
|
|
|
599
|
-
.. http:post:: /edit
|
|
618
|
+
.. http:post:: /design/step/edit/<uuid>
|
|
600
619
|
|
|
601
620
|
:param uuid: The step's uuid
|
|
602
621
|
:type uuid: str
|
|
603
622
|
|
|
604
623
|
:form dynamic form: workflow step dynamic inputs
|
|
605
|
-
:status 302: save changes and then redirects to :http:get:`/
|
|
624
|
+
:status 302: save changes and then redirects to :http:get:`/design/script`
|
|
606
625
|
"""
|
|
607
626
|
script = utils.get_script_file()
|
|
608
627
|
action = script.find_by_uuid(uuid)
|
|
@@ -625,18 +644,18 @@ def edit_action(uuid: str):
|
|
|
625
644
|
return redirect(url_for('design.experiment_builder'))
|
|
626
645
|
|
|
627
646
|
|
|
628
|
-
@design.route("/delete/<id>")
|
|
647
|
+
@design.route("/design/step/delete/<id>")
|
|
629
648
|
@login_required
|
|
630
649
|
def delete_action(id: int):
|
|
631
650
|
"""
|
|
632
651
|
.. :quickref: Workflow Design; delete an action step on canvas
|
|
633
652
|
|
|
634
|
-
.. http:get:: /delete
|
|
653
|
+
.. http:get:: /design/step/delete/<id>
|
|
635
654
|
|
|
636
655
|
:param id: The step number id
|
|
637
656
|
:type id: int
|
|
638
657
|
|
|
639
|
-
:status 302: save changes and then redirects to :http:get:`/
|
|
658
|
+
:status 302: save changes and then redirects to :http:get:`/design/script`
|
|
640
659
|
"""
|
|
641
660
|
back = request.referrer
|
|
642
661
|
script = utils.get_script_file()
|
|
@@ -645,18 +664,18 @@ def delete_action(id: int):
|
|
|
645
664
|
return redirect(back)
|
|
646
665
|
|
|
647
666
|
|
|
648
|
-
@design.route("/duplicate/<id>")
|
|
667
|
+
@design.route("/design/step/duplicate/<id>")
|
|
649
668
|
@login_required
|
|
650
669
|
def duplicate_action(id: int):
|
|
651
670
|
"""
|
|
652
671
|
.. :quickref: Workflow Design; duplicate an action step on canvas
|
|
653
672
|
|
|
654
|
-
.. http:get:: /duplicate
|
|
673
|
+
.. http:get:: /design/step/duplicate/<id>
|
|
655
674
|
|
|
656
675
|
:param id: The step number id
|
|
657
676
|
:type id: int
|
|
658
677
|
|
|
659
|
-
:status 302: save changes and then redirects to :http:get:`/
|
|
678
|
+
:status 302: save changes and then redirects to :http:get:`/design/script`
|
|
660
679
|
"""
|
|
661
680
|
back = request.referrer
|
|
662
681
|
script = utils.get_script_file()
|
|
@@ -667,8 +686,15 @@ def duplicate_action(id: int):
|
|
|
667
686
|
|
|
668
687
|
# ---- HTTP API Endpoints ----
|
|
669
688
|
|
|
670
|
-
@design.route("/api/status", methods=["GET"])
|
|
689
|
+
@design.route("/api/runner/status", methods=["GET"])
|
|
671
690
|
def runner_status():
|
|
691
|
+
"""
|
|
692
|
+
.. :quickref: Workflow Design; get the execution status
|
|
693
|
+
|
|
694
|
+
.. http:get:: /api/runner/status
|
|
695
|
+
|
|
696
|
+
:status 200: status
|
|
697
|
+
"""
|
|
672
698
|
runner_busy = global_config.runner_lock.locked()
|
|
673
699
|
status = {"busy": runner_busy}
|
|
674
700
|
task_status = global_config.runner_status
|
|
@@ -692,39 +718,69 @@ def runner_status():
|
|
|
692
718
|
|
|
693
719
|
|
|
694
720
|
|
|
695
|
-
@design.route("/api/abort_pending", methods=["POST"])
|
|
721
|
+
@design.route("/api/runner/abort_pending", methods=["POST"])
|
|
696
722
|
def api_abort_pending():
|
|
723
|
+
"""
|
|
724
|
+
.. :quickref: Workflow Design; abort pending action(s) during execution
|
|
725
|
+
|
|
726
|
+
.. http:get:: /api/runner/abort_pending
|
|
727
|
+
|
|
728
|
+
:status 200: {"status": "ok"}
|
|
729
|
+
"""
|
|
697
730
|
abort_pending()
|
|
698
731
|
return jsonify({"status": "ok"}), 200
|
|
699
732
|
|
|
700
|
-
@design.route("/api/abort_current", methods=["POST"])
|
|
733
|
+
@design.route("/api/runner/abort_current", methods=["POST"])
|
|
701
734
|
def api_abort_current():
|
|
735
|
+
"""
|
|
736
|
+
.. :quickref: Workflow Design; abort right after current action during execution
|
|
737
|
+
|
|
738
|
+
.. http:get:: /api/runner/abort_current
|
|
739
|
+
|
|
740
|
+
:status 200: {"status": "ok"}
|
|
741
|
+
"""
|
|
702
742
|
abort_current()
|
|
703
743
|
return jsonify({"status": "ok"}), 200
|
|
704
744
|
|
|
705
|
-
@design.route("/api/pause", methods=["POST"])
|
|
745
|
+
@design.route("/api/runner/pause", methods=["POST"])
|
|
706
746
|
def api_pause():
|
|
747
|
+
"""
|
|
748
|
+
.. :quickref: Workflow Design; pause during execution
|
|
749
|
+
|
|
750
|
+
.. http:get:: /api/runner/pause
|
|
751
|
+
|
|
752
|
+
:status 200: {"status": "ok"}
|
|
753
|
+
"""
|
|
707
754
|
msg = pause()
|
|
708
755
|
return jsonify({"status": "ok", "pause_status": msg}), 200
|
|
709
756
|
|
|
710
|
-
@design.route("/api/retry", methods=["POST"])
|
|
757
|
+
@design.route("/api/runner/retry", methods=["POST"])
|
|
711
758
|
def api_retry():
|
|
759
|
+
"""
|
|
760
|
+
.. :quickref: Workflow Design; retry when error occur during execution
|
|
761
|
+
|
|
762
|
+
.. http:get:: /api/runner/retry
|
|
763
|
+
|
|
764
|
+
:status 200: {"status": "ok"}
|
|
765
|
+
"""
|
|
712
766
|
retry()
|
|
713
767
|
return jsonify({"status": "ok, retrying failed step"}), 200
|
|
714
768
|
|
|
715
769
|
|
|
716
|
-
@design.route("/api/
|
|
717
|
-
def
|
|
718
|
-
|
|
719
|
-
script
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
770
|
+
@design.route("/api/design/submit", methods=["POST"])
|
|
771
|
+
def submit_script():
|
|
772
|
+
"""
|
|
773
|
+
.. :quickref: Workflow Design; submit script
|
|
774
|
+
|
|
775
|
+
.. http:get:: /api/design/submit
|
|
776
|
+
|
|
777
|
+
:status 200: {"status": "ok"}
|
|
778
|
+
"""
|
|
779
|
+
deck = global_config.deck
|
|
780
|
+
deck_name = os.path.splitext(os.path.basename(deck.__file__))[0] if deck.__name__ == "__main__" else deck.__name__
|
|
781
|
+
script = Script(author=session.get('user'), deck=deck_name)
|
|
782
|
+
script_collection = request.get_json()
|
|
783
|
+
script.python_script = script_collection
|
|
784
|
+
# todo check script format
|
|
785
|
+
utils.post_script_file(script)
|
|
786
|
+
return jsonify({"status": "ok"}), 200
|