ivoryos 1.0.9__py3-none-any.whl → 1.4.4__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.
- docs/source/conf.py +84 -0
- ivoryos/__init__.py +17 -207
- ivoryos/app.py +154 -0
- ivoryos/config.py +1 -0
- ivoryos/optimizer/ax_optimizer.py +191 -0
- ivoryos/optimizer/base_optimizer.py +84 -0
- ivoryos/optimizer/baybe_optimizer.py +193 -0
- ivoryos/optimizer/nimo_optimizer.py +173 -0
- ivoryos/optimizer/registry.py +11 -0
- ivoryos/routes/auth/auth.py +43 -14
- ivoryos/routes/auth/templates/change_password.html +32 -0
- ivoryos/routes/control/control.py +101 -366
- ivoryos/routes/control/control_file.py +33 -0
- ivoryos/routes/control/control_new_device.py +152 -0
- ivoryos/routes/control/templates/controllers.html +193 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/control/utils.py +40 -0
- ivoryos/routes/data/data.py +197 -0
- ivoryos/routes/data/templates/components/step_card.html +78 -0
- ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +14 -8
- ivoryos/routes/data/templates/workflow_view.html +360 -0
- ivoryos/routes/design/__init__.py +4 -0
- ivoryos/routes/design/design.py +348 -657
- ivoryos/routes/design/design_file.py +68 -0
- ivoryos/routes/design/design_step.py +171 -0
- ivoryos/routes/design/templates/components/action_form.html +53 -0
- ivoryos/routes/design/templates/components/actions_panel.html +25 -0
- ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
- ivoryos/routes/design/templates/components/canvas.html +5 -0
- ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
- ivoryos/routes/design/templates/components/canvas_header.html +75 -0
- ivoryos/routes/design/templates/components/canvas_main.html +39 -0
- ivoryos/routes/design/templates/components/deck_selector.html +10 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +53 -0
- ivoryos/routes/design/templates/components/info_modal.html +318 -0
- ivoryos/routes/design/templates/components/instruments_panel.html +88 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -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/python_code_overlay.html +56 -0
- ivoryos/routes/design/templates/components/sidebar.html +15 -0
- ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos/routes/design/templates/experiment_builder.html +44 -0
- ivoryos/routes/execute/__init__.py +0 -0
- ivoryos/routes/execute/execute.py +377 -0
- ivoryos/routes/execute/execute_file.py +78 -0
- ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos/routes/execute/templates/components/logging_panel.html +56 -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 +60 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +520 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +383 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +18 -0
- ivoryos/routes/execute/templates/experiment_run.html +30 -0
- ivoryos/routes/library/__init__.py +0 -0
- ivoryos/routes/library/library.py +157 -0
- ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +32 -23
- ivoryos/routes/main/main.py +31 -3
- ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
- ivoryos/server.py +180 -0
- ivoryos/socket_handlers.py +52 -0
- ivoryos/static/ivoryos_logo.png +0 -0
- ivoryos/static/js/action_handlers.js +384 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/socket_handler.js +40 -5
- ivoryos/static/js/sortable_design.js +107 -56
- ivoryos/static/js/ui_state.js +114 -0
- ivoryos/templates/base.html +67 -8
- ivoryos/utils/bo_campaign.py +180 -3
- ivoryos/utils/client_proxy.py +267 -36
- ivoryos/utils/db_models.py +300 -65
- ivoryos/utils/decorators.py +34 -0
- ivoryos/utils/form.py +63 -29
- ivoryos/utils/global_config.py +34 -1
- ivoryos/utils/nest_script.py +314 -0
- ivoryos/utils/py_to_json.py +295 -0
- ivoryos/utils/script_runner.py +599 -165
- ivoryos/utils/serilize.py +201 -0
- ivoryos/utils/task_runner.py +71 -21
- ivoryos/utils/utils.py +50 -6
- ivoryos/version.py +1 -1
- ivoryos-1.4.4.dist-info/METADATA +263 -0
- ivoryos-1.4.4.dist-info/RECORD +119 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/WHEEL +1 -1
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/top_level.txt +1 -0
- tests/unit/test_type_conversion.py +42 -0
- tests/unit/test_util.py +3 -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/database/database.py +0 -306
- ivoryos/routes/database/templates/database/step_card.html +0 -7
- ivoryos/routes/database/templates/database/workflow_view.html +0 -130
- ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
- ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- ivoryos-1.0.9.dist-info/METADATA +0 -218
- ivoryos-1.0.9.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/main/templates/{main/help.html → help.html} +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,350 +1,127 @@
|
|
|
1
|
-
import
|
|
1
|
+
import copy
|
|
2
2
|
|
|
3
|
-
from flask import Blueprint, redirect,
|
|
4
|
-
send_file
|
|
3
|
+
from flask import Blueprint, redirect, flash, request, render_template, session, current_app, jsonify
|
|
5
4
|
from flask_login import login_required
|
|
6
5
|
|
|
7
|
-
from ivoryos.
|
|
6
|
+
from ivoryos.routes.control.control_file import control_file
|
|
7
|
+
from ivoryos.routes.control.control_new_device import control_temp
|
|
8
|
+
from ivoryos.routes.control.utils import post_session_by_instrument, get_session_by_instrument, find_instrument_by_name
|
|
8
9
|
from ivoryos.utils.global_config import GlobalConfig
|
|
9
|
-
from ivoryos.utils import
|
|
10
|
-
from ivoryos.utils.form import create_form_from_module, format_name
|
|
10
|
+
from ivoryos.utils.form import create_form_from_module, create_form_from_pseudo
|
|
11
11
|
from ivoryos.utils.task_runner import TaskRunner
|
|
12
12
|
|
|
13
13
|
global_config = GlobalConfig()
|
|
14
14
|
runner = TaskRunner()
|
|
15
15
|
|
|
16
|
-
control = Blueprint('control', __name__, template_folder='templates
|
|
16
|
+
control = Blueprint('control', __name__, template_folder='templates')
|
|
17
17
|
|
|
18
|
+
control.register_blueprint(control_file)
|
|
19
|
+
control.register_blueprint(control_temp)
|
|
18
20
|
|
|
19
|
-
@control.route("/control/home/deck", strict_slashes=False)
|
|
20
|
-
@login_required
|
|
21
|
-
def deck_controllers():
|
|
22
|
-
"""
|
|
23
|
-
.. :quickref: Direct Control; controls home interface
|
|
24
|
-
|
|
25
|
-
deck control home interface for listing all deck instruments
|
|
26
|
-
|
|
27
|
-
.. http:get:: /control/home/deck
|
|
28
|
-
"""
|
|
29
|
-
deck_variables = global_config.deck_snapshot.keys()
|
|
30
|
-
deck_list = utils.import_history(os.path.join(current_app.config["OUTPUT_FOLDER"], 'deck_history.txt'))
|
|
31
|
-
return render_template('controllers_home.html', defined_variables=deck_variables, deck=True, history=deck_list)
|
|
32
21
|
|
|
33
22
|
|
|
34
|
-
@control.route("/
|
|
35
|
-
@control.route("
|
|
23
|
+
@control.route("/", strict_slashes=False, methods=["GET", "POST"])
|
|
24
|
+
@control.route("/<string:instrument>", strict_slashes=False, methods=["GET", "POST"])
|
|
36
25
|
@login_required
|
|
37
|
-
def
|
|
26
|
+
async def deck_controllers(instrument: str = None):
|
|
38
27
|
"""
|
|
39
|
-
.. :quickref: Direct Control;
|
|
40
|
-
|
|
41
|
-
interface for connecting a new <instrument>
|
|
42
|
-
|
|
43
|
-
.. http:get:: /control/new/
|
|
44
|
-
|
|
45
|
-
:param instrument: instrument name
|
|
46
|
-
:type instrument: str
|
|
47
|
-
|
|
48
|
-
.. http:post:: /control/new/
|
|
28
|
+
.. :quickref: Direct Control; device (instruments) and methods
|
|
49
29
|
|
|
50
|
-
|
|
51
|
-
:form kwargs: dynamic module initialization kwargs fields
|
|
30
|
+
device home interface for listing all instruments and methods, selecting an instrument to run its methods
|
|
52
31
|
|
|
53
|
-
|
|
54
|
-
device = None
|
|
55
|
-
args = None
|
|
56
|
-
if instrument:
|
|
57
|
-
|
|
58
|
-
device = find_instrument_by_name(instrument)
|
|
59
|
-
args = utils.inspect.signature(device.__init__)
|
|
60
|
-
|
|
61
|
-
if request.method == 'POST':
|
|
62
|
-
device_name = request.form.get("device_name", "")
|
|
63
|
-
if device_name and device_name in globals():
|
|
64
|
-
flash("Device name is defined. Try another name, or leave it as blank to auto-configure")
|
|
65
|
-
return render_template('controllers_new.html', instrument=instrument,
|
|
66
|
-
api_variables=global_config.api_variables,
|
|
67
|
-
device=device, args=args, defined_variables=global_config.defined_variables)
|
|
68
|
-
if device_name == "":
|
|
69
|
-
device_name = device.__name__.lower() + "_"
|
|
70
|
-
num = 1
|
|
71
|
-
while device_name + str(num) in global_config.defined_variables:
|
|
72
|
-
num += 1
|
|
73
|
-
device_name = device_name + str(num)
|
|
74
|
-
kwargs = request.form.to_dict()
|
|
75
|
-
kwargs.pop("device_name")
|
|
76
|
-
for i in kwargs:
|
|
77
|
-
if kwargs[i] in global_config.defined_variables:
|
|
78
|
-
kwargs[i] = global_config.defined_variables[kwargs[i]]
|
|
79
|
-
try:
|
|
80
|
-
utils.convert_config_type(kwargs, device.__init__.__annotations__, is_class=True)
|
|
81
|
-
except Exception as e:
|
|
82
|
-
flash(e)
|
|
83
|
-
try:
|
|
84
|
-
global_config.defined_variables[device_name] = device(**kwargs)
|
|
85
|
-
# global_config.defined_variables.add(device_name)
|
|
86
|
-
return redirect(url_for('control.controllers_home'))
|
|
87
|
-
except Exception as e:
|
|
88
|
-
flash(e)
|
|
89
|
-
return render_template('controllers_new.html', instrument=instrument, api_variables=global_config.api_variables,
|
|
90
|
-
device=device, args=args, defined_variables=global_config.defined_variables)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@control.route("/control/home/temp", strict_slashes=False)
|
|
94
|
-
@login_required
|
|
95
|
-
def controllers_home():
|
|
96
|
-
"""
|
|
97
|
-
.. :quickref: Direct Control; temp control home interface
|
|
98
|
-
|
|
99
|
-
temporarily connected devices home interface for listing all instruments
|
|
100
|
-
|
|
101
|
-
.. http:get:: /control/home/temp
|
|
102
|
-
|
|
103
|
-
"""
|
|
104
|
-
# defined_variables = parse_deck(deck)
|
|
105
|
-
defined_variables = global_config.defined_variables.keys()
|
|
106
|
-
return render_template('controllers_home.html', defined_variables=defined_variables)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
@control.route("/control/<instrument>/methods", methods=['GET', 'POST'])
|
|
110
|
-
@login_required
|
|
111
|
-
def controllers(instrument: str):
|
|
112
|
-
"""
|
|
113
|
-
.. :quickref: Direct Control; control interface
|
|
114
|
-
|
|
115
|
-
control interface for selected <instrument>
|
|
116
|
-
|
|
117
|
-
.. http:get:: /control/<instrument>/methods
|
|
118
|
-
|
|
119
|
-
:param instrument: instrument name
|
|
120
|
-
:type instrument: str
|
|
121
|
-
|
|
122
|
-
.. http:post:: /control/<instrument>/methods
|
|
123
|
-
|
|
124
|
-
:form hidden_name: function name (hidden field)
|
|
125
|
-
:form kwargs: dynamic kwargs field
|
|
126
|
-
|
|
127
|
-
"""
|
|
128
|
-
inst_object = find_instrument_by_name(instrument)
|
|
129
|
-
_forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
|
|
130
|
-
functions = list(_forms.keys())
|
|
131
|
-
|
|
132
|
-
order = get_session_by_instrument('card_order', instrument)
|
|
133
|
-
hidden_functions = get_session_by_instrument('hide_function', instrument)
|
|
134
|
-
|
|
135
|
-
for function in functions:
|
|
136
|
-
if function not in hidden_functions and function not in order:
|
|
137
|
-
order.append(function)
|
|
138
|
-
post_session_by_instrument('card_order', instrument, order)
|
|
139
|
-
forms = {name: _forms[name] for name in order if name in _forms}
|
|
140
|
-
if request.method == 'POST':
|
|
141
|
-
all_kwargs = request.form.copy()
|
|
142
|
-
method_name = all_kwargs.pop("hidden_name", None)
|
|
143
|
-
# if method_name is not None:
|
|
144
|
-
form = forms.get(method_name)
|
|
145
|
-
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
146
|
-
function_executable = getattr(inst_object, method_name)
|
|
147
|
-
if form and form.validate_on_submit():
|
|
148
|
-
try:
|
|
149
|
-
kwargs.pop("hidden_name")
|
|
150
|
-
output = runner.run_single_step(instrument, method_name, kwargs, wait=True,
|
|
151
|
-
current_app=current_app._get_current_object())
|
|
152
|
-
# output = function_executable(**kwargs)
|
|
153
|
-
flash(f"\nRun Success! Output value: {output}.")
|
|
154
|
-
except Exception as e:
|
|
155
|
-
flash(e.__str__())
|
|
156
|
-
else:
|
|
157
|
-
flash(form.errors)
|
|
158
|
-
return render_template('controllers.html', instrument=instrument, forms=forms, format_name=format_name)
|
|
32
|
+
.. http:get:: /instruments
|
|
159
33
|
|
|
160
|
-
|
|
161
|
-
@login_required
|
|
162
|
-
def download_proxy():
|
|
163
|
-
"""
|
|
164
|
-
.. :quickref: Direct Control; download proxy interface
|
|
34
|
+
get all instruments for home page
|
|
165
35
|
|
|
166
|
-
|
|
36
|
+
.. http:get:: /instruments/<string:instrument>
|
|
167
37
|
|
|
168
|
-
|
|
169
|
-
"""
|
|
170
|
-
snapshot = global_config.deck_snapshot.copy()
|
|
171
|
-
class_definitions = {}
|
|
172
|
-
# Iterate through each instrument in the snapshot
|
|
173
|
-
for instrument_key, instrument_data in snapshot.items():
|
|
174
|
-
# Iterate through each function associated with the current instrument
|
|
175
|
-
for function_key, function_data in instrument_data.items():
|
|
176
|
-
# Convert the function signature to a string representation
|
|
177
|
-
function_data['signature'] = str(function_data['signature'])
|
|
178
|
-
class_name = instrument_key.split('.')[-1] # Extracting the class name from the path
|
|
179
|
-
class_definitions[class_name.capitalize()] = create_function(request.url_root, class_name, instrument_data)
|
|
180
|
-
# Export the generated class definitions to a .py script
|
|
181
|
-
export_to_python(class_definitions, current_app.config["OUTPUT_FOLDER"])
|
|
182
|
-
filepath = os.path.join(current_app.config["OUTPUT_FOLDER"], "generated_proxy.py")
|
|
183
|
-
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
184
|
-
|
|
185
|
-
@control.route("/api/control/", strict_slashes=False, methods=['GET'])
|
|
186
|
-
@control.route("/api/control/<instrument>", methods=['POST'])
|
|
187
|
-
def backend_control(instrument: str=None):
|
|
188
|
-
"""
|
|
189
|
-
.. :quickref: Backend Control; backend control
|
|
38
|
+
get all methods of the given <instrument>
|
|
190
39
|
|
|
191
|
-
|
|
40
|
+
.. http:post:: /instruments/<string:instrument>
|
|
192
41
|
|
|
193
|
-
|
|
42
|
+
send POST request to run a method of the given <instrument>
|
|
194
43
|
|
|
195
|
-
:param instrument: instrument name
|
|
44
|
+
:param instrument: instrument name, if not provided, list all instruments
|
|
196
45
|
:type instrument: str
|
|
197
|
-
|
|
198
|
-
.. http:post:: /api/control/
|
|
46
|
+
:status 200: render template with instruments and methods
|
|
199
47
|
|
|
200
48
|
"""
|
|
49
|
+
instrument = instrument or request.args.get("instrument")
|
|
50
|
+
forms = None
|
|
201
51
|
if instrument:
|
|
202
52
|
inst_object = find_instrument_by_name(instrument)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
# @control.route("/api/control", strict_slashes=False, methods=['GET'])
|
|
225
|
-
# def backend_client():
|
|
226
|
-
# """
|
|
227
|
-
# .. :quickref: Backend Control; get snapshot
|
|
228
|
-
#
|
|
229
|
-
# backend control through http requests
|
|
230
|
-
#
|
|
231
|
-
# .. http:get:: /api/control/summary
|
|
232
|
-
# """
|
|
233
|
-
# # Create a snapshot of the current deck configuration
|
|
234
|
-
# snapshot = global_config.deck_snapshot.copy()
|
|
235
|
-
#
|
|
236
|
-
# # Iterate through each instrument in the snapshot
|
|
237
|
-
# for instrument_key, instrument_data in snapshot.items():
|
|
238
|
-
# # Iterate through each function associated with the current instrument
|
|
239
|
-
# for function_key, function_data in instrument_data.items():
|
|
240
|
-
# # Convert the function signature to a string representation
|
|
241
|
-
# function_data['signature'] = str(function_data['signature'])
|
|
242
|
-
# return jsonify(snapshot), 200
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
@control.route("/control/import/module", methods=['POST'])
|
|
246
|
-
def import_api():
|
|
247
|
-
"""
|
|
248
|
-
.. :quickref: Advanced Features; Manually import API module(s)
|
|
249
|
-
|
|
250
|
-
importing other Python modules
|
|
251
|
-
|
|
252
|
-
.. http:post:: /control/import/module
|
|
253
|
-
|
|
254
|
-
:form filepath: API (Python class) module filepath
|
|
255
|
-
|
|
256
|
-
import the module and redirect to :http:get:`/ivoryos/control/new/`
|
|
53
|
+
if instrument.startswith("blocks"):
|
|
54
|
+
forms = create_form_from_pseudo(pseudo=inst_object, autofill=False, design=False)
|
|
55
|
+
else:
|
|
56
|
+
forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
|
|
57
|
+
order = get_session_by_instrument('card_order', instrument)
|
|
58
|
+
hidden_functions = get_session_by_instrument('hidden_functions', instrument)
|
|
59
|
+
functions = list(forms.keys())
|
|
60
|
+
for function in functions:
|
|
61
|
+
if function not in hidden_functions and function not in order:
|
|
62
|
+
order.append(function)
|
|
63
|
+
post_session_by_instrument('card_order', instrument, order)
|
|
64
|
+
forms = {name: forms[name] for name in order if name in forms}
|
|
65
|
+
|
|
66
|
+
if request.method == "POST":
|
|
67
|
+
if not forms:
|
|
68
|
+
return jsonify({"success": False, "error": "Instrument not found"}), 404
|
|
69
|
+
|
|
70
|
+
payload = request.get_json() if request.is_json else request.form.to_dict()
|
|
71
|
+
method_name = payload.pop("hidden_name", None)
|
|
72
|
+
form = forms.get(method_name)
|
|
257
73
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
# filepath.replace('\\', '/')
|
|
261
|
-
name = os.path.split(filepath)[-1].split('.')[0]
|
|
262
|
-
try:
|
|
263
|
-
spec = utils.importlib.util.spec_from_file_location(name, filepath)
|
|
264
|
-
module = utils.importlib.util.module_from_spec(spec)
|
|
265
|
-
spec.loader.exec_module(module)
|
|
266
|
-
classes = utils.inspect.getmembers(module, utils.inspect.isclass)
|
|
267
|
-
if len(classes) == 0:
|
|
268
|
-
flash("Invalid import: no class found in the path")
|
|
269
|
-
return redirect(url_for("control.controllers_home"))
|
|
270
|
-
for i in classes:
|
|
271
|
-
globals()[i[0]] = i[1]
|
|
272
|
-
global_config.api_variables.add(i[0])
|
|
273
|
-
# should handle path error and file type error
|
|
274
|
-
except Exception as e:
|
|
275
|
-
flash(e.__str__())
|
|
276
|
-
return redirect(url_for("control.new_controller"))
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
# @control.route("/disconnect", methods=["GET"])
|
|
280
|
-
# @control.route("/disconnect/<device_name>", methods=["GET"])
|
|
281
|
-
# def disconnect(device_name=None):
|
|
282
|
-
# """TODO handle disconnect device"""
|
|
283
|
-
# if device_name:
|
|
284
|
-
# try:
|
|
285
|
-
# exec(device_name + ".disconnect()")
|
|
286
|
-
# except Exception:
|
|
287
|
-
# pass
|
|
288
|
-
# global_config.defined_variables.remove(device_name)
|
|
289
|
-
# globals().pop(device_name)
|
|
290
|
-
# return redirect(url_for('control.controllers_home'))
|
|
291
|
-
#
|
|
292
|
-
# deck_variables = ["deck." + var for var in set(dir(deck))
|
|
293
|
-
# if not (var.startswith("_") or var[0].isupper() or var.startswith("repackage"))
|
|
294
|
-
# and not type(eval("deck." + var)).__module__ == 'builtins']
|
|
295
|
-
# for i in deck_variables:
|
|
296
|
-
# try:
|
|
297
|
-
# exec(i + ".disconnect()")
|
|
298
|
-
# except Exception:
|
|
299
|
-
# pass
|
|
300
|
-
# globals()["deck"] = None
|
|
301
|
-
# return redirect(url_for('control.deck_controllers'))
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
@control.route("/control/import/deck", methods=['POST'])
|
|
305
|
-
def import_deck():
|
|
306
|
-
"""
|
|
307
|
-
.. :quickref: Advanced Features; Manually import a deck
|
|
74
|
+
if not form:
|
|
75
|
+
return jsonify({"success": False, "error": f"Method {method_name} not found"}), 404
|
|
308
76
|
|
|
309
|
-
|
|
77
|
+
# Extract kwargs
|
|
78
|
+
if request.is_json:
|
|
79
|
+
kwargs = {k: v for k, v in payload.items() if k not in ["csrf_token", "hidden_wait"]}
|
|
80
|
+
else:
|
|
81
|
+
kwargs = {field.name: field.data for field in form if field.name not in ["csrf_token", "hidden_name"]}
|
|
310
82
|
|
|
311
|
-
|
|
83
|
+
wait = str(payload.get("hidden_wait", "true")).lower() == "true"
|
|
312
84
|
|
|
313
|
-
|
|
85
|
+
output = await runner.run_single_step(
|
|
86
|
+
component=instrument, method=method_name, kwargs=kwargs, wait=wait,
|
|
87
|
+
current_app=current_app._get_current_object()
|
|
88
|
+
)
|
|
314
89
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
90
|
+
if request.is_json:
|
|
91
|
+
return jsonify(output)
|
|
92
|
+
else:
|
|
93
|
+
if output.get("success"):
|
|
94
|
+
flash(f"Run Success! Output: {output.get('output', 'None')}")
|
|
95
|
+
else:
|
|
96
|
+
flash(f"Run Error! {output.get('output', 'Unknown error occurred.')}", "error")
|
|
97
|
+
|
|
98
|
+
# GET request → render web form or return snapshot for API
|
|
99
|
+
if request.is_json or request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
100
|
+
# 1.3.2 fix snapshot copy, add building blocks to snapshots
|
|
101
|
+
snapshot = copy.deepcopy(global_config.deck_snapshot)
|
|
102
|
+
building_blocks = copy.deepcopy(global_config.building_blocks)
|
|
103
|
+
snapshot.update(building_blocks)
|
|
104
|
+
for instrument_key, instrument_data in snapshot.items():
|
|
105
|
+
for function_key, function_data in instrument_data.items():
|
|
106
|
+
function_data["signature"] = str(function_data["signature"])
|
|
107
|
+
return jsonify(snapshot)
|
|
108
|
+
|
|
109
|
+
return render_template(
|
|
110
|
+
"controllers.html",
|
|
111
|
+
defined_variables=global_config.deck_snapshot.keys(),
|
|
112
|
+
block_variables=global_config.building_blocks.keys(),
|
|
113
|
+
temp_variables=global_config.defined_variables.keys(),
|
|
114
|
+
instrument=instrument,
|
|
115
|
+
forms=forms,
|
|
116
|
+
session=session
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@control.route('/<string:instrument>/actions/order', methods=['POST'])
|
|
343
120
|
def save_order(instrument: str):
|
|
344
121
|
"""
|
|
345
122
|
.. :quickref: Control Customization; Save functions' order
|
|
346
123
|
|
|
347
|
-
.. http:post:: /
|
|
124
|
+
.. http:post:: instruments/<string:instrument>/actions/order
|
|
348
125
|
|
|
349
126
|
save function drag and drop order for the given <instrument>
|
|
350
127
|
|
|
@@ -354,76 +131,34 @@ def save_order(instrument: str):
|
|
|
354
131
|
post_session_by_instrument('card_order', instrument, data['order'])
|
|
355
132
|
return '', 204
|
|
356
133
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
def hide_function(instrument, function):
|
|
134
|
+
@control.route('/<string:instrument>/actions/<string:function>', methods=["PATCH"])
|
|
135
|
+
def hide_function(instrument: str, function: str):
|
|
360
136
|
"""
|
|
361
|
-
.. :quickref: Control Customization;
|
|
137
|
+
.. :quickref: Control Customization; Toggle function visibility
|
|
362
138
|
|
|
363
|
-
.. http:
|
|
139
|
+
.. http:patch:: /instruments/<instrument>/actions/<function>
|
|
364
140
|
|
|
365
|
-
|
|
141
|
+
Toggle visibility for the given <instrument> and <function>
|
|
366
142
|
|
|
367
143
|
"""
|
|
368
144
|
back = request.referrer
|
|
145
|
+
data = request.get_json()
|
|
146
|
+
hidden = data.get('hidden', True)
|
|
369
147
|
functions = get_session_by_instrument("hidden_functions", instrument)
|
|
370
148
|
order = get_session_by_instrument("card_order", instrument)
|
|
371
|
-
if function not in functions:
|
|
149
|
+
if hidden and function not in functions:
|
|
372
150
|
functions.append(function)
|
|
373
|
-
order
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
return redirect(back)
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
@control.route('/control/<instrument>/<function>/unhide')
|
|
380
|
-
def remove_hidden(instrument: str, function: str):
|
|
381
|
-
"""
|
|
382
|
-
.. :quickref: Control Customization; Remove a hidden function
|
|
383
|
-
|
|
384
|
-
.. http:get:: /control/<instrument>/<function>/unhide
|
|
385
|
-
|
|
386
|
-
Un-hide the given <instrument> and <function>
|
|
387
|
-
|
|
388
|
-
"""
|
|
389
|
-
back = request.referrer
|
|
390
|
-
functions = get_session_by_instrument("hidden_functions", instrument)
|
|
391
|
-
order = get_session_by_instrument("card_order", instrument)
|
|
392
|
-
if function in functions:
|
|
151
|
+
if function in order:
|
|
152
|
+
order.remove(function)
|
|
153
|
+
elif not hidden and function in functions:
|
|
393
154
|
functions.remove(function)
|
|
394
|
-
order
|
|
155
|
+
if function not in order:
|
|
156
|
+
order.append(function)
|
|
395
157
|
post_session_by_instrument('hidden_functions', instrument, functions)
|
|
396
158
|
post_session_by_instrument('card_order', instrument, order)
|
|
397
|
-
return
|
|
159
|
+
return jsonify(success=True, message="Visibility updated")
|
|
398
160
|
|
|
399
161
|
|
|
400
|
-
def get_session_by_instrument(session_name, instrument):
|
|
401
|
-
"""get data from session by instrument"""
|
|
402
|
-
session_object = session.get(session_name, {})
|
|
403
|
-
functions = session_object.get(instrument, [])
|
|
404
|
-
return functions
|
|
405
162
|
|
|
406
163
|
|
|
407
|
-
def post_session_by_instrument(session_name, instrument, data):
|
|
408
|
-
"""
|
|
409
|
-
save new data to session by instrument
|
|
410
|
-
:param session_name: "card_order" or "hidden_functions"
|
|
411
|
-
:param instrument: function name of class object
|
|
412
|
-
:param data: order list or hidden function list
|
|
413
|
-
"""
|
|
414
|
-
session_object = session.get(session_name, {})
|
|
415
|
-
session_object[instrument] = data
|
|
416
|
-
session[session_name] = session_object
|
|
417
|
-
|
|
418
164
|
|
|
419
|
-
def find_instrument_by_name(name: str):
|
|
420
|
-
"""
|
|
421
|
-
find instrument class object by instance name
|
|
422
|
-
"""
|
|
423
|
-
if name.startswith("deck"):
|
|
424
|
-
name = name.replace("deck.", "")
|
|
425
|
-
return getattr(global_config.deck, name)
|
|
426
|
-
elif name in global_config.defined_variables:
|
|
427
|
-
return global_config.defined_variables[name]
|
|
428
|
-
elif name in globals():
|
|
429
|
-
return globals()[name]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from flask import Blueprint, request,current_app, send_file
|
|
3
|
+
from flask_login import login_required
|
|
4
|
+
|
|
5
|
+
from ivoryos.utils.client_proxy import ProxyGenerator
|
|
6
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
7
|
+
|
|
8
|
+
global_config = GlobalConfig()
|
|
9
|
+
|
|
10
|
+
control_file = Blueprint('file', __name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@control_file.route("/files/proxy", strict_slashes=False)
|
|
15
|
+
@login_required
|
|
16
|
+
def download_proxy():
|
|
17
|
+
"""
|
|
18
|
+
.. :quickref: Direct Control Files; Download proxy Python interface
|
|
19
|
+
|
|
20
|
+
download proxy Python interface
|
|
21
|
+
|
|
22
|
+
.. http:get:: /files/proxy
|
|
23
|
+
"""
|
|
24
|
+
generator = ProxyGenerator(request.url_root)
|
|
25
|
+
snapshot = global_config.deck_snapshot.copy()
|
|
26
|
+
|
|
27
|
+
filepath = generator.generate_from_flask_route(
|
|
28
|
+
snapshot,
|
|
29
|
+
request.url_root,
|
|
30
|
+
current_app.config["OUTPUT_FOLDER"]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
return send_file(os.path.abspath(filepath), as_attachment=True)
|