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.
- ivoryos/__init__.py +19 -7
- ivoryos/routes/api/api.py +109 -0
- ivoryos/routes/auth/auth.py +5 -5
- ivoryos/routes/control/control.py +55 -353
- ivoryos/routes/control/control_file.py +36 -0
- ivoryos/routes/control/control_new_device.py +142 -0
- ivoryos/routes/control/templates/controllers.html +137 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/control/utils.py +38 -0
- ivoryos/routes/data/data.py +108 -0
- ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +7 -7
- ivoryos/routes/{database/templates/database → data/templates}/workflow_view.html +3 -3
- ivoryos/routes/design/__init__.py +4 -0
- ivoryos/routes/design/design.py +96 -517
- ivoryos/routes/design/design_file.py +57 -0
- ivoryos/routes/design/design_step.py +43 -0
- ivoryos/routes/design/templates/components/action_form.html +52 -0
- ivoryos/routes/design/templates/components/action_list.html +15 -0
- ivoryos/routes/design/templates/components/autofill_toggle.html +14 -0
- ivoryos/routes/design/templates/components/canvas.html +14 -0
- ivoryos/routes/design/templates/components/canvas_footer.html +5 -0
- ivoryos/routes/design/templates/components/canvas_header.html +54 -0
- ivoryos/routes/design/templates/components/deck_selector.html +12 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +29 -0
- ivoryos/routes/design/templates/components/instrument_panel.html +23 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +19 -0
- ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +18 -0
- ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
- ivoryos/routes/design/templates/components/modals.html +6 -0
- ivoryos/routes/design/templates/components/operations_panel.html +43 -0
- ivoryos/routes/design/templates/components/python_code_overlay.html +17 -0
- ivoryos/routes/design/templates/components/script_info.html +31 -0
- ivoryos/routes/design/templates/components/scripts.html +50 -0
- ivoryos/routes/design/templates/components/sidebar.html +16 -0
- ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos/routes/design/templates/experiment_builder.html +41 -0
- ivoryos/routes/execute/__init__.py +0 -0
- ivoryos/routes/execute/execute.py +173 -0
- ivoryos/routes/execute/execute_file.py +44 -0
- ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
- ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
- ivoryos/routes/execute/templates/components/run_panel.html +9 -0
- ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +147 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
- ivoryos/routes/execute/templates/experiment_run.html +294 -0
- ivoryos/routes/library/__init__.py +0 -0
- ivoryos/routes/{database/database.py → library/library.py} +10 -112
- ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +8 -8
- ivoryos/routes/main/main.py +1 -1
- ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
- ivoryos/socket_handlers.py +52 -0
- ivoryos/templates/base.html +4 -4
- ivoryos/utils/bo_campaign.py +43 -3
- ivoryos/utils/form.py +1 -0
- ivoryos/utils/py_to_json.py +225 -0
- ivoryos/utils/script_runner.py +30 -7
- ivoryos/version.py +1 -1
- {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/METADATA +5 -8
- ivoryos-1.1.0.dist-info/RECORD +102 -0
- ivoryos/routes/control/templates/control/controllers.html +0 -78
- ivoryos/routes/control/templates/control/controllers_home.html +0 -55
- ivoryos/routes/control/templates/control/controllers_new.html +0 -89
- ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
- ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- ivoryos-1.0.8.dist-info/RECORD +0 -61
- /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
- /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
- /ivoryos/routes/{database → data}/__init__.py +0 -0
- /ivoryos/routes/{database/templates/database → data/templates/components}/step_card.html +0 -0
- /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
- {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/LICENSE +0 -0
- {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/WHEEL +0 -0
- {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{# Text-to-code panel component #}
|
|
2
|
+
<div class="accordion-item text-to-code">
|
|
3
|
+
<h2 class="accordion-header">
|
|
4
|
+
<button class="accordion-button text-to-code" type="button" data-bs-toggle="collapse" data-bs-target="#text-to-code" aria-expanded="false" aria-controls="collapseExample">
|
|
5
|
+
Text-to-Code
|
|
6
|
+
</button>
|
|
7
|
+
</h2>
|
|
8
|
+
<div id="text-to-code" class="accordion-collapse collapse show" data-bs-parent="#accordionActions">
|
|
9
|
+
<div class="accordion-body">
|
|
10
|
+
<form role="form" method='POST' name="generate" id="generate" action="{{url_for('design.generate_code')}}">
|
|
11
|
+
<input type="hidden" id="instrument" name="instrument" value="{{instrument}}">
|
|
12
|
+
<textarea class="form-control" id="prompt" name="prompt" rows="6" aria-describedby="promptHelpBlock">{{ session['prompt'][instrument] if instrument in session['prompt'] else '' }}</textarea>
|
|
13
|
+
<div id="promptHelpBlock" class="form-text">
|
|
14
|
+
This will overwrite current design.
|
|
15
|
+
</div>
|
|
16
|
+
<button type="submit" class="btn btn-dark" id="gen" name="gen">Generate</button>
|
|
17
|
+
</form>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
{% block title %}IvoryOS | Design{% endblock %}
|
|
3
|
+
|
|
4
|
+
{% block body %}
|
|
5
|
+
{# overlay block for text-to-code gen #}
|
|
6
|
+
<style>
|
|
7
|
+
.code-overlay {
|
|
8
|
+
position: absolute;
|
|
9
|
+
top: 0;
|
|
10
|
+
right: -50%;
|
|
11
|
+
height: 100%;
|
|
12
|
+
width: 50%;
|
|
13
|
+
z-index: 100;
|
|
14
|
+
background: #f8f9fa;
|
|
15
|
+
box-shadow: -2px 0 6px rgba(0, 0, 0, 0.1);
|
|
16
|
+
transition: right 0.3s ease;
|
|
17
|
+
overflow-y: auto;
|
|
18
|
+
}
|
|
19
|
+
.code-overlay.show {
|
|
20
|
+
right: 0;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
23
|
+
<div id="overlay" class="overlay">
|
|
24
|
+
<div>
|
|
25
|
+
<h3 id="overlay-text">Generating design, please wait...</h3>
|
|
26
|
+
<div class="spinner-border" role="status"></div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="row">
|
|
31
|
+
{% include 'components/sidebar.html' %}
|
|
32
|
+
{% include 'components/canvas.html' %}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
{# Include all modals #}
|
|
36
|
+
{% include 'components/modals.html' %}
|
|
37
|
+
|
|
38
|
+
{# Include all scripts #}
|
|
39
|
+
{% include 'components/scripts.html' %}
|
|
40
|
+
|
|
41
|
+
{% endblock %}
|
|
File without changes
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from flask import Blueprint, redirect, url_for, flash, jsonify, request, render_template, session, \
|
|
6
|
+
current_app, g
|
|
7
|
+
from flask_login import login_required
|
|
8
|
+
|
|
9
|
+
from ivoryos.routes.execute.execute_file import files
|
|
10
|
+
from ivoryos.utils import utils
|
|
11
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
12
|
+
from ivoryos.utils.form import create_action_button, format_name, create_form_from_pseudo, \
|
|
13
|
+
create_form_from_action, create_all_builtin_forms
|
|
14
|
+
|
|
15
|
+
from werkzeug.utils import secure_filename
|
|
16
|
+
|
|
17
|
+
from ivoryos.socket_handlers import runner
|
|
18
|
+
|
|
19
|
+
execute = Blueprint('execute', __name__, template_folder='templates')
|
|
20
|
+
|
|
21
|
+
execute.register_blueprint(files)
|
|
22
|
+
# Register sub-blueprints
|
|
23
|
+
global_config = GlobalConfig()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@execute.route("/campaign", methods=['GET', 'POST'])
|
|
27
|
+
@login_required
|
|
28
|
+
def experiment_run():
|
|
29
|
+
"""
|
|
30
|
+
.. :quickref: Workflow Execution; Execute/iterate the workflow
|
|
31
|
+
|
|
32
|
+
.. http:get:: /design/campaign
|
|
33
|
+
|
|
34
|
+
Compile the workflow and load the experiment execution interface.
|
|
35
|
+
|
|
36
|
+
.. http:post:: /design/campaign
|
|
37
|
+
|
|
38
|
+
Start workflow execution
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
deck = global_config.deck
|
|
42
|
+
script = utils.get_script_file()
|
|
43
|
+
# runner = global_config.runner
|
|
44
|
+
existing_data = None
|
|
45
|
+
# script.sort_actions() # handled in update list
|
|
46
|
+
off_line = current_app.config["OFF_LINE"]
|
|
47
|
+
deck_list = utils.import_history(os.path.join(current_app.config["OUTPUT_FOLDER"], 'deck_history.txt'))
|
|
48
|
+
|
|
49
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
50
|
+
config_preview = []
|
|
51
|
+
config_file_list = [i for i in os.listdir(current_app.config["CSV_FOLDER"]) if not i == ".gitkeep"]
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
exec_string = script.python_script if script.python_script else script.compile(
|
|
55
|
+
current_app.config['SCRIPT_FOLDER'])
|
|
56
|
+
except Exception as e:
|
|
57
|
+
flash(e.__str__())
|
|
58
|
+
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
59
|
+
return jsonify({"error": e.__str__()})
|
|
60
|
+
else:
|
|
61
|
+
return redirect(url_for("design.experiment_builder"))
|
|
62
|
+
|
|
63
|
+
config_file = request.args.get("filename")
|
|
64
|
+
config = []
|
|
65
|
+
if config_file:
|
|
66
|
+
session['config_file'] = config_file
|
|
67
|
+
filename = session.get("config_file")
|
|
68
|
+
if filename:
|
|
69
|
+
config = list(csv.DictReader(open(os.path.join(current_app.config['CSV_FOLDER'], filename))))
|
|
70
|
+
config_preview = config[1:]
|
|
71
|
+
arg_type = config.pop(0) # first entry is types
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
# Handle both string and dict exec_string
|
|
75
|
+
if isinstance(exec_string, dict):
|
|
76
|
+
for key, func_str in exec_string.items():
|
|
77
|
+
exec(func_str)
|
|
78
|
+
line_collection = script.convert_to_lines(exec_string)
|
|
79
|
+
else:
|
|
80
|
+
# Handle string case - you might need to adjust this based on your needs
|
|
81
|
+
line_collection = []
|
|
82
|
+
except Exception:
|
|
83
|
+
flash(f"Please check syntax!!")
|
|
84
|
+
return redirect(url_for("design.experiment_builder"))
|
|
85
|
+
|
|
86
|
+
run_name = script.name if script.name else "untitled"
|
|
87
|
+
|
|
88
|
+
dismiss = session.get("dismiss", None)
|
|
89
|
+
script = utils.get_script_file()
|
|
90
|
+
no_deck_warning = False
|
|
91
|
+
|
|
92
|
+
_, return_list = script.config_return()
|
|
93
|
+
config_list, config_type_list = script.config("script")
|
|
94
|
+
data_list = os.listdir(current_app.config['DATA_FOLDER'])
|
|
95
|
+
data_list.remove(".gitkeep") if ".gitkeep" in data_list else data_list
|
|
96
|
+
|
|
97
|
+
if deck is None:
|
|
98
|
+
no_deck_warning = True
|
|
99
|
+
flash(f"No deck is found, import {script.deck}")
|
|
100
|
+
elif script.deck:
|
|
101
|
+
is_deck_match = script.deck == deck.__name__ or script.deck == \
|
|
102
|
+
os.path.splitext(os.path.basename(deck.__file__))[0]
|
|
103
|
+
if not is_deck_match:
|
|
104
|
+
flash(f"This script is not compatible with current deck, import {script.deck}")
|
|
105
|
+
|
|
106
|
+
if request.method == "POST":
|
|
107
|
+
bo_args = None
|
|
108
|
+
compiled = False
|
|
109
|
+
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
110
|
+
payload_json = request.get_json()
|
|
111
|
+
compiled = True
|
|
112
|
+
if "kwargs" in payload_json:
|
|
113
|
+
config = payload_json["kwargs"]
|
|
114
|
+
elif "parameters" in payload_json:
|
|
115
|
+
bo_args = payload_json
|
|
116
|
+
repeat = payload_json.pop("repeat", None)
|
|
117
|
+
else:
|
|
118
|
+
if "bo" in request.form:
|
|
119
|
+
bo_args = request.form.to_dict()
|
|
120
|
+
existing_data = bo_args.pop("existing_data")
|
|
121
|
+
if "online-config" in request.form:
|
|
122
|
+
config = utils.web_config_entry_wrapper(request.form.to_dict(), config_list)
|
|
123
|
+
repeat = request.form.get('repeat', None)
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
datapath = current_app.config["DATA_FOLDER"]
|
|
127
|
+
run_name = script.validate_function_name(run_name)
|
|
128
|
+
runner.run_script(script=script, run_name=run_name, config=config, bo_args=bo_args,
|
|
129
|
+
logger=g.logger, socketio=g.socketio, repeat_count=repeat,
|
|
130
|
+
output_path=datapath, compiled=compiled, history=existing_data,
|
|
131
|
+
current_app=current_app._get_current_object()
|
|
132
|
+
)
|
|
133
|
+
if utils.check_config_duplicate(config):
|
|
134
|
+
flash(f"WARNING: Duplicate in config entries.")
|
|
135
|
+
except Exception as e:
|
|
136
|
+
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
137
|
+
return jsonify({"error": e.__str__()})
|
|
138
|
+
else:
|
|
139
|
+
flash(e)
|
|
140
|
+
|
|
141
|
+
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
142
|
+
# wait to get a workflow ID
|
|
143
|
+
while not global_config.runner_status:
|
|
144
|
+
time.sleep(1)
|
|
145
|
+
return jsonify({"status": "task started", "task_id": global_config.runner_status.get("id")})
|
|
146
|
+
else:
|
|
147
|
+
return render_template('experiment_run.html', script=script.script_dict, filename=filename,
|
|
148
|
+
dot_py=exec_string, line_collection=line_collection,
|
|
149
|
+
return_list=return_list, config_list=config_list, config_file_list=config_file_list,
|
|
150
|
+
config_preview=config_preview, data_list=data_list, config_type_list=config_type_list,
|
|
151
|
+
no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons,
|
|
152
|
+
history=deck_list, pause_status=runner.pause_status())
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@execute.route('/data_preview/<filename>')
|
|
156
|
+
@login_required
|
|
157
|
+
def data_preview(filename):
|
|
158
|
+
"""Serve a preview of the selected data file (CSV) as JSON."""
|
|
159
|
+
import csv
|
|
160
|
+
import os
|
|
161
|
+
from flask import abort
|
|
162
|
+
|
|
163
|
+
data_folder = current_app.config['DATA_FOLDER']
|
|
164
|
+
file_path = os.path.join(data_folder, filename)
|
|
165
|
+
if not os.path.exists(file_path):
|
|
166
|
+
abort(404)
|
|
167
|
+
with open(file_path, newline='') as csvfile:
|
|
168
|
+
reader = csv.DictReader(csvfile)
|
|
169
|
+
rows = list(reader)
|
|
170
|
+
# Limit preview to first 10 rows
|
|
171
|
+
return jsonify({"columns": reader.fieldnames, "rows": rows})
|
|
172
|
+
|
|
173
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
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('execute_files', __name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@files.route('/upload/config', methods=['POST'])
|
|
13
|
+
def upload():
|
|
14
|
+
"""Upload a workflow config file (.CSV)"""
|
|
15
|
+
if request.method == "POST":
|
|
16
|
+
f = request.files['file']
|
|
17
|
+
if 'file' not in request.files:
|
|
18
|
+
flash('No file part')
|
|
19
|
+
if f.filename.split('.')[-1] == "csv":
|
|
20
|
+
filename = secure_filename(f.filename)
|
|
21
|
+
f.save(os.path.join(current_app.config['CSV_FOLDER'], filename))
|
|
22
|
+
session['config_file'] = filename
|
|
23
|
+
return redirect(url_for("execute.experiment_run"))
|
|
24
|
+
else:
|
|
25
|
+
flash("Config file is in csv format")
|
|
26
|
+
return redirect(url_for("execute.experiment_run"))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@files.route('/upload/history', methods=['POST'])
|
|
30
|
+
def upload_history():
|
|
31
|
+
"""Upload a workflow history file (.CSV)"""
|
|
32
|
+
if request.method == "POST":
|
|
33
|
+
f = request.files['historyfile']
|
|
34
|
+
if 'historyfile' not in request.files:
|
|
35
|
+
flash('No file part')
|
|
36
|
+
if f.filename.split('.')[-1] == "csv":
|
|
37
|
+
filename = secure_filename(f.filename)
|
|
38
|
+
f.save(os.path.join(current_app.config['DATA_FOLDER'], filename))
|
|
39
|
+
return redirect(url_for("execute.experiment_run"))
|
|
40
|
+
else:
|
|
41
|
+
flash("Config file is in csv format")
|
|
42
|
+
return redirect(url_for("execute.experiment_run"))
|
|
43
|
+
|
|
44
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{# Error modal component for experiment run #}
|
|
2
|
+
<div class="modal fade" id="error-modal" tabindex="-1" aria-labelledby="errorModalLabel" aria-hidden="true">
|
|
3
|
+
<div class="modal-dialog">
|
|
4
|
+
<div class="modal-content">
|
|
5
|
+
<div class="modal-header">
|
|
6
|
+
<h5 class="modal-title" id="errorModalLabel">Error Detected</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 id="error-message">An error has occurred.</p>
|
|
11
|
+
<p>Do you want to continue execution or stop?</p>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="modal-footer">
|
|
14
|
+
<button type="button" class="btn btn-primary" id="retry-btn" data-bs-dismiss="modal">Rerun Current Step</button>
|
|
15
|
+
<button type="button" class="btn btn-success" id="continue-btn" data-bs-dismiss="modal">Continue</button>
|
|
16
|
+
<button type="button" class="btn btn-danger" id="stop-btn" data-bs-dismiss="modal">Stop Execution</button>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{# Logging panel component for experiment run #}
|
|
2
|
+
<div class="col-lg-6 col-sm-12 logging-panel">
|
|
3
|
+
<p>
|
|
4
|
+
<div class="p d-flex justify-content-between align-items-center">
|
|
5
|
+
<h5>Progress:</h5>
|
|
6
|
+
<div class="d-flex gap-2 ms-auto">
|
|
7
|
+
<button id="pause-resume" class="btn btn-info text-white" data-bs-toggle="tooltip" title="Pause execution">
|
|
8
|
+
{% if pause_status %}
|
|
9
|
+
<i class="bi bi-play-circle"></i>
|
|
10
|
+
{% else %}
|
|
11
|
+
<i class="bi bi-pause-circle"></i>
|
|
12
|
+
{% endif %}
|
|
13
|
+
</button>
|
|
14
|
+
<button id="abort-current" class="btn btn-danger text-white" data-bs-toggle="tooltip" title="Stop execution after current step">
|
|
15
|
+
<i class="bi bi-stop-circle"></i>
|
|
16
|
+
</button>
|
|
17
|
+
<button id="abort-pending" class="btn btn-warning text-white" data-bs-toggle="tooltip" title="Stop execution after current iteration">
|
|
18
|
+
<i class="bi bi-hourglass-split"></i>
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="text-muted mt-2">
|
|
23
|
+
<small><strong>Note:</strong> The current step cannot be paused or stopped until it completes. </small>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="progress" role="progressbar" aria-label="Animated striped example" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100">
|
|
27
|
+
<div id="progress-bar-inner" class="progress-bar progress-bar-striped progress-bar-animated"></div>
|
|
28
|
+
</div>
|
|
29
|
+
<p><h5>Log:</h5></p>
|
|
30
|
+
<div id="logging-panel"></div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{# Progress panel component for experiment run #}
|
|
2
|
+
<div class="col-lg-6 col-sm-12" id="code-panel" style="{{ '' if pause_status else 'display: none;'}}">
|
|
3
|
+
<p>
|
|
4
|
+
<h5>Progress:</h5>
|
|
5
|
+
{% if "prep" in line_collection.keys() %}
|
|
6
|
+
{% set stype = "prep" %}
|
|
7
|
+
<h6>Preparation:</h6>
|
|
8
|
+
{% for code in line_collection["prep"] %}
|
|
9
|
+
<pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
|
|
10
|
+
{% endfor %}
|
|
11
|
+
{% endif %}
|
|
12
|
+
{% if "script" in line_collection.keys() %}
|
|
13
|
+
{% set stype = "script" %}
|
|
14
|
+
<h6>Experiment:</h6>
|
|
15
|
+
{% for code in line_collection["script"] %}
|
|
16
|
+
<pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
|
|
17
|
+
{% endfor %}
|
|
18
|
+
{% endif %}
|
|
19
|
+
{% if "cleanup" in line_collection.keys() %}
|
|
20
|
+
{% set stype = "cleanup" %}
|
|
21
|
+
<h6>Cleanup:</h6>
|
|
22
|
+
{% for code in line_collection["cleanup"] %}
|
|
23
|
+
<pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
{% endif %}
|
|
26
|
+
</p>
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{# Run panel component for experiment run #}
|
|
2
|
+
{% if script['script'] or script['prep'] or script['cleanup'] %}
|
|
3
|
+
<div class="col-lg-6 col-sm-12" id="run-panel" style="{{ 'display: none;' if pause_status else '' }}">
|
|
4
|
+
{% include 'components/run_tabs.html' %}
|
|
5
|
+
</div>
|
|
6
|
+
{% else %}
|
|
7
|
+
<div class="col-lg-6 col-sm-12" id="placeholder-panel">
|
|
8
|
+
</div>
|
|
9
|
+
{% endif %}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{# Run tabs component for experiment run #}
|
|
2
|
+
<ul class="nav nav-tabs" id="myTabs" role="tablist">
|
|
3
|
+
<li class="nav-item" role="presentation">
|
|
4
|
+
<a class="nav-link {{ 'disabled' if config_list else '' }} {{ 'active' if not config_list else '' }}" id="tab1-tab" data-bs-toggle="tab" href="#tab1" role="tab" aria-controls="tab1" aria-selected="false">Repeat</a>
|
|
5
|
+
</li>
|
|
6
|
+
<li class="nav-item" role="presentation">
|
|
7
|
+
<a class="nav-link {{ 'disabled' if not config_list else '' }} {{ 'active' if config_list else '' }}" id="tab2-tab" data-bs-toggle="tab" href="#tab2" role="tab" aria-controls="tab2" aria-selected="false">Configuration</a>
|
|
8
|
+
</li>
|
|
9
|
+
<li class="nav-item" role="presentation">
|
|
10
|
+
<a class="nav-link {{ 'disabled' if not config_list or not return_list else '' }}" id="tab3-tab" data-bs-toggle="tab" href="#tab3" role="tab" aria-controls="tab3" aria-selected="false">Bayesian Optimization</a>
|
|
11
|
+
</li>
|
|
12
|
+
</ul>
|
|
13
|
+
<div class="tab-content" id="myTabsContent">
|
|
14
|
+
{% include 'components/tab_repeat.html' %}
|
|
15
|
+
{% include 'components/tab_configuration.html' %}
|
|
16
|
+
{% include 'components/tab_bayesian.html' %}
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{# Bayesian optimization tab component #}
|
|
2
|
+
<div class="tab-pane fade" id="tab3" role="tabpanel" aria-labelledby="tab3-tab">
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
{# <div class="row align-items-center mb-3">#}
|
|
6
|
+
{# <div class="col-6">#}
|
|
7
|
+
{# <form method="POST" id="loadHistory" name="loadHistory" action="{{ url_for('execute.execute_files.upload_history') }}" enctype="multipart/form-data">#}
|
|
8
|
+
{# <div class="input-group">#}
|
|
9
|
+
{# <input class="form-control" name="historyfile" id="historyfile" type="file" accept=".csv" onchange="var f=document.getElementById('loadHistory'); if(f) f.submit();"> </div>#}
|
|
10
|
+
{# </form>#}
|
|
11
|
+
{# </div>#}
|
|
12
|
+
{# </div>#}
|
|
13
|
+
<h6 class="fw-bold mt-2 mb-1">Load Previous Data</h6>
|
|
14
|
+
|
|
15
|
+
<form method="POST" name="bo" action="{{ url_for('execute.experiment_run') }}">
|
|
16
|
+
<div class="container py-2">
|
|
17
|
+
<!-- Data Loading Section -->
|
|
18
|
+
<div class="input-group">
|
|
19
|
+
<label class="input-group-text"><i class="bi bi-folder2-open"></i></label>
|
|
20
|
+
<select class="form-select" id="existing_data" name="existing_data">
|
|
21
|
+
<option value="">Load existing data...</option>
|
|
22
|
+
{% for data in data_list %}
|
|
23
|
+
<option value="{{ data }}">{{ data }} </option>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</select>
|
|
26
|
+
</div>
|
|
27
|
+
<!-- Custom path input -->
|
|
28
|
+
<div class="row align-items-center mb-3" id="custom_path_row" style="display: none;">
|
|
29
|
+
<div class="col-3 col-form-label-sm">
|
|
30
|
+
Custom Path:
|
|
31
|
+
</div>
|
|
32
|
+
<div class="col-7">
|
|
33
|
+
<input type="text" class="form-control form-control-sm" id="custom_path" name="custom_path" placeholder="/path/to/data/folder">
|
|
34
|
+
</div>
|
|
35
|
+
<div class="col-2">
|
|
36
|
+
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="validateCustomPath()">Validate</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Data preview section -->
|
|
41
|
+
<div class="row mb-3" id="data_preview_section" style="display: none;">
|
|
42
|
+
<div class="col-12">
|
|
43
|
+
<div class="card">
|
|
44
|
+
<div class="card-header py-2">
|
|
45
|
+
<small class="fw-bold">Data Preview</small>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="card-body py-2">
|
|
48
|
+
<div id="data_preview_content">
|
|
49
|
+
<small class="text-muted">Select a data source to preview</small>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<hr class="my-3">
|
|
57
|
+
|
|
58
|
+
<!-- Parameters -->
|
|
59
|
+
<h6 class="fw-bold mt-2 mb-1">Parameters</h6>
|
|
60
|
+
{% for config in config_list %}
|
|
61
|
+
<div class="row align-items-center mb-2">
|
|
62
|
+
<div class="col-3 col-form-label-sm">
|
|
63
|
+
{{ config }}:
|
|
64
|
+
</div>
|
|
65
|
+
<div class="col-6">
|
|
66
|
+
<select class="form-select form-select-sm" id="{{config}}_type" name="{{config}}_type">
|
|
67
|
+
<option selected value="range">range</option>
|
|
68
|
+
<option value="choice">choice</option>
|
|
69
|
+
<option value="fixed">fixed</option>
|
|
70
|
+
</select>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="col-3">
|
|
73
|
+
<input type="text" class="form-control form-control-sm" id="{{config}}_value" name="{{config}}_value" placeholder="1, 2, 3">
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
{% endfor %}
|
|
77
|
+
<!-- Objective -->
|
|
78
|
+
<h6 class="fw-bold mt-3 mb-1">Objectives</h6>
|
|
79
|
+
{% for objective in return_list %}
|
|
80
|
+
<div class="row align-items-center mb-2">
|
|
81
|
+
<div class="col-3 col-form-label-sm">
|
|
82
|
+
{{ objective }}:
|
|
83
|
+
</div>
|
|
84
|
+
<div class="col-6">
|
|
85
|
+
<select class="form-select form-select-sm" id="{{objective}}_min" name="{{objective}}_min">
|
|
86
|
+
<option selected>minimize</option>
|
|
87
|
+
<option>maximize</option>
|
|
88
|
+
<option>none</option>
|
|
89
|
+
</select>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
{% endfor %}
|
|
93
|
+
<h6 class="fw-bold mt-3 mb-1">Budget</h6>
|
|
94
|
+
<div class="input-group mb-3">
|
|
95
|
+
<label class="input-group-text" for="repeat">Max iteration </label>
|
|
96
|
+
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="25">
|
|
97
|
+
</div>
|
|
98
|
+
{% if not no_deck_warning%}
|
|
99
|
+
<div class="input-group mb-3">
|
|
100
|
+
<button class="form-control" type="submit" name="bo">Run</button>
|
|
101
|
+
</div>
|
|
102
|
+
{% endif %}
|
|
103
|
+
</div>
|
|
104
|
+
</form>
|
|
105
|
+
</div>
|
|
106
|
+
<script>
|
|
107
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
108
|
+
const dataSelect = document.getElementById('existing_data');
|
|
109
|
+
const previewSection = document.getElementById('data_preview_section');
|
|
110
|
+
const previewContent = document.getElementById('data_preview_content');
|
|
111
|
+
|
|
112
|
+
dataSelect.addEventListener('change', function() {
|
|
113
|
+
const filename = dataSelect.value;
|
|
114
|
+
if (!filename) {
|
|
115
|
+
previewSection.style.display = 'none';
|
|
116
|
+
previewContent.innerHTML = '<small class="text-muted">Select a data source to preview</small>';
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
fetch('{{ url_for("execute.data_preview", filename="FILENAME") }}'.replace('FILENAME', encodeURIComponent(filename)))
|
|
120
|
+
.then(response => {
|
|
121
|
+
if (!response.ok) throw new Error('Network response was not ok');
|
|
122
|
+
return response.json();
|
|
123
|
+
})
|
|
124
|
+
.then(data => {
|
|
125
|
+
previewSection.style.display = '';
|
|
126
|
+
if (!data.rows || data.rows.length === 0) {
|
|
127
|
+
previewContent.innerHTML = '<small class="text-muted">No data found in file.</small>';
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let html = '<table class="table table-sm table-bordered mb-0"><thead><tr>';
|
|
131
|
+
data.columns.forEach(col => html += `<th>${col}</th>`);
|
|
132
|
+
html += '</tr></thead><tbody>';
|
|
133
|
+
data.rows.forEach(row => {
|
|
134
|
+
html += '<tr>';
|
|
135
|
+
data.columns.forEach(col => html += `<td>${row[col] || ''}</td>`);
|
|
136
|
+
html += '</tr>';
|
|
137
|
+
});
|
|
138
|
+
html += '</tbody></table>';
|
|
139
|
+
previewContent.innerHTML = html;
|
|
140
|
+
})
|
|
141
|
+
.catch(() => {
|
|
142
|
+
previewSection.style.display = '';
|
|
143
|
+
previewContent.innerHTML = '<small class="text-danger">Failed to load preview.</small>';
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
</script>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{# Configuration tab component #}
|
|
2
|
+
<div class="tab-pane fade {{ 'show active' if config_list else '' }}" id="tab2" role="tabpanel" aria-labelledby="tab2-tab">
|
|
3
|
+
<!-- File Management Section -->
|
|
4
|
+
<div class="card mb-4">
|
|
5
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
6
|
+
<h6 class="mb-0"><i class="bi bi-file-earmark-text"></i> Configuration File</h6>
|
|
7
|
+
<small class="text-muted">
|
|
8
|
+
<a href="{{ url_for('design.design_files.download', filetype='configure') }}">
|
|
9
|
+
<i class="bi bi-download"></i> Download Empty Template
|
|
10
|
+
</a>
|
|
11
|
+
</small>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="card-body">
|
|
14
|
+
<div class="row g-3">
|
|
15
|
+
<!-- File Selection -->
|
|
16
|
+
<div class="col-md-6">
|
|
17
|
+
<form name="filenameForm" id="filenameForm" method="GET" action="{{ url_for('execute.experiment_run') }}" enctype="multipart/form-data">
|
|
18
|
+
<div class="input-group">
|
|
19
|
+
<label class="input-group-text"><i class="bi bi-folder2-open"></i></label>
|
|
20
|
+
<select class="form-select" name="filename" id="filenameSelect" onchange="document.getElementById('filenameForm').submit();">
|
|
21
|
+
<option {{ 'selected' if not filename else '' }} value="">-- Select existing file --</option>
|
|
22
|
+
{% for config_file in config_file_list %}
|
|
23
|
+
<option {{ 'selected' if filename == config_file else '' }} value="{{ config_file }}">{{ config_file }}</option>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</select>
|
|
26
|
+
</div>
|
|
27
|
+
</form>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- File Upload -->
|
|
31
|
+
<div class="col-md-6">
|
|
32
|
+
<form method="POST" id="loadFile" name="loadFile" action="{{ url_for('execute.execute_files.upload') }}" enctype="multipart/form-data">
|
|
33
|
+
<div class="input-group">
|
|
34
|
+
<input class="form-control" name="file" type="file" accept=".csv" required="required" onchange="document.getElementById('loadFile').submit();">
|
|
35
|
+
</div>
|
|
36
|
+
</form>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Configuration Table -->
|
|
43
|
+
<div class="card mb-4">
|
|
44
|
+
<div class="card-header position-relative">
|
|
45
|
+
<div class="position-absolute top-50 end-0 translate-middle-y me-3">
|
|
46
|
+
<span id="saveStatus" class="badge bg-success" style="display: none;">
|
|
47
|
+
<i class="bi bi-check-circle"></i> Auto-saved
|
|
48
|
+
</span>
|
|
49
|
+
<span id="modifiedStatus" class="badge bg-warning" style="display: none;">
|
|
50
|
+
<i class="bi bi-pencil"></i> Modified
|
|
51
|
+
</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="card-body p-0">
|
|
55
|
+
<form method="POST" name="online-config" id="online-config" action="{{url_for('execute.experiment_run')}}">
|
|
56
|
+
<div class="table-responsive">
|
|
57
|
+
<table id="dataInputTable" class="table table-striped table-hover mb-0">
|
|
58
|
+
<thead class="table-dark">
|
|
59
|
+
<tr>
|
|
60
|
+
<th style="width: 40px;">#</th>
|
|
61
|
+
{% for column in config_list %}
|
|
62
|
+
<th>{{ column }}</th>
|
|
63
|
+
{% endfor %}
|
|
64
|
+
<th></th>
|
|
65
|
+
</tr>
|
|
66
|
+
</thead>
|
|
67
|
+
<tbody id="tableBody">
|
|
68
|
+
</tbody>
|
|
69
|
+
</table>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="card-footer">
|
|
72
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
73
|
+
<div class="d-flex gap-2">
|
|
74
|
+
<button type="button" class="btn btn-success" onclick="addRow()">
|
|
75
|
+
<i class="bi bi-plus-circle"></i> Add Row
|
|
76
|
+
</button>
|
|
77
|
+
<button type="button" class="btn btn-warning" onclick="clearAllRows()">
|
|
78
|
+
<i class="bi bi-trash"></i> Clear All
|
|
79
|
+
</button>
|
|
80
|
+
<button type="button" class="btn btn-info" onclick="resetToFile()" id="resetToFileBtn" style="display: none;">
|
|
81
|
+
<i class="bi bi-arrow-clockwise"></i> Reset to File
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
<button type="submit" name="online-config" class="btn btn-primary btn-lg">
|
|
85
|
+
<i class="bi bi-play-circle"></i> Run
|
|
86
|
+
</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</form>
|
|
90
|
+
</div>
|
|
91
|
+
<!-- Config Preview (if loaded from file) -->
|
|
92
|
+
{% if config_preview %}
|
|
93
|
+
<div class="alert alert-info">
|
|
94
|
+
<small><i class="bi bi-info-circle"></i> {{ config_preview|length }} rows loaded from {{ filename }}</small>
|
|
95
|
+
</div>
|
|
96
|
+
{% endif %}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|