refactor: reorganize repository to src/ layout
Move 6 packages (core, config, data_processing, analysis, visualization, cli) into src/ to reduce root clutter. Merge tools/data.py into data_processing/transforms.py. Move docs to docs/. Path resolution via .pth file (setup_dev.py), pytest pythonpath config, and sys.path bootstrap in rxconfig.py and CLI entry points. Clean up pyproject.toml deps (remove stale pins, add snowflake-connector-python). Fix tomllib import for Python 3.10 compatibility. All 113 tests pass.
This commit is contained in:
-197
@@ -1,197 +0,0 @@
|
||||
"""
|
||||
Configuration module for NHS High-Cost Drug Patient Pathway Analysis Tool.
|
||||
|
||||
Contains PathConfig dataclass for centralizing all file path references.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class PathConfig:
|
||||
"""
|
||||
Centralizes all file paths used across the application.
|
||||
|
||||
Provides a single source of truth for file locations, making it easier to:
|
||||
- Change the data directory location
|
||||
- Support different environments (development, production)
|
||||
- Validate that required files exist
|
||||
|
||||
Attributes:
|
||||
base_dir: Root directory of the application (defaults to current working directory)
|
||||
data_dir: Directory containing reference data files
|
||||
images_dir: Directory containing UI assets and fonts
|
||||
"""
|
||||
|
||||
base_dir: Path = field(default_factory=Path.cwd)
|
||||
_data_dir: Optional[Path] = field(default=None, repr=False)
|
||||
_images_dir: Optional[Path] = field(default=None, repr=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Set default subdirectories relative to base_dir if not provided."""
|
||||
if self._data_dir is None:
|
||||
self._data_dir = self.base_dir / "data"
|
||||
if self._images_dir is None:
|
||||
self._images_dir = self.base_dir / "images"
|
||||
|
||||
@property
|
||||
def data_dir(self) -> Path:
|
||||
"""Directory containing reference data files."""
|
||||
# _data_dir is always set after __post_init__
|
||||
assert self._data_dir is not None
|
||||
return self._data_dir
|
||||
|
||||
@property
|
||||
def images_dir(self) -> Path:
|
||||
"""Directory containing UI assets and fonts."""
|
||||
# _images_dir is always set after __post_init__
|
||||
assert self._images_dir is not None
|
||||
return self._images_dir
|
||||
|
||||
# Reference data files (read-only lookups)
|
||||
@property
|
||||
def drugnames_csv(self) -> Path:
|
||||
"""Drug name standardization mapping."""
|
||||
return self.data_dir / "drugnames.csv"
|
||||
|
||||
@property
|
||||
def directory_list_csv(self) -> Path:
|
||||
"""Medical specialties/directories list."""
|
||||
return self.data_dir / "directory_list.csv"
|
||||
|
||||
@property
|
||||
def treatment_function_codes_csv(self) -> Path:
|
||||
"""NHS treatment function code mappings."""
|
||||
return self.data_dir / "treatment_function_codes.csv"
|
||||
|
||||
@property
|
||||
def drug_directory_list_csv(self) -> Path:
|
||||
"""Valid drug-to-directory mappings (pipe-separated)."""
|
||||
return self.data_dir / "drug_directory_list.csv"
|
||||
|
||||
@property
|
||||
def org_codes_csv(self) -> Path:
|
||||
"""Provider code to organization name mapping."""
|
||||
return self.data_dir / "org_codes.csv"
|
||||
|
||||
@property
|
||||
def include_csv(self) -> Path:
|
||||
"""Drug filter list with default selections."""
|
||||
return self.data_dir / "include.csv"
|
||||
|
||||
@property
|
||||
def default_trusts_csv(self) -> Path:
|
||||
"""NHS Trust list for filter."""
|
||||
return self.data_dir / "defaultTrusts.csv"
|
||||
|
||||
# Output/diagnostic files
|
||||
@property
|
||||
def na_directory_rows_csv(self) -> Path:
|
||||
"""Exported rows with unresolved Directory for diagnostics."""
|
||||
return self.data_dir / "na_directory_rows.csv"
|
||||
|
||||
@property
|
||||
def ta_recommendations_xlsx(self) -> Path:
|
||||
"""NICE TA recommendations (downloaded from web)."""
|
||||
return self.data_dir / "ta-recommendations.xlsx"
|
||||
|
||||
# UI assets
|
||||
@property
|
||||
def font_medium(self) -> Path:
|
||||
"""AvenirLTStd-Medium font file."""
|
||||
return self.images_dir / "AvenirLTStd-Medium.ttf"
|
||||
|
||||
@property
|
||||
def font_roman(self) -> Path:
|
||||
"""AvenirLTStd-Roman font file."""
|
||||
return self.images_dir / "AvenirLTStd-Roman.ttf"
|
||||
|
||||
@property
|
||||
def logo_ico(self) -> Path:
|
||||
"""Application icon."""
|
||||
return self.images_dir / "logo.ico"
|
||||
|
||||
@property
|
||||
def logo_png(self) -> Path:
|
||||
"""Application logo."""
|
||||
return self.images_dir / "logo.png"
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""
|
||||
Validate that required files and directories exist.
|
||||
|
||||
Returns:
|
||||
List of error messages. Empty list means all validations passed.
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# Check directories exist
|
||||
if not self.data_dir.exists():
|
||||
errors.append(f"Data directory not found: {self.data_dir}")
|
||||
if not self.images_dir.exists():
|
||||
errors.append(f"Images directory not found: {self.images_dir}")
|
||||
|
||||
# Check required reference files
|
||||
required_files = [
|
||||
(self.drugnames_csv, "Drug names mapping"),
|
||||
(self.directory_list_csv, "Directory list"),
|
||||
(self.treatment_function_codes_csv, "Treatment function codes"),
|
||||
(self.drug_directory_list_csv, "Drug-directory mapping"),
|
||||
(self.org_codes_csv, "Organization codes"),
|
||||
(self.include_csv, "Drug include list"),
|
||||
(self.default_trusts_csv, "Default trusts"),
|
||||
]
|
||||
|
||||
for file_path, description in required_files:
|
||||
if not file_path.exists():
|
||||
errors.append(f"{description} not found: {file_path}")
|
||||
|
||||
return errors
|
||||
|
||||
def validate_fonts(self) -> list[str]:
|
||||
"""
|
||||
Validate that font files exist (for GUI mode).
|
||||
|
||||
Returns:
|
||||
List of error messages. Empty list means all validations passed.
|
||||
"""
|
||||
errors = []
|
||||
|
||||
font_files = [
|
||||
(self.font_medium, "Medium font"),
|
||||
(self.font_roman, "Roman font"),
|
||||
]
|
||||
|
||||
for file_path, description in font_files:
|
||||
if not file_path.exists():
|
||||
errors.append(f"{description} not found: {file_path}")
|
||||
|
||||
return errors
|
||||
|
||||
def as_legacy_paths(self) -> dict[str, str]:
|
||||
"""
|
||||
Return paths as strings with './' prefix for backwards compatibility.
|
||||
|
||||
This method eases migration by providing paths in the format
|
||||
currently used throughout the codebase.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping path names to legacy-format string paths.
|
||||
"""
|
||||
return {
|
||||
"drugnames_csv": f"./{self.drugnames_csv.relative_to(self.base_dir)}",
|
||||
"directory_list_csv": f"./{self.directory_list_csv.relative_to(self.base_dir)}",
|
||||
"treatment_function_codes_csv": f"./{self.treatment_function_codes_csv.relative_to(self.base_dir)}",
|
||||
"drug_directory_list_csv": f"./{self.drug_directory_list_csv.relative_to(self.base_dir)}",
|
||||
"org_codes_csv": f"./{self.org_codes_csv.relative_to(self.base_dir)}",
|
||||
"include_csv": f"./{self.include_csv.relative_to(self.base_dir)}",
|
||||
"default_trusts_csv": f"./{self.default_trusts_csv.relative_to(self.base_dir)}",
|
||||
"na_directory_rows_csv": f"./{self.na_directory_rows_csv.relative_to(self.base_dir)}",
|
||||
"ta_recommendations_xlsx": f"./{self.ta_recommendations_xlsx.relative_to(self.base_dir)}",
|
||||
}
|
||||
|
||||
|
||||
# Default instance for application-wide use
|
||||
default_paths = PathConfig()
|
||||
Reference in New Issue
Block a user