""" Pathology configuration manager for the MedTracker application. Handles dynamic loading and saving of pathology/symptom configurations. """ import json import logging import os from dataclasses import asdict, dataclass from typing import Any @dataclass class Pathology: """Data class representing a pathology/symptom.""" key: str # Internal key (e.g., "depression") display_name: str # Display name (e.g., "Depression") scale_info: str # Scale information (e.g., "0:good, 10:bad") color: str # Color for graph display default_enabled: bool = True # Whether to show in graph by default scale_min: int = 0 # Minimum scale value scale_max: int = 10 # Maximum scale value scale_orientation: str = "normal" # "normal" (0=good) or "inverted" (0=bad) class PathologyManager: """Manages pathology configurations and provides access to pathology data.""" def __init__( self, config_file: str = "pathologies.json", logger: logging.Logger = None ): self.config_file = config_file self.logger = logger or logging.getLogger(__name__) self.pathologies: dict[str, Pathology] = {} self._load_pathologies() def _get_default_pathologies(self) -> list[Pathology]: """Get the default pathology configuration.""" return [ Pathology( key="depression", display_name="Depression", scale_info="0:good, 10:bad", color="#FF6B6B", default_enabled=True, scale_orientation="normal", ), Pathology( key="anxiety", display_name="Anxiety", scale_info="0:good, 10:bad", color="#FFA726", default_enabled=True, scale_orientation="normal", ), Pathology( key="sleep", display_name="Sleep Quality", scale_info="0:bad, 10:good", color="#66BB6A", default_enabled=True, scale_orientation="inverted", ), Pathology( key="appetite", display_name="Appetite", scale_info="0:bad, 10:good", color="#42A5F5", default_enabled=True, scale_orientation="inverted", ), ] def _load_pathologies(self) -> None: """Load pathologies from configuration file.""" if os.path.exists(self.config_file): try: with open(self.config_file) as f: data = json.load(f) self.pathologies = {} for pathology_data in data.get("pathologies", []): pathology = Pathology(**pathology_data) self.pathologies[pathology.key] = pathology self.logger.info( f"Loaded {len(self.pathologies)} pathologies from " f"{self.config_file}" ) except Exception as e: self.logger.error(f"Error loading pathologies config: {e}") self._create_default_config() else: self._create_default_config() def _create_default_config(self) -> None: """Create default pathology configuration.""" default_pathologies = self._get_default_pathologies() self.pathologies = {path.key: path for path in default_pathologies} self.save_pathologies() self.logger.info("Created default pathology configuration") def save_pathologies(self) -> bool: """Save current pathologies to configuration file.""" try: data = { "pathologies": [ asdict(pathology) for pathology in self.pathologies.values() ] } with open(self.config_file, "w") as f: json.dump(data, f, indent=2) self.logger.info( f"Saved {len(self.pathologies)} pathologies to {self.config_file}" ) return True except Exception as e: self.logger.error(f"Error saving pathologies config: {e}") return False def get_all_pathologies(self) -> dict[str, Pathology]: """Get all pathologies.""" return self.pathologies.copy() def get_pathology(self, key: str) -> Pathology | None: """Get a specific pathology by key.""" return self.pathologies.get(key) def add_pathology(self, pathology: Pathology) -> bool: """Add a new pathology.""" if pathology.key in self.pathologies: self.logger.warning(f"Pathology with key '{pathology.key}' already exists") return False self.pathologies[pathology.key] = pathology return self.save_pathologies() def update_pathology(self, key: str, pathology: Pathology) -> bool: """Update an existing pathology.""" if key not in self.pathologies: self.logger.warning(f"Pathology with key '{key}' does not exist") return False # If key is changing, remove old entry if key != pathology.key: del self.pathologies[key] self.pathologies[pathology.key] = pathology return self.save_pathologies() def remove_pathology(self, key: str) -> bool: """Remove a pathology.""" if key not in self.pathologies: self.logger.warning(f"Pathology with key '{key}' does not exist") return False del self.pathologies[key] return self.save_pathologies() def get_pathology_keys(self) -> list[str]: """Get list of all pathology keys.""" return list(self.pathologies.keys()) def get_display_names(self) -> dict[str, str]: """Get mapping of keys to display names.""" return {key: path.display_name for key, path in self.pathologies.items()} def get_graph_colors(self) -> dict[str, str]: """Get mapping of pathology keys to graph colors.""" return {key: path.color for key, path in self.pathologies.items()} def get_default_enabled_pathologies(self) -> list[str]: """Get list of pathologies that should be enabled by default in graphs.""" return [key for key, path in self.pathologies.items() if path.default_enabled] def get_pathology_vars_dict(self) -> dict[str, tuple[Any, str]]: """Get pathology variables dictionary for UI compatibility.""" # This maintains compatibility with existing UI code import tkinter as tk return { key: (tk.IntVar(value=0), path.display_name) for key, path in self.pathologies.items() } def get_scale_info(self, key: str) -> tuple[int, int, str, str]: """Get scale information for a pathology.""" pathology = self.get_pathology(key) if pathology: return ( pathology.scale_min, pathology.scale_max, pathology.scale_info, pathology.scale_orientation, ) return (0, 10, "0-10", "normal")