ivoryos 1.2.6a0__tar.gz → 1.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/PKG-INFO +1 -1
- ivoryos-1.2.8/ivoryos/__init__.py +13 -0
- ivoryos-1.2.8/ivoryos/app.py +94 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/api/api.py +1 -1
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/control/control.py +54 -28
- ivoryos-1.2.8/ivoryos/routes/control/control_file.py +33 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/control/templates/controllers.html +18 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/control/utils.py +2 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/design.py +11 -4
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/action_form.html +2 -2
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/instruments_panel.html +23 -1
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/experiment_run.html +1 -6
- ivoryos-1.2.6a0/ivoryos/__init__.py → ivoryos-1.2.8/ivoryos/server.py +18 -98
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/action_handlers.js +1 -1
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/socket_handler.js +39 -4
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/sortable_design.js +28 -11
- ivoryos-1.2.8/ivoryos/utils/client_proxy.py +288 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/db_models.py +40 -5
- ivoryos-1.2.8/ivoryos/utils/decorators.py +33 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/form.py +9 -2
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/global_config.py +10 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/script_runner.py +21 -2
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/task_runner.py +10 -5
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/utils.py +20 -1
- ivoryos-1.2.8/ivoryos/version.py +1 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos.egg-info/PKG-INFO +1 -1
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos.egg-info/SOURCES.txt +3 -0
- ivoryos-1.2.6a0/ivoryos/routes/control/control_file.py +0 -36
- ivoryos-1.2.6a0/ivoryos/utils/client_proxy.py +0 -57
- ivoryos-1.2.6a0/ivoryos/version.py +0 -1
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/LICENSE +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/README.md +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/config.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/optimizer/ax_optimizer.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/optimizer/base_optimizer.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/optimizer/baybe_optimizer.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/optimizer/registry.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/auth/auth.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/auth/templates/login.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/auth/templates/signup.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/control/control_new_device.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/control/templates/controllers_new.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/data/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/data/data.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/data/templates/components/step_card.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/data/templates/workflow_database.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/data/templates/workflow_view.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/design_file.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/design_step.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/actions_panel.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/autofill_toggle.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/canvas.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/canvas_footer.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/canvas_header.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/canvas_main.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/deck_selector.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/edit_action_form.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/modals/drop_modal.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/modals/json_modal.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/modals/new_script_modal.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/modals/rename_modal.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/modals/saveas_modal.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/modals.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/python_code_overlay.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/sidebar.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/text_to_code_panel.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/experiment_builder.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/execute.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/execute_file.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/error_modal.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/logging_panel.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/progress_panel.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/run_panel.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/run_tabs.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/tab_bayesian.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/tab_configuration.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/execute/templates/components/tab_repeat.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/library/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/library/library.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/library/templates/library.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/main/main.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/main/templates/help.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/main/templates/home.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/socket_handlers.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/db_delete.js +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/script_metadata.js +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/js/ui_state.js +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/logo.webp +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/static/style.css +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/templates/base.html +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/bo_campaign.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/py_to_json.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/utils/serilize.py +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos.egg-info/requires.txt +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos.egg-info/top_level.txt +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/pyproject.toml +0 -0
- {ivoryos-1.2.6a0 → ivoryos-1.2.8}/setup.cfg +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ivoryos.server import run
|
|
2
|
+
from ivoryos.optimizer.registry import OPTIMIZER_REGISTRY
|
|
3
|
+
from ivoryos.version import __version__ as ivoryos_version
|
|
4
|
+
from ivoryos.utils.decorators import block, BUILDING_BLOCKS
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"block",
|
|
9
|
+
"BUILDING_BLOCKS",
|
|
10
|
+
"OPTIMIZER_REGISTRY",
|
|
11
|
+
"run",
|
|
12
|
+
"ivoryos_version",
|
|
13
|
+
]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
4
|
+
from flask import Flask, session, g, redirect, url_for
|
|
5
|
+
from flask_login import AnonymousUserMixin
|
|
6
|
+
|
|
7
|
+
from ivoryos.utils import utils
|
|
8
|
+
from ivoryos.utils.db_models import db
|
|
9
|
+
from ivoryos.config import Config, get_config
|
|
10
|
+
from ivoryos.routes.auth.auth import auth, login_manager
|
|
11
|
+
from ivoryos.routes.control.control import control
|
|
12
|
+
from ivoryos.routes.data.data import data
|
|
13
|
+
from ivoryos.routes.library.library import library
|
|
14
|
+
from ivoryos.routes.design.design import design
|
|
15
|
+
from ivoryos.routes.execute.execute import execute
|
|
16
|
+
from ivoryos.routes.api.api import api
|
|
17
|
+
from ivoryos.socket_handlers import socketio
|
|
18
|
+
from ivoryos.routes.main.main import main
|
|
19
|
+
from ivoryos.version import __version__ as ivoryos_version
|
|
20
|
+
|
|
21
|
+
def create_app(config_class=None):
|
|
22
|
+
"""
|
|
23
|
+
create app, init database
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
27
|
+
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
28
|
+
app.register_blueprint(main, url_prefix=url_prefix)
|
|
29
|
+
app.register_blueprint(auth, url_prefix=f'{url_prefix}/{auth.name}')
|
|
30
|
+
app.register_blueprint(library, url_prefix=f'{url_prefix}/{library.name}')
|
|
31
|
+
app.register_blueprint(control, url_prefix=f'{url_prefix}/instruments')
|
|
32
|
+
app.register_blueprint(design, url_prefix=f'{url_prefix}')
|
|
33
|
+
app.register_blueprint(execute, url_prefix=f'{url_prefix}')
|
|
34
|
+
app.register_blueprint(data, url_prefix=f'{url_prefix}')
|
|
35
|
+
app.register_blueprint(api, url_prefix=f'{url_prefix}/{api.name}')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
app.config.from_object(config_class or 'config.get_config()')
|
|
39
|
+
os.makedirs(app.config["OUTPUT_FOLDER"], exist_ok=True)
|
|
40
|
+
# Initialize extensions
|
|
41
|
+
socketio.init_app(app, cors_allowed_origins="*", cookie=None)
|
|
42
|
+
login_manager.init_app(app)
|
|
43
|
+
login_manager.login_view = "auth.login"
|
|
44
|
+
db.init_app(app)
|
|
45
|
+
|
|
46
|
+
# Create database tables
|
|
47
|
+
with app.app_context():
|
|
48
|
+
db.create_all()
|
|
49
|
+
|
|
50
|
+
# Additional setup
|
|
51
|
+
utils.create_gui_dir(app.config['OUTPUT_FOLDER'])
|
|
52
|
+
|
|
53
|
+
# logger_list = app.config["LOGGERS"]
|
|
54
|
+
logger_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["LOGGERS_PATH"])
|
|
55
|
+
logger = utils.start_logger(socketio, 'gui_logger', logger_path)
|
|
56
|
+
|
|
57
|
+
@app.before_request
|
|
58
|
+
def before_request():
|
|
59
|
+
"""
|
|
60
|
+
Called before
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
g.logger = logger
|
|
64
|
+
g.socketio = socketio
|
|
65
|
+
session.permanent = False
|
|
66
|
+
# DEMO_MODE: Simulate logged-in user per session
|
|
67
|
+
if app.config.get("DEMO_MODE", False):
|
|
68
|
+
if "demo_user_id" not in session:
|
|
69
|
+
session["demo_user_id"] = f"demo_{str(uuid.uuid4())[:8]}"
|
|
70
|
+
|
|
71
|
+
class SessionDemoUser(AnonymousUserMixin):
|
|
72
|
+
@property
|
|
73
|
+
def is_authenticated(self):
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
def get_id(self):
|
|
77
|
+
return session.get("demo_user_id")
|
|
78
|
+
|
|
79
|
+
login_manager.anonymous_user = SessionDemoUser
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@app.route('/')
|
|
84
|
+
def redirect_to_prefix():
|
|
85
|
+
return redirect(url_for('main.index', version=ivoryos_version)) # Assuming 'index' is a route in your blueprint
|
|
86
|
+
|
|
87
|
+
@app.template_filter('format_name')
|
|
88
|
+
def format_name(name):
|
|
89
|
+
name = name.split(".")[-1]
|
|
90
|
+
text = ' '.join(word for word in name.split('_'))
|
|
91
|
+
return text.capitalize()
|
|
92
|
+
|
|
93
|
+
# app.config.setdefault("DEMO_MODE", False)
|
|
94
|
+
return app
|
|
@@ -37,7 +37,7 @@ def backend_control(instrument: str=None):
|
|
|
37
37
|
forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
|
|
38
38
|
|
|
39
39
|
if request.method == 'POST':
|
|
40
|
-
method_name = request.
|
|
40
|
+
method_name = request.json.get("hidden_name", None)
|
|
41
41
|
form = forms.get(method_name, None)
|
|
42
42
|
if form:
|
|
43
43
|
kwargs = {field.name: field.data for field in form if field.name not in ['csrf_token', 'hidden_name']}
|
|
@@ -5,7 +5,7 @@ from ivoryos.routes.control.control_file import control_file
|
|
|
5
5
|
from ivoryos.routes.control.control_new_device import control_temp
|
|
6
6
|
from ivoryos.routes.control.utils import post_session_by_instrument, get_session_by_instrument, find_instrument_by_name
|
|
7
7
|
from ivoryos.utils.global_config import GlobalConfig
|
|
8
|
-
from ivoryos.utils.form import create_form_from_module
|
|
8
|
+
from ivoryos.utils.form import create_form_from_module, create_form_from_pseudo
|
|
9
9
|
from ivoryos.utils.task_runner import TaskRunner
|
|
10
10
|
|
|
11
11
|
global_config = GlobalConfig()
|
|
@@ -21,7 +21,7 @@ control.register_blueprint(control_temp)
|
|
|
21
21
|
@control.route("/", strict_slashes=False, methods=["GET", "POST"])
|
|
22
22
|
@control.route("/<string:instrument>", strict_slashes=False, methods=["GET", "POST"])
|
|
23
23
|
@login_required
|
|
24
|
-
def deck_controllers():
|
|
24
|
+
def deck_controllers(instrument: str = None):
|
|
25
25
|
"""
|
|
26
26
|
.. :quickref: Direct Control; device (instruments) and methods
|
|
27
27
|
|
|
@@ -44,43 +44,69 @@ def deck_controllers():
|
|
|
44
44
|
:status 200: render template with instruments and methods
|
|
45
45
|
|
|
46
46
|
"""
|
|
47
|
-
|
|
48
|
-
temp_variables = global_config.defined_variables.keys()
|
|
49
|
-
instrument = request.args.get('instrument')
|
|
47
|
+
instrument = instrument or request.args.get("instrument")
|
|
50
48
|
forms = None
|
|
51
49
|
if instrument:
|
|
52
50
|
inst_object = find_instrument_by_name(instrument)
|
|
53
|
-
|
|
51
|
+
if instrument.startswith("blocks"):
|
|
52
|
+
forms = create_form_from_pseudo(pseudo=inst_object, autofill=False, design=False)
|
|
53
|
+
else:
|
|
54
|
+
forms = create_form_from_module(sdl_module=inst_object, autofill=False, design=False)
|
|
54
55
|
order = get_session_by_instrument('card_order', instrument)
|
|
55
56
|
hidden_functions = get_session_by_instrument('hidden_functions', instrument)
|
|
56
|
-
functions = list(
|
|
57
|
+
functions = list(forms.keys())
|
|
57
58
|
for function in functions:
|
|
58
59
|
if function not in hidden_functions and function not in order:
|
|
59
60
|
order.append(function)
|
|
60
61
|
post_session_by_instrument('card_order', instrument, order)
|
|
61
|
-
forms = {name:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
forms = {name: forms[name] for name in order if name in forms}
|
|
63
|
+
|
|
64
|
+
if request.method == "POST":
|
|
65
|
+
if not forms:
|
|
66
|
+
return jsonify({"success": False, "error": "Instrument not found"}), 404
|
|
67
|
+
|
|
68
|
+
payload = request.get_json() if request.is_json else request.form.to_dict()
|
|
69
|
+
method_name = payload.pop("hidden_name", None)
|
|
70
|
+
form = forms.get(method_name)
|
|
71
|
+
|
|
72
|
+
if not form:
|
|
73
|
+
return jsonify({"success": False, "error": f"Method {method_name} not found"}), 404
|
|
74
|
+
|
|
75
|
+
# Extract kwargs
|
|
76
|
+
if request.is_json:
|
|
77
|
+
kwargs = {k: v for k, v in payload.items() if k not in ["csrf_token", "hidden_wait"]}
|
|
78
|
+
else:
|
|
79
|
+
kwargs = {field.name: field.data for field in form if field.name not in ["csrf_token", "hidden_name"]}
|
|
80
|
+
|
|
81
|
+
wait = str(payload.get("hidden_wait", "true")).lower() == "true"
|
|
82
|
+
|
|
83
|
+
output = runner.run_single_step(
|
|
84
|
+
component=instrument, method=method_name, kwargs=kwargs, wait=wait,
|
|
85
|
+
current_app=current_app._get_current_object()
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if request.is_json:
|
|
89
|
+
return jsonify(output)
|
|
90
|
+
else:
|
|
91
|
+
if output.get("success"):
|
|
92
|
+
flash(f"Run Success! Output: {output.get('output', 'None')}")
|
|
75
93
|
else:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
94
|
+
flash(f"Run Error! {output.get('output', 'Unknown error occurred.')}", "error")
|
|
95
|
+
|
|
96
|
+
# GET request → render web form or return snapshot for API
|
|
97
|
+
if request.is_json or request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
98
|
+
|
|
99
|
+
snapshot = global_config.deck_snapshot.copy()
|
|
100
|
+
for instrument_key, instrument_data in snapshot.items():
|
|
101
|
+
for function_key, function_data in instrument_data.items():
|
|
102
|
+
function_data["signature"] = str(function_data["signature"])
|
|
103
|
+
return jsonify(snapshot)
|
|
104
|
+
|
|
80
105
|
return render_template(
|
|
81
|
-
|
|
82
|
-
defined_variables=
|
|
83
|
-
|
|
106
|
+
"controllers.html",
|
|
107
|
+
defined_variables=global_config.deck_snapshot.keys(),
|
|
108
|
+
block_variables=global_config.building_blocks.keys(),
|
|
109
|
+
temp_variables=global_config.defined_variables.keys(),
|
|
84
110
|
instrument=instrument,
|
|
85
111
|
forms=forms,
|
|
86
112
|
session=session
|
|
@@ -0,0 +1,33 @@
|
|
|
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 ProxyGenerator
|
|
6
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
7
|
+
|
|
8
|
+
global_config = GlobalConfig()
|
|
9
|
+
|
|
10
|
+
control_file = Blueprint('file', __name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@control_file.route("/files/proxy", strict_slashes=False)
|
|
15
|
+
@login_required
|
|
16
|
+
def download_proxy():
|
|
17
|
+
"""
|
|
18
|
+
.. :quickref: Direct Control Files; Download proxy Python interface
|
|
19
|
+
|
|
20
|
+
download proxy Python interface
|
|
21
|
+
|
|
22
|
+
.. http:get:: /files/proxy
|
|
23
|
+
"""
|
|
24
|
+
generator = ProxyGenerator(request.url_root)
|
|
25
|
+
snapshot = global_config.deck_snapshot.copy()
|
|
26
|
+
|
|
27
|
+
filepath = generator.generate_from_flask_route(
|
|
28
|
+
snapshot,
|
|
29
|
+
request.url_root,
|
|
30
|
+
current_app.config["OUTPUT_FOLDER"]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
return send_file(os.path.abspath(filepath), as_attachment=True)
|
|
@@ -49,6 +49,24 @@
|
|
|
49
49
|
</div>
|
|
50
50
|
</div>
|
|
51
51
|
{% endif %}
|
|
52
|
+
|
|
53
|
+
{% if block_variables %}
|
|
54
|
+
<div class="mb-4">
|
|
55
|
+
<h6 class="fw-bold text-secondary mb-2" style="letter-spacing: 1px;">Methods</h6>
|
|
56
|
+
<div class="list-group">
|
|
57
|
+
{% for inst in block_variables %}
|
|
58
|
+
<a class="list-group-item list-group-item-action d-flex align-items-center {% if instrument == inst %}active bg-warning text-dark border-0{% else %}bg-light{% endif %}"
|
|
59
|
+
href="{{ url_for('control.deck_controllers') }}?instrument={{ inst }}"
|
|
60
|
+
style="border-radius: 0.5rem; margin-bottom: 0.5rem; transition: background 0.2s;">
|
|
61
|
+
<span class="flex-grow-1">{{ inst | format_name }}</span>
|
|
62
|
+
{% if instrument == inst %}
|
|
63
|
+
<span class="ms-auto">></span>
|
|
64
|
+
{% endif %}
|
|
65
|
+
</a>
|
|
66
|
+
{% endfor %}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
{% endif %}
|
|
52
70
|
<!-- Action Buttons -->
|
|
53
71
|
<div class="mb-4">
|
|
54
72
|
<a href="{{ url_for('control.file.download_proxy', filetype='proxy') }}" class="btn btn-outline-primary w-100 mb-2">
|
|
@@ -13,6 +13,8 @@ def find_instrument_by_name(name: str):
|
|
|
13
13
|
if name.startswith("deck"):
|
|
14
14
|
name = name.replace("deck.", "")
|
|
15
15
|
return getattr(global_config.deck, name)
|
|
16
|
+
elif name.startswith("blocks"):
|
|
17
|
+
return global_config.building_blocks[name]
|
|
16
18
|
elif name in global_config.defined_variables:
|
|
17
19
|
return global_config.defined_variables[name]
|
|
18
20
|
elif name in globals():
|
|
@@ -35,6 +35,9 @@ def _create_forms(instrument, script, autofill, pseudo_deck = None):
|
|
|
35
35
|
_object = global_config.defined_variables.get(instrument)
|
|
36
36
|
functions = utils._inspect_class(_object)
|
|
37
37
|
forms = create_form_from_pseudo(pseudo=functions, autofill=autofill, script=script)
|
|
38
|
+
elif instrument.startswith("blocks"):
|
|
39
|
+
forms = create_form_from_pseudo(pseudo=global_config.building_blocks[instrument], autofill=autofill, script=script)
|
|
40
|
+
functions = global_config.building_blocks[instrument]
|
|
38
41
|
else:
|
|
39
42
|
if deck:
|
|
40
43
|
functions = global_config.deck_snapshot.get(instrument, {})
|
|
@@ -92,7 +95,7 @@ def experiment_builder():
|
|
|
92
95
|
|
|
93
96
|
return render_template('experiment_builder.html', off_line=off_line, history=deck_list,
|
|
94
97
|
script=script, defined_variables=deck_variables, buttons_dict=design_buttons,
|
|
95
|
-
local_variables=global_config.defined_variables)
|
|
98
|
+
local_variables=global_config.defined_variables, block_variables=global_config.building_blocks)
|
|
96
99
|
|
|
97
100
|
|
|
98
101
|
@design.route("/draft/meta", methods=["PATCH"])
|
|
@@ -192,7 +195,8 @@ def update_ui_state():
|
|
|
192
195
|
deck_variables = list(pseudo_deck.keys()) if pseudo_deck else []
|
|
193
196
|
deck_variables.remove("deck_name") if len(deck_variables) > 0 else deck_variables
|
|
194
197
|
html = render_template("components/sidebar.html", history=deck_list,
|
|
195
|
-
defined_variables=deck_variables, local_variables = global_config.defined_variables
|
|
198
|
+
defined_variables=deck_variables, local_variables = global_config.defined_variables,
|
|
199
|
+
block_variables=global_config.building_blocks)
|
|
196
200
|
return jsonify({"html": html})
|
|
197
201
|
return jsonify({"error": "Invalid request"}), 400
|
|
198
202
|
|
|
@@ -310,6 +314,7 @@ def methods_handler(instrument: str = ''):
|
|
|
310
314
|
|
|
311
315
|
success = True
|
|
312
316
|
msg = ""
|
|
317
|
+
request.form
|
|
313
318
|
if "hidden_name" in request.form:
|
|
314
319
|
method_name = request.form.get("hidden_name", None)
|
|
315
320
|
form = forms.get(method_name) if forms else None
|
|
@@ -322,7 +327,7 @@ def methods_handler(instrument: str = ''):
|
|
|
322
327
|
primitive_arg_types = utils.get_arg_type(kwargs, functions[function_name])
|
|
323
328
|
|
|
324
329
|
# todo
|
|
325
|
-
print(primitive_arg_types)
|
|
330
|
+
# print(primitive_arg_types)
|
|
326
331
|
|
|
327
332
|
script.eval_list(kwargs, primitive_arg_types)
|
|
328
333
|
kwargs = script.validate_variables(kwargs)
|
|
@@ -422,7 +427,9 @@ def get_operation_sidebar(instrument: str = ''):
|
|
|
422
427
|
# edit_action_info = session.get("edit_action")
|
|
423
428
|
html = render_template("components/sidebar.html", off_line=off_line, history=deck_list,
|
|
424
429
|
defined_variables=deck_variables,
|
|
425
|
-
local_variables=global_config.defined_variables
|
|
430
|
+
local_variables=global_config.defined_variables,
|
|
431
|
+
block_variables=global_config.building_blocks,
|
|
432
|
+
)
|
|
426
433
|
return jsonify({"html": html})
|
|
427
434
|
|
|
428
435
|
|
{ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/action_form.html
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{# Action form component #}
|
|
2
|
-
<div class="accordion-item design-control"
|
|
3
|
-
<h2 class="accordion-header">
|
|
2
|
+
<div class="accordion-item design-control">
|
|
3
|
+
<h2 class="accordion-header" >
|
|
4
4
|
<button class="accordion-button collapsed draggable-action"
|
|
5
5
|
type="button" data-bs-toggle="collapse"
|
|
6
6
|
data-bs-target="#{{name}}" aria-expanded="false"
|
{ivoryos-1.2.6a0 → ivoryos-1.2.8}/ivoryos/routes/design/templates/components/instruments_panel.html
RENAMED
|
@@ -39,7 +39,29 @@
|
|
|
39
39
|
</ul>
|
|
40
40
|
</div>
|
|
41
41
|
</div>
|
|
42
|
-
|
|
42
|
+
{% if block_variables %}
|
|
43
|
+
<div class="accordion-item design-control">
|
|
44
|
+
<h5 class="accordion-header">
|
|
45
|
+
<button class="accordion-button" data-bs-toggle="collapse" data-bs-target="#block" role="button" aria-expanded="false" aria-controls="collapseExample">
|
|
46
|
+
Methods
|
|
47
|
+
</button>
|
|
48
|
+
</h5>
|
|
49
|
+
<div class="accordion-collapse collapse show" id="block">
|
|
50
|
+
<ul class="list-group">
|
|
51
|
+
{% for category in block_variables %}
|
|
52
|
+
<button class="list-group-item list-group-item-action"
|
|
53
|
+
type="button"
|
|
54
|
+
name="device"
|
|
55
|
+
value="{{category}}"
|
|
56
|
+
data-get-url="{{ url_for('design.get_operation_sidebar', instrument=category) }}"
|
|
57
|
+
onclick="updateInstrumentPanel(this)">
|
|
58
|
+
{{ category|format_name }}
|
|
59
|
+
</button>
|
|
60
|
+
{% endfor%}
|
|
61
|
+
</ul>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
{% endif %}
|
|
43
65
|
{% if local_variables %}
|
|
44
66
|
<div class="accordion-item design-control">
|
|
45
67
|
<h5 class="accordion-header">
|
|
@@ -15,12 +15,7 @@
|
|
|
15
15
|
};
|
|
16
16
|
</script>
|
|
17
17
|
{% endif %}
|
|
18
|
-
|
|
19
|
-
⚠️ <strong>Note:</strong> Real-time progress tracking in this demo is currently <strong>not working</strong>. The system may report busy if multiple runs are initiated.
|
|
20
|
-
<br>
|
|
21
|
-
Workflow will still work, results can be tracked via the <strong>Data</strong> tab.
|
|
22
|
-
<br>
|
|
23
|
-
</div>
|
|
18
|
+
|
|
24
19
|
<div class="row">
|
|
25
20
|
{% include 'components/run_panel.html' %}
|
|
26
21
|
{% include 'components/progress_panel.html' %}
|
|
@@ -1,37 +1,24 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sqlite3
|
|
2
3
|
import sys
|
|
3
|
-
import uuid
|
|
4
4
|
from typing import Union
|
|
5
5
|
|
|
6
|
-
from flask import
|
|
7
|
-
from flask_login import AnonymousUserMixin
|
|
6
|
+
from flask import Blueprint
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
from sqlalchemy import Engine, event
|
|
10
9
|
|
|
10
|
+
# from ivoryos import BUILDING_BLOCKS
|
|
11
|
+
from ivoryos.app import create_app
|
|
11
12
|
from ivoryos.config import Config, get_config
|
|
12
|
-
from ivoryos.
|
|
13
|
-
from ivoryos.routes.
|
|
14
|
-
from ivoryos.routes.
|
|
15
|
-
from ivoryos.routes.library.library import library
|
|
16
|
-
from ivoryos.routes.design.design import design
|
|
17
|
-
from ivoryos.routes.execute.execute import execute
|
|
18
|
-
from ivoryos.routes.api.api import api
|
|
13
|
+
from ivoryos.optimizer.registry import OPTIMIZER_REGISTRY
|
|
14
|
+
from ivoryos.routes.auth.auth import login_manager
|
|
15
|
+
from ivoryos.routes.control.control import global_config
|
|
19
16
|
from ivoryos.socket_handlers import socketio
|
|
20
|
-
from ivoryos.routes.main.main import main
|
|
21
|
-
# from ivoryos.routes.monitor.monitor import monitor
|
|
22
17
|
from ivoryos.utils import utils
|
|
23
18
|
from ivoryos.utils.db_models import db, User
|
|
24
|
-
from ivoryos.utils.global_config import GlobalConfig
|
|
25
|
-
from ivoryos.optimizer.registry import OPTIMIZER_REGISTRY
|
|
26
|
-
from ivoryos.utils.script_runner import ScriptRunner
|
|
27
|
-
from ivoryos.version import __version__ as ivoryos_version
|
|
28
|
-
# from importlib.metadata import entry_points
|
|
29
19
|
|
|
30
|
-
global_config = GlobalConfig()
|
|
31
|
-
from sqlalchemy import event
|
|
32
|
-
from sqlalchemy.engine import Engine
|
|
33
|
-
import sqlite3
|
|
34
20
|
|
|
21
|
+
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
35
22
|
|
|
36
23
|
@event.listens_for(Engine, "connect")
|
|
37
24
|
def enforce_sqlite_foreign_keys(dbapi_connection, connection_record):
|
|
@@ -41,16 +28,6 @@ def enforce_sqlite_foreign_keys(dbapi_connection, connection_record):
|
|
|
41
28
|
cursor.close()
|
|
42
29
|
|
|
43
30
|
|
|
44
|
-
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
45
|
-
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
46
|
-
app.register_blueprint(main, url_prefix=url_prefix)
|
|
47
|
-
app.register_blueprint(auth, url_prefix=f'{url_prefix}/{auth.name}')
|
|
48
|
-
app.register_blueprint(library, url_prefix=f'{url_prefix}/{library.name}')
|
|
49
|
-
app.register_blueprint(control, url_prefix=f'{url_prefix}/instruments')
|
|
50
|
-
app.register_blueprint(design, url_prefix=f'{url_prefix}')
|
|
51
|
-
app.register_blueprint(execute, url_prefix=f'{url_prefix}')
|
|
52
|
-
app.register_blueprint(data, url_prefix=f'{url_prefix}')
|
|
53
|
-
app.register_blueprint(api, url_prefix=f'{url_prefix}/{api.name}')
|
|
54
31
|
|
|
55
32
|
@login_manager.user_loader
|
|
56
33
|
def load_user(user_id):
|
|
@@ -62,68 +39,8 @@ def load_user(user_id):
|
|
|
62
39
|
return db.session.get(User, user_id)
|
|
63
40
|
|
|
64
41
|
|
|
65
|
-
def create_app(config_class=None):
|
|
66
|
-
"""
|
|
67
|
-
create app, init database
|
|
68
|
-
"""
|
|
69
|
-
app.config.from_object(config_class or 'config.get_config()')
|
|
70
|
-
os.makedirs(app.config["OUTPUT_FOLDER"], exist_ok=True)
|
|
71
|
-
# Initialize extensions
|
|
72
|
-
socketio.init_app(app, cors_allowed_origins="*", cookie=None)
|
|
73
|
-
login_manager.init_app(app)
|
|
74
|
-
login_manager.login_view = "auth.login"
|
|
75
|
-
db.init_app(app)
|
|
76
|
-
|
|
77
|
-
# Create database tables
|
|
78
|
-
with app.app_context():
|
|
79
|
-
db.create_all()
|
|
80
|
-
|
|
81
|
-
# Additional setup
|
|
82
|
-
utils.create_gui_dir(app.config['OUTPUT_FOLDER'])
|
|
83
|
-
|
|
84
|
-
# logger_list = app.config["LOGGERS"]
|
|
85
|
-
logger_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["LOGGERS_PATH"])
|
|
86
|
-
logger = utils.start_logger(socketio, 'gui_logger', logger_path)
|
|
87
|
-
|
|
88
|
-
@app.before_request
|
|
89
|
-
def before_request():
|
|
90
|
-
"""
|
|
91
|
-
Called before
|
|
92
|
-
|
|
93
|
-
"""
|
|
94
|
-
g.logger = logger
|
|
95
|
-
g.socketio = socketio
|
|
96
|
-
session.permanent = False
|
|
97
|
-
# DEMO_MODE: Simulate logged-in user per session
|
|
98
|
-
if app.config.get("DEMO_MODE", False):
|
|
99
|
-
if "demo_user_id" not in session:
|
|
100
|
-
session["demo_user_id"] = f"demo_{str(uuid.uuid4())[:8]}"
|
|
101
|
-
|
|
102
|
-
class SessionDemoUser(AnonymousUserMixin):
|
|
103
|
-
@property
|
|
104
|
-
def is_authenticated(self):
|
|
105
|
-
return True
|
|
106
|
-
|
|
107
|
-
def get_id(self):
|
|
108
|
-
return session.get("demo_user_id")
|
|
109
|
-
|
|
110
|
-
login_manager.anonymous_user = SessionDemoUser
|
|
111
|
-
|
|
112
42
|
|
|
113
43
|
|
|
114
|
-
@app.route('/')
|
|
115
|
-
def redirect_to_prefix():
|
|
116
|
-
return redirect(url_for('main.index', version=ivoryos_version)) # Assuming 'index' is a route in your blueprint
|
|
117
|
-
|
|
118
|
-
@app.template_filter('format_name')
|
|
119
|
-
def format_name(name):
|
|
120
|
-
name = name.split(".")[-1]
|
|
121
|
-
text = ' '.join(word for word in name.split('_'))
|
|
122
|
-
return text.capitalize()
|
|
123
|
-
|
|
124
|
-
# app.config.setdefault("DEMO_MODE", False)
|
|
125
|
-
return app
|
|
126
|
-
|
|
127
44
|
|
|
128
45
|
def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, model=None,
|
|
129
46
|
config: Config = None,
|
|
@@ -182,6 +99,8 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
182
99
|
save=True,
|
|
183
100
|
exclude_names=exclude_names
|
|
184
101
|
)
|
|
102
|
+
global_config.building_blocks = utils.create_block_snapshot()
|
|
103
|
+
|
|
185
104
|
else:
|
|
186
105
|
app.config["OFF_LINE"] = True
|
|
187
106
|
if model:
|
|
@@ -200,12 +119,12 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
200
119
|
for log in logger:
|
|
201
120
|
utils.start_logger(socketio, log_filename=logger_path, logger_name=log)
|
|
202
121
|
|
|
203
|
-
# in case Python 3.12 or higher doesn't log URL
|
|
204
|
-
if sys.version_info >= (3, 12):
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
122
|
+
# TODO in case Python 3.12 or higher doesn't log URL
|
|
123
|
+
# if sys.version_info >= (3, 12):
|
|
124
|
+
# ip = utils.get_local_ip()
|
|
125
|
+
# print(f"Server running at http://localhost:{port}")
|
|
126
|
+
# if not ip == "127.0.0.1":
|
|
127
|
+
# print(f"Server running at http://{ip}:{port}")
|
|
209
128
|
socketio.run(app, host=host, port=port, debug=debug, use_reloader=False, allow_unsafe_werkzeug=True)
|
|
210
129
|
# return app
|
|
211
130
|
|
|
@@ -246,3 +165,4 @@ def load_plugins(blueprints: Union[list, Blueprint], app, socketio):
|
|
|
246
165
|
plugin_names.append(blueprint.name)
|
|
247
166
|
app.register_blueprint(blueprint, url_prefix=f"{url_prefix}/{blueprint.name}")
|
|
248
167
|
return plugin_names
|
|
168
|
+
|
|
@@ -15,7 +15,7 @@ function saveWorkflow(link) {
|
|
|
15
15
|
.then(data => {
|
|
16
16
|
if (data.success) {
|
|
17
17
|
// flash a success message
|
|
18
|
-
flash("Workflow saved successfully", "success");
|
|
18
|
+
// flash("Workflow saved successfully", "success");
|
|
19
19
|
window.location.reload(); // or update the UI dynamically
|
|
20
20
|
} else {
|
|
21
21
|
alert("Failed to save workflow: " + data.error);
|
|
@@ -37,13 +37,43 @@ document.addEventListener("DOMContentLoaded", function() {
|
|
|
37
37
|
console.error("Error received:", errorData);
|
|
38
38
|
var progressBar = document.getElementById('progress-bar-inner');
|
|
39
39
|
|
|
40
|
-
progressBar.classList.remove('bg-success');
|
|
41
|
-
progressBar.classList.add('bg-danger');
|
|
42
|
-
|
|
40
|
+
progressBar.classList.remove('bg-success', 'bg-warning');
|
|
41
|
+
progressBar.classList.add('bg-danger');
|
|
42
|
+
|
|
43
43
|
var errorModal = new bootstrap.Modal(document.getElementById('error-modal'));
|
|
44
|
-
document.getElementById('
|
|
44
|
+
document.getElementById('errorModalLabel').innerText = "Error Detected";
|
|
45
|
+
document.getElementById('error-message').innerText =
|
|
46
|
+
"An error occurred: " + errorData.message;
|
|
47
|
+
|
|
48
|
+
// Show all buttons again
|
|
49
|
+
document.getElementById('retry-btn').style.display = "inline-block";
|
|
50
|
+
document.getElementById('continue-btn').style.display = "inline-block";
|
|
51
|
+
document.getElementById('stop-btn').style.display = "inline-block";
|
|
52
|
+
|
|
45
53
|
errorModal.show();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
socket.on('human_intervention', function(data) {
|
|
58
|
+
console.warn("Human intervention required:", data);
|
|
59
|
+
var progressBar = document.getElementById('progress-bar-inner');
|
|
46
60
|
|
|
61
|
+
// Set progress bar to yellow
|
|
62
|
+
progressBar.classList.remove('bg-success', 'bg-danger');
|
|
63
|
+
progressBar.classList.add('bg-warning');
|
|
64
|
+
|
|
65
|
+
// Reuse error modal but update content
|
|
66
|
+
var errorModal = new bootstrap.Modal(document.getElementById('error-modal'));
|
|
67
|
+
document.getElementById('errorModalLabel').innerText = "Human Intervention Required";
|
|
68
|
+
document.getElementById('error-message').innerText =
|
|
69
|
+
"Workflow paused: " + (data.message || "Please check and manually resume.");
|
|
70
|
+
|
|
71
|
+
// Optionally: hide retry button, since it may not apply
|
|
72
|
+
document.getElementById('retry-btn').style.display = "none";
|
|
73
|
+
document.getElementById('continue-btn').style.display = "inline-block";
|
|
74
|
+
document.getElementById('stop-btn').style.display = "inline-block";
|
|
75
|
+
|
|
76
|
+
errorModal.show();
|
|
47
77
|
});
|
|
48
78
|
|
|
49
79
|
// Handle Pause/Resume Button
|
|
@@ -71,6 +101,11 @@ document.addEventListener("DOMContentLoaded", function() {
|
|
|
71
101
|
document.getElementById('continue-btn').addEventListener('click', function() {
|
|
72
102
|
socket.emit('pause'); // Resume execution
|
|
73
103
|
console.log("Execution resumed.");
|
|
104
|
+
|
|
105
|
+
// Reset progress bar color to running (blue)
|
|
106
|
+
var progressBar = document.getElementById('progress-bar-inner');
|
|
107
|
+
progressBar.classList.remove('bg-danger', 'bg-warning');
|
|
108
|
+
progressBar.classList.add('bg-primary');
|
|
74
109
|
});
|
|
75
110
|
|
|
76
111
|
document.getElementById('retry-btn').addEventListener('click', function() {
|