ivoryos 0.1.19__py3-none-any.whl → 0.1.21__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.
- ivoryos/__init__.py +8 -11
- ivoryos/routes/database/database.py +2 -0
- ivoryos/routes/database/templates/database/experiment_database.html +3 -1
- ivoryos/routes/design/design.py +61 -17
- ivoryos/routes/design/templates/design/experiment_builder.html +84 -94
- ivoryos/routes/design/templates/design/experiment_run.html +39 -3
- ivoryos/static/js/socket_handler.js +69 -16
- ivoryos/static/js/sortable_design.js +102 -33
- ivoryos/static/style.css +9 -0
- ivoryos/templates/base.html +3 -3
- ivoryos/utils/db_models.py +44 -13
- ivoryos/utils/form.py +50 -2
- ivoryos/utils/global_config.py +10 -0
- ivoryos/utils/script_runner.py +71 -43
- ivoryos/utils/utils.py +1 -1
- ivoryos/version.py +1 -1
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/METADATA +6 -7
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/RECORD +21 -21
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/LICENSE +0 -0
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/WHEEL +0 -0
- {ivoryos-0.1.19.dist-info → ivoryos-0.1.21.dist-info}/top_level.txt +0 -0
|
@@ -10,13 +10,74 @@ document.addEventListener("DOMContentLoaded", function() {
|
|
|
10
10
|
var progressBar = document.getElementById('progress-bar-inner');
|
|
11
11
|
progressBar.style.width = progress + '%';
|
|
12
12
|
progressBar.setAttribute('aria-valuenow', progress);
|
|
13
|
+
if (progress === 1) {
|
|
14
|
+
document.getElementById("run-panel").style.display = "none";
|
|
15
|
+
document.getElementById("code-panel").style.display = "block";
|
|
13
16
|
|
|
17
|
+
// Optional: Scroll to the code panel
|
|
18
|
+
document.getElementById("code-panel").scrollIntoView({ behavior: "smooth" });
|
|
19
|
+
}
|
|
14
20
|
if (progress === 100) {
|
|
15
21
|
// Remove animation and set green color when 100% is reached
|
|
16
22
|
progressBar.classList.remove('progress-bar-animated');
|
|
17
23
|
progressBar.classList.add('bg-success'); // Bootstrap class for green color
|
|
24
|
+
setTimeout(() => {
|
|
25
|
+
document.getElementById("code-panel").style.display = "none";
|
|
26
|
+
document.getElementById("run-panel").style.display = "block";
|
|
27
|
+
}, 1000); // Small delay to let users see the completion
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
socket.on('error', function(errorData) {
|
|
32
|
+
console.error("Error received:", errorData);
|
|
33
|
+
var progressBar = document.getElementById('progress-bar-inner');
|
|
34
|
+
|
|
35
|
+
progressBar.classList.remove('bg-success');
|
|
36
|
+
progressBar.classList.add('bg-danger'); // Red color for error
|
|
37
|
+
// Show error modal
|
|
38
|
+
var errorModal = new bootstrap.Modal(document.getElementById('error-modal'));
|
|
39
|
+
document.getElementById('error-message').innerText = "An error occurred: " + errorData.message;
|
|
40
|
+
errorModal.show();
|
|
41
|
+
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Handle Pause/Resume Button
|
|
45
|
+
document.getElementById('pause-resume').addEventListener('click', function() {
|
|
46
|
+
socket.emit('pause');
|
|
47
|
+
console.log('Pause/Resume is toggled.');
|
|
48
|
+
var button = this;
|
|
49
|
+
var icon = button.querySelector("i");
|
|
50
|
+
|
|
51
|
+
// Toggle Pause and Resume
|
|
52
|
+
if (icon.classList.contains("bi-pause-circle")) {
|
|
53
|
+
icon.classList.remove("bi-pause-circle");
|
|
54
|
+
icon.classList.add("bi-play-circle");
|
|
55
|
+
button.innerHTML = '<i class="bi bi-play-circle"></i>';
|
|
56
|
+
button.setAttribute("title", "Resume execution");
|
|
57
|
+
} else {
|
|
58
|
+
icon.classList.remove("bi-play-circle");
|
|
59
|
+
icon.classList.add("bi-pause-circle");
|
|
60
|
+
button.innerHTML = '<i class="bi bi-pause-circle"></i>';
|
|
61
|
+
button.setAttribute("title", "Pause execution");
|
|
18
62
|
}
|
|
19
63
|
});
|
|
64
|
+
|
|
65
|
+
// Handle Modal Buttons
|
|
66
|
+
document.getElementById('continue-btn').addEventListener('click', function() {
|
|
67
|
+
socket.emit('pause'); // Resume execution
|
|
68
|
+
console.log("Execution resumed.");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
document.getElementById('stop-btn').addEventListener('click', function() {
|
|
72
|
+
socket.emit('pause'); // Resume execution
|
|
73
|
+
socket.emit('abort_current'); // Stop execution
|
|
74
|
+
console.log("Execution stopped.");
|
|
75
|
+
|
|
76
|
+
// Reset UI back to initial state
|
|
77
|
+
document.getElementById("code-panel").style.display = "none";
|
|
78
|
+
document.getElementById("run-panel").style.display = "block";
|
|
79
|
+
});
|
|
80
|
+
|
|
20
81
|
socket.on('log', function(data) {
|
|
21
82
|
var logMessage = data.message;
|
|
22
83
|
console.log(logMessage);
|
|
@@ -39,24 +100,16 @@ document.addEventListener("DOMContentLoaded", function() {
|
|
|
39
100
|
}
|
|
40
101
|
});
|
|
41
102
|
|
|
42
|
-
|
|
103
|
+
socket.on('execution', function(data) {
|
|
104
|
+
// Remove highlighting from all lines
|
|
105
|
+
document.querySelectorAll('pre code').forEach(el => el.style.backgroundColor = '');
|
|
43
106
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
107
|
+
// Get the currently executing line and highlight it
|
|
108
|
+
let executingLine = document.getElementById(data.section);
|
|
109
|
+
if (executingLine) {
|
|
110
|
+
executingLine.style.backgroundColor = '#cce5ff'; // Highlight
|
|
111
|
+
executingLine.style.transition = 'background-color 0.3s ease-in-out';
|
|
48
112
|
|
|
49
|
-
// Toggle between Pause and Resume
|
|
50
|
-
if (icon.classList.contains("bi-pause-circle")) {
|
|
51
|
-
icon.classList.remove("bi-pause-circle");
|
|
52
|
-
icon.classList.add("bi-play-circle");
|
|
53
|
-
button.innerHTML = '<i class="bi bi-play-circle"></i>';
|
|
54
|
-
button.setAttribute("title", "Resume execution");
|
|
55
|
-
} else {
|
|
56
|
-
icon.classList.remove("bi-play-circle");
|
|
57
|
-
icon.classList.add("bi-pause-circle");
|
|
58
|
-
button.innerHTML = '<i class="bi bi-pause-circle"></i>';
|
|
59
|
-
button.setAttribute("title", "Pause execution");
|
|
60
113
|
}
|
|
61
114
|
});
|
|
62
115
|
});
|
|
@@ -1,36 +1,105 @@
|
|
|
1
|
-
$(document).ready(function(){
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
$(document).ready(function () {
|
|
2
|
+
let dropTargetId = ""; // Store the ID of the drop target
|
|
3
|
+
|
|
4
|
+
$("#list ul").sortable({
|
|
5
|
+
cancel: ".unsortable",
|
|
6
|
+
opacity: 0.8,
|
|
7
|
+
cursor: "move",
|
|
8
|
+
placeholder: "drop-placeholder",
|
|
9
|
+
update: function () {
|
|
10
|
+
var item_order = [];
|
|
11
|
+
$("ul.reorder li").each(function () {
|
|
12
|
+
item_order.push($(this).attr("id"));
|
|
13
|
+
});
|
|
14
|
+
var order_string = "order=" + item_order.join(",");
|
|
15
|
+
|
|
16
|
+
$.ajax({
|
|
17
|
+
method: "POST",
|
|
18
|
+
url: updateListUrl,
|
|
19
|
+
data: order_string,
|
|
20
|
+
cache: false,
|
|
21
|
+
success: function (data) {
|
|
22
|
+
$("#response").html(data);
|
|
23
|
+
$("#response").slideDown("slow");
|
|
24
|
+
slideout();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Make Entire Accordion Item Draggable
|
|
31
|
+
$(".accordion-item").on("dragstart", function (event) {
|
|
32
|
+
let formHtml = $(this).find(".accordion-body").html(); // Get the correct form
|
|
33
|
+
event.originalEvent.dataTransfer.setData("form", formHtml || ""); // Store form HTML
|
|
34
|
+
event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
|
|
35
|
+
event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
|
|
36
|
+
|
|
37
|
+
$(this).addClass("dragging");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
$("#list ul, .canvas").on("dragover", function (event) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
let $target = $(event.target).closest("li");
|
|
44
|
+
|
|
45
|
+
// If we're over a valid <li> element in the list
|
|
46
|
+
if ($target.length) {
|
|
47
|
+
dropTargetId = $target.attr("id") || ""; // Store the drop target ID
|
|
48
|
+
|
|
49
|
+
$(".drop-placeholder").remove(); // Remove existing placeholders
|
|
50
|
+
$("<li class='drop-placeholder'></li>").insertBefore($target); // Insert before the target element
|
|
51
|
+
} else if (!$("#list ul").children().length && $(this).hasClass("canvas")) {
|
|
52
|
+
$(".drop-placeholder").remove(); // Remove any placeholder
|
|
53
|
+
// $("#list ul").append("<li class='drop-placeholder'></li>"); // Append placeholder to canvas
|
|
54
|
+
} else {
|
|
55
|
+
dropTargetId = ""; // Append placeholder to canvas
|
|
56
|
+
}
|
|
57
|
+
});
|
|
7
58
|
|
|
8
|
-
$("#
|
|
9
|
-
|
|
10
|
-
$(function() {
|
|
11
|
-
$("#list ul").sortable({
|
|
12
|
-
cancel: ".unsortable",
|
|
13
|
-
opacity: 0.8,
|
|
14
|
-
cursor: 'move',
|
|
15
|
-
update: function() {
|
|
16
|
-
var item_order = [];
|
|
17
|
-
$('ul.reorder li').each(function() {
|
|
18
|
-
item_order.push($(this).attr("id"));
|
|
19
|
-
});
|
|
20
|
-
var order_string = 'order=' + item_order.join(',');
|
|
21
|
-
|
|
22
|
-
$.ajax({
|
|
23
|
-
method: "POST",
|
|
24
|
-
url: updateListUrl, // updateListUrl should be set dynamically in your HTML template
|
|
25
|
-
data: order_string,
|
|
26
|
-
cache: false,
|
|
27
|
-
success: function(data){
|
|
28
|
-
$("#response").html(data);
|
|
29
|
-
$("#response").slideDown('slow');
|
|
30
|
-
slideout();
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
});
|
|
59
|
+
$("#list ul, .canvas").on("dragleave", function () {
|
|
60
|
+
$(".drop-placeholder").remove(); // Remove placeholder on leave
|
|
35
61
|
});
|
|
62
|
+
|
|
63
|
+
$("#list ul, .canvas").on("drop", function (event) {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
|
|
66
|
+
var actionName = event.originalEvent.dataTransfer.getData("action");
|
|
67
|
+
var actionId = event.originalEvent.dataTransfer.getData("id");
|
|
68
|
+
var formHtml = event.originalEvent.dataTransfer.getData("form"); // Retrieve form HTML
|
|
69
|
+
let listLength = $("ul.reorder li").length;
|
|
70
|
+
dropTargetId = dropTargetId || listLength + 1; // Assign a "last" ID or unique identifier
|
|
71
|
+
$(".drop-placeholder").remove();
|
|
72
|
+
// Trigger the modal with the appropriate action
|
|
73
|
+
triggerModal(formHtml, actionName, actionId, dropTargetId);
|
|
74
|
+
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Function to trigger the modal (same for both buttons and accordion items)
|
|
78
|
+
function triggerModal(formHtml, actionName, actionId, dropTargetId) {
|
|
79
|
+
if (formHtml && formHtml.trim() !== "") {
|
|
80
|
+
var $form = $("<div>").html(formHtml); // Convert HTML string to jQuery object
|
|
81
|
+
|
|
82
|
+
// Create a hidden input for the drop target ID
|
|
83
|
+
var $hiddenInput = $("<input>")
|
|
84
|
+
.attr("type", "hidden")
|
|
85
|
+
.attr("name", "drop_target_id")
|
|
86
|
+
.attr("id", "dropTargetInput")
|
|
87
|
+
.val(dropTargetId);
|
|
88
|
+
|
|
89
|
+
// Insert before the submit button
|
|
90
|
+
$form.find("button[type='submit']").before($hiddenInput);
|
|
91
|
+
|
|
92
|
+
$("#modalFormFields").empty().append($form.children());
|
|
93
|
+
$("#dropModal").modal("show"); // Show modal
|
|
94
|
+
|
|
95
|
+
// Store and display drop target ID in the modal
|
|
96
|
+
$("#modalDropTarget").text(dropTargetId || "N/A");
|
|
97
|
+
|
|
98
|
+
$("#modalFormFields").data("action-id", actionId);
|
|
99
|
+
$("#modalFormFields").data("action-name", actionName);
|
|
100
|
+
$("#modalFormFields").data("drop-target-id", dropTargetId);
|
|
101
|
+
} else {
|
|
102
|
+
console.error("Form HTML is undefined or empty!");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
36
105
|
});
|
ivoryos/static/style.css
CHANGED
|
@@ -199,4 +199,13 @@ hr.vertical {
|
|
|
199
199
|
color: white;
|
|
200
200
|
font-size: 24px;
|
|
201
201
|
padding-top: 20%;
|
|
202
|
+
}
|
|
203
|
+
.drop-placeholder {
|
|
204
|
+
height: 2px !important; /* Keep it very thin */
|
|
205
|
+
min-height: 2px !important;
|
|
206
|
+
margin: 0 !important;
|
|
207
|
+
padding: 0 !important;
|
|
208
|
+
background: rgba(0, 0, 0, 0.2); /* Slight visibility */
|
|
209
|
+
border-radius: 2px;
|
|
210
|
+
list-style: none; /* Remove any default list styling */
|
|
202
211
|
}
|
ivoryos/templates/base.html
CHANGED
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
<li class="nav-item">
|
|
55
55
|
<a class="nav-link" href="{{ url_for('control.controllers_home') }}">Temp Devices</a></li>
|
|
56
56
|
</li>
|
|
57
|
-
<li class="nav-item"
|
|
58
|
-
<a class="nav-link" href="{{ url_for('main.help_info') }}">About</a
|
|
59
|
-
</li
|
|
57
|
+
{# <li class="nav-item">#}
|
|
58
|
+
{# <a class="nav-link" href="{{ url_for('main.help_info') }}">About</a>#}
|
|
59
|
+
{# </li>#}
|
|
60
60
|
{% if plugins %}
|
|
61
61
|
{% for plugin in plugins %}
|
|
62
62
|
<li class="nav-item">
|
ivoryos/utils/db_models.py
CHANGED
|
@@ -196,28 +196,37 @@ class Script(db.Model):
|
|
|
196
196
|
for i in self.stypes:
|
|
197
197
|
self._sort(i)
|
|
198
198
|
|
|
199
|
-
def add_action(self, action: dict):
|
|
199
|
+
def add_action(self, action: dict, insert_position=None):
|
|
200
200
|
current_len = len(self.currently_editing_script)
|
|
201
201
|
action_to_add = action.copy()
|
|
202
202
|
action_to_add['id'] = current_len + 1
|
|
203
203
|
action_to_add['uuid'] = uuid.uuid4().fields[-1]
|
|
204
204
|
self.currently_editing_script.append(action_to_add)
|
|
205
|
-
self.
|
|
205
|
+
self._insert_action(insert_position, current_len)
|
|
206
206
|
self.update_time_stamp()
|
|
207
207
|
|
|
208
|
-
def add_variable(self, statement, variable, type):
|
|
208
|
+
def add_variable(self, statement, variable, type, insert_position=None):
|
|
209
209
|
variable = self.validate_function_name(variable)
|
|
210
210
|
convert_type = getattr(builtins, type)
|
|
211
211
|
statement = convert_type(statement)
|
|
212
212
|
current_len = len(self.currently_editing_script)
|
|
213
213
|
uid = uuid.uuid4().fields[-1]
|
|
214
|
-
|
|
214
|
+
action = {"id": current_len + 1, "instrument": 'variable', "action": variable,
|
|
215
215
|
"args": {"statement": 'None' if statement == '' else statement}, "return": '', "uuid": uid,
|
|
216
|
-
"arg_types": {"statement": type}}
|
|
217
|
-
self.currently_editing_script.
|
|
218
|
-
self.
|
|
216
|
+
"arg_types": {"statement": type}}
|
|
217
|
+
self.currently_editing_script.append(action)
|
|
218
|
+
self._insert_action(insert_position, current_len)
|
|
219
219
|
self.update_time_stamp()
|
|
220
220
|
|
|
221
|
+
def _insert_action(self, insert_position, current_len, action_len:int=1):
|
|
222
|
+
|
|
223
|
+
if insert_position is None:
|
|
224
|
+
self.currently_editing_order.extend([str(current_len + i + 1) for i in range(action_len)])
|
|
225
|
+
else:
|
|
226
|
+
index = int(insert_position) - 1
|
|
227
|
+
self.currently_editing_order[index:index] = [str(current_len + i + 1) for i in range(action_len)]
|
|
228
|
+
self.sort_actions()
|
|
229
|
+
|
|
221
230
|
def get_added_variables(self):
|
|
222
231
|
added_variables: Dict[str, str] = {action["action"]: action["arg_types"]["statement"] for action in
|
|
223
232
|
self.currently_editing_script if action["instrument"] == "variable"}
|
|
@@ -251,7 +260,7 @@ class Script(db.Model):
|
|
|
251
260
|
kwargs[key] = f"#{self.validate_function_name(value[1:])}"
|
|
252
261
|
return kwargs
|
|
253
262
|
|
|
254
|
-
def add_logic_action(self, logic_type: str, statement):
|
|
263
|
+
def add_logic_action(self, logic_type: str, statement, insert_position=None):
|
|
255
264
|
current_len = len(self.currently_editing_script)
|
|
256
265
|
uid = uuid.uuid4().fields[-1]
|
|
257
266
|
logic_dict = {
|
|
@@ -291,7 +300,7 @@ class Script(db.Model):
|
|
|
291
300
|
}
|
|
292
301
|
action_list = logic_dict[logic_type]
|
|
293
302
|
self.currently_editing_script.extend(action_list)
|
|
294
|
-
self.
|
|
303
|
+
self._insert_action(insert_position, current_len, len(action_list))
|
|
295
304
|
self.update_time_stamp()
|
|
296
305
|
|
|
297
306
|
def delete_action(self, id: int):
|
|
@@ -390,6 +399,23 @@ class Script(db.Model):
|
|
|
390
399
|
string += "\t"
|
|
391
400
|
return string
|
|
392
401
|
|
|
402
|
+
def convert_to_lines(self, exec_str_collection: dict):
|
|
403
|
+
"""
|
|
404
|
+
Parse a dictionary of script functions and extract function body lines.
|
|
405
|
+
|
|
406
|
+
:param exec_str_collection: Dictionary containing script types and corresponding function strings.
|
|
407
|
+
:return: A dict containing script types as keys and lists of function body lines as values.
|
|
408
|
+
"""
|
|
409
|
+
line_collection = {}
|
|
410
|
+
for stype, func_str in exec_str_collection.items():
|
|
411
|
+
module = ast.parse(func_str)
|
|
412
|
+
func_def = next(node for node in module.body if isinstance(node, ast.FunctionDef))
|
|
413
|
+
|
|
414
|
+
# Extract function body as source lines
|
|
415
|
+
line_collection[stype] = [ast.unparse(node) for node in func_def.body if not isinstance(node, ast.Return)]
|
|
416
|
+
# print(line_collection[stype])
|
|
417
|
+
return line_collection
|
|
418
|
+
|
|
393
419
|
def compile(self, script_path=None):
|
|
394
420
|
"""
|
|
395
421
|
Compile the current script to a Python file.
|
|
@@ -401,8 +427,9 @@ class Script(db.Model):
|
|
|
401
427
|
exec_str_collection = {}
|
|
402
428
|
|
|
403
429
|
for i in self.stypes:
|
|
404
|
-
|
|
405
|
-
|
|
430
|
+
if self.script_dict[i]:
|
|
431
|
+
func_str = self._generate_function_header(run_name, i) + self._generate_function_body(i)
|
|
432
|
+
exec_str_collection[i] = func_str
|
|
406
433
|
if script_path:
|
|
407
434
|
self._write_to_file(script_path, run_name, exec_str_collection)
|
|
408
435
|
|
|
@@ -428,13 +455,14 @@ class Script(db.Model):
|
|
|
428
455
|
configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
|
|
429
456
|
config_type.items()]
|
|
430
457
|
|
|
431
|
-
|
|
458
|
+
script_type = f"_{stype}" if stype != "script" else ""
|
|
459
|
+
function_header = f"def {run_name}{script_type}("
|
|
432
460
|
|
|
433
461
|
if stype == "script":
|
|
434
462
|
function_header += ", ".join(configure)
|
|
435
463
|
|
|
436
464
|
function_header += "):"
|
|
437
|
-
function_header += self.indent(1) + f"global {run_name}_{stype}"
|
|
465
|
+
# function_header += self.indent(1) + f"global {run_name}_{stype}"
|
|
438
466
|
return function_header
|
|
439
467
|
|
|
440
468
|
def _generate_function_body(self, stype):
|
|
@@ -474,6 +502,9 @@ class Script(db.Model):
|
|
|
474
502
|
return f"{self.indent(indent_unit)}time.sleep({statement})", indent_unit
|
|
475
503
|
elif instrument == 'repeat':
|
|
476
504
|
return self._process_repeat(indent_unit, action_name, statement, next_action)
|
|
505
|
+
#todo
|
|
506
|
+
# elif instrument == 'registered_workflows':
|
|
507
|
+
# return inspect.getsource(my_function)
|
|
477
508
|
else:
|
|
478
509
|
return self._process_instrument_action(indent_unit, instrument, action_name, args, save_data)
|
|
479
510
|
|
ivoryos/utils/form.py
CHANGED
|
@@ -7,6 +7,10 @@ from flask_wtf import FlaskForm
|
|
|
7
7
|
from wtforms import StringField, FloatField, HiddenField, BooleanField, IntegerField
|
|
8
8
|
import inspect
|
|
9
9
|
|
|
10
|
+
from ivoryos.utils.db_models import Script
|
|
11
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
12
|
+
|
|
13
|
+
global_config = GlobalConfig()
|
|
10
14
|
|
|
11
15
|
def find_variable(data, script):
|
|
12
16
|
"""
|
|
@@ -345,6 +349,13 @@ def create_form_from_action(action: dict, script=None, design=True):
|
|
|
345
349
|
setattr(DynamicForm, 'return', return_value)
|
|
346
350
|
return DynamicForm()
|
|
347
351
|
|
|
352
|
+
def create_all_builtin_forms(script):
|
|
353
|
+
all_builtin_forms = {}
|
|
354
|
+
for logic_name in ['if', 'while', 'variable', 'wait', 'repeat']:
|
|
355
|
+
# signature = info.get('signature', {})
|
|
356
|
+
form_class = create_builtin_form(logic_name, script)
|
|
357
|
+
all_builtin_forms[logic_name] = form_class()
|
|
358
|
+
return all_builtin_forms
|
|
348
359
|
|
|
349
360
|
def create_builtin_form(logic_type, script):
|
|
350
361
|
"""
|
|
@@ -379,14 +390,51 @@ def create_builtin_form(logic_type, script):
|
|
|
379
390
|
render_kw=render_kwargs)
|
|
380
391
|
type_field = SelectField(
|
|
381
392
|
'Select Input Type',
|
|
382
|
-
choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String')],
|
|
393
|
+
choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String'), ('bool', 'Boolean')],
|
|
383
394
|
default='str' # Optional default value
|
|
384
395
|
)
|
|
385
396
|
setattr(BuiltinFunctionForm, "variable", variable_field)
|
|
386
397
|
setattr(BuiltinFunctionForm, "type", type_field)
|
|
387
398
|
hidden_field = HiddenField(name=f'builtin_name', render_kw={"value": f'{logic_type}'})
|
|
388
399
|
setattr(BuiltinFunctionForm, "builtin_name", hidden_field)
|
|
389
|
-
return BuiltinFunctionForm
|
|
400
|
+
return BuiltinFunctionForm
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def get_method_from_workflow(function_string):
|
|
404
|
+
"""Creates a function from a string and assigns it a new name."""
|
|
405
|
+
|
|
406
|
+
namespace = {}
|
|
407
|
+
exec(function_string, globals(), namespace) # Execute the string in a safe namespace
|
|
408
|
+
func_name = next(iter(namespace))
|
|
409
|
+
# Get the function name dynamically
|
|
410
|
+
return namespace[func_name]
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def create_workflow_forms(script, autofill: bool = False, design: bool = False):
|
|
414
|
+
workflow_forms = {}
|
|
415
|
+
functions = {}
|
|
416
|
+
class RegisteredWorkflows:
|
|
417
|
+
pass
|
|
418
|
+
|
|
419
|
+
deck_name = script.deck
|
|
420
|
+
workflows = Script.query.filter(Script.deck==deck_name, Script.name != script.name).all()
|
|
421
|
+
for workflow in workflows:
|
|
422
|
+
compiled_strs = workflow.compile().get('script', "")
|
|
423
|
+
method = get_method_from_workflow(compiled_strs)
|
|
424
|
+
functions[workflow.name] = dict(signature=inspect.signature(method), docstring=inspect.getdoc(method))
|
|
425
|
+
setattr(RegisteredWorkflows, workflow.name, method)
|
|
426
|
+
|
|
427
|
+
form_class = create_form_for_method(method, autofill, script, design)
|
|
428
|
+
|
|
429
|
+
hidden_method_name = HiddenField(name=f'hidden_name', description="",
|
|
430
|
+
render_kw={"value": f'{workflow.name}'})
|
|
431
|
+
if design:
|
|
432
|
+
return_value = StringField(label='Save value as', render_kw={"placeholder": "Optional"})
|
|
433
|
+
setattr(form_class, 'return', return_value)
|
|
434
|
+
setattr(form_class, 'workflow_name', hidden_method_name)
|
|
435
|
+
workflow_forms[workflow.name] = form_class()
|
|
436
|
+
global_config.registered_workflows = RegisteredWorkflows
|
|
437
|
+
return workflow_forms, functions
|
|
390
438
|
|
|
391
439
|
|
|
392
440
|
def create_action_button(script, stype=None):
|
ivoryos/utils/global_config.py
CHANGED
|
@@ -8,6 +8,7 @@ class GlobalConfig:
|
|
|
8
8
|
if cls._instance is None:
|
|
9
9
|
cls._instance = super(GlobalConfig, cls).__new__(cls, *args, **kwargs)
|
|
10
10
|
cls._instance._deck = None
|
|
11
|
+
cls._instance._registered_workflows = None
|
|
11
12
|
cls._instance._agent = None
|
|
12
13
|
cls._instance._defined_variables = {}
|
|
13
14
|
cls._instance._api_variables = set()
|
|
@@ -24,6 +25,15 @@ class GlobalConfig:
|
|
|
24
25
|
if self._deck is None:
|
|
25
26
|
self._deck = value
|
|
26
27
|
|
|
28
|
+
@property
|
|
29
|
+
def registered_workflows(self):
|
|
30
|
+
return self._registered_workflows
|
|
31
|
+
|
|
32
|
+
@registered_workflows.setter
|
|
33
|
+
def registered_workflows(self, value):
|
|
34
|
+
if self._registered_workflows is None:
|
|
35
|
+
self._registered_workflows = value
|
|
36
|
+
|
|
27
37
|
|
|
28
38
|
@property
|
|
29
39
|
def deck_snapshot(self):
|