141 lines
4.6 KiB
Python
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)
|