"""Alert notification domain models.""" from __future__ import annotations import json from dataclasses import asdict, dataclass from pathlib import Path from typing import Any class AlertHistoryLoadError(RuntimeError): def __init__(self, history_path: Path, message: str) -> None: super().__init__(message) self.history_path = history_path @dataclass class AlertEvent: severity: str message: str ltv_ratio: float warning_threshold: float critical_threshold: float spot_price: float updated_at: str email_alerts_enabled: bool def to_dict(self) -> dict[str, Any]: return asdict(self) @classmethod def from_dict(cls, data: dict[str, Any]) -> "AlertEvent": return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__}) @dataclass class AlertStatus: severity: str message: str ltv_ratio: float warning_threshold: float critical_threshold: float email_alerts_enabled: bool history: list[AlertEvent] history_unavailable: bool = False history_notice: str | None = None class AlertHistoryRepository: """File-backed alert history store.""" HISTORY_PATH = Path("data/alert_history.json") def __init__(self, history_path: Path | None = None) -> None: self.history_path = history_path or self.HISTORY_PATH self.history_path.parent.mkdir(parents=True, exist_ok=True) def load(self) -> list[AlertEvent]: if not self.history_path.exists(): return [] try: with self.history_path.open() as f: data = json.load(f) except json.JSONDecodeError as exc: raise AlertHistoryLoadError(self.history_path, f"Alert history is not valid JSON: {exc}") from exc except OSError as exc: raise AlertHistoryLoadError(self.history_path, f"Alert history could not be read: {exc}") from exc if not isinstance(data, list): raise AlertHistoryLoadError(self.history_path, "Alert history payload must be a list") events: list[AlertEvent] = [] for index, item in enumerate(data): if not isinstance(item, dict): raise AlertHistoryLoadError(self.history_path, f"Alert history entry {index} must be an object") try: events.append(AlertEvent.from_dict(item)) except (TypeError, ValueError) as exc: raise AlertHistoryLoadError( self.history_path, f"Alert history entry {index} is invalid: {exc}", ) from exc return events def save(self, events: list[AlertEvent]) -> None: with self.history_path.open("w") as f: json.dump([event.to_dict() for event in events], f, indent=2)