ivoryos 1.0.8__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.

Files changed (78) hide show
  1. ivoryos/__init__.py +19 -7
  2. ivoryos/routes/api/api.py +109 -0
  3. ivoryos/routes/auth/auth.py +5 -5
  4. ivoryos/routes/control/control.py +55 -353
  5. ivoryos/routes/control/control_file.py +36 -0
  6. ivoryos/routes/control/control_new_device.py +142 -0
  7. ivoryos/routes/control/templates/controllers.html +137 -0
  8. ivoryos/routes/control/templates/controllers_new.html +112 -0
  9. ivoryos/routes/control/utils.py +38 -0
  10. ivoryos/routes/data/data.py +108 -0
  11. ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +7 -7
  12. ivoryos/routes/{database/templates/database → data/templates}/workflow_view.html +3 -3
  13. ivoryos/routes/design/__init__.py +4 -0
  14. ivoryos/routes/design/design.py +96 -517
  15. ivoryos/routes/design/design_file.py +57 -0
  16. ivoryos/routes/design/design_step.py +43 -0
  17. ivoryos/routes/design/templates/components/action_form.html +52 -0
  18. ivoryos/routes/design/templates/components/action_list.html +15 -0
  19. ivoryos/routes/design/templates/components/autofill_toggle.html +14 -0
  20. ivoryos/routes/design/templates/components/canvas.html +14 -0
  21. ivoryos/routes/design/templates/components/canvas_footer.html +5 -0
  22. ivoryos/routes/design/templates/components/canvas_header.html +54 -0
  23. ivoryos/routes/design/templates/components/deck_selector.html +12 -0
  24. ivoryos/routes/design/templates/components/edit_action_form.html +29 -0
  25. ivoryos/routes/design/templates/components/instrument_panel.html +23 -0
  26. ivoryos/routes/design/templates/components/modals/drop_modal.html +19 -0
  27. ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
  28. ivoryos/routes/design/templates/components/modals/new_script_modal.html +18 -0
  29. ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
  30. ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
  31. ivoryos/routes/design/templates/components/modals.html +6 -0
  32. ivoryos/routes/design/templates/components/operations_panel.html +43 -0
  33. ivoryos/routes/design/templates/components/python_code_overlay.html +17 -0
  34. ivoryos/routes/design/templates/components/script_info.html +31 -0
  35. ivoryos/routes/design/templates/components/scripts.html +50 -0
  36. ivoryos/routes/design/templates/components/sidebar.html +16 -0
  37. ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
  38. ivoryos/routes/design/templates/experiment_builder.html +41 -0
  39. ivoryos/routes/execute/__init__.py +0 -0
  40. ivoryos/routes/execute/execute.py +173 -0
  41. ivoryos/routes/execute/execute_file.py +44 -0
  42. ivoryos/routes/execute/templates/components/error_modal.html +20 -0
  43. ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
  44. ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
  45. ivoryos/routes/execute/templates/components/run_panel.html +9 -0
  46. ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
  47. ivoryos/routes/execute/templates/components/tab_bayesian.html +147 -0
  48. ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
  49. ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
  50. ivoryos/routes/execute/templates/experiment_run.html +294 -0
  51. ivoryos/routes/library/__init__.py +0 -0
  52. ivoryos/routes/{database/database.py → library/library.py} +10 -112
  53. ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +8 -8
  54. ivoryos/routes/main/main.py +1 -1
  55. ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
  56. ivoryos/socket_handlers.py +52 -0
  57. ivoryos/templates/base.html +4 -4
  58. ivoryos/utils/bo_campaign.py +43 -3
  59. ivoryos/utils/form.py +1 -0
  60. ivoryos/utils/py_to_json.py +225 -0
  61. ivoryos/utils/script_runner.py +30 -7
  62. ivoryos/version.py +1 -1
  63. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/METADATA +5 -8
  64. ivoryos-1.1.0.dist-info/RECORD +102 -0
  65. ivoryos/routes/control/templates/control/controllers.html +0 -78
  66. ivoryos/routes/control/templates/control/controllers_home.html +0 -55
  67. ivoryos/routes/control/templates/control/controllers_new.html +0 -89
  68. ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
  69. ivoryos/routes/design/templates/design/experiment_run.html +0 -558
  70. ivoryos-1.0.8.dist-info/RECORD +0 -61
  71. /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
  72. /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
  73. /ivoryos/routes/{database → data}/__init__.py +0 -0
  74. /ivoryos/routes/{database/templates/database → data/templates/components}/step_card.html +0 -0
  75. /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
  76. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/LICENSE +0 -0
  77. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/WHEEL +0 -0
  78. {ivoryos-1.0.8.dist-info → ivoryos-1.1.0.dist-info}/top_level.txt +0 -0
@@ -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
- ax_client = AxClient()
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)))
@@ -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, run_id=run_id)
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
- ax_client = bo_campaign.ax_init_form(bo_args, arg_types)
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.8"
1
+ __version__ = "1.1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 1.0.8
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.7 for best compatibility.
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
- - openai (optional ~=1.53)
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 -e .
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
  [//]: # (![PyPI - Downloads](https://img.shields.io/pypi/dm/ivoryos))
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
  ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/source/_static/demo.gif)
152
151
 
153
- ### Text-to-code demo
154
- ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/source/_static/text-to-code.gif)
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 %}