Files
HighCostDrugsDemo/core/models.py
T

141 lines
4.6 KiB
Python

"""
Data models for NHS High-Cost Drug Patient Pathway Analysis Tool.
Contains dataclasses for encapsulating application state and filter parameters.
"""
from dataclasses import dataclass, field
from datetime import date
from pathlib import Path
from typing import Optional
@dataclass
class AnalysisFilters:
"""
Encapsulates all filter state for the analysis pipeline.
Replaces the individual parameters currently passed to generate_graph()
and the global state managed in the GUI. This provides:
- Type safety for filter values
- Validation of filter combinations
- Easy serialization for caching/persistence
- Clear interface between GUI and analysis engine
Attributes:
start_date: Patient initiated start date (treatment pathway start)
end_date: Patient initiated end date (treatment pathway start cutoff)
last_seen_date: Minimum last seen date (filters out patients not seen recently)
trusts: List of NHS Trust names to include (empty = all)
drugs: List of drug names to include (empty = all)
directories: List of medical directories/specialties to include (empty = all)
custom_title: Optional custom title for the graph (blank = auto-generated)
minimum_patients: Minimum number of patients for a pathway to be included
output_dir: Directory where output files should be saved
"""
start_date: date
end_date: date
last_seen_date: date
trusts: list[str] = field(default_factory=list)
drugs: list[str] = field(default_factory=list)
directories: list[str] = field(default_factory=list)
custom_title: str = ""
minimum_patients: int = 0
output_dir: Optional[Path] = None
def validate(self) -> list[str]:
"""
Validate filter configuration for logical consistency.
Returns:
List of error messages. Empty list means all validations passed.
"""
errors = []
# Date range validation
if self.end_date < self.start_date:
errors.append(
f"End date ({self.end_date}) cannot be before start date ({self.start_date})"
)
if self.last_seen_date > self.end_date:
errors.append(
f"Last seen date ({self.last_seen_date}) is after end date ({self.end_date}), "
"which would exclude all patients"
)
# Minimum patients validation
if self.minimum_patients < 0:
errors.append(
f"Minimum patients ({self.minimum_patients}) cannot be negative"
)
# Output directory validation
if self.output_dir is not None and not self.output_dir.exists():
errors.append(f"Output directory does not exist: {self.output_dir}")
# Filter list validation (warn if empty but don't error)
# Empty lists are valid and mean "include all"
return errors
@property
def has_trust_filter(self) -> bool:
"""Check if any trust filter is applied."""
return len(self.trusts) > 0
@property
def has_drug_filter(self) -> bool:
"""Check if any drug filter is applied."""
return len(self.drugs) > 0
@property
def has_directory_filter(self) -> bool:
"""Check if any directory filter is applied."""
return len(self.directories) > 0
@property
def title(self) -> str:
"""
Return the display title for the graph.
If custom_title is set, use it. Otherwise, generate a default title
based on the date range.
"""
if self.custom_title:
return self.custom_title
return f"Patients initiated from {self.start_date} to {self.end_date}"
def summary(self) -> str:
"""
Return a human-readable summary of the filter configuration.
Useful for logging and display in the GUI.
"""
lines = [
f"Date range: {self.start_date} to {self.end_date}",
f"Last seen after: {self.last_seen_date}",
f"Minimum patients: {self.minimum_patients}",
]
if self.trusts:
lines.append(f"Trusts: {len(self.trusts)} selected")
else:
lines.append("Trusts: All")
if self.drugs:
lines.append(f"Drugs: {len(self.drugs)} selected")
else:
lines.append("Drugs: All")
if self.directories:
lines.append(f"Directories: {len(self.directories)} selected")
else:
lines.append("Directories: All")
if self.custom_title:
lines.append(f"Custom title: {self.custom_title}")
return "\n".join(lines)