| """ |
| Session state management for UAP Data Analysis Tool |
| Centralizes session state initialization and management |
| """ |
|
|
| import streamlit as st |
| from typing import Dict, Any, Optional |
| import logging |
|
|
| logger = logging.getLogger(__name__) |
|
|
| class SessionStateManager: |
| """Centralized session state management""" |
| |
| |
| DEFAULTS = { |
| |
| 'analyzers': [], |
| 'col_names': [], |
| 'clusters': {}, |
| 'new_data': None, |
| 'dataset': None, |
| 'parsed_responses': None, |
| 'parsed_responses_df': None, |
| 'filtered_data': None, |
| |
| |
| 'data_processed': False, |
| 'data_loaded': False, |
| 'map_generated': False, |
| |
| |
| 'user_subscribed': False, |
| 'email': '', |
| |
| |
| 'api_key_valid': False, |
| 'previous_api_key': None, |
| 'api_keys_validated': False, |
| |
| |
| 'gemini_answer': None, |
| 'json_format': None, |
| |
| |
| 'stage': 0, |
| 'buttons': {}, |
| |
| |
| 'cached_embeddings': {}, |
| 'cached_clusters': {}, |
| 'cached_visualizations': {} |
| } |
| |
| @classmethod |
| def initialize(cls) -> None: |
| """Initialize all session state variables with defaults""" |
| for key, default_value in cls.DEFAULTS.items(): |
| if key not in st.session_state: |
| |
| if key in ['new_data', 'dataset']: |
| import pandas as pd |
| st.session_state[key] = pd.DataFrame() |
| elif isinstance(default_value, (dict, list)): |
| |
| st.session_state[key] = type(default_value)(default_value) |
| else: |
| st.session_state[key] = default_value |
| |
| logger.info("Session state initialized") |
| |
| @classmethod |
| def get(cls, key: str, default: Any = None) -> Any: |
| """Safely get a session state value""" |
| return st.session_state.get(key, default if default is not None else cls.DEFAULTS.get(key)) |
| |
| @classmethod |
| def set(cls, key: str, value: Any) -> None: |
| """Safely set a session state value""" |
| st.session_state[key] = value |
| logger.debug(f"Session state '{key}' set to {type(value).__name__}") |
| |
| @classmethod |
| def update(cls, updates: Dict[str, Any]) -> None: |
| """Update multiple session state values at once""" |
| for key, value in updates.items(): |
| cls.set(key, value) |
| |
| @classmethod |
| def clear(cls, keys: Optional[list] = None) -> None: |
| """Clear specific session state keys or all if none specified""" |
| if keys is None: |
| |
| preserve_keys = {'email', 'user_subscribed'} |
| keys_to_clear = [k for k in st.session_state.keys() if k not in preserve_keys] |
| else: |
| keys_to_clear = keys |
| |
| for key in keys_to_clear: |
| if key in st.session_state: |
| del st.session_state[key] |
| |
| |
| for key in keys_to_clear: |
| if key in cls.DEFAULTS: |
| if key in ['new_data', 'dataset']: |
| import pandas as pd |
| st.session_state[key] = pd.DataFrame() |
| elif isinstance(cls.DEFAULTS[key], (dict, list)): |
| st.session_state[key] = type(cls.DEFAULTS[key])(cls.DEFAULTS[key]) |
| else: |
| st.session_state[key] = cls.DEFAULTS[key] |
| |
| logger.info(f"Cleared session state keys: {keys_to_clear}") |
| |
| @classmethod |
| def exists(cls, key: str) -> bool: |
| """Check if a session state key exists""" |
| return key in st.session_state |
| |
| @classmethod |
| def increment(cls, key: str, amount: int = 1) -> None: |
| """Increment a numeric session state value""" |
| current = cls.get(key, 0) |
| if isinstance(current, (int, float)): |
| cls.set(key, current + amount) |
| else: |
| logger.warning(f"Cannot increment non-numeric session state key: {key}") |
| |
| @classmethod |
| def append(cls, key: str, value: Any) -> None: |
| """Append to a list session state value""" |
| current = cls.get(key, []) |
| if isinstance(current, list): |
| current.append(value) |
| cls.set(key, current) |
| else: |
| logger.warning(f"Cannot append to non-list session state key: {key}") |
| |
| @classmethod |
| def update_dict(cls, key: str, updates: Dict[str, Any]) -> None: |
| """Update a dictionary session state value""" |
| current = cls.get(key, {}) |
| if isinstance(current, dict): |
| current.update(updates) |
| cls.set(key, current) |
| else: |
| logger.warning(f"Cannot update non-dict session state key: {key}") |
| |
| @classmethod |
| def get_state_summary(cls) -> Dict[str, Any]: |
| """Get a summary of current session state for debugging""" |
| summary = {} |
| for key in st.session_state: |
| value = st.session_state[key] |
| if hasattr(value, '__len__') and not isinstance(value, str): |
| summary[key] = f"{type(value).__name__} (length: {len(value)})" |
| else: |
| summary[key] = f"{type(value).__name__}" |
| return summary |
| |
| @classmethod |
| def cache_visualization(cls, viz_type: str, data_hash: str, figure: Any) -> None: |
| """Cache a visualization for reuse""" |
| cache = cls.get('cached_visualizations', {}) |
| cache_key = f"{viz_type}_{data_hash}" |
| cache[cache_key] = figure |
| cls.set('cached_visualizations', cache) |
| |
| @classmethod |
| def get_cached_visualization(cls, viz_type: str, data_hash: str) -> Optional[Any]: |
| """Retrieve a cached visualization""" |
| cache = cls.get('cached_visualizations', {}) |
| cache_key = f"{viz_type}_{data_hash}" |
| return cache.get(cache_key) |