ivoryos 1.2.5__py3-none-any.whl → 1.4.4__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.
Files changed (75) hide show
  1. docs/source/conf.py +84 -0
  2. ivoryos/__init__.py +16 -246
  3. ivoryos/app.py +154 -0
  4. ivoryos/optimizer/ax_optimizer.py +55 -28
  5. ivoryos/optimizer/base_optimizer.py +20 -1
  6. ivoryos/optimizer/baybe_optimizer.py +27 -17
  7. ivoryos/optimizer/nimo_optimizer.py +173 -0
  8. ivoryos/optimizer/registry.py +3 -1
  9. ivoryos/routes/auth/auth.py +35 -8
  10. ivoryos/routes/auth/templates/change_password.html +32 -0
  11. ivoryos/routes/control/control.py +58 -28
  12. ivoryos/routes/control/control_file.py +12 -15
  13. ivoryos/routes/control/control_new_device.py +21 -11
  14. ivoryos/routes/control/templates/controllers.html +27 -0
  15. ivoryos/routes/control/utils.py +2 -0
  16. ivoryos/routes/data/data.py +110 -44
  17. ivoryos/routes/data/templates/components/step_card.html +78 -13
  18. ivoryos/routes/data/templates/workflow_view.html +343 -113
  19. ivoryos/routes/design/design.py +59 -10
  20. ivoryos/routes/design/design_file.py +3 -3
  21. ivoryos/routes/design/design_step.py +43 -17
  22. ivoryos/routes/design/templates/components/action_form.html +2 -2
  23. ivoryos/routes/design/templates/components/canvas_main.html +6 -1
  24. ivoryos/routes/design/templates/components/edit_action_form.html +18 -3
  25. ivoryos/routes/design/templates/components/info_modal.html +318 -0
  26. ivoryos/routes/design/templates/components/instruments_panel.html +23 -1
  27. ivoryos/routes/design/templates/components/python_code_overlay.html +27 -10
  28. ivoryos/routes/design/templates/experiment_builder.html +3 -0
  29. ivoryos/routes/execute/execute.py +82 -22
  30. ivoryos/routes/execute/templates/components/logging_panel.html +50 -25
  31. ivoryos/routes/execute/templates/components/run_tabs.html +45 -2
  32. ivoryos/routes/execute/templates/components/tab_bayesian.html +447 -325
  33. ivoryos/routes/execute/templates/components/tab_configuration.html +303 -18
  34. ivoryos/routes/execute/templates/components/tab_repeat.html +6 -2
  35. ivoryos/routes/execute/templates/experiment_run.html +0 -264
  36. ivoryos/routes/library/library.py +9 -11
  37. ivoryos/routes/main/main.py +30 -2
  38. ivoryos/server.py +180 -0
  39. ivoryos/socket_handlers.py +1 -1
  40. ivoryos/static/ivoryos_logo.png +0 -0
  41. ivoryos/static/js/action_handlers.js +259 -88
  42. ivoryos/static/js/socket_handler.js +40 -5
  43. ivoryos/static/js/sortable_design.js +29 -11
  44. ivoryos/templates/base.html +61 -2
  45. ivoryos/utils/bo_campaign.py +18 -17
  46. ivoryos/utils/client_proxy.py +267 -36
  47. ivoryos/utils/db_models.py +286 -60
  48. ivoryos/utils/decorators.py +34 -0
  49. ivoryos/utils/form.py +52 -19
  50. ivoryos/utils/global_config.py +21 -0
  51. ivoryos/utils/nest_script.py +314 -0
  52. ivoryos/utils/py_to_json.py +80 -10
  53. ivoryos/utils/script_runner.py +573 -189
  54. ivoryos/utils/task_runner.py +69 -22
  55. ivoryos/utils/utils.py +48 -5
  56. ivoryos/version.py +1 -1
  57. {ivoryos-1.2.5.dist-info → ivoryos-1.4.4.dist-info}/METADATA +109 -47
  58. ivoryos-1.4.4.dist-info/RECORD +119 -0
  59. ivoryos-1.4.4.dist-info/top_level.txt +3 -0
  60. tests/__init__.py +0 -0
  61. tests/conftest.py +133 -0
  62. tests/integration/__init__.py +0 -0
  63. tests/integration/test_route_auth.py +80 -0
  64. tests/integration/test_route_control.py +94 -0
  65. tests/integration/test_route_database.py +61 -0
  66. tests/integration/test_route_design.py +36 -0
  67. tests/integration/test_route_main.py +35 -0
  68. tests/integration/test_sockets.py +26 -0
  69. tests/unit/test_type_conversion.py +42 -0
  70. tests/unit/test_util.py +3 -0
  71. ivoryos/routes/api/api.py +0 -56
  72. ivoryos-1.2.5.dist-info/RECORD +0 -100
  73. ivoryos-1.2.5.dist-info/top_level.txt +0 -1
  74. {ivoryos-1.2.5.dist-info → ivoryos-1.4.4.dist-info}/WHEEL +0 -0
  75. {ivoryos-1.2.5.dist-info → ivoryos-1.4.4.dist-info}/licenses/LICENSE +0 -0
@@ -12,7 +12,7 @@ def get_step(uuid: int):
12
12
  """
13
13
  .. :quickref: Workflow Design Steps; get an action step editing form
14
14
 
15
- .. http:get:: /steps/<int:uuid>
15
+ .. http:get:: /draft/steps/<int:uuid>
16
16
 
17
17
  get the editing form for an action step
18
18
 
@@ -23,12 +23,16 @@ def get_step(uuid: int):
23
23
  """
24
24
  script = utils.get_script_file()
25
25
  action = script.find_by_uuid(uuid)
26
- if request.method == 'GET':
27
- # forms = create_form_from_action(action, script=script)
26
+ if action is None:
27
+ return jsonify({"warning": "Step not found, please refresh the page."}), 404
28
+
29
+ elif request.method == 'GET':
30
+ forms = create_form_from_action(action, script=script)
28
31
  # session['edit_action'] = action
29
32
  return render_template("components/edit_action_form.html",
30
33
  action=action,
31
- forms=create_form_from_action(action, script=script))
34
+ forms=forms)
35
+
32
36
 
33
37
 
34
38
  @steps.post("/draft/steps/<int:uuid>")
@@ -36,7 +40,7 @@ def save_step(uuid: int):
36
40
  """
37
41
  .. :quickref: Workflow Design Steps; save an action step on canvas
38
42
 
39
- .. http:post:: /steps/<int:uuid>
43
+ .. http:post:: /draft/steps/<int:uuid>
40
44
 
41
45
  save the changes of an action step
42
46
 
@@ -47,29 +51,36 @@ def save_step(uuid: int):
47
51
  """
48
52
  script = utils.get_script_file()
49
53
  action = script.find_by_uuid(uuid)
54
+ warning = None
50
55
  if action is not None:
51
56
  forms = create_form_from_action(action, script=script)
52
57
  kwargs = {field.name: field.data for field in forms if field.name != 'csrf_token'}
53
58
  if forms and forms.validate_on_submit():
54
59
  save_as = kwargs.pop('return', '')
60
+ batch_action = kwargs.pop('batch_action', False)
55
61
  kwargs = script.validate_variables(kwargs)
56
- script.update_by_uuid(uuid=uuid, args=kwargs, output=save_as)
62
+ script.update_by_uuid(uuid=uuid, args=kwargs, output=save_as, batch_action=batch_action)
57
63
  else:
58
- flash(forms.errors)
64
+ warning = f"Compilation failed: {str(forms.errors)}"
59
65
  utils.post_script_file(script)
60
- exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
66
+ try:
67
+ exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
68
+ except Exception as e:
69
+ exec_string = {}
70
+ warning = f"Compilation failed: {str(e)}"
61
71
  session['python_code'] = exec_string
62
72
  design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
63
73
  return render_template("components/canvas_main.html",
64
- script=script,
65
- buttons_dict=design_buttons)
74
+ script=script,
75
+ buttons_dict=design_buttons,
76
+ warning=warning)
66
77
 
67
78
  @steps.delete("/draft/steps/<int:uuid>")
68
79
  def delete_step(uuid: int):
69
80
  """
70
81
  .. :quickref: Workflow Design Steps; delete an action step on canvas
71
82
 
72
- .. http:delete:: /steps/<int:uuid>
83
+ .. http:delete:: /draft/steps/<int:uuid>
73
84
 
74
85
  delete an action step
75
86
 
@@ -82,12 +93,17 @@ def delete_step(uuid: int):
82
93
  if request.method == 'DELETE':
83
94
  script.delete_action(uuid)
84
95
  utils.post_script_file(script)
85
- exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
96
+ warning = None
97
+ try:
98
+ exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
99
+ except Exception as e:
100
+ exec_string = {}
101
+ warning = f"Compilation failed: {str(e)}"
86
102
  session['python_code'] = exec_string
87
103
  design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
88
104
  return render_template("components/canvas_main.html",
89
105
  script=script,
90
- buttons_dict=design_buttons)
106
+ buttons_dict=design_buttons, warning=warning)
91
107
 
92
108
 
93
109
  @steps.route("/draft/steps/<int:uuid>/duplicate", methods=["POST"], strict_slashes=False,)
@@ -107,13 +123,18 @@ def duplicate_action(uuid: int):
107
123
  script = utils.get_script_file()
108
124
  script.duplicate_action(uuid)
109
125
  utils.post_script_file(script)
110
- exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
126
+ warning = None
127
+ try:
128
+ exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
129
+ except Exception as e:
130
+ exec_string = {}
131
+ warning = f"Compilation failed: {str(e)}"
111
132
  session['python_code'] = exec_string
112
133
  design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
113
134
 
114
135
  return render_template("components/canvas_main.html",
115
136
  script=script,
116
- buttons_dict=design_buttons)
137
+ buttons_dict=design_buttons, warning=warning)
117
138
 
118
139
 
119
140
  @steps.route("/draft/steps/order", methods=['POST'])
@@ -133,13 +154,18 @@ def update_list():
133
154
  script = utils.get_script_file()
134
155
  script.currently_editing_order = order.split(",", len(script.currently_editing_script))
135
156
  script.sort_actions()
157
+ warning = None
136
158
 
137
159
  utils.post_script_file(script)
138
- exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
160
+ try:
161
+ exec_string = script.compile(current_app.config['SCRIPT_FOLDER'])
162
+ except Exception as e:
163
+ exec_string = {}
164
+ warning = f"Compilation failed: {str(e)}"
139
165
  session['python_code'] = exec_string
140
166
 
141
167
  # Return the updated canvas HTML instead of JSON
142
168
  design_buttons = {stype: create_action_button(script, stype) for stype in script.stypes}
143
169
  return render_template("components/canvas_main.html",
144
170
  script=script,
145
- buttons_dict=design_buttons)
171
+ buttons_dict=design_buttons, warning=warning)
@@ -1,6 +1,6 @@
1
1
  {# Action form component #}
2
- <div class="accordion-item design-control" draggable="true">
3
- <h2 class="accordion-header">
2
+ <div class="accordion-item design-control">
3
+ <h2 class="accordion-header" >
4
4
  <button class="accordion-button collapsed draggable-action"
5
5
  type="button" data-bs-toggle="collapse"
6
6
  data-bs-target="#{{name}}" aria-expanded="false"
@@ -31,4 +31,9 @@
31
31
  </div>
32
32
  <div class="python-code-wrapper" id="python-code-wrapper">
33
33
  {% include 'components/python_code_overlay.html' %}
34
- </div>
34
+ </div>
35
+
36
+
37
+ {% if warning %}
38
+ <div id="warning" style="display:none;">{{ warning }}</div>
39
+ {% endif %}
@@ -6,6 +6,7 @@
6
6
  <i class="bi bi-arrow-return-left"></i>
7
7
  </a>
8
8
  </div>
9
+ {% if action %}
9
10
  <h5> {{ action['action'] | format_name }} </h5>
10
11
  <form role="form" method='POST' name="{{instrument}}"
11
12
  action="{{ url_for('design.design_steps.get_step', uuid=action['uuid']) }}"
@@ -22,11 +23,24 @@
22
23
  {% else %}
23
24
  {{ forms.hidden_tag() }}
24
25
  {% for field in forms %}
26
+
25
27
  {% if field.type not in ['CSRFTokenField'] %}
26
28
  <div class="input-group mb-3">
27
- <label class="input-group-text">{{ field.label.text }}</label>
28
- {{ field(class="form-control") }}
29
- <div class="form-text">{{ field.description }} </div>
29
+ <label class="input-group-text">{{ field.label.text | format_name }}</label>
30
+ {% if field.type == "BooleanField" %}
31
+ {{ field(class="form-check-input") }}
32
+ {% elif field.type == "FlexibleEnumField" %}
33
+ <input type="text" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.data }}"
34
+ list="{{ field.id }}_options" placeholder="{{ field.render_kw.placeholder if field.render_kw and field.render_kw.placeholder }}"
35
+ class="form-control">
36
+ <datalist id="{{ field.id }}_options">
37
+ {% for key in field.choices %}
38
+ <option value="{{ key }}">{{ key }}</option>
39
+ {% endfor %}
40
+ </datalist>
41
+ {% else %}
42
+ {{ field(class="form-control") }}
43
+ {% endif %}
30
44
  </div>
31
45
  {% endif %}
32
46
  {% endfor %}
@@ -36,3 +50,4 @@
36
50
  <button type="submit" class="btn btn-primary">Save</button>
37
51
  <button type="button" class="btn btn-primary" id="back">Back</button>
38
52
  </form>
53
+ {% endif %}
@@ -0,0 +1,318 @@
1
+ {# Tips Button - Fixed Position #}
2
+ <button id="tipsButton" class="btn btn-info tips-button" data-bs-toggle="modal" data-bs-target="#tipsModal" title="View Workflow Tips">
3
+ <i class="bi bi-question-circle"></i>
4
+ </button>
5
+ {# Tips Modal #}
6
+ <div class="modal fade" id="tipsModal" tabindex="-1" aria-labelledby="tipsModalLabel" aria-hidden="true">
7
+ <div class="modal-dialog modal-lg">
8
+ <div class="modal-content">
9
+ <div class="modal-header bg-info text-white">
10
+ <h5 class="modal-title" id="tipsModalLabel">
11
+ <i class="bi bi-lightbulb"></i> Workflow Building Tips
12
+ </h5>
13
+ <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
14
+ </div>
15
+ <div class="modal-body">
16
+ <div class="accordion" id="tipsAccordion">
17
+ {# Tip 1: Dynamic Variables #}
18
+ <div class="accordion-item">
19
+ <h2 class="accordion-header" id="headingOne">
20
+ <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
21
+ <i class="bi bi-hash me-2"></i> Dynamic Variables
22
+ </button>
23
+ </h2>
24
+ <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#tipsAccordion">
25
+ <div class="accordion-body">
26
+ <p><strong>Use <code>#name</code> to make variables dynamic</strong></p>
27
+ <p>When you want a parameter to be adjustable across multiple workflow runs, use <code>#</code> followed by the parameter name in place of the actual value.</p>
28
+ <div class="alert alert-success mb-0">
29
+ <strong>Example:</strong><br>
30
+ Instead of: <code>temperature = 300</code><br>
31
+ Use: <code>temperature = #temperature</code><br>
32
+ <small class="text-muted">This makes "temperature" a dynamic parameter that can be varied in parameter sweeps or optimization runs.</small>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ {# Tip 2: Repeat vs Dynamic #}
38
+ <div class="accordion-item">
39
+ <h2 class="accordion-header" id="headingTwo">
40
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
41
+ <i class="bi bi-arrow-repeat me-2"></i> Repeat vs Dynamic Parameters
42
+ </button>
43
+ </h2>
44
+ <div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#tipsAccordion">
45
+ <div class="accordion-body">
46
+ <p><strong>Repeat is for static workflows only</strong></p>
47
+ <ul>
48
+ <li><strong>Static (Repeat):</strong> Runs the exact same workflow multiple times with identical parameters</li>
49
+ <li><strong>Dynamic (#name):</strong> Automatically triggers the <span class="badge bg-primary">Parameter Sweep</span> option, allowing you to vary parameters across runs</li>
50
+ </ul>
51
+ <div class="alert alert-info mb-0">
52
+ <i class="bi bi-info-circle"></i> Use repeat when you want statistical averaging of identical runs. Use dynamic parameters (e.g., <code>#temperature</code>) when you want to explore different parameter values.
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ {# Tip 3: Parameter Sweep #}
58
+ <div class="accordion-item">
59
+ <h2 class="accordion-header" id="headingThree">
60
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
61
+ <i class="bi bi-sliders me-2"></i> Parameter Sweep Mode
62
+ </button>
63
+ </h2>
64
+ <div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#tipsAccordion">
65
+ <div class="accordion-body">
66
+ <p><strong>Automatically enabled when using dynamic parameters</strong></p>
67
+ <p>Parameter sweep allows you to systematically explore different combinations of parameter values.</p>
68
+ <div class="alert alert-warning">
69
+ <strong>Triggered by:</strong> Using <code>#name</code> syntax (e.g., <code>#temperature</code>) in your workflow
70
+ </div>
71
+ <p class="mb-0"><strong>Use cases:</strong> Sensitivity analysis, parameter optimization, design space exploration</p>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ {# Tip 4: Optimizer #}
76
+ <div class="accordion-item">
77
+ <h2 class="accordion-header" id="headingFour">
78
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
79
+ <i class="bi bi-bullseye me-2"></i> Optimizer Mode
80
+ </button>
81
+ </h2>
82
+ <div id="collapseFour" class="accordion-collapse collapse" aria-labelledby="headingFour" data-bs-parent="#tipsAccordion">
83
+ <div class="accordion-body">
84
+ <p><strong>Enabled when you have both dynamic parameters AND output values</strong></p>
85
+ <p>The optimizer automatically finds the best parameter values to optimize your output metric.</p>
86
+ <div class="alert alert-success">
87
+ <strong>Requirements:</strong>
88
+ <ul class="mb-0">
89
+ <li>At least one dynamic parameter using <code>#name</code> syntax (input)</li>
90
+ <li>At least one output value with "Save value as" name (objective function)</li>
91
+ </ul>
92
+ </div>
93
+ <p class="mt-2 mb-0"><small class="text-muted"><i class="bi bi-info-circle"></i> Note: Parameter sweep option remains available even in optimizer mode</small></p>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ {# Tip 5: Output Values #}
98
+ <div class="accordion-item">
99
+ <h2 class="accordion-header" id="headingFive">
100
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFive" aria-expanded="false" aria-controls="collapseFive">
101
+ <i class="bi bi-save me-2"></i> Saving Output Values
102
+ </button>
103
+ </h2>
104
+ <div id="collapseFive" class="accordion-collapse collapse" aria-labelledby="headingFive" data-bs-parent="#tipsAccordion">
105
+ <div class="accordion-body">
106
+ <p><strong>Use "Save value as" to capture important results</strong></p>
107
+ <p>When you want to track or optimize a result from your workflow, give it a meaningful name in the "Save value as" field.</p>
108
+ <div class="alert alert-info">
109
+ <strong>Naming conventions:</strong><br>
110
+ • Use descriptive names: <code>final_stress</code>, <code>total_energy</code>, <code>efficiency</code><br>
111
+ • Use underscores instead of spaces: <code>max_temperature</code> not <code>max temperature</code><br>
112
+ • Keep names concise but clear
113
+ </div>
114
+ <p class="mb-0"><small class="text-muted">Saved values become available for optimization and can be tracked across parameter sweeps.</small></p>
115
+ </div>
116
+ </div>
117
+ </div>
118
+ {# Tip 6: Batch Mode #}
119
+ <div class="accordion-item">
120
+ <h2 class="accordion-header" id="headingSix">
121
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSix" aria-expanded="false" aria-controls="collapseSix">
122
+ <i class="bi bi-collection me-2"></i> Batch Mode Actions
123
+ </button>
124
+ </h2>
125
+ <div id="collapseSix" class="accordion-collapse collapse" aria-labelledby="headingSix" data-bs-parent="#tipsAccordion">
126
+ <div class="accordion-body">
127
+ <p><strong>Mark steps as "Once per batch" to run them only once for multiple samples</strong></p>
128
+ <p>Some actions apply to all samples at once (batch actions), while others must run for each individual sample (per-sample actions).</p>
129
+ <div class="row">
130
+ <div class="col-md-6 mb-3">
131
+ <div class="card h-100">
132
+ <div class="card-header bg-light">
133
+ <strong>Workflow Steps</strong>
134
+ </div>
135
+ <div class="card-body">
136
+ <div class="workflow-left">
137
+ <div class="step-box regular">Add Liquid<br><small>per-sample</small></div>
138
+ <div class="arrow-down">↓</div>
139
+ <div class="step-box regular">Add Solid<br><small>per-sample</small></div>
140
+ <div class="arrow-down">↓</div>
141
+ <div class="step-box batch">Heat Sample<br><small>batch action</small></div>
142
+ <div class="arrow-down">↓</div>
143
+ <div class="step-box regular">Analyze Sample<br><small>per-sample</small></div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ <div class="col-md-6 mb-3">
149
+ <div class="card h-100">
150
+ <div class="card-header bg-light">
151
+ <strong>Execution (n=3 samples)</strong>
152
+ </div>
153
+ <div class="card-body">
154
+ <div class="workflow-right">
155
+ <div class="sample-group">
156
+ <div class="step-box-small regular">Add Liquid S1</div>
157
+ <div class="step-box-small regular">Add Liquid S2</div>
158
+ <div class="step-box-small regular">Add Liquid S3</div>
159
+ </div>
160
+ <div class="arrow-down">↓</div>
161
+ <div class="sample-group">
162
+ <div class="step-box-small regular">Add Solid S1</div>
163
+ <div class="step-box-small regular">Add Solid S2</div>
164
+ <div class="step-box-small regular">Add Solid S3</div>
165
+ </div>
166
+ <div class="arrow-down">↓</div>
167
+ <div class="step-box batch">Heat All Samples</div>
168
+ <div class="arrow-down">↓</div>
169
+ <div class="sample-group">
170
+ <div class="step-box-small regular">Analyze S1</div>
171
+ <div class="step-box-small regular">Analyze S2</div>
172
+ <div class="step-box-small regular">Analyze S3</div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ </div>
179
+ <div class="alert alert-warning mb-0">
180
+ <strong>Per-sample actions:</strong> Run individually for each sample (Add Liquid, Add Solid, Analyze)<br>
181
+ <strong>Batch actions:</strong> Run once for all samples together (Heat Sample)
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+ {# Tip 7: Best Practices #}
187
+ <div class="accordion-item">
188
+ <h2 class="accordion-header" id="headingSeven">
189
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSeven" aria-expanded="false" aria-controls="collapseSeven">
190
+ <i class="bi bi-star me-2"></i> Best Practices
191
+ </button>
192
+ </h2>
193
+ <div id="collapseSeven" class="accordion-collapse collapse" aria-labelledby="headingSeven" data-bs-parent="#tipsAccordion">
194
+ <div class="accordion-body">
195
+ <ul>
196
+ <li><strong>Name parameters clearly:</strong> Use descriptive names like <code>#mesh_size</code> instead of <code>#x</code></li>
197
+ <li><strong>Set reasonable ranges:</strong> Define min/max values that make physical sense for your problem</li>
198
+ <li><strong>Start simple:</strong> Begin with 1-2 parameters before adding more complexity</li>
199
+ <li><strong>Document outputs:</strong> Clearly label which outputs are optimization objectives</li>
200
+ <li><strong>Test first:</strong> Run a single workflow before launching sweeps or optimizations</li>
201
+ <li><strong>Use batch mode wisely:</strong> Mark setup steps as "once per batch" to avoid unnecessary repetition</li>
202
+ </ul>
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ <div class="modal-footer">
209
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ <style>
214
+ /* Tips button styling */
215
+ .tips-button {
216
+ position: fixed;
217
+ bottom: 30px;
218
+ right: 30px;
219
+ width: 60px;
220
+ height: 60px;
221
+ border-radius: 50%;
222
+ font-size: 24px;
223
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
224
+ z-index: 1000;
225
+ display: flex;
226
+ align-items: center;
227
+ justify-content: center;
228
+ transition: all 0.3s ease;
229
+ }
230
+
231
+ .tips-button:hover {
232
+ transform: scale(1.1);
233
+ box-shadow: 0 6px 20px rgba(0,0,0,0.2);
234
+ }
235
+
236
+ .tips-button i {
237
+ line-height: 1;
238
+ }
239
+
240
+
241
+ .accordion-body code {
242
+ background-color: #f8f9fa;
243
+ padding: 2px 6px;
244
+ border-radius: 3px;
245
+ color: #d63384;
246
+ }
247
+ /* Batch mode visual styling */
248
+ .workflow-left {
249
+ display: flex;
250
+ flex-direction: column;
251
+ align-items: center;
252
+ gap: 10px;
253
+ }
254
+
255
+ .workflow-right {
256
+ display: flex;
257
+ flex-direction: column;
258
+ align-items: center;
259
+ gap: 10px;
260
+ }
261
+
262
+ .sample-group {
263
+ display: flex;
264
+ gap: 8px;
265
+ padding: 10px;
266
+ background: #f8f9fa;
267
+ border-radius: 6px;
268
+ border: 1px dashed #dee2e6;
269
+ }
270
+
271
+ .step-box {
272
+ padding: 12px 20px;
273
+ border-radius: 6px;
274
+ font-size: 0.85em;
275
+ text-align: center;
276
+ min-width: 120px;
277
+ }
278
+
279
+ .step-box.regular {
280
+ background: white;
281
+ border: 2px solid #dee2e6;
282
+ color: #495057;
283
+ }
284
+
285
+ .step-box.batch {
286
+ background: #e7f3ff;
287
+ border: 2px solid #0d6efd;
288
+ color: #0d6efd;
289
+ font-weight: 600;
290
+ }
291
+
292
+ .step-box-small {
293
+ padding: 8px 12px;
294
+ border-radius: 4px;
295
+ font-size: 0.75em;
296
+ text-align: center;
297
+ background: white;
298
+ border: 1px solid #dee2e6;
299
+ color: #495057;
300
+ }
301
+
302
+ .step-box-small.regular {
303
+ background: white;
304
+ border: 1px solid #dee2e6;
305
+ }
306
+
307
+ .arrow-down {
308
+ font-size: 1.5em;
309
+ color: #6c757d;
310
+ line-height: 1;
311
+ }
312
+
313
+ .arrow {
314
+ font-size: 1.2em;
315
+ color: #6c757d;
316
+ }
317
+ </style>
318
+ </div>
@@ -39,7 +39,29 @@
39
39
  </ul>
40
40
  </div>
41
41
  </div>
42
-
42
+ {% if block_variables %}
43
+ <div class="accordion-item design-control">
44
+ <h5 class="accordion-header">
45
+ <button class="accordion-button" data-bs-toggle="collapse" data-bs-target="#block" role="button" aria-expanded="false" aria-controls="collapseExample">
46
+ Methods
47
+ </button>
48
+ </h5>
49
+ <div class="accordion-collapse collapse show" id="block">
50
+ <ul class="list-group">
51
+ {% for category in block_variables %}
52
+ <button class="list-group-item list-group-item-action"
53
+ type="button"
54
+ name="device"
55
+ value="{{category}}"
56
+ data-get-url="{{ url_for('design.get_operation_sidebar', instrument=category) }}"
57
+ onclick="updateInstrumentPanel(this)">
58
+ {{ category|format_name }}
59
+ </button>
60
+ {% endfor%}
61
+ </ul>
62
+ </div>
63
+ </div>
64
+ {% endif %}
43
65
  {% if local_variables %}
44
66
  <div class="accordion-item design-control">
45
67
  <h5 class="accordion-header">
@@ -1,5 +1,4 @@
1
- {# Python code overlay component #}
2
- {#{% if session.get('show_code') %}#}
1
+ {# Python code overlay component - Pure HTML #}
3
2
  <style>
4
3
  .code-overlay {
5
4
  position: fixed;
@@ -16,7 +15,7 @@
16
15
  transform: translateX(0);
17
16
  }
18
17
  </style>
19
- <script>hljs.highlightAll();</script>
18
+
20
19
  {% if session.get('show_code') %}
21
20
  <div id="pythonCodeOverlay" class="code-overlay bg-light border-start show">
22
21
  {% else %}
@@ -29,11 +28,29 @@
29
28
  </button>
30
29
  </div>
31
30
  <div class="overlay-content p-3">
32
- {% for stype, script in session.get('python_code', {}).items() %}
33
- <pre><code class="language-python">{{ script }}</code></pre>
34
- {% endfor %}
35
- <a href="{{ url_for('design.design_files.download_python', filetype='python') }}">
36
- Download <i class="bi bi-download"></i>
37
- </a>
31
+ <!-- Top toggles (Single / Batch) -->
32
+ <div class="btn-group mb-3">
33
+ <button type="button" class="btn btn-outline-primary mode-toggle active" data-mode="single">Single</button>
34
+ <button type="button" class="btn btn-outline-primary mode-toggle" data-mode="batch">Batch</button>
35
+ </div>
36
+
37
+ {# <!-- Secondary toggles (By Sample / By Step) TODO -->#}
38
+ {# <div class="btn-group mb-3" id="batch-options" style="display: none;">#}
39
+ {# <button type="button" class="btn btn-outline-secondary batch-toggle active" data-batch="sample">By Sample</button>#}
40
+ {# <button type="button" class="btn btn-outline-secondary batch-toggle" data-batch="step">By Step</button>#}
41
+ {# </div>#}
42
+
43
+ <!-- Code display -->
44
+ <pre><code id="python-code" class="language-python">Loading...</code></pre>
45
+
46
+ <!-- Action buttons -->
47
+ <div class="mt-2">
48
+ <button type="button" id="copy-code" class="btn btn-sm btn-outline-success">
49
+ <i class="bi bi-clipboard"></i> Copy
50
+ </button>
51
+ <button type="button" id="download-code" class="btn btn-sm btn-outline-primary">
52
+ <i class="bi bi-download"></i> Download
53
+ </button>
54
+ </div>
38
55
  </div>
39
- </div>
56
+ </div>
@@ -23,6 +23,8 @@
23
23
 
24
24
  {# Include all modals #}
25
25
  {% include 'components/modals.html' %}
26
+ {% include 'components/info_modal.html' %}
27
+
26
28
 
27
29
  {# Include all scripts #}
28
30
  <script>
@@ -32,6 +34,7 @@
32
34
  const scriptStepUrl = `{{ url_for('design.design_steps.get_step', uuid=0) }}`;
33
35
  const scriptStepDupUrl = `{{ url_for('design.design_steps.duplicate_action', uuid=0) }}`;
34
36
  const scriptDeleteUrl = "{{ url_for('design.clear_draft') }}";
37
+ const scriptCompileUrl = "{{ url_for('design.compile_preview') }}";
35
38
  </script>
36
39
  <script src="{{ url_for('static', filename='js/sortable_design.js') }}"></script>
37
40
  <script src="{{ url_for('static', filename='js/action_handlers.js') }}"></script>