ivoryos 1.0.9__tar.gz → 1.2.0__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.9/ivoryos.egg-info → ivoryos-1.2.0}/PKG-INFO +5 -8
- {ivoryos-1.0.9 → ivoryos-1.2.0}/README.md +4 -7
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/__init__.py +26 -7
- ivoryos-1.2.0/ivoryos/routes/api/api.py +56 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/routes/auth/auth.py +5 -5
- ivoryos-1.2.0/ivoryos/routes/control/control.py +134 -0
- ivoryos-1.2.0/ivoryos/routes/control/control_file.py +36 -0
- ivoryos-1.2.0/ivoryos/routes/control/control_new_device.py +142 -0
- ivoryos-1.2.0/ivoryos/routes/control/templates/controllers.html +166 -0
- ivoryos-1.2.0/ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos-1.2.0/ivoryos/routes/control/utils.py +38 -0
- ivoryos-1.2.0/ivoryos/routes/data/data.py +129 -0
- ivoryos-1.2.0/ivoryos/routes/data/templates/components/step_card.html +13 -0
- {ivoryos-1.0.9/ivoryos/routes/database/templates/database → ivoryos-1.2.0/ivoryos/routes/data/templates}/workflow_database.html +14 -8
- {ivoryos-1.0.9/ivoryos/routes/database/templates/database → ivoryos-1.2.0/ivoryos/routes/data/templates}/workflow_view.html +3 -3
- ivoryos-1.2.0/ivoryos/routes/design/__init__.py +4 -0
- ivoryos-1.2.0/ivoryos/routes/design/design.py +428 -0
- ivoryos-1.2.0/ivoryos/routes/design/design_file.py +68 -0
- ivoryos-1.2.0/ivoryos/routes/design/design_step.py +145 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/action_form.html +53 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/actions_panel.html +25 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/canvas.html +5 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/canvas_header.html +75 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/canvas_main.html +34 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/deck_selector.html +10 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/edit_action_form.html +38 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/modals.html +6 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/python_code_overlay.html +39 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/sidebar.html +15 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos-1.2.0/ivoryos/routes/design/templates/experiment_builder.html +41 -0
- ivoryos-1.2.0/ivoryos/routes/execute/execute.py +317 -0
- ivoryos-1.2.0/ivoryos/routes/execute/execute_file.py +78 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/run_panel.html +9 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/tab_bayesian.html +398 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
- ivoryos-1.2.0/ivoryos/routes/execute/templates/experiment_run.html +294 -0
- ivoryos-1.2.0/ivoryos/routes/library/library.py +159 -0
- ivoryos-1.0.9/ivoryos/routes/database/templates/database/scripts_database.html → ivoryos-1.2.0/ivoryos/routes/library/templates/library.html +30 -22
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/routes/main/main.py +1 -1
- {ivoryos-1.0.9/ivoryos/routes/main/templates/main → ivoryos-1.2.0/ivoryos/routes/main/templates}/home.html +4 -4
- ivoryos-1.2.0/ivoryos/socket_handlers.py +52 -0
- ivoryos-1.2.0/ivoryos/static/js/action_handlers.js +213 -0
- ivoryos-1.2.0/ivoryos/static/js/db_delete.js +23 -0
- ivoryos-1.2.0/ivoryos/static/js/script_metadata.js +39 -0
- ivoryos-1.2.0/ivoryos/static/js/sortable_design.js +138 -0
- ivoryos-1.2.0/ivoryos/static/js/ui_state.js +113 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/templates/base.html +4 -4
- ivoryos-1.2.0/ivoryos/utils/bo_campaign.py +263 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/db_models.py +14 -5
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/form.py +5 -9
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/global_config.py +13 -1
- ivoryos-1.2.0/ivoryos/utils/py_to_json.py +225 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/script_runner.py +49 -7
- ivoryos-1.2.0/ivoryos/utils/serilize.py +203 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/task_runner.py +4 -1
- ivoryos-1.2.0/ivoryos/version.py +1 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0/ivoryos.egg-info}/PKG-INFO +5 -8
- ivoryos-1.2.0/ivoryos.egg-info/SOURCES.txt +109 -0
- ivoryos-1.2.0/tests/integration/__init__.py +0 -0
- ivoryos-1.0.9/ivoryos/routes/control/control.py +0 -429
- ivoryos-1.0.9/ivoryos/routes/control/templates/control/controllers.html +0 -78
- ivoryos-1.0.9/ivoryos/routes/control/templates/control/controllers_home.html +0 -55
- ivoryos-1.0.9/ivoryos/routes/control/templates/control/controllers_new.html +0 -89
- ivoryos-1.0.9/ivoryos/routes/database/database.py +0 -306
- ivoryos-1.0.9/ivoryos/routes/database/templates/database/step_card.html +0 -7
- ivoryos-1.0.9/ivoryos/routes/design/design.py +0 -786
- ivoryos-1.0.9/ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
- ivoryos-1.0.9/ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- ivoryos-1.0.9/ivoryos/static/js/sortable_design.js +0 -105
- ivoryos-1.0.9/ivoryos/utils/bo_campaign.py +0 -87
- ivoryos-1.0.9/ivoryos/version.py +0 -1
- ivoryos-1.0.9/ivoryos.egg-info/SOURCES.txt +0 -65
- {ivoryos-1.0.9 → ivoryos-1.2.0}/LICENSE +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/MANIFEST.in +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/config.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-1.0.9/ivoryos/routes/auth/templates/auth → ivoryos-1.2.0/ivoryos/routes/auth/templates}/login.html +0 -0
- {ivoryos-1.0.9/ivoryos/routes/auth/templates/auth → ivoryos-1.2.0/ivoryos/routes/auth/templates}/signup.html +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-1.0.9/ivoryos/routes/database → ivoryos-1.2.0/ivoryos/routes/data}/__init__.py +0 -0
- {ivoryos-1.0.9/ivoryos/routes/design → ivoryos-1.2.0/ivoryos/routes/execute}/__init__.py +0 -0
- {ivoryos-1.0.9/ivoryos/routes/main → ivoryos-1.2.0/ivoryos/routes/library}/__init__.py +0 -0
- {ivoryos-1.0.9/ivoryos/utils → ivoryos-1.2.0/ivoryos/routes/main}/__init__.py +0 -0
- {ivoryos-1.0.9/ivoryos/routes/main/templates/main → ivoryos-1.2.0/ivoryos/routes/main/templates}/help.html +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/js/socket_handler.js +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/logo.webp +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/static/style.css +0 -0
- {ivoryos-1.0.9/tests → ivoryos-1.2.0/ivoryos/utils}/__init__.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/client_proxy.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos/utils/utils.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos.egg-info/requires.txt +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/ivoryos.egg-info/top_level.txt +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/setup.cfg +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/setup.py +0 -0
- {ivoryos-1.0.9/tests/integration → ivoryos-1.2.0/tests}/__init__.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/tests/conftest.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/tests/integration/test_route_auth.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/tests/integration/test_route_control.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/tests/integration/test_route_database.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/tests/integration/test_route_design.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/tests/integration/test_route_main.py +0 -0
- {ivoryos-1.0.9 → ivoryos-1.2.0}/tests/integration/test_sockets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
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
|
|
@@ -39,7 +39,7 @@ With the least modification of the current workflow, user can design, manage and
|
|
|
39
39
|
This software is developed and tested using Windows. This software and its dependencies are compatible across major platforms: Linux, macOS, and Windows. Some dependencies (Flask-SQLAlchemy) may require additional setup.
|
|
40
40
|
|
|
41
41
|
### Python Version
|
|
42
|
-
Python >=3.
|
|
42
|
+
Python >=3.10 for best compatibility. Python >=3.7 without Ax.
|
|
43
43
|
### Python dependencies
|
|
44
44
|
This software is compatible with the latest versions of all dependencies.
|
|
45
45
|
- bcrypt~=4.0
|
|
@@ -50,8 +50,7 @@ This software is compatible with the latest versions of all dependencies.
|
|
|
50
50
|
- SQLAlchemy-Utils~=0.41
|
|
51
51
|
- Flask-WTF~=1.2
|
|
52
52
|
- python-dotenv==1.0.1
|
|
53
|
-
-
|
|
54
|
-
- ax-platform (optional ~=0.3 or ~=0.4 for Python>=3.9)
|
|
53
|
+
- ax-platform (optional ~=0.4 for Python>=3.9)
|
|
55
54
|
|
|
56
55
|
## Installation
|
|
57
56
|
```bash
|
|
@@ -61,7 +60,7 @@ or
|
|
|
61
60
|
```bash
|
|
62
61
|
git clone https://gitlab.com/heingroup/ivoryos.git
|
|
63
62
|
cd ivoryos
|
|
64
|
-
pip install
|
|
63
|
+
pip install .
|
|
65
64
|
```
|
|
66
65
|
|
|
67
66
|
The installation may take 10 to 30 seconds to install. The installation time may vary and take up to several minutes, depending on the network speed, computer performance, and virtual environment settings.
|
|
@@ -89,7 +88,7 @@ Create an account and login (local database)
|
|
|
89
88
|
[//]: # ()
|
|
90
89
|
|
|
91
90
|
|
|
92
|
-
### Additional settings
|
|
91
|
+
### Additional settings (not actively maintained)
|
|
93
92
|
#### AI assistant
|
|
94
93
|
To streamline the experimental design on SDLs, we also integrate Large Language Models (LLMs) to interpret the inspected functions and generate code according to task descriptions.
|
|
95
94
|
|
|
@@ -141,8 +140,6 @@ ivoryos.run(__name__)
|
|
|
141
140
|
### Deck function and web form
|
|
142
141
|

|
|
143
142
|
|
|
144
|
-
### Text-to-code demo
|
|
145
|
-

|
|
146
143
|
|
|
147
144
|
### Directory structure
|
|
148
145
|
|
|
@@ -28,7 +28,7 @@ With the least modification of the current workflow, user can design, manage and
|
|
|
28
28
|
This software is developed and tested using Windows. This software and its dependencies are compatible across major platforms: Linux, macOS, and Windows. Some dependencies (Flask-SQLAlchemy) may require additional setup.
|
|
29
29
|
|
|
30
30
|
### Python Version
|
|
31
|
-
Python >=3.
|
|
31
|
+
Python >=3.10 for best compatibility. Python >=3.7 without Ax.
|
|
32
32
|
### Python dependencies
|
|
33
33
|
This software is compatible with the latest versions of all dependencies.
|
|
34
34
|
- bcrypt~=4.0
|
|
@@ -39,8 +39,7 @@ This software is compatible with the latest versions of all dependencies.
|
|
|
39
39
|
- SQLAlchemy-Utils~=0.41
|
|
40
40
|
- Flask-WTF~=1.2
|
|
41
41
|
- python-dotenv==1.0.1
|
|
42
|
-
-
|
|
43
|
-
- ax-platform (optional ~=0.3 or ~=0.4 for Python>=3.9)
|
|
42
|
+
- ax-platform (optional ~=0.4 for Python>=3.9)
|
|
44
43
|
|
|
45
44
|
## Installation
|
|
46
45
|
```bash
|
|
@@ -50,7 +49,7 @@ or
|
|
|
50
49
|
```bash
|
|
51
50
|
git clone https://gitlab.com/heingroup/ivoryos.git
|
|
52
51
|
cd ivoryos
|
|
53
|
-
pip install
|
|
52
|
+
pip install .
|
|
54
53
|
```
|
|
55
54
|
|
|
56
55
|
The installation may take 10 to 30 seconds to install. The installation time may vary and take up to several minutes, depending on the network speed, computer performance, and virtual environment settings.
|
|
@@ -78,7 +77,7 @@ Create an account and login (local database)
|
|
|
78
77
|
[//]: # ()
|
|
79
78
|
|
|
80
79
|
|
|
81
|
-
### Additional settings
|
|
80
|
+
### Additional settings (not actively maintained)
|
|
82
81
|
#### AI assistant
|
|
83
82
|
To streamline the experimental design on SDLs, we also integrate Large Language Models (LLMs) to interpret the inspected functions and generate code according to task descriptions.
|
|
84
83
|
|
|
@@ -130,8 +129,6 @@ ivoryos.run(__name__)
|
|
|
130
129
|
### Deck function and web form
|
|
131
130
|

|
|
132
131
|
|
|
133
|
-
### Text-to-code demo
|
|
134
|
-

|
|
135
132
|
|
|
136
133
|
### Directory structure
|
|
137
134
|
|
|
@@ -3,17 +3,23 @@ import sys
|
|
|
3
3
|
from typing import Union
|
|
4
4
|
|
|
5
5
|
from flask import Flask, redirect, url_for, g, Blueprint
|
|
6
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
6
7
|
|
|
7
8
|
from ivoryos.config import Config, get_config
|
|
8
9
|
from ivoryos.routes.auth.auth import auth, login_manager
|
|
9
10
|
from ivoryos.routes.control.control import control
|
|
10
|
-
from ivoryos.routes.
|
|
11
|
-
from ivoryos.routes.
|
|
11
|
+
from ivoryos.routes.data.data import data
|
|
12
|
+
from ivoryos.routes.library.library import library
|
|
13
|
+
from ivoryos.routes.design.design import design
|
|
14
|
+
from ivoryos.routes.execute.execute import execute
|
|
15
|
+
from ivoryos.routes.api.api import api
|
|
16
|
+
from ivoryos.socket_handlers import socketio
|
|
12
17
|
from ivoryos.routes.main.main import main
|
|
13
18
|
# from ivoryos.routes.monitor.monitor import monitor
|
|
14
19
|
from ivoryos.utils import utils
|
|
15
20
|
from ivoryos.utils.db_models import db, User
|
|
16
21
|
from ivoryos.utils.global_config import GlobalConfig
|
|
22
|
+
from ivoryos.optimizer.registry import OPTIMIZER_REGISTRY
|
|
17
23
|
from ivoryos.utils.script_runner import ScriptRunner
|
|
18
24
|
from ivoryos.version import __version__ as ivoryos_version
|
|
19
25
|
from importlib.metadata import entry_points
|
|
@@ -35,10 +41,13 @@ def enforce_sqlite_foreign_keys(dbapi_connection, connection_record):
|
|
|
35
41
|
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
36
42
|
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
37
43
|
app.register_blueprint(main, url_prefix=url_prefix)
|
|
38
|
-
app.register_blueprint(auth, url_prefix=url_prefix)
|
|
39
|
-
app.register_blueprint(
|
|
40
|
-
app.register_blueprint(
|
|
41
|
-
app.register_blueprint(
|
|
44
|
+
app.register_blueprint(auth, url_prefix=f'{url_prefix}/{auth.name}')
|
|
45
|
+
app.register_blueprint(library, url_prefix=f'{url_prefix}/{library.name}')
|
|
46
|
+
app.register_blueprint(control, url_prefix=f'{url_prefix}/instruments')
|
|
47
|
+
app.register_blueprint(design, url_prefix=f'{url_prefix}')
|
|
48
|
+
app.register_blueprint(execute, url_prefix=f'{url_prefix}')
|
|
49
|
+
app.register_blueprint(data, url_prefix=f'{url_prefix}')
|
|
50
|
+
app.register_blueprint(api, url_prefix=f'{url_prefix}/{api.name}')
|
|
42
51
|
|
|
43
52
|
@login_manager.user_loader
|
|
44
53
|
def load_user(user_id):
|
|
@@ -86,6 +95,12 @@ def create_app(config_class=None):
|
|
|
86
95
|
def redirect_to_prefix():
|
|
87
96
|
return redirect(url_for('main.index', version=ivoryos_version)) # Assuming 'index' is a route in your blueprint
|
|
88
97
|
|
|
98
|
+
@app.template_filter('format_name')
|
|
99
|
+
def format_name(name):
|
|
100
|
+
name = name.split(".")[-1]
|
|
101
|
+
text = ' '.join(word for word in name.split('_'))
|
|
102
|
+
return text.capitalize()
|
|
103
|
+
|
|
89
104
|
return app
|
|
90
105
|
|
|
91
106
|
|
|
@@ -136,7 +151,7 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
136
151
|
app.config["LOGGERS_PATH"] = logger_output_name or app.config["LOGGERS_PATH"] # default.log
|
|
137
152
|
logger_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["LOGGERS_PATH"])
|
|
138
153
|
dummy_deck_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["DUMMY_DECK"])
|
|
139
|
-
|
|
154
|
+
global_config.optimizers = OPTIMIZER_REGISTRY
|
|
140
155
|
if module:
|
|
141
156
|
app.config["MODULE"] = module
|
|
142
157
|
app.config["OFF_LINE"] = False
|
|
@@ -195,6 +210,10 @@ def load_installed_plugins(app, socketio):
|
|
|
195
210
|
def load_plugins(blueprints: Union[list, Blueprint], app, socketio):
|
|
196
211
|
"""
|
|
197
212
|
Dynamically load installed plugins and attach Flask-SocketIO.
|
|
213
|
+
:param blueprints: Union[list, Blueprint] list of Blueprint objects or a single Blueprint object
|
|
214
|
+
:param app: Flask application instance
|
|
215
|
+
:param socketio: Flask-SocketIO instance
|
|
216
|
+
:return: list of plugin names
|
|
198
217
|
"""
|
|
199
218
|
plugin_names = []
|
|
200
219
|
if not isinstance(blueprints, list):
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from flask import Blueprint, jsonify, request, current_app
|
|
3
|
+
|
|
4
|
+
from ivoryos.routes.control.control import find_instrument_by_name
|
|
5
|
+
from ivoryos.utils.form import create_form_from_module
|
|
6
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
7
|
+
from ivoryos.utils.db_models import Script, WorkflowRun, SingleStep, WorkflowStep
|
|
8
|
+
|
|
9
|
+
from ivoryos.socket_handlers import abort_pending, abort_current, pause, retry, runner
|
|
10
|
+
from ivoryos.utils.task_runner import TaskRunner
|
|
11
|
+
|
|
12
|
+
api = Blueprint('api', __name__)
|
|
13
|
+
global_config = GlobalConfig()
|
|
14
|
+
task_runner = TaskRunner()
|
|
15
|
+
|
|
16
|
+
#TODO: add authentication and authorization to the API endpoints
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@api.route("/control/", strict_slashes=False, methods=['GET'])
|
|
20
|
+
@api.route("/control/<string:instrument>", methods=['POST'])
|
|
21
|
+
def backend_control(instrument: str=None):
|
|
22
|
+
"""
|
|
23
|
+
.. :quickref: Backend Control; backend control
|
|
24
|
+
|
|
25
|
+
backend control through http requests
|
|
26
|
+
|
|
27
|
+
.. http:get:: /api/control/
|
|
28
|
+
|
|
29
|
+
:param instrument: instrument name
|
|
30
|
+
:type instrument: str
|
|
31
|
+
|
|
32
|
+
.. http:post:: /api/control/
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
if instrument:
|
|
36
|
+
inst_object = find_instrument_by_name(instrument)
|
|
37
|
+
forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
|
|
38
|
+
|
|
39
|
+
if request.method == 'POST':
|
|
40
|
+
method_name = request.form.get("hidden_name", None)
|
|
41
|
+
form = forms.get(method_name, None)
|
|
42
|
+
if form:
|
|
43
|
+
kwargs = {field.name: field.data for field in form if field.name not in ['csrf_token', 'hidden_name']}
|
|
44
|
+
wait = request.form.get("hidden_wait", "true") == "true"
|
|
45
|
+
output = task_runner.run_single_step(component=instrument, method=method_name, kwargs=kwargs, wait=wait,
|
|
46
|
+
current_app=current_app._get_current_object())
|
|
47
|
+
return jsonify(output), 200
|
|
48
|
+
|
|
49
|
+
snapshot = global_config.deck_snapshot.copy()
|
|
50
|
+
# Iterate through each instrument in the snapshot
|
|
51
|
+
for instrument_key, instrument_data in snapshot.items():
|
|
52
|
+
# Iterate through each function associated with the current instrument
|
|
53
|
+
for function_key, function_data in instrument_data.items():
|
|
54
|
+
# Convert the function signature to a string representation
|
|
55
|
+
function_data['signature'] = str(function_data['signature'])
|
|
56
|
+
return jsonify(snapshot), 200
|
|
@@ -6,10 +6,10 @@ from ivoryos.utils.db_models import Script, User, db
|
|
|
6
6
|
from ivoryos.utils.utils import post_script_file
|
|
7
7
|
login_manager = LoginManager()
|
|
8
8
|
|
|
9
|
-
auth = Blueprint('auth', __name__, template_folder='templates
|
|
9
|
+
auth = Blueprint('auth', __name__, template_folder='templates')
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@auth.route('/
|
|
12
|
+
@auth.route('/login', methods=['GET', 'POST'])
|
|
13
13
|
def login():
|
|
14
14
|
"""
|
|
15
15
|
.. :quickref: User; login user
|
|
@@ -46,11 +46,11 @@ def login():
|
|
|
46
46
|
return redirect(url_for('main.index'))
|
|
47
47
|
else:
|
|
48
48
|
flash("Incorrect username or password")
|
|
49
|
-
return redirect(url_for('auth.login'))
|
|
49
|
+
return redirect(url_for('auth.login'))
|
|
50
50
|
return render_template('login.html')
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
@auth.route('/
|
|
53
|
+
@auth.route('/signup', methods=['GET', 'POST'])
|
|
54
54
|
def signup():
|
|
55
55
|
"""
|
|
56
56
|
.. :quickref: User; signup for a new account
|
|
@@ -84,7 +84,7 @@ def signup():
|
|
|
84
84
|
return render_template('signup.html')
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
@auth.route("/
|
|
87
|
+
@auth.route("/logout")
|
|
88
88
|
@login_required
|
|
89
89
|
def logout():
|
|
90
90
|
"""
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from flask import Blueprint, redirect, flash, request, render_template, session, current_app, jsonify
|
|
2
|
+
from flask_login import login_required
|
|
3
|
+
|
|
4
|
+
from ivoryos.routes.control.control_file import control_file
|
|
5
|
+
from ivoryos.routes.control.control_new_device import control_temp
|
|
6
|
+
from ivoryos.routes.control.utils import post_session_by_instrument, get_session_by_instrument, find_instrument_by_name
|
|
7
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
8
|
+
from ivoryos.utils.form import create_form_from_module
|
|
9
|
+
from ivoryos.utils.task_runner import TaskRunner
|
|
10
|
+
|
|
11
|
+
global_config = GlobalConfig()
|
|
12
|
+
runner = TaskRunner()
|
|
13
|
+
|
|
14
|
+
control = Blueprint('control', __name__, template_folder='templates')
|
|
15
|
+
|
|
16
|
+
control.register_blueprint(control_file)
|
|
17
|
+
control.register_blueprint(control_temp)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@control.route("/", strict_slashes=False, methods=["GET", "POST"])
|
|
22
|
+
@control.route("/<string:instrument>", strict_slashes=False, methods=["GET", "POST"])
|
|
23
|
+
@login_required
|
|
24
|
+
def deck_controllers():
|
|
25
|
+
"""
|
|
26
|
+
.. :quickref: Direct Control; device (instruments) and methods
|
|
27
|
+
|
|
28
|
+
device home interface for listing all instruments and methods, selecting an instrument to run its methods
|
|
29
|
+
|
|
30
|
+
.. http:get:: /instruments
|
|
31
|
+
|
|
32
|
+
get all instruments for home page
|
|
33
|
+
|
|
34
|
+
.. http:get:: /instruments/<string:instrument>
|
|
35
|
+
|
|
36
|
+
get all methods of the given <instrument>
|
|
37
|
+
|
|
38
|
+
.. http:post:: /instruments/<string:instrument>
|
|
39
|
+
|
|
40
|
+
send POST request to run a method of the given <instrument>
|
|
41
|
+
|
|
42
|
+
:param instrument: instrument name, if not provided, list all instruments
|
|
43
|
+
:type instrument: str
|
|
44
|
+
:status 200: render template with instruments and methods
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
deck_variables = global_config.deck_snapshot.keys()
|
|
48
|
+
temp_variables = global_config.defined_variables.keys()
|
|
49
|
+
instrument = request.args.get('instrument')
|
|
50
|
+
forms = None
|
|
51
|
+
if instrument:
|
|
52
|
+
inst_object = find_instrument_by_name(instrument)
|
|
53
|
+
_forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
|
|
54
|
+
order = get_session_by_instrument('card_order', instrument)
|
|
55
|
+
hidden_functions = get_session_by_instrument('hidden_functions', instrument)
|
|
56
|
+
functions = list(_forms.keys())
|
|
57
|
+
for function in functions:
|
|
58
|
+
if function not in hidden_functions and function not in order:
|
|
59
|
+
order.append(function)
|
|
60
|
+
post_session_by_instrument('card_order', instrument, order)
|
|
61
|
+
forms = {name: _forms[name] for name in order if name in _forms}
|
|
62
|
+
# Handle POST for method execution
|
|
63
|
+
if request.method == 'POST':
|
|
64
|
+
all_kwargs = request.form.copy()
|
|
65
|
+
method_name = all_kwargs.pop("hidden_name", None)
|
|
66
|
+
form = forms.get(method_name)
|
|
67
|
+
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'} if form else {}
|
|
68
|
+
if form and form.validate_on_submit():
|
|
69
|
+
kwargs.pop("hidden_name", None)
|
|
70
|
+
output = runner.run_single_step(instrument, method_name, kwargs, wait=True, current_app=current_app._get_current_object())
|
|
71
|
+
if output["success"]:
|
|
72
|
+
flash(f"\nRun Success! Output value: {output.get('output', 'None')}.")
|
|
73
|
+
else:
|
|
74
|
+
flash(f"\nRun Error! {output.get('output', 'Unknown error occurred.')}", "error")
|
|
75
|
+
else:
|
|
76
|
+
if form:
|
|
77
|
+
flash(form.errors)
|
|
78
|
+
else:
|
|
79
|
+
flash("Invalid method selected.")
|
|
80
|
+
return render_template(
|
|
81
|
+
'controllers.html',
|
|
82
|
+
defined_variables=deck_variables,
|
|
83
|
+
temp_variables=temp_variables,
|
|
84
|
+
instrument=instrument,
|
|
85
|
+
forms=forms,
|
|
86
|
+
session=session
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@control.route('/<string:instrument>/actions/order', methods=['POST'])
|
|
90
|
+
def save_order(instrument: str):
|
|
91
|
+
"""
|
|
92
|
+
.. :quickref: Control Customization; Save functions' order
|
|
93
|
+
|
|
94
|
+
.. http:post:: instruments/<string:instrument>/actions/order
|
|
95
|
+
|
|
96
|
+
save function drag and drop order for the given <instrument>
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
# Save the new order for the specified group to session
|
|
100
|
+
data = request.json
|
|
101
|
+
post_session_by_instrument('card_order', instrument, data['order'])
|
|
102
|
+
return '', 204
|
|
103
|
+
|
|
104
|
+
@control.route('/<string:instrument>/actions/<string:function>', methods=["PATCH"])
|
|
105
|
+
def hide_function(instrument: str, function: str):
|
|
106
|
+
"""
|
|
107
|
+
.. :quickref: Control Customization; Toggle function visibility
|
|
108
|
+
|
|
109
|
+
.. http:patch:: /instruments/<instrument>/actions/<function>
|
|
110
|
+
|
|
111
|
+
Toggle visibility for the given <instrument> and <function>
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
back = request.referrer
|
|
115
|
+
data = request.get_json()
|
|
116
|
+
hidden = data.get('hidden', True)
|
|
117
|
+
functions = get_session_by_instrument("hidden_functions", instrument)
|
|
118
|
+
order = get_session_by_instrument("card_order", instrument)
|
|
119
|
+
if hidden and function not in functions:
|
|
120
|
+
functions.append(function)
|
|
121
|
+
if function in order:
|
|
122
|
+
order.remove(function)
|
|
123
|
+
elif not hidden and function in functions:
|
|
124
|
+
functions.remove(function)
|
|
125
|
+
if function not in order:
|
|
126
|
+
order.append(function)
|
|
127
|
+
post_session_by_instrument('hidden_functions', instrument, functions)
|
|
128
|
+
post_session_by_instrument('card_order', instrument, order)
|
|
129
|
+
return jsonify(success=True, message="Visibility updated")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
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 export_to_python, create_function
|
|
6
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
7
|
+
|
|
8
|
+
global_config = GlobalConfig()
|
|
9
|
+
|
|
10
|
+
control_file = Blueprint('file', __name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@control_file.route("/files/proxy", strict_slashes=False)
|
|
14
|
+
@login_required
|
|
15
|
+
def download_proxy():
|
|
16
|
+
"""
|
|
17
|
+
.. :quickref: Direct Control Files; download proxy interface
|
|
18
|
+
|
|
19
|
+
download proxy Python interface
|
|
20
|
+
|
|
21
|
+
.. http:get:: /files/proxy
|
|
22
|
+
"""
|
|
23
|
+
snapshot = global_config.deck_snapshot.copy()
|
|
24
|
+
class_definitions = {}
|
|
25
|
+
# Iterate through each instrument in the snapshot
|
|
26
|
+
for instrument_key, instrument_data in snapshot.items():
|
|
27
|
+
# Iterate through each function associated with the current instrument
|
|
28
|
+
for function_key, function_data in instrument_data.items():
|
|
29
|
+
# Convert the function signature to a string representation
|
|
30
|
+
function_data['signature'] = str(function_data['signature'])
|
|
31
|
+
class_name = instrument_key.split('.')[-1] # Extracting the class name from the path
|
|
32
|
+
class_definitions[class_name.capitalize()] = create_function(request.url_root, class_name, instrument_data)
|
|
33
|
+
# Export the generated class definitions to a .py script
|
|
34
|
+
export_to_python(class_definitions, current_app.config["OUTPUT_FOLDER"])
|
|
35
|
+
filepath = os.path.join(current_app.config["OUTPUT_FOLDER"], "generated_proxy.py")
|
|
36
|
+
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from flask import Blueprint, request, current_app, send_file, flash, redirect, url_for, session, render_template
|
|
3
|
+
from flask_login import login_required
|
|
4
|
+
|
|
5
|
+
from ivoryos.utils import utils
|
|
6
|
+
from ivoryos.routes.control.utils import find_instrument_by_name
|
|
7
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
8
|
+
|
|
9
|
+
global_config = GlobalConfig()
|
|
10
|
+
|
|
11
|
+
control_temp = Blueprint('temp', __name__)
|
|
12
|
+
|
|
13
|
+
@control_temp.route("/new/module", methods=['POST'])
|
|
14
|
+
def import_api():
|
|
15
|
+
"""
|
|
16
|
+
.. :quickref: Advanced Features; Manually import API module(s)
|
|
17
|
+
|
|
18
|
+
importing other Python modules
|
|
19
|
+
|
|
20
|
+
.. http:post:: /instruments/new/module
|
|
21
|
+
|
|
22
|
+
:form filepath: API (Python class) module filepath
|
|
23
|
+
|
|
24
|
+
import the module and redirect to :http:get:`/ivoryos/instruments/new/`
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
filepath = request.form.get('filepath')
|
|
28
|
+
# filepath.replace('\\', '/')
|
|
29
|
+
name = os.path.split(filepath)[-1].split('.')[0]
|
|
30
|
+
try:
|
|
31
|
+
spec = utils.importlib.util.spec_from_file_location(name, filepath)
|
|
32
|
+
module = utils.importlib.util.module_from_spec(spec)
|
|
33
|
+
spec.loader.exec_module(module)
|
|
34
|
+
classes = utils.inspect.getmembers(module, utils.inspect.isclass)
|
|
35
|
+
if len(classes) == 0:
|
|
36
|
+
flash("Invalid import: no class found in the path")
|
|
37
|
+
return redirect(url_for("control.controllers_home"))
|
|
38
|
+
for i in classes:
|
|
39
|
+
globals()[i[0]] = i[1]
|
|
40
|
+
global_config.api_variables.add(i[0])
|
|
41
|
+
# should handle path error and file type error
|
|
42
|
+
except Exception as e:
|
|
43
|
+
flash(e.__str__())
|
|
44
|
+
return redirect(url_for("control.temp.new_controller"))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@control_temp.route("/new/deck-python", methods=['POST'])
|
|
49
|
+
def import_deck():
|
|
50
|
+
"""
|
|
51
|
+
.. :quickref: Advanced Features; Manually import a deck
|
|
52
|
+
|
|
53
|
+
.. http:post:: /instruments/new/deck-python
|
|
54
|
+
|
|
55
|
+
:form filepath: deck module filepath
|
|
56
|
+
|
|
57
|
+
import the module and redirect to the previous page
|
|
58
|
+
|
|
59
|
+
"""
|
|
60
|
+
script = utils.get_script_file()
|
|
61
|
+
filepath = request.form.get('filepath')
|
|
62
|
+
session['dismiss'] = request.form.get('dismiss')
|
|
63
|
+
update = request.form.get('update')
|
|
64
|
+
back = request.referrer
|
|
65
|
+
if session['dismiss']:
|
|
66
|
+
return redirect(back)
|
|
67
|
+
name = os.path.split(filepath)[-1].split('.')[0]
|
|
68
|
+
try:
|
|
69
|
+
module = utils.import_module_by_filepath(filepath=filepath, name=name)
|
|
70
|
+
utils.save_to_history(filepath, current_app.config["DECK_HISTORY"])
|
|
71
|
+
module_sigs = utils.create_deck_snapshot(module, save=update, output_path=current_app.config["DUMMY_DECK"])
|
|
72
|
+
if not len(module_sigs) > 0:
|
|
73
|
+
flash("Invalid hardware deck, connect instruments in deck script", "error")
|
|
74
|
+
return redirect(url_for("control.deck_controllers"))
|
|
75
|
+
global_config.deck = module
|
|
76
|
+
global_config.deck_snapshot = module_sigs
|
|
77
|
+
|
|
78
|
+
if script.deck is None:
|
|
79
|
+
script.deck = module.__name__
|
|
80
|
+
# file path error exception
|
|
81
|
+
except Exception as e:
|
|
82
|
+
flash(e.__str__())
|
|
83
|
+
return redirect(back)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@control_temp.route("/new/", strict_slashes=False)
|
|
87
|
+
@control_temp.route("/new/<string:instrument>", methods=['GET', 'POST'])
|
|
88
|
+
@login_required
|
|
89
|
+
def new_controller(instrument:str=None):
|
|
90
|
+
"""
|
|
91
|
+
.. :quickref: Advanced Features; connect to a new device
|
|
92
|
+
|
|
93
|
+
interface for connecting a new <instrument>
|
|
94
|
+
|
|
95
|
+
.. http:get:: /instruments/new/
|
|
96
|
+
|
|
97
|
+
:param instrument: instrument name
|
|
98
|
+
:type instrument: str
|
|
99
|
+
|
|
100
|
+
.. http:post:: /instruments/new/
|
|
101
|
+
|
|
102
|
+
:form device_name: module instance name (e.g. my_instance = MyClass())
|
|
103
|
+
:form kwargs: dynamic module initialization kwargs fields
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
device = None
|
|
107
|
+
args = None
|
|
108
|
+
if instrument:
|
|
109
|
+
|
|
110
|
+
device = globals()[instrument]
|
|
111
|
+
args = utils.inspect.signature(device.__init__)
|
|
112
|
+
|
|
113
|
+
if request.method == 'POST':
|
|
114
|
+
device_name = request.form.get("device_name", "")
|
|
115
|
+
if device_name and device_name in globals():
|
|
116
|
+
flash("Device name is defined. Try another name, or leave it as blank to auto-configure")
|
|
117
|
+
# return render_template('controllers_new.html', instrument=instrument,
|
|
118
|
+
# api_variables=global_config.api_variables,
|
|
119
|
+
# device=device, args=args, defined_variables=global_config.defined_variables)
|
|
120
|
+
if device_name == "":
|
|
121
|
+
device_name = device.__name__.lower() + "_"
|
|
122
|
+
num = 1
|
|
123
|
+
while device_name + str(num) in global_config.defined_variables:
|
|
124
|
+
num += 1
|
|
125
|
+
device_name = device_name + str(num)
|
|
126
|
+
kwargs = request.form.to_dict()
|
|
127
|
+
kwargs.pop("device_name")
|
|
128
|
+
for i in kwargs:
|
|
129
|
+
if kwargs[i] in global_config.defined_variables:
|
|
130
|
+
kwargs[i] = global_config.defined_variables[kwargs[i]]
|
|
131
|
+
try:
|
|
132
|
+
utils.convert_config_type(kwargs, device.__init__.__annotations__, is_class=True)
|
|
133
|
+
except Exception as e:
|
|
134
|
+
flash(e)
|
|
135
|
+
try:
|
|
136
|
+
global_config.defined_variables[device_name] = device(**kwargs)
|
|
137
|
+
# global_config.defined_variables.add(device_name)
|
|
138
|
+
return redirect(url_for('control.deck_controllers'))
|
|
139
|
+
except Exception as e:
|
|
140
|
+
flash(e)
|
|
141
|
+
return render_template('controllers_new.html', instrument=instrument, api_variables=global_config.api_variables,
|
|
142
|
+
device=device, args=args, defined_variables=global_config.defined_variables)
|