ivoryos 0.1.11__tar.gz → 0.1.13__tar.gz
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-0.1.11/ivoryos.egg-info → ivoryos-0.1.13}/PKG-INFO +1 -1
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/__init__.py +5 -5
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/control/control.py +1 -1
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/design/design.py +42 -29
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/design/templates/design/experiment_builder.html +24 -13
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/utils/db_models.py +152 -48
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/utils/form.py +154 -71
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/utils/utils.py +38 -25
- ivoryos-0.1.13/ivoryos/version.py +1 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13/ivoryos.egg-info}/PKG-INFO +1 -1
- ivoryos-0.1.11/ivoryos/version.py +0 -1
- {ivoryos-0.1.11 → ivoryos-0.1.13}/LICENSE +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/MANIFEST.in +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/README.md +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/config.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/auth/auth.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/auth/templates/auth/login.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/auth/templates/auth/signup.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/control/templates/control/controllers.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/control/templates/control/controllers_home.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/control/templates/control/controllers_new.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/database/__init__.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/database/database.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/database/templates/database/experiment_database.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/design/templates/design/experiment_run.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/main/main.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/main/templates/main/help.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/main/templates/main/home.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/js/socket_handler.js +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/js/sortable_design.js +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/logo.webp +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/static/style.css +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/templates/base.html +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/utils/global_config.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/utils/script_runner.py +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos.egg-info/SOURCES.txt +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos.egg-info/requires.txt +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos.egg-info/top_level.txt +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/setup.cfg +0 -0
- {ivoryos-0.1.11 → ivoryos-0.1.13}/setup.py +0 -0
|
@@ -18,7 +18,6 @@ from ivoryos.version import __version__ as ivoryos_version
|
|
|
18
18
|
|
|
19
19
|
global_config = GlobalConfig()
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
23
22
|
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
24
23
|
app.register_blueprint(main, url_prefix=url_prefix)
|
|
@@ -27,6 +26,7 @@ app.register_blueprint(design, url_prefix=url_prefix)
|
|
|
27
26
|
app.register_blueprint(database, url_prefix=url_prefix)
|
|
28
27
|
app.register_blueprint(control, url_prefix=url_prefix)
|
|
29
28
|
|
|
29
|
+
|
|
30
30
|
def create_app(config_class=None):
|
|
31
31
|
# url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
32
32
|
# app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
@@ -59,8 +59,6 @@ def create_app(config_class=None):
|
|
|
59
59
|
g.logger = logger
|
|
60
60
|
g.socketio = socketio
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
62
|
@app.route('/')
|
|
65
63
|
def redirect_to_prefix():
|
|
66
64
|
return redirect(url_for('main.index', version=ivoryos_version)) # Assuming 'index' is a route in your blueprint
|
|
@@ -93,14 +91,15 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
93
91
|
debug = debug if debug is not None else app.config.get('DEBUG', True)
|
|
94
92
|
|
|
95
93
|
app.config["LOGGERS"] = logger
|
|
96
|
-
app.config["LOGGERS_PATH"] = logger_output_name or app.config["LOGGERS_PATH"]
|
|
94
|
+
app.config["LOGGERS_PATH"] = logger_output_name or app.config["LOGGERS_PATH"] # default.log
|
|
97
95
|
logger_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["LOGGERS_PATH"])
|
|
98
96
|
|
|
99
97
|
if module:
|
|
100
98
|
app.config["MODULE"] = module
|
|
101
99
|
app.config["OFF_LINE"] = False
|
|
102
100
|
global_config.deck = sys.modules[module]
|
|
103
|
-
global_config.deck_snapshot = utils.create_deck_snapshot(global_config.deck,
|
|
101
|
+
global_config.deck_snapshot = utils.create_deck_snapshot(global_config.deck,
|
|
102
|
+
output_path=app.config["DUMMY_DECK"], save=True)
|
|
104
103
|
# global_config.runner = ScriptRunner(globals())
|
|
105
104
|
else:
|
|
106
105
|
app.config["OFF_LINE"] = True
|
|
@@ -120,3 +119,4 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
120
119
|
for log in logger:
|
|
121
120
|
utils.start_logger(socketio, log_filename=logger_path, logger_name=log)
|
|
122
121
|
socketio.run(app, host=host, port=port, debug=debug, use_reloader=False, allow_unsafe_werkzeug=True)
|
|
122
|
+
# return app
|
|
@@ -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
|
|
|
@@ -105,9 +106,10 @@ def experiment_builder(instrument=None):
|
|
|
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
|
|
109
|
+
edit_action = session.get("edit_action")
|
|
108
110
|
if instrument:
|
|
109
|
-
if instrument in ['if', 'while', 'variable', 'wait']:
|
|
110
|
-
forms = create_builtin_form(instrument)
|
|
111
|
+
if instrument in ['if', 'while', 'variable', 'wait', 'repeat']:
|
|
112
|
+
forms = create_builtin_form(instrument, autofill=autofill, script=script)
|
|
111
113
|
else:
|
|
112
114
|
if deck:
|
|
113
115
|
function_metadata = global_config.deck_snapshot.get(instrument, {})
|
|
@@ -116,8 +118,8 @@ def experiment_builder(instrument=None):
|
|
|
116
118
|
functions = {key: data.get('signature', {}) for key, data in function_metadata.items()}
|
|
117
119
|
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
118
120
|
if request.method == 'POST' and "hidden_name" in request.form:
|
|
119
|
-
all_kwargs = request.form.copy()
|
|
120
|
-
method_name =
|
|
121
|
+
# all_kwargs = request.form.copy()
|
|
122
|
+
method_name = request.form.get("hidden_name", None)
|
|
121
123
|
# if method_name is not None:
|
|
122
124
|
form = forms.get(method_name)
|
|
123
125
|
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
@@ -125,25 +127,18 @@ def experiment_builder(instrument=None):
|
|
|
125
127
|
if form and form.validate_on_submit():
|
|
126
128
|
function_name = kwargs.pop("hidden_name")
|
|
127
129
|
save_data = kwargs.pop('return', '')
|
|
128
|
-
variable_kwargs = {}
|
|
129
|
-
variable_kwargs_types = {}
|
|
130
130
|
|
|
131
|
-
try:
|
|
132
|
-
variable_kwargs, variable_kwargs_types = utils.find_variable_in_script(script, kwargs)
|
|
133
131
|
|
|
134
|
-
for name in variable_kwargs.keys():
|
|
135
|
-
del kwargs[name]
|
|
136
|
-
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
137
132
|
|
|
138
|
-
|
|
139
|
-
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
133
|
+
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
140
134
|
|
|
141
|
-
|
|
135
|
+
# print(script.get_added_variables(), script.get_output_variables())
|
|
142
136
|
arg_types = {}
|
|
143
|
-
arg_types.update(variable_kwargs_types)
|
|
137
|
+
# arg_types.update(variable_kwargs_types)
|
|
144
138
|
arg_types.update(primitive_arg_types)
|
|
145
|
-
all_kwargs.update(variable_kwargs)
|
|
146
|
-
|
|
139
|
+
# all_kwargs.update(variable_kwargs)
|
|
140
|
+
script.eval_list(kwargs, arg_types)
|
|
141
|
+
kwargs = script.eval_variables(kwargs)
|
|
147
142
|
action = {"instrument": instrument, "action": function_name,
|
|
148
143
|
"args": {name: arg for (name, arg) in kwargs.items()},
|
|
149
144
|
"return": save_data,
|
|
@@ -155,9 +150,13 @@ def experiment_builder(instrument=None):
|
|
|
155
150
|
elif request.method == 'POST' and "builtin_name" in request.form:
|
|
156
151
|
kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
|
|
157
152
|
if forms.validate_on_submit():
|
|
153
|
+
# print(kwargs)
|
|
158
154
|
logic_type = kwargs.pop('builtin_name')
|
|
159
155
|
if 'variable' in kwargs:
|
|
160
|
-
|
|
156
|
+
try:
|
|
157
|
+
script.add_variable(**kwargs)
|
|
158
|
+
except ValueError:
|
|
159
|
+
flash("Invalid variable type")
|
|
161
160
|
else:
|
|
162
161
|
script.add_logic_action(logic_type=logic_type, **kwargs)
|
|
163
162
|
else:
|
|
@@ -168,8 +167,10 @@ def experiment_builder(instrument=None):
|
|
|
168
167
|
autofill = not autofill
|
|
169
168
|
forms = create_form_from_pseudo(functions, autofill=autofill, script=script)
|
|
170
169
|
session['autofill'] = autofill
|
|
170
|
+
elif edit_action:
|
|
171
|
+
forms = create_form_from_action(edit_action, script=script)
|
|
171
172
|
utils.post_script_file(script)
|
|
172
|
-
design_buttons =
|
|
173
|
+
design_buttons = create_action_button(script)
|
|
173
174
|
return render_template('experiment_builder.html', off_line=off_line, instrument=instrument, history=deck_list,
|
|
174
175
|
script=script, defined_variables=deck_variables,
|
|
175
176
|
local_variables=global_config.defined_variables,
|
|
@@ -250,10 +251,14 @@ def experiment_run():
|
|
|
250
251
|
# module = current_app.config.get('MODULE', '')
|
|
251
252
|
# deck = sys.modules[module] if module else None
|
|
252
253
|
# script.deck = os.path.splitext(os.path.basename(deck.__file__))[0]
|
|
253
|
-
design_buttons = {stype:
|
|
254
|
+
design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
|
|
254
255
|
config_preview = []
|
|
255
256
|
config_file_list = [i for i in os.listdir(current_app.config["CSV_FOLDER"]) if not i == ".gitkeep"]
|
|
256
|
-
|
|
257
|
+
try:
|
|
258
|
+
exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
|
|
259
|
+
except ValueError as e:
|
|
260
|
+
flash(e.__str__())
|
|
261
|
+
return redirect(url_for("design.experiment_builder"))
|
|
257
262
|
# print(exec_string)
|
|
258
263
|
config_file = request.args.get("filename")
|
|
259
264
|
config = []
|
|
@@ -306,6 +311,8 @@ def experiment_run():
|
|
|
306
311
|
logger=g.logger, socketio=g.socketio, repeat_count=repeat,
|
|
307
312
|
output_path=datapath
|
|
308
313
|
)
|
|
314
|
+
if utils.check_config_duplicate(config):
|
|
315
|
+
flash(f"WARNING: Duplicate in config entries.")
|
|
309
316
|
except Exception as e:
|
|
310
317
|
flash(e)
|
|
311
318
|
return render_template('experiment_run.html', script=script.script_dict, filename=filename, dot_py=exec_string,
|
|
@@ -490,14 +497,20 @@ def edit_action(uuid: str):
|
|
|
490
497
|
script = utils.get_script_file()
|
|
491
498
|
action = script.find_by_uuid(uuid)
|
|
492
499
|
session['edit_action'] = action
|
|
493
|
-
|
|
500
|
+
|
|
501
|
+
if request.method == "POST" and action is not None:
|
|
502
|
+
forms = create_form_from_action(action, script=script)
|
|
494
503
|
if "back" not in request.form:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
504
|
+
kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
|
|
505
|
+
# print(kwargs)
|
|
506
|
+
if forms and forms.validate_on_submit():
|
|
507
|
+
save_as = kwargs.pop('return', '')
|
|
508
|
+
kwargs = script.eval_variables(kwargs)
|
|
509
|
+
# try:
|
|
510
|
+
script.update_by_uuid(uuid=uuid, args=kwargs, output=save_as)
|
|
511
|
+
# except Exception as e:
|
|
512
|
+
else:
|
|
513
|
+
flash(forms.errors)
|
|
501
514
|
session.pop('edit_action')
|
|
502
515
|
return redirect(url_for('design.experiment_builder'))
|
|
503
516
|
|
{ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/design/templates/design/experiment_builder.html
RENAMED
|
@@ -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">
|
|
@@ -185,7 +196,7 @@
|
|
|
185
196
|
</h5>
|
|
186
197
|
<div class="accordion-collapse collapse show" id="advanced">
|
|
187
198
|
<ul class="list-group">
|
|
188
|
-
{% for instrument in ['if', 'while', 'variable', 'wait'] %}
|
|
199
|
+
{% for instrument in ['if', 'while', 'variable', 'wait', 'repeat'] %}
|
|
189
200
|
<form role="form" method='GET' name="device" action="{{url_for('design.experiment_builder',instrument=instrument)}}">
|
|
190
201
|
<div class="form-group">
|
|
191
202
|
<button class="list-group-item list-group-item-action" aria-current="true" type="submit">{{instrument}}</button>
|
|
@@ -302,7 +313,7 @@
|
|
|
302
313
|
{% for button in buttons %}
|
|
303
314
|
<li id="{{ button['id'] }}" style="list-style-type: none;">
|
|
304
315
|
<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"] %}
|
|
316
|
+
{% if not button["instrument"] in ["if","while","repeat"] %}
|
|
306
317
|
<a href="{{ url_for('design.duplicate_action', id=button['id']) }}" type="button" class="btn btn-light"><span class="bi bi-copy"></span></a>
|
|
307
318
|
{% endif %}
|
|
308
319
|
<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 +414,7 @@
|
|
|
403
414
|
</div>
|
|
404
415
|
</div>
|
|
405
416
|
|
|
406
|
-
{% if instrument and not instrument in ['if' , 'while' , 'variable' ,'wait'] and use_llm %}
|
|
417
|
+
{% if instrument and not instrument in ['if' , 'while' , 'variable' ,'wait', 'repeat'] and use_llm %}
|
|
407
418
|
<script>
|
|
408
419
|
const buttonIds = {{ ['generate'] | tojson }};
|
|
409
420
|
</script>
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import builtins
|
|
1
3
|
import json
|
|
2
4
|
import keyword
|
|
3
5
|
import re
|
|
4
6
|
import uuid
|
|
5
7
|
from datetime import datetime
|
|
8
|
+
from typing import Dict
|
|
6
9
|
|
|
7
10
|
from flask_login import UserMixin
|
|
8
11
|
from flask_sqlalchemy import SQLAlchemy
|
|
@@ -29,6 +32,16 @@ class User(db.Model, UserMixin):
|
|
|
29
32
|
return self.username
|
|
30
33
|
|
|
31
34
|
|
|
35
|
+
class Variable:
|
|
36
|
+
def __init__(self, name: str = None, data_type=str, value=None):
|
|
37
|
+
self.name = name
|
|
38
|
+
self.data_type = data_type
|
|
39
|
+
self.value = value
|
|
40
|
+
|
|
41
|
+
def __str__(self):
|
|
42
|
+
return self.name
|
|
43
|
+
|
|
44
|
+
|
|
32
45
|
class Script(db.Model):
|
|
33
46
|
__tablename__ = 'script'
|
|
34
47
|
# id = db.Column(db.Integer, primary_key=True)
|
|
@@ -89,52 +102,70 @@ class Script(db.Model):
|
|
|
89
102
|
return action
|
|
90
103
|
|
|
91
104
|
def _convert_type(self, args, arg_types):
|
|
105
|
+
if arg_types in ["list", "tuple", "set"]:
|
|
106
|
+
try:
|
|
107
|
+
args = ast.literal_eval(args)
|
|
108
|
+
return args
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
92
111
|
if type(arg_types) is not list:
|
|
93
112
|
arg_types = [arg_types]
|
|
94
113
|
for arg_type in arg_types:
|
|
95
114
|
try:
|
|
115
|
+
# print(arg_type)
|
|
96
116
|
args = eval(f"{arg_type}('{args}')")
|
|
97
117
|
return
|
|
98
118
|
except Exception:
|
|
119
|
+
|
|
99
120
|
pass
|
|
100
121
|
raise TypeError(f"Input type error: cannot convert '{args}' to {arg_type}.")
|
|
101
122
|
|
|
102
123
|
def update_by_uuid(self, uuid, args, output):
|
|
103
|
-
bool_dict = {"True": True, "False": False}
|
|
104
124
|
action = self.find_by_uuid(uuid)
|
|
125
|
+
if not action:
|
|
126
|
+
return
|
|
127
|
+
arg_types = action['arg_types']
|
|
105
128
|
if type(action['args']) is dict:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if args[arg] in bool_dict.keys():
|
|
110
|
-
args[arg] = bool_dict[args[arg]]
|
|
111
|
-
elif args[arg] == "None" or args[arg] == "":
|
|
112
|
-
args[arg] = None
|
|
113
|
-
else:
|
|
114
|
-
if arg in action['arg_types']:
|
|
115
|
-
arg_types = action['arg_types'][arg]
|
|
116
|
-
self._convert_type(args[arg], arg_types)
|
|
117
|
-
else:
|
|
118
|
-
try:
|
|
119
|
-
args[arg] = eval(args[arg])
|
|
120
|
-
except Exception:
|
|
121
|
-
pass
|
|
122
|
-
else:
|
|
123
|
-
args = list(args.values())[0]
|
|
124
|
-
if not args.startswith("#"):
|
|
125
|
-
if args in bool_dict.keys():
|
|
126
|
-
args = bool_dict[args]
|
|
129
|
+
# pass
|
|
130
|
+
self.eval_list(args, arg_types)
|
|
127
131
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
else:
|
|
133
|
+
pass
|
|
134
|
+
# """handle"""
|
|
135
|
+
# args = list(args.values())[0]
|
|
136
|
+
# if not args.startswith("#"):
|
|
137
|
+
# if args in bool_dict.keys():
|
|
138
|
+
# args = bool_dict[args]
|
|
139
|
+
#
|
|
140
|
+
# else:
|
|
141
|
+
# if 'arg_types' in action:
|
|
142
|
+
# arg_types = action['arg_types']
|
|
143
|
+
# self._convert_type(args, arg_types)
|
|
132
144
|
|
|
133
|
-
# print(args)
|
|
134
145
|
action['args'] = args
|
|
135
|
-
# print(action)
|
|
136
146
|
action['return'] = output
|
|
137
147
|
|
|
148
|
+
@staticmethod
|
|
149
|
+
def eval_list(args, arg_types):
|
|
150
|
+
for arg in args:
|
|
151
|
+
arg_type = arg_types[arg]
|
|
152
|
+
if arg_type in ["list", "tuple", "set"]:
|
|
153
|
+
|
|
154
|
+
if type(arg) is str and not args[arg].startswith("#"):
|
|
155
|
+
# arg_types = arg_types[arg]
|
|
156
|
+
# if arg_types in ["list", "tuple", "set"]:
|
|
157
|
+
convert_type = getattr(builtins, arg_type) # Handle unknown types s
|
|
158
|
+
try:
|
|
159
|
+
output = ast.literal_eval(args[arg])
|
|
160
|
+
if type(output) not in [list, tuple, set]:
|
|
161
|
+
output = [output]
|
|
162
|
+
args[arg] = convert_type(output)
|
|
163
|
+
# return args
|
|
164
|
+
except ValueError:
|
|
165
|
+
_list = ''.join(args[arg]).split(',')
|
|
166
|
+
# convert_type = getattr(builtins, arg_types) # Handle unknown types s
|
|
167
|
+
args[arg] = convert_type([s.strip() for s in _list])
|
|
168
|
+
|
|
138
169
|
@property
|
|
139
170
|
def stypes(self):
|
|
140
171
|
return list(self.script_dict.keys())
|
|
@@ -204,15 +235,48 @@ class Script(db.Model):
|
|
|
204
235
|
self.currently_editing_order.append(str(current_len + 1))
|
|
205
236
|
self.update_time_stamp()
|
|
206
237
|
|
|
207
|
-
def add_variable(self, statement, variable):
|
|
238
|
+
def add_variable(self, statement, variable, type):
|
|
239
|
+
convert_type = getattr(builtins, type)
|
|
240
|
+
statement = convert_type(statement)
|
|
208
241
|
current_len = len(self.currently_editing_script)
|
|
209
242
|
uid = uuid.uuid4().fields[-1]
|
|
210
243
|
action_list = [{"id": current_len + 1, "instrument": 'variable', "action": variable,
|
|
211
|
-
"args": 'None' if statement == '' else statement, "return": '', "uuid": uid,
|
|
244
|
+
"args": {"statement": 'None' if statement == '' else statement}, "return": '', "uuid": uid,
|
|
245
|
+
"arg_types": {"statement": type}}]
|
|
212
246
|
self.currently_editing_script.extend(action_list)
|
|
213
247
|
self.currently_editing_order.extend([str(current_len + i + 1) for i in range(len(action_list))])
|
|
214
248
|
self.update_time_stamp()
|
|
215
249
|
|
|
250
|
+
def get_added_variables(self):
|
|
251
|
+
added_variables: Dict[str, str] = {action["action"]: action["arg_types"]["statement"] for action in
|
|
252
|
+
self.currently_editing_script if action["instrument"] == "variable"}
|
|
253
|
+
|
|
254
|
+
return added_variables
|
|
255
|
+
|
|
256
|
+
def get_output_variables(self):
|
|
257
|
+
output_variables: Dict[str, str] = {action["return"]: "function_output" for action in
|
|
258
|
+
self.currently_editing_script if action["return"]}
|
|
259
|
+
|
|
260
|
+
return output_variables
|
|
261
|
+
|
|
262
|
+
def get_variables(self):
|
|
263
|
+
output_variables: Dict[str, str] = self.get_output_variables()
|
|
264
|
+
added_variables = self.get_added_variables()
|
|
265
|
+
output_variables.update(added_variables)
|
|
266
|
+
|
|
267
|
+
return output_variables
|
|
268
|
+
|
|
269
|
+
def eval_variables(self, kwargs):
|
|
270
|
+
output_variables: Dict[str, str] = self.get_variables()
|
|
271
|
+
# print(output_variables)
|
|
272
|
+
for key, value in kwargs.items():
|
|
273
|
+
if type(value) is str and value in output_variables:
|
|
274
|
+
var_type = output_variables[value]
|
|
275
|
+
kwargs[key] = {value:var_type}
|
|
276
|
+
|
|
277
|
+
return kwargs
|
|
278
|
+
|
|
279
|
+
|
|
216
280
|
def add_logic_action(self, logic_type: str, statement):
|
|
217
281
|
current_len = len(self.currently_editing_script)
|
|
218
282
|
uid = uuid.uuid4().fields[-1]
|
|
@@ -220,26 +284,33 @@ class Script(db.Model):
|
|
|
220
284
|
"if":
|
|
221
285
|
[
|
|
222
286
|
{"id": current_len + 1, "instrument": 'if', "action": 'if',
|
|
223
|
-
"args": 'True' if statement == '' else statement,
|
|
224
|
-
"return": '', "uuid": uid, "arg_types": ''},
|
|
225
|
-
{"id": current_len + 2, "instrument": 'if', "action": 'else', "args":
|
|
287
|
+
"args": {"statement": 'True' if statement == '' else statement},
|
|
288
|
+
"return": '', "uuid": uid, "arg_types": {"statement": ''}},
|
|
289
|
+
{"id": current_len + 2, "instrument": 'if', "action": 'else', "args": {}, "return": '',
|
|
226
290
|
"uuid": uid},
|
|
227
|
-
{"id": current_len + 3, "instrument": 'if', "action": 'endif', "args":
|
|
291
|
+
{"id": current_len + 3, "instrument": 'if', "action": 'endif', "args": {}, "return": '',
|
|
228
292
|
"uuid": uid},
|
|
229
293
|
],
|
|
230
294
|
"while":
|
|
231
295
|
[
|
|
232
296
|
{"id": current_len + 1, "instrument": 'while', "action": 'while',
|
|
233
|
-
"args": 'False' if statement == '' else statement, "return": '', "uuid": uid, "arg_types": ''},
|
|
234
|
-
{"id": current_len + 2, "instrument": 'while', "action": 'endwhile', "args":
|
|
297
|
+
"args": {"statement": 'False' if statement == '' else statement}, "return": '', "uuid": uid, "arg_types": {"statement": ''}},
|
|
298
|
+
{"id": current_len + 2, "instrument": 'while', "action": 'endwhile', "args": {}, "return": '',
|
|
235
299
|
"uuid": uid},
|
|
236
300
|
],
|
|
237
301
|
|
|
238
302
|
"wait":
|
|
239
303
|
[
|
|
240
304
|
{"id": current_len + 1, "instrument": 'wait', "action": "wait",
|
|
241
|
-
"args":
|
|
242
|
-
"return": '', "uuid": uid, "arg_types": "float"},
|
|
305
|
+
"args": {"statement": 1 if statement == '' else statement},
|
|
306
|
+
"return": '', "uuid": uid, "arg_types": {"statement": "float"}},
|
|
307
|
+
],
|
|
308
|
+
"repeat":
|
|
309
|
+
[
|
|
310
|
+
{"id": current_len + 1, "instrument": 'repeat', "action": "repeat",
|
|
311
|
+
"args": {"statement": 1 if statement == '' else statement}, "return": '', "uuid": uid, "arg_types": {"statement": "int"}},
|
|
312
|
+
{"id": current_len + 2, "instrument": 'repeat', "action": 'endrepeat',
|
|
313
|
+
"args": {}, "return": '', "uuid": uid},
|
|
243
314
|
],
|
|
244
315
|
}
|
|
245
316
|
action_list = logic_dict[logic_type]
|
|
@@ -259,7 +330,8 @@ class Script(db.Model):
|
|
|
259
330
|
self.update_time_stamp()
|
|
260
331
|
|
|
261
332
|
def duplicate_action(self, id: int):
|
|
262
|
-
action_to_duplicate = next((action for action in self.currently_editing_script if action['id'] == int(id)),
|
|
333
|
+
action_to_duplicate = next((action for action in self.currently_editing_script if action['id'] == int(id)),
|
|
334
|
+
None)
|
|
263
335
|
insert_id = action_to_duplicate.get("id")
|
|
264
336
|
self.add_action(action_to_duplicate)
|
|
265
337
|
# print(self.currently_editing_script)
|
|
@@ -366,11 +438,15 @@ class Script(db.Model):
|
|
|
366
438
|
"""
|
|
367
439
|
Generate the function header.
|
|
368
440
|
"""
|
|
369
|
-
configure,
|
|
441
|
+
configure, config_type = self.config(stype)
|
|
442
|
+
|
|
443
|
+
configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
|
|
444
|
+
config_type.items()]
|
|
445
|
+
|
|
370
446
|
function_header = f"\n\ndef {run_name}_{stype}("
|
|
371
447
|
|
|
372
448
|
if stype == "script":
|
|
373
|
-
function_header += ",".join(configure)
|
|
449
|
+
function_header += ", ".join(configure)
|
|
374
450
|
|
|
375
451
|
function_header += "):"
|
|
376
452
|
function_header += self.indent(1) + f"global {run_name}_{stype}"
|
|
@@ -389,7 +465,6 @@ class Script(db.Model):
|
|
|
389
465
|
return_str, return_list = self.config_return()
|
|
390
466
|
if return_list and stype == "script":
|
|
391
467
|
body += self.indent(indent_unit) + return_str
|
|
392
|
-
|
|
393
468
|
return body
|
|
394
469
|
|
|
395
470
|
def _process_action(self, indent_unit, action, index, stype):
|
|
@@ -397,18 +472,24 @@ class Script(db.Model):
|
|
|
397
472
|
Process each action within the script dictionary.
|
|
398
473
|
"""
|
|
399
474
|
instrument = action['instrument']
|
|
475
|
+
statement = action['args'].get('statement')
|
|
400
476
|
args = self._process_args(action['args'])
|
|
477
|
+
|
|
401
478
|
save_data = action['return']
|
|
402
479
|
action_name = action['action']
|
|
403
480
|
next_action = self._get_next_action(stype, index)
|
|
481
|
+
# print(args)
|
|
404
482
|
if instrument == 'if':
|
|
405
|
-
return self._process_if(indent_unit, action_name,
|
|
483
|
+
return self._process_if(indent_unit, action_name, statement, next_action)
|
|
406
484
|
elif instrument == 'while':
|
|
407
|
-
return self._process_while(indent_unit, action_name,
|
|
485
|
+
return self._process_while(indent_unit, action_name, statement, next_action)
|
|
408
486
|
elif instrument == 'variable':
|
|
409
|
-
return self.indent(indent_unit) + f"{action_name} = {
|
|
487
|
+
return self.indent(indent_unit) + f"{action_name} = {statement}", indent_unit
|
|
410
488
|
elif instrument == 'wait':
|
|
411
|
-
return f"{self.indent(indent_unit)}time.sleep({
|
|
489
|
+
return f"{self.indent(indent_unit)}time.sleep({statement})", indent_unit
|
|
490
|
+
elif instrument == 'repeat':
|
|
491
|
+
return self._process_repeat(indent_unit, action_name, statement, next_action)
|
|
492
|
+
|
|
412
493
|
else:
|
|
413
494
|
return self._process_instrument_action(indent_unit, instrument, action_name, args, save_data)
|
|
414
495
|
|
|
@@ -456,10 +537,25 @@ class Script(db.Model):
|
|
|
456
537
|
indent_unit -= 1
|
|
457
538
|
return exec_string, indent_unit
|
|
458
539
|
|
|
540
|
+
def _process_repeat(self, indent_unit, action, args, next_action):
|
|
541
|
+
"""
|
|
542
|
+
Process 'while' and 'endwhile' actions.
|
|
543
|
+
"""
|
|
544
|
+
exec_string = ""
|
|
545
|
+
if action == 'repeat':
|
|
546
|
+
exec_string += self.indent(indent_unit) + f"for _ in range({args}):"
|
|
547
|
+
indent_unit += 1
|
|
548
|
+
if next_action and next_action['instrument'] == 'repeat':
|
|
549
|
+
exec_string += self.indent(indent_unit) + "pass"
|
|
550
|
+
elif action == 'endrepeat':
|
|
551
|
+
indent_unit -= 1
|
|
552
|
+
return exec_string, indent_unit
|
|
553
|
+
|
|
459
554
|
def _process_instrument_action(self, indent_unit, instrument, action, args, save_data):
|
|
460
555
|
"""
|
|
461
556
|
Process actions related to instruments.
|
|
462
557
|
"""
|
|
558
|
+
|
|
463
559
|
if isinstance(args, dict):
|
|
464
560
|
args_str = self._process_dict_args(args)
|
|
465
561
|
single_line = f"{instrument}.{action}(**{args_str})"
|
|
@@ -481,8 +577,16 @@ class Script(db.Model):
|
|
|
481
577
|
for arg in args:
|
|
482
578
|
if isinstance(args[arg], str) and args[arg].startswith("#"):
|
|
483
579
|
args_str = args_str.replace(f"'#{args[arg][1:]}'", args[arg][1:])
|
|
484
|
-
elif
|
|
485
|
-
|
|
580
|
+
elif isinstance(args[arg], dict):
|
|
581
|
+
print(args[arg])
|
|
582
|
+
variables = self.get_variables()
|
|
583
|
+
value = next(iter(args[arg]))
|
|
584
|
+
if value not in variables:
|
|
585
|
+
raise ValueError(f"Variable ({value}) is not defined.")
|
|
586
|
+
args_str = args_str.replace(f"{args[arg]}", next(iter(args[arg])))
|
|
587
|
+
# elif self._is_variable(arg):
|
|
588
|
+
# print("is variable")
|
|
589
|
+
# args_str = args_str.replace(f"'{args[arg]}'", args[arg])
|
|
486
590
|
return args_str
|
|
487
591
|
|
|
488
592
|
def _get_next_action(self, stype, index):
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from wtforms.fields.choices import SelectField
|
|
1
2
|
from wtforms.fields.core import Field
|
|
2
3
|
from wtforms.validators import InputRequired
|
|
3
4
|
from wtforms.widgets.core import TextInput
|
|
@@ -6,16 +7,22 @@ from flask_wtf import FlaskForm
|
|
|
6
7
|
from wtforms import StringField, FloatField, HiddenField, BooleanField, IntegerField
|
|
7
8
|
import inspect
|
|
8
9
|
|
|
10
|
+
from ivoryos.utils.db_models import Variable, Script
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
def find_variable(data, script):
|
|
11
14
|
# TODO: needs to check for valid order of variables, important when editting
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
# variable name: variable type
|
|
16
|
+
added_variables: dict[str, str] = script.get_added_variables()
|
|
17
|
+
output_variables: dict[str, str] = script.get_output_variables()
|
|
18
|
+
# [action for action in script.currently_editing_script if
|
|
19
|
+
# action["instrument"] == "variable"
|
|
20
|
+
# # or action["return"] # TODO find returns
|
|
21
|
+
# ]
|
|
22
|
+
added_variables.update(output_variables)
|
|
23
|
+
for variable_name, variable_type in added_variables.items():
|
|
24
|
+
if variable_name == data:
|
|
25
|
+
return data, variable_type # variable_type int float str or "function_output"
|
|
19
26
|
# if added_variable["return"] == data:
|
|
20
27
|
# return data, None
|
|
21
28
|
return None, None
|
|
@@ -36,9 +43,9 @@ class VariableOrStringField(Field):
|
|
|
36
43
|
|
|
37
44
|
def _value(self):
|
|
38
45
|
if self.script:
|
|
39
|
-
variable,
|
|
46
|
+
variable, variable_type = find_variable(self.data, self.script)
|
|
40
47
|
if variable:
|
|
41
|
-
return variable
|
|
48
|
+
return Variable(variable, variable_type)
|
|
42
49
|
|
|
43
50
|
return str(self.data) if self.data is not None else ""
|
|
44
51
|
|
|
@@ -52,9 +59,9 @@ class VariableOrFloatField(Field):
|
|
|
52
59
|
|
|
53
60
|
def _value(self):
|
|
54
61
|
if self.script:
|
|
55
|
-
variable,
|
|
62
|
+
variable, variable_type = find_variable(self.data, self.script)
|
|
56
63
|
if variable:
|
|
57
|
-
return variable
|
|
64
|
+
return Variable(variable, variable_type)
|
|
58
65
|
|
|
59
66
|
if self.raw_data:
|
|
60
67
|
return self.raw_data[0]
|
|
@@ -73,14 +80,15 @@ class VariableOrFloatField(Field):
|
|
|
73
80
|
try:
|
|
74
81
|
if self.script:
|
|
75
82
|
try:
|
|
76
|
-
variable,
|
|
83
|
+
variable, variable_type = find_variable(valuelist[0], self.script)
|
|
77
84
|
if variable:
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
if not variable_type == "function_output":
|
|
86
|
+
if variable_type not in ["float", "int"]:
|
|
87
|
+
raise ValueError("Variable is not a valid float")
|
|
88
|
+
self.data = variable
|
|
80
89
|
return
|
|
81
90
|
except ValueError:
|
|
82
91
|
pass
|
|
83
|
-
|
|
84
92
|
self.data = float(valuelist[0])
|
|
85
93
|
except ValueError as exc:
|
|
86
94
|
self.data = None
|
|
@@ -99,9 +107,9 @@ class VariableOrIntField(Field):
|
|
|
99
107
|
|
|
100
108
|
def _value(self):
|
|
101
109
|
if self.script:
|
|
102
|
-
variable,
|
|
110
|
+
variable, variable_type = find_variable(self.data, self.script)
|
|
103
111
|
if variable:
|
|
104
|
-
return variable
|
|
112
|
+
return Variable(variable, variable_type)
|
|
105
113
|
|
|
106
114
|
if self.raw_data:
|
|
107
115
|
return self.raw_data[0]
|
|
@@ -109,34 +117,16 @@ class VariableOrIntField(Field):
|
|
|
109
117
|
return str(self.data)
|
|
110
118
|
return ""
|
|
111
119
|
|
|
112
|
-
# def process_data(self, value):
|
|
113
|
-
#
|
|
114
|
-
# if self.script:
|
|
115
|
-
# variable, var_value = find_variable(value, self.script)
|
|
116
|
-
# if variable:
|
|
117
|
-
# try:
|
|
118
|
-
# int(var_value)
|
|
119
|
-
# self.data = str(variable)
|
|
120
|
-
# return
|
|
121
|
-
# except ValueError:
|
|
122
|
-
# pass
|
|
123
|
-
# if value is None or value is unset_value:
|
|
124
|
-
# self.data = None
|
|
125
|
-
# return
|
|
126
|
-
# try:
|
|
127
|
-
# self.data = int(value)
|
|
128
|
-
# except (ValueError, TypeError) as exc:
|
|
129
|
-
# self.data = None
|
|
130
|
-
# raise ValueError(self.gettext("Not a valid integer value.")) from exc
|
|
131
|
-
|
|
132
120
|
def process_formdata(self, valuelist):
|
|
133
121
|
if not valuelist:
|
|
134
122
|
return
|
|
135
123
|
if self.script:
|
|
136
|
-
variable,
|
|
124
|
+
variable, variable_type = find_variable(valuelist[0], self.script)
|
|
137
125
|
if variable:
|
|
138
126
|
try:
|
|
139
|
-
|
|
127
|
+
if not variable_type == "function_output":
|
|
128
|
+
if not variable_type == "int":
|
|
129
|
+
raise ValueError("Not a valid integer value")
|
|
140
130
|
self.data = str(variable)
|
|
141
131
|
return
|
|
142
132
|
except ValueError:
|
|
@@ -155,6 +145,7 @@ class VariableOrIntField(Field):
|
|
|
155
145
|
|
|
156
146
|
class VariableOrBoolField(BooleanField):
|
|
157
147
|
widget = TextInput()
|
|
148
|
+
false_values = (False, "false", "", "False", "f", "F")
|
|
158
149
|
|
|
159
150
|
def __init__(self, label='', validators=None, script=None, **kwargs):
|
|
160
151
|
super(VariableOrBoolField, self).__init__(label, validators, **kwargs)
|
|
@@ -163,32 +154,36 @@ class VariableOrBoolField(BooleanField):
|
|
|
163
154
|
def process_data(self, value):
|
|
164
155
|
|
|
165
156
|
if self.script:
|
|
166
|
-
variable,
|
|
157
|
+
variable, variable_type = find_variable(value, self.script)
|
|
167
158
|
if variable:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
except ValueError:
|
|
172
|
-
return
|
|
159
|
+
if not variable_type == "function_output":
|
|
160
|
+
raise ValueError("Not accepting boolean variables")
|
|
161
|
+
return Variable(variable, variable_type)
|
|
173
162
|
|
|
174
163
|
self.data = bool(value)
|
|
175
164
|
|
|
176
165
|
def process_formdata(self, valuelist):
|
|
177
|
-
|
|
166
|
+
# todo
|
|
167
|
+
# print(valuelist)
|
|
168
|
+
if not valuelist or not type(valuelist) is list:
|
|
178
169
|
self.data = False
|
|
179
|
-
elif valuelist and valuelist[0].startswith("#"):
|
|
180
|
-
if not self.script.editing_type == "script":
|
|
181
|
-
raise ValueError(self.gettext("Variable is not supported in prep/cleanup"))
|
|
182
|
-
self.data = valuelist[0]
|
|
183
170
|
else:
|
|
184
|
-
|
|
171
|
+
value = valuelist[0] if type(valuelist) is list else valuelist
|
|
172
|
+
if value.startswith("#"):
|
|
173
|
+
if not self.script.editing_type == "script":
|
|
174
|
+
raise ValueError(self.gettext("Variable is not supported in prep/cleanup"))
|
|
175
|
+
self.data = valuelist[0]
|
|
176
|
+
elif value in self.false_values:
|
|
177
|
+
self.data = False
|
|
178
|
+
else:
|
|
179
|
+
self.data = True
|
|
185
180
|
|
|
186
181
|
def _value(self):
|
|
187
182
|
|
|
188
183
|
if self.script:
|
|
189
|
-
variable,
|
|
184
|
+
variable, variable_type = find_variable(self.raw_data, self.script)
|
|
190
185
|
if variable:
|
|
191
|
-
return variable
|
|
186
|
+
return Variable(variable, variable_type)
|
|
192
187
|
|
|
193
188
|
if self.raw_data:
|
|
194
189
|
return str(self.raw_data[0])
|
|
@@ -269,17 +264,76 @@ def create_form_from_pseudo(pseudo: dict, autofill: bool, script=None, design=Tr
|
|
|
269
264
|
return method_forms
|
|
270
265
|
|
|
271
266
|
|
|
272
|
-
def
|
|
267
|
+
def create_form_from_action(action: dict, script=None, design=True):
|
|
268
|
+
'''
|
|
269
|
+
{'action': 'dose_solid', 'arg_types': {'amount_in_mg': 'float', 'bring_in': 'bool'}, 'args': {'amount_in_mg':
|
|
270
|
+
5.0, 'bring_in': False}, 'id': 9, 'instrument': 'deck.sdl', 'return': '', 'uuid': 266929188668995}
|
|
271
|
+
'''
|
|
272
|
+
# print(action)
|
|
273
|
+
|
|
274
|
+
arg_types = action.get("arg_types", {})
|
|
275
|
+
args = action.get("args", {})
|
|
276
|
+
save_as = action.get("return")
|
|
277
|
+
action = action.get("action")
|
|
278
|
+
|
|
279
|
+
class DynamicForm(FlaskForm):
|
|
280
|
+
pass
|
|
281
|
+
|
|
282
|
+
annotation_mapping = {
|
|
283
|
+
"int": (VariableOrIntField if design else IntegerField, 'Enter integer value'),
|
|
284
|
+
"float": (VariableOrFloatField if design else FloatField, 'Enter numeric value'),
|
|
285
|
+
"str": (VariableOrStringField if design else StringField, 'Enter text'),
|
|
286
|
+
"bool": (VariableOrBoolField if design else BooleanField, 'Empty for false')
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
for name, param_type in arg_types.items():
|
|
290
|
+
formatted_param_name = format_name(name)
|
|
291
|
+
value = args.get(name, "")
|
|
292
|
+
if type(value) is dict:
|
|
293
|
+
value = next(iter(value))
|
|
294
|
+
field_kwargs = {
|
|
295
|
+
"label": formatted_param_name,
|
|
296
|
+
"default": f'{value}',
|
|
297
|
+
"validators": [InputRequired()],
|
|
298
|
+
**({"script": script})
|
|
299
|
+
}
|
|
300
|
+
param_type = param_type if type(param_type) is str else f"{param_type}"
|
|
301
|
+
field_class, placeholder_text = annotation_mapping.get(
|
|
302
|
+
param_type,
|
|
303
|
+
(VariableOrStringField if design else StringField, f'Enter {param_type} value')
|
|
304
|
+
)
|
|
305
|
+
render_kwargs = {"placeholder": placeholder_text}
|
|
306
|
+
|
|
307
|
+
# Create the field with additional rendering kwargs for placeholder text
|
|
308
|
+
field = field_class(**field_kwargs, render_kw=render_kwargs)
|
|
309
|
+
setattr(DynamicForm, name, field)
|
|
310
|
+
|
|
311
|
+
if design:
|
|
312
|
+
return_value = StringField(label='Save value as', default=f"{save_as}", render_kw={"placeholder": "Optional"})
|
|
313
|
+
setattr(DynamicForm, 'return', return_value)
|
|
314
|
+
return DynamicForm()
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def create_builtin_form(logic_type, autofill, script):
|
|
273
318
|
class BuiltinFunctionForm(FlaskForm):
|
|
274
319
|
pass
|
|
275
320
|
|
|
276
|
-
placeholder_text =
|
|
277
|
-
|
|
278
|
-
|
|
321
|
+
placeholder_text = {
|
|
322
|
+
'wait': 'Enter second',
|
|
323
|
+
'repeat': 'Enter an integer'
|
|
324
|
+
}.get(logic_type, 'Enter statement')
|
|
325
|
+
description_text = {
|
|
326
|
+
'variable': 'Your variable can be numbers, boolean (True or False) or text ("text")',
|
|
327
|
+
}.get(logic_type, '')
|
|
328
|
+
field_class = {
|
|
329
|
+
'wait': VariableOrFloatField,
|
|
330
|
+
'repeat': VariableOrIntField
|
|
331
|
+
}.get(logic_type, VariableOrStringField) # Default to StringField as a fallback
|
|
279
332
|
field_kwargs = {
|
|
280
333
|
"label": f'statement',
|
|
281
334
|
"validators": [InputRequired()] if logic_type in ['wait', "variable"] else [],
|
|
282
335
|
"description": description_text,
|
|
336
|
+
"script": script
|
|
283
337
|
}
|
|
284
338
|
render_kwargs = {"placeholder": placeholder_text}
|
|
285
339
|
field = field_class(**field_kwargs, render_kw=render_kwargs)
|
|
@@ -288,29 +342,58 @@ def create_builtin_form(logic_type):
|
|
|
288
342
|
variable_field = StringField(label=f'variable', validators=[InputRequired()],
|
|
289
343
|
description="Your variable name cannot include space",
|
|
290
344
|
render_kw=render_kwargs)
|
|
345
|
+
type_field = SelectField(
|
|
346
|
+
'Select Input Type',
|
|
347
|
+
choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String')],
|
|
348
|
+
default='str' # Optional default value
|
|
349
|
+
)
|
|
291
350
|
setattr(BuiltinFunctionForm, "variable", variable_field)
|
|
351
|
+
setattr(BuiltinFunctionForm, "type", type_field)
|
|
292
352
|
hidden_field = HiddenField(name=f'builtin_name', render_kw={"value": f'{logic_type}'})
|
|
293
353
|
setattr(BuiltinFunctionForm, "builtin_name", hidden_field)
|
|
294
354
|
return BuiltinFunctionForm()
|
|
295
355
|
|
|
296
356
|
|
|
297
|
-
def create_action_button(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
357
|
+
def create_action_button(script, stype=None):
|
|
358
|
+
stype = stype or script.editing_type
|
|
359
|
+
variables = script.get_variables()
|
|
360
|
+
return [_action_button(i, variables) for i in script.get_script(stype)]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _action_button(action: dict, variables: dict):
|
|
364
|
+
style = {
|
|
365
|
+
"repeat": "background-color: lightsteelblue",
|
|
366
|
+
"if": "background-color: salmon",
|
|
367
|
+
"while": "background-color: salmon",
|
|
368
|
+
}.get(action['instrument'], "")
|
|
369
|
+
|
|
370
|
+
if action['instrument'] in ['if', 'while', 'repeat']:
|
|
371
|
+
text = f"{action['action']} {action['args']}"
|
|
372
|
+
elif action['instrument'] == 'variable':
|
|
373
|
+
text = f"{action['action']} = {action['args'].get('statement')}"
|
|
304
374
|
else:
|
|
305
375
|
# regular action button
|
|
306
|
-
prefix = f"{
|
|
307
|
-
action_text = f"{
|
|
376
|
+
prefix = f"{action['return']} = " if action['return'] else ""
|
|
377
|
+
action_text = f"{action['instrument'].split('.')[-1] if action['instrument'].startswith('deck') else action['instrument']}.{action['action']}"
|
|
308
378
|
arg_string = ""
|
|
309
|
-
if
|
|
310
|
-
if type(
|
|
311
|
-
|
|
379
|
+
if action['args']:
|
|
380
|
+
if type(action['args']) is dict:
|
|
381
|
+
arg_list = []
|
|
382
|
+
for k, v in action['args'].items():
|
|
383
|
+
if isinstance(v, dict):
|
|
384
|
+
value = next(iter(v)) # Extract the first key if it's a dict
|
|
385
|
+
|
|
386
|
+
style = "background-color: khaki" if value not in variables.keys() else ""
|
|
387
|
+
|
|
388
|
+
else:
|
|
389
|
+
value = v # Keep the original value if not a dict
|
|
390
|
+
|
|
391
|
+
arg_list.append(f"{k} = {value}") # Format the key-value pair
|
|
392
|
+
|
|
393
|
+
arg_string = "(" + ", ".join(arg_list) + ")"
|
|
312
394
|
else:
|
|
313
|
-
arg_string = f"= {
|
|
395
|
+
arg_string = f"= {action['args']}"
|
|
314
396
|
|
|
315
397
|
text = f"{prefix}{action_text} {arg_string}"
|
|
316
|
-
|
|
398
|
+
|
|
399
|
+
return dict(label=text, style=style, uuid=action["uuid"], id=action["id"], instrument=action['instrument'])
|
|
@@ -6,6 +6,7 @@ import os
|
|
|
6
6
|
import pickle
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
|
+
from collections import Counter
|
|
9
10
|
from typing import Optional, Dict, Tuple
|
|
10
11
|
|
|
11
12
|
from flask import session
|
|
@@ -83,7 +84,6 @@ def available_pseudo_deck(path):
|
|
|
83
84
|
"""
|
|
84
85
|
load pseudo deck (snapshot) from connection history
|
|
85
86
|
"""
|
|
86
|
-
import os
|
|
87
87
|
return os.listdir(path)
|
|
88
88
|
|
|
89
89
|
|
|
@@ -131,34 +131,37 @@ def _get_type_from_parameters(arg, parameters):
|
|
|
131
131
|
if annotation is not inspect._empty:
|
|
132
132
|
# print(p[arg].annotation)
|
|
133
133
|
if annotation.__module__ == 'typing':
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
|
|
135
|
+
if hasattr(annotation, '__origin__'):
|
|
136
|
+
origin = annotation.__origin__
|
|
137
|
+
if hasattr(origin, '_name') and origin._name in ["Optional", "Union"]:
|
|
138
|
+
arg_type = [i.__name__ for i in annotation.__args__]
|
|
139
|
+
elif hasattr(origin, '__name__'):
|
|
140
|
+
arg_type = origin.__name__
|
|
141
|
+
# todo other types
|
|
142
|
+
elif annotation.__module__ == 'types':
|
|
143
|
+
arg_type = [i.__name__ for i in annotation.__args__]
|
|
144
|
+
|
|
142
145
|
else:
|
|
143
146
|
arg_type = annotation.__name__
|
|
144
147
|
return arg_type
|
|
145
148
|
|
|
146
|
-
|
|
147
|
-
def find_variable_in_script(script: Script, args: Dict[str, str]) -> Optional[Tuple[Dict[str, str], Dict[str, str]]]:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
149
|
+
# # moved to script
|
|
150
|
+
# def find_variable_in_script(script: Script, args: Dict[str, str]) -> Optional[Tuple[Dict[str, str], Dict[str, str]]]:
|
|
151
|
+
# # TODO: need to search for if the variable exists
|
|
152
|
+
# added_variables: list[Dict[str, str]] = [action for action in script.currently_editing_script if
|
|
153
|
+
# action["instrument"] == "variable"]
|
|
154
|
+
#
|
|
155
|
+
# possible_variable_arguments = {}
|
|
156
|
+
# possible_variable_types = {}
|
|
157
|
+
#
|
|
158
|
+
# for arg_name, arg_val in args.items():
|
|
159
|
+
# for added_variable in added_variables:
|
|
160
|
+
# if added_variable["action"] == arg_val:
|
|
161
|
+
# possible_variable_arguments[arg_name] = added_variable["action"]
|
|
162
|
+
# possible_variable_types[arg_name] = "variable"
|
|
163
|
+
#
|
|
164
|
+
# return possible_variable_arguments, possible_variable_types
|
|
162
165
|
|
|
163
166
|
|
|
164
167
|
def _convert_by_str(args, arg_types):
|
|
@@ -424,3 +427,13 @@ def load_deck(pkl_name: str):
|
|
|
424
427
|
return pseudo_deck
|
|
425
428
|
except FileNotFoundError:
|
|
426
429
|
return None
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def check_config_duplicate(config):
|
|
433
|
+
"""
|
|
434
|
+
Checks if the config entry has any duplicate
|
|
435
|
+
:param config: [{"arg": 1}, {"arg": 1}, {"arg": 1}]
|
|
436
|
+
:return: [True, False]
|
|
437
|
+
"""
|
|
438
|
+
hashable_data = [tuple(sorted(d.items())) for d in config]
|
|
439
|
+
return any(count > 1 for count in Counter(hashable_data).values())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.13"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.11"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/control/templates/control/controllers_home.html
RENAMED
|
File without changes
|
{ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/control/templates/control/controllers_new.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ivoryos-0.1.11 → ivoryos-0.1.13}/ivoryos/routes/design/templates/design/experiment_run.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|