ivoryos 0.1.10__py3-none-any.whl → 0.1.18__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 +54 -14
- ivoryos/routes/control/control.py +1 -1
- ivoryos/routes/control/templates/control/controllers.html +3 -0
- ivoryos/routes/design/design.py +49 -41
- ivoryos/routes/design/templates/design/experiment_builder.html +27 -14
- ivoryos/routes/design/templates/design/experiment_run.html +2 -2
- ivoryos/routes/main/templates/main/home.html +19 -17
- ivoryos/templates/base.html +20 -10
- ivoryos/utils/db_models.py +181 -59
- ivoryos/utils/form.py +204 -81
- ivoryos/utils/script_runner.py +13 -9
- ivoryos/utils/utils.py +27 -43
- ivoryos/version.py +1 -1
- {ivoryos-0.1.10.dist-info → ivoryos-0.1.18.dist-info}/METADATA +4 -1
- {ivoryos-0.1.10.dist-info → ivoryos-0.1.18.dist-info}/RECORD +18 -19
- {ivoryos-0.1.10.dist-info → ivoryos-0.1.18.dist-info}/WHEEL +1 -1
- ivoryos/static/.DS_Store +0 -0
- {ivoryos-0.1.10.dist-info → ivoryos-0.1.18.dist-info}/LICENSE +0 -0
- {ivoryos-0.1.10.dist-info → ivoryos-0.1.18.dist-info}/top_level.txt +0 -0
ivoryos/__init__.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import inspect
|
|
1
3
|
import os
|
|
2
4
|
import sys
|
|
3
5
|
from typing import Union
|
|
4
6
|
|
|
5
|
-
from flask import Flask, redirect, url_for
|
|
7
|
+
from flask import Flask, redirect, url_for, Blueprint, g
|
|
6
8
|
|
|
7
9
|
from ivoryos.config import Config, get_config
|
|
8
10
|
from ivoryos.routes.auth.auth import auth, login_manager
|
|
@@ -10,22 +12,18 @@ from ivoryos.routes.control.control import control
|
|
|
10
12
|
from ivoryos.routes.database.database import database
|
|
11
13
|
from ivoryos.routes.design.design import design, socketio
|
|
12
14
|
from ivoryos.routes.main.main import main
|
|
15
|
+
# from ivoryos.routes.monitor.monitor import monitor
|
|
13
16
|
from ivoryos.utils import utils
|
|
14
17
|
from ivoryos.utils.db_models import db
|
|
15
18
|
from ivoryos.utils.global_config import GlobalConfig
|
|
16
19
|
from ivoryos.utils.script_runner import ScriptRunner
|
|
17
20
|
from ivoryos.version import __version__ as ivoryos_version
|
|
18
|
-
|
|
21
|
+
from importlib.metadata import entry_points
|
|
19
22
|
global_config = GlobalConfig()
|
|
20
23
|
|
|
21
|
-
|
|
22
24
|
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
23
25
|
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
24
|
-
|
|
25
|
-
app.register_blueprint(auth, url_prefix=url_prefix)
|
|
26
|
-
app.register_blueprint(design, url_prefix=url_prefix)
|
|
27
|
-
app.register_blueprint(database, url_prefix=url_prefix)
|
|
28
|
-
app.register_blueprint(control, url_prefix=url_prefix)
|
|
26
|
+
|
|
29
27
|
|
|
30
28
|
def create_app(config_class=None):
|
|
31
29
|
# url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
@@ -51,12 +49,13 @@ def create_app(config_class=None):
|
|
|
51
49
|
|
|
52
50
|
@app.before_request
|
|
53
51
|
def before_request():
|
|
54
|
-
|
|
52
|
+
"""
|
|
53
|
+
Called before
|
|
54
|
+
|
|
55
|
+
"""
|
|
55
56
|
g.logger = logger
|
|
56
57
|
g.socketio = socketio
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
59
|
@app.route('/')
|
|
61
60
|
def redirect_to_prefix():
|
|
62
61
|
return redirect(url_for('main.index', version=ivoryos_version)) # Assuming 'index' is a route in your blueprint
|
|
@@ -68,6 +67,7 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
68
67
|
config: Config = None,
|
|
69
68
|
logger: Union[str, list] = None,
|
|
70
69
|
logger_output_name: str = None,
|
|
70
|
+
enable_design=True
|
|
71
71
|
):
|
|
72
72
|
"""
|
|
73
73
|
Start ivoryOS app server.
|
|
@@ -81,22 +81,43 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
81
81
|
:param config: config class, defaults to None
|
|
82
82
|
:param logger: logger name of list of logger names, defaults to None
|
|
83
83
|
:param logger_output_name: log file save name of logger, defaults to None, and will use "default.log"
|
|
84
|
-
|
|
84
|
+
:param enable_design: enable design canvas, database and workflow execution
|
|
85
|
+
:param stream_address:
|
|
85
86
|
"""
|
|
86
87
|
app = create_app(config_class=config or get_config()) # Create app instance using factory function
|
|
87
88
|
|
|
89
|
+
app.register_blueprint(main, url_prefix=url_prefix)
|
|
90
|
+
app.register_blueprint(auth, url_prefix=url_prefix)
|
|
91
|
+
app.register_blueprint(control, url_prefix=url_prefix)
|
|
92
|
+
|
|
93
|
+
if enable_design:
|
|
94
|
+
app.register_blueprint(design, url_prefix=url_prefix)
|
|
95
|
+
app.register_blueprint(database, url_prefix=url_prefix)
|
|
96
|
+
|
|
97
|
+
plugins = load_plugins(app, socketio)
|
|
98
|
+
|
|
99
|
+
def inject_nav_config():
|
|
100
|
+
"""Make NAV_CONFIG available globally to all templates."""
|
|
101
|
+
return dict(
|
|
102
|
+
enable_design=enable_design,
|
|
103
|
+
plugins=plugins,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
app.context_processor(inject_nav_config)
|
|
88
107
|
port = port or int(os.environ.get("PORT", 8000))
|
|
89
108
|
debug = debug if debug is not None else app.config.get('DEBUG', True)
|
|
90
109
|
|
|
91
110
|
app.config["LOGGERS"] = logger
|
|
92
|
-
app.config["LOGGERS_PATH"] = logger_output_name or app.config["LOGGERS_PATH"]
|
|
111
|
+
app.config["LOGGERS_PATH"] = logger_output_name or app.config["LOGGERS_PATH"] # default.log
|
|
93
112
|
logger_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["LOGGERS_PATH"])
|
|
94
113
|
|
|
95
114
|
if module:
|
|
96
115
|
app.config["MODULE"] = module
|
|
97
116
|
app.config["OFF_LINE"] = False
|
|
98
117
|
global_config.deck = sys.modules[module]
|
|
99
|
-
global_config.
|
|
118
|
+
# global_config.heinsight = HeinsightAPI("http://127.0.0.1:8080")
|
|
119
|
+
global_config.deck_snapshot = utils.create_deck_snapshot(global_config.deck,
|
|
120
|
+
output_path=app.config["DUMMY_DECK"], save=True)
|
|
100
121
|
# global_config.runner = ScriptRunner(globals())
|
|
101
122
|
else:
|
|
102
123
|
app.config["OFF_LINE"] = True
|
|
@@ -116,3 +137,22 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
116
137
|
for log in logger:
|
|
117
138
|
utils.start_logger(socketio, log_filename=logger_path, logger_name=log)
|
|
118
139
|
socketio.run(app, host=host, port=port, debug=debug, use_reloader=False, allow_unsafe_werkzeug=True)
|
|
140
|
+
# return app
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def load_plugins(app, socketio):
|
|
144
|
+
"""
|
|
145
|
+
Dynamically load installed plugins and attach Flask-SocketIO.
|
|
146
|
+
"""
|
|
147
|
+
plugin_names = []
|
|
148
|
+
for entry_point in entry_points().get("ivoryos.plugins", []):
|
|
149
|
+
plugin = entry_point.load()
|
|
150
|
+
|
|
151
|
+
# If the plugin has an `init_socketio()` function, pass socketio
|
|
152
|
+
if hasattr(plugin, 'init_socketio'):
|
|
153
|
+
plugin.init_socketio(socketio)
|
|
154
|
+
|
|
155
|
+
plugin_names.append(entry_point.name)
|
|
156
|
+
app.register_blueprint(getattr(plugin, entry_point.name), url_prefix=f"{url_prefix}/{entry_point.name}")
|
|
157
|
+
|
|
158
|
+
return plugin_names
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
{% if function not in hidden_instrument %}
|
|
17
17
|
<div class="card" id="{{function}}">
|
|
18
18
|
<div class="bg-white rounded shadow-sm flex-fill">
|
|
19
|
+
<i class="bi bi-info-circle ms-2" data-bs-toggle="tooltip" data-bs-placement="top" title='{{ form.hidden_name.description or "Docstring is not available" }}' ></i>
|
|
19
20
|
<a style="float: right" aria-label="Close" href="{{ url_for('control.hide_function', instrument=instrument, function=function) }}"><i class="bi bi-eye-slash-fill"></i></a>
|
|
20
21
|
<div class="form-control" style="border: none">
|
|
21
22
|
<form role="form" method='POST' name="{{function}}" id="{{function}}">
|
|
@@ -38,7 +39,9 @@
|
|
|
38
39
|
</div>
|
|
39
40
|
<div class="input-group mb-3">
|
|
40
41
|
<button type="submit" name="{{ function }}" id="{{ function }}" class="form-control" style="background-color: #a5cece;">{{format_name(function)}} </button>
|
|
42
|
+
|
|
41
43
|
</div>
|
|
44
|
+
|
|
42
45
|
</form>
|
|
43
46
|
</div>
|
|
44
47
|
</div>
|
ivoryos/routes/design/design.py
CHANGED
|
@@ -12,7 +12,8 @@ from werkzeug.utils import secure_filename
|
|
|
12
12
|
|
|
13
13
|
from ivoryos.utils import utils
|
|
14
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
|
|
15
|
+
from ivoryos.utils.form import create_builtin_form, create_action_button, format_name, create_form_from_pseudo, \
|
|
16
|
+
create_form_from_action
|
|
16
17
|
from ivoryos.utils.db_models import Script
|
|
17
18
|
from ivoryos.utils.script_runner import ScriptRunner
|
|
18
19
|
|
|
@@ -99,25 +100,29 @@ def experiment_builder(instrument=None):
|
|
|
99
100
|
|
|
100
101
|
deck_list = utils.available_pseudo_deck(current_app.config["DUMMY_DECK"])
|
|
101
102
|
|
|
102
|
-
functions =
|
|
103
|
+
functions = {}
|
|
103
104
|
if deck:
|
|
104
105
|
deck_variables = global_config.deck_snapshot.keys()
|
|
105
106
|
else:
|
|
106
107
|
deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
|
|
107
108
|
deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
edit_action_info = session.get("edit_action")
|
|
110
|
+
if edit_action_info:
|
|
111
|
+
forms = create_form_from_action(edit_action_info, script=script)
|
|
112
|
+
elif instrument:
|
|
113
|
+
if instrument in ['if', 'while', 'variable', 'wait', 'repeat']:
|
|
114
|
+
forms = create_builtin_form(instrument, script=script)
|
|
111
115
|
else:
|
|
112
116
|
if deck:
|
|
113
|
-
|
|
117
|
+
functions = global_config.deck_snapshot.get(instrument, {})
|
|
114
118
|
elif pseudo_deck:
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
functions = pseudo_deck.get(instrument, {})
|
|
120
|
+
# print(function_metadata)
|
|
121
|
+
# functions = {key: data.get('signature', {}) for key, data in function_metadata.items()}
|
|
117
122
|
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
118
123
|
if request.method == 'POST' and "hidden_name" in request.form:
|
|
119
|
-
all_kwargs = request.form.copy()
|
|
120
|
-
method_name =
|
|
124
|
+
# all_kwargs = request.form.copy()
|
|
125
|
+
method_name = request.form.get("hidden_name", None)
|
|
121
126
|
# if method_name is not None:
|
|
122
127
|
form = forms.get(method_name)
|
|
123
128
|
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
@@ -125,29 +130,15 @@ def experiment_builder(instrument=None):
|
|
|
125
130
|
if form and form.validate_on_submit():
|
|
126
131
|
function_name = kwargs.pop("hidden_name")
|
|
127
132
|
save_data = kwargs.pop('return', '')
|
|
128
|
-
variable_kwargs = {}
|
|
129
|
-
variable_kwargs_types = {}
|
|
130
133
|
|
|
131
|
-
|
|
132
|
-
variable_kwargs, variable_kwargs_types = utils.find_variable_in_script(script, kwargs)
|
|
133
|
-
|
|
134
|
-
for name in variable_kwargs.keys():
|
|
135
|
-
del kwargs[name]
|
|
136
|
-
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
137
|
-
|
|
138
|
-
except:
|
|
139
|
-
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
140
|
-
|
|
141
|
-
kwargs.update(variable_kwargs)
|
|
142
|
-
arg_types = {}
|
|
143
|
-
arg_types.update(variable_kwargs_types)
|
|
144
|
-
arg_types.update(primitive_arg_types)
|
|
145
|
-
all_kwargs.update(variable_kwargs)
|
|
134
|
+
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
146
135
|
|
|
136
|
+
script.eval_list(kwargs, primitive_arg_types)
|
|
137
|
+
kwargs = script.validate_variables(kwargs)
|
|
147
138
|
action = {"instrument": instrument, "action": function_name,
|
|
148
|
-
"args":
|
|
139
|
+
"args": kwargs,
|
|
149
140
|
"return": save_data,
|
|
150
|
-
'arg_types':
|
|
141
|
+
'arg_types': primitive_arg_types}
|
|
151
142
|
script.add_action(action=action)
|
|
152
143
|
else:
|
|
153
144
|
flash(form.errors)
|
|
@@ -155,9 +146,13 @@ def experiment_builder(instrument=None):
|
|
|
155
146
|
elif request.method == 'POST' and "builtin_name" in request.form:
|
|
156
147
|
kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
|
|
157
148
|
if forms.validate_on_submit():
|
|
149
|
+
# print(kwargs)
|
|
158
150
|
logic_type = kwargs.pop('builtin_name')
|
|
159
151
|
if 'variable' in kwargs:
|
|
160
|
-
|
|
152
|
+
try:
|
|
153
|
+
script.add_variable(**kwargs)
|
|
154
|
+
except ValueError:
|
|
155
|
+
flash("Invalid variable type")
|
|
161
156
|
else:
|
|
162
157
|
script.add_logic_action(logic_type=logic_type, **kwargs)
|
|
163
158
|
else:
|
|
@@ -168,12 +163,13 @@ def experiment_builder(instrument=None):
|
|
|
168
163
|
autofill = not autofill
|
|
169
164
|
forms = create_form_from_pseudo(functions, autofill=autofill, script=script)
|
|
170
165
|
session['autofill'] = autofill
|
|
166
|
+
|
|
171
167
|
utils.post_script_file(script)
|
|
172
|
-
design_buttons =
|
|
168
|
+
design_buttons = create_action_button(script)
|
|
173
169
|
return render_template('experiment_builder.html', off_line=off_line, instrument=instrument, history=deck_list,
|
|
174
170
|
script=script, defined_variables=deck_variables,
|
|
175
171
|
local_variables=global_config.defined_variables,
|
|
176
|
-
|
|
172
|
+
forms=forms, buttons=design_buttons, format_name=format_name,
|
|
177
173
|
use_llm=enable_llm)
|
|
178
174
|
|
|
179
175
|
|
|
@@ -250,10 +246,14 @@ def experiment_run():
|
|
|
250
246
|
# module = current_app.config.get('MODULE', '')
|
|
251
247
|
# deck = sys.modules[module] if module else None
|
|
252
248
|
# script.deck = os.path.splitext(os.path.basename(deck.__file__))[0]
|
|
253
|
-
design_buttons = {stype:
|
|
249
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
254
250
|
config_preview = []
|
|
255
251
|
config_file_list = [i for i in os.listdir(current_app.config["CSV_FOLDER"]) if not i == ".gitkeep"]
|
|
256
|
-
|
|
252
|
+
try:
|
|
253
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
254
|
+
except ValueError as e:
|
|
255
|
+
flash(e.__str__())
|
|
256
|
+
return redirect(url_for("design.experiment_builder"))
|
|
257
257
|
# print(exec_string)
|
|
258
258
|
config_file = request.args.get("filename")
|
|
259
259
|
config = []
|
|
@@ -306,6 +306,8 @@ def experiment_run():
|
|
|
306
306
|
logger=g.logger, socketio=g.socketio, repeat_count=repeat,
|
|
307
307
|
output_path=datapath
|
|
308
308
|
)
|
|
309
|
+
if utils.check_config_duplicate(config):
|
|
310
|
+
flash(f"WARNING: Duplicate in config entries.")
|
|
309
311
|
except Exception as e:
|
|
310
312
|
flash(e)
|
|
311
313
|
return render_template('experiment_run.html', script=script.script_dict, filename=filename, dot_py=exec_string,
|
|
@@ -490,14 +492,20 @@ def edit_action(uuid: str):
|
|
|
490
492
|
script = utils.get_script_file()
|
|
491
493
|
action = script.find_by_uuid(uuid)
|
|
492
494
|
session['edit_action'] = action
|
|
493
|
-
|
|
495
|
+
|
|
496
|
+
if request.method == "POST" and action is not None:
|
|
497
|
+
forms = create_form_from_action(action, script=script)
|
|
494
498
|
if "back" not in request.form:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
499
|
+
kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
|
|
500
|
+
# print(kwargs)
|
|
501
|
+
if forms and forms.validate_on_submit():
|
|
502
|
+
save_as = kwargs.pop('return', '')
|
|
503
|
+
kwargs = script.validate_variables(kwargs)
|
|
504
|
+
# try:
|
|
505
|
+
script.update_by_uuid(uuid=uuid, args=kwargs, output=save_as)
|
|
506
|
+
# except Exception as e:
|
|
507
|
+
else:
|
|
508
|
+
flash(forms.errors)
|
|
501
509
|
session.pop('edit_action')
|
|
502
510
|
return redirect(url_for('design.experiment_builder'))
|
|
503
511
|
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
{# edit action #}
|
|
32
32
|
{% if session["edit_action"] %}
|
|
33
|
+
{# {{ session["edit_action"] }}#}
|
|
33
34
|
{% with action = session["edit_action"] %}
|
|
34
35
|
<h5> {{ format_name(action['action']) }} </h5>
|
|
35
36
|
<form role="form" method='POST' name="{{instrument}}" action="{{ url_for('design.edit_action', uuid=session["edit_action"]['uuid']) }}">
|
|
@@ -41,16 +42,26 @@
|
|
|
41
42
|
<input class="form-control" type="text" id="arg" name="arg" placeholder="{{ action['arg_types']}}" value="{{ action['args'] }}" aria-labelledby="variableHelpBlock">
|
|
42
43
|
</div>
|
|
43
44
|
{% else %}
|
|
44
|
-
{% for arg in action['args'] %}
|
|
45
|
-
<div class="input-group mb-3"
|
|
46
|
-
<label class="input-group-text">{{ format_name(arg) }}</label
|
|
47
|
-
<input class="form-control" type="text" id="{{ arg }}" name="{{ arg }}" placeholder="{{ action['arg_types'][arg] }}" value="{{ action['args'][arg] }}" aria-labelledby="variableHelpBlock"
|
|
48
|
-
</div
|
|
45
|
+
{# {% for arg in action['args'] %}#}
|
|
46
|
+
{# <div class="input-group mb-3">#}
|
|
47
|
+
{# <label class="input-group-text">{{ format_name(arg) }}</label>#}
|
|
48
|
+
{# <input class="form-control" type="text" id="{{ arg }}" name="{{ arg }}" placeholder="{{ action['arg_types'][arg] }}" value="{{ action['args'][arg] }}" aria-labelledby="variableHelpBlock">#}
|
|
49
|
+
{# </div>#}
|
|
50
|
+
{# {% endfor %}#}
|
|
51
|
+
{# <div class="input-group mb-3">#}
|
|
52
|
+
{# <label class="input-group-text">Save Output?</label>#}
|
|
53
|
+
{# <input class="form-control" type="text" id="return" name="return" value="{{ action['return'] }}" aria-labelledby="variableHelpBlock">#}
|
|
54
|
+
{# </div>#}
|
|
55
|
+
{{ forms.hidden_tag() }}
|
|
56
|
+
{% for field in forms %}
|
|
57
|
+
{% if field.type not in ['CSRFTokenField'] %}
|
|
58
|
+
<div class="input-group mb-3">
|
|
59
|
+
<label class="input-group-text">{{ field.label.text }}</label>
|
|
60
|
+
{{ field(class="form-control") }}
|
|
61
|
+
<div class="form-text">{{ field.description }} </div>
|
|
62
|
+
</div>
|
|
63
|
+
{% endif %}
|
|
49
64
|
{% endfor %}
|
|
50
|
-
<div class="input-group mb-3">
|
|
51
|
-
<label class="input-group-text">Save Output?</label>
|
|
52
|
-
<input class="form-control" type="text" id="return" name="return" value="{{ action['return'] }}" aria-labelledby="variableHelpBlock">
|
|
53
|
-
</div>
|
|
54
65
|
{% endif %}
|
|
55
66
|
</div>
|
|
56
67
|
{% endif %}
|
|
@@ -72,7 +83,7 @@
|
|
|
72
83
|
<!-- <div class="rounded flex-fill" style="height: 30px;background-color: aliceblue">-->
|
|
73
84
|
<!-- <h6 style=" text-align: center; ">{{ format_name(instrument) }}</h6>-->
|
|
74
85
|
<!-- </div>-->
|
|
75
|
-
{% if instrument in ['if' , 'while' , 'variable' ,'wait'] %}
|
|
86
|
+
{% if instrument in ['if' , 'while' , 'variable' ,'wait', 'repeat'] %}
|
|
76
87
|
{# form for builtin functions #}
|
|
77
88
|
<form role="form" method='POST' name="{{instrument}}" action="{{url_for('design.experiment_builder',instrument=instrument)}}" >
|
|
78
89
|
<div class="form-group">
|
|
@@ -139,12 +150,12 @@
|
|
|
139
150
|
{{ format_name(name) }}
|
|
140
151
|
</button>
|
|
141
152
|
</h2>
|
|
153
|
+
|
|
142
154
|
<div id="{{name}}" class="accordion-collapse collapse" data-bs-parent="#accordionActions">
|
|
143
155
|
<div class="accordion-body">
|
|
144
156
|
<form role="form" method='POST' name="add" id="add">
|
|
145
157
|
<div class="form-group">
|
|
146
158
|
{{ form.hidden_tag() }}
|
|
147
|
-
{# {{ form.hidden_name() }}#}
|
|
148
159
|
{% for field in form %}
|
|
149
160
|
{% if field.type not in ['CSRFTokenField', 'HiddenField'] %}
|
|
150
161
|
<div class="input-group mb-3">
|
|
@@ -161,6 +172,8 @@
|
|
|
161
172
|
{% endfor %}
|
|
162
173
|
</div>
|
|
163
174
|
<button type="submit" class="btn btn-dark">Add </button>
|
|
175
|
+
<i class="bi bi-info-circle ms-2" data-bs-toggle="tooltip" data-bs-placement="top" title='{{ form.hidden_name.description or "Docstring is not available" }}' ></i>
|
|
176
|
+
|
|
164
177
|
</form>
|
|
165
178
|
|
|
166
179
|
|
|
@@ -185,7 +198,7 @@
|
|
|
185
198
|
</h5>
|
|
186
199
|
<div class="accordion-collapse collapse show" id="advanced">
|
|
187
200
|
<ul class="list-group">
|
|
188
|
-
{% for instrument in ['if', 'while', 'variable', 'wait'] %}
|
|
201
|
+
{% for instrument in ['if', 'while', 'variable', 'wait', 'repeat'] %}
|
|
189
202
|
<form role="form" method='GET' name="device" action="{{url_for('design.experiment_builder',instrument=instrument)}}">
|
|
190
203
|
<div class="form-group">
|
|
191
204
|
<button class="list-group-item list-group-item-action" aria-current="true" type="submit">{{instrument}}</button>
|
|
@@ -302,7 +315,7 @@
|
|
|
302
315
|
{% for button in buttons %}
|
|
303
316
|
<li id="{{ button['id'] }}" style="list-style-type: none;">
|
|
304
317
|
<a href="{{ url_for('design.edit_action', uuid=button['uuid']) }}" type="button" class="btn btn-light" style="{{ button['style'] }}">{{ button['label'] }}</a>
|
|
305
|
-
{% if not button["instrument"] in ["if","while"] %}
|
|
318
|
+
{% if not button["instrument"] in ["if","while","repeat"] %}
|
|
306
319
|
<a href="{{ url_for('design.duplicate_action', id=button['id']) }}" type="button" class="btn btn-light"><span class="bi bi-copy"></span></a>
|
|
307
320
|
{% endif %}
|
|
308
321
|
<a href="{{ url_for('design.delete_action', id=button['id']) }}" type="button" class="btn btn-light"><span class="bi bi-trash"></span></a>
|
|
@@ -403,7 +416,7 @@
|
|
|
403
416
|
</div>
|
|
404
417
|
</div>
|
|
405
418
|
|
|
406
|
-
{% if instrument and not instrument in ['if' , 'while' , 'variable' ,'wait'] and use_llm %}
|
|
419
|
+
{% if instrument and not instrument in ['if' , 'while' , 'variable' ,'wait', 'repeat'] and use_llm %}
|
|
407
420
|
<script>
|
|
408
421
|
const buttonIds = {{ ['generate'] | tojson }};
|
|
409
422
|
</script>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
<form role="form" method='POST' name="run" action="{{url_for('design.experiment_run')}}">
|
|
51
51
|
<div class="input-group mb-3">
|
|
52
52
|
<label class="input-group-text" for="repeat">Repeat for </label>
|
|
53
|
-
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="
|
|
53
|
+
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="1">
|
|
54
54
|
<label class="input-group-text" for="repeat"> times</label>
|
|
55
55
|
</div>
|
|
56
56
|
{# {% if not no_deck_warning%}#}
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
<p><h5>Budget:</h5></p>
|
|
186
186
|
<div class="input-group mb-3">
|
|
187
187
|
<label class="input-group-text" for="repeat">Max iteration </label>
|
|
188
|
-
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="
|
|
188
|
+
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="25">
|
|
189
189
|
</div>
|
|
190
190
|
{% if not no_deck_warning%}
|
|
191
191
|
<div class="input-group mb-3">
|
|
@@ -9,32 +9,32 @@
|
|
|
9
9
|
</h1>
|
|
10
10
|
<p>Version: {{ version }}</p>
|
|
11
11
|
<div class="row">
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
{% if enable_design %}
|
|
13
|
+
<div class="col-lg-6 mb-4 d-flex align-items-stretch">
|
|
14
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
15
|
+
<div class="card-body">
|
|
16
|
+
<h5 class="card-title">Browse designs</h5>
|
|
17
|
+
<p class="card-text">Browse all workflows saved in the database.</p>
|
|
18
|
+
<a href="{{ url_for('database.load_from_database') }}" class="stretched-link"></a>
|
|
19
|
+
</div>
|
|
19
20
|
</div>
|
|
20
21
|
</div>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
<div class="col-lg-6 mb-4 d-flex align-items-stretch">
|
|
23
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
24
|
+
<div class="card-body">
|
|
25
|
+
<h5 class="card-title">Edit designs</h5>
|
|
26
|
+
<p class="card-text">Build your workflow from current deck functions.</p>
|
|
27
|
+
<a href="{{ url_for('design.experiment_builder') }}" class="stretched-link"></a>
|
|
28
|
+
</div>
|
|
28
29
|
</div>
|
|
29
30
|
</div>
|
|
30
|
-
|
|
31
|
+
{% endif %}
|
|
31
32
|
</div>
|
|
32
33
|
|
|
33
34
|
<br><br><br>
|
|
34
35
|
{% if not off_line %}
|
|
35
|
-
{# <h5>Only available in online mode</h5>#}
|
|
36
|
-
{# <hr>#}
|
|
37
36
|
<div class="row">
|
|
37
|
+
{% if enable_design %}
|
|
38
38
|
<div class="col-lg-6 mb-4 d-flex align-items-stretch">
|
|
39
39
|
<div class="card rounded shadow-sm flex-fill">
|
|
40
40
|
<div class="card-body">
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
</div>
|
|
45
45
|
</div>
|
|
46
46
|
</div>
|
|
47
|
+
{% endif %}
|
|
48
|
+
|
|
47
49
|
<div class="col-lg-6 mb-4 d-flex align-items-stretch">
|
|
48
50
|
<div class="card rounded shadow-sm flex-fill">
|
|
49
51
|
<div class="card-body">
|
ivoryos/templates/base.html
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<body>
|
|
24
24
|
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
|
25
25
|
<div class= "container">
|
|
26
|
-
|
|
26
|
+
{# {{ module_config }}#}
|
|
27
27
|
<a class="navbar-brand" href="{{ url_for('main.index') }}">
|
|
28
28
|
<img src="{{url_for('static', filename='logo.webp')}}" alt="Logo" height="60" class="d-inline-block align-text-bottom">
|
|
29
29
|
</a>
|
|
@@ -36,15 +36,18 @@
|
|
|
36
36
|
<li class="nav-item">
|
|
37
37
|
<a class="nav-link" href="{{ url_for('main.index') }}" aria-current="page">Home</a>
|
|
38
38
|
</li>
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
39
|
+
{% if enable_design %}
|
|
40
|
+
<li class="nav-item">
|
|
41
|
+
<a class="nav-link" href="{{ url_for('database.load_from_database') }}" aria-current="page">Library</a>
|
|
42
|
+
</li>
|
|
43
|
+
<li class="nav-item">
|
|
44
|
+
<a class="nav-link" href="{{ url_for('design.experiment_builder') }}">Design</a>
|
|
45
|
+
</li>
|
|
46
|
+
<li class="nav-item">
|
|
47
|
+
<a class="nav-link" href="{{ url_for('design.experiment_run') }}">Compile/Run</a>
|
|
48
|
+
</li>
|
|
49
|
+
{% endif %}
|
|
50
|
+
|
|
48
51
|
<li class="nav-item">
|
|
49
52
|
<a class="nav-link" href="{{ url_for('control.deck_controllers') }}">Devices</a></li>
|
|
50
53
|
</li>
|
|
@@ -54,6 +57,13 @@
|
|
|
54
57
|
<li class="nav-item">
|
|
55
58
|
<a class="nav-link" href="{{ url_for('main.help_info') }}">About</a>
|
|
56
59
|
</li>
|
|
60
|
+
{% if plugins %}
|
|
61
|
+
{% for plugin in plugins %}
|
|
62
|
+
<li class="nav-item">
|
|
63
|
+
<a class="nav-link" href="{{ url_for(plugin+'.main') }}">{{ plugin.capitalize() }}</a></li>
|
|
64
|
+
</li>
|
|
65
|
+
{% endfor %}
|
|
66
|
+
{% endif %}
|
|
57
67
|
</ul>
|
|
58
68
|
<ul class="navbar-nav ms-auto">
|
|
59
69
|
|