- Implemented MedicineManagementWindow for adding, editing, and removing medicines. - Created MedicineManager to handle medicine configurations, including loading and saving to JSON. - Updated UIManager to dynamically generate medicine-related UI components based on the MedicineManager. - Enhanced test suite with mock objects for MedicineManager to ensure proper functionality in DataManager tests. - Added validation for medicine input fields in the UI. - Introduced default medicine configurations for initial setup.
196 lines
6.8 KiB
Python
196 lines
6.8 KiB
Python
"""
|
|
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()
|
|
}
|