ivoryos 1.1.0__py3-none-any.whl → 1.2.0__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 +12 -5
- ivoryos/routes/api/api.py +5 -58
- ivoryos/routes/control/control.py +46 -43
- ivoryos/routes/control/control_file.py +4 -4
- ivoryos/routes/control/control_new_device.py +10 -10
- ivoryos/routes/control/templates/controllers.html +38 -9
- ivoryos/routes/control/templates/controllers_new.html +1 -1
- ivoryos/routes/data/data.py +81 -60
- ivoryos/routes/data/templates/components/step_card.html +9 -3
- ivoryos/routes/data/templates/workflow_database.html +10 -4
- ivoryos/routes/design/design.py +306 -243
- ivoryos/routes/design/design_file.py +42 -31
- ivoryos/routes/design/design_step.py +132 -30
- ivoryos/routes/design/templates/components/action_form.html +4 -3
- ivoryos/routes/design/templates/components/{instrument_panel.html → actions_panel.html} +7 -5
- ivoryos/routes/design/templates/components/autofill_toggle.html +8 -12
- ivoryos/routes/design/templates/components/canvas.html +5 -14
- ivoryos/routes/design/templates/components/canvas_footer.html +5 -1
- ivoryos/routes/design/templates/components/canvas_header.html +36 -15
- ivoryos/routes/design/templates/components/canvas_main.html +34 -0
- ivoryos/routes/design/templates/components/deck_selector.html +8 -10
- ivoryos/routes/design/templates/components/edit_action_form.html +16 -7
- ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +3 -5
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +1 -2
- ivoryos/routes/design/templates/components/modals/rename_modal.html +5 -5
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +2 -2
- ivoryos/routes/design/templates/components/python_code_overlay.html +26 -4
- ivoryos/routes/design/templates/components/sidebar.html +12 -13
- ivoryos/routes/design/templates/experiment_builder.html +20 -20
- ivoryos/routes/execute/execute.py +157 -13
- ivoryos/routes/execute/execute_file.py +38 -4
- ivoryos/routes/execute/templates/components/tab_bayesian.html +365 -114
- ivoryos/routes/execute/templates/components/tab_configuration.html +1 -1
- ivoryos/routes/library/library.py +70 -115
- ivoryos/routes/library/templates/library.html +27 -19
- ivoryos/static/js/action_handlers.js +213 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/sortable_design.js +89 -56
- ivoryos/static/js/ui_state.js +113 -0
- ivoryos/utils/bo_campaign.py +137 -1
- ivoryos/utils/db_models.py +14 -5
- ivoryos/utils/form.py +4 -9
- ivoryos/utils/global_config.py +13 -1
- ivoryos/utils/script_runner.py +24 -5
- ivoryos/utils/serilize.py +203 -0
- ivoryos/utils/task_runner.py +4 -1
- ivoryos/version.py +1 -1
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/METADATA +1 -1
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/RECORD +54 -51
- ivoryos/routes/design/templates/components/action_list.html +0 -15
- ivoryos/routes/design/templates/components/operations_panel.html +0 -43
- ivoryos/routes/design/templates/components/script_info.html +0 -31
- ivoryos/routes/design/templates/components/scripts.html +0 -50
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/LICENSE +0 -0
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/WHEEL +0 -0
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -8,114 +8,88 @@ library = Blueprint('library', __name__, template_folder='templates')
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
@library.route("
|
|
11
|
+
@library.route("/<string:script_name>", methods=["GET", "POST", "DELETE"])
|
|
12
12
|
@login_required
|
|
13
|
-
def
|
|
13
|
+
def workflow_script(script_name:str):
|
|
14
|
+
# todo: split this into two routes, one for GET and POST, another for DELETE
|
|
14
15
|
"""
|
|
15
|
-
.. :quickref: Database;
|
|
16
|
+
.. :quickref: Workflow Script Database; get, post, delete workflow script
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.. http:get:: /database/scripts/edit/<script_name>
|
|
18
|
+
.. http:get:: /<string:script_name>
|
|
20
19
|
|
|
21
20
|
:param script_name: script name
|
|
22
21
|
:type script_name: str
|
|
23
22
|
:status 302: redirect to :http:get:`/ivoryos/design/script/`
|
|
24
|
-
"""
|
|
25
|
-
row = Script.query.get(script_name)
|
|
26
|
-
script = Script(**row.as_dict())
|
|
27
|
-
post_script_file(script)
|
|
28
|
-
pseudo_name = session.get("pseudo_deck", "")
|
|
29
|
-
off_line = current_app.config["OFF_LINE"]
|
|
30
|
-
if off_line and pseudo_name and not script.deck == pseudo_name:
|
|
31
|
-
flash(f"Choose the deck with name {script.deck}")
|
|
32
|
-
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
33
|
-
return jsonify({
|
|
34
|
-
"script": script.as_dict(),
|
|
35
|
-
"python_script": script.compile(),
|
|
36
|
-
})
|
|
37
|
-
return redirect(url_for('design.experiment_builder'))
|
|
38
23
|
|
|
24
|
+
.. http:post:: /<string:script_name>
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"""
|
|
44
|
-
.. :quickref: Database; delete workflow
|
|
45
|
-
|
|
46
|
-
delete workflow from database
|
|
26
|
+
:param script_name: script name
|
|
27
|
+
:type script_name: str
|
|
28
|
+
:status 200: json response with success status
|
|
47
29
|
|
|
48
|
-
.. http:
|
|
30
|
+
.. http:delete:: /<string:script_name>
|
|
49
31
|
|
|
50
|
-
:param script_name:
|
|
32
|
+
:param script_name: script name
|
|
51
33
|
:type script_name: str
|
|
52
|
-
:status 302: redirect to :http:get:`/ivoryos/
|
|
34
|
+
:status 302: redirect to :http:get:`/ivoryos/design/script/`
|
|
53
35
|
|
|
54
36
|
"""
|
|
55
|
-
Script.query.
|
|
56
|
-
|
|
57
|
-
|
|
37
|
+
row = Script.query.get(script_name)
|
|
38
|
+
if request.method == "DELETE":
|
|
39
|
+
if not row:
|
|
40
|
+
return jsonify(success=False)
|
|
41
|
+
db.session.delete(row)
|
|
42
|
+
db.session.commit()
|
|
43
|
+
return jsonify(success=True)
|
|
44
|
+
if request.method == "GET":
|
|
45
|
+
if not row:
|
|
46
|
+
return jsonify(success=False)
|
|
47
|
+
script = Script(**row.as_dict())
|
|
48
|
+
post_script_file(script)
|
|
49
|
+
pseudo_name = session.get("pseudo_deck", "")
|
|
50
|
+
off_line = current_app.config["OFF_LINE"]
|
|
51
|
+
if off_line and pseudo_name and not script.deck == pseudo_name:
|
|
52
|
+
flash(f"Choose the deck with name {script.deck}")
|
|
53
|
+
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
54
|
+
return jsonify({
|
|
55
|
+
"script": script.as_dict(),
|
|
56
|
+
"python_script": script.compile(),
|
|
57
|
+
})
|
|
58
|
+
return redirect(url_for('design.experiment_builder'))
|
|
59
|
+
if request.method == "POST":
|
|
60
|
+
status = publish()
|
|
61
|
+
return jsonify(status)
|
|
62
|
+
return None
|
|
58
63
|
|
|
59
64
|
|
|
60
|
-
@library.route("/save")
|
|
61
|
-
@login_required
|
|
62
65
|
def publish():
|
|
63
|
-
"""
|
|
64
|
-
.. :quickref: Database; save workflow to database
|
|
65
|
-
|
|
66
|
-
save workflow to database
|
|
67
|
-
|
|
68
|
-
.. http:get:: /database/scripts/save
|
|
69
|
-
|
|
70
|
-
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
71
|
-
"""
|
|
72
66
|
script = get_script_file()
|
|
67
|
+
|
|
68
|
+
if script.author is None:
|
|
69
|
+
script.author = session.get('user')
|
|
73
70
|
if not script.name or not script.deck:
|
|
74
|
-
|
|
71
|
+
return {"success": False, "error": "Deck cannot be empty, try to re-submit deck configuration on the left panel"}
|
|
75
72
|
row = Script.query.get(script.name)
|
|
76
73
|
if row and row.status == "finalized":
|
|
77
|
-
|
|
78
|
-
elif row and not session['user'] == row.author:
|
|
79
|
-
flash("You are not the author, use save as to rename.")
|
|
80
|
-
else:
|
|
81
|
-
db.session.merge(script)
|
|
82
|
-
db.session.commit()
|
|
83
|
-
flash("Saved!")
|
|
84
|
-
return redirect(url_for('design.experiment_builder'))
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@library.route("/finalize")
|
|
88
|
-
@login_required
|
|
89
|
-
def finalize():
|
|
90
|
-
"""
|
|
91
|
-
.. :quickref: Database; finalize the workflow
|
|
74
|
+
return {"success": False, "error": "This is a protected script, use save as to rename."}
|
|
92
75
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
98
|
-
|
|
99
|
-
"""
|
|
100
|
-
script = get_script_file()
|
|
101
|
-
script.finalize()
|
|
102
|
-
if script.name:
|
|
76
|
+
elif row and session.get('user') != row.author:
|
|
77
|
+
return {"success": False, "error": "You are not the author, use save as to rename."}
|
|
78
|
+
else:
|
|
103
79
|
db.session.merge(script)
|
|
104
80
|
db.session.commit()
|
|
105
|
-
|
|
106
|
-
return redirect(url_for('design.experiment_builder'))
|
|
81
|
+
return {"success": True, "message": "Script published successfully"}
|
|
107
82
|
|
|
108
83
|
|
|
109
|
-
@library.
|
|
110
|
-
@library.route("/get/<deck_name>")
|
|
84
|
+
@library.get("/", strict_slashes=False)
|
|
111
85
|
@login_required
|
|
112
|
-
def load_from_database(
|
|
86
|
+
def load_from_database():
|
|
113
87
|
"""
|
|
114
|
-
.. :quickref: Database; database page
|
|
88
|
+
.. :quickref: Script Database; database page
|
|
115
89
|
|
|
116
90
|
backend control through http requests
|
|
117
91
|
|
|
118
|
-
.. http:get:: /
|
|
92
|
+
.. http:get:: /designs/get/<deck_name>
|
|
119
93
|
|
|
120
94
|
:param deck_name: filter for deck name
|
|
121
95
|
:type deck_name: str
|
|
@@ -124,6 +98,7 @@ def load_from_database(deck_name=None):
|
|
|
124
98
|
session.pop('edit_action', None) # reset cache
|
|
125
99
|
query = Script.query
|
|
126
100
|
search_term = request.args.get("keyword", None)
|
|
101
|
+
deck_name = request.args.get("deck", None)
|
|
127
102
|
if search_term:
|
|
128
103
|
query = query.filter(Script.name.like(f'%{search_term}%'))
|
|
129
104
|
if deck_name is None:
|
|
@@ -147,58 +122,38 @@ def load_from_database(deck_name=None):
|
|
|
147
122
|
return render_template("library.html", scripts=scripts, deck_list=deck_list, deck_name=deck_name)
|
|
148
123
|
|
|
149
124
|
|
|
150
|
-
@library.route("/rename", methods=['POST'])
|
|
151
|
-
@login_required
|
|
152
|
-
def edit_run_name():
|
|
153
|
-
"""
|
|
154
|
-
.. :quickref: Database; edit workflow name
|
|
155
|
-
|
|
156
|
-
edit the name of the current workflow, won't save to the database
|
|
157
|
-
|
|
158
|
-
.. http:post:: database/scripts/rename
|
|
159
|
-
|
|
160
|
-
: form run_name: new workflow name
|
|
161
|
-
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
162
|
-
|
|
163
|
-
"""
|
|
164
|
-
if request.method == "POST":
|
|
165
|
-
run_name = request.form.get("run_name")
|
|
166
|
-
exist_script = Script.query.get(run_name)
|
|
167
|
-
if not exist_script:
|
|
168
|
-
script = get_script_file()
|
|
169
|
-
script.save_as(run_name)
|
|
170
|
-
post_script_file(script)
|
|
171
|
-
else:
|
|
172
|
-
flash("Script name is already exist in database")
|
|
173
|
-
return redirect(url_for("design.experiment_builder"))
|
|
174
125
|
|
|
175
126
|
|
|
176
|
-
@library.
|
|
127
|
+
@library.post("/", strict_slashes=False)
|
|
177
128
|
@login_required
|
|
178
129
|
def save_as():
|
|
179
130
|
"""
|
|
180
|
-
.. :quickref: Database; save the
|
|
131
|
+
.. :quickref: Script Database; save the script as
|
|
181
132
|
|
|
182
133
|
save the current workflow script as
|
|
183
134
|
|
|
184
|
-
.. http:post:: /
|
|
135
|
+
.. http:post:: /library/save_as
|
|
185
136
|
|
|
186
137
|
: form run_name: new workflow name
|
|
187
|
-
:status 302: redirect to :http:get:`/ivoryos/
|
|
138
|
+
:status 302: redirect to :http:get:`/ivoryos/design/script/`
|
|
188
139
|
|
|
189
140
|
"""
|
|
190
141
|
if request.method == "POST":
|
|
191
142
|
run_name = request.form.get("run_name")
|
|
143
|
+
## TODO: check if run_name is valid
|
|
192
144
|
register_workflow = request.form.get("register_workflow")
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
145
|
+
script = get_script_file()
|
|
146
|
+
script.save_as(run_name)
|
|
147
|
+
script.registered = register_workflow == "on"
|
|
148
|
+
script.author = session.get('user')
|
|
149
|
+
post_script_file(script)
|
|
150
|
+
status = publish()
|
|
151
|
+
if request.accept_mimetypes.best_match(['application/json', 'text/html']) == 'application/json':
|
|
152
|
+
return jsonify(status)
|
|
201
153
|
else:
|
|
202
|
-
|
|
203
|
-
|
|
154
|
+
if status["success"]:
|
|
155
|
+
flash("Script saved successfully")
|
|
156
|
+
else:
|
|
157
|
+
flash(status["error"], "error")
|
|
158
|
+
return redirect(url_for('design.experiment_builder'))
|
|
204
159
|
|
|
@@ -2,25 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
{% block title %}IvoryOS | Design Database{% endblock %}
|
|
4
4
|
{% block body %}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
<!-- Deck Filter Buttons -->
|
|
6
|
+
<div class="btn-group" role="group">
|
|
7
|
+
{% for deck in deck_list %}
|
|
8
|
+
<a class="btn {% if deck == current_deck_name %}btn-primary{% else %}btn-secondary{% endif %}"
|
|
9
|
+
href="{{ url_for('library.load_from_database', deck_name=deck) }}">
|
|
10
|
+
{{ deck }}
|
|
11
|
+
</a>
|
|
10
12
|
{% endfor %}
|
|
11
|
-
|
|
12
|
-
<form id="search" style="display: inline-block;float: right;" action="{{url_for('library.load_from_database',deck_name=deck_name)}}" method="GET">
|
|
13
|
-
<div class="input-group">
|
|
14
|
-
<div class="form-outline">
|
|
15
|
-
<input type="search" name="keyword" id="keyword" class="form-control" placeholder="Search workflows...">
|
|
16
|
-
</div>
|
|
17
|
-
<button type="submit" class="btn btn-primary">
|
|
18
|
-
<i class="bi bi-search"></i>
|
|
19
|
-
</button>
|
|
20
|
-
</div>
|
|
21
|
-
</form>
|
|
22
13
|
</div>
|
|
23
14
|
|
|
15
|
+
<!-- Search Form -->
|
|
16
|
+
<form id="search" class="d-flex " method="GET" style="display: inline-block;float: right;"
|
|
17
|
+
action="{{ url_for('library.load_from_database', deck_name=current_deck_name or 'ALL') }}">
|
|
18
|
+
<div class="input-group">
|
|
19
|
+
<input type="search" name="keyword" id="keyword" class="form-control"
|
|
20
|
+
placeholder="Search workflows..." value="{{ request.args.get('keyword', '') }}">
|
|
21
|
+
<button type="submit" class="btn btn-primary">
|
|
22
|
+
<i class="bi bi-search"></i>
|
|
23
|
+
</button>
|
|
24
|
+
</div>
|
|
25
|
+
</form>
|
|
26
|
+
|
|
24
27
|
<table class="table table-hover" id="workflowLibrary">
|
|
25
28
|
<thead>
|
|
26
29
|
<tr>
|
|
@@ -37,7 +40,7 @@
|
|
|
37
40
|
<tbody>
|
|
38
41
|
{% for script in scripts %}
|
|
39
42
|
<tr>
|
|
40
|
-
<td><a href="{{ url_for('library.
|
|
43
|
+
<td><a href="{{ url_for('library.workflow_script', script_name=script.name) }}">{{ script.name }}</a></td>
|
|
41
44
|
<td>{{ script.deck }}</td>
|
|
42
45
|
<td>{{ script.status }}</td>
|
|
43
46
|
<td>{{ script.time_created }}</td>
|
|
@@ -47,7 +50,12 @@
|
|
|
47
50
|
<td>
|
|
48
51
|
{#not workflow.status == "finalized" or#}
|
|
49
52
|
{% if session['user'] == 'admin' or session['user'] == script.author %}
|
|
50
|
-
<a href="
|
|
53
|
+
<a href="#"
|
|
54
|
+
class="text-danger"
|
|
55
|
+
data-delete-url="{{ url_for('library.workflow_script', script_name=script.name) }}"
|
|
56
|
+
onclick="deleteWorkflow(this); return false;">
|
|
57
|
+
Delete
|
|
58
|
+
</a>
|
|
51
59
|
{% else %}
|
|
52
60
|
<a class="disabled-link">delete</a>
|
|
53
61
|
{% endif %}
|
|
@@ -79,5 +87,5 @@
|
|
|
79
87
|
<a class="page-link" href="{{ url_for('library.load_from_database', page=scripts.next_num) }}">Next</a>
|
|
80
88
|
</div>
|
|
81
89
|
</div>
|
|
82
|
-
|
|
90
|
+
<script src="{{ url_for('static', filename='js/db_delete.js') }}"></script>
|
|
83
91
|
{% endblock %}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
function saveWorkflow(link) {
|
|
6
|
+
const url = link.dataset.postUrl;
|
|
7
|
+
|
|
8
|
+
fetch(url, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: {
|
|
11
|
+
'Content-Type': 'application/json'
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
.then(res => res.json())
|
|
15
|
+
.then(data => {
|
|
16
|
+
if (data.success) {
|
|
17
|
+
// flash a success message
|
|
18
|
+
flash("Workflow saved successfully", "success");
|
|
19
|
+
window.location.reload(); // or update the UI dynamically
|
|
20
|
+
} else {
|
|
21
|
+
alert("Failed to save workflow: " + data.error);
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
.catch(err => {
|
|
25
|
+
console.error("Save error:", err);
|
|
26
|
+
alert("Something went wrong.");
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
function updateInstrumentPanel(link) {
|
|
32
|
+
const url = link.dataset.getUrl;
|
|
33
|
+
fetch(url)
|
|
34
|
+
.then(res => res.json())
|
|
35
|
+
.then(data => {
|
|
36
|
+
if (data.html) {
|
|
37
|
+
document.getElementById("sidebar-wrapper").innerHTML = data.html;
|
|
38
|
+
initializeDragHandlers()
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function addMethodToDesign(event, form) {
|
|
44
|
+
event.preventDefault(); // Prevent default form submission
|
|
45
|
+
|
|
46
|
+
const formData = new FormData(form);
|
|
47
|
+
|
|
48
|
+
fetch(form.action, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
body: formData
|
|
51
|
+
})
|
|
52
|
+
.then(response => response.json())
|
|
53
|
+
.then(data => {
|
|
54
|
+
if (data.success) {
|
|
55
|
+
updateActionCanvas(data.html);
|
|
56
|
+
hideModal();
|
|
57
|
+
} else {
|
|
58
|
+
alert("Failed to add method: " + data.error);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.catch(error => console.error('Error:', error));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
function updateActionCanvas(html) {
|
|
66
|
+
document.getElementById("canvas-action-wrapper").innerHTML = html;
|
|
67
|
+
initializeCanvas(); // Reinitialize canvas functionality
|
|
68
|
+
document.querySelectorAll('#pythonCodeOverlay pre code').forEach((block) => {
|
|
69
|
+
hljs.highlightElement(block);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
let lastFocusedElement = null;
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
function hideModal() {
|
|
78
|
+
if (document.activeElement) {
|
|
79
|
+
document.activeElement.blur();
|
|
80
|
+
}
|
|
81
|
+
$('#dropModal').modal('hide');
|
|
82
|
+
if (lastFocusedElement) {
|
|
83
|
+
lastFocusedElement.focus(); // Return focus to the triggering element
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function submitEditForm(event) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
const form = event.target;
|
|
90
|
+
const formData = new FormData(form);
|
|
91
|
+
|
|
92
|
+
fetch(form.action, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
body: formData
|
|
95
|
+
})
|
|
96
|
+
.then(response => response.text())
|
|
97
|
+
.then(html => {
|
|
98
|
+
if (html) {
|
|
99
|
+
// Update only the action list
|
|
100
|
+
updateActionCanvas(html);
|
|
101
|
+
|
|
102
|
+
if (previousHtmlState) {
|
|
103
|
+
document.getElementById('instrument-panel').innerHTML = previousHtmlState;
|
|
104
|
+
previousHtmlState = null; // Clear the stored state
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
.catch(error => {
|
|
109
|
+
console.error('Error:', error);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function clearDraft() {
|
|
114
|
+
fetch(scriptDeleteUrl, {
|
|
115
|
+
method: "DELETE",
|
|
116
|
+
headers: {
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
.then(res => res.json())
|
|
121
|
+
.then(data => {
|
|
122
|
+
if (data.success) {
|
|
123
|
+
window.location.reload();
|
|
124
|
+
} else {
|
|
125
|
+
alert("Failed to clear draft");
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
.catch(error => console.error("Failed to clear draft", error));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
let previousHtmlState = null; // Store the previous state
|
|
135
|
+
|
|
136
|
+
function duplicateAction(uuid) {
|
|
137
|
+
if (!uuid) {
|
|
138
|
+
console.error('Invalid UUID');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fetch(scriptStepDupUrl.replace('0', uuid), {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers: {
|
|
145
|
+
'Content-Type': 'application/json'
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
.then(response => response.text())
|
|
150
|
+
.then(html => {
|
|
151
|
+
updateActionCanvas(html);
|
|
152
|
+
})
|
|
153
|
+
.catch(error => console.error('Error:', error));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function editAction(uuid) {
|
|
157
|
+
if (!uuid) {
|
|
158
|
+
console.error('Invalid UUID');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Save current state before fetching new content
|
|
163
|
+
previousHtmlState = document.getElementById('instrument-panel').innerHTML;
|
|
164
|
+
|
|
165
|
+
fetch(scriptStepUrl.replace('0', uuid), {
|
|
166
|
+
method: 'GET',
|
|
167
|
+
headers: {
|
|
168
|
+
'Content-Type': 'application/json'
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
.then(response => response.text())
|
|
172
|
+
.then(html => {
|
|
173
|
+
document.getElementById('instrument-panel').innerHTML = html;
|
|
174
|
+
|
|
175
|
+
// Add click handler for back button
|
|
176
|
+
document.getElementById('back').addEventListener('click', function(e) {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
if (previousHtmlState) {
|
|
179
|
+
document.getElementById('instrument-panel').innerHTML = previousHtmlState;
|
|
180
|
+
previousHtmlState = null; // Clear the stored state
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
})
|
|
184
|
+
.catch(error => console.error('Error:', error));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
function deleteAction(uuid) {
|
|
190
|
+
if (!uuid) {
|
|
191
|
+
console.error('Invalid UUID');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
fetch(scriptStepUrl.replace('0', uuid), {
|
|
196
|
+
method: 'DELETE',
|
|
197
|
+
headers: {
|
|
198
|
+
'Content-Type': 'application/json'
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
.then(response => response.text())
|
|
202
|
+
.then(html => {
|
|
203
|
+
// Find the first list element's content and replace it
|
|
204
|
+
updateActionCanvas(html);
|
|
205
|
+
})
|
|
206
|
+
.catch(error => console.error('Error:', error));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
function deleteWorkflow(link) {
|
|
3
|
+
const url = link.dataset.deleteUrl;
|
|
4
|
+
|
|
5
|
+
fetch(url, {
|
|
6
|
+
method: 'DELETE',
|
|
7
|
+
headers: {
|
|
8
|
+
'Content-Type': 'application/json'
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
.then(res => res.json())
|
|
12
|
+
.then(data => {
|
|
13
|
+
if (data.success) {
|
|
14
|
+
window.location.reload(); // or remove the row dynamically
|
|
15
|
+
} else {
|
|
16
|
+
alert("Failed to delete workflow: " + data.error);
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
.catch(err => {
|
|
20
|
+
console.error("Delete error:", err);
|
|
21
|
+
alert("Something went wrong.");
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function editScriptName(event) {
|
|
2
|
+
event.preventDefault(); // Prevent full form submission
|
|
3
|
+
const newName = document.getElementById("new-name").value;
|
|
4
|
+
fetch(scriptMetaUrl, {
|
|
5
|
+
method: "PATCH",
|
|
6
|
+
headers: {
|
|
7
|
+
"Content-Type": "application/json",
|
|
8
|
+
},
|
|
9
|
+
body: JSON.stringify({name: newName})
|
|
10
|
+
})
|
|
11
|
+
.then(res => res.json())
|
|
12
|
+
.then(data => {
|
|
13
|
+
if (data.success) {
|
|
14
|
+
window.location.reload(); // or update the title on page directly
|
|
15
|
+
} else {
|
|
16
|
+
alert("Failed to rename script");
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
.catch(error => console.error("Failed to rename script", error));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function lockScriptEditing() {
|
|
23
|
+
fetch(scriptMetaUrl, {
|
|
24
|
+
method: "PATCH",
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify({ status: "finalized" })
|
|
29
|
+
})
|
|
30
|
+
.then(res => res.json())
|
|
31
|
+
.then(data => {
|
|
32
|
+
if (data.success) {
|
|
33
|
+
window.location.reload();
|
|
34
|
+
} else {
|
|
35
|
+
alert("Failed to update script status");
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
.catch(error => console.error("Failed to update script status", error));
|
|
39
|
+
}
|