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.

Files changed (46) hide show
  1. ivoryos/__init__.py +94 -0
  2. ivoryos/config.py +46 -0
  3. ivoryos/routes/__init__.py +0 -0
  4. ivoryos/routes/auth/__init__.py +0 -0
  5. ivoryos/routes/auth/auth.py +65 -0
  6. ivoryos/routes/auth/templates/auth/login.html +25 -0
  7. ivoryos/routes/auth/templates/auth/signup.html +32 -0
  8. ivoryos/routes/control/__init__.py +0 -0
  9. ivoryos/routes/control/control.py +233 -0
  10. ivoryos/routes/control/templates/control/controllers.html +71 -0
  11. ivoryos/routes/control/templates/control/controllers_home.html +50 -0
  12. ivoryos/routes/control/templates/control/controllers_new.html +89 -0
  13. ivoryos/routes/database/__init__.py +0 -0
  14. ivoryos/routes/database/database.py +122 -0
  15. ivoryos/routes/database/templates/database/experiment_database.html +72 -0
  16. ivoryos/routes/design/__init__.py +0 -0
  17. ivoryos/routes/design/design.py +396 -0
  18. ivoryos/routes/design/templates/design/experiment_builder.html +413 -0
  19. ivoryos/routes/design/templates/design/experiment_run.html +325 -0
  20. ivoryos/routes/main/__init__.py +0 -0
  21. ivoryos/routes/main/main.py +25 -0
  22. ivoryos/routes/main/templates/main/help.html +144 -0
  23. ivoryos/routes/main/templates/main/home.html +68 -0
  24. ivoryos/static/favicon.ico +0 -0
  25. ivoryos/static/gui_annotation/Slide1.png +0 -0
  26. ivoryos/static/gui_annotation/Slide2.PNG +0 -0
  27. ivoryos/static/js/overlay.js +12 -0
  28. ivoryos/static/js/socket_handler.js +25 -0
  29. ivoryos/static/js/sortable_card.js +24 -0
  30. ivoryos/static/js/sortable_design.js +36 -0
  31. ivoryos/static/logo.png +0 -0
  32. ivoryos/static/style.css +202 -0
  33. ivoryos/templates/base.html +141 -0
  34. ivoryos/utils/__init__.py +0 -0
  35. ivoryos/utils/db_models.py +501 -0
  36. ivoryos/utils/form.py +316 -0
  37. ivoryos/utils/global_config.py +68 -0
  38. ivoryos/utils/llm_agent.py +183 -0
  39. ivoryos/utils/script_runner.py +158 -0
  40. ivoryos/utils/task_manager.py +80 -0
  41. ivoryos/utils/utils.py +337 -0
  42. ivoryos-0.1.5.dist-info/LICENSE +21 -0
  43. ivoryos-0.1.5.dist-info/METADATA +96 -0
  44. ivoryos-0.1.5.dist-info/RECORD +46 -0
  45. ivoryos-0.1.5.dist-info/WHEEL +5 -0
  46. 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'))