""" Medicine configuration manager for the MedTracker application. Handles dynamic loading and saving of medicine configurations. """ import json import logging import os from dataclasses import asdict, dataclass from typing import Any @dataclass class Medicine: """Data class representing a medicine.""" key: str # Internal key (e.g., "bupropion") display_name: str # Display name (e.g., "Bupropion") dosage_info: str # Dosage information (e.g., "150/300 mg") quick_doses: list[str] # Common dose amounts for quick selection color: str # Color for graph display default_enabled: bool = False # Whether to show in graph by default class MedicineManager: """Manages medicine configurations and provides access to medicine data.""" def __init__( self, config_file: str = "medicines.json", logger: logging.Logger = None ): self.config_file = config_file self.logger = logger or logging.getLogger(__name__) self.medicines: dict[str, Medicine] = {} self._load_medicines() def _get_default_medicines(self) -> list[Medicine]: """Get the default medicine configuration.""" return [ Medicine( key="bupropion", display_name="Bupropion", dosage_info="150/300 mg", quick_doses=["150", "300"], color="#FF6B6B", default_enabled=True, ), Medicine( key="hydroxyzine", display_name="Hydroxyzine", dosage_info="25 mg", quick_doses=["25", "50"], color="#4ECDC4", default_enabled=False, ), Medicine( key="gabapentin", display_name="Gabapentin", dosage_info="100 mg", quick_doses=["100", "300", "600"], color="#45B7D1", default_enabled=False, ), Medicine( key="propranolol", display_name="Propranolol", dosage_info="10 mg", quick_doses=["10", "20", "40"], color="#96CEB4", default_enabled=True, ), Medicine( key="quetiapine", display_name="Quetiapine", dosage_info="25 mg", quick_doses=["25", "50", "100"], color="#FFEAA7", default_enabled=False, ), ] def _load_medicines(self) -> None: """Load medicines from configuration file.""" if os.path.exists(self.config_file): try: with open(self.config_file) as f: data = json.load(f) self.medicines = {} for medicine_data in data.get("medicines", []): medicine = Medicine(**medicine_data) self.medicines[medicine.key] = medicine self.logger.info( f"Loaded {len(self.medicines)} medicines from {self.config_file}" ) except Exception as e: self.logger.error(f"Error loading medicines config: {e}") self._create_default_config() else: self._create_default_config() def _create_default_config(self) -> None: """Create default medicine configuration.""" default_medicines = self._get_default_medicines() self.medicines = {med.key: med for med in default_medicines} self.save_medicines() self.logger.info("Created default medicine configuration") def save_medicines(self) -> bool: """Save current medicines to configuration file.""" try: data = { "medicines": [asdict(medicine) for medicine in self.medicines.values()] } with open(self.config_file, "w") as f: json.dump(data, f, indent=2) self.logger.info( f"Saved {len(self.medicines)} medicines to {self.config_file}" ) return True except Exception as e: self.logger.error(f"Error saving medicines config: {e}") return False def get_all_medicines(self) -> dict[str, Medicine]: """Get all medicines.""" return self.medicines.copy() def get_medicine(self, key: str) -> Medicine | None: """Get a specific medicine by key.""" return self.medicines.get(key) def add_medicine(self, medicine: Medicine) -> bool: """Add a new medicine.""" if medicine.key in self.medicines: self.logger.warning(f"Medicine with key '{medicine.key}' already exists") return False self.medicines[medicine.key] = medicine return self.save_medicines() def update_medicine(self, key: str, medicine: Medicine) -> bool: """Update an existing medicine.""" if key not in self.medicines: self.logger.warning(f"Medicine with key '{key}' does not exist") return False # If key is changing, remove old entry if key != medicine.key: del self.medicines[key] self.medicines[medicine.key] = medicine return self.save_medicines() def remove_medicine(self, key: str) -> bool: """Remove a medicine.""" if key not in self.medicines: self.logger.warning(f"Medicine with key '{key}' does not exist") return False del self.medicines[key] return self.save_medicines() def get_medicine_keys(self) -> list[str]: """Get list of all medicine keys.""" return list(self.medicines.keys()) def get_display_names(self) -> dict[str, str]: """Get mapping of keys to display names.""" return {key: med.display_name for key, med in self.medicines.items()} def get_quick_doses(self, key: str) -> list[str]: """Get quick dose options for a medicine.""" medicine = self.medicines.get(key) return medicine.quick_doses if medicine else ["25", "50"] def get_graph_colors(self) -> dict[str, str]: """Get mapping of medicine keys to graph colors.""" return {key: med.color for key, med in self.medicines.items()} def get_default_enabled_medicines(self) -> list[str]: """Get list of medicines that should be enabled by default in graphs.""" return [key for key, med in self.medicines.items() if med.default_enabled] def get_medicine_vars_dict(self) -> dict[str, tuple[Any, str]]: """Get medicine variables dictionary for UI compatibility.""" # This maintains compatibility with existing UI code import tkinter as tk return { key: (tk.IntVar(value=0), f"{med.display_name} {med.dosage_info}") for key, med in self.medicines.items() }