ivoryos 1.0.9__tar.gz → 1.2.0b1__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.

Files changed (124) hide show
  1. {ivoryos-1.0.9/ivoryos.egg-info → ivoryos-1.2.0b1}/PKG-INFO +5 -8
  2. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/README.md +4 -7
  3. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/__init__.py +26 -7
  4. ivoryos-1.2.0b1/ivoryos/routes/api/api.py +56 -0
  5. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/routes/auth/auth.py +5 -5
  6. ivoryos-1.2.0b1/ivoryos/routes/control/control.py +134 -0
  7. ivoryos-1.2.0b1/ivoryos/routes/control/control_file.py +36 -0
  8. ivoryos-1.2.0b1/ivoryos/routes/control/control_new_device.py +142 -0
  9. ivoryos-1.2.0b1/ivoryos/routes/control/templates/controllers.html +166 -0
  10. ivoryos-1.2.0b1/ivoryos/routes/control/templates/controllers_new.html +112 -0
  11. ivoryos-1.2.0b1/ivoryos/routes/control/utils.py +38 -0
  12. ivoryos-1.2.0b1/ivoryos/routes/data/data.py +129 -0
  13. ivoryos-1.2.0b1/ivoryos/routes/data/templates/components/step_card.html +13 -0
  14. {ivoryos-1.0.9/ivoryos/routes/database/templates/database → ivoryos-1.2.0b1/ivoryos/routes/data/templates}/workflow_database.html +14 -8
  15. {ivoryos-1.0.9/ivoryos/routes/database/templates/database → ivoryos-1.2.0b1/ivoryos/routes/data/templates}/workflow_view.html +3 -3
  16. ivoryos-1.2.0b1/ivoryos/routes/design/__init__.py +4 -0
  17. ivoryos-1.2.0b1/ivoryos/routes/design/design.py +428 -0
  18. ivoryos-1.2.0b1/ivoryos/routes/design/design_file.py +68 -0
  19. ivoryos-1.2.0b1/ivoryos/routes/design/design_step.py +145 -0
  20. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/action_form.html +53 -0
  21. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/actions_panel.html +25 -0
  22. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
  23. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/canvas.html +5 -0
  24. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
  25. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/canvas_header.html +75 -0
  26. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/canvas_main.html +34 -0
  27. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/deck_selector.html +10 -0
  28. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/edit_action_form.html +38 -0
  29. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
  30. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
  31. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
  32. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
  33. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
  34. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
  35. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/modals.html +6 -0
  36. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/python_code_overlay.html +39 -0
  37. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/sidebar.html +15 -0
  38. ivoryos-1.2.0b1/ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
  39. ivoryos-1.2.0b1/ivoryos/routes/design/templates/experiment_builder.html +41 -0
  40. ivoryos-1.2.0b1/ivoryos/routes/execute/execute.py +317 -0
  41. ivoryos-1.2.0b1/ivoryos/routes/execute/execute_file.py +78 -0
  42. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/error_modal.html +20 -0
  43. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
  44. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
  45. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/run_panel.html +9 -0
  46. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
  47. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/tab_bayesian.html +399 -0
  48. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
  49. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
  50. ivoryos-1.2.0b1/ivoryos/routes/execute/templates/experiment_run.html +294 -0
  51. ivoryos-1.2.0b1/ivoryos/routes/library/library.py +159 -0
  52. ivoryos-1.0.9/ivoryos/routes/database/templates/database/scripts_database.html → ivoryos-1.2.0b1/ivoryos/routes/library/templates/library.html +30 -22
  53. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/routes/main/main.py +1 -1
  54. {ivoryos-1.0.9/ivoryos/routes/main/templates/main → ivoryos-1.2.0b1/ivoryos/routes/main/templates}/home.html +4 -4
  55. ivoryos-1.2.0b1/ivoryos/socket_handlers.py +52 -0
  56. ivoryos-1.2.0b1/ivoryos/static/js/action_handlers.js +213 -0
  57. ivoryos-1.2.0b1/ivoryos/static/js/db_delete.js +23 -0
  58. ivoryos-1.2.0b1/ivoryos/static/js/script_metadata.js +39 -0
  59. ivoryos-1.2.0b1/ivoryos/static/js/sortable_design.js +138 -0
  60. ivoryos-1.2.0b1/ivoryos/static/js/ui_state.js +113 -0
  61. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/templates/base.html +4 -4
  62. ivoryos-1.2.0b1/ivoryos/utils/bo_campaign.py +263 -0
  63. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/db_models.py +14 -5
  64. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/form.py +5 -9
  65. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/global_config.py +13 -1
  66. ivoryos-1.2.0b1/ivoryos/utils/py_to_json.py +225 -0
  67. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/script_runner.py +49 -7
  68. ivoryos-1.2.0b1/ivoryos/utils/serilize.py +203 -0
  69. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/task_runner.py +4 -1
  70. ivoryos-1.2.0b1/ivoryos/version.py +1 -0
  71. {ivoryos-1.0.9 → ivoryos-1.2.0b1/ivoryos.egg-info}/PKG-INFO +5 -8
  72. ivoryos-1.2.0b1/ivoryos.egg-info/SOURCES.txt +109 -0
  73. ivoryos-1.2.0b1/tests/integration/__init__.py +0 -0
  74. ivoryos-1.0.9/ivoryos/routes/control/control.py +0 -429
  75. ivoryos-1.0.9/ivoryos/routes/control/templates/control/controllers.html +0 -78
  76. ivoryos-1.0.9/ivoryos/routes/control/templates/control/controllers_home.html +0 -55
  77. ivoryos-1.0.9/ivoryos/routes/control/templates/control/controllers_new.html +0 -89
  78. ivoryos-1.0.9/ivoryos/routes/database/database.py +0 -306
  79. ivoryos-1.0.9/ivoryos/routes/database/templates/database/step_card.html +0 -7
  80. ivoryos-1.0.9/ivoryos/routes/design/design.py +0 -786
  81. ivoryos-1.0.9/ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
  82. ivoryos-1.0.9/ivoryos/routes/design/templates/design/experiment_run.html +0 -558
  83. ivoryos-1.0.9/ivoryos/static/js/sortable_design.js +0 -105
  84. ivoryos-1.0.9/ivoryos/utils/bo_campaign.py +0 -87
  85. ivoryos-1.0.9/ivoryos/version.py +0 -1
  86. ivoryos-1.0.9/ivoryos.egg-info/SOURCES.txt +0 -65
  87. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/LICENSE +0 -0
  88. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/MANIFEST.in +0 -0
  89. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/config.py +0 -0
  90. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/routes/__init__.py +0 -0
  91. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/routes/auth/__init__.py +0 -0
  92. {ivoryos-1.0.9/ivoryos/routes/auth/templates/auth → ivoryos-1.2.0b1/ivoryos/routes/auth/templates}/login.html +0 -0
  93. {ivoryos-1.0.9/ivoryos/routes/auth/templates/auth → ivoryos-1.2.0b1/ivoryos/routes/auth/templates}/signup.html +0 -0
  94. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/routes/control/__init__.py +0 -0
  95. {ivoryos-1.0.9/ivoryos/routes/database → ivoryos-1.2.0b1/ivoryos/routes/data}/__init__.py +0 -0
  96. {ivoryos-1.0.9/ivoryos/routes/design → ivoryos-1.2.0b1/ivoryos/routes/execute}/__init__.py +0 -0
  97. {ivoryos-1.0.9/ivoryos/routes/main → ivoryos-1.2.0b1/ivoryos/routes/library}/__init__.py +0 -0
  98. {ivoryos-1.0.9/ivoryos/utils → ivoryos-1.2.0b1/ivoryos/routes/main}/__init__.py +0 -0
  99. {ivoryos-1.0.9/ivoryos/routes/main/templates/main → ivoryos-1.2.0b1/ivoryos/routes/main/templates}/help.html +0 -0
  100. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/favicon.ico +0 -0
  101. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/gui_annotation/Slide1.png +0 -0
  102. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
  103. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/js/overlay.js +0 -0
  104. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/js/socket_handler.js +0 -0
  105. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/js/sortable_card.js +0 -0
  106. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/logo.webp +0 -0
  107. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/static/style.css +0 -0
  108. {ivoryos-1.0.9/tests → ivoryos-1.2.0b1/ivoryos/utils}/__init__.py +0 -0
  109. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/client_proxy.py +0 -0
  110. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/llm_agent.py +0 -0
  111. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos/utils/utils.py +0 -0
  112. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos.egg-info/dependency_links.txt +0 -0
  113. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos.egg-info/requires.txt +0 -0
  114. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/ivoryos.egg-info/top_level.txt +0 -0
  115. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/setup.cfg +0 -0
  116. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/setup.py +0 -0
  117. {ivoryos-1.0.9/tests/integration → ivoryos-1.2.0b1/tests}/__init__.py +0 -0
  118. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/tests/conftest.py +0 -0
  119. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/tests/integration/test_route_auth.py +0 -0
  120. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/tests/integration/test_route_control.py +0 -0
  121. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/tests/integration/test_route_database.py +0 -0
  122. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/tests/integration/test_route_design.py +0 -0
  123. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/tests/integration/test_route_main.py +0 -0
  124. {ivoryos-1.0.9 → ivoryos-1.2.0b1}/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.9
3
+ Version: 1.2.0b1
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.7 for best compatibility.
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
- - openai (optional ~=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 -e .
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
  [//]: # (![PyPI - Downloads](https://img.shields.io/pypi/dm/ivoryos))
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
  ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/source/_static/demo.gif)
143
142
 
144
- ### Text-to-code demo
145
- ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/source/_static/text-to-code.gif)
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.7 for best compatibility.
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
- - openai (optional ~=1.53)
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 -e .
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
  [//]: # (![PyPI - Downloads](https://img.shields.io/pypi/dm/ivoryos))
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
  ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/source/_static/demo.gif)
132
131
 
133
- ### Text-to-code demo
134
- ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/source/_static/text-to-code.gif)
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.database.database import database
11
- from ivoryos.routes.design.design import design, socketio
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(control, url_prefix=url_prefix)
40
- app.register_blueprint(design, url_prefix=url_prefix)
41
- app.register_blueprint(database, url_prefix=url_prefix)
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/auth')
9
+ auth = Blueprint('auth', __name__, template_folder='templates')
10
10
 
11
11
 
12
- @auth.route('/auth/login', methods=['GET', 'POST'])
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')), 401
49
+ return redirect(url_for('auth.login'))
50
50
  return render_template('login.html')
51
51
 
52
52
 
53
- @auth.route('/auth/signup', methods=['GET', 'POST'])
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("/auth/logout")
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)