Refactor indicator management to a data-driven approach
- Introduced dynamic generation of parameter fields and callback handling for indicators, enhancing modularity and maintainability. - Updated `config_utils.py` with new utility functions to load indicator templates and generate dynamic outputs and states for parameter fields. - Refactored `indicators.py` to utilize these utilities, streamlining the callback logic and improving user experience by reducing hardcoded elements. - Modified `indicator_modal.py` to create parameter fields dynamically based on JSON templates, eliminating the need for manual updates when adding new indicators. - Added documentation outlining the new data-driven architecture for indicators, improving clarity and guidance for future development. These changes significantly enhance the flexibility and scalability of the indicator system, aligning with project goals for maintainability and performance.
This commit is contained in:
@@ -6,6 +6,7 @@ import json
|
||||
import os
|
||||
import logging
|
||||
from typing import List, Dict, Any, Optional
|
||||
from dash import Output, Input, State
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -164,4 +165,174 @@ def generate_parameter_fields_config(indicator_type: str) -> Optional[Dict[str,
|
||||
|
||||
fields_config[param_name] = field_config
|
||||
|
||||
return fields_config
|
||||
return fields_config
|
||||
|
||||
|
||||
def get_parameter_field_outputs() -> List[Output]:
|
||||
"""Generate dynamic Output components for parameter field visibility callbacks.
|
||||
|
||||
Returns:
|
||||
List[Output]: List of Output components for all parameter containers
|
||||
"""
|
||||
templates = load_indicator_templates()
|
||||
outputs = [Output('indicator-parameters-message', 'style')]
|
||||
|
||||
for indicator_type in templates.keys():
|
||||
outputs.append(Output(f'{indicator_type}-parameters', 'style'))
|
||||
|
||||
return outputs
|
||||
|
||||
|
||||
def get_parameter_field_states() -> List[State]:
|
||||
"""Generate dynamic State components for parameter input fields.
|
||||
|
||||
Returns:
|
||||
List[State]: List of State components for all parameter input fields
|
||||
"""
|
||||
templates = load_indicator_templates()
|
||||
states = []
|
||||
|
||||
for indicator_type, template in templates.items():
|
||||
config = generate_parameter_fields_config(indicator_type)
|
||||
if config:
|
||||
for field_config in config.values():
|
||||
states.append(State(field_config['input_id'], 'value'))
|
||||
|
||||
return states
|
||||
|
||||
|
||||
def get_parameter_field_edit_outputs() -> List[Output]:
|
||||
"""Generate dynamic Output components for parameter fields in edit mode.
|
||||
|
||||
Returns:
|
||||
List[Output]: List of Output components for setting parameter values
|
||||
"""
|
||||
templates = load_indicator_templates()
|
||||
outputs = []
|
||||
|
||||
for indicator_type, template in templates.items():
|
||||
config = generate_parameter_fields_config(indicator_type)
|
||||
if config:
|
||||
for field_config in config.values():
|
||||
outputs.append(Output(field_config['input_id'], 'value'))
|
||||
|
||||
return outputs
|
||||
|
||||
|
||||
def get_parameter_field_reset_outputs() -> List[Output]:
|
||||
"""Generate dynamic Output components for resetting parameter fields.
|
||||
|
||||
Returns:
|
||||
List[Output]: List of Output components for resetting parameter values
|
||||
"""
|
||||
templates = load_indicator_templates()
|
||||
outputs = []
|
||||
|
||||
for indicator_type, template in templates.items():
|
||||
config = generate_parameter_fields_config(indicator_type)
|
||||
if config:
|
||||
for field_config in config.values():
|
||||
outputs.append(Output(field_config['input_id'], 'value', allow_duplicate=True))
|
||||
|
||||
return outputs
|
||||
|
||||
|
||||
def collect_parameter_values(indicator_type: str, all_parameter_values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Collect parameter values for a specific indicator type from callback arguments.
|
||||
|
||||
Args:
|
||||
indicator_type (str): The indicator type
|
||||
all_parameter_values (Dict[str, Any]): All parameter values from callback
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Parameters specific to the indicator type
|
||||
"""
|
||||
config = generate_parameter_fields_config(indicator_type)
|
||||
if not config:
|
||||
return {}
|
||||
|
||||
parameters = {}
|
||||
defaults = get_indicator_default_parameters(indicator_type)
|
||||
|
||||
for param_name, field_config in config.items():
|
||||
field_id = field_config['input_id']
|
||||
value = all_parameter_values.get(field_id)
|
||||
default_value = defaults.get(param_name, field_config.get('default'))
|
||||
|
||||
# Use provided value or fall back to default
|
||||
parameters[param_name] = value if value is not None else default_value
|
||||
|
||||
return parameters
|
||||
|
||||
|
||||
def set_parameter_values(indicator_type: str, parameters: Dict[str, Any]) -> List[Any]:
|
||||
"""Generate parameter values for setting in edit mode.
|
||||
|
||||
Args:
|
||||
indicator_type (str): The indicator type
|
||||
parameters (Dict[str, Any]): Parameter values to set
|
||||
|
||||
Returns:
|
||||
List[Any]: Values in the order expected by the callback outputs
|
||||
"""
|
||||
templates = load_indicator_templates()
|
||||
values = []
|
||||
|
||||
for current_type, template in templates.items():
|
||||
config = generate_parameter_fields_config(current_type)
|
||||
if config:
|
||||
for param_name, field_config in config.items():
|
||||
if current_type == indicator_type:
|
||||
# Set the actual parameter value for the matching indicator type
|
||||
value = parameters.get(param_name)
|
||||
else:
|
||||
# Set None for other indicator types
|
||||
value = None
|
||||
values.append(value)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def reset_parameter_values() -> List[Any]:
|
||||
"""Generate default parameter values for resetting the form.
|
||||
|
||||
Returns:
|
||||
List[Any]: Default values in the order expected by reset callback outputs
|
||||
"""
|
||||
templates = load_indicator_templates()
|
||||
values = []
|
||||
|
||||
for indicator_type, template in templates.items():
|
||||
config = generate_parameter_fields_config(indicator_type)
|
||||
if config:
|
||||
defaults = get_indicator_default_parameters(indicator_type)
|
||||
for param_name, field_config in config.items():
|
||||
default_value = defaults.get(param_name, field_config.get('default'))
|
||||
values.append(default_value)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def get_parameter_visibility_styles(selected_indicator_type: str) -> List[Dict[str, str]]:
|
||||
"""Generate visibility styles for parameter containers.
|
||||
|
||||
Args:
|
||||
selected_indicator_type (str): The currently selected indicator type
|
||||
|
||||
Returns:
|
||||
List[Dict[str, str]]: Visibility styles for each parameter container
|
||||
"""
|
||||
templates = load_indicator_templates()
|
||||
hidden_style = {'display': 'none'}
|
||||
visible_style = {'display': 'block'}
|
||||
|
||||
# First style is for the message
|
||||
message_style = {'display': 'block'} if not selected_indicator_type else {'display': 'none'}
|
||||
styles = [message_style]
|
||||
|
||||
# Then styles for each indicator type container
|
||||
for indicator_type in templates.keys():
|
||||
style = visible_style if indicator_type == selected_indicator_type else hidden_style
|
||||
styles.append(style)
|
||||
|
||||
return styles
|
||||
Reference in New Issue
Block a user