ivoryos 1.0.0__py3-none-any.whl → 1.0.3__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/routes/auth/auth.py +3 -3
- ivoryos/routes/control/control.py +89 -60
- ivoryos/routes/control/templates/control/controllers_home.html +7 -7
- ivoryos/routes/database/database.py +98 -39
- ivoryos/routes/database/templates/database/{experiment_database.html → scripts_database.html} +27 -18
- ivoryos/routes/database/templates/database/{workflow_run_database.html → workflow_database.html} +27 -5
- ivoryos/routes/design/design.py +215 -78
- ivoryos/routes/design/templates/design/experiment_run.html +62 -123
- ivoryos/static/js/socket_handler.js +13 -8
- ivoryos/utils/bo_campaign.py +87 -0
- ivoryos/utils/client_proxy.py +1 -1
- ivoryos/utils/db_models.py +27 -7
- ivoryos/utils/global_config.py +16 -7
- ivoryos/utils/script_runner.py +56 -40
- ivoryos/utils/task_runner.py +81 -0
- ivoryos/utils/utils.py +0 -68
- ivoryos/version.py +1 -1
- {ivoryos-1.0.0.dist-info → ivoryos-1.0.3.dist-info}/METADATA +3 -3
- {ivoryos-1.0.0.dist-info → ivoryos-1.0.3.dist-info}/RECORD +23 -21
- /ivoryos/routes/database/templates/database/{experiment_step_view.html → workflow_view.html} +0 -0
- {ivoryos-1.0.0.dist-info → ivoryos-1.0.3.dist-info}/LICENSE +0 -0
- {ivoryos-1.0.0.dist-info → ivoryos-1.0.3.dist-info}/WHEEL +0 -0
- {ivoryos-1.0.0.dist-info → ivoryos-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
</h2>
|
|
27
27
|
<div id="runpanel" class="accordion-collapse collapse show">
|
|
28
28
|
<div class="accordion-body">
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
<div class="row">
|
|
30
|
+
{% if script['script'] or script['prep'] or script['cleanup'] %}
|
|
31
31
|
<div class="col-lg-6 col-sm-12" id="run-panel" style="{{ 'display: none;' if pause_status else '' }}">
|
|
32
32
|
<ul class="nav nav-tabs" id="myTabs" role="tablist">
|
|
33
33
|
<li class="nav-item" role="presentation">
|
|
@@ -127,129 +127,62 @@
|
|
|
127
127
|
</form>
|
|
128
128
|
{% endif %}
|
|
129
129
|
</div>
|
|
130
|
-
|
|
131
|
-
{# <div class="tab-pane fade " id="tab3" role="tabpanel" aria-labelledby="tab3-tab">#}
|
|
132
|
-
{# <form role="form" method='POST' name="bo" action="{{ url_for('design.experiment_run')}}">#}
|
|
133
|
-
{# <div class="form-group">#}
|
|
134
|
-
{# <p><h5>Parameters:</h5><p>#}
|
|
135
|
-
{# {% for config in config_list %}#}
|
|
136
|
-
{# <div class="row g-3 align-items-center">#}
|
|
137
|
-
{# <div class="col-lg-3 col-sm-6 ">#}
|
|
138
|
-
{# {{config}}:#}
|
|
139
|
-
{# </div>#}
|
|
140
|
-
{# <div class="col-auto">#}
|
|
141
|
-
{# <label class="col-form-label" for="{{config}}_type">Type</label>#}
|
|
142
|
-
{# </div>#}
|
|
143
|
-
{# <div class="col-auto">#}
|
|
144
|
-
{# <select class="form-select" id="{{config}}_type" name="{{config}}_type">#}
|
|
145
|
-
{# <option selected value="range">range</option>#}
|
|
146
|
-
{# <option value="choice">choice</option>#}
|
|
147
|
-
{# <option value="fixed">fixed</option>#}
|
|
148
|
-
{# </select>#}
|
|
149
|
-
{# </div>#}
|
|
150
|
-
{# <div class="col-auto">#}
|
|
151
|
-
{# <label class="" for="{{config}}_value">Values</label>#}
|
|
152
|
-
{# </div>#}
|
|
153
|
-
{# <div class="col-auto">#}
|
|
154
|
-
{# <input type="text" class="form-control" id="{{config}}_value" name="{{config}}_value" placeholder="1, 2, 3">#}
|
|
155
|
-
{# </div>#}
|
|
156
|
-
{# <div class="col-auto">#}
|
|
157
|
-
{# <input type="text" class="form-control" id="{{config}}_value_max" style="display: none;" placeholder="1, 2, 3">#}
|
|
158
|
-
{# </div>#}
|
|
159
|
-
{# </div>#}
|
|
160
|
-
{# {% endfor %}#}
|
|
161
|
-
{# <p><h5>Objective:</h5><p>#}
|
|
162
|
-
{# {% for objective in return_list %}#}
|
|
163
|
-
{# <div class="row gy-2 gx-3 align-items-center input-group">#}
|
|
164
|
-
{# <div class="col-3">#}
|
|
165
|
-
{# {{objective}}:#}
|
|
166
|
-
{# </div>#}
|
|
167
|
-
{# <div class="col-auto">#}
|
|
168
|
-
{# <label class="" for="{{objective}}_min">Minimize</label>#}
|
|
169
|
-
{# </div>#}
|
|
170
|
-
{# <div class="col-auto">#}
|
|
171
|
-
{# <select class="form-select" id="{{objective}}_min" name="{{objective}}_min">#}
|
|
172
|
-
{# <option selected>minimize</option>#}
|
|
173
|
-
{# <option>maximize</option>#}
|
|
174
|
-
{# <option>none</option>#}
|
|
175
|
-
{# </select>#}
|
|
176
|
-
{# </div>#}
|
|
177
|
-
{# <div class="col-auto">#}
|
|
178
|
-
{# <label class="" for="{{objective}}_threshold">Threshold</label>#}
|
|
179
|
-
{# </div>#}
|
|
180
|
-
{# <div class="col-auto">#}
|
|
181
|
-
{# <input type="text" class="form-control" id="{{objective}}_threshold" name="{{objective}}_threshold" placeholder="None">#}
|
|
182
|
-
{# </div>#}
|
|
183
|
-
{# </div>#}
|
|
184
|
-
{# {% endfor %}#}
|
|
185
|
-
{# <p><h5>Budget:</h5></p>#}
|
|
186
|
-
{# <div class="input-group mb-3">#}
|
|
187
|
-
{# <label class="input-group-text" for="repeat">Max iteration </label>#}
|
|
188
|
-
{# <input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="25">#}
|
|
189
|
-
{# </div>#}
|
|
190
|
-
{# {% if not no_deck_warning%}#}
|
|
191
|
-
{# <div class="input-group mb-3">#}
|
|
192
|
-
{# <button class="form-control" type="submit" name="bo">Run</button>#}
|
|
193
|
-
{# </div>#}
|
|
194
|
-
{# {% endif %}#}
|
|
195
|
-
{# </div>#}
|
|
196
|
-
{# </form>#}
|
|
197
|
-
{# </div>#}
|
|
198
|
-
<div class="tab-pane fade" id="tab3" role="tabpanel" aria-labelledby="tab3-tab">
|
|
199
|
-
<form method="POST" name="bo" action="{{ url_for('design.experiment_run') }}">
|
|
200
|
-
<div class="container py-2">
|
|
201
130
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
<div class="row align-items-center mb-2">
|
|
206
|
-
<div class="col-3 col-form-label-sm">
|
|
207
|
-
{{ config }}:
|
|
208
|
-
</div>
|
|
209
|
-
<div class="col-6">
|
|
210
|
-
<select class="form-select form-select-sm" id="{{config}}_type" name="{{config}}_type">
|
|
211
|
-
<option selected value="range">range</option>
|
|
212
|
-
<option value="choice">choice</option>
|
|
213
|
-
<option value="fixed">fixed</option>
|
|
214
|
-
</select>
|
|
215
|
-
</div>
|
|
216
|
-
<div class="col-3">
|
|
217
|
-
<input type="text" class="form-control form-control-sm" id="{{config}}_value" name="{{config}}_value" placeholder="1, 2, 3">
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
{% endfor %}
|
|
131
|
+
<div class="tab-pane fade" id="tab3" role="tabpanel" aria-labelledby="tab3-tab">
|
|
132
|
+
<form method="POST" name="bo" action="{{ url_for('design.experiment_run') }}">
|
|
133
|
+
<div class="container py-2">
|
|
221
134
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
135
|
+
<!-- Parameters -->
|
|
136
|
+
<h6 class="fw-bold mt-2 mb-1">Parameters</h6>
|
|
137
|
+
{% for config in config_list %}
|
|
138
|
+
<div class="row align-items-center mb-2">
|
|
139
|
+
<div class="col-3 col-form-label-sm">
|
|
140
|
+
{{ config }}:
|
|
141
|
+
</div>
|
|
142
|
+
<div class="col-6">
|
|
143
|
+
<select class="form-select form-select-sm" id="{{config}}_type" name="{{config}}_type">
|
|
144
|
+
<option selected value="range">range</option>
|
|
145
|
+
<option value="choice">choice</option>
|
|
146
|
+
<option value="fixed">fixed</option>
|
|
147
|
+
</select>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="col-3">
|
|
150
|
+
<input type="text" class="form-control form-control-sm" id="{{config}}_value" name="{{config}}_value" placeholder="1, 2, 3">
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
{% endfor %}
|
|
238
154
|
|
|
239
|
-
|
|
155
|
+
<!-- Objective -->
|
|
156
|
+
<h6 class="fw-bold mt-3 mb-1">Objectives</h6>
|
|
157
|
+
{% for objective in return_list %}
|
|
158
|
+
<div class="row align-items-center mb-2">
|
|
159
|
+
<div class="col-3 col-form-label-sm">
|
|
160
|
+
{{ objective }}:
|
|
161
|
+
</div>
|
|
162
|
+
<div class="col-6">
|
|
163
|
+
<select class="form-select form-select-sm" id="{{objective}}_min" name="{{objective}}_min">
|
|
164
|
+
<option selected>minimize</option>
|
|
165
|
+
<option>maximize</option>
|
|
166
|
+
<option>none</option>
|
|
167
|
+
</select>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
{% endfor %}
|
|
240
171
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
</div>
|
|
172
|
+
<h6 class="fw-bold mt-3 mb-1">Budget</h6>
|
|
173
|
+
|
|
174
|
+
<div class="input-group mb-3">
|
|
175
|
+
<label class="input-group-text" for="repeat">Max iteration </label>
|
|
176
|
+
<input class="form-control" type="number" id="repeat" name="repeat" min="1" max="1000" value="25">
|
|
177
|
+
</div>
|
|
178
|
+
{% if not no_deck_warning%}
|
|
179
|
+
<div class="input-group mb-3">
|
|
180
|
+
<button class="form-control" type="submit" name="bo">Run</button>
|
|
181
|
+
</div>
|
|
182
|
+
{% endif %}
|
|
183
|
+
</div>
|
|
184
|
+
</form>
|
|
185
|
+
</div>
|
|
253
186
|
|
|
254
187
|
|
|
255
188
|
<div class="tab-pane fade {{ 'show active' if config_list and config_list|count<=5 else '' }}" id="tab4" role="tabpanel" aria-labelledby="tab4-tab">
|
|
@@ -278,6 +211,12 @@
|
|
|
278
211
|
</div>
|
|
279
212
|
</div>
|
|
280
213
|
</div>
|
|
214
|
+
{% else %}
|
|
215
|
+
<div class="col-lg-6 col-sm-12" id="placeholder-panel">
|
|
216
|
+
|
|
217
|
+
</div>
|
|
218
|
+
{% endif %}
|
|
219
|
+
|
|
281
220
|
<div class="col-lg-6 col-sm-12" id="code-panel" style="{{ '' if pause_status else 'display: none;'}}">
|
|
282
221
|
<p>
|
|
283
222
|
<h5>Progress:</h5>
|
|
@@ -299,7 +238,7 @@
|
|
|
299
238
|
{% endif %}
|
|
300
239
|
{% if "cleanup" in line_collection.keys() %}
|
|
301
240
|
{% set stype = "cleanup" %}
|
|
302
|
-
<h6>
|
|
241
|
+
<h6>Cleanup:</h6>
|
|
303
242
|
{% for code in line_collection["cleanup"] %}
|
|
304
243
|
<pre style="margin: 0; padding: 0; line-height: 1;"><code class="python" id="{{ stype }}-{{ loop.index0 }}" >{{code}}</code></pre>
|
|
305
244
|
{% endfor %}
|
|
@@ -337,7 +276,7 @@
|
|
|
337
276
|
<div id="logging-panel"></div>
|
|
338
277
|
</div>
|
|
339
278
|
</div>
|
|
340
|
-
|
|
279
|
+
|
|
341
280
|
</div>
|
|
342
281
|
</div>
|
|
343
282
|
</div>
|
|
@@ -10,21 +10,26 @@ 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
|
+
const runPanel = document.getElementById("run-panel");
|
|
14
|
+
const codePanel = document.getElementById("code-panel");
|
|
13
15
|
if (progress === 1) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
if (runPanel) runPanel.style.display = "none";
|
|
17
|
+
if (codePanel) {
|
|
18
|
+
codePanel.style.display = "block";
|
|
19
|
+
codePanel.scrollIntoView({ behavior: "smooth" });
|
|
20
|
+
}
|
|
21
|
+
progressBar.classList.remove('bg-success');
|
|
22
|
+
progressBar.classList.remove('bg-danger');
|
|
23
|
+
progressBar.classList.add('progress-bar-animated');
|
|
19
24
|
}
|
|
20
25
|
if (progress === 100) {
|
|
21
26
|
// Remove animation and set green color when 100% is reached
|
|
22
27
|
progressBar.classList.remove('progress-bar-animated');
|
|
23
28
|
progressBar.classList.add('bg-success'); // Bootstrap class for green color
|
|
24
29
|
setTimeout(() => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
if (runPanel) runPanel.style.display = "block";
|
|
31
|
+
if (codePanel) codePanel.style.display = "none";
|
|
32
|
+
}, 1000); // Small delay to let users see the completion
|
|
28
33
|
}
|
|
29
34
|
});
|
|
30
35
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from ivoryos.utils.utils import install_and_import
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def ax_init_form(data, arg_types):
|
|
5
|
+
"""
|
|
6
|
+
create Ax campaign from the web form input
|
|
7
|
+
:param data:
|
|
8
|
+
"""
|
|
9
|
+
install_and_import("ax", "ax-platform")
|
|
10
|
+
parameter, objectives = ax_wrapper(data, arg_types)
|
|
11
|
+
from ax.service.ax_client import AxClient
|
|
12
|
+
ax_client = AxClient()
|
|
13
|
+
ax_client.create_experiment(parameter, objectives=objectives)
|
|
14
|
+
return ax_client
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def ax_wrapper(data: dict, arg_types: list):
|
|
18
|
+
"""
|
|
19
|
+
Ax platform wrapper function for creating optimization campaign parameters and objective from the web form input
|
|
20
|
+
:param data: e.g.,
|
|
21
|
+
{
|
|
22
|
+
"param_1_type": "range", "param_1_value": [1,2],
|
|
23
|
+
"param_2_type": "range", "param_2_value": [1,2],
|
|
24
|
+
"obj_1_min": True,
|
|
25
|
+
"obj_2_min": True
|
|
26
|
+
}
|
|
27
|
+
:return: the optimization campaign parameters
|
|
28
|
+
parameter=[
|
|
29
|
+
{"name": "param_1", "type": "range", "bounds": [1,2]},
|
|
30
|
+
{"name": "param_1", "type": "range", "bounds": [1,2]}
|
|
31
|
+
]
|
|
32
|
+
objectives=[
|
|
33
|
+
{"name": "obj_1", "min": True, "threshold": None},
|
|
34
|
+
{"name": "obj_2", "min": True, "threshold": None},
|
|
35
|
+
]
|
|
36
|
+
"""
|
|
37
|
+
from ax.service.utils.instantiation import ObjectiveProperties
|
|
38
|
+
parameter = []
|
|
39
|
+
objectives = {}
|
|
40
|
+
# Iterate through the webui_data dictionary
|
|
41
|
+
for key, value in data.items():
|
|
42
|
+
# Check if the key corresponds to a parameter type
|
|
43
|
+
if "_type" in key:
|
|
44
|
+
param_name = key.split("_type")[0]
|
|
45
|
+
param_type = value
|
|
46
|
+
param_value = data[f"{param_name}_value"].split(",")
|
|
47
|
+
try:
|
|
48
|
+
values = [float(v) for v in param_value]
|
|
49
|
+
except Exception:
|
|
50
|
+
values = param_value
|
|
51
|
+
if param_type == "range":
|
|
52
|
+
param = {"name": param_name, "type": param_type, "bounds": values}
|
|
53
|
+
if param_type == "choice":
|
|
54
|
+
param = {"name": param_name, "type": param_type, "values": values}
|
|
55
|
+
if param_type == "fixed":
|
|
56
|
+
param = {"name": param_name, "type": param_type, "value": values[0]}
|
|
57
|
+
_type = arg_types[param_name] if arg_types[param_name] in ["str", "bool", "int"] else "float"
|
|
58
|
+
param.update({"value_type": _type})
|
|
59
|
+
parameter.append(param)
|
|
60
|
+
elif key.endswith("_min"):
|
|
61
|
+
if not value == 'none':
|
|
62
|
+
obj_name = key.split("_min")[0]
|
|
63
|
+
is_min = True if value == "minimize" else False
|
|
64
|
+
|
|
65
|
+
threshold = None if f"{obj_name}_threshold" not in data else data[f"{obj_name}_threshold"]
|
|
66
|
+
properties = ObjectiveProperties(minimize=is_min)
|
|
67
|
+
objectives[obj_name] = properties
|
|
68
|
+
|
|
69
|
+
return parameter, objectives
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def ax_init_opc(bo_args):
|
|
73
|
+
install_and_import("ax", "ax-platform")
|
|
74
|
+
from ax.service.ax_client import AxClient
|
|
75
|
+
from ax.service.utils.instantiation import ObjectiveProperties
|
|
76
|
+
|
|
77
|
+
ax_client = AxClient()
|
|
78
|
+
objectives = bo_args.get("objectives")
|
|
79
|
+
objectives_formatted = {}
|
|
80
|
+
for obj in objectives:
|
|
81
|
+
obj_name = obj.get("name")
|
|
82
|
+
minimize = obj.get("minimize")
|
|
83
|
+
objectives_formatted[obj_name] = ObjectiveProperties(minimize=minimize)
|
|
84
|
+
bo_args["objectives"] = objectives_formatted
|
|
85
|
+
ax_client.create_experiment(**bo_args)
|
|
86
|
+
|
|
87
|
+
return ax_client
|
ivoryos/utils/client_proxy.py
CHANGED
|
@@ -8,7 +8,7 @@ import os
|
|
|
8
8
|
|
|
9
9
|
# Function to create class and methods dynamically
|
|
10
10
|
def create_function(url, class_name, functions):
|
|
11
|
-
class_template = f'class {class_name.capitalize()}:\n url = "{url}ivoryos/
|
|
11
|
+
class_template = f'class {class_name.capitalize()}:\n url = "{url}ivoryos/api/control/deck.{class_name}"\n'
|
|
12
12
|
|
|
13
13
|
for function_name, details in functions.items():
|
|
14
14
|
signature = details['signature']
|
ivoryos/utils/db_models.py
CHANGED
|
@@ -48,7 +48,8 @@ class Script(db.Model):
|
|
|
48
48
|
|
|
49
49
|
def __init__(self, name=None, deck=None, status=None, script_dict: dict = None, id_order: dict = None,
|
|
50
50
|
time_created=None, last_modified=None, editing_type=None, author: str = None,
|
|
51
|
-
registered:bool=False,
|
|
51
|
+
# registered:bool=False,
|
|
52
|
+
python_script: str = None
|
|
52
53
|
):
|
|
53
54
|
if script_dict is None:
|
|
54
55
|
script_dict = {"prep": [], "script": [], "cleanup": []}
|
|
@@ -76,6 +77,7 @@ class Script(db.Model):
|
|
|
76
77
|
self.id_order = id_order
|
|
77
78
|
self.editing_type = editing_type
|
|
78
79
|
self.author = author
|
|
80
|
+
self.python_script = python_script
|
|
79
81
|
# self.r = registered
|
|
80
82
|
|
|
81
83
|
def as_dict(self):
|
|
@@ -412,12 +414,13 @@ class Script(db.Model):
|
|
|
412
414
|
"""
|
|
413
415
|
line_collection = {}
|
|
414
416
|
for stype, func_str in exec_str_collection.items():
|
|
415
|
-
|
|
416
|
-
|
|
417
|
+
if func_str:
|
|
418
|
+
module = ast.parse(func_str)
|
|
419
|
+
func_def = next(node for node in module.body if isinstance(node, ast.FunctionDef))
|
|
417
420
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
+
# Extract function body as source lines
|
|
422
|
+
line_collection[stype] = [ast.unparse(node) for node in func_def.body if not isinstance(node, ast.Return)]
|
|
423
|
+
# print(line_collection[stype])
|
|
421
424
|
return line_collection
|
|
422
425
|
|
|
423
426
|
def compile(self, script_path=None):
|
|
@@ -670,7 +673,24 @@ class WorkflowStep(db.Model):
|
|
|
670
673
|
run_error = db.Column(db.Boolean, default=False)
|
|
671
674
|
|
|
672
675
|
def as_dict(self):
|
|
673
|
-
dict = self.__dict__
|
|
676
|
+
dict = self.__dict__.copy()
|
|
677
|
+
dict.pop('_sa_instance_state', None)
|
|
678
|
+
return dict
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
class SingleStep(db.Model):
|
|
682
|
+
__tablename__ = 'single_steps'
|
|
683
|
+
|
|
684
|
+
id = db.Column(db.Integer, primary_key=True)
|
|
685
|
+
method_name = db.Column(db.String(128), nullable=False)
|
|
686
|
+
kwargs = db.Column(JSONType, nullable=False)
|
|
687
|
+
start_time = db.Column(db.DateTime)
|
|
688
|
+
end_time = db.Column(db.DateTime)
|
|
689
|
+
run_error = db.Column(db.String(128))
|
|
690
|
+
output = db.Column(JSONType)
|
|
691
|
+
|
|
692
|
+
def as_dict(self):
|
|
693
|
+
dict = self.__dict__.copy()
|
|
674
694
|
dict.pop('_sa_instance_state', None)
|
|
675
695
|
return dict
|
|
676
696
|
|
ivoryos/utils/global_config.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import threading
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class GlobalConfig:
|
|
@@ -13,7 +13,8 @@ class GlobalConfig:
|
|
|
13
13
|
cls._instance._defined_variables = {}
|
|
14
14
|
cls._instance._api_variables = set()
|
|
15
15
|
cls._instance._deck_snapshot = {}
|
|
16
|
-
cls._instance.
|
|
16
|
+
cls._instance._runner_lock = threading.Lock()
|
|
17
|
+
cls._instance._runner_status = None
|
|
17
18
|
return cls._instance
|
|
18
19
|
|
|
19
20
|
@property
|
|
@@ -70,9 +71,17 @@ class GlobalConfig:
|
|
|
70
71
|
self._api_variables = value
|
|
71
72
|
|
|
72
73
|
@property
|
|
73
|
-
def
|
|
74
|
-
return self.
|
|
74
|
+
def runner_lock(self):
|
|
75
|
+
return self._runner_lock
|
|
75
76
|
|
|
76
|
-
@
|
|
77
|
-
def
|
|
78
|
-
self.
|
|
77
|
+
@runner_lock.setter
|
|
78
|
+
def runner_lock(self, value):
|
|
79
|
+
self._runner_lock = value
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def runner_status(self):
|
|
83
|
+
return self._runner_status
|
|
84
|
+
|
|
85
|
+
@runner_status.setter
|
|
86
|
+
def runner_status(self, value):
|
|
87
|
+
self._runner_status = value
|