from __future__ import annotations import json from dataclasses import dataclass from pathlib import Path from app_config import APP_DIR @dataclass(frozen=True) class JobDefinition: job_id: str schedule: str input_date_policy: str dependencies: tuple[str, ...] lock_key: str timeout_seconds: int max_attempts: int output_version: str class JobRegistry: def __init__(self, definitions: tuple[JobDefinition, ...]) -> None: self.definitions = definitions self._by_id = {item.job_id: item for item in definitions} if len(self._by_id) != len(definitions): raise ValueError("Background job IDs must be unique") @classmethod def load(cls, path: Path | None = None) -> "JobRegistry": config_path = path or APP_DIR / "config" / "jobs.config.json" payload = json.loads(config_path.read_text(encoding="utf-8")) definitions = tuple( JobDefinition( job_id=str(item["id"]), schedule=str(item["schedule"]), input_date_policy=str(item["input_date_policy"]), dependencies=tuple(str(value) for value in item.get("dependencies") or []), lock_key=str(item["lock_key"]), timeout_seconds=max(1, int(item["timeout_seconds"])), max_attempts=max(1, int(item["max_attempts"])), output_version=str(item["output_version"]), ) for item in payload.get("jobs") or [] ) if not definitions: raise ValueError("Background job registry is empty") return cls(definitions) def get(self, job_id: str) -> JobDefinition: try: return self._by_id[job_id] except KeyError as exc: raise ValueError(f"Background job is not registered: {job_id}") from exc