ivoryos 0.1.5__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 +94 -0
- ivoryos/config.py +46 -0
- ivoryos/routes/__init__.py +0 -0
- ivoryos/routes/auth/__init__.py +0 -0
- ivoryos/routes/auth/auth.py +65 -0
- ivoryos/routes/auth/templates/auth/login.html +25 -0
- ivoryos/routes/auth/templates/auth/signup.html +32 -0
- ivoryos/routes/control/__init__.py +0 -0
- ivoryos/routes/control/control.py +233 -0
- ivoryos/routes/control/templates/control/controllers.html +71 -0
- ivoryos/routes/control/templates/control/controllers_home.html +50 -0
- ivoryos/routes/control/templates/control/controllers_new.html +89 -0
- ivoryos/routes/database/__init__.py +0 -0
- ivoryos/routes/database/database.py +122 -0
- ivoryos/routes/database/templates/database/experiment_database.html +72 -0
- ivoryos/routes/design/__init__.py +0 -0
- ivoryos/routes/design/design.py +396 -0
- ivoryos/routes/design/templates/design/experiment_builder.html +413 -0
- ivoryos/routes/design/templates/design/experiment_run.html +325 -0
- ivoryos/routes/main/__init__.py +0 -0
- ivoryos/routes/main/main.py +25 -0
- ivoryos/routes/main/templates/main/help.html +144 -0
- ivoryos/routes/main/templates/main/home.html +68 -0
- ivoryos/static/favicon.ico +0 -0
- ivoryos/static/gui_annotation/Slide1.png +0 -0
- ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- ivoryos/static/js/overlay.js +12 -0
- ivoryos/static/js/socket_handler.js +25 -0
- ivoryos/static/js/sortable_card.js +24 -0
- ivoryos/static/js/sortable_design.js +36 -0
- ivoryos/static/logo.png +0 -0
- ivoryos/static/style.css +202 -0
- ivoryos/templates/base.html +141 -0
- ivoryos/utils/__init__.py +0 -0
- ivoryos/utils/db_models.py +501 -0
- ivoryos/utils/form.py +316 -0
- ivoryos/utils/global_config.py +68 -0
- ivoryos/utils/llm_agent.py +183 -0
- ivoryos/utils/script_runner.py +158 -0
- ivoryos/utils/task_manager.py +80 -0
- ivoryos/utils/utils.py +337 -0
- ivoryos-0.1.5.dist-info/LICENSE +21 -0
- ivoryos-0.1.5.dist-info/METADATA +96 -0
- ivoryos-0.1.5.dist-info/RECORD +46 -0
- ivoryos-0.1.5.dist-info/WHEEL +5 -0
- ivoryos-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import pickle
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from flask import Blueprint, redirect, url_for, flash, jsonify, send_file, request, render_template, session, \
|
|
8
|
+
current_app, g
|
|
9
|
+
from flask_login import login_required
|
|
10
|
+
from flask_socketio import SocketIO
|
|
11
|
+
from werkzeug.utils import secure_filename
|
|
12
|
+
|
|
13
|
+
from ivoryos.utils import utils
|
|
14
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
15
|
+
from ivoryos.utils.form import create_builtin_form, create_action_button, format_name, create_form_from_pseudo
|
|
16
|
+
from ivoryos.utils.llm_agent import LlmAgent
|
|
17
|
+
from ivoryos.utils.db_models import Script
|
|
18
|
+
from ivoryos.utils.script_runner import ScriptRunner
|
|
19
|
+
|
|
20
|
+
socketio = SocketIO()
|
|
21
|
+
design = Blueprint('design', __name__, template_folder='templates/design')
|
|
22
|
+
|
|
23
|
+
global_config = GlobalConfig()
|
|
24
|
+
runner = ScriptRunner()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@socketio.on('abort_action')
|
|
28
|
+
def handle_abort_action():
|
|
29
|
+
runner.stop_execution()
|
|
30
|
+
socketio.emit('log', {'message': "aborted pending tasks"})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@socketio.on('connect')
|
|
34
|
+
def handle_abort_action():
|
|
35
|
+
# Fetch log messages from local file
|
|
36
|
+
filename = os.path.join(current_app.config["OUTPUT_FOLDER"], current_app.config["LOGGERS_PATH"])
|
|
37
|
+
with open(filename, 'r') as log_file:
|
|
38
|
+
log_history = log_file.readlines()
|
|
39
|
+
for message in log_history[-10:]:
|
|
40
|
+
socketio.emit('log', {'message': message})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@design.route("/experiment/build/", methods=['GET', 'POST'])
|
|
44
|
+
@design.route("/experiment/build/<instrument>/", methods=['GET', 'POST'])
|
|
45
|
+
@login_required
|
|
46
|
+
def experiment_builder(instrument=None):
|
|
47
|
+
# global deck
|
|
48
|
+
deck = global_config.deck
|
|
49
|
+
script = utils.get_script_file()
|
|
50
|
+
if deck and script.deck is None:
|
|
51
|
+
script.deck = os.path.splitext(os.path.basename(deck.__file__))[
|
|
52
|
+
0] if deck.__name__ == "__main__" else deck.__name__
|
|
53
|
+
script.sort_actions()
|
|
54
|
+
|
|
55
|
+
pseudo_deck_name = session.get('pseudo_deck', '')
|
|
56
|
+
pseudo_deck_path = os.path.join(current_app.config["DUMMY_DECK"], pseudo_deck_name)
|
|
57
|
+
off_line = current_app.config["OFF_LINE"]
|
|
58
|
+
enable_llm = current_app.config["ENABLE_LLM"]
|
|
59
|
+
autofill = session.get('autofill')
|
|
60
|
+
|
|
61
|
+
# autofill is not allowed for prep and cleanup
|
|
62
|
+
autofill = autofill if script.editing_type == "script" else False
|
|
63
|
+
forms = None
|
|
64
|
+
pseudo_deck = utils.load_deck(pseudo_deck_path) if off_line and pseudo_deck_name else None
|
|
65
|
+
if off_line and pseudo_deck is None:
|
|
66
|
+
flash("Choose available deck below.")
|
|
67
|
+
|
|
68
|
+
deck_list = utils.available_pseudo_deck(current_app.config["DUMMY_DECK"])
|
|
69
|
+
|
|
70
|
+
functions = []
|
|
71
|
+
if deck:
|
|
72
|
+
deck_variables = global_config.deck_variables.keys()
|
|
73
|
+
else:
|
|
74
|
+
deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
|
|
75
|
+
deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
|
|
76
|
+
if instrument:
|
|
77
|
+
if instrument in ['if', 'while', 'variable', 'wait']:
|
|
78
|
+
forms = create_builtin_form(instrument)
|
|
79
|
+
else:
|
|
80
|
+
if deck:
|
|
81
|
+
function_metadata = global_config.deck_variables.get(instrument, {})
|
|
82
|
+
elif pseudo_deck:
|
|
83
|
+
function_metadata = pseudo_deck.get(instrument, {})
|
|
84
|
+
functions = {key: data.get('signature', {}) for key, data in function_metadata.items()}
|
|
85
|
+
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
86
|
+
if request.method == 'POST' and "hidden_name" in request.form:
|
|
87
|
+
all_kwargs = request.form.copy()
|
|
88
|
+
method_name = all_kwargs.pop("hidden_name", None)
|
|
89
|
+
# if method_name is not None:
|
|
90
|
+
form = forms.get(method_name)
|
|
91
|
+
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
92
|
+
|
|
93
|
+
if form and form.validate_on_submit():
|
|
94
|
+
function_name = kwargs.pop("hidden_name")
|
|
95
|
+
save_data = kwargs.pop('return', '')
|
|
96
|
+
variable_kwargs = {}
|
|
97
|
+
variable_kwargs_types = {}
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
variable_kwargs, variable_kwargs_types = utils.find_variable_in_script(script, kwargs)
|
|
101
|
+
|
|
102
|
+
for name in variable_kwargs.keys():
|
|
103
|
+
del kwargs[name]
|
|
104
|
+
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
105
|
+
|
|
106
|
+
except:
|
|
107
|
+
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
108
|
+
|
|
109
|
+
kwargs.update(variable_kwargs)
|
|
110
|
+
arg_types = {}
|
|
111
|
+
arg_types.update(variable_kwargs_types)
|
|
112
|
+
arg_types.update(primitive_arg_types)
|
|
113
|
+
all_kwargs.update(variable_kwargs)
|
|
114
|
+
|
|
115
|
+
action = {"instrument": instrument, "action": function_name,
|
|
116
|
+
"args": {name: arg for (name, arg) in kwargs.items()},
|
|
117
|
+
"return": save_data,
|
|
118
|
+
'arg_types': arg_types}
|
|
119
|
+
script.add_action(action=action)
|
|
120
|
+
else:
|
|
121
|
+
flash(form.errors)
|
|
122
|
+
|
|
123
|
+
elif request.method == 'POST' and "builtin_name" in request.form:
|
|
124
|
+
kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
|
|
125
|
+
if forms.validate_on_submit():
|
|
126
|
+
logic_type = kwargs.pop('builtin_name')
|
|
127
|
+
if 'variable' in kwargs:
|
|
128
|
+
script.add_variable(**kwargs)
|
|
129
|
+
else:
|
|
130
|
+
script.add_logic_action(logic_type=logic_type, **kwargs)
|
|
131
|
+
else:
|
|
132
|
+
flash(forms.errors)
|
|
133
|
+
|
|
134
|
+
# toggle autofill
|
|
135
|
+
elif request.method == 'POST' and "autofill" in request.form:
|
|
136
|
+
autofill = not autofill
|
|
137
|
+
forms = create_form_from_pseudo(functions, autofill=autofill, script=script)
|
|
138
|
+
session['autofill'] = autofill
|
|
139
|
+
utils.post_script_file(script)
|
|
140
|
+
design_buttons = [create_action_button(i) for i in script.currently_editing_script]
|
|
141
|
+
return render_template('experiment_builder.html', off_line=off_line, instrument=instrument, history=deck_list,
|
|
142
|
+
script=script, defined_variables=deck_variables,
|
|
143
|
+
local_variables=global_config.defined_variables,
|
|
144
|
+
functions=functions, forms=forms, buttons=design_buttons, format_name=format_name,
|
|
145
|
+
use_llm=enable_llm)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@design.route("/generate_code", methods=['POST'])
|
|
149
|
+
@login_required
|
|
150
|
+
def generate_code():
|
|
151
|
+
agent = global_config.agent
|
|
152
|
+
enable_llm = current_app.config["ENABLE_LLM"]
|
|
153
|
+
instrument = request.form.get("instrument")
|
|
154
|
+
|
|
155
|
+
if request.method == 'POST' and "clear" in request.form:
|
|
156
|
+
session['prompt'][instrument] = ''
|
|
157
|
+
if request.method == 'POST' and "gen" in request.form:
|
|
158
|
+
prompt = request.form.get("prompt")
|
|
159
|
+
session['prompt'][instrument] = prompt
|
|
160
|
+
# sdl_module = utils.parse_functions(find_instrument_by_name(f'deck.{instrument}'), doc_string=True)
|
|
161
|
+
sdl_module = global_config.deck_variables.get(instrument, {})
|
|
162
|
+
empty_script = Script(author=session.get('user'))
|
|
163
|
+
if enable_llm and agent is None:
|
|
164
|
+
try:
|
|
165
|
+
model = current_app.config["LLM_MODEL"]
|
|
166
|
+
server = current_app.config["LLM_SERVER"]
|
|
167
|
+
module = current_app.config["MODULE"]
|
|
168
|
+
agent = LlmAgent(host=server, model=model, output_path=os.path.dirname(os.path.abspath(module)))
|
|
169
|
+
except Exception as e:
|
|
170
|
+
flash(e.__str__())
|
|
171
|
+
action_list = agent.generate_code(sdl_module, prompt)
|
|
172
|
+
for action in action_list:
|
|
173
|
+
action['instrument'] = instrument
|
|
174
|
+
action['return'] = ''
|
|
175
|
+
if "args" not in action:
|
|
176
|
+
action['args'] = {}
|
|
177
|
+
if "arg_types" not in action:
|
|
178
|
+
action['arg_types'] = {}
|
|
179
|
+
empty_script.add_action(action)
|
|
180
|
+
utils.post_script_file(empty_script)
|
|
181
|
+
return redirect(url_for("design.experiment_builder", instrument=instrument, use_llm=True))
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@design.route("/experiment", methods=['GET', 'POST'])
|
|
185
|
+
@login_required
|
|
186
|
+
def experiment_run():
|
|
187
|
+
# global deck
|
|
188
|
+
deck = global_config.deck
|
|
189
|
+
script = utils.get_script_file()
|
|
190
|
+
script.sort_actions()
|
|
191
|
+
off_line = current_app.config["OFF_LINE"]
|
|
192
|
+
deck_list = utils.import_history(os.path.join(current_app.config["OUTPUT_FOLDER"], 'deck_history.txt'))
|
|
193
|
+
# if not off_line and deck is None:
|
|
194
|
+
# # print("loading deck")
|
|
195
|
+
# module = current_app.config.get('MODULE', '')
|
|
196
|
+
# deck = sys.modules[module] if module else None
|
|
197
|
+
# script.deck = os.path.splitext(os.path.basename(deck.__file__))[0]
|
|
198
|
+
design_buttons = {stype: [create_action_button(i) for i in script.get_script(stype)] for stype in script.stypes}
|
|
199
|
+
config_preview = []
|
|
200
|
+
config_file_list = [i for i in os.listdir(current_app.config["CSV_FOLDER"]) if not i == ".gitkeep"]
|
|
201
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
202
|
+
# print(exec_string)
|
|
203
|
+
config_file = request.args.get("filename")
|
|
204
|
+
config = []
|
|
205
|
+
if config_file:
|
|
206
|
+
session['config_file'] = config_file
|
|
207
|
+
filename = session.get("config_file")
|
|
208
|
+
if filename:
|
|
209
|
+
# config_preview = list(csv.DictReader(open(os.path.join(current_app.config['CSV_FOLDER'], filename))))
|
|
210
|
+
config = list(csv.DictReader(open(os.path.join(current_app.config['CSV_FOLDER'], filename))))
|
|
211
|
+
config_preview = config[1:6]
|
|
212
|
+
arg_type = config.pop(0) # first entry is types
|
|
213
|
+
try:
|
|
214
|
+
exec(exec_string)
|
|
215
|
+
except Exception:
|
|
216
|
+
flash("Please check syntax!!")
|
|
217
|
+
return redirect(url_for("design.experiment_builder"))
|
|
218
|
+
# runner.globals_dict.update(globals())
|
|
219
|
+
run_name = script.name if script.name else "untitled"
|
|
220
|
+
|
|
221
|
+
dismiss = session.get("dismiss", None)
|
|
222
|
+
script = utils.get_script_file()
|
|
223
|
+
no_deck_warning = False
|
|
224
|
+
|
|
225
|
+
_, return_list = script.config_return()
|
|
226
|
+
config_list, config_type_list = script.config("script")
|
|
227
|
+
# config = script.config("script")
|
|
228
|
+
data_list = os.listdir(current_app.config['DATA_FOLDER'])
|
|
229
|
+
data_list.remove(".gitkeep") if ".gitkeep" in data_list else data_list
|
|
230
|
+
if deck is None:
|
|
231
|
+
no_deck_warning = True
|
|
232
|
+
flash(f"No deck is found, import {script.deck}")
|
|
233
|
+
elif script.deck:
|
|
234
|
+
is_deck_match = script.deck == deck.__name__ or script.deck == \
|
|
235
|
+
os.path.splitext(os.path.basename(deck.__file__))[0]
|
|
236
|
+
if not is_deck_match:
|
|
237
|
+
flash(f"This script is not compatible with current deck, import {script.deck}")
|
|
238
|
+
if request.method == "POST":
|
|
239
|
+
bo_args = None
|
|
240
|
+
if "bo" in request.form:
|
|
241
|
+
bo_args = request.form.to_dict()
|
|
242
|
+
# ax_client = utils.ax_initiation(bo_args)
|
|
243
|
+
if "online-config" in request.form:
|
|
244
|
+
config = utils.process_data(request.form.to_dict(), config_list)
|
|
245
|
+
repeat = request.form.get('repeat', None)
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
datapath = current_app.config["DATA_FOLDER"]
|
|
249
|
+
run_name = script.validate_function_name(run_name)
|
|
250
|
+
runner.run_script(script=script, run_name=run_name, config=config, bo_args=bo_args,
|
|
251
|
+
logger=g.logger, socketio=g.socketio, repeat_count=repeat,
|
|
252
|
+
output_path=datapath
|
|
253
|
+
)
|
|
254
|
+
except Exception as e:
|
|
255
|
+
flash(e)
|
|
256
|
+
return render_template('experiment_run.html', script=script.script_dict, filename=filename, dot_py=exec_string,
|
|
257
|
+
return_list=return_list, config_list=config_list, config_file_list=config_file_list,
|
|
258
|
+
config_preview=config_preview, data_list=data_list, config_type_list=config_type_list,
|
|
259
|
+
no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons, history=deck_list)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@design.route("/toggle_script_type/<stype>")
|
|
263
|
+
@login_required
|
|
264
|
+
def toggle_script_type(stype=None):
|
|
265
|
+
script = utils.get_script_file()
|
|
266
|
+
script.editing_type = stype
|
|
267
|
+
utils.post_script_file(script)
|
|
268
|
+
return redirect(url_for('design.experiment_builder'))
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@design.route("/updateList", methods=['GET', 'POST'])
|
|
272
|
+
@login_required
|
|
273
|
+
def update_list():
|
|
274
|
+
order = request.form['order']
|
|
275
|
+
script = utils.get_script_file()
|
|
276
|
+
script.currently_editing_order = order.split(",", len(script.currently_editing_script))
|
|
277
|
+
utils.post_script_file(script)
|
|
278
|
+
return jsonify('Successfully Updated')
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# --------------------handle all the import/export and download/upload--------------------------
|
|
282
|
+
@design.route("/clear")
|
|
283
|
+
@login_required
|
|
284
|
+
def clear():
|
|
285
|
+
deck = global_config.deck
|
|
286
|
+
pseudo_name = session.get("pseudo_deck", "")
|
|
287
|
+
if deck:
|
|
288
|
+
deck_name = os.path.splitext(os.path.basename(deck.__file__))[
|
|
289
|
+
0] if deck.__name__ == "__main__" else deck.__name__
|
|
290
|
+
elif pseudo_name:
|
|
291
|
+
deck_name = pseudo_name
|
|
292
|
+
else:
|
|
293
|
+
deck_name = ''
|
|
294
|
+
script = Script(deck=deck_name, author=session.get('username'))
|
|
295
|
+
utils.post_script_file(script)
|
|
296
|
+
return redirect(url_for("design.experiment_builder"))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@design.route("/import_pseudo", methods=['GET', 'POST'])
|
|
300
|
+
@login_required
|
|
301
|
+
def import_pseudo():
|
|
302
|
+
pkl_name = request.form.get('pkl_name')
|
|
303
|
+
script = utils.get_script_file()
|
|
304
|
+
session['pseudo_deck'] = pkl_name
|
|
305
|
+
|
|
306
|
+
if script.deck is None or script.isEmpty():
|
|
307
|
+
script.deck = pkl_name.split('.')[0]
|
|
308
|
+
utils.post_script_file(script)
|
|
309
|
+
elif script.deck and not script.deck == pkl_name.split('.')[0]:
|
|
310
|
+
flash(f"Choose the deck with name {script.deck}")
|
|
311
|
+
return redirect(url_for("design.experiment_builder"))
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@design.route('/uploads', methods=['GET', 'POST'])
|
|
315
|
+
@login_required
|
|
316
|
+
def upload():
|
|
317
|
+
"""
|
|
318
|
+
upload csv configuration file
|
|
319
|
+
:return:
|
|
320
|
+
"""
|
|
321
|
+
if request.method == "POST":
|
|
322
|
+
f = request.files['file']
|
|
323
|
+
if 'file' not in request.files:
|
|
324
|
+
flash('No file part')
|
|
325
|
+
if f.filename.split('.')[-1] == "csv":
|
|
326
|
+
filename = secure_filename(f.filename)
|
|
327
|
+
f.save(os.path.join(current_app.config['CSV_FOLDER'], filename))
|
|
328
|
+
session['config_file'] = filename
|
|
329
|
+
return redirect(url_for("design.experiment_run"))
|
|
330
|
+
else:
|
|
331
|
+
flash("Config file is in csv format")
|
|
332
|
+
return redirect(url_for("design.experiment_run"))
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@design.route('/download_results/<filename>')
|
|
336
|
+
@login_required
|
|
337
|
+
def download_results(filename):
|
|
338
|
+
filepath = os.path.join(current_app.config["DATA_FOLDER"], filename)
|
|
339
|
+
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
@design.route('/load_json', methods=['GET', 'POST'])
|
|
343
|
+
@login_required
|
|
344
|
+
def load_json():
|
|
345
|
+
if request.method == "POST":
|
|
346
|
+
f = request.files['file']
|
|
347
|
+
if 'file' not in request.files:
|
|
348
|
+
flash('No file part')
|
|
349
|
+
if f.filename.endswith("json"):
|
|
350
|
+
script_dict = json.load(f)
|
|
351
|
+
utils.post_script_file(script_dict, is_dict=True)
|
|
352
|
+
else:
|
|
353
|
+
flash("Script file need to be JSON file")
|
|
354
|
+
return redirect(url_for("design.experiment_builder"))
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@design.route('/download/<filetype>')
|
|
358
|
+
@login_required
|
|
359
|
+
def download(filetype):
|
|
360
|
+
script = utils.get_script_file()
|
|
361
|
+
run_name = script.name if script.name else "untitled"
|
|
362
|
+
if filetype == "configure":
|
|
363
|
+
filepath = os.path.join(current_app.config['SCRIPT_FOLDER'], f"{run_name}_config.csv")
|
|
364
|
+
with open(filepath, 'w', newline='') as f:
|
|
365
|
+
writer = csv.writer(f)
|
|
366
|
+
cfg, cfg_types = script.config("script")
|
|
367
|
+
writer.writerow(cfg)
|
|
368
|
+
writer.writerow(list(cfg_types.values()))
|
|
369
|
+
elif filetype == "script":
|
|
370
|
+
script.sort_actions()
|
|
371
|
+
json_object = json.dumps(script.as_dict())
|
|
372
|
+
filepath = os.path.join(current_app.config['SCRIPT_FOLDER'], f"{run_name}.json")
|
|
373
|
+
with open(filepath, "w") as outfile:
|
|
374
|
+
outfile.write(json_object)
|
|
375
|
+
elif filetype == "python":
|
|
376
|
+
filepath = os.path.join(current_app.config["SCRIPT_FOLDER"], f"{run_name}.py")
|
|
377
|
+
|
|
378
|
+
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@design.route("/edit/<uuid>", methods=['GET', 'POST'])
|
|
382
|
+
@login_required
|
|
383
|
+
def edit_action(uuid):
|
|
384
|
+
script = utils.get_script_file()
|
|
385
|
+
action = script.find_by_uuid(uuid)
|
|
386
|
+
session['edit_action'] = action
|
|
387
|
+
if request.method == "POST":
|
|
388
|
+
if "back" not in request.form:
|
|
389
|
+
args = request.form.to_dict()
|
|
390
|
+
save_as = args.pop('return', '')
|
|
391
|
+
try:
|
|
392
|
+
script.update_by_uuid(uuid=uuid, args=args, output=save_as)
|
|
393
|
+
except Exception as e:
|
|
394
|
+
flash(e.__str__())
|
|
395
|
+
session.pop('edit_action')
|
|
396
|
+
return redirect(url_for('design.experiment_builder'))
|