75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
"""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")
|
|
return [AlertEvent.from_dict(item) for item in data if isinstance(item, dict)]
|
|
|
|
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)
|