ivoryos 0.1.21__tar.gz → 0.1.22__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-0.1.21/ivoryos.egg-info → ivoryos-0.1.22}/PKG-INFO +7 -3
- {ivoryos-0.1.21 → ivoryos-0.1.22}/README.md +6 -2
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/__init__.py +13 -5
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/control/control.py +2 -2
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/control/templates/control/controllers_home.html +6 -1
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/database/database.py +43 -2
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/database/templates/database/experiment_database.html +3 -3
- ivoryos-0.1.22/ivoryos/routes/database/templates/database/workflow_run_database.html +81 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/design/design.py +16 -2
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/design/templates/design/experiment_run.html +159 -81
- ivoryos-0.1.22/ivoryos/routes/main/templates/main/home.html +103 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/templates/base.html +6 -3
- ivoryos-0.1.22/ivoryos/utils/client_proxy.py +57 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/utils/db_models.py +43 -1
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/utils/llm_agent.py +1 -1
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/utils/script_runner.py +91 -42
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/utils/utils.py +23 -0
- ivoryos-0.1.22/ivoryos/version.py +1 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22/ivoryos.egg-info}/PKG-INFO +7 -3
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos.egg-info/SOURCES.txt +2 -0
- ivoryos-0.1.21/ivoryos/routes/main/templates/main/home.html +0 -70
- ivoryos-0.1.21/ivoryos/version.py +0 -1
- {ivoryos-0.1.21 → ivoryos-0.1.22}/LICENSE +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/MANIFEST.in +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/config.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/auth/auth.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/auth/templates/auth/login.html +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/auth/templates/auth/signup.html +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/control/templates/control/controllers.html +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/control/templates/control/controllers_new.html +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/database/__init__.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/design/templates/design/experiment_builder.html +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/main/main.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/main/templates/main/help.html +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/js/socket_handler.js +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/js/sortable_design.js +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/logo.webp +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/static/style.css +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/utils/form.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/utils/global_config.py +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos.egg-info/requires.txt +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos.egg-info/top_level.txt +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/setup.cfg +0 -0
- {ivoryos-0.1.21 → ivoryos-0.1.22}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.22
|
|
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
|
|
@@ -28,7 +28,7 @@ License-File: LICENSE
|
|
|
28
28
|
- [Instructions for use](#instructions-for-use)
|
|
29
29
|
- [Demo](#demo)
|
|
30
30
|
- [Roadmap](#roadmap)
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
|
|
33
33
|
## Description
|
|
34
34
|
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).
|
|
@@ -85,6 +85,10 @@ Create an account and login (local database)
|
|
|
85
85
|
- **Database**: manage workflows in _Library_ tab.
|
|
86
86
|
- **Info page**: additional info in _About_ tab.
|
|
87
87
|
|
|
88
|
+
[//]: # ()
|
|
89
|
+
|
|
90
|
+
[//]: # ()
|
|
91
|
+
|
|
88
92
|
|
|
89
93
|
### Additional settings
|
|
90
94
|
#### AI assistant
|
|
@@ -125,7 +129,7 @@ After one successful connection, a blueprint will be automatically saved and mad
|
|
|
125
129
|
ivoryos.run()
|
|
126
130
|
```
|
|
127
131
|
## Demo
|
|
128
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/
|
|
132
|
+
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
|
|
129
133
|
addresses will be available on terminal.
|
|
130
134
|
```Python
|
|
131
135
|
ivoryos.run(__name__)
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- [Instructions for use](#instructions-for-use)
|
|
17
17
|
- [Demo](#demo)
|
|
18
18
|
- [Roadmap](#roadmap)
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
|
|
21
21
|
## Description
|
|
22
22
|
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).
|
|
@@ -73,6 +73,10 @@ Create an account and login (local database)
|
|
|
73
73
|
- **Database**: manage workflows in _Library_ tab.
|
|
74
74
|
- **Info page**: additional info in _About_ tab.
|
|
75
75
|
|
|
76
|
+
[//]: # ()
|
|
77
|
+
|
|
78
|
+
[//]: # ()
|
|
79
|
+
|
|
76
80
|
|
|
77
81
|
### Additional settings
|
|
78
82
|
#### AI assistant
|
|
@@ -113,7 +117,7 @@ After one successful connection, a blueprint will be automatically saved and mad
|
|
|
113
117
|
ivoryos.run()
|
|
114
118
|
```
|
|
115
119
|
## Demo
|
|
116
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/
|
|
120
|
+
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
|
|
117
121
|
addresses will be available on terminal.
|
|
118
122
|
```Python
|
|
119
123
|
ivoryos.run(__name__)
|
|
@@ -18,6 +18,16 @@ from ivoryos.utils.script_runner import ScriptRunner
|
|
|
18
18
|
from ivoryos.version import __version__ as ivoryos_version
|
|
19
19
|
from importlib.metadata import entry_points
|
|
20
20
|
global_config = GlobalConfig()
|
|
21
|
+
from sqlalchemy import event
|
|
22
|
+
from sqlalchemy.engine import Engine
|
|
23
|
+
import sqlite3
|
|
24
|
+
|
|
25
|
+
@event.listens_for(Engine, "connect")
|
|
26
|
+
def enforce_sqlite_foreign_keys(dbapi_connection, connection_record):
|
|
27
|
+
if isinstance(dbapi_connection, sqlite3.Connection):
|
|
28
|
+
cursor = dbapi_connection.cursor()
|
|
29
|
+
cursor.execute("PRAGMA foreign_keys=ON")
|
|
30
|
+
cursor.close()
|
|
21
31
|
|
|
22
32
|
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
23
33
|
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
@@ -29,8 +39,9 @@ app.register_blueprint(database, url_prefix=url_prefix)
|
|
|
29
39
|
|
|
30
40
|
|
|
31
41
|
def create_app(config_class=None):
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
"""
|
|
43
|
+
create app, init database
|
|
44
|
+
"""
|
|
34
45
|
app.config.from_object(config_class or 'config.get_config()')
|
|
35
46
|
|
|
36
47
|
# Initialize extensions
|
|
@@ -85,7 +96,6 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
85
96
|
:param logger: logger name of list of logger names, defaults to None
|
|
86
97
|
:param logger_output_name: log file save name of logger, defaults to None, and will use "default.log"
|
|
87
98
|
:param enable_design: enable design canvas, database and workflow execution
|
|
88
|
-
:param stream_address:
|
|
89
99
|
"""
|
|
90
100
|
app = create_app(config_class=config or get_config()) # Create app instance using factory function
|
|
91
101
|
|
|
@@ -110,10 +120,8 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
110
120
|
app.config["MODULE"] = module
|
|
111
121
|
app.config["OFF_LINE"] = False
|
|
112
122
|
global_config.deck = sys.modules[module]
|
|
113
|
-
# global_config.heinsight = HeinsightAPI("http://127.0.0.1:8080")
|
|
114
123
|
global_config.deck_snapshot = utils.create_deck_snapshot(global_config.deck,
|
|
115
124
|
output_path=app.config["DUMMY_DECK"], save=True)
|
|
116
|
-
# global_config.runner = ScriptRunner(globals())
|
|
117
125
|
else:
|
|
118
126
|
app.config["OFF_LINE"] = True
|
|
119
127
|
if model:
|
|
@@ -151,7 +151,7 @@ def controllers(instrument: str):
|
|
|
151
151
|
return render_template('controllers.html', instrument=instrument, forms=forms, format_name=format_name)
|
|
152
152
|
|
|
153
153
|
|
|
154
|
-
@control.route("/backend_control/<instrument>", methods=['
|
|
154
|
+
@control.route("/backend_control/<instrument>", methods=['POST'])
|
|
155
155
|
def backend_control(instrument: str=None):
|
|
156
156
|
"""
|
|
157
157
|
.. :quickref: Backend Control; backend control
|
|
@@ -187,7 +187,7 @@ def backend_control(instrument: str=None):
|
|
|
187
187
|
return json_output, 400
|
|
188
188
|
else:
|
|
189
189
|
return "instrument not exist", 400
|
|
190
|
-
|
|
190
|
+
return json_output, 200
|
|
191
191
|
|
|
192
192
|
|
|
193
193
|
@control.route("/backend_control", methods=['GET'])
|
{ivoryos-0.1.21 → ivoryos-0.1.22}/ivoryos/routes/control/templates/control/controllers_home.html
RENAMED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{# {% if not deck %}#}
|
|
10
10
|
{# <a href="{{ url_for('control.disconnect', instrument=instrument) }}" class="stretched-link controller-card" style="float: right;color: red; position: relative;">Disconnect <i class="bi bi-x-square"></i></a>#}
|
|
11
11
|
<div class="p-4 controller-card">
|
|
12
|
-
<h5 class=""><a href="{{ url_for('control.controllers', instrument=instrument) }}" class="text-dark stretched-link">{{instrument}}</a></h5>
|
|
12
|
+
<h5 class=""><a href="{{ url_for('control.controllers', instrument=instrument) }}" class="text-dark stretched-link">{{instrument.split(".")[1]}}</a></h5>
|
|
13
13
|
</div>
|
|
14
14
|
{# {% else %}#}
|
|
15
15
|
{# <div class="p-4 controller-card">#}
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
21
21
|
{% endfor %}
|
|
22
|
+
<div class="d-flex mb-3">
|
|
23
|
+
<a href="{{ url_for('design.download', filetype='proxy') }}" class="btn btn-outline-primary">
|
|
24
|
+
<i class="bi bi-download"></i> Download remote control script
|
|
25
|
+
</a>
|
|
26
|
+
</div>
|
|
22
27
|
{% if not deck %}
|
|
23
28
|
<div class="col-xl-3 col-lg-4 col-md-6 mb-4 ">
|
|
24
29
|
<div class="bg-white rounded shadow-sm position-relative">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app
|
|
1
|
+
from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app, jsonify
|
|
2
2
|
from flask_login import login_required
|
|
3
3
|
|
|
4
|
-
from ivoryos.utils.db_models import Script, db
|
|
4
|
+
from ivoryos.utils.db_models import Script, db, WorkflowRun, WorkflowStep
|
|
5
5
|
from ivoryos.utils.utils import get_script_file, post_script_file
|
|
6
6
|
|
|
7
7
|
database = Blueprint('database', __name__, template_folder='templates/database')
|
|
@@ -188,3 +188,44 @@ def save_as():
|
|
|
188
188
|
else:
|
|
189
189
|
flash("Script name is already exist in database")
|
|
190
190
|
return redirect(url_for("design.experiment_builder"))
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@database.route('/workflow_runs')
|
|
194
|
+
def list_workflows():
|
|
195
|
+
query = WorkflowRun.query
|
|
196
|
+
search_term = request.args.get("keyword", None)
|
|
197
|
+
if search_term:
|
|
198
|
+
query = query.filter(WorkflowRun.name.like(f'%{search_term}%'))
|
|
199
|
+
page = request.args.get('page', default=1, type=int)
|
|
200
|
+
per_page = 10
|
|
201
|
+
|
|
202
|
+
workflows = query.paginate(page=page, per_page=per_page, error_out=False)
|
|
203
|
+
return render_template('workflow_run_database.html', workflows=workflows)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@database.route('/workflow_steps/<int:workflow_id>')
|
|
207
|
+
def get_workflow_steps(workflow_id):
|
|
208
|
+
steps = WorkflowStep.query.filter_by(workflow_id=workflow_id).all()
|
|
209
|
+
steps_data = [step.as_dict() for step in steps]
|
|
210
|
+
return jsonify({'steps': steps_data})
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@database.route("/delete_workflow_data/<workflow_id>")
|
|
214
|
+
@login_required
|
|
215
|
+
def delete_workflow_data(workflow_id: str):
|
|
216
|
+
"""
|
|
217
|
+
.. :quickref: Database; delete experiment data from database
|
|
218
|
+
|
|
219
|
+
delete workflow data from database
|
|
220
|
+
|
|
221
|
+
.. http:get:: /delete_workflow_data/<workflow_id>
|
|
222
|
+
|
|
223
|
+
:param workflow_id: workflow id
|
|
224
|
+
:type workflow_id: str
|
|
225
|
+
:status 302: redirect to :http:get:`/ivoryos/workflow_runs/`
|
|
226
|
+
|
|
227
|
+
"""
|
|
228
|
+
run = WorkflowRun.query.get(workflow_id)
|
|
229
|
+
db.session.delete(run)
|
|
230
|
+
db.session.commit()
|
|
231
|
+
return redirect(url_for('database.list_workflows'))
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<th scope="col">Time created</th>
|
|
31
31
|
<th scope="col">Last modified</th>
|
|
32
32
|
<th scope="col">Author</th>
|
|
33
|
-
<th scope="col">Registered</th
|
|
33
|
+
{# <th scope="col">Registered</th>#}
|
|
34
34
|
<th scope="col"></th>
|
|
35
35
|
</tr>
|
|
36
36
|
</thead>
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
<td>{{ workflow.time_created }}</td>
|
|
44
44
|
<td>{{ workflow.last_modified }}</td>
|
|
45
45
|
<td>{{ workflow.author }}</td>
|
|
46
|
-
<td>{{ workflow.registered }}</td
|
|
46
|
+
{# <td>{{ workflow.registered }}</td>#}
|
|
47
47
|
<td>
|
|
48
48
|
{#not workflow.status == "finalized" or#}
|
|
49
49
|
{% if session['user'] == 'admin' or session['user'] == workflow.author %}
|
|
50
50
|
<a href="{{ url_for('database.delete_workflow', workflow_name=workflow.name) }}">delete</a>
|
|
51
51
|
{% else %}
|
|
52
|
-
<a class="disabled-link"
|
|
52
|
+
<a class="disabled-link">delete</a>
|
|
53
53
|
{% endif %}
|
|
54
54
|
<td>
|
|
55
55
|
</tr>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
|
|
3
|
+
{% block title %}IvoryOS | Design Database{% endblock %}
|
|
4
|
+
{% block body %}
|
|
5
|
+
|
|
6
|
+
<table class="table table-hover" id="workflowResultLibrary">
|
|
7
|
+
<thead>
|
|
8
|
+
<tr>
|
|
9
|
+
<th scope="col">Workflow name</th>
|
|
10
|
+
<th scope="col">Start time</th>
|
|
11
|
+
<th scope="col">End time</th>
|
|
12
|
+
<th scope="col">Data</th>
|
|
13
|
+
</tr>
|
|
14
|
+
</thead>
|
|
15
|
+
<tbody>
|
|
16
|
+
{% for workflow in workflows %}
|
|
17
|
+
<tr>
|
|
18
|
+
<td><a href="{{ url_for('database.get_workflow_steps', workflow_id=workflow.id) }}">{{ workflow.name }}</a></td>
|
|
19
|
+
<td>{{ workflow.start_time.strftime("%Y-%m-%d %H:%M:%S") if workflow.start_time else '' }}</td>
|
|
20
|
+
<td>{{ workflow.end_time.strftime("%Y-%m-%d %H:%M:%S") if workflow.end_time else '' }}</td>
|
|
21
|
+
|
|
22
|
+
<td>
|
|
23
|
+
{% if workflow.data_path %}
|
|
24
|
+
<a href="{{ url_for('design.download_results', filename=workflow.data_path) }}">{{ workflow.data_path }}</a>
|
|
25
|
+
{% endif %}
|
|
26
|
+
</td>
|
|
27
|
+
<td>
|
|
28
|
+
{% if session['user'] == 'admin' or session['user'] == workflow.author %}
|
|
29
|
+
<a href="{{ url_for('database.delete_workflow_data', workflow_id=workflow.id) }}">delete</a>
|
|
30
|
+
{% else %}
|
|
31
|
+
<a class="disabled-link">delete</a>
|
|
32
|
+
{% endif %}
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
{% endfor %}
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
|
|
39
|
+
{# paging#}
|
|
40
|
+
<div class="pagination justify-content-center">
|
|
41
|
+
<div class="page-item {{ 'disabled' if not workflows.has_prev else '' }}">
|
|
42
|
+
<a class="page-link" href="{{ url_for('database.list_workflows', page=workflows.prev_num) }}">Previous</a>
|
|
43
|
+
</div>
|
|
44
|
+
{% for num in workflows.iter_pages() %}
|
|
45
|
+
<div class="page-item">
|
|
46
|
+
<a class="page-link {{ 'active' if num == workflows.page else '' }}" href="{{ url_for('database.list_workflows', page=num) }}">{{ num }}</a>
|
|
47
|
+
</div>
|
|
48
|
+
{% endfor %}
|
|
49
|
+
<div class="page-item {{ 'disabled' if not workflows.has_next else '' }}">
|
|
50
|
+
<a class="page-link" href="{{ url_for('database.list_workflows', page=workflows.next_num) }}">Next</a>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div id="steps-container"></div>
|
|
55
|
+
|
|
56
|
+
<script>
|
|
57
|
+
function showSteps(workflowId) {
|
|
58
|
+
fetch(`/workflow_steps/${workflowId}`)
|
|
59
|
+
.then(response => response.json())
|
|
60
|
+
.then(data => {
|
|
61
|
+
const container = document.getElementById('steps-container');
|
|
62
|
+
container.innerHTML = ''; // Clear previous content
|
|
63
|
+
const stepsList = document.createElement('ul');
|
|
64
|
+
|
|
65
|
+
data.steps.forEach(step => {
|
|
66
|
+
const li = document.createElement('li');
|
|
67
|
+
li.innerHTML = `
|
|
68
|
+
<strong>Step: </strong> ${step.method_name} <br>
|
|
69
|
+
<strong>Start Time:</strong> ${step.start_time} <br>
|
|
70
|
+
<strong>End Time:</strong> ${step.end_time} <br>
|
|
71
|
+
<strong>Human Intervention:</strong> ${step.run_error ? 'Yes' : 'No'}
|
|
72
|
+
`;
|
|
73
|
+
stepsList.appendChild(li);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
container.appendChild(stepsList);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
{% endblock %}
|
|
@@ -11,6 +11,7 @@ from flask_socketio import SocketIO
|
|
|
11
11
|
from werkzeug.utils import secure_filename
|
|
12
12
|
|
|
13
13
|
from ivoryos.utils import utils
|
|
14
|
+
from ivoryos.utils.client_proxy import create_function, export_to_python
|
|
14
15
|
from ivoryos.utils.global_config import GlobalConfig
|
|
15
16
|
from ivoryos.utils.form import create_builtin_form, create_action_button, format_name, create_form_from_pseudo, \
|
|
16
17
|
create_form_from_action, create_all_builtin_forms
|
|
@@ -360,7 +361,7 @@ def experiment_run():
|
|
|
360
361
|
run_name = script.validate_function_name(run_name)
|
|
361
362
|
runner.run_script(script=script, run_name=run_name, config=config, bo_args=bo_args,
|
|
362
363
|
logger=g.logger, socketio=g.socketio, repeat_count=repeat,
|
|
363
|
-
output_path=datapath
|
|
364
|
+
output_path=datapath, current_app=current_app._get_current_object()
|
|
364
365
|
)
|
|
365
366
|
if utils.check_config_duplicate(config):
|
|
366
367
|
flash(f"WARNING: Duplicate in config entries.")
|
|
@@ -525,7 +526,20 @@ def download(filetype):
|
|
|
525
526
|
outfile.write(json_object)
|
|
526
527
|
elif filetype == "python":
|
|
527
528
|
filepath = os.path.join(current_app.config["SCRIPT_FOLDER"], f"{run_name}.py")
|
|
528
|
-
|
|
529
|
+
elif filetype == "proxy":
|
|
530
|
+
snapshot = global_config.deck_snapshot.copy()
|
|
531
|
+
class_definitions = {}
|
|
532
|
+
# Iterate through each instrument in the snapshot
|
|
533
|
+
for instrument_key, instrument_data in snapshot.items():
|
|
534
|
+
# Iterate through each function associated with the current instrument
|
|
535
|
+
for function_key, function_data in instrument_data.items():
|
|
536
|
+
# Convert the function signature to a string representation
|
|
537
|
+
function_data['signature'] = str(function_data['signature'])
|
|
538
|
+
class_name = instrument_key.split('.')[-1] # Extracting the class name from the path
|
|
539
|
+
class_definitions[class_name.capitalize()] = create_function(request.url_root, class_name, instrument_data)
|
|
540
|
+
# Export the generated class definitions to a .py script
|
|
541
|
+
export_to_python(class_definitions, current_app.config["OUTPUT_FOLDER"])
|
|
542
|
+
filepath = os.path.join(current_app.config["OUTPUT_FOLDER"], "generated_proxy.py")
|
|
529
543
|
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
530
544
|
|
|
531
545
|
|