ivoryos 1.0.9__py3-none-any.whl → 1.4.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.
- docs/source/conf.py +84 -0
- ivoryos/__init__.py +17 -207
- ivoryos/app.py +154 -0
- ivoryos/config.py +1 -0
- ivoryos/optimizer/ax_optimizer.py +191 -0
- ivoryos/optimizer/base_optimizer.py +84 -0
- ivoryos/optimizer/baybe_optimizer.py +193 -0
- ivoryos/optimizer/nimo_optimizer.py +173 -0
- ivoryos/optimizer/registry.py +11 -0
- ivoryos/routes/auth/auth.py +43 -14
- ivoryos/routes/auth/templates/change_password.html +32 -0
- ivoryos/routes/control/control.py +101 -366
- ivoryos/routes/control/control_file.py +33 -0
- ivoryos/routes/control/control_new_device.py +152 -0
- ivoryos/routes/control/templates/controllers.html +193 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/control/utils.py +40 -0
- ivoryos/routes/data/data.py +197 -0
- ivoryos/routes/data/templates/components/step_card.html +78 -0
- ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +14 -8
- ivoryos/routes/data/templates/workflow_view.html +360 -0
- ivoryos/routes/design/__init__.py +4 -0
- ivoryos/routes/design/design.py +348 -657
- ivoryos/routes/design/design_file.py +68 -0
- ivoryos/routes/design/design_step.py +171 -0
- ivoryos/routes/design/templates/components/action_form.html +53 -0
- ivoryos/routes/design/templates/components/actions_panel.html +25 -0
- ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
- ivoryos/routes/design/templates/components/canvas.html +5 -0
- ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
- ivoryos/routes/design/templates/components/canvas_header.html +75 -0
- ivoryos/routes/design/templates/components/canvas_main.html +39 -0
- ivoryos/routes/design/templates/components/deck_selector.html +10 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +53 -0
- ivoryos/routes/design/templates/components/info_modal.html +318 -0
- ivoryos/routes/design/templates/components/instruments_panel.html +88 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
- ivoryos/routes/design/templates/components/modals.html +6 -0
- ivoryos/routes/design/templates/components/python_code_overlay.html +56 -0
- ivoryos/routes/design/templates/components/sidebar.html +15 -0
- ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos/routes/design/templates/experiment_builder.html +44 -0
- ivoryos/routes/execute/__init__.py +0 -0
- ivoryos/routes/execute/execute.py +377 -0
- ivoryos/routes/execute/execute_file.py +78 -0
- ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos/routes/execute/templates/components/logging_panel.html +56 -0
- ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
- ivoryos/routes/execute/templates/components/run_panel.html +9 -0
- ivoryos/routes/execute/templates/components/run_tabs.html +60 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +520 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +383 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +18 -0
- ivoryos/routes/execute/templates/experiment_run.html +30 -0
- ivoryos/routes/library/__init__.py +0 -0
- ivoryos/routes/library/library.py +157 -0
- ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +32 -23
- ivoryos/routes/main/main.py +31 -3
- ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
- ivoryos/server.py +180 -0
- ivoryos/socket_handlers.py +52 -0
- ivoryos/static/ivoryos_logo.png +0 -0
- ivoryos/static/js/action_handlers.js +384 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/socket_handler.js +40 -5
- ivoryos/static/js/sortable_design.js +107 -56
- ivoryos/static/js/ui_state.js +114 -0
- ivoryos/templates/base.html +67 -8
- ivoryos/utils/bo_campaign.py +180 -3
- ivoryos/utils/client_proxy.py +267 -36
- ivoryos/utils/db_models.py +300 -65
- ivoryos/utils/decorators.py +34 -0
- ivoryos/utils/form.py +63 -29
- ivoryos/utils/global_config.py +34 -1
- ivoryos/utils/nest_script.py +314 -0
- ivoryos/utils/py_to_json.py +295 -0
- ivoryos/utils/script_runner.py +599 -165
- ivoryos/utils/serilize.py +201 -0
- ivoryos/utils/task_runner.py +71 -21
- ivoryos/utils/utils.py +50 -6
- ivoryos/version.py +1 -1
- ivoryos-1.4.4.dist-info/METADATA +263 -0
- ivoryos-1.4.4.dist-info/RECORD +119 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/WHEEL +1 -1
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/top_level.txt +1 -0
- tests/unit/test_type_conversion.py +42 -0
- tests/unit/test_util.py +3 -0
- ivoryos/routes/control/templates/control/controllers.html +0 -78
- ivoryos/routes/control/templates/control/controllers_home.html +0 -55
- ivoryos/routes/control/templates/control/controllers_new.html +0 -89
- ivoryos/routes/database/database.py +0 -306
- ivoryos/routes/database/templates/database/step_card.html +0 -7
- ivoryos/routes/database/templates/database/workflow_view.html +0 -130
- ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
- ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- ivoryos-1.0.9.dist-info/METADATA +0 -218
- ivoryos-1.0.9.dist-info/RECORD +0 -61
- /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
- /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
- /ivoryos/routes/{database → data}/__init__.py +0 -0
- /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from flask import Blueprint, send_file, request, flash, redirect, url_for, current_app
|
|
5
|
+
from werkzeug.utils import secure_filename
|
|
6
|
+
from ivoryos.utils import utils
|
|
7
|
+
|
|
8
|
+
files = Blueprint('design_files', __name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@files.route('/files/script-json', methods=['POST'])
|
|
13
|
+
def load_json():
|
|
14
|
+
"""
|
|
15
|
+
.. :quickref: Workflow Design Ext; upload a workflow design file (.JSON)
|
|
16
|
+
|
|
17
|
+
.. http:post:: /files/script-json
|
|
18
|
+
|
|
19
|
+
:form file: workflow design JSON file
|
|
20
|
+
:status 302: load script json and then redirects to :http:get:`/ivoryos/draft`
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
if request.method == "POST":
|
|
24
|
+
f = request.files['file']
|
|
25
|
+
if 'file' not in request.files:
|
|
26
|
+
flash('No file part')
|
|
27
|
+
if f.filename.endswith("json"):
|
|
28
|
+
script_dict = json.load(f)
|
|
29
|
+
utils.post_script_file(script_dict, is_dict=True)
|
|
30
|
+
else:
|
|
31
|
+
flash("Script file need to be JSON file")
|
|
32
|
+
return redirect(url_for("design.experiment_builder"))
|
|
33
|
+
|
|
34
|
+
@files.route('/files/script-python')
|
|
35
|
+
def download_python():
|
|
36
|
+
"""
|
|
37
|
+
.. :quickref: Workflow Design Ext; export a workflow design file (.py)
|
|
38
|
+
|
|
39
|
+
.. http:post:: /files/script-python
|
|
40
|
+
|
|
41
|
+
:status 302: redirects to :http:get:`/ivoryos/draft`
|
|
42
|
+
"""
|
|
43
|
+
script = utils.get_script_file()
|
|
44
|
+
run_name = script.name if script.name else "untitled"
|
|
45
|
+
filepath = os.path.join(current_app.config["SCRIPT_FOLDER"], f"{run_name}.py")
|
|
46
|
+
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@files.route('/files/script-json')
|
|
50
|
+
def download_json():
|
|
51
|
+
"""
|
|
52
|
+
.. :quickref: Workflow Design Ext; export a workflow design file (.JSON)
|
|
53
|
+
|
|
54
|
+
.. http:post:: /files/script-json
|
|
55
|
+
|
|
56
|
+
:status 302: redirects to :http:get:`/ivoryos/draft`
|
|
57
|
+
"""
|
|
58
|
+
script = utils.get_script_file()
|
|
59
|
+
run_name = script.name if script.name else "untitled"
|
|
60
|
+
|
|
61
|
+
script.sort_actions()
|
|
62
|
+
json_object = json.dumps(script.as_dict())
|
|
63
|
+
filepath = os.path.join(current_app.config['SCRIPT_FOLDER'], f"{run_name}.json")
|
|
64
|
+
with open(filepath, "w") as outfile:
|
|
65
|
+
outfile.write(json_object)
|
|
66
|
+
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
67
|
+
|
|
68
|
+
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from flask import Blueprint, request, session, flash, redirect, url_for, jsonify, render_template, current_app
|
|
2
|
+
from flask_login import login_required
|
|
3
|
+
|
|
4
|
+
from ivoryos.utils import utils
|
|
5
|
+
from ivoryos.utils.form import create_form_from_action, create_action_button
|
|
6
|
+
|
|
7
|
+
steps = Blueprint('design_steps', __name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@steps.get("/draft/steps/<int:uuid>")
|
|
11
|
+
def get_step(uuid: int):
|
|
12
|
+
"""
|
|
13
|
+
.. :quickref: Workflow Design Steps; get an action step editing form
|
|
14
|
+
|
|
15
|
+
.. http:get:: /draft/steps/<int:uuid>
|
|
16
|
+
|
|
17
|
+
get the editing form for an action step
|
|
18
|
+
|
|
19
|
+
:param uuid: The step number id
|
|
20
|
+
:type uuid: int
|
|
21
|
+
|
|
22
|
+
:status 200: render template with action step form
|
|
23
|
+
"""
|
|
24
|
+
script = utils.get_script_file()
|
|
25
|
+
action = script.find_by_uuid(uuid)
|
|
26
|
+
if action is None:
|
|
27
|
+
return jsonify({"warning": "Step not found, please refresh the page."}), 404
|
|
28
|
+
|
|
29
|
+
elif request.method == 'GET':
|
|
30
|
+
forms = create_form_from_action(action, script=script)
|
|
31
|
+
# session['edit_action'] = action
|
|
32
|
+
return render_template("components/edit_action_form.html",
|
|
33
|
+
action=action,
|
|
34
|
+
forms=forms)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@steps.post("/draft/steps/<int:uuid>")
|
|
39
|
+
def save_step(uuid: int):
|
|
40
|
+
"""
|
|
41
|
+
.. :quickref: Workflow Design Steps; save an action step on canvas
|
|
42
|
+
|
|
43
|
+
.. http:post:: /draft/steps/<int:uuid>
|
|
44
|
+
|
|
45
|
+
save the changes of an action step
|
|
46
|
+
|
|
47
|
+
:param uuid: The step number id
|
|
48
|
+
:type uuid: int
|
|
49
|
+
|
|
50
|
+
:status 200: render template with action step form
|
|
51
|
+
"""
|
|
52
|
+
script = utils.get_script_file()
|
|
53
|
+
action = script.find_by_uuid(uuid)
|
|
54
|
+
warning = None
|
|
55
|
+
if action is not None:
|
|
56
|
+
forms = create_form_from_action(action, script=script)
|
|
57
|
+
kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
|
|
58
|
+
if forms and forms.validate_on_submit():
|
|
59
|
+
save_as = kwargs.pop('return', '')
|
|
60
|
+
batch_action = kwargs.pop('batch_action', False)
|
|
61
|
+
kwargs = script.validate_variables(kwargs)
|
|
62
|
+
script.update_by_uuid(uuid=uuid, args=kwargs, output=save_as, batch_action=batch_action)
|
|
63
|
+
else:
|
|
64
|
+
warning = f"Compilation failed: {str(forms.errors)}"
|
|
65
|
+
utils.post_script_file(script)
|
|
66
|
+
try:
|
|
67
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
68
|
+
except Exception as e:
|
|
69
|
+
exec_string = {}
|
|
70
|
+
warning = f"Compilation failed: {str(e)}"
|
|
71
|
+
session['python_code'] = exec_string
|
|
72
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
73
|
+
return render_template("components/canvas_main.html",
|
|
74
|
+
script=script,
|
|
75
|
+
buttons_dict=design_buttons,
|
|
76
|
+
warning=warning)
|
|
77
|
+
|
|
78
|
+
@steps.delete("/draft/steps/<int:uuid>")
|
|
79
|
+
def delete_step(uuid: int):
|
|
80
|
+
"""
|
|
81
|
+
.. :quickref: Workflow Design Steps; delete an action step on canvas
|
|
82
|
+
|
|
83
|
+
.. http:delete:: /draft/steps/<int:uuid>
|
|
84
|
+
|
|
85
|
+
delete an action step
|
|
86
|
+
|
|
87
|
+
:param uuid: The step number id
|
|
88
|
+
:type uuid: int
|
|
89
|
+
|
|
90
|
+
:status 200: render template with action step form
|
|
91
|
+
"""
|
|
92
|
+
script = utils.get_script_file()
|
|
93
|
+
if request.method == 'DELETE':
|
|
94
|
+
script.delete_action(uuid)
|
|
95
|
+
utils.post_script_file(script)
|
|
96
|
+
warning = None
|
|
97
|
+
try:
|
|
98
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
99
|
+
except Exception as e:
|
|
100
|
+
exec_string = {}
|
|
101
|
+
warning = f"Compilation failed: {str(e)}"
|
|
102
|
+
session['python_code'] = exec_string
|
|
103
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
104
|
+
return render_template("components/canvas_main.html",
|
|
105
|
+
script=script,
|
|
106
|
+
buttons_dict=design_buttons, warning=warning)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@steps.route("/draft/steps/<int:uuid>/duplicate", methods=["POST"], strict_slashes=False,)
|
|
110
|
+
def duplicate_action(uuid: int):
|
|
111
|
+
"""
|
|
112
|
+
.. :quickref: Workflow Design Steps; duplicate an action step on canvas
|
|
113
|
+
|
|
114
|
+
.. http:post:: /draft/steps/<int:uuid>/duplicate
|
|
115
|
+
|
|
116
|
+
:param uuid: The step number uuid
|
|
117
|
+
:type uuid: int
|
|
118
|
+
|
|
119
|
+
:status 200: render new design script template
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
# back = request.referrer
|
|
123
|
+
script = utils.get_script_file()
|
|
124
|
+
script.duplicate_action(uuid)
|
|
125
|
+
utils.post_script_file(script)
|
|
126
|
+
warning = None
|
|
127
|
+
try:
|
|
128
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
129
|
+
except Exception as e:
|
|
130
|
+
exec_string = {}
|
|
131
|
+
warning = f"Compilation failed: {str(e)}"
|
|
132
|
+
session['python_code'] = exec_string
|
|
133
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
134
|
+
|
|
135
|
+
return render_template("components/canvas_main.html",
|
|
136
|
+
script=script,
|
|
137
|
+
buttons_dict=design_buttons, warning=warning)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@steps.route("/draft/steps/order", methods=['POST'])
|
|
141
|
+
@login_required
|
|
142
|
+
def update_list():
|
|
143
|
+
"""
|
|
144
|
+
.. :quickref: Workflow Design Steps; update the order of steps in the design canvas when reordering steps.
|
|
145
|
+
|
|
146
|
+
.. http:post:: /draft/steps/order
|
|
147
|
+
|
|
148
|
+
Update the order of steps in the design canvas when reordering steps.
|
|
149
|
+
|
|
150
|
+
:form order: A comma-separated string representing the new order of steps.
|
|
151
|
+
:status 200: Successfully updated the order of steps.
|
|
152
|
+
"""
|
|
153
|
+
order = request.form['order']
|
|
154
|
+
script = utils.get_script_file()
|
|
155
|
+
script.currently_editing_order = order.split(",", len(script.currently_editing_script))
|
|
156
|
+
script.sort_actions()
|
|
157
|
+
warning = None
|
|
158
|
+
|
|
159
|
+
utils.post_script_file(script)
|
|
160
|
+
try:
|
|
161
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
162
|
+
except Exception as e:
|
|
163
|
+
exec_string = {}
|
|
164
|
+
warning = f"Compilation failed: {str(e)}"
|
|
165
|
+
session['python_code'] = exec_string
|
|
166
|
+
|
|
167
|
+
# Return the updated canvas HTML instead of JSON
|
|
168
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
169
|
+
return render_template("components/canvas_main.html",
|
|
170
|
+
script=script,
|
|
171
|
+
buttons_dict=design_buttons, warning=warning)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{# Action form component #}
|
|
2
|
+
<div class="accordion-item design-control">
|
|
3
|
+
<h2 class="accordion-header" >
|
|
4
|
+
<button class="accordion-button collapsed draggable-action"
|
|
5
|
+
type="button" data-bs-toggle="collapse"
|
|
6
|
+
data-bs-target="#{{name}}" aria-expanded="false"
|
|
7
|
+
aria-controls="collapseExample"
|
|
8
|
+
data-action="{{ name }}">
|
|
9
|
+
{{ name | format_name }}
|
|
10
|
+
</button>
|
|
11
|
+
</h2>
|
|
12
|
+
<div id="{{name}}" class="accordion-collapse collapse" data-bs-parent="#accordionActions">
|
|
13
|
+
<div class="accordion-body">
|
|
14
|
+
<form role="form" method='POST' action="{{ url_for('design.methods_handler', instrument=instrument) }}"
|
|
15
|
+
name="add" id="add-{{name}}" onsubmit="addMethodToDesign(event, this); return false;">
|
|
16
|
+
<div class="form-group">
|
|
17
|
+
{{ form.hidden_tag() }}
|
|
18
|
+
{% for field in form %}
|
|
19
|
+
{% if field.type not in ['CSRFTokenField', 'HiddenField'] %}
|
|
20
|
+
<div class="input-group mb-3">
|
|
21
|
+
<label class="input-group-text">{{ field.label.text | format_name }}</label>
|
|
22
|
+
{% if field.type == "SubmitField" %}
|
|
23
|
+
{{ field(class="btn btn-dark") }}
|
|
24
|
+
{% elif field.type == "BooleanField" %}
|
|
25
|
+
{{ field(class="form-check-input") }}
|
|
26
|
+
{% elif field.type == "FlexibleEnumField" %}
|
|
27
|
+
<input type="text" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.data }}"
|
|
28
|
+
list="{{ field.id }}_options" placeholder="{{ field.render_kw.placeholder if field.render_kw and field.render_kw.placeholder }}"
|
|
29
|
+
class="form-control">
|
|
30
|
+
<datalist id="{{ field.id }}_options">
|
|
31
|
+
{% for key in field.choices %}
|
|
32
|
+
<option value="{{ key }}">{{ key }}</option>
|
|
33
|
+
{% endfor %}
|
|
34
|
+
</datalist>
|
|
35
|
+
{% else %}
|
|
36
|
+
{{ field(class="form-control") }}
|
|
37
|
+
{% endif %}
|
|
38
|
+
</div>
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% endfor %}
|
|
41
|
+
</div>
|
|
42
|
+
<button type="submit" class="btn btn-dark">Add</button>
|
|
43
|
+
{% if 'hidden_name' in form %}
|
|
44
|
+
<i class="bi bi-info-circle ms-2" data-bs-toggle="tooltip" data-bs-placement="top"
|
|
45
|
+
title='{{ form.hidden_name.description or "Docstring is not available" }}'>
|
|
46
|
+
</i>
|
|
47
|
+
{% else %}
|
|
48
|
+
<!-- handle info tooltip for flow control / workflows -->
|
|
49
|
+
{% endif %}
|
|
50
|
+
</form>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{# Instrument panel component #}
|
|
2
|
+
<div>
|
|
3
|
+
<div class="d-flex justify-content-between align-items-center " style="margin-bottom: 1vh;margin-top: 1vh;">
|
|
4
|
+
{# <a class="btn btn-primary" role="button" type="button" href="{{url_for('design.experiment_builder')}}"><i class="bi bi-arrow-return-left"></i></a>#}
|
|
5
|
+
<a type="button"
|
|
6
|
+
data-get-url="{{ url_for('design.get_operation_sidebar') }}"
|
|
7
|
+
onclick="updateInstrumentPanel(this)">
|
|
8
|
+
<i class="bi bi-arrow-return-left"></i>
|
|
9
|
+
</a>
|
|
10
|
+
{{ instrument | format_name }}
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
{% if script.editing_type == "script" %}
|
|
14
|
+
{# Auto Fill Toggle #}
|
|
15
|
+
{% include 'components/autofill_toggle.html' %}
|
|
16
|
+
{% endif %}
|
|
17
|
+
|
|
18
|
+
{# accordion for instrument #}
|
|
19
|
+
<div class="accordion accordion-flush" id="accordionActions" >
|
|
20
|
+
|
|
21
|
+
{% for name, form in forms.items() %}
|
|
22
|
+
{% include 'components/action_form.html' %}
|
|
23
|
+
{% endfor %}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{# Autofill toggle component #}
|
|
2
|
+
<div class="d-flex justify-content-between align-items-center " style="margin-bottom: 1vh;margin-top: 1vh;">
|
|
3
|
+
<div class="form-check form-switch" data-instrument="{{ instrument }}">
|
|
4
|
+
{# <input type="hidden" id="autofill" name="autofill" value="temp_value">#}
|
|
5
|
+
<input class="form-check-input" type="checkbox" id="autoFillCheck" name="autoFillCheck" onchange="toggleAutoFill()"
|
|
6
|
+
value="temp_value"
|
|
7
|
+
{{ "checked" if session["autofill"] else "" }}>
|
|
8
|
+
<label class="form-check-label" for="autoFillCheck">Auto fill</label>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{# Canvas footer component #}
|
|
2
|
+
<div>
|
|
3
|
+
<a class="btn btn-dark {{ 'disabled' if not script.name or script.status == "finalized" else '' }}"
|
|
4
|
+
href="#" data-post-url="{{ url_for('library.workflow_script', script_name=script.name or 'untitled') }}"
|
|
5
|
+
onclick="saveWorkflow(this); return false;">
|
|
6
|
+
Quick Save
|
|
7
|
+
</a>
|
|
8
|
+
<a class="btn btn-dark " href="{{ url_for('execute.experiment_run') }}">Compile and Run</a>
|
|
9
|
+
</div>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{# Canvas header component #}
|
|
2
|
+
<div class="d-flex align-items-center justify-content-between">
|
|
3
|
+
<!-- Left: Script Info -->
|
|
4
|
+
<div>
|
|
5
|
+
<span class="fw-bold">{{ script.name or "Untitled Script" }}</span>
|
|
6
|
+
<span class="badge bg-secondary">{{ script.status }}</span>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<!-- Center: Main Actions -->
|
|
10
|
+
<div class="btn-toolbar" role="toolbar">
|
|
11
|
+
<button class="btn btn-primary btn-sm me-1" data-bs-toggle="modal" data-bs-target="#newScriptModal">New</button>
|
|
12
|
+
<button class="btn btn-secondary btn-sm me-1" data-bs-toggle="modal" data-bs-target="#jsonModal">Import</button>
|
|
13
|
+
<a class="btn btn-secondary btn-sm me-1" href="{{url_for('design.design_files.download_json', filetype='script')}}">Export</a>
|
|
14
|
+
<button class="btn btn-secondary btn-sm me-1" data-bs-toggle="modal" data-bs-target="#saveasModal">Save as</button>
|
|
15
|
+
<div class="btn-group">
|
|
16
|
+
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">More</button>
|
|
17
|
+
<ul class="dropdown-menu">
|
|
18
|
+
<li>
|
|
19
|
+
<button class="dropdown-item" data-bs-toggle="modal" data-bs-target="#renameModal">Rename</button>
|
|
20
|
+
</li>
|
|
21
|
+
{% if script.name %}
|
|
22
|
+
<li>
|
|
23
|
+
<a class="dropdown-item {{ 'disabled' if not script.name or script.status == "finalized" else '' }}"
|
|
24
|
+
href="#" data-post-url="{{ url_for('library.workflow_script', script_name=script.name) }}"
|
|
25
|
+
onclick="saveWorkflow(this); return false;">
|
|
26
|
+
Save
|
|
27
|
+
</a>
|
|
28
|
+
</li>
|
|
29
|
+
{% endif %}
|
|
30
|
+
{% if not script.status == 'finalized' %}
|
|
31
|
+
<li>
|
|
32
|
+
<a class="dropdown-item" onclick="lockScriptEditing()">Disable editing</a>
|
|
33
|
+
</li>
|
|
34
|
+
{% endif %}
|
|
35
|
+
</ul>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Tabs for Info/Prep/Experiment/Cleanup below header -->
|
|
41
|
+
<div class="d-flex align-items-center mt-2 justify-content-between">
|
|
42
|
+
<ul class="nav nav-tabs mb-0">
|
|
43
|
+
<li class="nav-item">
|
|
44
|
+
<a class="nav-link {{ 'active' if script.editing_type == 'prep' else '' }}"
|
|
45
|
+
href="#" onclick="setScriptPhase('prep')">
|
|
46
|
+
Prep
|
|
47
|
+
</a>
|
|
48
|
+
</li>
|
|
49
|
+
<li class="nav-item">
|
|
50
|
+
<a class="nav-link {{ 'active' if script.editing_type == 'script' else '' }}"
|
|
51
|
+
href="#" onclick="setScriptPhase('script')">
|
|
52
|
+
Experiment
|
|
53
|
+
</a>
|
|
54
|
+
</li>
|
|
55
|
+
<li class="nav-item">
|
|
56
|
+
<a class="nav-link {{ 'active' if script.editing_type == 'cleanup' else '' }}"
|
|
57
|
+
href="#" onclick="setScriptPhase('cleanup')">
|
|
58
|
+
Clean up
|
|
59
|
+
</a>
|
|
60
|
+
</li>
|
|
61
|
+
</ul>
|
|
62
|
+
|
|
63
|
+
<div class="d-flex align-items-center ms-auto">
|
|
64
|
+
<div class="form-check form-switch">
|
|
65
|
+
<input class="form-check-input" type="checkbox" id="showPythonCodeSwitch" name="show_code"
|
|
66
|
+
onchange="toggleCodeOverlay()" {% if session.get('show_code') %}checked{% endif %}>
|
|
67
|
+
<label class="form-check-label" for="showPythonCodeSwitch">Preview code</label>
|
|
68
|
+
</div>
|
|
69
|
+
{# <div class="form-check form-switch">#}
|
|
70
|
+
{# <input class="form-check-input" type="checkbox" id="toggleLineNumbers"#}
|
|
71
|
+
{# onchange="toggleLineNumbers()">#}
|
|
72
|
+
{# <label class="form-check-label" for="toggleLineNumbers">Show Line Numbers</label>#}
|
|
73
|
+
{# </div>#}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{# Action list component #}
|
|
2
|
+
|
|
3
|
+
<!-- partials/canvas_content.html -->
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
<div class="list-group" id="list" style="margin-top: 20px">
|
|
7
|
+
<ul class="reorder">
|
|
8
|
+
|
|
9
|
+
{% if script.editing_type == "prep" %}
|
|
10
|
+
{% set buttons = buttons_dict["prep"] or [] %}
|
|
11
|
+
{% elif script.editing_type == "script" %}
|
|
12
|
+
{% set buttons = buttons_dict["script"] or [] %}
|
|
13
|
+
{% elif script.editing_type == "cleanup" %}
|
|
14
|
+
{% set buttons = buttons_dict["cleanup"] or [] %}
|
|
15
|
+
{% endif %}
|
|
16
|
+
|
|
17
|
+
{% for button in buttons %}
|
|
18
|
+
<li id="{{ button['id'] }}" style="list-style-type: none;">
|
|
19
|
+
<span class="line-number d-none">{{ button['id'] }}.</span>
|
|
20
|
+
<a href="#" onclick="editAction('{{ button['uuid'] }}'); return false;" type="button" class="btn btn-light" style="{{ button['style'] }}">{{ button['label'] }}</a>
|
|
21
|
+
{% if not button["instrument"] in ["if","while","repeat"] %}
|
|
22
|
+
<button onclick="duplicateAction('{{ button['id'] }}')" type="button" class="btn btn-light"><span class="bi bi-copy"></span></button>
|
|
23
|
+
|
|
24
|
+
{% endif %}
|
|
25
|
+
<button onclick="deleteAction('{{ button['id'] }}')" type="button" class="btn btn-light">
|
|
26
|
+
<span class="bi bi-trash"></span>
|
|
27
|
+
</button>
|
|
28
|
+
</li>
|
|
29
|
+
{% endfor %}
|
|
30
|
+
</ul>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="python-code-wrapper" id="python-code-wrapper">
|
|
33
|
+
{% include 'components/python_code_overlay.html' %}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
{% if warning %}
|
|
38
|
+
<div id="warning" style="display:none;">{{ warning }}</div>
|
|
39
|
+
{% endif %}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{# Deck selector component #}
|
|
2
|
+
<div class="input-group mb-3">
|
|
3
|
+
<select class="form-select" name="deck_name" id="deck_name" required onchange="changeDeck(this.value);">
|
|
4
|
+
<option {{ '' if 'pseudo_deck' in session else 'selected' }} disabled hidden style="overflow-wrap: break-word;" name="deck_name" id="deck_name" value=""> -- choose deck --</option>
|
|
5
|
+
{% for connection in history %}
|
|
6
|
+
<option {{ 'selected' if session['pseudo_deck']==connection else '' }} style="overflow-wrap: break-word;" name="deck_name" id="deck_name" value="{{connection}}">{{connection.split('.')[0]}}</option>
|
|
7
|
+
{% endfor %}
|
|
8
|
+
</select>
|
|
9
|
+
</div>
|
|
10
|
+
<hr>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{# Edit action form component #}
|
|
2
|
+
<div style="margin-bottom: 1vh;margin-top: 1vh;">
|
|
3
|
+
<a type="button"
|
|
4
|
+
data-get-url="{{ url_for('design.get_operation_sidebar') }}"
|
|
5
|
+
onclick="updateInstrumentPanel(this)">
|
|
6
|
+
<i class="bi bi-arrow-return-left"></i>
|
|
7
|
+
</a>
|
|
8
|
+
</div>
|
|
9
|
+
{% if action %}
|
|
10
|
+
<h5> {{ action['action'] | format_name }} </h5>
|
|
11
|
+
<form role="form" method='POST' name="{{instrument}}"
|
|
12
|
+
action="{{ url_for('design.design_steps.get_step', uuid=action['uuid']) }}"
|
|
13
|
+
onsubmit="submitEditForm(event); return false;">
|
|
14
|
+
{% if not action['args'] == None %}
|
|
15
|
+
<div class="form-group">
|
|
16
|
+
{% if not action['args'].__class__.__name__ == 'dict' %}
|
|
17
|
+
<div class="input-group mb-3">
|
|
18
|
+
<label class="input-group-text">{{ action['action'] }}</label>
|
|
19
|
+
<input class="form-control" type="text" id="arg" name="arg"
|
|
20
|
+
placeholder="{{ action['arg_types']}}" value="{{ action['args'] }}"
|
|
21
|
+
aria-labelledby="variableHelpBlock">
|
|
22
|
+
</div>
|
|
23
|
+
{% else %}
|
|
24
|
+
{{ forms.hidden_tag() }}
|
|
25
|
+
{% for field in forms %}
|
|
26
|
+
|
|
27
|
+
{% if field.type not in ['CSRFTokenField'] %}
|
|
28
|
+
<div class="input-group mb-3">
|
|
29
|
+
<label class="input-group-text">{{ field.label.text | format_name }}</label>
|
|
30
|
+
{% if field.type == "BooleanField" %}
|
|
31
|
+
{{ field(class="form-check-input") }}
|
|
32
|
+
{% elif field.type == "FlexibleEnumField" %}
|
|
33
|
+
<input type="text" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.data }}"
|
|
34
|
+
list="{{ field.id }}_options" placeholder="{{ field.render_kw.placeholder if field.render_kw and field.render_kw.placeholder }}"
|
|
35
|
+
class="form-control">
|
|
36
|
+
<datalist id="{{ field.id }}_options">
|
|
37
|
+
{% for key in field.choices %}
|
|
38
|
+
<option value="{{ key }}">{{ key }}</option>
|
|
39
|
+
{% endfor %}
|
|
40
|
+
</datalist>
|
|
41
|
+
{% else %}
|
|
42
|
+
{{ field(class="form-control") }}
|
|
43
|
+
{% endif %}
|
|
44
|
+
</div>
|
|
45
|
+
{% endif %}
|
|
46
|
+
{% endfor %}
|
|
47
|
+
{% endif %}
|
|
48
|
+
</div>
|
|
49
|
+
{% endif %}
|
|
50
|
+
<button type="submit" class="btn btn-primary">Save</button>
|
|
51
|
+
<button type="button" class="btn btn-primary" id="back">Back</button>
|
|
52
|
+
</form>
|
|
53
|
+
{% endif %}
|