ivoryos 1.0.1__tar.gz → 1.0.4__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-1.0.1/ivoryos.egg-info → ivoryos-1.0.4}/PKG-INFO +37 -1
- {ivoryos-1.0.1 → ivoryos-1.0.4}/README.md +36 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/__init__.py +22 -6
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/auth/auth.py +15 -18
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/control/control.py +78 -43
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/control/templates/control/controllers_home.html +7 -7
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/database/database.py +64 -44
- ivoryos-1.0.1/ivoryos/routes/database/templates/database/experiment_database.html → ivoryos-1.0.4/ivoryos/routes/database/templates/database/scripts_database.html +27 -18
- ivoryos-1.0.1/ivoryos/routes/database/templates/database/workflow_run_database.html → ivoryos-1.0.4/ivoryos/routes/database/templates/database/workflow_database.html +27 -5
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/design/design.py +135 -79
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/design/templates/design/experiment_builder.html +100 -45
- ivoryos-1.0.4/ivoryos/routes/design/templates/design/experiment_run.html +558 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/js/sortable_design.js +1 -1
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/client_proxy.py +1 -1
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/form.py +30 -3
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/script_runner.py +2 -1
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/utils.py +13 -0
- ivoryos-1.0.4/ivoryos/version.py +1 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4/ivoryos.egg-info}/PKG-INFO +37 -1
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos.egg-info/SOURCES.txt +3 -3
- ivoryos-1.0.1/ivoryos/routes/design/templates/design/experiment_run.html +0 -395
- ivoryos-1.0.1/ivoryos/version.py +0 -1
- {ivoryos-1.0.1 → ivoryos-1.0.4}/LICENSE +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/MANIFEST.in +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/config.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/auth/templates/auth/login.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/auth/templates/auth/signup.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/control/templates/control/controllers.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/control/templates/control/controllers_new.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/database/__init__.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/database/templates/database/step_card.html +0 -0
- /ivoryos-1.0.1/ivoryos/routes/database/templates/database/experiment_step_view.html → /ivoryos-1.0.4/ivoryos/routes/database/templates/database/workflow_view.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/main/main.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/main/templates/main/help.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/main/templates/main/home.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/js/socket_handler.js +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/logo.webp +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/static/style.css +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/templates/base.html +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/bo_campaign.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/db_models.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/global_config.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/utils/task_runner.py +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos.egg-info/requires.txt +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos.egg-info/top_level.txt +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/setup.cfg +0 -0
- {ivoryos-1.0.1 → ivoryos-1.0.4}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
|
|
5
5
|
Home-page: https://gitlab.com/heingroup/ivoryos
|
|
6
6
|
Author: Ivory Zhang
|
|
@@ -168,6 +168,42 @@ When you run the application for the first time, it will automatically create th
|
|
|
168
168
|
- [x] show line number option ✅
|
|
169
169
|
- [ ] snapshot version control
|
|
170
170
|
|
|
171
|
+
## Citing
|
|
172
|
+
|
|
173
|
+
If you find this project useful, please consider citing the following manuscript:
|
|
174
|
+
|
|
175
|
+
> Zhang, W., Hao, L., Lai, V. et al. [IvoryOS: an interoperable web interface for orchestrating Python-based self-driving laboratories.](https://www.nature.com/articles/s41467-025-60514-w) Nat Commun 16, 5182 (2025).
|
|
176
|
+
|
|
177
|
+
```bibtex
|
|
178
|
+
@article{zhang_et_al_2025,
|
|
179
|
+
author = {Wenyu Zhang and Lucy Hao and Veronica Lai and Ryan Corkery and Jacob Jessiman and Jiayu Zhang and Junliang Liu and Yusuke Sato and Maria Politi and Matthew E. Reish and Rebekah Greenwood and Noah Depner and Jiyoon Min and Rama El-khawaldeh and Paloma Prieto and Ekaterina Trushina and Jason E. Hein},
|
|
180
|
+
title = {{IvoryOS}: an interoperable web interface for orchestrating {Python-based} self-driving laboratories},
|
|
181
|
+
journal = {Nature Communications},
|
|
182
|
+
year = {2025},
|
|
183
|
+
volume = {16},
|
|
184
|
+
number = {1},
|
|
185
|
+
pages = {5182},
|
|
186
|
+
doi = {10.1038/s41467-025-60514-w},
|
|
187
|
+
url = {https://doi.org/10.1038/s41467-025-60514-w}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
For an additional perspective related to the development of the tool, please see:
|
|
192
|
+
|
|
193
|
+
> Zhang, W., Hein, J. [Behind IvoryOS: Empowering Scientists to Harness Self-Driving Labs for Accelerated Discovery](https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery). Springer Nature Research Communities (2025).
|
|
194
|
+
|
|
195
|
+
```bibtex
|
|
196
|
+
@misc{zhang_hein_2025,
|
|
197
|
+
author = {Wenyu Zhang and Jason Hein},
|
|
198
|
+
title = {Behind {IvoryOS}: Empowering Scientists to Harness Self-Driving Labs for Accelerated Discovery},
|
|
199
|
+
howpublished = {Springer Nature Research Communities},
|
|
200
|
+
year = {2025},
|
|
201
|
+
month = {Jun},
|
|
202
|
+
day = {18},
|
|
203
|
+
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
171
207
|
## Authors and Acknowledgement
|
|
172
208
|
Ivory Zhang, Lucy Hao
|
|
173
209
|
|
|
@@ -156,6 +156,42 @@ When you run the application for the first time, it will automatically create th
|
|
|
156
156
|
- [x] show line number option ✅
|
|
157
157
|
- [ ] snapshot version control
|
|
158
158
|
|
|
159
|
+
## Citing
|
|
160
|
+
|
|
161
|
+
If you find this project useful, please consider citing the following manuscript:
|
|
162
|
+
|
|
163
|
+
> Zhang, W., Hao, L., Lai, V. et al. [IvoryOS: an interoperable web interface for orchestrating Python-based self-driving laboratories.](https://www.nature.com/articles/s41467-025-60514-w) Nat Commun 16, 5182 (2025).
|
|
164
|
+
|
|
165
|
+
```bibtex
|
|
166
|
+
@article{zhang_et_al_2025,
|
|
167
|
+
author = {Wenyu Zhang and Lucy Hao and Veronica Lai and Ryan Corkery and Jacob Jessiman and Jiayu Zhang and Junliang Liu and Yusuke Sato and Maria Politi and Matthew E. Reish and Rebekah Greenwood and Noah Depner and Jiyoon Min and Rama El-khawaldeh and Paloma Prieto and Ekaterina Trushina and Jason E. Hein},
|
|
168
|
+
title = {{IvoryOS}: an interoperable web interface for orchestrating {Python-based} self-driving laboratories},
|
|
169
|
+
journal = {Nature Communications},
|
|
170
|
+
year = {2025},
|
|
171
|
+
volume = {16},
|
|
172
|
+
number = {1},
|
|
173
|
+
pages = {5182},
|
|
174
|
+
doi = {10.1038/s41467-025-60514-w},
|
|
175
|
+
url = {https://doi.org/10.1038/s41467-025-60514-w}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
For an additional perspective related to the development of the tool, please see:
|
|
180
|
+
|
|
181
|
+
> Zhang, W., Hein, J. [Behind IvoryOS: Empowering Scientists to Harness Self-Driving Labs for Accelerated Discovery](https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery). Springer Nature Research Communities (2025).
|
|
182
|
+
|
|
183
|
+
```bibtex
|
|
184
|
+
@misc{zhang_hein_2025,
|
|
185
|
+
author = {Wenyu Zhang and Jason Hein},
|
|
186
|
+
title = {Behind {IvoryOS}: Empowering Scientists to Harness Self-Driving Labs for Accelerated Discovery},
|
|
187
|
+
howpublished = {Springer Nature Research Communities},
|
|
188
|
+
year = {2025},
|
|
189
|
+
month = {Jun},
|
|
190
|
+
day = {18},
|
|
191
|
+
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
159
195
|
## Authors and Acknowledgement
|
|
160
196
|
Ivory Zhang, Lucy Hao
|
|
161
197
|
|
|
@@ -12,7 +12,7 @@ from ivoryos.routes.design.design import design, socketio
|
|
|
12
12
|
from ivoryos.routes.main.main import main
|
|
13
13
|
# from ivoryos.routes.monitor.monitor import monitor
|
|
14
14
|
from ivoryos.utils import utils
|
|
15
|
-
from ivoryos.utils.db_models import db
|
|
15
|
+
from ivoryos.utils.db_models import db, User
|
|
16
16
|
from ivoryos.utils.global_config import GlobalConfig
|
|
17
17
|
from ivoryos.utils.script_runner import ScriptRunner
|
|
18
18
|
from ivoryos.version import __version__ as ivoryos_version
|
|
@@ -40,6 +40,15 @@ app.register_blueprint(control, url_prefix=url_prefix)
|
|
|
40
40
|
app.register_blueprint(design, url_prefix=url_prefix)
|
|
41
41
|
app.register_blueprint(database, url_prefix=url_prefix)
|
|
42
42
|
|
|
43
|
+
@login_manager.user_loader
|
|
44
|
+
def load_user(user_id):
|
|
45
|
+
"""
|
|
46
|
+
This function is called by Flask-Login on every request to get the
|
|
47
|
+
current user object from the user ID stored in the session.
|
|
48
|
+
"""
|
|
49
|
+
# The correct implementation is to fetch the user from the database.
|
|
50
|
+
return db.session.get(User, user_id)
|
|
51
|
+
|
|
43
52
|
|
|
44
53
|
def create_app(config_class=None):
|
|
45
54
|
"""
|
|
@@ -85,8 +94,8 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
85
94
|
logger: Union[str, list] = None,
|
|
86
95
|
logger_output_name: str = None,
|
|
87
96
|
enable_design: bool = True,
|
|
88
|
-
blueprint_plugins: Union[list
|
|
89
|
-
exclude_names: list
|
|
97
|
+
blueprint_plugins: Union[list, Blueprint] = [],
|
|
98
|
+
exclude_names: list = [],
|
|
90
99
|
):
|
|
91
100
|
"""
|
|
92
101
|
Start ivoryOS app server.
|
|
@@ -101,8 +110,8 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
101
110
|
:param logger: logger name of list of logger names, defaults to None
|
|
102
111
|
:param logger_output_name: log file save name of logger, defaults to None, and will use "default.log"
|
|
103
112
|
:param enable_design: enable design canvas, database and workflow execution
|
|
104
|
-
:param blueprint_plugins: custom Blueprint pages
|
|
105
|
-
:param exclude_names: module names to exclude from parsing
|
|
113
|
+
:param blueprint_plugins: Union[list[Blueprint], Blueprint] custom Blueprint pages
|
|
114
|
+
:param exclude_names: list[str] module names to exclude from parsing
|
|
106
115
|
"""
|
|
107
116
|
app = create_app(config_class=config or get_config()) # Create app instance using factory function
|
|
108
117
|
|
|
@@ -153,6 +162,13 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
153
162
|
elif type(logger) is list:
|
|
154
163
|
for log in logger:
|
|
155
164
|
utils.start_logger(socketio, log_filename=logger_path, logger_name=log)
|
|
165
|
+
|
|
166
|
+
# in case Python 3.12 or higher doesn't log URL
|
|
167
|
+
if sys.version_info >= (3, 12):
|
|
168
|
+
ip = utils.get_ip_address()
|
|
169
|
+
print(f"Server running at http://localhost:{port}")
|
|
170
|
+
if not ip == "127.0.0.1":
|
|
171
|
+
print(f"Server running at http://{ip}:{port}")
|
|
156
172
|
socketio.run(app, host=host, port=port, debug=debug, use_reloader=False, allow_unsafe_werkzeug=True)
|
|
157
173
|
# return app
|
|
158
174
|
|
|
@@ -175,7 +191,7 @@ def load_installed_plugins(app, socketio):
|
|
|
175
191
|
return plugin_names
|
|
176
192
|
|
|
177
193
|
|
|
178
|
-
def load_plugins(blueprints: Union[list
|
|
194
|
+
def load_plugins(blueprints: Union[list, Blueprint], app, socketio):
|
|
179
195
|
"""
|
|
180
196
|
Dynamically load installed plugins and attach Flask-SocketIO.
|
|
181
197
|
"""
|
|
@@ -9,7 +9,7 @@ login_manager = LoginManager()
|
|
|
9
9
|
auth = Blueprint('auth', __name__, template_folder='templates/auth')
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@auth.route('/login', methods=['GET', 'POST'])
|
|
12
|
+
@auth.route('/auth/login', methods=['GET', 'POST'])
|
|
13
13
|
def login():
|
|
14
14
|
"""
|
|
15
15
|
.. :quickref: User; login user
|
|
@@ -50,7 +50,7 @@ def login():
|
|
|
50
50
|
return render_template('login.html')
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
@auth.route('/signup', methods=['GET', 'POST'])
|
|
53
|
+
@auth.route('/auth/signup', methods=['GET', 'POST'])
|
|
54
54
|
def signup():
|
|
55
55
|
"""
|
|
56
56
|
.. :quickref: User; signup for a new account
|
|
@@ -69,20 +69,22 @@ def signup():
|
|
|
69
69
|
if request.method == 'POST':
|
|
70
70
|
username = request.form.get('username')
|
|
71
71
|
password = request.form.get('password')
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return redirect(url_for('auth.login'))
|
|
79
|
-
except Exception:
|
|
80
|
-
flash("username exists :(", "error")
|
|
72
|
+
|
|
73
|
+
# Query the database to see if the user already exists.
|
|
74
|
+
existing_user = User.query.filter_by(username=username).first()
|
|
75
|
+
|
|
76
|
+
if existing_user:
|
|
77
|
+
flash("User already exists :(", "error")
|
|
81
78
|
return render_template('signup.html'), 409
|
|
79
|
+
hashed = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
|
|
80
|
+
user = User(username, hashed)
|
|
81
|
+
db.session.add(user)
|
|
82
|
+
db.session.commit()
|
|
83
|
+
return redirect(url_for('auth.login'))
|
|
82
84
|
return render_template('signup.html')
|
|
83
85
|
|
|
84
86
|
|
|
85
|
-
@auth.route("/logout")
|
|
87
|
+
@auth.route("/auth/logout")
|
|
86
88
|
@login_required
|
|
87
89
|
def logout():
|
|
88
90
|
"""
|
|
@@ -92,9 +94,4 @@ def logout():
|
|
|
92
94
|
"""
|
|
93
95
|
logout_user()
|
|
94
96
|
session.clear()
|
|
95
|
-
return redirect(url_for('auth.login'))
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@login_manager.user_loader
|
|
99
|
-
def load_user(username):
|
|
100
|
-
return User(username, password=None)
|
|
97
|
+
return redirect(url_for('auth.login'))
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
-
from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app, jsonify
|
|
3
|
+
from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app, jsonify, \
|
|
4
|
+
send_file
|
|
4
5
|
from flask_login import login_required
|
|
5
6
|
|
|
7
|
+
from ivoryos.utils.client_proxy import export_to_python, create_function
|
|
6
8
|
from ivoryos.utils.global_config import GlobalConfig
|
|
7
9
|
from ivoryos.utils import utils
|
|
8
10
|
from ivoryos.utils.form import create_form_from_module, format_name
|
|
@@ -14,7 +16,7 @@ runner = TaskRunner()
|
|
|
14
16
|
control = Blueprint('control', __name__, template_folder='templates/control')
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
@control.route("/
|
|
19
|
+
@control.route("/control/home/deck", strict_slashes=False)
|
|
18
20
|
@login_required
|
|
19
21
|
def deck_controllers():
|
|
20
22
|
"""
|
|
@@ -22,15 +24,15 @@ def deck_controllers():
|
|
|
22
24
|
|
|
23
25
|
deck control home interface for listing all deck instruments
|
|
24
26
|
|
|
25
|
-
.. http:get:: /
|
|
27
|
+
.. http:get:: /control/home/deck
|
|
26
28
|
"""
|
|
27
29
|
deck_variables = global_config.deck_snapshot.keys()
|
|
28
30
|
deck_list = utils.import_history(os.path.join(current_app.config["OUTPUT_FOLDER"], 'deck_history.txt'))
|
|
29
31
|
return render_template('controllers_home.html', defined_variables=deck_variables, deck=True, history=deck_list)
|
|
30
32
|
|
|
31
33
|
|
|
32
|
-
@control.route("/
|
|
33
|
-
@control.route("/
|
|
34
|
+
@control.route("/control/new/", strict_slashes=False)
|
|
35
|
+
@control.route("/control/new/<instrument>", methods=['GET', 'POST'])
|
|
34
36
|
@login_required
|
|
35
37
|
def new_controller(instrument=None):
|
|
36
38
|
"""
|
|
@@ -38,12 +40,12 @@ def new_controller(instrument=None):
|
|
|
38
40
|
|
|
39
41
|
interface for connecting a new <instrument>
|
|
40
42
|
|
|
41
|
-
.. http:get:: /
|
|
43
|
+
.. http:get:: /control/new/
|
|
42
44
|
|
|
43
45
|
:param instrument: instrument name
|
|
44
46
|
:type instrument: str
|
|
45
47
|
|
|
46
|
-
.. http:post:: /
|
|
48
|
+
.. http:post:: /control/new/
|
|
47
49
|
|
|
48
50
|
:form device_name: module instance name (e.g. my_instance = MyClass())
|
|
49
51
|
:form kwargs: dynamic module initialization kwargs fields
|
|
@@ -88,7 +90,7 @@ def new_controller(instrument=None):
|
|
|
88
90
|
device=device, args=args, defined_variables=global_config.defined_variables)
|
|
89
91
|
|
|
90
92
|
|
|
91
|
-
@control.route("/
|
|
93
|
+
@control.route("/control/home/temp", strict_slashes=False)
|
|
92
94
|
@login_required
|
|
93
95
|
def controllers_home():
|
|
94
96
|
"""
|
|
@@ -96,14 +98,15 @@ def controllers_home():
|
|
|
96
98
|
|
|
97
99
|
temporarily connected devices home interface for listing all instruments
|
|
98
100
|
|
|
99
|
-
.. http:get:: /
|
|
101
|
+
.. http:get:: /control/home/temp
|
|
100
102
|
|
|
101
103
|
"""
|
|
102
104
|
# defined_variables = parse_deck(deck)
|
|
103
|
-
|
|
105
|
+
defined_variables = global_config.defined_variables.keys()
|
|
106
|
+
return render_template('controllers_home.html', defined_variables=defined_variables)
|
|
104
107
|
|
|
105
108
|
|
|
106
|
-
@control.route("/
|
|
109
|
+
@control.route("/control/<instrument>/methods", methods=['GET', 'POST'])
|
|
107
110
|
@login_required
|
|
108
111
|
def controllers(instrument: str):
|
|
109
112
|
"""
|
|
@@ -111,12 +114,12 @@ def controllers(instrument: str):
|
|
|
111
114
|
|
|
112
115
|
control interface for selected <instrument>
|
|
113
116
|
|
|
114
|
-
.. http:get:: /
|
|
117
|
+
.. http:get:: /control/<instrument>/methods
|
|
115
118
|
|
|
116
119
|
:param instrument: instrument name
|
|
117
120
|
:type instrument: str
|
|
118
121
|
|
|
119
|
-
.. http:post:: /
|
|
122
|
+
.. http:post:: /control/<instrument>/methods
|
|
120
123
|
|
|
121
124
|
:form hidden_name: function name (hidden field)
|
|
122
125
|
:form kwargs: dynamic kwargs field
|
|
@@ -154,24 +157,50 @@ def controllers(instrument: str):
|
|
|
154
157
|
flash(form.errors)
|
|
155
158
|
return render_template('controllers.html', instrument=instrument, forms=forms, format_name=format_name)
|
|
156
159
|
|
|
160
|
+
@control.route("/control/download", strict_slashes=False)
|
|
161
|
+
@login_required
|
|
162
|
+
def download_proxy():
|
|
163
|
+
"""
|
|
164
|
+
.. :quickref: Direct Control; download proxy interface
|
|
165
|
+
|
|
166
|
+
download proxy interface
|
|
157
167
|
|
|
158
|
-
|
|
168
|
+
.. http:get:: /control/download
|
|
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'])
|
|
159
187
|
def backend_control(instrument: str=None):
|
|
160
188
|
"""
|
|
161
189
|
.. :quickref: Backend Control; backend control
|
|
162
190
|
|
|
163
191
|
backend control through http requests
|
|
164
192
|
|
|
165
|
-
.. http:get:: /
|
|
193
|
+
.. http:get:: /api/control/
|
|
166
194
|
|
|
167
195
|
:param instrument: instrument name
|
|
168
196
|
:type instrument: str
|
|
169
197
|
|
|
170
|
-
.. http:post:: /
|
|
198
|
+
.. http:post:: /api/control/
|
|
171
199
|
|
|
172
200
|
"""
|
|
173
|
-
|
|
174
|
-
|
|
201
|
+
if instrument:
|
|
202
|
+
inst_object = find_instrument_by_name(instrument)
|
|
203
|
+
forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
|
|
175
204
|
|
|
176
205
|
if request.method == 'POST':
|
|
177
206
|
method_name = request.form.get("hidden_name", None)
|
|
@@ -183,42 +212,48 @@ def backend_control(instrument: str=None):
|
|
|
183
212
|
current_app=current_app._get_current_object())
|
|
184
213
|
return jsonify(output), 200
|
|
185
214
|
|
|
186
|
-
|
|
187
|
-
@control.route("/backend_control", methods=['GET'])
|
|
188
|
-
def backend_client():
|
|
189
|
-
"""
|
|
190
|
-
.. :quickref: Backend Control; get snapshot
|
|
191
|
-
|
|
192
|
-
backend control through http requests
|
|
193
|
-
|
|
194
|
-
.. http:get:: /backend_control
|
|
195
|
-
"""
|
|
196
|
-
# Create a snapshot of the current deck configuration
|
|
197
215
|
snapshot = global_config.deck_snapshot.copy()
|
|
198
|
-
|
|
199
216
|
# Iterate through each instrument in the snapshot
|
|
200
217
|
for instrument_key, instrument_data in snapshot.items():
|
|
201
218
|
# Iterate through each function associated with the current instrument
|
|
202
219
|
for function_key, function_data in instrument_data.items():
|
|
203
220
|
# Convert the function signature to a string representation
|
|
204
221
|
function_data['signature'] = str(function_data['signature'])
|
|
222
|
+
return jsonify(snapshot), 200
|
|
205
223
|
|
|
206
|
-
|
|
207
|
-
|
|
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
|
|
208
243
|
|
|
209
244
|
|
|
210
|
-
@control.route("/
|
|
245
|
+
@control.route("/control/import/module", methods=['POST'])
|
|
211
246
|
def import_api():
|
|
212
247
|
"""
|
|
213
248
|
.. :quickref: Advanced Features; Manually import API module(s)
|
|
214
249
|
|
|
215
250
|
importing other Python modules
|
|
216
251
|
|
|
217
|
-
.. http:post:: /
|
|
252
|
+
.. http:post:: /control/import/module
|
|
218
253
|
|
|
219
254
|
:form filepath: API (Python class) module filepath
|
|
220
255
|
|
|
221
|
-
import the module and redirect to :http:get:`/ivoryos/
|
|
256
|
+
import the module and redirect to :http:get:`/ivoryos/control/new/`
|
|
222
257
|
|
|
223
258
|
"""
|
|
224
259
|
filepath = request.form.get('filepath')
|
|
@@ -266,12 +301,12 @@ def import_api():
|
|
|
266
301
|
# return redirect(url_for('control.deck_controllers'))
|
|
267
302
|
|
|
268
303
|
|
|
269
|
-
@control.route("/
|
|
304
|
+
@control.route("/control/import/deck", methods=['POST'])
|
|
270
305
|
def import_deck():
|
|
271
306
|
"""
|
|
272
307
|
.. :quickref: Advanced Features; Manually import a deck
|
|
273
308
|
|
|
274
|
-
.. http:post:: /import_deck
|
|
309
|
+
.. http:post:: /control/import_deck
|
|
275
310
|
|
|
276
311
|
:form filepath: deck module filepath
|
|
277
312
|
|
|
@@ -304,12 +339,12 @@ def import_deck():
|
|
|
304
339
|
return redirect(back)
|
|
305
340
|
|
|
306
341
|
|
|
307
|
-
@control.route('/save-order
|
|
342
|
+
@control.route('/control/<instrument>/save-order', methods=['POST'])
|
|
308
343
|
def save_order(instrument: str):
|
|
309
344
|
"""
|
|
310
345
|
.. :quickref: Control Customization; Save functions' order
|
|
311
346
|
|
|
312
|
-
.. http:post:: /save-order
|
|
347
|
+
.. http:post:: /control/save-order
|
|
313
348
|
|
|
314
349
|
save function drag and drop order for the given <instrument>
|
|
315
350
|
|
|
@@ -320,12 +355,12 @@ def save_order(instrument: str):
|
|
|
320
355
|
return '', 204
|
|
321
356
|
|
|
322
357
|
|
|
323
|
-
@control.route('/
|
|
358
|
+
@control.route('/control/<instrument>/<function>/hide')
|
|
324
359
|
def hide_function(instrument, function):
|
|
325
360
|
"""
|
|
326
361
|
.. :quickref: Control Customization; Hide function
|
|
327
362
|
|
|
328
|
-
.. http:get::
|
|
363
|
+
.. http:get:: //control/<instrument>/<function>/hide
|
|
329
364
|
|
|
330
365
|
Hide the given <instrument> and <function>
|
|
331
366
|
|
|
@@ -341,12 +376,12 @@ def hide_function(instrument, function):
|
|
|
341
376
|
return redirect(back)
|
|
342
377
|
|
|
343
378
|
|
|
344
|
-
@control.route('/
|
|
379
|
+
@control.route('/control/<instrument>/<function>/unhide')
|
|
345
380
|
def remove_hidden(instrument: str, function: str):
|
|
346
381
|
"""
|
|
347
382
|
.. :quickref: Control Customization; Remove a hidden function
|
|
348
383
|
|
|
349
|
-
.. http:get:: /
|
|
384
|
+
.. http:get:: /control/<instrument>/<function>/unhide
|
|
350
385
|
|
|
351
386
|
Un-hide the given <instrument> and <function>
|
|
352
387
|
|
{ivoryos-1.0.1 → ivoryos-1.0.4}/ivoryos/routes/control/templates/control/controllers_home.html
RENAMED
|
@@ -6,21 +6,21 @@
|
|
|
6
6
|
{% for instrument in defined_variables %}
|
|
7
7
|
<div class="col-xl-3 col-lg-4 col-md-6 mb-4 ">
|
|
8
8
|
<div class="bg-white rounded shadow-sm position-relative">
|
|
9
|
-
|
|
9
|
+
{% if deck %}
|
|
10
10
|
{# <a href="{{ url_for('control.disconnect', instrument=instrument) }}" class="stretched-link controller-card" style="float: right;color: red; position: relative;">Disconnect <i class="bi bi-x-square"></i></a>#}
|
|
11
11
|
<div class="p-4 controller-card">
|
|
12
12
|
<h5 class=""><a href="{{ url_for('control.controllers', instrument=instrument) }}" class="text-dark stretched-link">{{instrument.split(".")[1]}}</a></h5>
|
|
13
13
|
</div>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
{% else %}
|
|
15
|
+
<div class="p-4 controller-card">
|
|
16
|
+
<h5 class=""><a href="{{ url_for('control.controllers', instrument=instrument) }}" class="text-dark stretched-link">{{instrument}}</a></h5>
|
|
17
|
+
</div>
|
|
18
|
+
{% endif %}
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
21
21
|
{% endfor %}
|
|
22
22
|
<div class="d-flex mb-3">
|
|
23
|
-
<a href="{{ url_for('
|
|
23
|
+
<a href="{{ url_for('control.download_proxy', filetype='proxy') }}" class="btn btn-outline-primary">
|
|
24
24
|
<i class="bi bi-download"></i> Download remote control script
|
|
25
25
|
</a>
|
|
26
26
|
</div>
|