ivoryos 1.0.8__py3-none-any.whl → 1.1.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.

Files changed (78) hide show
  1. ivoryos/__init__.py +19 -7
  2. ivoryos/routes/api/api.py +109 -0
  3. ivoryos/routes/auth/auth.py +5 -5
  4. ivoryos/routes/control/control.py +55 -353
  5. ivoryos/routes/control/control_file.py +36 -0
  6. ivoryos/routes/control/control_new_device.py +142 -0
  7. ivoryos/routes/control/templates/controllers.html +137 -0
  8. ivoryos/routes/control/templates/controllers_new.html +112 -0
  9. ivoryos/routes/control/utils.py +38 -0
  10. ivoryos/routes/data/data.py +108 -0
  11. ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +7 -7
  12. ivoryos/routes/{database/templates/database → data/templates}/workflow_view.html +3 -3
  13. ivoryos/routes/design/__init__.py +4 -0
  14. ivoryos/routes/design/design.py +96 -517
  15. ivoryos/routes/design/design_file.py +57 -0
  16. ivoryos/routes/design/design_step.py +43 -0
  17. ivoryos/routes/design/templates/components/action_form.html +52 -0
  18. ivoryos/routes/design/templates/components/action_list.html +15 -0
  19. ivoryos/routes/design/templates/components/autofill_toggle.html +14 -0
  20. ivoryos/routes/design/templates/components/canvas.html +14 -0
  21. ivoryos/routes/design/templates/components/canvas_footer.html +5 -0
  22. ivoryos/routes/design/templates/components/canvas_header.html +54 -0
  23. ivoryos/routes/design/templates/components/deck_selector.html +12 -0
  24. ivoryos/routes/design/templates/components/edit_action_form.html +29 -0
  25. ivoryos/routes/design/templates/components/instrument_panel.html +23 -0
  26. ivoryos/routes/design/templates/components/modals/drop_modal.html +19 -0
  27. ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
  28. ivoryos/routes/design/templates/components/modals/new_script_modal.html +18 -0
  29. ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
  30. ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
  31. ivoryos/routes/design/templates/components/modals.html +6 -0
  32. ivoryos/routes/design/templates/components/operations_panel.html +43 -0
  33. ivoryos/routes/design/templates/components/python_code_overlay.html +17 -0
  34. ivoryos/routes/design/templates/components/script_info.html +31 -0
  35. ivoryos/routes/design/templates/components/scripts.html +50 -0
  36. ivoryos/routes/design/templates/components/sidebar.html +16 -0
  37. ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
  38. ivoryos/routes/design/templates/experiment_builder.html +41 -0
  39. ivoryos/routes/execute/__init__.py +0 -0
  40. ivoryos/routes/execute/execute.py +173 -0
  41. ivoryos/routes/execute/execute_file.py +44 -0
  42. ivoryos/routes/execute/templates/components/error_modal.html +20 -0
  43. ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
  44. ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
  45. ivoryos/routes/execute/templates/components/run_panel.html +9 -0
  46. ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
  47. ivoryos/routes/execute/templates/components/tab_bayesian.html +147 -0
  48. ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
  49. ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
  50. ivoryos/routes/execute/templates/experiment_run.html +294 -0
  51. ivoryos/routes/library/__init__.py +0 -0
  52. ivoryos/routes/{database/database.py → library/library.py} +10 -112
  53. ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +8 -8
  54. ivoryos/routes/main/main.py +1 -1
  55. ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
  56. ivoryos/socket_handlers.py +52 -0
  57. ivoryos/templates/base.html +4 -4
  58. ivoryos/utils/bo_campaign.py +43 -3
  59. ivoryos/utils/form.py +1 -0
  60. ivoryos/utils/py_to_json.py +225 -0
  61. ivoryos/utils/script_runner.py +30 -7
  62. ivoryos/version.py +1 -1
  63. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/METADATA +5 -8
  64. ivoryos-1.1.0.dist-info/RECORD +102 -0
  65. ivoryos/routes/control/templates/control/controllers.html +0 -78
  66. ivoryos/routes/control/templates/control/controllers_home.html +0 -55
  67. ivoryos/routes/control/templates/control/controllers_new.html +0 -89
  68. ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
  69. ivoryos/routes/design/templates/design/experiment_run.html +0 -558
  70. ivoryos-1.0.8.dist-info/RECORD +0 -61
  71. /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
  72. /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
  73. /ivoryos/routes/{database → data}/__init__.py +0 -0
  74. /ivoryos/routes/{database/templates/database → data/templates/components}/step_card.html +0 -0
  75. /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
  76. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/LICENSE +0 -0
  77. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/WHEEL +0 -0
  78. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/top_level.txt +0 -0
@@ -1,81 +1,36 @@
1
- import csv
2
- import json
3
1
  import os
4
- import pickle
5
- import sys
6
- import time
7
2
 
8
- from flask import Blueprint, redirect, url_for, flash, jsonify, send_file, request, render_template, session, \
9
- current_app, g
3
+ from flask import Blueprint, redirect, url_for, flash, jsonify, request, render_template, session, \
4
+ current_app
10
5
  from flask_login import login_required
11
- from flask_socketio import SocketIO
12
- from werkzeug.utils import secure_filename
13
6
 
7
+ from ivoryos.routes.library.library import publish
14
8
  from ivoryos.utils import utils
15
9
  from ivoryos.utils.global_config import GlobalConfig
16
- from ivoryos.utils.form import create_builtin_form, create_action_button, format_name, create_form_from_pseudo, \
10
+ from ivoryos.utils.form import create_action_button, format_name, create_form_from_pseudo, \
17
11
  create_form_from_action, create_all_builtin_forms
18
- from ivoryos.utils.db_models import Script, WorkflowRun, SingleStep, WorkflowStep
12
+ from ivoryos.utils.db_models import Script
13
+ from ivoryos.utils.py_to_json import convert_to_cards
19
14
  from ivoryos.utils.script_runner import ScriptRunner
20
- # from ivoryos.utils.utils import load_workflows
21
15
 
22
- socketio = SocketIO()
23
- design = Blueprint('design', __name__, template_folder='templates/design')
16
+ # Import the new modular components
17
+ from ivoryos.routes.design.design_file import files
18
+ from ivoryos.routes.design.design_step import steps
24
19
 
25
- global_config = GlobalConfig()
26
- runner = ScriptRunner()
27
-
28
- def abort_pending():
29
- runner.abort_pending()
30
- socketio.emit('log', {'message': "aborted pending iterations"})
31
-
32
- def abort_current():
33
- runner.stop_execution()
34
- socketio.emit('log', {'message': "stopped next task"})
35
-
36
- def pause():
37
- runner.retry = False
38
- msg = runner.toggle_pause()
39
- socketio.emit('log', {'message': msg})
40
- return msg
41
-
42
- def retry():
43
- runner.retry = True
44
- msg = runner.toggle_pause()
45
- socketio.emit('log', {'message': msg})
46
-
47
-
48
- # ---- Socket.IO Event Handlers ----
49
-
50
- @socketio.on('abort_pending')
51
- def handle_abort_pending():
52
- abort_pending()
53
-
54
- @socketio.on('abort_current')
55
- def handle_abort_current():
56
- abort_current()
20
+ # from ...utils.py_to_json import convert_to_cards
57
21
 
58
- @socketio.on('pause')
59
- def handle_pause():
60
- pause()
22
+ design = Blueprint('design', __name__, template_folder='templates')
61
23
 
62
- @socketio.on('retry')
63
- def handle_retry():
64
- retry()
24
+ # Register sub-blueprints
25
+ design.register_blueprint(files)
26
+ design.register_blueprint(steps)
65
27
 
28
+ global_config = GlobalConfig()
66
29
 
67
- @socketio.on('connect')
68
- def handle_abort_action():
69
- # Fetch log messages from local file
70
- filename = os.path.join(current_app.config["OUTPUT_FOLDER"], current_app.config["LOGGERS_PATH"])
71
- with open(filename, 'r') as log_file:
72
- log_history = log_file.readlines()
73
- for message in log_history[-10:]:
74
- socketio.emit('log', {'message': message})
75
-
30
+ # ---- Main Design Routes ----
76
31
 
77
- @design.route("/design/script/", methods=['GET', 'POST'])
78
- @design.route("/design/script/<instrument>/", methods=['GET', 'POST'])
32
+ @design.route("/script/", methods=['GET', 'POST'])
33
+ @design.route("/script/<instrument>/", methods=['GET', 'POST'])
79
34
  @login_required
80
35
  def experiment_builder(instrument=None):
81
36
  """
@@ -114,13 +69,10 @@ def experiment_builder(instrument=None):
114
69
  """
115
70
  deck = global_config.deck
116
71
  script = utils.get_script_file()
117
- # load_workflows(script)
118
- # registered_workflows = global_config.registered_workflows
119
72
 
120
73
  if deck and script.deck is None:
121
74
  script.deck = os.path.splitext(os.path.basename(deck.__file__))[
122
75
  0] if deck.__name__ == "__main__" else deck.__name__
123
- # script.sort_actions()
124
76
 
125
77
  pseudo_deck_name = session.get('pseudo_deck', '')
126
78
  pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pseudo_deck_name)
@@ -140,24 +92,17 @@ def experiment_builder(instrument=None):
140
92
  functions = {}
141
93
  if deck:
142
94
  deck_variables = list(global_config.deck_snapshot.keys())
143
- # deck_variables.insert(0, "registered_workflows")
144
95
  deck_variables.insert(0, "flow_control")
145
-
146
96
  else:
147
97
  deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
148
98
  deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
99
+
149
100
  edit_action_info = session.get("edit_action")
150
101
  if edit_action_info:
151
102
  forms = create_form_from_action(edit_action_info, script=script)
152
103
  elif instrument:
153
- # if instrument in ['if', 'while', 'variable', 'wait', 'repeat']:
154
- # forms = create_builtin_form(instrument, script=script)
155
104
  if instrument == 'flow_control':
156
105
  forms = create_all_builtin_forms(script=script)
157
- # elif instrument == 'registered_workflows':
158
- # functions = utils._inspect_class(registered_workflows)
159
- # # forms = create_workflow_forms(script=script)
160
- # forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
161
106
  elif instrument in global_config.defined_variables.keys():
162
107
  _object = global_config.defined_variables.get(instrument)
163
108
  functions = utils._inspect_class(_object)
@@ -168,69 +113,71 @@ def experiment_builder(instrument=None):
168
113
  elif pseudo_deck:
169
114
  functions = pseudo_deck.get(instrument, {})
170
115
  forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
116
+
171
117
  if request.method == 'POST' and "hidden_name" in request.form:
172
- # all_kwargs = request.form.copy()
173
118
  method_name = request.form.get("hidden_name", None)
174
- # if method_name is not None:
175
- form = forms.get(method_name)
119
+ form = forms.get(method_name) if forms else None
176
120
  insert_position = request.form.get("drop_target_id", None)
177
- kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
178
- if form and form.validate_on_submit():
179
- function_name = kwargs.pop("hidden_name")
180
- save_data = kwargs.pop('return', '')
181
-
182
- primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
183
-
184
- script.eval_list(kwargs, primitive_arg_types)
185
- kwargs = script.validate_variables(kwargs)
186
- action = {"instrument": instrument, "action": function_name,
187
- "args": kwargs,
188
- "return": save_data,
189
- 'arg_types': primitive_arg_types}
190
- script.add_action(action=action, insert_position=insert_position)
191
- else:
192
- flash(form.errors)
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)
193
139
 
194
140
  elif request.method == 'POST' and "builtin_name" in request.form:
195
141
  function_name = request.form.get("builtin_name")
196
- form = forms.get(function_name)
197
- kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
142
+ form = forms.get(function_name) if forms else None
198
143
  insert_position = request.form.get("drop_target_id", None)
199
144
 
200
- if form.validate_on_submit():
201
- # print(kwargs)
202
- logic_type = kwargs.pop('builtin_name')
203
- if 'variable' in kwargs:
204
- try:
205
- script.add_variable(insert_position=insert_position, **kwargs)
206
- except ValueError:
207
- flash("Invalid variable type")
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)
208
156
  else:
209
- script.add_logic_action(logic_type=logic_type, insert_position=insert_position, **kwargs)
210
- else:
211
- flash(form.errors)
157
+ flash(form.errors)
158
+
212
159
  elif request.method == 'POST' and "workflow_name" in request.form:
213
160
  workflow_name = request.form.get("workflow_name")
214
- form = forms.get(workflow_name)
215
- kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
161
+ form = forms.get(workflow_name) if forms else None
216
162
  insert_position = request.form.get("drop_target_id", None)
217
163
 
218
- if form.validate_on_submit():
219
- # workflow_name = kwargs.pop('workflow_name')
220
- save_data = kwargs.pop('return', '')
221
-
222
- primitive_arg_types = utils.get_arg_type(kwargs, functions[workflow_name])
223
-
224
- script.eval_list(kwargs, primitive_arg_types)
225
- kwargs = script.validate_variables(kwargs)
226
- action = {"instrument": instrument, "action": workflow_name,
227
- "args": kwargs,
228
- "return": save_data,
229
- 'arg_types': primitive_arg_types}
230
- script.add_action(action=action, insert_position=insert_position)
231
- script.add_workflow(**kwargs, insert_position=insert_position)
232
- else:
233
- flash(form.errors)
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)
234
181
 
235
182
  # toggle autofill, autofill doesn't apply to control flow ops
236
183
  elif request.method == 'POST' and "autofill" in request.form:
@@ -252,7 +199,7 @@ def experiment_builder(instrument=None):
252
199
  use_llm=enable_llm)
253
200
 
254
201
 
255
- @design.route("/design/generate_code", methods=['POST'])
202
+ @design.route("/generate_code", methods=['POST'])
256
203
  @login_required
257
204
  def generate_code():
258
205
  """
@@ -274,7 +221,6 @@ def generate_code():
274
221
  if request.method == 'POST' and "gen" in request.form:
275
222
  prompt = request.form.get("prompt")
276
223
  session['prompt'][instrument] = prompt
277
- # sdl_module = utils.parse_functions(find_instrument_by_name(f'deck.{instrument}'), doc_string=True)
278
224
  sdl_module = global_config.deck_snapshot.get(instrument, {})
279
225
  empty_script = Script(author=session.get('user'))
280
226
  if enable_llm and agent is None:
@@ -300,134 +246,8 @@ def generate_code():
300
246
  return redirect(url_for("design.experiment_builder", instrument=instrument, use_llm=True))
301
247
 
302
248
 
303
- @design.route("/design/campaign", methods=['GET', 'POST'])
304
- @login_required
305
- def experiment_run():
306
- """
307
- .. :quickref: Workflow Execution; Execute/iterate the workflow
308
-
309
- .. http:get:: /design/campaign
310
-
311
- Compile the workflow and load the experiment execution interface.
312
-
313
- .. http:post:: /design/campaign
314
-
315
- Start workflow execution
316
-
317
- """
318
- deck = global_config.deck
319
- script = utils.get_script_file()
320
-
321
- # script.sort_actions() # handled in update list
322
- off_line = current_app.config["OFF_LINE"]
323
- deck_list = utils.import_history(os.path.join(current_app.config["OUTPUT_FOLDER"], 'deck_history.txt'))
324
- # if not off_line and deck is None:
325
- # # print("loading deck")
326
- # module = current_app.config.get('MODULE', '')
327
- # deck = sys.modules[module] if module else None
328
- # script.deck = os.path.splitext(os.path.basename(deck.__file__))[0]
329
- design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
330
- config_preview = []
331
- config_file_list = [i for i in os.listdir(current_app.config["CSV_FOLDER"]) if not i == ".gitkeep"]
332
- try:
333
- # todo
334
- exec_string = script.python_script if script.python_script else script.compile(current_app.config['SCRIPT_FOLDER'])
335
- # exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
336
- # print(exec_string)
337
- except Exception as e:
338
- flash(e.__str__())
339
- # handle api request
340
- if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
341
- return jsonify({"error": e.__str__()})
342
- else:
343
- return redirect(url_for("design.experiment_builder"))
344
-
345
- config_file = request.args.get("filename")
346
- config = []
347
- if config_file:
348
- session['config_file'] = config_file
349
- filename = session.get("config_file")
350
- if filename:
351
- # config_preview = list(csv.DictReader(open(os.path.join(current_app.config['CSV_FOLDER'], filename))))
352
- config = list(csv.DictReader(open(os.path.join(current_app.config['CSV_FOLDER'], filename))))
353
- config_preview = config[1:]
354
- arg_type = config.pop(0) # first entry is types
355
- try:
356
- for key, func_str in exec_string.items():
357
- exec(func_str)
358
- line_collection = script.convert_to_lines(exec_string)
359
-
360
- except Exception:
361
- flash(f"Please check {key} syntax!!")
362
- return redirect(url_for("design.experiment_builder"))
363
- # runner.globals_dict.update(globals())
364
- run_name = script.name if script.name else "untitled"
365
-
366
- dismiss = session.get("dismiss", None)
367
- script = utils.get_script_file()
368
- no_deck_warning = False
369
-
370
- _, return_list = script.config_return()
371
- config_list, config_type_list = script.config("script")
372
- # config = script.config("script")
373
- data_list = os.listdir(current_app.config['DATA_FOLDER'])
374
- data_list.remove(".gitkeep") if ".gitkeep" in data_list else data_list
375
- if deck is None:
376
- no_deck_warning = True
377
- flash(f"No deck is found, import {script.deck}")
378
- elif script.deck:
379
- is_deck_match = script.deck == deck.__name__ or script.deck == \
380
- os.path.splitext(os.path.basename(deck.__file__))[0]
381
- if not is_deck_match:
382
- flash(f"This script is not compatible with current deck, import {script.deck}")
383
- if request.method == "POST":
384
- bo_args = None
385
- compiled = False
386
- if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
387
- payload_json = request.get_json()
388
- compiled = True
389
- if "kwargs" in payload_json:
390
- config = payload_json["kwargs"]
391
- elif "parameters" in payload_json:
392
- bo_args = payload_json
393
- repeat = payload_json.pop("repeat", None)
394
- else:
395
- if "bo" in request.form:
396
- bo_args = request.form.to_dict()
397
- if "online-config" in request.form:
398
- config = utils.web_config_entry_wrapper(request.form.to_dict(), config_list)
399
- repeat = request.form.get('repeat', None)
400
249
 
401
- try:
402
- datapath = current_app.config["DATA_FOLDER"]
403
- run_name = script.validate_function_name(run_name)
404
- runner.run_script(script=script, run_name=run_name, config=config, bo_args=bo_args,
405
- logger=g.logger, socketio=g.socketio, repeat_count=repeat,
406
- output_path=datapath, compiled=compiled,
407
- current_app=current_app._get_current_object()
408
- )
409
- if utils.check_config_duplicate(config):
410
- flash(f"WARNING: Duplicate in config entries.")
411
- except Exception as e:
412
- if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
413
- return jsonify({"error": e.__str__()})
414
- else:
415
- flash(e)
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)
420
- return jsonify({"status": "task started", "task_id": global_config.runner_status.get("id")})
421
- else:
422
- return render_template('experiment_run.html', script=script.script_dict, filename=filename,
423
- dot_py=exec_string, line_collection=line_collection,
424
- return_list=return_list, config_list=config_list, config_file_list=config_file_list,
425
- config_preview=config_preview, data_list=data_list, config_type_list=config_type_list,
426
- no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons,
427
- history=deck_list, pause_status=runner.pause_status())
428
-
429
-
430
- @design.route("/design/script/toggle/<stype>")
250
+ @design.route("/script/toggle/<stype>")
431
251
  @login_required
432
252
  def toggle_script_type(stype=None):
433
253
  """
@@ -465,7 +285,7 @@ def toggle_show_code():
465
285
 
466
286
 
467
287
  # --------------------handle all the import/export and download/upload--------------------------
468
- @design.route("/design/clear")
288
+ @design.route("/clear")
469
289
  @login_required
470
290
  def clear():
471
291
  """
@@ -490,7 +310,7 @@ def clear():
490
310
  return redirect(url_for("design.experiment_builder"))
491
311
 
492
312
 
493
- @design.route("/design/import/pseudo", methods=['POST'])
313
+ @design.route("/import/pseudo", methods=['POST'])
494
314
  @login_required
495
315
  def import_pseudo():
496
316
  """
@@ -513,274 +333,33 @@ def import_pseudo():
513
333
  return redirect(url_for("design.experiment_builder"))
514
334
 
515
335
 
516
- @design.route('/design/uploads', methods=['POST'])
517
- @login_required
518
- def upload():
519
- """
520
- .. :quickref: Workflow Execution; upload a workflow config file (.CSV)
521
-
522
- .. http:post:: /design/uploads
523
-
524
- :form file: workflow CSV config file
525
- :status 302: save csv file and then redirects to :http:get:`/design/campaign`
526
- """
527
- if request.method == "POST":
528
- f = request.files['file']
529
- if 'file' not in request.files:
530
- flash('No file part')
531
- if f.filename.split('.')[-1] == "csv":
532
- filename = secure_filename(f.filename)
533
- f.save(os.path.join(current_app.config['CSV_FOLDER'], filename))
534
- session['config_file'] = filename
535
- return redirect(url_for("design.experiment_run"))
536
- else:
537
- flash("Config file is in csv format")
538
- return redirect(url_for("design.experiment_run"))
539
-
540
-
541
- @design.route('/design/workflow/download/<filename>')
542
- @login_required
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
- """
550
- filepath = os.path.join(current_app.config["DATA_FOLDER"], filename)
551
- return send_file(os.path.abspath(filepath), as_attachment=True)
552
-
553
-
554
- @design.route('/design/load_json', methods=['POST'])
555
- @login_required
556
- def load_json():
557
- """
558
- .. :quickref: Workflow Design Ext; upload a workflow design file (.JSON)
559
-
560
- .. http:post:: /load_json
561
-
562
- :form file: workflow design JSON file
563
- :status 302: load pseudo deck and then redirects to :http:get:`/design/script`
564
- """
565
- if request.method == "POST":
566
- f = request.files['file']
567
- if 'file' not in request.files:
568
- flash('No file part')
569
- if f.filename.endswith("json"):
570
- script_dict = json.load(f)
571
- utils.post_script_file(script_dict, is_dict=True)
572
- else:
573
- flash("Script file need to be JSON file")
574
- return redirect(url_for("design.experiment_builder"))
575
-
576
-
577
- @design.route('/design/script/download/<filetype>')
578
- @login_required
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
- """
586
- script = utils.get_script_file()
587
- run_name = script.name if script.name else "untitled"
588
- if filetype == "configure":
589
- filepath = os.path.join(current_app.config['SCRIPT_FOLDER'], f"{run_name}_config.csv")
590
- with open(filepath, 'w', newline='') as f:
591
- writer = csv.writer(f)
592
- cfg, cfg_types = script.config("script")
593
- writer.writerow(cfg)
594
- writer.writerow(list(cfg_types.values()))
595
- elif filetype == "script":
596
- script.sort_actions()
597
- json_object = json.dumps(script.as_dict())
598
- filepath = os.path.join(current_app.config['SCRIPT_FOLDER'], f"{run_name}.json")
599
- with open(filepath, "w") as outfile:
600
- outfile.write(json_object)
601
- elif filetype == "python":
602
- filepath = os.path.join(current_app.config["SCRIPT_FOLDER"], f"{run_name}.py")
603
- else:
604
- return "Unsupported file type", 400
605
- return send_file(os.path.abspath(filepath), as_attachment=True)
606
-
607
-
608
- @design.route("/design/step/edit/<uuid>", methods=['GET', 'POST'])
609
- @login_required
610
- def edit_action(uuid: str):
611
- """
612
- .. :quickref: Workflow Design; edit parameters of an action step on canvas
613
-
614
- .. http:get:: /design/step/edit/<uuid>
615
-
616
- Load parameter form of an action step
617
-
618
- .. http:post:: /design/step/edit/<uuid>
619
-
620
- :param uuid: The step's uuid
621
- :type uuid: str
622
-
623
- :form dynamic form: workflow step dynamic inputs
624
- :status 302: save changes and then redirects to :http:get:`/design/script`
625
- """
626
- script = utils.get_script_file()
627
- action = script.find_by_uuid(uuid)
628
- session['edit_action'] = action
629
-
630
- if request.method == "POST" and action is not None:
631
- forms = create_form_from_action(action, script=script)
632
- if "back" not in request.form:
633
- kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
634
- # print(kwargs)
635
- if forms and forms.validate_on_submit():
636
- save_as = kwargs.pop('return', '')
637
- kwargs = script.validate_variables(kwargs)
638
- # try:
639
- script.update_by_uuid(uuid=uuid, args=kwargs, output=save_as)
640
- # except Exception as e:
641
- else:
642
- flash(forms.errors)
643
- session.pop('edit_action')
644
- return redirect(url_for('design.experiment_builder'))
645
-
646
-
647
- @design.route("/design/step/delete/<id>")
648
- @login_required
649
- def delete_action(id: int):
650
- """
651
- .. :quickref: Workflow Design; delete an action step on canvas
652
-
653
- .. http:get:: /design/step/delete/<id>
654
-
655
- :param id: The step number id
656
- :type id: int
657
-
658
- :status 302: save changes and then redirects to :http:get:`/design/script`
659
- """
660
- back = request.referrer
661
- script = utils.get_script_file()
662
- script.delete_action(id)
663
- utils.post_script_file(script)
664
- return redirect(back)
665
-
666
-
667
- @design.route("/design/step/duplicate/<id>")
668
- @login_required
669
- def duplicate_action(id: int):
670
- """
671
- .. :quickref: Workflow Design; duplicate an action step on canvas
672
-
673
- .. http:get:: /design/step/duplicate/<id>
674
-
675
- :param id: The step number id
676
- :type id: int
677
-
678
- :status 302: save changes and then redirects to :http:get:`/design/script`
679
- """
680
- back = request.referrer
681
- script = utils.get_script_file()
682
- script.duplicate_action(id)
683
- utils.post_script_file(script)
684
- return redirect(back)
685
-
686
-
687
- # ---- HTTP API Endpoints ----
688
336
 
689
- @design.route("/api/runner/status", methods=["GET"])
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
- """
698
- runner_busy = global_config.runner_lock.locked()
699
- status = {"busy": runner_busy}
700
- task_status = global_config.runner_status
701
- current_step = {}
702
- # print(task_status)
703
- if task_status is not None:
704
- task_type = task_status["type"]
705
- task_id = task_status["id"]
706
- if task_type == "task":
707
- step = SingleStep.query.get(task_id)
708
- current_step = step.as_dict()
709
- if task_type == "workflow":
710
- workflow = WorkflowRun.query.get(task_id)
711
- if workflow is not None:
712
- latest_step = WorkflowStep.query.filter_by(workflow_id=workflow.id).order_by(WorkflowStep.start_time.desc()).first()
713
- if latest_step is not None:
714
- current_step = latest_step.as_dict()
715
- status["workflow_status"] = {"workflow_info": workflow.as_dict(), "runner_status": runner.get_status()}
716
- status["current_task"] = current_step
717
- return jsonify(status), 200
718
-
719
-
720
-
721
- @design.route("/api/runner/abort_pending", methods=["POST"])
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
- """
730
- abort_pending()
731
- return jsonify({"status": "ok"}), 200
732
-
733
- @design.route("/api/runner/abort_current", methods=["POST"])
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
- """
742
- abort_current()
743
- return jsonify({"status": "ok"}), 200
744
-
745
- @design.route("/api/runner/pause", methods=["POST"])
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
- """
754
- msg = pause()
755
- return jsonify({"status": "ok", "pause_status": msg}), 200
756
-
757
- @design.route("/api/runner/retry", methods=["POST"])
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
- """
766
- retry()
767
- return jsonify({"status": "ok, retrying failed step"}), 200
768
-
769
-
770
- @design.route("/api/design/submit", methods=["POST"])
337
+ @design.route("/submit_python", methods=["POST"])
771
338
  def submit_script():
772
- """
773
- .. :quickref: Workflow Design; submit script
774
-
775
- .. http:get:: /api/design/submit
776
-
777
- :status 200: {"status": "ok"}
778
- """
339
+ """Submit script"""
779
340
  deck = global_config.deck
780
341
  deck_name = os.path.splitext(os.path.basename(deck.__file__))[0] if deck.__name__ == "__main__" else deck.__name__
781
342
  script = Script(author=session.get('user'), deck=deck_name)
782
343
  script_collection = request.get_json()
344
+ workflow_name = script_collection.pop("workflow_name")
783
345
  script.python_script = script_collection
784
346
  # todo check script format
347
+ script.name = workflow_name
348
+ result = {}
349
+ for stype, py_str in script_collection.items():
350
+ try:
351
+ card = convert_to_cards(py_str)
352
+ script.script_dict[stype] = card
353
+ result[stype] = "success"
354
+ except Exception as e:
355
+ result[
356
+ stype] = f"failed to transcript to ivoryos visualization, but function can still run. error: {str(e)}"
785
357
  utils.post_script_file(script)
786
- return jsonify({"status": "ok"}), 200
358
+ try:
359
+ publish()
360
+ db_status = "success"
361
+ except Exception as e:
362
+ db_status = "failed"
363
+ return jsonify({"script": result, "db": db_status}), 200
364
+
365
+