ivoryos 0.1.5__tar.gz → 0.1.7__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 (56) hide show
  1. ivoryos-0.1.7/PKG-INFO +166 -0
  2. ivoryos-0.1.7/README.md +147 -0
  3. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/__init__.py +13 -8
  4. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/config.py +1 -0
  5. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/control/control.py +70 -31
  6. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/control/templates/control/controllers.html +6 -2
  7. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/database/database.py +3 -2
  8. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/design/design.py +4 -4
  9. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/main/templates/main/help.html +0 -3
  10. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/js/socket_handler.js +10 -1
  11. ivoryos-0.1.7/ivoryos/static/logo.webp +0 -0
  12. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/templates/base.html +4 -1
  13. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/utils/db_models.py +7 -8
  14. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/utils/form.py +2 -3
  15. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/utils/global_config.py +6 -6
  16. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/utils/llm_agent.py +1 -1
  17. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/utils/script_runner.py +9 -3
  18. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/utils/utils.py +131 -46
  19. ivoryos-0.1.7/ivoryos.egg-info/PKG-INFO +166 -0
  20. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos.egg-info/SOURCES.txt +1 -2
  21. {ivoryos-0.1.5 → ivoryos-0.1.7}/setup.py +1 -1
  22. ivoryos-0.1.5/PKG-INFO +0 -96
  23. ivoryos-0.1.5/README.md +0 -77
  24. ivoryos-0.1.5/ivoryos/static/logo.png +0 -0
  25. ivoryos-0.1.5/ivoryos/utils/task_manager.py +0 -80
  26. ivoryos-0.1.5/ivoryos.egg-info/PKG-INFO +0 -96
  27. {ivoryos-0.1.5 → ivoryos-0.1.7}/LICENSE +0 -0
  28. {ivoryos-0.1.5 → ivoryos-0.1.7}/MANIFEST.in +0 -0
  29. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/__init__.py +0 -0
  30. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/auth/__init__.py +0 -0
  31. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/auth/auth.py +0 -0
  32. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/auth/templates/auth/login.html +0 -0
  33. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/auth/templates/auth/signup.html +0 -0
  34. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/control/__init__.py +0 -0
  35. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/control/templates/control/controllers_home.html +0 -0
  36. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/control/templates/control/controllers_new.html +0 -0
  37. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/database/__init__.py +0 -0
  38. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/database/templates/database/experiment_database.html +0 -0
  39. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/design/__init__.py +0 -0
  40. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/design/templates/design/experiment_builder.html +0 -0
  41. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/design/templates/design/experiment_run.html +0 -0
  42. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/main/__init__.py +0 -0
  43. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/main/main.py +0 -0
  44. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/routes/main/templates/main/home.html +0 -0
  45. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/favicon.ico +0 -0
  46. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/gui_annotation/Slide1.png +0 -0
  47. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
  48. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/js/overlay.js +0 -0
  49. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/js/sortable_card.js +0 -0
  50. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/js/sortable_design.js +0 -0
  51. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/static/style.css +0 -0
  52. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos/utils/__init__.py +0 -0
  53. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos.egg-info/dependency_links.txt +0 -0
  54. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos.egg-info/requires.txt +0 -0
  55. {ivoryos-0.1.5 → ivoryos-0.1.7}/ivoryos.egg-info/top_level.txt +0 -0
  56. {ivoryos-0.1.5 → ivoryos-0.1.7}/setup.cfg +0 -0
ivoryos-0.1.7/PKG-INFO ADDED
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.1
2
+ Name: ivoryos
3
+ Version: 0.1.7
4
+ Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
5
+ Home-page: https://gitlab.com/heingroup/ivoryos
6
+ Author: Ivory Zhang
7
+ Author-email: ivoryzhang@chem.ubc.ca
8
+ License: MIT
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: bcrypt
12
+ Requires-Dist: Flask-Login
13
+ Requires-Dist: Flask-Session
14
+ Requires-Dist: Flask-SocketIO
15
+ Requires-Dist: Flask-SQLAlchemy
16
+ Requires-Dist: Flask-WTF
17
+ Requires-Dist: SQLAlchemy-Utils
18
+ Requires-Dist: python-dotenv
19
+
20
+ ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/ivoryos.png)
21
+ # ivoryOS: interoperable Web UI for self-driving laboratories (SDLs)
22
+ "plug and play" web UI extension for flexible SDLs.
23
+
24
+ ## Table of Contents
25
+ - [Description](#description)
26
+ - [System requirements](#system-requirements)
27
+ - [Installation](#installation)
28
+ - [Instructions for use](#instructions-for-use)
29
+ - [Demo](#demo)
30
+ - [License](#license)
31
+
32
+ ## Description
33
+ Granting SDLs flexibility and modularity makes it almost impossible to design a UI, yet it's a necessity for allowing more people to interact with it (democratisation).
34
+ This web UI aims to ease up the control of any Python-based SDLs by displaying functions and parameters for initialized modules dynamically.
35
+ The modules can be hardware API, high-level functions, or experiment workflow.
36
+ With the least modification of the current workflow, user can design, manage and execute their experimental designs and monitor the execution process.
37
+
38
+ ## System requirements
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
+
41
+ ### Python Version
42
+ Python >=3.7 for best compatibility.
43
+ ### Python dependencies
44
+ This software is compatible with the latest versions of all dependencies.
45
+ - bcrypt~=4.0
46
+ - Flask-Login~=0.6
47
+ - Flask-Session~=0.8
48
+ - Flask-SocketIO~=5.3
49
+ - Flask-SQLAlchemy~=3.1
50
+ - SQLAlchemy-Utils~=0.41
51
+ - Flask-WTF~=1.2
52
+ - python-dotenv==1.0.1
53
+ - openai (optional ~=1.53)
54
+ - ax-platform (optional ~=0.3 or ~=0.4 for Python>=3.9)
55
+
56
+ ## Installation
57
+ ```bash
58
+ pip install ivoryos
59
+ ```
60
+ or
61
+ ```bash
62
+ git clone https://gitlab.com/heingroup/ivoryos.git
63
+ cd ivoryos
64
+ pip install -e .
65
+ ```
66
+
67
+ 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.
68
+
69
+ ## Instructions for use
70
+ ### Quick start
71
+ In your SDL script, use `ivoryos(__name__)`.
72
+ ```python
73
+ import ivoryos
74
+
75
+ ivoryos.run(__name__)
76
+ ```
77
+ ### Login
78
+ Create an account and login (local database)
79
+ ### Features
80
+ - **Direct control**: direct function calling _Device_ tab
81
+ - **Workflow design and iteration**:
82
+ - **Design**: add function to canvas in _Design_ tab. click `Compile and Run` button to go to the execution page
83
+ - **Execution**: configure iteration methods and parameters in _Compile/Run_ tab.
84
+ - **Database**: manage workflows in _Library_ tab.
85
+ - **Info page**: additional info in _About_ tab.
86
+
87
+
88
+ ### Additional settings
89
+ #### AI assistant
90
+ 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.
91
+
92
+ #### Enable LLMs with [OpenAI API](https://github.com/openai/openai-python)
93
+ 1. Create a `.env` file for `OPENAI_API_KEY`
94
+ ```
95
+ OPENAI_API_KEY="Your API Key"
96
+ ```
97
+ 2. In your SDL script, define model, you can use any GPT models.
98
+
99
+ ```python
100
+ ivoryos.run(__name__, model="gpt-3.5-turbo")
101
+ ```
102
+
103
+ #### Enable local LLMs with [Ollama](https://ollama.com/)
104
+ 1. Download Ollama.
105
+ 2. pull models from Ollama
106
+ 3. In your SDL script, define LLM server and model, you can use any models available on Ollama.
107
+
108
+ ```python
109
+ ivoryos.run(__name__, llm_server="localhost", model="llama3.1")
110
+ ```
111
+
112
+ #### Add additional logger(s)
113
+ ```python
114
+ ivoryos.run(__name__, logger="logger name")
115
+ ```
116
+ or
117
+ ```python
118
+ ivoryos.run(__name__, logger=["logger 1", "logger 2"])
119
+ ```
120
+ #### Offline (design without hardware connection)
121
+ After one successful connection, a blueprint will be automatically saved and made accessible without hardware connection. In a new Python script in the same directory, use `ivoryos.run()` to start offline mode.
122
+
123
+ ```python
124
+ ivoryos.run()
125
+ ```
126
+ ## Demo
127
+ In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
128
+ addresses will be available on terminal.
129
+ ```Python
130
+ ivoryos.run(__name__)
131
+ ```
132
+
133
+ * Running on all addresses (0.0.0.0)
134
+ * Running on http://127.0.0.1:8000
135
+ * Running on http://xxx.xx.xx.xxx:8000
136
+
137
+ ### Deck function and web form
138
+ ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/demo.gif)
139
+
140
+ ### Directory structure
141
+
142
+ When you run the application for the first time, it will automatically create the following folders and files in the same directory:
143
+
144
+ - **`ivoryos_data/`**: Main directory for application-related data.
145
+ - **`ivoryos_data/config_csv/`**: Contains iteration configuration files in CSV format.
146
+ - **`ivoryos_data/llm_output/`**: Stores raw prompt generated for the large language model.
147
+ - **`ivoryos_data/pseudo_deck/`**: Contains pseudo-deck `.pkl` files for offline access.
148
+ - **`ivoryos_data/results/`**: Used for storing results or outputs during workflow execution.
149
+ - **`ivoryos_data/scripts/`**: Holds Python scripts compiled from the visual programming script design.
150
+
151
+ - **`default.log`**: Log file that captures application logs.
152
+ - **`ivoryos.db`**: Database file that stores application data locally.
153
+
154
+
155
+ ### Demo video
156
+ Intro + Tutorial + Demo with PurPOSE platform
157
+ https://youtu.be/dFfJv9I2-1g
158
+
159
+
160
+ ## Authors and Acknowledgement
161
+ Ivory Zhang, Lucy Hao
162
+
163
+ Authors acknowledge all former and current Hein Lab members for their valuable suggestions.
164
+
165
+ ## License
166
+ [LICENSE](LICENSE)
@@ -0,0 +1,147 @@
1
+ ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/ivoryos.png)
2
+ # ivoryOS: interoperable Web UI for self-driving laboratories (SDLs)
3
+ "plug and play" web UI extension for flexible SDLs.
4
+
5
+ ## Table of Contents
6
+ - [Description](#description)
7
+ - [System requirements](#system-requirements)
8
+ - [Installation](#installation)
9
+ - [Instructions for use](#instructions-for-use)
10
+ - [Demo](#demo)
11
+ - [License](#license)
12
+
13
+ ## Description
14
+ Granting SDLs flexibility and modularity makes it almost impossible to design a UI, yet it's a necessity for allowing more people to interact with it (democratisation).
15
+ This web UI aims to ease up the control of any Python-based SDLs by displaying functions and parameters for initialized modules dynamically.
16
+ The modules can be hardware API, high-level functions, or experiment workflow.
17
+ With the least modification of the current workflow, user can design, manage and execute their experimental designs and monitor the execution process.
18
+
19
+ ## System requirements
20
+ 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.
21
+
22
+ ### Python Version
23
+ Python >=3.7 for best compatibility.
24
+ ### Python dependencies
25
+ This software is compatible with the latest versions of all dependencies.
26
+ - bcrypt~=4.0
27
+ - Flask-Login~=0.6
28
+ - Flask-Session~=0.8
29
+ - Flask-SocketIO~=5.3
30
+ - Flask-SQLAlchemy~=3.1
31
+ - SQLAlchemy-Utils~=0.41
32
+ - Flask-WTF~=1.2
33
+ - python-dotenv==1.0.1
34
+ - openai (optional ~=1.53)
35
+ - ax-platform (optional ~=0.3 or ~=0.4 for Python>=3.9)
36
+
37
+ ## Installation
38
+ ```bash
39
+ pip install ivoryos
40
+ ```
41
+ or
42
+ ```bash
43
+ git clone https://gitlab.com/heingroup/ivoryos.git
44
+ cd ivoryos
45
+ pip install -e .
46
+ ```
47
+
48
+ 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.
49
+
50
+ ## Instructions for use
51
+ ### Quick start
52
+ In your SDL script, use `ivoryos(__name__)`.
53
+ ```python
54
+ import ivoryos
55
+
56
+ ivoryos.run(__name__)
57
+ ```
58
+ ### Login
59
+ Create an account and login (local database)
60
+ ### Features
61
+ - **Direct control**: direct function calling _Device_ tab
62
+ - **Workflow design and iteration**:
63
+ - **Design**: add function to canvas in _Design_ tab. click `Compile and Run` button to go to the execution page
64
+ - **Execution**: configure iteration methods and parameters in _Compile/Run_ tab.
65
+ - **Database**: manage workflows in _Library_ tab.
66
+ - **Info page**: additional info in _About_ tab.
67
+
68
+
69
+ ### Additional settings
70
+ #### AI assistant
71
+ 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.
72
+
73
+ #### Enable LLMs with [OpenAI API](https://github.com/openai/openai-python)
74
+ 1. Create a `.env` file for `OPENAI_API_KEY`
75
+ ```
76
+ OPENAI_API_KEY="Your API Key"
77
+ ```
78
+ 2. In your SDL script, define model, you can use any GPT models.
79
+
80
+ ```python
81
+ ivoryos.run(__name__, model="gpt-3.5-turbo")
82
+ ```
83
+
84
+ #### Enable local LLMs with [Ollama](https://ollama.com/)
85
+ 1. Download Ollama.
86
+ 2. pull models from Ollama
87
+ 3. In your SDL script, define LLM server and model, you can use any models available on Ollama.
88
+
89
+ ```python
90
+ ivoryos.run(__name__, llm_server="localhost", model="llama3.1")
91
+ ```
92
+
93
+ #### Add additional logger(s)
94
+ ```python
95
+ ivoryos.run(__name__, logger="logger name")
96
+ ```
97
+ or
98
+ ```python
99
+ ivoryos.run(__name__, logger=["logger 1", "logger 2"])
100
+ ```
101
+ #### Offline (design without hardware connection)
102
+ After one successful connection, a blueprint will be automatically saved and made accessible without hardware connection. In a new Python script in the same directory, use `ivoryos.run()` to start offline mode.
103
+
104
+ ```python
105
+ ivoryos.run()
106
+ ```
107
+ ## Demo
108
+ In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
109
+ addresses will be available on terminal.
110
+ ```Python
111
+ ivoryos.run(__name__)
112
+ ```
113
+
114
+ * Running on all addresses (0.0.0.0)
115
+ * Running on http://127.0.0.1:8000
116
+ * Running on http://xxx.xx.xx.xxx:8000
117
+
118
+ ### Deck function and web form
119
+ ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/demo.gif)
120
+
121
+ ### Directory structure
122
+
123
+ When you run the application for the first time, it will automatically create the following folders and files in the same directory:
124
+
125
+ - **`ivoryos_data/`**: Main directory for application-related data.
126
+ - **`ivoryos_data/config_csv/`**: Contains iteration configuration files in CSV format.
127
+ - **`ivoryos_data/llm_output/`**: Stores raw prompt generated for the large language model.
128
+ - **`ivoryos_data/pseudo_deck/`**: Contains pseudo-deck `.pkl` files for offline access.
129
+ - **`ivoryos_data/results/`**: Used for storing results or outputs during workflow execution.
130
+ - **`ivoryos_data/scripts/`**: Holds Python scripts compiled from the visual programming script design.
131
+
132
+ - **`default.log`**: Log file that captures application logs.
133
+ - **`ivoryos.db`**: Database file that stores application data locally.
134
+
135
+
136
+ ### Demo video
137
+ Intro + Tutorial + Demo with PurPOSE platform
138
+ https://youtu.be/dFfJv9I2-1g
139
+
140
+
141
+ ## Authors and Acknowledgement
142
+ Ivory Zhang, Lucy Hao
143
+
144
+ Authors acknowledge all former and current Hein Lab members for their valuable suggestions.
145
+
146
+ ## License
147
+ [LICENSE](LICENSE)
@@ -2,7 +2,7 @@ import os
2
2
  import sys
3
3
  from typing import Union
4
4
 
5
- from flask import Flask
5
+ from flask import Flask, redirect, url_for
6
6
 
7
7
  from ivoryos.config import Config, get_config
8
8
  from ivoryos.routes.auth.auth import auth, login_manager
@@ -19,7 +19,8 @@ global_config = GlobalConfig()
19
19
 
20
20
 
21
21
  def create_app(config_class=None):
22
- app = Flask(__name__)
22
+ url_prefix = os.getenv('URL_PREFIX', None)
23
+ app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
23
24
  app.config.from_object(config_class or 'config.get_config()')
24
25
 
25
26
  # Initialize extensions
@@ -45,11 +46,15 @@ def create_app(config_class=None):
45
46
  g.logger = logger
46
47
  g.socketio = socketio
47
48
 
48
- app.register_blueprint(main)
49
- app.register_blueprint(auth)
50
- app.register_blueprint(design)
51
- app.register_blueprint(database)
52
- app.register_blueprint(control)
49
+ app.register_blueprint(main, url_prefix=url_prefix)
50
+ app.register_blueprint(auth, url_prefix=url_prefix)
51
+ app.register_blueprint(design, url_prefix=url_prefix)
52
+ app.register_blueprint(database, url_prefix=url_prefix)
53
+ app.register_blueprint(control, url_prefix=url_prefix)
54
+
55
+ @app.route('/')
56
+ def redirect_to_prefix():
57
+ return redirect(url_for('main.index')) # Assuming 'index' is a route in your blueprint
53
58
 
54
59
  return app
55
60
 
@@ -72,7 +77,7 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
72
77
  app.config["MODULE"] = module
73
78
  app.config["OFF_LINE"] = False
74
79
  global_config.deck = sys.modules[module]
75
- global_config.deck_variables = utils.parse_deck(global_config.deck, output_path=app.config["DUMMY_DECK"], save=True)
80
+ global_config.deck_snapshot = utils.create_deck_snapshot(global_config.deck, output_path=app.config["DUMMY_DECK"], save=True)
76
81
  # global_config.runner = ScriptRunner(globals())
77
82
  else:
78
83
  app.config["OFF_LINE"] = True
@@ -15,6 +15,7 @@ class Config:
15
15
  SCRIPT_FOLDER = os.path.join(OUTPUT_FOLDER, 'scripts/')
16
16
  DATA_FOLDER = os.path.join(OUTPUT_FOLDER, 'results/')
17
17
  DUMMY_DECK = os.path.join(OUTPUT_FOLDER, 'pseudo_deck/')
18
+ LLM_OUTPUT = os.path.join(OUTPUT_FOLDER, 'llm_output/')
18
19
  DECK_HISTORY = os.path.join(OUTPUT_FOLDER, 'deck_history.txt')
19
20
  LOGGERS_PATH = "default.log"
20
21
 
@@ -1,8 +1,6 @@
1
1
  import os
2
- import pickle
3
- import sys
4
2
 
5
- from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app
3
+ from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app, jsonify
6
4
  from flask_login import login_required
7
5
 
8
6
  from ivoryos.utils.global_config import GlobalConfig
@@ -17,7 +15,7 @@ control = Blueprint('control', __name__, template_folder='templates/control')
17
15
  @control.route("/my_deck")
18
16
  @login_required
19
17
  def deck_controllers():
20
- deck_variables = global_config.deck_variables.keys()
18
+ deck_variables = global_config.deck_snapshot.keys()
21
19
  deck_list = utils.import_history(os.path.join(current_app.config["OUTPUT_FOLDER"], 'deck_history.txt'))
22
20
  return render_template('controllers_home.html', defined_variables=deck_variables, deck=True, history=deck_list)
23
21
 
@@ -77,12 +75,15 @@ def controllers_home():
77
75
  def controllers(instrument):
78
76
  inst_object = find_instrument_by_name(instrument)
79
77
  _forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
80
- card_order = session.get('card_order')
81
- order = card_order.get(instrument, _forms.keys())
82
- if instrument not in card_order:
83
- card_order[instrument] = list(order)
84
- session['card_order'] = card_order
85
- # print(session['card_order'])
78
+ functions = list(_forms.keys())
79
+
80
+ order = get_session_by_instrument('card_order', instrument)
81
+ hidden_functions = get_session_by_instrument('hide_function', instrument)
82
+
83
+ for function in functions:
84
+ if function not in hidden_functions and function not in order:
85
+ order.append(function)
86
+ post_session_by_instrument('card_order', instrument, order)
86
87
  forms = {name: _forms[name] for name in order if name in _forms}
87
88
  if request.method == 'POST':
88
89
  all_kwargs = request.form.copy()
@@ -103,6 +104,32 @@ def controllers(instrument):
103
104
  return render_template('controllers.html', instrument=instrument, forms=forms, format_name=format_name)
104
105
 
105
106
 
107
+ @control.route("/backend_control/<instrument>", methods=['GET', 'POST'])
108
+ @login_required
109
+ def backend_control(instrument):
110
+ inst_object = find_instrument_by_name(instrument)
111
+ forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
112
+ if request.method == 'POST':
113
+ all_kwargs = request.form.copy()
114
+ method_name = all_kwargs.pop("hidden_name", None)
115
+ # if method_name is not None:
116
+ form = forms.get(method_name, None)
117
+ kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
118
+ function_executable = getattr(inst_object, method_name)
119
+ if form:
120
+ # print(kwargs)
121
+ try:
122
+ kwargs.pop("hidden_name")
123
+ output = function_executable(**kwargs)
124
+ json_output = jsonify(output)
125
+ except Exception as e:
126
+ json_output = jsonify(e.__str__())
127
+ return json_output, 400
128
+ else:
129
+ return "instrument not exist", 400
130
+ return json_output, 200
131
+
132
+
106
133
  @control.route("/import_api", methods=['GET', 'POST'])
107
134
  def import_api():
108
135
  filepath = request.form.get('filepath')
@@ -164,12 +191,12 @@ def import_deck():
164
191
  try:
165
192
  module = utils.import_module_by_filepath(filepath=filepath, name=name)
166
193
  utils.save_to_history(filepath, current_app.config["DECK_HISTORY"])
167
- module_sigs = utils.parse_deck(module, save=update, output_path=current_app.config["DUMMY_DECK"])
194
+ module_sigs = utils.create_deck_snapshot(module, save=update, output_path=current_app.config["DUMMY_DECK"])
168
195
  if not len(module_sigs) > 0:
169
196
  flash("Invalid hardware deck, connect instruments in deck script", "error")
170
197
  return redirect(url_for("control.deck_controllers"))
171
198
  global_config.deck = module
172
- global_config.deck_variables = module_sigs
199
+ global_config.deck_snapshot = module_sigs
173
200
 
174
201
  if script.deck is None:
175
202
  script.deck = module.__name__
@@ -183,47 +210,59 @@ def import_deck():
183
210
  def save_order(instrument):
184
211
  # Save the new order for the specified group to session
185
212
  data = request.json
186
- card_order = session.get("card_order", {})
187
- card_order[instrument] = data['order']
188
- session['card_order'] = card_order
213
+ post_session_by_instrument('card_order', instrument, data['order'])
189
214
  return '', 204
190
215
 
191
216
 
192
217
  @control.route('/hide_function/<instrument>/<function>')
193
218
  def hide_function(instrument, function):
194
219
  back = request.referrer
195
- hidden_functions = session.get("hidden_functions")
196
- functions = hidden_functions.get(instrument, [])
197
- card_order = session.get("card_order")
198
- order = card_order.get(instrument)
220
+ functions = get_session_by_instrument("hidden_functions", instrument)
221
+ order = get_session_by_instrument("card_order", instrument)
199
222
  if function not in functions:
200
223
  functions.append(function)
201
224
  order.remove(function)
202
- hidden_functions[instrument] = functions
203
- card_order[instrument] = order
204
- session['hidden_functions'] = hidden_functions
205
- session['card_order'] = card_order
225
+ post_session_by_instrument('hidden_functions', instrument, functions)
226
+ post_session_by_instrument('card_order', instrument, order)
206
227
  return redirect(back)
207
228
 
208
229
 
209
230
  @control.route('/remove_hidden/<instrument>/<function>')
210
231
  def remove_hidden(instrument, function):
211
232
  back = request.referrer
212
- hidden_functions = session.get("hidden_functions")
213
- functions = hidden_functions.get(instrument, [])
214
- card_order = session.get("card_order")
215
- order = card_order.get(instrument)
233
+ functions = get_session_by_instrument("hidden_functions", instrument)
234
+ order = get_session_by_instrument("card_order", instrument)
216
235
  if function in functions:
217
236
  functions.remove(function)
218
237
  order.append(function)
219
- hidden_functions[instrument] = functions
220
- card_order[instrument] = order
221
- session['hidden_functions'] = hidden_functions
222
- session['card_order'] = card_order
238
+ post_session_by_instrument('hidden_functions', instrument, functions)
239
+ post_session_by_instrument('card_order', instrument, order)
223
240
  return redirect(back)
224
241
 
225
242
 
243
+ def get_session_by_instrument(session_name, instrument):
244
+ """get data from session by instrument"""
245
+ session_object = session.get(session_name, {})
246
+ functions = session_object.get(instrument, [])
247
+ return functions
248
+
249
+
250
+ def post_session_by_instrument(session_name, instrument, data):
251
+ """
252
+ save new data to session by instrument
253
+ :param session_name: "card_order" or "hidden_functions"
254
+ :param instrument: function name of class object
255
+ :param data: order list or hidden function list
256
+ """
257
+ session_object = session.get(session_name, {})
258
+ session_object[instrument] = data
259
+ session[session_name] = session_object
260
+
261
+
226
262
  def find_instrument_by_name(name: str):
263
+ """
264
+ find instrument class object by instance name
265
+ """
227
266
  if name.startswith("deck"):
228
267
  name = name.replace("deck.", "")
229
268
  return getattr(global_config.deck, name)
@@ -8,9 +8,12 @@
8
8
  </div>
9
9
  </div>
10
10
  <h1>{{instrument}} controller</h1>
11
+ {% set hidden = session.get('hidden_functions', {}) %}
11
12
  <div class="grid-container" id="sortable-grid">
12
13
  {% for function, form in forms.items() %}
13
- {% if function not in session['hidden_functions'][instrument] %}
14
+
15
+ {% set hidden_instrument = hidden.get(instrument, []) %}
16
+ {% if function not in hidden_instrument %}
14
17
  <div class="card" id="{{function}}">
15
18
  <div class="bg-white rounded shadow-sm flex-fill">
16
19
  <a style="float: right" aria-label="Close" href="{{ url_for('control.hide_function', instrument=instrument, function=function) }}"><i class="bi bi-eye-slash-fill"></i></a>
@@ -53,7 +56,8 @@
53
56
  </div>
54
57
  <div id="hidden" class="accordion-collapse collapse" data-bs-parent="#accordionActions">
55
58
  <div class="accordion-body">
56
- {% for function in session['hidden_functions'][instrument] %}
59
+ {% set hidden_instrument = hidden.get(instrument, []) %}
60
+ {% for function in hidden_instrument %}
57
61
  <div>
58
62
  {{ function }} <a href="{{ url_for('control.remove_hidden', instrument=instrument, function=function) }}"><i class="bi bi-eye-fill"></i></a>
59
63
  </div>
@@ -61,8 +61,9 @@ def publish():
61
61
  def finalize():
62
62
  script = get_script_file()
63
63
  script.finalize()
64
- db.session.merge(script)
65
- db.session.commit()
64
+ if script.name:
65
+ db.session.merge(script)
66
+ db.session.commit()
66
67
  post_script_file(script)
67
68
  return redirect(url_for('design.experiment_builder'))
68
69
 
@@ -69,7 +69,7 @@ def experiment_builder(instrument=None):
69
69
 
70
70
  functions = []
71
71
  if deck:
72
- deck_variables = global_config.deck_variables.keys()
72
+ deck_variables = global_config.deck_snapshot.keys()
73
73
  else:
74
74
  deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
75
75
  deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
@@ -78,7 +78,7 @@ def experiment_builder(instrument=None):
78
78
  forms = create_builtin_form(instrument)
79
79
  else:
80
80
  if deck:
81
- function_metadata = global_config.deck_variables.get(instrument, {})
81
+ function_metadata = global_config.deck_snapshot.get(instrument, {})
82
82
  elif pseudo_deck:
83
83
  function_metadata = pseudo_deck.get(instrument, {})
84
84
  functions = {key: data.get('signature', {}) for key, data in function_metadata.items()}
@@ -158,7 +158,7 @@ def generate_code():
158
158
  prompt = request.form.get("prompt")
159
159
  session['prompt'][instrument] = prompt
160
160
  # sdl_module = utils.parse_functions(find_instrument_by_name(f'deck.{instrument}'), doc_string=True)
161
- sdl_module = global_config.deck_variables.get(instrument, {})
161
+ sdl_module = global_config.deck_snapshot.get(instrument, {})
162
162
  empty_script = Script(author=session.get('user'))
163
163
  if enable_llm and agent is None:
164
164
  try:
@@ -241,7 +241,7 @@ def experiment_run():
241
241
  bo_args = request.form.to_dict()
242
242
  # ax_client = utils.ax_initiation(bo_args)
243
243
  if "online-config" in request.form:
244
- config = utils.process_data(request.form.to_dict(), config_list)
244
+ config = utils.web_config_entry_wrapper(request.form.to_dict(), config_list)
245
245
  repeat = request.form.get('repeat', None)
246
246
 
247
247
  try:
@@ -134,9 +134,6 @@
134
134
  <div class="accordion-body">
135
135
  This is project is a work in progress. In case of any bugs or suggestions, reach out to Ivory: ivoryzhang@chem.ubc.ca or create an issue on GitLab:
136
136
  <a href="https://gitlab.com/heingroup/ivoryos">https://gitlab.com/heingroup/ivoryos</a>.
137
- {# <img src="{{url_for('static', filename='gui_annotation/installation_guide.PNG')}}">#}
138
-
139
- {# <object webui_data="{{ url_for('static', filename='setup-help.pdf')}}" type="application/pdf" style="min-height:80vh;width:100%"></object>#}
140
137
  </div>
141
138
  </div>
142
139
  </div>
@@ -6,7 +6,16 @@ document.addEventListener("DOMContentLoaded", function() {
6
6
  socket.on('progress', function(data) {
7
7
  var progress = data.progress;
8
8
  console.log(progress);
9
- $('#progress-bar-inner').css('width', progress + '%')
9
+ // Update the progress bar's width and appearance
10
+ var progressBar = document.getElementById('progress-bar-inner');
11
+ progressBar.style.width = progress + '%';
12
+ progressBar.setAttribute('aria-valuenow', progress);
13
+
14
+ if (progress === 100) {
15
+ // Remove animation and set green color when 100% is reached
16
+ progressBar.classList.remove('progress-bar-animated');
17
+ progressBar.classList.add('bg-success'); // Bootstrap class for green color
18
+ }
10
19
  });
11
20
  socket.on('log', function(data) {
12
21
  var logMessage = data.message;
Binary file
@@ -25,7 +25,7 @@
25
25
  <div class= "container">
26
26
 
27
27
  <a class="navbar-brand" href="{{ url_for('main.index') }}">
28
- <img src="{{url_for('static', filename='logo.png')}}" alt="Logo" height="60" class="d-inline-block align-text-bottom">
28
+ <img src="{{url_for('static', filename='logo.webp')}}" alt="Logo" height="60" class="d-inline-block align-text-bottom">
29
29
  </a>
30
30
  <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
31
31
  <span class="navbar-toggler-icon"></span>
@@ -33,6 +33,9 @@
33
33
 
34
34
  <div class="collapse navbar-collapse" id="navbarSupportedContent">
35
35
  <ul class="navbar-nav mr-auto">
36
+ <li class="nav-item">
37
+ <a class="nav-link" href="{{ url_for('main.index') }}" aria-current="page">Home</a>
38
+ </li>
36
39
  <li class="nav-item">
37
40
  <a class="nav-link" href="{{ url_for('database.load_from_database') }}" aria-current="page">Library</a>
38
41
  </li>