Skip to content

Commit 233d5b0

Browse files
committed
feat: add parameter presets system for quick workflow configuration
Adds a new presets feature that allows users to quickly apply optimized parameter configurations with a single click. Key changes: - ParameterManager: Added load_presets(), get_preset_names(), get_preset_description(), and apply_preset() methods - StreamlitUI: Added preset_buttons() method that renders a grid of preset buttons with tooltips - parameter_section() now displays preset buttons when available - Created presets.json with example presets for the TOPP workflow (High Sensitivity, High Specificity, Fast Analysis, Metabolomics Default) The feature is opt-in and backward compatible: workflows without a presets.json file will not show any preset buttons. https://claude.ai/code/session_01BnipAqTf16J7kJVfnzah2U
1 parent 8cb8b57 commit 233d5b0

File tree

4 files changed

+485
-1
lines changed

4 files changed

+485
-1
lines changed

presets.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"topp-workflow": {
3+
"High Sensitivity": {
4+
"_description": "Optimized for detecting low-abundance features with higher noise tolerance",
5+
"FeatureFinderMetabo": {
6+
"algorithm:common:noise_threshold_int": 500.0,
7+
"algorithm:common:chrom_peak_snr": 2.0,
8+
"algorithm:mtd:mass_error_ppm": 15.0
9+
}
10+
},
11+
"High Specificity": {
12+
"_description": "Strict parameters for high-confidence feature detection",
13+
"FeatureFinderMetabo": {
14+
"algorithm:common:noise_threshold_int": 5000.0,
15+
"algorithm:common:chrom_peak_snr": 5.0,
16+
"algorithm:mtd:mass_error_ppm": 5.0
17+
}
18+
},
19+
"Fast Analysis": {
20+
"_description": "Faster processing with relaxed parameters for quick exploration",
21+
"FeatureFinderMetabo": {
22+
"algorithm:common:noise_threshold_int": 2000.0,
23+
"algorithm:ffm:isotope_filtering_model": "none"
24+
},
25+
"FeatureLinkerUnlabeledKD": {
26+
"algorithm:link:rt_tol": 60.0,
27+
"algorithm:link:mz_tol": 15.0
28+
}
29+
},
30+
"Metabolomics Default": {
31+
"_description": "Balanced parameters for general metabolomics analysis",
32+
"FeatureFinderMetabo": {
33+
"algorithm:common:noise_threshold_int": 1000.0,
34+
"algorithm:common:chrom_peak_snr": 3.0,
35+
"algorithm:mtd:mass_error_ppm": 10.0,
36+
"algorithm:ffm:charge_lower_bound": 1,
37+
"algorithm:ffm:charge_upper_bound": 3
38+
},
39+
"FeatureLinkerUnlabeledKD": {
40+
"algorithm:link:rt_tol": 30.0,
41+
"algorithm:link:mz_tol": 10.0
42+
}
43+
}
44+
}
45+
}

src/workflow/ParameterManager.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,98 @@ def reset_to_default_parameters(self) -> None:
167167
JSON file.
168168
"""
169169
# Delete custom params json file
170-
self.params_file.unlink(missing_ok=True)
170+
self.params_file.unlink(missing_ok=True)
171+
172+
def load_presets(self) -> dict:
173+
"""
174+
Load preset definitions from presets.json file.
175+
176+
Returns:
177+
dict: Dictionary of presets for the current workflow, or empty dict if
178+
presets.json doesn't exist or has no presets for this workflow.
179+
"""
180+
presets_file = Path("presets.json")
181+
if not presets_file.exists():
182+
return {}
183+
184+
try:
185+
with open(presets_file, "r", encoding="utf-8") as f:
186+
all_presets = json.load(f)
187+
except (json.JSONDecodeError, IOError):
188+
return {}
189+
190+
# Get workflow name from the workflow directory stem
191+
workflow_name = self.ini_dir.parent.stem
192+
return all_presets.get(workflow_name, {})
193+
194+
def get_preset_names(self) -> list:
195+
"""
196+
Get list of available preset names for the current workflow.
197+
198+
Returns:
199+
list: List of preset names (strings), excluding special keys like _description.
200+
"""
201+
presets = self.load_presets()
202+
return [name for name in presets.keys() if not name.startswith("_")]
203+
204+
def get_preset_description(self, preset_name: str) -> str:
205+
"""
206+
Get the description for a specific preset.
207+
208+
Args:
209+
preset_name: Name of the preset
210+
211+
Returns:
212+
str: Description text for the preset, or empty string if not found.
213+
"""
214+
presets = self.load_presets()
215+
preset = presets.get(preset_name, {})
216+
return preset.get("_description", "")
217+
218+
def apply_preset(self, preset_name: str) -> bool:
219+
"""
220+
Apply a preset to the current session state and save to params.json.
221+
222+
This method updates session state keys for both TOPP tool parameters and
223+
general workflow parameters based on the preset definition.
224+
225+
Args:
226+
preset_name: Name of the preset to apply
227+
228+
Returns:
229+
bool: True if preset was applied successfully, False otherwise.
230+
"""
231+
presets = self.load_presets()
232+
preset = presets.get(preset_name)
233+
if not preset:
234+
return False
235+
236+
# Load existing parameters
237+
current_params = self.get_parameters_from_json()
238+
239+
for key, value in preset.items():
240+
# Skip description key
241+
if key == "_description":
242+
continue
243+
244+
if key == "_general":
245+
# Handle general workflow parameters
246+
for param_name, param_value in value.items():
247+
session_key = f"{self.param_prefix}{param_name}"
248+
st.session_state[session_key] = param_value
249+
current_params[param_name] = param_value
250+
elif isinstance(value, dict) and not key.startswith("_"):
251+
# Handle TOPP tool parameters
252+
tool_name = key
253+
if tool_name not in current_params:
254+
current_params[tool_name] = {}
255+
for param_name, param_value in value.items():
256+
session_key = f"{self.topp_param_prefix}{tool_name}:1:{param_name}"
257+
st.session_state[session_key] = param_value
258+
current_params[tool_name][param_name] = param_value
259+
260+
# Save updated parameters
261+
with open(self.params_file, "w", encoding="utf-8") as f:
262+
json.dump(current_params, f, indent=4)
263+
264+
return True

src/workflow/StreamlitUI.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,45 @@ def zip_and_download_files(self, directory: str):
10611061
use_container_width=True,
10621062
)
10631063

1064+
def preset_buttons(self, num_cols: int = 4) -> None:
1065+
"""
1066+
Renders a grid of preset buttons for the current workflow.
1067+
1068+
When a preset button is clicked, the preset parameters are applied to the
1069+
session state and saved to params.json, then the page is reloaded.
1070+
1071+
Args:
1072+
num_cols: Number of columns for the button grid. Defaults to 4.
1073+
"""
1074+
preset_names = self.parameter_manager.get_preset_names()
1075+
if not preset_names:
1076+
return
1077+
1078+
st.markdown("---")
1079+
st.markdown("**Parameter Presets**")
1080+
st.caption("Click a preset to apply optimized parameters")
1081+
1082+
# Create button grid
1083+
cols = st.columns(num_cols)
1084+
for i, preset_name in enumerate(preset_names):
1085+
col_idx = i % num_cols
1086+
description = self.parameter_manager.get_preset_description(preset_name)
1087+
with cols[col_idx]:
1088+
if st.button(
1089+
preset_name,
1090+
key=f"preset_{preset_name}",
1091+
help=description if description else None,
1092+
use_container_width=True,
1093+
):
1094+
if self.parameter_manager.apply_preset(preset_name):
1095+
st.success(f"Applied preset: {preset_name}")
1096+
streamlit_js_eval(js_expressions="parent.window.location.reload()")
1097+
else:
1098+
st.error(f"Failed to apply preset: {preset_name}")
1099+
# Start new row if needed
1100+
if col_idx == num_cols - 1 and i < len(preset_names) - 1:
1101+
cols = st.columns(num_cols)
1102+
10641103
def file_upload_section(self, custom_upload_function) -> None:
10651104
custom_upload_function()
10661105
c1, _ = st.columns(2)
@@ -1070,6 +1109,9 @@ def file_upload_section(self, custom_upload_function) -> None:
10701109
def parameter_section(self, custom_parameter_function) -> None:
10711110
st.toggle("Show advanced parameters", value=False, key="advanced")
10721111

1112+
# Display preset buttons if presets are available for this workflow
1113+
self.preset_buttons()
1114+
10731115
custom_parameter_function()
10741116

10751117
# File Import / Export section

0 commit comments

Comments
 (0)