ivoryos 1.0.9__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 +18 -6
  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.9.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.9.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.9.dist-info → ivoryos-1.1.0.dist-info}/LICENSE +0 -0
  77. {ivoryos-1.0.9.dist-info → ivoryos-1.1.0.dist-info}/WHEEL +0 -0
  78. {ivoryos-1.0.9.dist-info → ivoryos-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,57 @@
1
+ import csv
2
+ import json
3
+ import os
4
+ from flask import Blueprint, send_file, request, flash, redirect, url_for, session, current_app
5
+ from werkzeug.utils import secure_filename
6
+ from ivoryos.utils import utils
7
+
8
+ files = Blueprint('design_files', __name__)
9
+
10
+
11
+
12
+ @files.route('/download/data/<filename>')
13
+ def download_results(filename):
14
+ """Download a workflow data file"""
15
+ filepath = os.path.join(current_app.config["DATA_FOLDER"], filename)
16
+ return send_file(os.path.abspath(filepath), as_attachment=True)
17
+
18
+ @files.route('/upload/json', methods=['POST'])
19
+ def load_json():
20
+ """Upload a workflow design file (.JSON)"""
21
+ if request.method == "POST":
22
+ f = request.files['file']
23
+ if 'file' not in request.files:
24
+ flash('No file part')
25
+ if f.filename.endswith("json"):
26
+ script_dict = json.load(f)
27
+ utils.post_script_file(script_dict, is_dict=True)
28
+ else:
29
+ flash("Script file need to be JSON file")
30
+ return redirect(url_for("design.experiment_builder"))
31
+
32
+ @files.route('/download/script/<filetype>')
33
+ def download(filetype):
34
+ """Download a workflow design file"""
35
+ script = utils.get_script_file()
36
+ run_name = script.name if script.name else "untitled"
37
+
38
+ if filetype == "configure":
39
+ filepath = os.path.join(current_app.config['SCRIPT_FOLDER'], f"{run_name}_config.csv")
40
+ with open(filepath, 'w', newline='') as f:
41
+ writer = csv.writer(f)
42
+ cfg, cfg_types = script.config("script")
43
+ writer.writerow(cfg)
44
+ writer.writerow(list(cfg_types.values()))
45
+ elif filetype == "script":
46
+ script.sort_actions()
47
+ json_object = json.dumps(script.as_dict())
48
+ filepath = os.path.join(current_app.config['SCRIPT_FOLDER'], f"{run_name}.json")
49
+ with open(filepath, "w") as outfile:
50
+ outfile.write(json_object)
51
+ elif filetype == "python":
52
+ filepath = os.path.join(current_app.config["SCRIPT_FOLDER"], f"{run_name}.py")
53
+ else:
54
+ return "Unsupported file type", 400
55
+ return send_file(os.path.abspath(filepath), as_attachment=True)
56
+
57
+
@@ -0,0 +1,43 @@
1
+ from flask import Blueprint, request, session, flash, redirect, url_for
2
+ from ivoryos.utils import utils
3
+ from ivoryos.utils.form import create_form_from_action
4
+
5
+ steps = Blueprint('design_steps', __name__)
6
+
7
+ @steps.route("/step/edit/<uuid>", methods=['GET', 'POST'])
8
+ def edit_action(uuid: str):
9
+ """Edit parameters of an action step on canvas"""
10
+ script = utils.get_script_file()
11
+ action = script.find_by_uuid(uuid)
12
+ session['edit_action'] = action
13
+
14
+ if request.method == "POST" and action is not None:
15
+ forms = create_form_from_action(action, script=script)
16
+ if "back" not in request.form:
17
+ kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
18
+ if forms and forms.validate_on_submit():
19
+ save_as = kwargs.pop('return', '')
20
+ kwargs = script.validate_variables(kwargs)
21
+ script.update_by_uuid(uuid=uuid, args=kwargs, output=save_as)
22
+ else:
23
+ flash(forms.errors)
24
+ session.pop('edit_action')
25
+ return redirect(url_for('design.experiment_builder'))
26
+
27
+ @steps.route("/step/delete/<id>")
28
+ def delete_action(id: int):
29
+ """Delete an action step on canvas"""
30
+ back = request.referrer
31
+ script = utils.get_script_file()
32
+ script.delete_action(id)
33
+ utils.post_script_file(script)
34
+ return redirect(back)
35
+
36
+ @steps.route("/step/duplicate/<id>")
37
+ def duplicate_action(id: int):
38
+ """Duplicate an action step on canvas"""
39
+ back = request.referrer
40
+ script = utils.get_script_file()
41
+ script.duplicate_action(id)
42
+ utils.post_script_file(script)
43
+ return redirect(back)
@@ -0,0 +1,52 @@
1
+ {# Action form component #}
2
+ <div class="accordion-item design-control" draggable="true">
3
+ <h2 class="accordion-header">
4
+ <button class="accordion-button collapsed draggable-action"
5
+ type="button" data-bs-toggle="collapse"
6
+ data-bs-target="#{{name}}" aria-expanded="false"
7
+ aria-controls="collapseExample"
8
+ data-action="{{ name }}">
9
+ {{ format_name(name) }}
10
+ </button>
11
+ </h2>
12
+ <div id="{{name}}" class="accordion-collapse collapse" data-bs-parent="#accordionActions">
13
+ <div class="accordion-body">
14
+ <form role="form" method='POST' name="add" id="add-{{name}}">
15
+ <div class="form-group">
16
+ {{ form.hidden_tag() }}
17
+ {% for field in form %}
18
+ {% if field.type not in ['CSRFTokenField', 'HiddenField'] %}
19
+ <div class="input-group mb-3">
20
+ <label class="input-group-text">{{ field.label.text }}</label>
21
+ {% if field.type == "SubmitField" %}
22
+ {{ field(class="btn btn-dark") }}
23
+ {% elif field.type == "BooleanField" %}
24
+ {{ field(class="form-check-input") }}
25
+ {% elif field.type == "FlexibleEnumField" %}
26
+ <input type="text" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.data }}"
27
+ list="{{ field.id }}_options" placeholder="{{ field.render_kw.placeholder if field.render_kw and field.render_kw.placeholder }}"
28
+ class="form-control">
29
+ <datalist id="{{ field.id }}_options">
30
+ {% for key in field.choices %}
31
+ <option value="{{ key }}">{{ key }}</option>
32
+ {% endfor %}
33
+ </datalist>
34
+ {% else %}
35
+ {{ field(class="form-control") }}
36
+ {% endif %}
37
+ </div>
38
+ {% endif %}
39
+ {% endfor %}
40
+ </div>
41
+ <button type="submit" class="btn btn-dark">Add</button>
42
+ {% if 'hidden_name' in form %}
43
+ <i class="bi bi-info-circle ms-2" data-bs-toggle="tooltip" data-bs-placement="top"
44
+ title='{{ form.hidden_name.description or "Docstring is not available" }}'>
45
+ </i>
46
+ {% else %}
47
+ <!-- handle info tooltip for flow control / workflows -->
48
+ {% endif %}
49
+ </form>
50
+ </div>
51
+ </div>
52
+ </div>
@@ -0,0 +1,15 @@
1
+ {# Action list component #}
2
+ <div class="list-group" id="list" style="margin-top: 20px">
3
+ <ul class="reorder">
4
+ {% for button in buttons %}
5
+ <li id="{{ button['id'] }}" style="list-style-type: none;">
6
+ <span class="line-number d-none">{{ button['id'] }}.</span>
7
+ <a href="{{ url_for('design.design_steps.edit_action', uuid=button['uuid']) }}" type="button" class="btn btn-light" style="{{ button['style'] }}">{{ button['label'] }}</a>
8
+ {% if not button["instrument"] in ["if","while","repeat"] %}
9
+ <a href="{{ url_for('design.design_steps.duplicate_action', id=button['id']) }}" type="button" class="btn btn-light"><span class="bi bi-copy"></span></a>
10
+ {% endif %}
11
+ <a href="{{ url_for('design.design_steps.delete_action', id=button['id']) }}" type="button" class="btn btn-light"><span class="bi bi-trash"></span></a>
12
+ </li>
13
+ {% endfor %}
14
+ </ul>
15
+ </div>
@@ -0,0 +1,14 @@
1
+ {# Autofill toggle component #}
2
+ <div class="d-flex justify-content-between align-items-center " style="margin-bottom: 1vh;margin-top: 1vh;">
3
+ <div></div>
4
+ <form role="form" method='POST' name="autoFill" id="autoFill">
5
+ <div class="form-check form-switch">
6
+ <input type="hidden" id="autofill" name="autofill" value="temp_value">
7
+ <input class="form-check-input" type="checkbox" id="autoFillCheck" name="autoFillCheck" onchange="document.getElementById('autoFill').submit();"
8
+ value="temp_value"
9
+ {{ "checked" if session["autofill"] else "" }}>
10
+ <label class="form-check-label" for="autoFillCheck">Auto fill</label>
11
+ </div>
12
+ <button type="submit" class="btn btn-default" style="display: none;">Auto fill </button>
13
+ </form>
14
+ </div>
@@ -0,0 +1,14 @@
1
+ {# Canvas component for experiment builder #}
2
+ <div class="col-md-9 scroll-column">
3
+ {% include 'components/canvas_header.html' %}
4
+
5
+ <div class="canvas-wrapper position-relative">
6
+ <div class="canvas" droppable="true">
7
+ {% include 'components/script_info.html' %}
8
+ {% include 'components/action_list.html' %}
9
+ {% include 'components/python_code_overlay.html' %}
10
+ </div>
11
+ </div>
12
+
13
+ {% include 'components/canvas_footer.html' %}
14
+ </div>
@@ -0,0 +1,5 @@
1
+ {# Canvas footer component #}
2
+ <div>
3
+ <a class="btn btn-dark {{ 'disabled' if not script.name or script.status == "finalized" else ''}}" href="{{url_for('library.publish')}}">Quick Save</a>
4
+ <a class="btn btn-dark " href="{{ url_for('execute.experiment_run') }}">Compile and Run</a>
5
+ </div>
@@ -0,0 +1,54 @@
1
+ {# Canvas header component #}
2
+ <div class="d-flex align-items-center justify-content-between">
3
+ <!-- Left: Script Info -->
4
+ <div>
5
+ <span class="fw-bold">{{ script.name or "Untitled Script" }}</span>
6
+ <span class="badge bg-secondary">{{ script.status }}</span>
7
+ </div>
8
+
9
+ <!-- Center: Main Actions -->
10
+ <div class="btn-toolbar" role="toolbar">
11
+ <button class="btn btn-primary btn-sm me-1" data-bs-toggle="modal" data-bs-target="#newScriptModal">New</button>
12
+ <button class="btn btn-secondary btn-sm me-1" data-bs-toggle="modal" data-bs-target="#jsonModal">Import</button>
13
+ <a class="btn btn-secondary btn-sm me-1" href="{{url_for('design.design_files.download', filetype='script')}}">Export</a>
14
+ <button class="btn btn-secondary btn-sm me-1" data-bs-toggle="modal" data-bs-target="#saveasModal">Save as</button>
15
+ <div class="btn-group">
16
+ <button class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">More</button>
17
+ <ul class="dropdown-menu">
18
+ <li>
19
+ <button class="dropdown-item" data-bs-toggle="modal" data-bs-target="#renameModal">Rename</button>
20
+ </li>
21
+ <li>
22
+ <a class="dropdown-item {% if not script.name or script.status == 'finalized' %}disabled{% endif %}" href="{% if script.name and script.status != 'finalized' %}{{url_for('library.publish')}}{% else %}#{% endif %}">Save</a>
23
+ </li>
24
+ {% if not script.status == 'finalized' %}
25
+ <li>
26
+ <a class="dropdown-item" href="{{url_for('library.finalize')}}">Disable editing</a>
27
+ </li>
28
+ {% endif %}
29
+ </ul>
30
+ </div>
31
+ </div>
32
+ </div>
33
+
34
+ <!-- Tabs for Info/Prep/Experiment/Cleanup below header -->
35
+ <div class="d-flex align-items-center mt-2 justify-content-between">
36
+ <ul class="nav nav-tabs mb-0">
37
+ <li class="nav-item"><a class="{{'nav-link active' if script.editing_type=='prep' else 'nav-link'}}" href="{{url_for('design.toggle_script_type', stype='prep') }}">Prep</a></li>
38
+ <li class="nav-item"><a class="{{'nav-link active' if script.editing_type=='script' else 'nav-link'}}" href="{{url_for('design.toggle_script_type', stype='script') }}">Experiment</a></li>
39
+ <li class="nav-item"><a class="{{'nav-link active' if script.editing_type=='cleanup' else 'nav-link'}}" href="{{url_for('design.toggle_script_type', stype='cleanup') }}">Clean up</a></li>
40
+ </ul>
41
+ <div class="d-flex align-items-center ms-auto">
42
+ <form method="POST" action="{{ url_for('design.toggle_show_code') }}" class="mb-0 me-3">
43
+ <div class="form-check form-switch">
44
+ <input class="form-check-input" type="checkbox" id="showPythonCodeSwitch" name="show_code"
45
+ onchange="this.form.submit()" {% if session.get('show_code') %}checked{% endif %}>
46
+ <label class="form-check-label" for="showPythonCodeSwitch">Show Code</label>
47
+ </div>
48
+ </form>
49
+ <div class="form-check form-switch">
50
+ <input class="form-check-input" type="checkbox" id="toggleLineNumbers" onchange="toggleLineNumbers()">
51
+ <label class="form-check-label" for="toggleLineNumbers">Show Line Numbers</label>
52
+ </div>
53
+ </div>
54
+ </div>
@@ -0,0 +1,12 @@
1
+ {# Deck selector component #}
2
+ <form id="select-deck" method="POST" action="{{ url_for('design.temp.import_pseudo') }}" enctype="multipart/form-data">
3
+ <div class="input-group mb-3">
4
+ <select class="form-select" name="pkl_name" id="pkl_name" required onchange="document.getElementById('select-deck').submit();">
5
+ <option {{ '' if 'pseudo_deck' in session else 'selected' }} disabled hidden style="overflow-wrap: break-word;" name="pkl_name" id="pkl_name" value=""> -- choose deck --</option>
6
+ {% for connection in history %}
7
+ <option {{ 'selected' if session['pseudo_deck']==connection else '' }} style="overflow-wrap: break-word;" name="pkl_name" id="pkl_name" value="{{connection}}">{{connection.split('.')[0]}}</option>
8
+ {% endfor %}
9
+ </select>
10
+ </div>
11
+ </form>
12
+ <hr>
@@ -0,0 +1,29 @@
1
+ {# Edit action form component #}
2
+ {% with action = session["edit_action"] %}
3
+ <h5> {{ format_name(action['action']) }} </h5>
4
+ <form role="form" method='POST' name="{{instrument}}" action="{{ url_for('design.design_steps.edit_action', uuid=session["edit_action"]['uuid']) }}">
5
+ {% if not action['args'] == None %}
6
+ <div class="form-group">
7
+ {% if not action['args'].__class__.__name__ == 'dict' %}
8
+ <div class="input-group mb-3">
9
+ <label class="input-group-text">{{ action['action'] }}</label>
10
+ <input class="form-control" type="text" id="arg" name="arg" placeholder="{{ action['arg_types']}}" value="{{ action['args'] }}" aria-labelledby="variableHelpBlock">
11
+ </div>
12
+ {% else %}
13
+ {{ forms.hidden_tag() }}
14
+ {% for field in forms %}
15
+ {% if field.type not in ['CSRFTokenField'] %}
16
+ <div class="input-group mb-3">
17
+ <label class="input-group-text">{{ field.label.text }}</label>
18
+ {{ field(class="form-control") }}
19
+ <div class="form-text">{{ field.description }} </div>
20
+ </div>
21
+ {% endif %}
22
+ {% endfor %}
23
+ {% endif %}
24
+ </div>
25
+ {% endif %}
26
+ <button class="btn btn-primary" type="submit">Save</button>
27
+ <button class="btn btn-primary" type="submit" name="back" id="back" value="back">Back</button>
28
+ </form>
29
+ {% endwith %}
@@ -0,0 +1,23 @@
1
+ {# Instrument panel component #}
2
+ <div>
3
+ <div class="d-flex justify-content-between align-items-center " style="margin-bottom: 1vh;margin-top: 1vh;">
4
+ <a class="btn btn-primary" role="button" type="button" href="{{url_for('design.experiment_builder')}}"><i class="bi bi-arrow-return-left"></i></a>
5
+ {{ format_name(instrument) }}
6
+ </div>
7
+
8
+ {% if script.editing_type == "script" %}
9
+ {# Auto Fill Toggle #}
10
+ {% include 'components/autofill_toggle.html' %}
11
+ {% endif %}
12
+
13
+ {# accordion for instrument #}
14
+ <div class="accordion accordion-flush" id="accordionActions" >
15
+ {% if use_llm and not instrument == "flow_control" %}
16
+ {% include 'components/text_to_code_panel.html' %}
17
+ {% endif %}
18
+
19
+ {% for name, form in forms.items() %}
20
+ {% include 'components/action_form.html' %}
21
+ {% endfor %}
22
+ </div>
23
+ </div>
@@ -0,0 +1,19 @@
1
+ {# Drop modal component #}
2
+ <div class="modal fade" id="dropModal" tabindex="-1" aria-labelledby="dropModalLabel" aria-hidden="true">
3
+ <div class="modal-dialog">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title" id="dropModalLabel">Configure Action</h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body">
10
+ <p>Drop Position ID: <strong id="modalDropTarget"></strong></p>
11
+ <!-- Form will be dynamically inserted here -->
12
+ <div id="modalFormFields"></div>
13
+ </div>
14
+ <div class="modal-footer">
15
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ </div>
@@ -0,0 +1,22 @@
1
+ {# JSON import modal component #}
2
+ <div class="modal fade" id="jsonModal" tabindex="-1" aria-labelledby="jsonModal" aria-hidden="true" >
3
+ <div class="modal-dialog">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h1 class="modal-title fs-5" id="jsonModal">Import from JSON</h1>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <form method="POST" action="{{ url_for('design.design_files.load_json') }}" enctype="multipart/form-data">
10
+ <div class="modal-body">
11
+ <div class="input-group mb-3">
12
+ <input class="form-control" type="file" name="file" required="required">
13
+ </div>
14
+ </div>
15
+ <div class="modal-footer">
16
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> Close </button>
17
+ <button type="submit" class="btn btn-primary"> Upload </button>
18
+ </div>
19
+ </form>
20
+ </div>
21
+ </div>
22
+ </div>
@@ -0,0 +1,18 @@
1
+ {# New script modal component #}
2
+ <div class="modal fade" id="newScriptModal" tabindex="-1" aria-labelledby="newScriptModalLabel" aria-hidden="true">
3
+ <div class="modal-dialog">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h1 class="modal-title fs-5" id="newScriptModalLabel">Save your current editing!</h1>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body">
10
+ The current editing won't be saved. Are you sure you want to proceed?
11
+ </div>
12
+ <div class="modal-footer">
13
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> Continue editing </button>
14
+ <a role="button" class="btn btn-primary" href="{{url_for('design.clear')}}"> Already saved, clear all </a>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ </div>
@@ -0,0 +1,23 @@
1
+ {# Rename modal component #}
2
+ <div class="modal fade" id="renameModal" tabindex="-1" aria-labelledby="renameModal" aria-hidden="true" >
3
+ <div class="modal-dialog">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h1 class="modal-title fs-5" id="renameModal">Rename your script</h1>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <form method="POST" name="run_name" action="{{ url_for('library.edit_run_name') }}">
10
+ <div class="modal-body">
11
+ <div class="input-group mb-3">
12
+ <label class="input-group-text" for="run_name">Run Name</label>
13
+ <input class="form-control" type="text" name="run_name" id="run_name" placeholder="{{script['name']}}" required="required">
14
+ </div>
15
+ </div>
16
+ <div class="modal-footer">
17
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> Close </button>
18
+ <button type="submit" class="btn btn-primary"> Save </button>
19
+ </div>
20
+ </form>
21
+ </div>
22
+ </div>
23
+ </div>
@@ -0,0 +1,27 @@
1
+ {# Save as modal component #}
2
+ <div class="modal fade" id="saveasModal" tabindex="-1" aria-labelledby="saveasModal" aria-hidden="true" >
3
+ <div class="modal-dialog">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h1 class="modal-title fs-5" id="saveasModal">Save your script as </h1>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <form method="POST" name="run_name" action="{{ url_for('library.save_as') }}">
10
+ <div class="modal-body">
11
+ <div class="input-group mb-3">
12
+ <label class="input-group-text" for="run_name">Run Name</label>
13
+ <input class="form-control" type="text" name="run_name" id="run_name" placeholder="{{script['name']}}" required="required">
14
+ </div>
15
+ <div class="form-check form-switch">
16
+ <input class="form-check-input" type="checkbox" name="register_workflow" id="register_workflow">
17
+ <label class="input-group-label" for="register_workflow">Register this workflow</label>
18
+ </div>
19
+ </div>
20
+ <div class="modal-footer">
21
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> Close </button>
22
+ <button type="submit" class="btn btn-primary"> Save </button>
23
+ </div>
24
+ </form>
25
+ </div>
26
+ </div>
27
+ </div>
@@ -0,0 +1,6 @@
1
+ {# Modals component for experiment builder #}
2
+ {% include 'components/modals/new_script_modal.html' %}
3
+ {% include 'components/modals/saveas_modal.html' %}
4
+ {% include 'components/modals/rename_modal.html' %}
5
+ {% include 'components/modals/json_modal.html' %}
6
+ {% include 'components/modals/drop_modal.html' %}
@@ -0,0 +1,43 @@
1
+ {# Operations panel component #}
2
+ <div style="margin-bottom: 4vh;"></div>
3
+ <div class="accordion accordion-flush">
4
+ <div class="accordion-item design-control">
5
+ <h5 class="accordion-header">
6
+ <button class="accordion-button" data-bs-toggle="collapse" data-bs-target="#deck" role="button" aria-expanded="false" aria-controls="collapseExample">
7
+ Operations
8
+ </button>
9
+ </h5>
10
+ <div class="accordion-collapse collapse show" id="deck">
11
+ <ul class="list-group">
12
+ {% for instrument in defined_variables %}
13
+ <form role="form" method='GET' name="{{instrument}}" action="{{url_for('design.experiment_builder',instrument=instrument)}}">
14
+ <div>
15
+ <button class="list-group-item list-group-item-action" type="submit">{{format_name(instrument)}}</button>
16
+ </div>
17
+ </form>
18
+ {% endfor %}
19
+ </ul>
20
+ </div>
21
+ </div>
22
+
23
+ {% if local_variables %}
24
+ <div class="accordion-item design-control">
25
+ <h5 class="accordion-header">
26
+ <button class="accordion-button" data-bs-toggle="collapse" data-bs-target="#local" role="button" aria-expanded="false" aria-controls="collapseExample">
27
+ Local Operations
28
+ </button>
29
+ </h5>
30
+ <div class="accordion-collapse collapse show" id="local">
31
+ <ul class="list-group">
32
+ {% for instrument in local_variables %}
33
+ <form role="form" method='GET' name="{{instrument}}" action="{{url_for('design.experiment_builder',instrument=instrument)}}">
34
+ <div>
35
+ <button class="list-group-item list-group-item-action" type="submit" name="device" value="{{instrument}}" >{{instrument}}</button>
36
+ </div>
37
+ </form>
38
+ {% endfor%}
39
+ </ul>
40
+ </div>
41
+ </div>
42
+ {% endif %}
43
+ </div>
@@ -0,0 +1,17 @@
1
+ {# Python code overlay component #}
2
+ {% if session.get('show_code') %}
3
+ <div id="pythonCodeOverlay" class="code-overlay bg-light border-start show">
4
+ <div class="overlay-header d-flex justify-content-between align-items-center px-3 py-2 border-bottom">
5
+ <strong>Python Code</strong>
6
+ <button class="btn btn-sm btn-outline-secondary" onclick="toggleCodeOverlay(false)">
7
+ <i class="bi bi-x-lg"></i>
8
+ </button>
9
+ </div>
10
+ <div class="overlay-content p-3">
11
+ {% for stype, script in session['python_code'].items() %}
12
+ <pre><code class="language-python">{{ script }}</code></pre>
13
+ {% endfor %}
14
+ <a href="{{ url_for('design.design_files.download', filetype='python') }}">Download <i class="bi bi-download"></i></a>
15
+ </div>
16
+ </div>
17
+ {% endif %}
@@ -0,0 +1,31 @@
1
+ {# Script info component #}
2
+ <div class="collapse" id="info">
3
+ <table class="table script-table">
4
+ <tbody>
5
+ <tr><th scope="row">Deck Name</th><td>{{script.deck}}</td></tr>
6
+ <tr><th scope="row">Script Name</th><td>{{ script.name }}</td></tr>
7
+ <tr>
8
+ <th scope="row">Editing status <a role="button" data-bs-toggle="popover" data-bs-title="How to use:" data-bs-content="You can choose to disable editing, so the script is finalized and cannot be edited. Use save as to rename the script"><i class="bi bi-info-circle"></i></a></th>
9
+ <td>{{script.status}}</td>
10
+ </tr>
11
+ <tr>
12
+ <th scope="row">Output Values <a role="button" data-bs-toggle="popover" data-bs-title="How to use:" data-bs-content="This will be your output data. If the return data is not a value, it will save as None is the result file"><i class="bi bi-info-circle"></i></a></th>
13
+ <td>
14
+ {% for i in script.config_return()[1] %}
15
+ <input type="checkbox">{{i}}
16
+ {% endfor %}
17
+ </td>
18
+ </tr>
19
+ <tr>
20
+ <th scope="row">Config Variables <a role="button" data-bs-toggle="popover" data-bs-title="How to use:" data-bs-content="This shows variables you want to configure later using .csv file"><i class="bi bi-info-circle"></i></a></th>
21
+ <td>
22
+ <ul>
23
+ {% for i in script.config("script")[0] %}
24
+ <li>{{i}}</li>
25
+ {% endfor %}
26
+ </ul>
27
+ </td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
31
+ </div>
@@ -0,0 +1,50 @@
1
+ {# Scripts component for experiment builder #}
2
+ {% if instrument and use_llm %}
3
+ <script>
4
+ const buttonIds = {{ ['generate'] | tojson }};
5
+ </script>
6
+ <script src="{{ url_for('static', filename='js/overlay.js') }}"></script>
7
+ {% endif %}
8
+
9
+ <script>
10
+ const updateListUrl = "{{ url_for('design.update_list') }}";
11
+
12
+ // Toggle visibility of line numbers
13
+ function toggleLineNumbers(save = true) {
14
+ const show = document.getElementById('toggleLineNumbers').checked;
15
+ document.querySelectorAll('.line-number').forEach(el => {
16
+ el.classList.toggle('d-none', !show);
17
+ });
18
+
19
+ if (save) {
20
+ localStorage.setItem('showLineNumbers', show ? 'true' : 'false');
21
+ }
22
+ }
23
+
24
+ function toggleCodeOverlay() {
25
+ const overlay = document.getElementById("pythonCodeOverlay");
26
+ const toggleBtn = document.getElementById("codeToggleBtn");
27
+ overlay.classList.toggle("show");
28
+
29
+ // Change arrow icon
30
+ const icon = toggleBtn.querySelector("i");
31
+ icon.classList.toggle("bi-chevron-left");
32
+ icon.classList.toggle("bi-chevron-right");
33
+ }
34
+
35
+ // Restore state on page load
36
+ document.addEventListener('DOMContentLoaded', () => {
37
+ const savedState = localStorage.getItem('showLineNumbers');
38
+ const checkbox = document.getElementById('toggleLineNumbers');
39
+
40
+ if (savedState === 'true') {
41
+ checkbox.checked = true;
42
+ }
43
+
44
+ toggleLineNumbers(false); // don't overwrite localStorage on load
45
+
46
+ checkbox.addEventListener('change', () => toggleLineNumbers());
47
+ });
48
+ </script>
49
+
50
+ <script src="{{ url_for('static', filename='js/sortable_design.js') }}"></script>
@@ -0,0 +1,16 @@
1
+ {# Sidebar component for experiment builder #}
2
+ <div class="col-md-3 scroll-column">
3
+ {# select deck if this is online#}
4
+ {% if off_line %}
5
+ {% include 'components/deck_selector.html' %}
6
+ {% endif %}
7
+
8
+ {# edit action #}
9
+ {% if session["edit_action"] %}
10
+ {% include 'components/edit_action_form.html' %}
11
+ {% elif instrument %}
12
+ {% include 'components/instrument_panel.html' %}
13
+ {% else %}
14
+ {% include 'components/operations_panel.html' %}
15
+ {% endif %}
16
+ </div>