- Implemented `test_dose_parsing_simple.py` to validate the dose parsing workflow. - Created `test_dose_save.py` to verify the saving functionality of dose tracking. - Added `test_dose_save_simple.py` for programmatic testing of dose saving without UI interaction. - Developed `test_final_workflow.py` to test the complete dose tracking workflow, ensuring doses are preserved during edits. - Enhanced `conftest.py` with a mock pathology manager for testing. - Updated `test_data_manager.py` to include pathology manager in DataManager tests and ensure compatibility with new features.
200 lines
7.1 KiB
Python
200 lines
7.1 KiB
Python
"""
|
|
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")
|