ivoryos 1.0.9__py3-none-any.whl → 1.1.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 +18 -6
- ivoryos/routes/api/api.py +109 -0
- ivoryos/routes/auth/auth.py +5 -5
- ivoryos/routes/control/control.py +55 -353
- ivoryos/routes/control/control_file.py +36 -0
- ivoryos/routes/control/control_new_device.py +142 -0
- ivoryos/routes/control/templates/controllers.html +137 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/control/utils.py +38 -0
- ivoryos/routes/data/data.py +108 -0
- ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +7 -7
- ivoryos/routes/{database/templates/database → data/templates}/workflow_view.html +3 -3
- ivoryos/routes/design/__init__.py +4 -0
- ivoryos/routes/design/design.py +96 -517
- ivoryos/routes/design/design_file.py +57 -0
- ivoryos/routes/design/design_step.py +43 -0
- ivoryos/routes/design/templates/components/action_form.html +52 -0
- ivoryos/routes/design/templates/components/action_list.html +15 -0
- ivoryos/routes/design/templates/components/autofill_toggle.html +14 -0
- ivoryos/routes/design/templates/components/canvas.html +14 -0
- ivoryos/routes/design/templates/components/canvas_footer.html +5 -0
- ivoryos/routes/design/templates/components/canvas_header.html +54 -0
- ivoryos/routes/design/templates/components/deck_selector.html +12 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +29 -0
- ivoryos/routes/design/templates/components/instrument_panel.html +23 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +19 -0
- ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +18 -0
- ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
- ivoryos/routes/design/templates/components/modals.html +6 -0
- ivoryos/routes/design/templates/components/operations_panel.html +43 -0
- ivoryos/routes/design/templates/components/python_code_overlay.html +17 -0
- ivoryos/routes/design/templates/components/script_info.html +31 -0
- ivoryos/routes/design/templates/components/scripts.html +50 -0
- ivoryos/routes/design/templates/components/sidebar.html +16 -0
- ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos/routes/design/templates/experiment_builder.html +41 -0
- ivoryos/routes/execute/__init__.py +0 -0
- ivoryos/routes/execute/execute.py +173 -0
- ivoryos/routes/execute/execute_file.py +44 -0
- ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
- ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
- ivoryos/routes/execute/templates/components/run_panel.html +9 -0
- ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +147 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
- ivoryos/routes/execute/templates/experiment_run.html +294 -0
- ivoryos/routes/library/__init__.py +0 -0
- ivoryos/routes/{database/database.py → library/library.py} +10 -112
- ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +8 -8
- ivoryos/routes/main/main.py +1 -1
- ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
- ivoryos/socket_handlers.py +52 -0
- ivoryos/templates/base.html +4 -4
- ivoryos/utils/bo_campaign.py +43 -3
- ivoryos/utils/form.py +1 -0
- ivoryos/utils/py_to_json.py +225 -0
- ivoryos/utils/script_runner.py +30 -7
- ivoryos/version.py +1 -1
- {ivoryos-1.0.9.dist-info → ivoryos-1.1.0.dist-info}/METADATA +5 -8
- ivoryos-1.1.0.dist-info/RECORD +102 -0
- ivoryos/routes/control/templates/control/controllers.html +0 -78
- ivoryos/routes/control/templates/control/controllers_home.html +0 -55
- ivoryos/routes/control/templates/control/controllers_new.html +0 -89
- ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
- ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- ivoryos-1.0.9.dist-info/RECORD +0 -61
- /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
- /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
- /ivoryos/routes/{database → data}/__init__.py +0 -0
- /ivoryos/routes/{database/templates/database → data/templates/components}/step_card.html +0 -0
- /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.1.0.dist-info}/LICENSE +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.1.0.dist-info}/WHEEL +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.1.0.dist-info}/top_level.txt +0 -0
ivoryos/utils/bo_campaign.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from ivoryos.utils.utils import install_and_import
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def ax_init_form(data, arg_types):
|
|
4
|
+
def ax_init_form(data, arg_types, previous_data_len=0):
|
|
5
5
|
"""
|
|
6
6
|
create Ax campaign from the web form input
|
|
7
7
|
:param data:
|
|
@@ -9,7 +9,11 @@ def ax_init_form(data, arg_types):
|
|
|
9
9
|
install_and_import("ax", "ax-platform")
|
|
10
10
|
parameter, objectives = ax_wrapper(data, arg_types)
|
|
11
11
|
from ax.service.ax_client import AxClient
|
|
12
|
-
|
|
12
|
+
if previous_data_len > 0:
|
|
13
|
+
gs = exisitng_data_gs(previous_data_len)
|
|
14
|
+
ax_client = AxClient(generation_strategy=gs)
|
|
15
|
+
else:
|
|
16
|
+
ax_client = AxClient()
|
|
13
17
|
ax_client.create_experiment(parameter, objectives=objectives)
|
|
14
18
|
return ax_client
|
|
15
19
|
|
|
@@ -84,4 +88,40 @@ def ax_init_opc(bo_args):
|
|
|
84
88
|
bo_args["objectives"] = objectives_formatted
|
|
85
89
|
ax_client.create_experiment(**bo_args)
|
|
86
90
|
|
|
87
|
-
return ax_client
|
|
91
|
+
return ax_client
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def exisitng_data_gs(data_len):
|
|
95
|
+
"""
|
|
96
|
+
temporal generation strategy for existing data
|
|
97
|
+
"""
|
|
98
|
+
from ax.generation_strategy.generation_node import GenerationStep
|
|
99
|
+
from ax.generation_strategy.generation_strategy import GenerationStrategy
|
|
100
|
+
from ax.modelbridge.registry import Generators
|
|
101
|
+
if data_len > 4:
|
|
102
|
+
gs = GenerationStrategy(
|
|
103
|
+
steps=[
|
|
104
|
+
GenerationStep(
|
|
105
|
+
model=Generators.BOTORCH_MODULAR,
|
|
106
|
+
num_trials=-1,
|
|
107
|
+
max_parallelism=3,
|
|
108
|
+
),
|
|
109
|
+
]
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
gs = GenerationStrategy(
|
|
113
|
+
steps=[
|
|
114
|
+
GenerationStep(
|
|
115
|
+
model=Generators.SOBOL,
|
|
116
|
+
num_trials=5-data_len, # how many sobol trials to perform (rule of thumb: 2 * number of params)
|
|
117
|
+
max_parallelism=5,
|
|
118
|
+
model_kwargs={"seed": 999},
|
|
119
|
+
),
|
|
120
|
+
GenerationStep(
|
|
121
|
+
model=Generators.BOTORCH_MODULAR,
|
|
122
|
+
num_trials=-1,
|
|
123
|
+
max_parallelism=3,
|
|
124
|
+
),
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
return gs
|
ivoryos/utils/form.py
CHANGED
|
@@ -321,6 +321,7 @@ def create_add_form(attr, attr_name, autofill: bool, script=None, design: bool =
|
|
|
321
321
|
"""
|
|
322
322
|
signature = attr.get('signature', {})
|
|
323
323
|
docstring = attr.get('docstring', "")
|
|
324
|
+
# print(signature, docstring)
|
|
324
325
|
dynamic_form = create_form_for_method(signature, autofill, script, design)
|
|
325
326
|
if design:
|
|
326
327
|
return_value = StringField(label='Save value as', render_kw={"placeholder": "Optional"})
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import json
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
def generate_uuid():
|
|
6
|
+
return int(str(uuid.uuid4().int)[:15])
|
|
7
|
+
|
|
8
|
+
def infer_type(value):
|
|
9
|
+
if isinstance(value, bool):
|
|
10
|
+
return "bool"
|
|
11
|
+
elif isinstance(value, int):
|
|
12
|
+
return "int"
|
|
13
|
+
elif isinstance(value, float):
|
|
14
|
+
return "float"
|
|
15
|
+
elif isinstance(value, str):
|
|
16
|
+
return "str"
|
|
17
|
+
elif isinstance(value, (ast.Name, str)) and str(value).startswith("#"):
|
|
18
|
+
return "float" # default fallback for variables
|
|
19
|
+
else:
|
|
20
|
+
return "unknown"
|
|
21
|
+
|
|
22
|
+
def convert_to_cards(source_code: str):
|
|
23
|
+
tree = ast.parse(source_code)
|
|
24
|
+
cards = []
|
|
25
|
+
card_id = 1
|
|
26
|
+
block_stack = [] # to track control flow UUIDs
|
|
27
|
+
|
|
28
|
+
def new_id():
|
|
29
|
+
nonlocal card_id
|
|
30
|
+
val = card_id
|
|
31
|
+
card_id += 1
|
|
32
|
+
return val
|
|
33
|
+
|
|
34
|
+
def add_card(card):
|
|
35
|
+
cards.append(card)
|
|
36
|
+
|
|
37
|
+
def is_supported_assignment(node):
|
|
38
|
+
return (
|
|
39
|
+
isinstance(node.targets[0], ast.Name) and
|
|
40
|
+
isinstance(node.value, (ast.Constant, ast.Num, ast.Str, ast.NameConstant))
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
class CardVisitor(ast.NodeVisitor):
|
|
44
|
+
def visit_FunctionDef(self, node):
|
|
45
|
+
self.defined_types = {
|
|
46
|
+
arg.arg: ast.unparse(arg.annotation) if arg.annotation else "float"
|
|
47
|
+
for arg in node.args.args
|
|
48
|
+
}
|
|
49
|
+
for stmt in node.body:
|
|
50
|
+
self.visit(stmt)
|
|
51
|
+
|
|
52
|
+
def visit_If(self, node):
|
|
53
|
+
uuid_ = generate_uuid()
|
|
54
|
+
block_stack.append(("if", uuid_))
|
|
55
|
+
|
|
56
|
+
add_card({
|
|
57
|
+
"action": "if",
|
|
58
|
+
"arg_types": {"statement": ""},
|
|
59
|
+
"args": {"statement": ast.unparse(node.test)},
|
|
60
|
+
"id": new_id(),
|
|
61
|
+
"instrument": "if",
|
|
62
|
+
"return": "",
|
|
63
|
+
"uuid": uuid_
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
for stmt in node.body:
|
|
67
|
+
self.visit(stmt)
|
|
68
|
+
|
|
69
|
+
if node.orelse:
|
|
70
|
+
add_card({
|
|
71
|
+
"action": "else",
|
|
72
|
+
"args": {},
|
|
73
|
+
"id": new_id(),
|
|
74
|
+
"instrument": "if",
|
|
75
|
+
"return": "",
|
|
76
|
+
"uuid": uuid_
|
|
77
|
+
})
|
|
78
|
+
for stmt in node.orelse:
|
|
79
|
+
self.visit(stmt)
|
|
80
|
+
|
|
81
|
+
_, block_uuid = block_stack.pop()
|
|
82
|
+
add_card({
|
|
83
|
+
"action": "endif",
|
|
84
|
+
"args": {},
|
|
85
|
+
"id": new_id(),
|
|
86
|
+
"instrument": "if",
|
|
87
|
+
"return": "",
|
|
88
|
+
"uuid": block_uuid
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
def visit_While(self, node):
|
|
92
|
+
uuid_ = generate_uuid()
|
|
93
|
+
block_stack.append(("while", uuid_))
|
|
94
|
+
|
|
95
|
+
add_card({
|
|
96
|
+
"action": "while",
|
|
97
|
+
"arg_types": {"statement": ""},
|
|
98
|
+
"args": {"statement": ast.unparse(node.test)},
|
|
99
|
+
"id": new_id(),
|
|
100
|
+
"instrument": "while",
|
|
101
|
+
"return": "",
|
|
102
|
+
"uuid": uuid_
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
for stmt in node.body:
|
|
106
|
+
self.visit(stmt)
|
|
107
|
+
|
|
108
|
+
_, block_uuid = block_stack.pop()
|
|
109
|
+
add_card({
|
|
110
|
+
"action": "endwhile",
|
|
111
|
+
"args": {},
|
|
112
|
+
"id": new_id(),
|
|
113
|
+
"instrument": "while",
|
|
114
|
+
"return": "",
|
|
115
|
+
"uuid": block_uuid
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
def visit_Assign(self, node):
|
|
119
|
+
if is_supported_assignment(node):
|
|
120
|
+
var_name = node.targets[0].id
|
|
121
|
+
value = node.value.value
|
|
122
|
+
add_card({
|
|
123
|
+
"action": var_name,
|
|
124
|
+
"arg_types": {"statement": infer_type(value)},
|
|
125
|
+
"args": {"statement": value},
|
|
126
|
+
"id": new_id(),
|
|
127
|
+
"instrument": "variable",
|
|
128
|
+
"return": "",
|
|
129
|
+
"uuid": generate_uuid()
|
|
130
|
+
})
|
|
131
|
+
elif isinstance(node.value, ast.Call):
|
|
132
|
+
self.handle_call(node.value, ret_var=node.targets[0].id)
|
|
133
|
+
|
|
134
|
+
def visit_Expr(self, node):
|
|
135
|
+
if isinstance(node.value, ast.Call):
|
|
136
|
+
self.handle_call(node.value)
|
|
137
|
+
|
|
138
|
+
def handle_call(self, node, ret_var=""):
|
|
139
|
+
func_parts = []
|
|
140
|
+
f = node.func
|
|
141
|
+
while isinstance(f, ast.Attribute):
|
|
142
|
+
func_parts.insert(0, f.attr)
|
|
143
|
+
f = f.value
|
|
144
|
+
if isinstance(f, ast.Name):
|
|
145
|
+
func_parts.insert(0, f.id)
|
|
146
|
+
if not func_parts or not func_parts[0].startswith("deck"):
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
instrument = ".".join(func_parts[:-1])
|
|
150
|
+
action = func_parts[-1]
|
|
151
|
+
|
|
152
|
+
args = {}
|
|
153
|
+
arg_types = {}
|
|
154
|
+
|
|
155
|
+
for kw in node.keywords:
|
|
156
|
+
if kw.arg is None and isinstance(kw.value, ast.Dict):
|
|
157
|
+
for k_node, v_node in zip(kw.value.keys, kw.value.values):
|
|
158
|
+
key = k_node.s if isinstance(k_node, ast.Constant) else ast.unparse(k_node)
|
|
159
|
+
if isinstance(v_node, ast.Constant):
|
|
160
|
+
value = v_node.value
|
|
161
|
+
elif isinstance(v_node, ast.Name):
|
|
162
|
+
value = f"#{v_node.id}"
|
|
163
|
+
else:
|
|
164
|
+
value = ast.unparse(v_node)
|
|
165
|
+
args[key] = value
|
|
166
|
+
arg_types[key] = infer_type(value)
|
|
167
|
+
else:
|
|
168
|
+
if isinstance(kw.value, ast.Constant):
|
|
169
|
+
value = kw.value.value
|
|
170
|
+
elif isinstance(kw.value, ast.Name):
|
|
171
|
+
value = f"#{kw.value.id}"
|
|
172
|
+
else:
|
|
173
|
+
value = ast.unparse(kw.value)
|
|
174
|
+
args[kw.arg] = value
|
|
175
|
+
arg_types[kw.arg] = (
|
|
176
|
+
self.defined_types.get(kw.value.id, "float")
|
|
177
|
+
if isinstance(kw.value, ast.Name)
|
|
178
|
+
else infer_type(value)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
add_card({
|
|
182
|
+
"action": action,
|
|
183
|
+
"arg_types": arg_types,
|
|
184
|
+
"args": args,
|
|
185
|
+
"id": new_id(),
|
|
186
|
+
"instrument": instrument,
|
|
187
|
+
"return": ret_var,
|
|
188
|
+
"uuid": generate_uuid()
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
CardVisitor().visit(tree)
|
|
192
|
+
return cards
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
if __name__ == "__main__":
|
|
196
|
+
test = '''def workflow_dynamic(solid_amount_mg, methanol_amount_ml):
|
|
197
|
+
"""
|
|
198
|
+
SDL workflow: dose solid, add methanol, equilibrate, and analyze
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
solid_amount_mg (float): Amount of solid to dose in mg
|
|
202
|
+
methanol_amount_ml (float): Amount of methanol to dose in ml
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
dict: Results containing analysis data
|
|
206
|
+
"""
|
|
207
|
+
# Step 1: Dose solid material
|
|
208
|
+
deck.sdl.dose_solid(amount_in_mg=solid_amount_mg)
|
|
209
|
+
|
|
210
|
+
# Step 2: Add methanol solvent
|
|
211
|
+
deck.sdl.dose_solvent(solvent_name='Methanol', amount_in_ml=methanol_amount_ml)
|
|
212
|
+
|
|
213
|
+
# Step 3: Equilibrate at room temperature (assuming ~23°C) for 20 seconds
|
|
214
|
+
deck.sdl.equilibrate(temp=23.0, duration=20.0)
|
|
215
|
+
|
|
216
|
+
# Step 4: Analyze the sample
|
|
217
|
+
analysis_results = deck.sdl.analyze(param_1=1, param_2=2)
|
|
218
|
+
|
|
219
|
+
# Brief pause for system stability
|
|
220
|
+
time.sleep(1.0)
|
|
221
|
+
|
|
222
|
+
# Return only analysis results
|
|
223
|
+
return {'analysis_results': analysis_results}
|
|
224
|
+
'''
|
|
225
|
+
print(json.dumps(convert_to_cards(test)))
|
ivoryos/utils/script_runner.py
CHANGED
|
@@ -62,11 +62,11 @@ class ScriptRunner:
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
|
|
65
|
-
output_path="", compiled=False, current_app=None):
|
|
65
|
+
output_path="", compiled=False, current_app=None, history=None):
|
|
66
66
|
global deck
|
|
67
67
|
if deck is None:
|
|
68
68
|
deck = global_config.deck
|
|
69
|
-
|
|
69
|
+
print("history", history)
|
|
70
70
|
if self.current_app is None:
|
|
71
71
|
self.current_app = current_app
|
|
72
72
|
# time.sleep(1) # Optional: may help ensure deck readiness
|
|
@@ -81,7 +81,8 @@ class ScriptRunner:
|
|
|
81
81
|
|
|
82
82
|
thread = threading.Thread(
|
|
83
83
|
target=self._run_with_stop_check,
|
|
84
|
-
args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path, current_app, compiled
|
|
84
|
+
args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path, current_app, compiled,
|
|
85
|
+
history)
|
|
85
86
|
)
|
|
86
87
|
thread.start()
|
|
87
88
|
return thread
|
|
@@ -182,7 +183,7 @@ class ScriptRunner:
|
|
|
182
183
|
return exec_locals # Return the 'results' variable
|
|
183
184
|
|
|
184
185
|
def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
|
|
185
|
-
output_path, current_app, compiled):
|
|
186
|
+
output_path, current_app, compiled, history=None):
|
|
186
187
|
time.sleep(1)
|
|
187
188
|
# _func_str = script.compile()
|
|
188
189
|
# step_list_dict: dict = script.convert_to_lines(_func_str)
|
|
@@ -205,7 +206,8 @@ class ScriptRunner:
|
|
|
205
206
|
# Run "script" section multiple times
|
|
206
207
|
if repeat_count:
|
|
207
208
|
self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
|
|
208
|
-
run_name, return_list, compiled, logger, socketio,
|
|
209
|
+
run_name, return_list, compiled, logger, socketio,
|
|
210
|
+
history, output_path, run_id=run_id)
|
|
209
211
|
elif config:
|
|
210
212
|
self._run_config_section(config, arg_type, output_list, script, run_name, logger,
|
|
211
213
|
socketio, run_id=run_id, compiled=compiled)
|
|
@@ -262,13 +264,28 @@ class ScriptRunner:
|
|
|
262
264
|
output_list.append(output)
|
|
263
265
|
|
|
264
266
|
def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list, compiled,
|
|
265
|
-
logger, socketio, run_id):
|
|
267
|
+
logger, socketio, history, output_path, run_id):
|
|
266
268
|
if bo_args:
|
|
267
269
|
logger.info('Initializing optimizer...')
|
|
268
270
|
if compiled:
|
|
269
271
|
ax_client = bo_campaign.ax_init_opc(bo_args)
|
|
270
272
|
else:
|
|
271
|
-
|
|
273
|
+
if history:
|
|
274
|
+
import pandas as pd
|
|
275
|
+
file_path = os.path.join(output_path, history)
|
|
276
|
+
previous_runs = pd.read_csv(file_path).to_dict(orient='records')
|
|
277
|
+
ax_client = bo_campaign.ax_init_form(bo_args, arg_types, len(previous_runs))
|
|
278
|
+
for row in previous_runs:
|
|
279
|
+
parameter = {key: value for key, value in row.items() if key in arg_types.keys()}
|
|
280
|
+
raw_data = {key: value for key, value in row.items() if key in return_list}
|
|
281
|
+
_, trial_index = ax_client.attach_trial(parameter)
|
|
282
|
+
ax_client.complete_trial(trial_index=trial_index, raw_data=raw_data)
|
|
283
|
+
output_list.append(row)
|
|
284
|
+
else:
|
|
285
|
+
ax_client = bo_campaign.ax_init_form(bo_args, arg_types)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
272
289
|
for i_progress in range(int(repeat_count)):
|
|
273
290
|
if self.stop_pending_event.is_set():
|
|
274
291
|
logger.info(f'Stopping execution during {run_name}: {i_progress + 1}/{int(repeat_count)}')
|
|
@@ -298,6 +315,12 @@ class ScriptRunner:
|
|
|
298
315
|
if output:
|
|
299
316
|
output_list.append(output)
|
|
300
317
|
logger.info(f'Output value: {output}')
|
|
318
|
+
|
|
319
|
+
if bo_args:
|
|
320
|
+
ax_client.save_to_json_file(os.path.join(output_path, f"{run_name}_ax_client.json"))
|
|
321
|
+
logger.info(
|
|
322
|
+
f'Optimization complete. Results saved to {os.path.join(output_path, f"{run_name}_ax_client.json")}'
|
|
323
|
+
)
|
|
301
324
|
return output_list
|
|
302
325
|
|
|
303
326
|
@staticmethod
|
ivoryos/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.0
|
|
1
|
+
__version__ = "1.1.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
|
|
5
5
|
Home-page: https://gitlab.com/heingroup/ivoryos
|
|
6
6
|
Author: Ivory Zhang
|
|
@@ -48,7 +48,7 @@ With the least modification of the current workflow, user can design, manage and
|
|
|
48
48
|
This software is developed and tested using Windows. This software and its dependencies are compatible across major platforms: Linux, macOS, and Windows. Some dependencies (Flask-SQLAlchemy) may require additional setup.
|
|
49
49
|
|
|
50
50
|
### Python Version
|
|
51
|
-
Python >=3.
|
|
51
|
+
Python >=3.10 for best compatibility. Python >=3.7 without Ax.
|
|
52
52
|
### Python dependencies
|
|
53
53
|
This software is compatible with the latest versions of all dependencies.
|
|
54
54
|
- bcrypt~=4.0
|
|
@@ -59,8 +59,7 @@ This software is compatible with the latest versions of all dependencies.
|
|
|
59
59
|
- SQLAlchemy-Utils~=0.41
|
|
60
60
|
- Flask-WTF~=1.2
|
|
61
61
|
- python-dotenv==1.0.1
|
|
62
|
-
-
|
|
63
|
-
- ax-platform (optional ~=0.3 or ~=0.4 for Python>=3.9)
|
|
62
|
+
- ax-platform (optional ~=0.4 for Python>=3.9)
|
|
64
63
|
|
|
65
64
|
## Installation
|
|
66
65
|
```bash
|
|
@@ -70,7 +69,7 @@ or
|
|
|
70
69
|
```bash
|
|
71
70
|
git clone https://gitlab.com/heingroup/ivoryos.git
|
|
72
71
|
cd ivoryos
|
|
73
|
-
pip install
|
|
72
|
+
pip install .
|
|
74
73
|
```
|
|
75
74
|
|
|
76
75
|
The installation may take 10 to 30 seconds to install. The installation time may vary and take up to several minutes, depending on the network speed, computer performance, and virtual environment settings.
|
|
@@ -98,7 +97,7 @@ Create an account and login (local database)
|
|
|
98
97
|
[//]: # ()
|
|
99
98
|
|
|
100
99
|
|
|
101
|
-
### Additional settings
|
|
100
|
+
### Additional settings (not actively maintained)
|
|
102
101
|
#### AI assistant
|
|
103
102
|
To streamline the experimental design on SDLs, we also integrate Large Language Models (LLMs) to interpret the inspected functions and generate code according to task descriptions.
|
|
104
103
|
|
|
@@ -150,8 +149,6 @@ ivoryos.run(__name__)
|
|
|
150
149
|
### Deck function and web form
|
|
151
150
|

|
|
152
151
|
|
|
153
|
-
### Text-to-code demo
|
|
154
|
-

|
|
155
152
|
|
|
156
153
|
### Directory structure
|
|
157
154
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
ivoryos/__init__.py,sha256=Rqw9LExz-XuluiXnwextp8wsa39k8R6UWAvE42lz058,8892
|
|
2
|
+
ivoryos/config.py,sha256=sk4dskm-K_Nv4uaA3QuE6xtew8wL6q3HmIoLgRm7p8U,2153
|
|
3
|
+
ivoryos/socket_handlers.py,sha256=VWVWiIdm4jYAutwGu6R0t1nK5MuMyOCL0xAnFn06jWQ,1302
|
|
4
|
+
ivoryos/version.py,sha256=LGVQyDsWifdACo7qztwb8RWWHds1E7uQ-ZqD8SAjyw4,22
|
|
5
|
+
ivoryos/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
ivoryos/routes/api/api.py,sha256=nHjRdZIYHKkA0FxU27qHd9rLlC6fa9ZcvHmYcqJFP9c,4003
|
|
7
|
+
ivoryos/routes/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
ivoryos/routes/auth/auth.py,sha256=SbtiMc1kFHDXIauvT1o3_8rJ-jVVz2C6hfKeSCmoXSE,3231
|
|
9
|
+
ivoryos/routes/auth/templates/login.html,sha256=WSRrKbdM_oobqSXFRTo-j9UlOgp6sYzS9tm7TqqPULI,1207
|
|
10
|
+
ivoryos/routes/auth/templates/signup.html,sha256=b5LTXtpfTSkSS7X8u1ldwQbbgEFTk6UNMAediA5BwBY,1465
|
|
11
|
+
ivoryos/routes/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
ivoryos/routes/control/control.py,sha256=P18Eu_Q-arrTxKjTbDtSp7JMy8TKeGVHq_rftuMmCBM,4914
|
|
13
|
+
ivoryos/routes/control/control_file.py,sha256=DTs2dZc1ZwS-4ZpKqEUL-krZ9b4ItYzFBv92NlY_h9I,1543
|
|
14
|
+
ivoryos/routes/control/control_new_device.py,sha256=aZuMGMgbz9fjfTfD0_u1ojwmqFFAlP8H6eyOYlCN9jM,5420
|
|
15
|
+
ivoryos/routes/control/utils.py,sha256=at11wA5HPAZN4BfMaymj1GKEvRTrqi4Wg6cTqUZJDjU,1155
|
|
16
|
+
ivoryos/routes/control/templates/controllers.html,sha256=Ltl7rVx_hMP10JZDh0SNcnMKDe1M5jFuNxCF2T9i32I,6863
|
|
17
|
+
ivoryos/routes/control/templates/controllers_new.html,sha256=5DNLZN4I2yKbv8RPtjwkhEGIoEVuPh4Gko6JvXhVtk4,5941
|
|
18
|
+
ivoryos/routes/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
ivoryos/routes/data/data.py,sha256=tEl8wetuZLAHObBffzBvI-3lQfHH_4HqLk8c0YQrKMQ,3406
|
|
20
|
+
ivoryos/routes/data/templates/workflow_database.html,sha256=wBOyAkBj6ErDLRYmgXFCFO8x3-DpKhiEWjU0K_FdVD4,4456
|
|
21
|
+
ivoryos/routes/data/templates/workflow_view.html,sha256=72xKreX9WhYx-0n0cFf-CL-fJIWXPCIaTi_Aa8Tq3xg,3651
|
|
22
|
+
ivoryos/routes/data/templates/components/step_card.html,sha256=F4JRfacrEQfk2rrEbcI_i7G84nzKKDmCrMSmStLb4W4,290
|
|
23
|
+
ivoryos/routes/design/__init__.py,sha256=zS3HXKaw0ALL5n6t_W1rUz5Uj5_tTQ-Y1VMXyzewvR0,113
|
|
24
|
+
ivoryos/routes/design/design.py,sha256=fwORYtBKvbhpE4H_rEXmiH5FwYuS9KJJ2U1TIgrUmCw,14700
|
|
25
|
+
ivoryos/routes/design/design_file.py,sha256=Ewm6AOa6eJhnbWF_sdnw2U0GObLPtXVW5uZxA1gredE,2119
|
|
26
|
+
ivoryos/routes/design/design_step.py,sha256=bKXIMZ5C1bRzvHYeAGn0TdFSvyklXZ2Eo8dmfEt56M8,1632
|
|
27
|
+
ivoryos/routes/design/templates/experiment_builder.html,sha256=SmLUH799N4BfdvMCJj41isIKIeWguSt8ZyVYcDkBcrA,1010
|
|
28
|
+
ivoryos/routes/design/templates/components/action_form.html,sha256=Tl_iq8vUmB2YKPGD0JND7Vph3hzmqL0Ljw5BO153zL0,2896
|
|
29
|
+
ivoryos/routes/design/templates/components/action_list.html,sha256=LsfWNCzZ8rLsa1_0TJ7xpGGtwtY5jvLcmO_oO2VKxoE,948
|
|
30
|
+
ivoryos/routes/design/templates/components/autofill_toggle.html,sha256=TddA5xSXA159HUtDCPrToznj4FXHYSLc4zRXOVrGGi0,828
|
|
31
|
+
ivoryos/routes/design/templates/components/canvas.html,sha256=THswB6cNBJ5Ucsz-42zjqvjgx69PzT7SF6TJ_bEj72w,499
|
|
32
|
+
ivoryos/routes/design/templates/components/canvas_footer.html,sha256=5Zndz5Q5risjWwEFMPw-Y1vQIqJTyXr6RwDz4XpCJQc,295
|
|
33
|
+
ivoryos/routes/design/templates/components/canvas_header.html,sha256=fwJ8lCugUaHjKi1fUrz3MSeJP_hbw88g5cZKpAtJGTk,3386
|
|
34
|
+
ivoryos/routes/design/templates/components/deck_selector.html,sha256=a4aaUjYZzzaB84zPgvBEI_sA8aqkxsbdEEdDwZvPWoA,836
|
|
35
|
+
ivoryos/routes/design/templates/components/edit_action_form.html,sha256=FALFNxjcABRRGRQdapTkT53UDBmyCVmP6Isp4STRZCM,1574
|
|
36
|
+
ivoryos/routes/design/templates/components/instrument_panel.html,sha256=6QKgkvn9jCpEQLUNPCa5g0bKADwiB5U8CKUwBVtjIYM,895
|
|
37
|
+
ivoryos/routes/design/templates/components/modals.html,sha256=6Dl8I8oD4ln7kK8C5e92pFVVH5KDte-vVTL0U_6NSTg,306
|
|
38
|
+
ivoryos/routes/design/templates/components/operations_panel.html,sha256=yQOuRr_wpY0s_i52erWiRTY1BnC8h0azFEyi-8b8zDE,2096
|
|
39
|
+
ivoryos/routes/design/templates/components/python_code_overlay.html,sha256=-gwXNJ4yYyTRf_hwL-fNEEE7ZERdeV1-4LaoAysDJ9c,807
|
|
40
|
+
ivoryos/routes/design/templates/components/script_info.html,sha256=aYI5QHjpeoWvwKAQWI6y4T3h7VpqbJZMq3U9m0zB2Hs,1710
|
|
41
|
+
ivoryos/routes/design/templates/components/scripts.html,sha256=y6Sw_Fjm6IeFG1qLA4bH5caeN4vEUukMwehRIjoTmp0,1703
|
|
42
|
+
ivoryos/routes/design/templates/components/sidebar.html,sha256=95mbPs-er3VN6Tf8oIp2vFCEQaNuCE4cu1I7SiPJ8Xg,509
|
|
43
|
+
ivoryos/routes/design/templates/components/text_to_code_panel.html,sha256=d-omdXk-PXAR5AyWPr4Rc4pqsebZOiTiMrnz3pPCnUY,1197
|
|
44
|
+
ivoryos/routes/design/templates/components/modals/drop_modal.html,sha256=edMqtMPE-t0UxW1uDbKxDXFWTrm_2xStnlrO67jvoG4,893
|
|
45
|
+
ivoryos/routes/design/templates/components/modals/json_modal.html,sha256=R-SeEdhtuDVbwOWYYH_hCdpul7y4ybCWoNwVIO5j49s,1122
|
|
46
|
+
ivoryos/routes/design/templates/components/modals/new_script_modal.html,sha256=46IGEY7Q5whn58T8h8dIeW1nUj_Y7hqXnnY8EfoKQno,957
|
|
47
|
+
ivoryos/routes/design/templates/components/modals/rename_modal.html,sha256=2NNSerxsWgA-k0KqTA_fhL9Y-Xt-OI0HGvjnMVEalGU,1241
|
|
48
|
+
ivoryos/routes/design/templates/components/modals/saveas_modal.html,sha256=hTyHgxse7uYSGsrLvoiaIRKKKp8UvQaapQmYHEtg4lk,1555
|
|
49
|
+
ivoryos/routes/execute/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
+
ivoryos/routes/execute/execute.py,sha256=xam3HywT7lR7HoMCqQ_txeClnCwK7ulpzLQkwPrE6Ys,7040
|
|
51
|
+
ivoryos/routes/execute/execute_file.py,sha256=GiEwVVhwx2H775JL7Cg_AWRZDTTK8sGMfygJRoR2aqc,1560
|
|
52
|
+
ivoryos/routes/execute/templates/experiment_run.html,sha256=D-ek7ISQrIQXy4PH37TnsURihbGNdpCgdTC8w79cwQc,10355
|
|
53
|
+
ivoryos/routes/execute/templates/components/error_modal.html,sha256=5Dmp9V0Ys6781Y-pKn_mc4N9J46c8EwIkjkHX22xCsw,1025
|
|
54
|
+
ivoryos/routes/execute/templates/components/logging_panel.html,sha256=FllozlPd6o7uBd8eflGjRktPV435J3XgiEeLZugoUi0,1623
|
|
55
|
+
ivoryos/routes/execute/templates/components/progress_panel.html,sha256=-nX76aFLxSOiYgI1xMjznC9rDYF-Vb92TmfjXYpBtps,1323
|
|
56
|
+
ivoryos/routes/execute/templates/components/run_panel.html,sha256=CmK-LYJ4K6RonHn6l9eJkqRw0XQizThOugxiXZonSDs,373
|
|
57
|
+
ivoryos/routes/execute/templates/components/run_tabs.html,sha256=u-msoTQPBGvyE_5_UczRtR9bh7zD3EvsgJhT77rTzOI,1145
|
|
58
|
+
ivoryos/routes/execute/templates/components/tab_bayesian.html,sha256=Ud50BE-O7FicFW6G057WIyS65ZEPVGmYhmRPf8yVB_M,7301
|
|
59
|
+
ivoryos/routes/execute/templates/components/tab_configuration.html,sha256=BVSKeHug2sxAfDYon2eMEnTwQEdf8PJTV7xnA4NSySs,5116
|
|
60
|
+
ivoryos/routes/execute/templates/components/tab_repeat.html,sha256=X-r7p78tVRrfwmAbhhZGBZbm78C4nTJS6O2A4dvzGEg,760
|
|
61
|
+
ivoryos/routes/library/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
|
+
ivoryos/routes/library/library.py,sha256=TFKY9ByiW1h0pskmwru4BjKLqei7lYKZ9CsElZbSkNY,6381
|
|
63
|
+
ivoryos/routes/library/templates/library.html,sha256=y2KIYt_f9QS8hdPtyz_x2vzx_AbxVaZNRFkGerM5T5U,3632
|
|
64
|
+
ivoryos/routes/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
|
+
ivoryos/routes/main/main.py,sha256=1AcSouCocHWjlpEED-ECn5OFiu0-3u0N-0st5RtKCVY,952
|
|
66
|
+
ivoryos/routes/main/templates/help.html,sha256=IOktMEsOPk0SCiMBXZ4mpffClERAyX8W82fel71M3M0,9370
|
|
67
|
+
ivoryos/routes/main/templates/home.html,sha256=BDvwkVthxniQ157H6E2hgYHT1Vv1GVBwu6dQejtzwoo,4633
|
|
68
|
+
ivoryos/static/favicon.ico,sha256=RhlrPtfITOkzC9BjP1UB1V5L9Oyp6NwNtWeMcGOnpyc,15406
|
|
69
|
+
ivoryos/static/logo.webp,sha256=lXgfQR-4mHTH83k7VV9iB54-oC2ipe6uZvbwdOnLETc,14974
|
|
70
|
+
ivoryos/static/style.css,sha256=zQVx35A5g6JMJ-K84-6fSKtzXGjp_p5ZVG6KLHPM2IE,4021
|
|
71
|
+
ivoryos/static/gui_annotation/Slide1.png,sha256=Lm4gdOkUF5HIUFaB94tl6koQVkzpitKj43GXV_XYMMc,121727
|
|
72
|
+
ivoryos/static/gui_annotation/Slide2.PNG,sha256=z3wQ9oVgg4JTWVLQGKK_KhtepRHUYP1e05XUWGT2A0I,118761
|
|
73
|
+
ivoryos/static/js/overlay.js,sha256=dPxop19es0E0ZUSY3d_4exIk7CJuQEnlW5uTt5fZfzI,483
|
|
74
|
+
ivoryos/static/js/socket_handler.js,sha256=2Iyv_3METjhSlSavs_L9FE3PKY4xDEpfzJpd2FywY9o,5300
|
|
75
|
+
ivoryos/static/js/sortable_card.js,sha256=ifmlGe3yy0U_KzMphV4ClRhK2DLOvkELYMlq1vECuac,807
|
|
76
|
+
ivoryos/static/js/sortable_design.js,sha256=d55AEz8LpPX_j-hREom-I19i4l-SOG2RVSR4CnozRtY,4366
|
|
77
|
+
ivoryos/templates/base.html,sha256=SdZswZmfLWehorMsoGkm-FjLFtB1ivLkdUJFbpDRqp4,8519
|
|
78
|
+
ivoryos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
+
ivoryos/utils/bo_campaign.py,sha256=7WSYVhCaDOuXIMJll_pMRUGWPQCnVt47uLHjlIomsv8,4608
|
|
80
|
+
ivoryos/utils/client_proxy.py,sha256=0OT2xTMkqh_2ybgCxMV_71ZVUThWwrsnAhTIBY5vDR8,2095
|
|
81
|
+
ivoryos/utils/db_models.py,sha256=HQSjMrZc1o1fvjx5Mf7W_53xPbT9aZ00xtUJJNPbwio,27502
|
|
82
|
+
ivoryos/utils/form.py,sha256=KlzTyG4sg1PQ-lnhKYpqaGugNWwL54PvmspqQfBoMu0,21967
|
|
83
|
+
ivoryos/utils/global_config.py,sha256=OqfDrPgOzRdIUMD4V3pA9t6b-BATMjGZl8Jn7nkI56k,2138
|
|
84
|
+
ivoryos/utils/llm_agent.py,sha256=-lVCkjPlpLues9sNTmaT7bT4sdhWvV2DiojNwzB2Lcw,6422
|
|
85
|
+
ivoryos/utils/py_to_json.py,sha256=fyqjaxDHPh-sahgT6IHSn34ktwf6y51_x1qvhbNlH-U,7314
|
|
86
|
+
ivoryos/utils/script_runner.py,sha256=YIdOs_s-UroN1f1s_VSlWY74dWamaGBAgxDUzqvrwfU,15791
|
|
87
|
+
ivoryos/utils/task_runner.py,sha256=u4nF0wOADu_HVlGYVTOXnUm1woWGgYAccr-ZCzgtb6Q,2899
|
|
88
|
+
ivoryos/utils/utils.py,sha256=BzgKIMMb7vyUIwYMhGDsWtwJEy5vNKtEHRJHHSiTSnM,13881
|
|
89
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
|
+
tests/conftest.py,sha256=u2sQ6U-Lghyl7et1Oz6J2E5VZ47VINKcjRM_2leAE2s,3627
|
|
91
|
+
tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
92
|
+
tests/integration/test_route_auth.py,sha256=l3ZDqr0oiCWS3yYSXGK5yMP6qI2t7Sv5I9zoYTkiyQU,2754
|
|
93
|
+
tests/integration/test_route_control.py,sha256=YYIll84bTUEKiAxFiFSz6LF3fTldPNfCtHs0IR3mSdM,3935
|
|
94
|
+
tests/integration/test_route_database.py,sha256=mS026W_hEuCTMpSkdRWvM-f4MYykK_6nRDJ4K5a7QA0,2342
|
|
95
|
+
tests/integration/test_route_design.py,sha256=PJAvGRiCY6B53Pu1v5vPAVHHsuaqRmRKk2eesSNshLU,1157
|
|
96
|
+
tests/integration/test_route_main.py,sha256=bmuf8Y_9CRWhiLLf4up11ltEd5YCdsLx6I-o26VGDEw,1228
|
|
97
|
+
tests/integration/test_sockets.py,sha256=4ZyFyExm7a-DYzVqpzEONpWeb1a0IT68wyFaQu0rY_Y,925
|
|
98
|
+
ivoryos-1.1.0.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
|
|
99
|
+
ivoryos-1.1.0.dist-info/METADATA,sha256=Wc_isMpJxE7DyOOrpBSoEILSAEOVgghO_LVIU7lt1Ys,8834
|
|
100
|
+
ivoryos-1.1.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
101
|
+
ivoryos-1.1.0.dist-info/top_level.txt,sha256=mIOiZkdpSwxFJt1R5fsyOff8mNprXHq1nMGNKNULIyE,14
|
|
102
|
+
ivoryos-1.1.0.dist-info/RECORD,,
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
{% extends 'base.html' %}
|
|
2
|
-
{% block title %}IvoryOS | Controller for {{instrument}}{% endblock %}
|
|
3
|
-
{% block body %}
|
|
4
|
-
<div id="overlay" class="overlay">
|
|
5
|
-
<div>
|
|
6
|
-
<h3 id="overlay-text"></h3>
|
|
7
|
-
<div class="spinner-border" role="status"></div>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
<h1>{{instrument}} controller</h1>
|
|
11
|
-
{% set hidden = session.get('hidden_functions', {}) %}
|
|
12
|
-
<div class="grid-container" id="sortable-grid">
|
|
13
|
-
{% for function, form in forms.items() %}
|
|
14
|
-
|
|
15
|
-
{% set hidden_instrument = hidden.get(instrument, []) %}
|
|
16
|
-
{% if function not in hidden_instrument %}
|
|
17
|
-
<div class="card" id="{{function}}">
|
|
18
|
-
<div class="bg-white rounded shadow-sm flex-fill">
|
|
19
|
-
<i class="bi bi-info-circle ms-2" data-bs-toggle="tooltip" data-bs-placement="top" title='{{ form.hidden_name.description or "Docstring is not available" }}' ></i>
|
|
20
|
-
<a style="float: right" aria-label="Close" href="{{ url_for('control.hide_function', instrument=instrument, function=function) }}"><i class="bi bi-eye-slash-fill"></i></a>
|
|
21
|
-
<div class="form-control" style="border: none">
|
|
22
|
-
<form role="form" method='POST' name="{{function}}" id="{{function}}">
|
|
23
|
-
<div class="form-group">
|
|
24
|
-
{{ form.hidden_tag() }}
|
|
25
|
-
{% for field in form %}
|
|
26
|
-
{% if field.type not in ['CSRFTokenField', 'HiddenField'] %}
|
|
27
|
-
<div class="input-group mb-3">
|
|
28
|
-
<label class="input-group-text">{{ field.label.text }}</label>
|
|
29
|
-
{% if field.type == "SubmitField" %}
|
|
30
|
-
{{ field(class="btn btn-dark") }}
|
|
31
|
-
{% elif field.type == "BooleanField" %}
|
|
32
|
-
{{ field(class="form-check-input") }}
|
|
33
|
-
{% else %}
|
|
34
|
-
{{ field(class="form-control") }}
|
|
35
|
-
{% endif %}
|
|
36
|
-
</div>
|
|
37
|
-
{% endif %}
|
|
38
|
-
{% endfor %}
|
|
39
|
-
</div>
|
|
40
|
-
<div class="input-group mb-3">
|
|
41
|
-
<button type="submit" name="{{ function }}" id="{{ function }}" class="form-control" style="background-color: #a5cece;">{{format_name(function)}} </button>
|
|
42
|
-
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
</form>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
{% endif %}
|
|
50
|
-
{% endfor %}
|
|
51
|
-
</div>
|
|
52
|
-
<div class="accordion accordion-flush" id="accordionActions" >
|
|
53
|
-
<div class="accordion-item">
|
|
54
|
-
<h4 class="accordion-header">
|
|
55
|
-
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#hidden">
|
|
56
|
-
Hidden functions
|
|
57
|
-
</button>
|
|
58
|
-
</h4>
|
|
59
|
-
</div>
|
|
60
|
-
<div id="hidden" class="accordion-collapse collapse" data-bs-parent="#accordionActions">
|
|
61
|
-
<div class="accordion-body">
|
|
62
|
-
{% set hidden_instrument = hidden.get(instrument, []) %}
|
|
63
|
-
{% for function in hidden_instrument %}
|
|
64
|
-
<div>
|
|
65
|
-
{{ function }} <a href="{{ url_for('control.remove_hidden', instrument=instrument, function=function) }}"><i class="bi bi-eye-fill"></i></a>
|
|
66
|
-
</div>
|
|
67
|
-
{% endfor %}
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
<script>
|
|
73
|
-
const saveOrderUrl = `{{ url_for('control.save_order', instrument=instrument) }}`;
|
|
74
|
-
const buttonIds = {{ session['card_order'][instrument] | tojson }};
|
|
75
|
-
</script>
|
|
76
|
-
<script src="{{ url_for('static', filename='js/sortable_card.js') }}"></script>
|
|
77
|
-
<script src="{{ url_for('static', filename='js/overlay.js') }}"></script>
|
|
78
|
-
{% endblock %}
|