ivoryos 0.1.20__py3-none-any.whl → 0.1.22__py3-none-any.whl
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/__init__.py +21 -16
- ivoryos/routes/control/control.py +2 -2
- ivoryos/routes/control/templates/control/controllers_home.html +6 -1
- ivoryos/routes/database/database.py +45 -2
- ivoryos/routes/database/templates/database/experiment_database.html +4 -2
- ivoryos/routes/database/templates/database/workflow_run_database.html +81 -0
- ivoryos/routes/design/design.py +74 -18
- ivoryos/routes/design/templates/design/experiment_builder.html +84 -94
- ivoryos/routes/design/templates/design/experiment_run.html +188 -101
- ivoryos/routes/main/templates/main/home.html +80 -47
- ivoryos/static/js/socket_handler.js +59 -16
- ivoryos/static/js/sortable_design.js +102 -33
- ivoryos/static/style.css +9 -0
- ivoryos/templates/base.html +9 -6
- ivoryos/utils/client_proxy.py +57 -0
- ivoryos/utils/db_models.py +66 -11
- ivoryos/utils/form.py +50 -2
- ivoryos/utils/global_config.py +10 -0
- ivoryos/utils/llm_agent.py +1 -1
- ivoryos/utils/script_runner.py +115 -43
- ivoryos/utils/utils.py +24 -1
- ivoryos/version.py +1 -1
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/METADATA +7 -3
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/RECORD +27 -25
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/LICENSE +0 -0
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/WHEEL +0 -0
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/top_level.txt +0 -0
ivoryos/__init__.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import importlib
|
|
2
|
-
import inspect
|
|
3
1
|
import os
|
|
4
2
|
import sys
|
|
5
3
|
from typing import Union
|
|
6
4
|
|
|
7
|
-
from flask import Flask, redirect, url_for,
|
|
5
|
+
from flask import Flask, redirect, url_for, g
|
|
8
6
|
|
|
9
7
|
from ivoryos.config import Config, get_config
|
|
10
8
|
from ivoryos.routes.auth.auth import auth, login_manager
|
|
@@ -20,14 +18,30 @@ from ivoryos.utils.script_runner import ScriptRunner
|
|
|
20
18
|
from ivoryos.version import __version__ as ivoryos_version
|
|
21
19
|
from importlib.metadata import entry_points
|
|
22
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()
|
|
23
31
|
|
|
24
32
|
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
25
33
|
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
34
|
+
app.register_blueprint(main, url_prefix=url_prefix)
|
|
35
|
+
app.register_blueprint(auth, url_prefix=url_prefix)
|
|
36
|
+
app.register_blueprint(control, url_prefix=url_prefix)
|
|
37
|
+
app.register_blueprint(design, url_prefix=url_prefix)
|
|
38
|
+
app.register_blueprint(database, url_prefix=url_prefix)
|
|
26
39
|
|
|
27
40
|
|
|
28
41
|
def create_app(config_class=None):
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
"""
|
|
43
|
+
create app, init database
|
|
44
|
+
"""
|
|
31
45
|
app.config.from_object(config_class or 'config.get_config()')
|
|
32
46
|
|
|
33
47
|
# Initialize extensions
|
|
@@ -82,18 +96,9 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
82
96
|
:param logger: logger name of list of logger names, defaults to None
|
|
83
97
|
:param logger_output_name: log file save name of logger, defaults to None, and will use "default.log"
|
|
84
98
|
:param enable_design: enable design canvas, database and workflow execution
|
|
85
|
-
:param stream_address:
|
|
86
99
|
"""
|
|
87
100
|
app = create_app(config_class=config or get_config()) # Create app instance using factory function
|
|
88
101
|
|
|
89
|
-
app.register_blueprint(main, url_prefix=url_prefix)
|
|
90
|
-
app.register_blueprint(auth, url_prefix=url_prefix)
|
|
91
|
-
app.register_blueprint(control, url_prefix=url_prefix)
|
|
92
|
-
|
|
93
|
-
if enable_design:
|
|
94
|
-
app.register_blueprint(design, url_prefix=url_prefix)
|
|
95
|
-
app.register_blueprint(database, url_prefix=url_prefix)
|
|
96
|
-
|
|
97
102
|
plugins = load_plugins(app, socketio)
|
|
98
103
|
|
|
99
104
|
def inject_nav_config():
|
|
@@ -115,10 +120,8 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
115
120
|
app.config["MODULE"] = module
|
|
116
121
|
app.config["OFF_LINE"] = False
|
|
117
122
|
global_config.deck = sys.modules[module]
|
|
118
|
-
# global_config.heinsight = HeinsightAPI("http://127.0.0.1:8080")
|
|
119
123
|
global_config.deck_snapshot = utils.create_deck_snapshot(global_config.deck,
|
|
120
124
|
output_path=app.config["DUMMY_DECK"], save=True)
|
|
121
|
-
# global_config.runner = ScriptRunner(globals())
|
|
122
125
|
else:
|
|
123
126
|
app.config["OFF_LINE"] = True
|
|
124
127
|
if model:
|
|
@@ -156,3 +159,5 @@ def load_plugins(app, socketio):
|
|
|
156
159
|
app.register_blueprint(getattr(plugin, entry_point.name), url_prefix=f"{url_prefix}/{entry_point.name}")
|
|
157
160
|
|
|
158
161
|
return plugin_names
|
|
162
|
+
|
|
163
|
+
|
|
@@ -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'])
|
|
@@ -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')
|
|
@@ -176,13 +176,56 @@ def save_as():
|
|
|
176
176
|
"""
|
|
177
177
|
if request.method == "POST":
|
|
178
178
|
run_name = request.form.get("run_name")
|
|
179
|
+
register_workflow = request.form.get("register_workflow")
|
|
179
180
|
exist_script = Script.query.get(run_name)
|
|
180
181
|
if not exist_script:
|
|
181
182
|
script = get_script_file()
|
|
182
183
|
script.save_as(run_name)
|
|
184
|
+
script.registered = register_workflow == "on"
|
|
183
185
|
script.author = session.get('user')
|
|
184
186
|
post_script_file(script)
|
|
185
187
|
publish()
|
|
186
188
|
else:
|
|
187
189
|
flash("Script name is already exist in database")
|
|
188
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'))
|
|
@@ -26,10 +26,11 @@
|
|
|
26
26
|
<tr>
|
|
27
27
|
<th scope="col">Workflow name</th>
|
|
28
28
|
<th scope="col">Deck </th>
|
|
29
|
-
<th scope="col">
|
|
29
|
+
<th scope="col">Editing</th>
|
|
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
34
|
<th scope="col"></th>
|
|
34
35
|
</tr>
|
|
35
36
|
</thead>
|
|
@@ -42,12 +43,13 @@
|
|
|
42
43
|
<td>{{ workflow.time_created }}</td>
|
|
43
44
|
<td>{{ workflow.last_modified }}</td>
|
|
44
45
|
<td>{{ workflow.author }}</td>
|
|
46
|
+
{# <td>{{ workflow.registered }}</td>#}
|
|
45
47
|
<td>
|
|
46
48
|
{#not workflow.status == "finalized" or#}
|
|
47
49
|
{% if session['user'] == 'admin' or session['user'] == workflow.author %}
|
|
48
50
|
<a href="{{ url_for('database.delete_workflow', workflow_name=workflow.name) }}">delete</a>
|
|
49
51
|
{% else %}
|
|
50
|
-
<a class="disabled-link"
|
|
52
|
+
<a class="disabled-link">delete</a>
|
|
51
53
|
{% endif %}
|
|
52
54
|
<td>
|
|
53
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 %}
|
ivoryos/routes/design/design.py
CHANGED
|
@@ -11,11 +11,13 @@ 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
|
-
create_form_from_action
|
|
17
|
+
create_form_from_action, create_all_builtin_forms
|
|
17
18
|
from ivoryos.utils.db_models import Script
|
|
18
19
|
from ivoryos.utils.script_runner import ScriptRunner
|
|
20
|
+
# from ivoryos.utils.utils import load_workflows
|
|
19
21
|
|
|
20
22
|
socketio = SocketIO()
|
|
21
23
|
design = Blueprint('design', __name__, template_folder='templates/design')
|
|
@@ -92,6 +94,9 @@ def experiment_builder(instrument=None):
|
|
|
92
94
|
"""
|
|
93
95
|
deck = global_config.deck
|
|
94
96
|
script = utils.get_script_file()
|
|
97
|
+
# load_workflows(script)
|
|
98
|
+
# registered_workflows = global_config.registered_workflows
|
|
99
|
+
|
|
95
100
|
if deck and script.deck is None:
|
|
96
101
|
script.deck = os.path.splitext(os.path.basename(deck.__file__))[
|
|
97
102
|
0] if deck.__name__ == "__main__" else deck.__name__
|
|
@@ -114,7 +119,10 @@ def experiment_builder(instrument=None):
|
|
|
114
119
|
|
|
115
120
|
functions = {}
|
|
116
121
|
if deck:
|
|
117
|
-
deck_variables = global_config.deck_snapshot.keys()
|
|
122
|
+
deck_variables = list(global_config.deck_snapshot.keys())
|
|
123
|
+
# deck_variables.insert(0, "registered_workflows")
|
|
124
|
+
deck_variables.insert(0, "flow_control")
|
|
125
|
+
|
|
118
126
|
else:
|
|
119
127
|
deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
|
|
120
128
|
deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
|
|
@@ -122,23 +130,31 @@ def experiment_builder(instrument=None):
|
|
|
122
130
|
if edit_action_info:
|
|
123
131
|
forms = create_form_from_action(edit_action_info, script=script)
|
|
124
132
|
elif instrument:
|
|
125
|
-
if instrument in ['if', 'while', 'variable', 'wait', 'repeat']:
|
|
126
|
-
|
|
133
|
+
# if instrument in ['if', 'while', 'variable', 'wait', 'repeat']:
|
|
134
|
+
# forms = create_builtin_form(instrument, script=script)
|
|
135
|
+
if instrument == 'flow_control':
|
|
136
|
+
forms = create_all_builtin_forms(script=script)
|
|
137
|
+
# elif instrument == 'registered_workflows':
|
|
138
|
+
# functions = utils._inspect_class(registered_workflows)
|
|
139
|
+
# # forms = create_workflow_forms(script=script)
|
|
140
|
+
# forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
141
|
+
elif instrument in global_config.defined_variables.keys():
|
|
142
|
+
_object = global_config.defined_variables.get(instrument)
|
|
143
|
+
functions = utils._inspect_class(_object)
|
|
144
|
+
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
127
145
|
else:
|
|
128
146
|
if deck:
|
|
129
147
|
functions = global_config.deck_snapshot.get(instrument, {})
|
|
130
148
|
elif pseudo_deck:
|
|
131
149
|
functions = pseudo_deck.get(instrument, {})
|
|
132
|
-
# print(function_metadata)
|
|
133
|
-
# functions = {key: data.get('signature', {}) for key, data in function_metadata.items()}
|
|
134
150
|
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
135
151
|
if request.method == 'POST' and "hidden_name" in request.form:
|
|
136
152
|
# all_kwargs = request.form.copy()
|
|
137
153
|
method_name = request.form.get("hidden_name", None)
|
|
138
154
|
# if method_name is not None:
|
|
139
155
|
form = forms.get(method_name)
|
|
156
|
+
insert_position = request.form.get("drop_target_id", None)
|
|
140
157
|
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
141
|
-
|
|
142
158
|
if form and form.validate_on_submit():
|
|
143
159
|
function_name = kwargs.pop("hidden_name")
|
|
144
160
|
save_data = kwargs.pop('return', '')
|
|
@@ -151,30 +167,57 @@ def experiment_builder(instrument=None):
|
|
|
151
167
|
"args": kwargs,
|
|
152
168
|
"return": save_data,
|
|
153
169
|
'arg_types': primitive_arg_types}
|
|
154
|
-
script.add_action(action=action)
|
|
170
|
+
script.add_action(action=action, insert_position=insert_position)
|
|
155
171
|
else:
|
|
156
172
|
flash(form.errors)
|
|
157
173
|
|
|
158
174
|
elif request.method == 'POST' and "builtin_name" in request.form:
|
|
159
|
-
|
|
160
|
-
|
|
175
|
+
function_name = request.form.get("builtin_name")
|
|
176
|
+
form = forms.get(function_name)
|
|
177
|
+
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
178
|
+
insert_position = request.form.get("drop_target_id", None)
|
|
179
|
+
|
|
180
|
+
if form.validate_on_submit():
|
|
161
181
|
# print(kwargs)
|
|
162
182
|
logic_type = kwargs.pop('builtin_name')
|
|
163
183
|
if 'variable' in kwargs:
|
|
164
184
|
try:
|
|
165
|
-
script.add_variable(**kwargs)
|
|
185
|
+
script.add_variable(**kwargs, insert_position=insert_position)
|
|
166
186
|
except ValueError:
|
|
167
187
|
flash("Invalid variable type")
|
|
168
188
|
else:
|
|
169
|
-
script.add_logic_action(logic_type=logic_type, **kwargs)
|
|
189
|
+
script.add_logic_action(logic_type=logic_type, **kwargs, insert_position=insert_position)
|
|
170
190
|
else:
|
|
171
|
-
flash(
|
|
191
|
+
flash(form.errors)
|
|
192
|
+
elif request.method == 'POST' and "workflow_name" in request.form:
|
|
193
|
+
workflow_name = request.form.get("workflow_name")
|
|
194
|
+
form = forms.get(workflow_name)
|
|
195
|
+
kwargs = {field.name: field.data for field in form if field.name != 'csrf_token'}
|
|
196
|
+
insert_position = request.form.get("drop_target_id", None)
|
|
197
|
+
|
|
198
|
+
if form.validate_on_submit():
|
|
199
|
+
# workflow_name = kwargs.pop('workflow_name')
|
|
200
|
+
save_data = kwargs.pop('return', '')
|
|
201
|
+
|
|
202
|
+
primitive_arg_types = utils.get_arg_type(kwargs, functions[workflow_name])
|
|
172
203
|
|
|
173
|
-
|
|
204
|
+
script.eval_list(kwargs, primitive_arg_types)
|
|
205
|
+
kwargs = script.validate_variables(kwargs)
|
|
206
|
+
action = {"instrument": instrument, "action": workflow_name,
|
|
207
|
+
"args": kwargs,
|
|
208
|
+
"return": save_data,
|
|
209
|
+
'arg_types': primitive_arg_types}
|
|
210
|
+
script.add_action(action=action, insert_position=insert_position)
|
|
211
|
+
script.add_workflow(**kwargs, insert_position=insert_position)
|
|
212
|
+
else:
|
|
213
|
+
flash(form.errors)
|
|
214
|
+
|
|
215
|
+
# toggle autofill, autofill doesn't apply to control flow ops
|
|
174
216
|
elif request.method == 'POST' and "autofill" in request.form:
|
|
175
217
|
autofill = not autofill
|
|
176
|
-
forms = create_form_from_pseudo(functions, autofill=autofill, script=script)
|
|
177
218
|
session['autofill'] = autofill
|
|
219
|
+
if not instrument == 'flow_control':
|
|
220
|
+
forms = create_form_from_pseudo(functions, autofill=autofill, script=script)
|
|
178
221
|
|
|
179
222
|
utils.post_script_file(script)
|
|
180
223
|
design_buttons = create_action_button(script)
|
|
@@ -318,7 +361,7 @@ def experiment_run():
|
|
|
318
361
|
run_name = script.validate_function_name(run_name)
|
|
319
362
|
runner.run_script(script=script, run_name=run_name, config=config, bo_args=bo_args,
|
|
320
363
|
logger=g.logger, socketio=g.socketio, repeat_count=repeat,
|
|
321
|
-
output_path=datapath
|
|
364
|
+
output_path=datapath, current_app=current_app._get_current_object()
|
|
322
365
|
)
|
|
323
366
|
if utils.check_config_duplicate(config):
|
|
324
367
|
flash(f"WARNING: Duplicate in config entries.")
|
|
@@ -329,7 +372,7 @@ def experiment_run():
|
|
|
329
372
|
return_list=return_list, config_list=config_list, config_file_list=config_file_list,
|
|
330
373
|
config_preview=config_preview, data_list=data_list, config_type_list=config_type_list,
|
|
331
374
|
no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons,
|
|
332
|
-
history=deck_list)
|
|
375
|
+
history=deck_list, pause_status=runner.pause_status())
|
|
333
376
|
|
|
334
377
|
|
|
335
378
|
@design.route("/toggle_script_type/<stype>")
|
|
@@ -483,7 +526,20 @@ def download(filetype):
|
|
|
483
526
|
outfile.write(json_object)
|
|
484
527
|
elif filetype == "python":
|
|
485
528
|
filepath = os.path.join(current_app.config["SCRIPT_FOLDER"], f"{run_name}.py")
|
|
486
|
-
|
|
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")
|
|
487
543
|
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
488
544
|
|
|
489
545
|
|