ivoryos 1.1.0__py3-none-any.whl → 1.2.0__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 +12 -5
- ivoryos/routes/api/api.py +5 -58
- ivoryos/routes/control/control.py +46 -43
- ivoryos/routes/control/control_file.py +4 -4
- ivoryos/routes/control/control_new_device.py +10 -10
- ivoryos/routes/control/templates/controllers.html +38 -9
- ivoryos/routes/control/templates/controllers_new.html +1 -1
- ivoryos/routes/data/data.py +81 -60
- ivoryos/routes/data/templates/components/step_card.html +9 -3
- ivoryos/routes/data/templates/workflow_database.html +10 -4
- ivoryos/routes/design/design.py +306 -243
- ivoryos/routes/design/design_file.py +42 -31
- ivoryos/routes/design/design_step.py +132 -30
- ivoryos/routes/design/templates/components/action_form.html +4 -3
- ivoryos/routes/design/templates/components/{instrument_panel.html → actions_panel.html} +7 -5
- ivoryos/routes/design/templates/components/autofill_toggle.html +8 -12
- ivoryos/routes/design/templates/components/canvas.html +5 -14
- ivoryos/routes/design/templates/components/canvas_footer.html +5 -1
- ivoryos/routes/design/templates/components/canvas_header.html +36 -15
- ivoryos/routes/design/templates/components/canvas_main.html +34 -0
- ivoryos/routes/design/templates/components/deck_selector.html +8 -10
- ivoryos/routes/design/templates/components/edit_action_form.html +16 -7
- ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +3 -5
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +1 -2
- ivoryos/routes/design/templates/components/modals/rename_modal.html +5 -5
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +2 -2
- ivoryos/routes/design/templates/components/python_code_overlay.html +26 -4
- ivoryos/routes/design/templates/components/sidebar.html +12 -13
- ivoryos/routes/design/templates/experiment_builder.html +20 -20
- ivoryos/routes/execute/execute.py +157 -13
- ivoryos/routes/execute/execute_file.py +38 -4
- ivoryos/routes/execute/templates/components/tab_bayesian.html +365 -114
- ivoryos/routes/execute/templates/components/tab_configuration.html +1 -1
- ivoryos/routes/library/library.py +70 -115
- ivoryos/routes/library/templates/library.html +27 -19
- ivoryos/static/js/action_handlers.js +213 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/sortable_design.js +89 -56
- ivoryos/static/js/ui_state.js +113 -0
- ivoryos/utils/bo_campaign.py +137 -1
- ivoryos/utils/db_models.py +14 -5
- ivoryos/utils/form.py +4 -9
- ivoryos/utils/global_config.py +13 -1
- ivoryos/utils/script_runner.py +24 -5
- ivoryos/utils/serilize.py +203 -0
- ivoryos/utils/task_runner.py +4 -1
- ivoryos/version.py +1 -1
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/METADATA +1 -1
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/RECORD +54 -51
- ivoryos/routes/design/templates/components/action_list.html +0 -15
- ivoryos/routes/design/templates/components/operations_panel.html +0 -43
- ivoryos/routes/design/templates/components/script_info.html +0 -31
- ivoryos/routes/design/templates/components/scripts.html +0 -50
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/LICENSE +0 -0
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/WHEEL +0 -0
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/top_level.txt +0 -0
ivoryos/routes/design/design.py
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
-
from flask import Blueprint, redirect, url_for, flash, jsonify, request, render_template, session,
|
|
4
|
-
current_app
|
|
3
|
+
from flask import Blueprint, redirect, url_for, flash, jsonify, request, render_template, session, current_app
|
|
5
4
|
from flask_login import login_required
|
|
6
5
|
|
|
7
6
|
from ivoryos.routes.library.library import publish
|
|
8
7
|
from ivoryos.utils import utils
|
|
9
8
|
from ivoryos.utils.global_config import GlobalConfig
|
|
10
|
-
from ivoryos.utils.form import create_action_button,
|
|
11
|
-
create_form_from_action, create_all_builtin_forms
|
|
9
|
+
from ivoryos.utils.form import create_action_button, create_form_from_pseudo, create_all_builtin_forms
|
|
12
10
|
from ivoryos.utils.db_models import Script
|
|
13
11
|
from ivoryos.utils.py_to_json import convert_to_cards
|
|
14
|
-
from ivoryos.utils.script_runner import ScriptRunner
|
|
15
12
|
|
|
16
13
|
# Import the new modular components
|
|
17
14
|
from ivoryos.routes.design.design_file import files
|
|
18
15
|
from ivoryos.routes.design.design_step import steps
|
|
19
16
|
|
|
20
|
-
# from ...utils.py_to_json import convert_to_cards
|
|
21
17
|
|
|
22
18
|
design = Blueprint('design', __name__, template_folder='templates')
|
|
23
19
|
|
|
@@ -29,43 +25,38 @@ global_config = GlobalConfig()
|
|
|
29
25
|
|
|
30
26
|
# ---- Main Design Routes ----
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
|
|
29
|
+
def _create_forms(instrument, script, autofill, pseudo_deck = None):
|
|
30
|
+
deck = global_config.deck
|
|
31
|
+
functions = {}
|
|
32
|
+
if instrument == 'flow_control':
|
|
33
|
+
forms = create_all_builtin_forms(script=script)
|
|
34
|
+
elif instrument in global_config.defined_variables.keys():
|
|
35
|
+
_object = global_config.defined_variables.get(instrument)
|
|
36
|
+
functions = utils._inspect_class(_object)
|
|
37
|
+
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
38
|
+
else:
|
|
39
|
+
if deck:
|
|
40
|
+
functions = global_config.deck_snapshot.get(instrument, {})
|
|
41
|
+
elif pseudo_deck:
|
|
42
|
+
functions = pseudo_deck.get(instrument, {})
|
|
43
|
+
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
44
|
+
return functions, forms
|
|
45
|
+
|
|
46
|
+
@design.route("/draft")
|
|
34
47
|
@login_required
|
|
35
|
-
def experiment_builder(
|
|
48
|
+
def experiment_builder():
|
|
36
49
|
"""
|
|
37
50
|
.. :quickref: Workflow Design; Build experiment workflow
|
|
38
51
|
|
|
39
52
|
**Experiment Builder**
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
define variables, and manage experiment scripts.
|
|
43
|
-
|
|
44
|
-
.. http:get:: /design/script
|
|
54
|
+
.. http:get:: /draft
|
|
45
55
|
|
|
46
|
-
Load the experiment builder
|
|
56
|
+
Load the experiment builder page where users can design their workflow by adding actions, instruments, and logic.
|
|
47
57
|
|
|
48
|
-
:param instrument: The specific instrument for which to load functions and forms.
|
|
49
|
-
:type instrument: str
|
|
50
58
|
:status 200: Experiment builder loaded successfully.
|
|
51
59
|
|
|
52
|
-
.. http:post:: /design/script
|
|
53
|
-
|
|
54
|
-
Submit form data to add or modify actions in the experiment script.
|
|
55
|
-
|
|
56
|
-
**Adding action to canvas**
|
|
57
|
-
|
|
58
|
-
:form return: (optional) The name of the function or method to add to the script.
|
|
59
|
-
:form dynamic: depend on the selected instrument and its metadata.
|
|
60
|
-
|
|
61
|
-
:status 200: Action added or modified successfully.
|
|
62
|
-
:status 400: Validation errors in submitted form data.
|
|
63
|
-
:status 302: Toggles autofill or redirects to refresh the page.
|
|
64
|
-
|
|
65
|
-
**Toggle auto parameter name fill**:
|
|
66
|
-
|
|
67
|
-
:status 200: autofill toggled successfully
|
|
68
|
-
|
|
69
60
|
"""
|
|
70
61
|
deck = global_config.deck
|
|
71
62
|
script = utils.get_script_file()
|
|
@@ -77,266 +68,201 @@ def experiment_builder(instrument=None):
|
|
|
77
68
|
pseudo_deck_name = session.get('pseudo_deck', '')
|
|
78
69
|
pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pseudo_deck_name)
|
|
79
70
|
off_line = current_app.config["OFF_LINE"]
|
|
80
|
-
enable_llm = current_app.config["ENABLE_LLM"]
|
|
81
|
-
autofill = session.get('autofill')
|
|
82
71
|
|
|
83
|
-
# autofill is not allowed for prep and cleanup
|
|
84
|
-
autofill = autofill if script.editing_type == "script" else False
|
|
85
|
-
forms = None
|
|
86
72
|
pseudo_deck = utils.load_deck(pseudo_deck_path) if off_line and pseudo_deck_name else None
|
|
87
73
|
if off_line and pseudo_deck is None:
|
|
88
74
|
flash("Choose available deck below.")
|
|
89
75
|
|
|
90
76
|
deck_list = utils.available_pseudo_deck(current_app.config["DUMMY_DECK"])
|
|
91
77
|
|
|
92
|
-
functions = {}
|
|
93
78
|
if deck:
|
|
94
79
|
deck_variables = list(global_config.deck_snapshot.keys())
|
|
95
|
-
deck_variables.insert(0, "flow_control")
|
|
80
|
+
# deck_variables.insert(0, "flow_control")
|
|
96
81
|
else:
|
|
97
82
|
deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
|
|
98
83
|
deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
|
|
99
|
-
|
|
100
|
-
edit_action_info = session.get("edit_action")
|
|
101
|
-
if edit_action_info:
|
|
102
|
-
forms = create_form_from_action(edit_action_info, script=script)
|
|
103
|
-
elif instrument:
|
|
104
|
-
if instrument == 'flow_control':
|
|
105
|
-
forms = create_all_builtin_forms(script=script)
|
|
106
|
-
elif instrument in global_config.defined_variables.keys():
|
|
107
|
-
_object = global_config.defined_variables.get(instrument)
|
|
108
|
-
functions = utils._inspect_class(_object)
|
|
109
|
-
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
110
|
-
else:
|
|
111
|
-
if deck:
|
|
112
|
-
functions = global_config.deck_snapshot.get(instrument, {})
|
|
113
|
-
elif pseudo_deck:
|
|
114
|
-
functions = pseudo_deck.get(instrument, {})
|
|
115
|
-
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
116
|
-
|
|
117
|
-
if request.method == 'POST' and "hidden_name" in request.form:
|
|
118
|
-
method_name = request.form.get("hidden_name", None)
|
|
119
|
-
form = forms.get(method_name) if forms else None
|
|
120
|
-
insert_position = request.form.get("drop_target_id", None)
|
|
121
|
-
|
|
122
|
-
if form:
|
|
123
|
-
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
124
|
-
if form.validate_on_submit():
|
|
125
|
-
function_name = kwargs.pop("hidden_name")
|
|
126
|
-
save_data = kwargs.pop('return', '')
|
|
127
|
-
|
|
128
|
-
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
129
|
-
|
|
130
|
-
script.eval_list(kwargs, primitive_arg_types)
|
|
131
|
-
kwargs = script.validate_variables(kwargs)
|
|
132
|
-
action = {"instrument": instrument, "action": function_name,
|
|
133
|
-
"args": kwargs,
|
|
134
|
-
"return": save_data,
|
|
135
|
-
'arg_types': primitive_arg_types}
|
|
136
|
-
script.add_action(action=action, insert_position=insert_position)
|
|
137
|
-
else:
|
|
138
|
-
flash(form.errors)
|
|
139
|
-
|
|
140
|
-
elif request.method == 'POST' and "builtin_name" in request.form:
|
|
141
|
-
function_name = request.form.get("builtin_name")
|
|
142
|
-
form = forms.get(function_name) if forms else None
|
|
143
|
-
insert_position = request.form.get("drop_target_id", None)
|
|
144
|
-
|
|
145
|
-
if form:
|
|
146
|
-
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
147
|
-
if form.validate_on_submit():
|
|
148
|
-
logic_type = kwargs.pop('builtin_name')
|
|
149
|
-
if 'variable' in kwargs:
|
|
150
|
-
try:
|
|
151
|
-
script.add_variable(insert_position=insert_position, **kwargs)
|
|
152
|
-
except ValueError:
|
|
153
|
-
flash("Invalid variable type")
|
|
154
|
-
else:
|
|
155
|
-
script.add_logic_action(logic_type=logic_type, insert_position=insert_position, **kwargs)
|
|
156
|
-
else:
|
|
157
|
-
flash(form.errors)
|
|
158
|
-
|
|
159
|
-
elif request.method == 'POST' and "workflow_name" in request.form:
|
|
160
|
-
workflow_name = request.form.get("workflow_name")
|
|
161
|
-
form = forms.get(workflow_name) if forms else None
|
|
162
|
-
insert_position = request.form.get("drop_target_id", None)
|
|
163
|
-
|
|
164
|
-
if form:
|
|
165
|
-
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
166
|
-
if form.validate_on_submit():
|
|
167
|
-
save_data = kwargs.pop('return', '')
|
|
168
|
-
|
|
169
|
-
primitive_arg_types = utils.get_arg_type(kwargs, functions[workflow_name])
|
|
170
|
-
|
|
171
|
-
script.eval_list(kwargs, primitive_arg_types)
|
|
172
|
-
kwargs = script.validate_variables(kwargs)
|
|
173
|
-
action = {"instrument": instrument, "action": workflow_name,
|
|
174
|
-
"args": kwargs,
|
|
175
|
-
"return": save_data,
|
|
176
|
-
'arg_types': primitive_arg_types}
|
|
177
|
-
script.add_action(action=action, insert_position=insert_position)
|
|
178
|
-
script.add_workflow(**kwargs, insert_position=insert_position)
|
|
179
|
-
else:
|
|
180
|
-
flash(form.errors)
|
|
181
84
|
|
|
182
|
-
|
|
183
|
-
elif request.method == 'POST' and "autofill" in request.form:
|
|
184
|
-
autofill = not autofill
|
|
185
|
-
session['autofill'] = autofill
|
|
186
|
-
if not instrument == 'flow_control':
|
|
187
|
-
forms = create_form_from_pseudo(functions, autofill=autofill, script=script)
|
|
85
|
+
# edit_action_info = session.get("edit_action")
|
|
188
86
|
|
|
189
|
-
utils.post_script_file(script)
|
|
190
87
|
|
|
191
88
|
exec_string = script.python_script if script.python_script else script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
192
89
|
session['python_code'] = exec_string
|
|
193
90
|
|
|
194
|
-
design_buttons = create_action_button(script)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
use_llm=enable_llm)
|
|
91
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
92
|
+
|
|
93
|
+
return render_template('experiment_builder.html', off_line=off_line, history=deck_list,
|
|
94
|
+
script=script, defined_variables=deck_variables, buttons_dict=design_buttons,
|
|
95
|
+
local_variables=global_config.defined_variables)
|
|
200
96
|
|
|
201
97
|
|
|
202
|
-
@design.route("/
|
|
98
|
+
@design.route("/draft/meta", methods=["PATCH"])
|
|
203
99
|
@login_required
|
|
204
|
-
def
|
|
100
|
+
def update_script_meta():
|
|
205
101
|
"""
|
|
206
|
-
.. :quickref:
|
|
102
|
+
.. :quickref: Workflow Design; update the script metadata.
|
|
207
103
|
|
|
208
|
-
.. http:
|
|
104
|
+
.. http:patch:: /draft/meta
|
|
209
105
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
:status 400: failed to initialize the AI agent redirects to :http:get:`/design/script`
|
|
106
|
+
Update the script metadata, including the script name and status. If the script name is provided,
|
|
107
|
+
it saves the script with that name. If the status is "finished", it finalizes the script.
|
|
213
108
|
|
|
109
|
+
:form name: The name to save the script as.
|
|
110
|
+
:form status: The status of the script (e.g., "finished").
|
|
111
|
+
|
|
112
|
+
:status 200: Successfully updated the script metadata.
|
|
214
113
|
"""
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
try:
|
|
228
|
-
model = current_app.config["LLM_MODEL"]
|
|
229
|
-
server = current_app.config["LLM_SERVER"]
|
|
230
|
-
module = current_app.config["MODULE"]
|
|
231
|
-
from ivoryos.utils.llm_agent import LlmAgent
|
|
232
|
-
agent = LlmAgent(host=server, model=model, output_path=os.path.dirname(os.path.abspath(module)))
|
|
233
|
-
except Exception as e:
|
|
234
|
-
flash(e.__str__())
|
|
235
|
-
return redirect(url_for("design.experiment_builder", instrument=instrument, use_llm=True)), 400
|
|
236
|
-
action_list = agent.generate_code(sdl_module, prompt)
|
|
237
|
-
for action in action_list:
|
|
238
|
-
action['instrument'] = instrument
|
|
239
|
-
action['return'] = ''
|
|
240
|
-
if "args" not in action:
|
|
241
|
-
action['args'] = {}
|
|
242
|
-
if "arg_types" not in action:
|
|
243
|
-
action['arg_types'] = {}
|
|
244
|
-
empty_script.add_action(action)
|
|
245
|
-
utils.post_script_file(empty_script)
|
|
246
|
-
return redirect(url_for("design.experiment_builder", instrument=instrument, use_llm=True))
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
@design.route("/script/toggle/<stype>")
|
|
251
|
-
@login_required
|
|
252
|
-
def toggle_script_type(stype=None):
|
|
253
|
-
"""
|
|
254
|
-
.. :quickref: Workflow Design; toggle the experimental phase for design canvas.
|
|
114
|
+
data = request.get_json()
|
|
115
|
+
script = utils.get_script_file()
|
|
116
|
+
if 'name' in data:
|
|
117
|
+
run_name = data.get("name")
|
|
118
|
+
exist_script = Script.query.get(run_name)
|
|
119
|
+
if exist_script is None:
|
|
120
|
+
script.save_as(run_name)
|
|
121
|
+
utils.post_script_file(script)
|
|
122
|
+
return jsonify(success=True)
|
|
123
|
+
else:
|
|
124
|
+
flash("Script name is already exist in database")
|
|
125
|
+
return jsonify(success=False)
|
|
255
126
|
|
|
256
|
-
|
|
127
|
+
if 'status' in data:
|
|
128
|
+
if data['status'] == "finished":
|
|
129
|
+
script.finalize()
|
|
130
|
+
utils.post_script_file(script)
|
|
131
|
+
return jsonify(success=True)
|
|
132
|
+
return jsonify(success=False)
|
|
257
133
|
|
|
258
|
-
:status 200: and then redirects to :http:get:`/design/script`
|
|
259
134
|
|
|
135
|
+
@design.route("/draft/ui-state", methods=["PATCH"])
|
|
136
|
+
@login_required
|
|
137
|
+
def update_ui_state():
|
|
260
138
|
"""
|
|
261
|
-
|
|
262
|
-
script.editing_type = stype
|
|
263
|
-
utils.post_script_file(script)
|
|
264
|
-
return redirect(url_for('design.experiment_builder'))
|
|
139
|
+
.. :quickref: Workflow Design; update the UI state for the design canvas.
|
|
265
140
|
|
|
141
|
+
.. http:patch:: /draft/ui-state
|
|
266
142
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
def update_list():
|
|
270
|
-
order = request.form['order']
|
|
271
|
-
script = utils.get_script_file()
|
|
272
|
-
script.currently_editing_order = order.split(",", len(script.currently_editing_script))
|
|
273
|
-
script.sort_actions()
|
|
274
|
-
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
275
|
-
utils.post_script_file(script)
|
|
276
|
-
session['python_code'] = exec_string
|
|
143
|
+
Update the UI state for the design canvas, including showing code overlays, setting editing types,
|
|
144
|
+
and handling deck selection.
|
|
277
145
|
|
|
278
|
-
|
|
146
|
+
:form show_code: Whether to show the code overlay (true/false).
|
|
147
|
+
:form editing_type: The type of editing to set (prep, script, cleanup).
|
|
148
|
+
:form autofill: Whether to enable autofill for the instrument panel (true/false).
|
|
149
|
+
:form deck_name: The name of the deck to select.
|
|
279
150
|
|
|
151
|
+
:status 200: Updates the UI state and returns a success message.
|
|
152
|
+
"""
|
|
153
|
+
data = request.get_json()
|
|
280
154
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
155
|
+
if "show_code" in data:
|
|
156
|
+
session["show_code"] = bool(data["show_code"])
|
|
157
|
+
return jsonify({"success": True})
|
|
158
|
+
if "editing_type" in data:
|
|
159
|
+
stype = data.get("editing_type")
|
|
285
160
|
|
|
161
|
+
script = utils.get_script_file()
|
|
162
|
+
script.editing_type = stype
|
|
163
|
+
utils.post_script_file(script)
|
|
286
164
|
|
|
287
|
-
#
|
|
288
|
-
|
|
165
|
+
# Re-render only the part of the page you want to update
|
|
166
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
167
|
+
rendered_html = render_template("components/canvas.html", script=script, buttons_dict=design_buttons)
|
|
168
|
+
return jsonify({"html": rendered_html})
|
|
169
|
+
|
|
170
|
+
if "autofill" in data:
|
|
171
|
+
script = utils.get_script_file()
|
|
172
|
+
instrument = data.get("instrument", '')
|
|
173
|
+
autofill = data.get("autofill", False)
|
|
174
|
+
session['autofill'] = autofill
|
|
175
|
+
_, forms = _create_forms(instrument, script, autofill)
|
|
176
|
+
rendered_html = render_template("components/methods_panel.html", forms=forms, script=script, instrument=instrument)
|
|
177
|
+
return jsonify({"html": rendered_html})
|
|
178
|
+
|
|
179
|
+
if "deck_name" in data:
|
|
180
|
+
pkl_name = data.get('deck_name', "")
|
|
181
|
+
script = utils.get_script_file()
|
|
182
|
+
session['pseudo_deck'] = pkl_name
|
|
183
|
+
deck_list = utils.available_pseudo_deck(current_app.config["DUMMY_DECK"])
|
|
184
|
+
|
|
185
|
+
if script.deck is None or script.isEmpty():
|
|
186
|
+
script.deck = pkl_name.split('.')[0]
|
|
187
|
+
utils.post_script_file(script)
|
|
188
|
+
elif script.deck and not script.deck == pkl_name.split('.')[0]:
|
|
189
|
+
flash(f"Choose the deck with name {script.deck}")
|
|
190
|
+
pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pkl_name)
|
|
191
|
+
pseudo_deck = utils.load_deck(pseudo_deck_path)
|
|
192
|
+
deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
|
|
193
|
+
deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
|
|
194
|
+
html = render_template("components/sidebar.html", history=deck_list,
|
|
195
|
+
defined_variables=deck_variables, local_variables = global_config.defined_variables)
|
|
196
|
+
return jsonify({"html": html})
|
|
197
|
+
return jsonify({"error": "Invalid request"}), 400
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# @design.route("/draft/steps/order", methods=['POST'])
|
|
201
|
+
# @login_required
|
|
202
|
+
# def update_list():
|
|
203
|
+
# """
|
|
204
|
+
# .. :quickref: Workflow Design Steps; update the order of steps in the design canvas when reordering steps.
|
|
205
|
+
#
|
|
206
|
+
# .. http:post:: /draft/steps/order
|
|
207
|
+
#
|
|
208
|
+
# Update the order of steps in the design canvas when reordering steps.
|
|
209
|
+
#
|
|
210
|
+
# :form order: A comma-separated string representing the new order of steps.
|
|
211
|
+
# :status 200: Successfully updated the order of steps.
|
|
212
|
+
# """
|
|
213
|
+
# order = request.form['order']
|
|
214
|
+
# script = utils.get_script_file()
|
|
215
|
+
# script.currently_editing_order = order.split(",", len(script.currently_editing_script))
|
|
216
|
+
# script.sort_actions()
|
|
217
|
+
# exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
218
|
+
# utils.post_script_file(script)
|
|
219
|
+
# session['python_code'] = exec_string
|
|
220
|
+
#
|
|
221
|
+
# return jsonify({'success': True})
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@design.route("/draft", methods=['DELETE'])
|
|
289
226
|
@login_required
|
|
290
|
-
def
|
|
227
|
+
def clear_draft():
|
|
291
228
|
"""
|
|
292
229
|
.. :quickref: Workflow Design; clear the design canvas.
|
|
293
230
|
|
|
294
|
-
.. http:
|
|
231
|
+
.. http:delete:: /draft
|
|
295
232
|
|
|
296
|
-
:
|
|
297
|
-
:status 200: clear canvas and then redirects to :http:get:`/design/script`
|
|
233
|
+
:status 200: clear canvas
|
|
298
234
|
"""
|
|
299
235
|
deck = global_config.deck
|
|
300
|
-
pseudo_name = session.get("pseudo_deck", "")
|
|
301
236
|
if deck:
|
|
302
237
|
deck_name = os.path.splitext(os.path.basename(deck.__file__))[
|
|
303
238
|
0] if deck.__name__ == "__main__" else deck.__name__
|
|
304
|
-
elif pseudo_name:
|
|
305
|
-
deck_name = pseudo_name
|
|
306
239
|
else:
|
|
307
|
-
deck_name =
|
|
240
|
+
deck_name = session.get("pseudo_deck", "")
|
|
308
241
|
script = Script(deck=deck_name, author=session.get('username'))
|
|
309
242
|
utils.post_script_file(script)
|
|
310
|
-
|
|
243
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
244
|
+
session['python_code'] = exec_string
|
|
245
|
+
return jsonify({'success': True})
|
|
311
246
|
|
|
312
247
|
|
|
313
|
-
@design.route("/import/pseudo", methods=['POST'])
|
|
314
|
-
@login_required
|
|
315
|
-
def import_pseudo():
|
|
316
|
-
"""
|
|
317
|
-
.. :quickref: Workflow Design; Import pseudo deck from deck history
|
|
318
248
|
|
|
319
|
-
.. http:post:: /design/import/pseudo
|
|
320
249
|
|
|
321
|
-
:form pkl_name: pseudo deck name
|
|
322
|
-
:status 302: load pseudo deck and then redirects to :http:get:`/design/script`
|
|
323
|
-
"""
|
|
324
|
-
pkl_name = request.form.get('pkl_name')
|
|
325
|
-
script = utils.get_script_file()
|
|
326
|
-
session['pseudo_deck'] = pkl_name
|
|
327
250
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
flash(f"Choose the deck with name {script.deck}")
|
|
333
|
-
return redirect(url_for("design.experiment_builder"))
|
|
251
|
+
@design.route("/draft/submit_python", methods=["POST"])
|
|
252
|
+
def submit_script():
|
|
253
|
+
"""
|
|
254
|
+
.. :quickref: Workflow Design; convert Python to workflow script
|
|
334
255
|
|
|
256
|
+
.. http:post:: /design/submit_python
|
|
335
257
|
|
|
258
|
+
Convert a Python script to a workflow script and save it in the database.
|
|
336
259
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
260
|
+
:form workflow_name: workflow name
|
|
261
|
+
:form script: main script
|
|
262
|
+
:form prep: prep script
|
|
263
|
+
:form cleanup: post script
|
|
264
|
+
:status 200: clear canvas
|
|
265
|
+
"""
|
|
340
266
|
deck = global_config.deck
|
|
341
267
|
deck_name = os.path.splitext(os.path.basename(deck.__file__))[0] if deck.__name__ == "__main__" else deck.__name__
|
|
342
268
|
script = Script(author=session.get('user'), deck=deck_name)
|
|
@@ -352,14 +278,151 @@ def submit_script():
|
|
|
352
278
|
script.script_dict[stype] = card
|
|
353
279
|
result[stype] = "success"
|
|
354
280
|
except Exception as e:
|
|
355
|
-
result[
|
|
356
|
-
stype] = f"failed to transcript to ivoryos visualization, but function can still run. error: {str(e)}"
|
|
281
|
+
result[stype] = f"failed to transcript, but function can still run. error: {str(e)}"
|
|
357
282
|
utils.post_script_file(script)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
283
|
+
status = publish()
|
|
284
|
+
return jsonify({"script": result, "db": status}), 200
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@design.post("/draft/instruments/<string:instrument>")
|
|
289
|
+
@login_required
|
|
290
|
+
def methods_handler(instrument: str = ''):
|
|
291
|
+
"""
|
|
292
|
+
.. :quickref: Workflow Design; handle methods of a specific instrument
|
|
293
|
+
|
|
294
|
+
.. http:post:: /draft/instruments/<string:instrument>
|
|
295
|
+
|
|
296
|
+
Add methods for a specific instrument in the workflow design.
|
|
297
|
+
|
|
298
|
+
:param instrument: The name of the instrument to handle methods for.
|
|
299
|
+
:type instrument: str
|
|
300
|
+
:status 200: Render the methods for the specified instrument.
|
|
301
|
+
"""
|
|
302
|
+
script = utils.get_script_file()
|
|
303
|
+
pseudo_deck_name = session.get('pseudo_deck', '')
|
|
304
|
+
pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pseudo_deck_name)
|
|
305
|
+
off_line = current_app.config["OFF_LINE"]
|
|
306
|
+
pseudo_deck = utils.load_deck(pseudo_deck_path) if off_line and pseudo_deck_name else None
|
|
307
|
+
autofill = session.get('autofill', False)
|
|
308
|
+
|
|
309
|
+
functions, forms = _create_forms(instrument, script, autofill, pseudo_deck)
|
|
310
|
+
|
|
311
|
+
success = True
|
|
312
|
+
msg = ""
|
|
313
|
+
if "hidden_name" in request.form:
|
|
314
|
+
method_name = request.form.get("hidden_name", None)
|
|
315
|
+
form = forms.get(method_name) if forms else None
|
|
316
|
+
insert_position = request.form.get("drop_target_id", None)
|
|
317
|
+
if form:
|
|
318
|
+
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
319
|
+
if form.validate_on_submit():
|
|
320
|
+
function_name = kwargs.pop("hidden_name")
|
|
321
|
+
save_data = kwargs.pop('return', '')
|
|
322
|
+
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
323
|
+
|
|
324
|
+
# todo
|
|
325
|
+
print(primitive_arg_types)
|
|
326
|
+
|
|
327
|
+
script.eval_list(kwargs, primitive_arg_types)
|
|
328
|
+
kwargs = script.validate_variables(kwargs)
|
|
329
|
+
action = {"instrument": instrument, "action": function_name,
|
|
330
|
+
"args": kwargs,
|
|
331
|
+
"return": save_data,
|
|
332
|
+
'arg_types': primitive_arg_types}
|
|
333
|
+
script.add_action(action=action, insert_position=insert_position)
|
|
334
|
+
else:
|
|
335
|
+
msg = [f"{field}: {', '.join(messages)}" for field, messages in form.errors.items()]
|
|
336
|
+
success = False
|
|
337
|
+
elif "builtin_name" in request.form:
|
|
338
|
+
function_name = request.form.get("builtin_name")
|
|
339
|
+
form = forms.get(function_name) if forms else None
|
|
340
|
+
insert_position = request.form.get("drop_target_id", None)
|
|
341
|
+
if form:
|
|
342
|
+
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
343
|
+
if form.validate_on_submit():
|
|
344
|
+
logic_type = kwargs.pop('builtin_name')
|
|
345
|
+
if 'variable' in kwargs:
|
|
346
|
+
try:
|
|
347
|
+
script.add_variable(insert_position=insert_position, **kwargs)
|
|
348
|
+
except ValueError:
|
|
349
|
+
success = False
|
|
350
|
+
msg = [f"{field}: {', '.join(messages)}" for field, messages in form.errors.items()]
|
|
351
|
+
else:
|
|
352
|
+
script.add_logic_action(logic_type=logic_type, insert_position=insert_position, **kwargs)
|
|
353
|
+
else:
|
|
354
|
+
success = False
|
|
355
|
+
msg = [f"{field}: {', '.join(messages)}" for field, messages in form.errors.items()]
|
|
356
|
+
elif "workflow_name" in request.form:
|
|
357
|
+
workflow_name = request.form.get("workflow_name")
|
|
358
|
+
form = forms.get(workflow_name) if forms else None
|
|
359
|
+
insert_position = request.form.get("drop_target_id", None)
|
|
360
|
+
if form:
|
|
361
|
+
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
362
|
+
if form.validate_on_submit():
|
|
363
|
+
save_data = kwargs.pop('return', '')
|
|
364
|
+
primitive_arg_types = utils.get_arg_type(kwargs, functions[workflow_name])
|
|
365
|
+
script.eval_list(kwargs, primitive_arg_types)
|
|
366
|
+
kwargs = script.validate_variables(kwargs)
|
|
367
|
+
action = {"instrument": instrument, "action": workflow_name,
|
|
368
|
+
"args": kwargs,
|
|
369
|
+
"return": save_data,
|
|
370
|
+
'arg_types': primitive_arg_types}
|
|
371
|
+
script.add_action(action=action, insert_position=insert_position)
|
|
372
|
+
script.add_workflow(**kwargs, insert_position=insert_position)
|
|
373
|
+
else:
|
|
374
|
+
success = False
|
|
375
|
+
msg = [f"{field}: {', '.join(messages)}" for field, messages in form.errors.items()]
|
|
376
|
+
utils.post_script_file(script)
|
|
377
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
378
|
+
session['python_code'] = exec_string
|
|
379
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
380
|
+
html = render_template("components/canvas_main.html", script=script, buttons_dict=design_buttons)
|
|
381
|
+
return jsonify({"html": html, "success": success, "error": msg})
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
@design.get("/draft/instruments", strict_slashes=False)
|
|
385
|
+
@design.get("/draft/instruments/<string:instrument>")
|
|
386
|
+
@login_required
|
|
387
|
+
def get_operation_sidebar(instrument: str = ''):
|
|
388
|
+
"""
|
|
389
|
+
.. :quickref: Workflow Design; handle methods of a specific instrument
|
|
390
|
+
|
|
391
|
+
.. http:get:: /design/instruments/<string:instrument>
|
|
392
|
+
|
|
393
|
+
:param instrument: The name of the instrument to handle methods for.
|
|
394
|
+
:type instrument: str
|
|
395
|
+
|
|
396
|
+
:status 200: Render the methods for the specified instrument.
|
|
397
|
+
"""
|
|
398
|
+
script = utils.get_script_file()
|
|
399
|
+
pseudo_deck_name = session.get('pseudo_deck', '')
|
|
400
|
+
pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pseudo_deck_name)
|
|
401
|
+
off_line = current_app.config["OFF_LINE"]
|
|
402
|
+
pseudo_deck = utils.load_deck(pseudo_deck_path) if off_line and pseudo_deck_name else None
|
|
403
|
+
autofill = session.get('autofill', False)
|
|
404
|
+
|
|
405
|
+
functions, forms = _create_forms(instrument, script, autofill, pseudo_deck)
|
|
406
|
+
|
|
407
|
+
if instrument:
|
|
408
|
+
html = render_template("components/sidebar.html", forms=forms, instrument=instrument, script=script)
|
|
409
|
+
else:
|
|
410
|
+
pseudo_deck_name = session.get('pseudo_deck', '')
|
|
411
|
+
pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pseudo_deck_name)
|
|
412
|
+
off_line = current_app.config["OFF_LINE"]
|
|
413
|
+
pseudo_deck = utils.load_deck(pseudo_deck_path) if off_line and pseudo_deck_name else None
|
|
414
|
+
if off_line and pseudo_deck is None:
|
|
415
|
+
flash("Choose available deck below.")
|
|
416
|
+
deck_list = utils.available_pseudo_deck(current_app.config["DUMMY_DECK"])
|
|
417
|
+
if not off_line:
|
|
418
|
+
deck_variables = list(global_config.deck_snapshot.keys())
|
|
419
|
+
else:
|
|
420
|
+
deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
|
|
421
|
+
deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
|
|
422
|
+
# edit_action_info = session.get("edit_action")
|
|
423
|
+
html = render_template("components/sidebar.html", off_line=off_line, history=deck_list,
|
|
424
|
+
defined_variables=deck_variables,
|
|
425
|
+
local_variables=global_config.defined_variables)
|
|
426
|
+
return jsonify({"html": html})
|
|
364
427
|
|
|
365
428
|
|