"""Local archive deliverer: writes reports/YYYY-MM-DD.md (+ .json) keyed by payload.krx_date.""" from __future__ import annotations import logging from datetime import datetime from pathlib import Path from zoneinfo import ZoneInfo from us2kr.config import KST from us2kr.deliver.base import DeliveryError from us2kr.models import BriefingResult log = logging.getLogger(__name__) class FileDeliverer: """Archive each briefing as Markdown and as the full BriefingResult JSON.""" name = "file" def __init__(self, reports_dir: Path) -> None: self.reports_dir = Path(reports_dir) def _ensure_dir(self) -> None: self.reports_dir.mkdir(parents=True, exist_ok=True) def deliver(self, result: BriefingResult) -> str: """Write {krx_date}.md and {krx_date}.json; return the Markdown path.""" stem = result.payload.krx_date.isoformat() md_path = self.reports_dir / f"{stem}.md" json_path = self.reports_dir / f"{stem}.json" try: self._ensure_dir() md_path.write_text(result.markdown, encoding="utf-8") json_path.write_text(result.model_dump_json(indent=2), encoding="utf-8") except OSError as exc: raise DeliveryError(f"file delivery failed for {stem}: {exc}") from exc log.info("report archived: %s (+ .json)", md_path) return str(md_path) def notify_error(self, subject: str, detail: str) -> None: """Append a timestamped line to {reports_dir}/errors.log. Never raises.""" try: self._ensure_dir() ts = datetime.now(ZoneInfo(KST)).isoformat(timespec="seconds") flat_detail = " | ".join(part.strip() for part in detail.splitlines() if part.strip()) line = f"{ts}\t{subject}\t{flat_detail}\n" with (self.reports_dir / "errors.log").open("a", encoding="utf-8") as fh: fh.write(line) except Exception: log.exception("could not append to errors.log")