from datetime import datetime from sqlalchemy import DateTime, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.database import Base class JobRunState(Base): """How each scheduled job last finished. One row per job, overwritten. The scheduler's ``_job_runtime`` dict is the live view and is deliberately in-memory, but it is also wiped by every process restart -- so after a deploy Admin → Jobs could only say "Active" with no indication of whether a job had ever run. This is the durable half. Deliberately not history: ``system_events`` already grows without a reaper, and a second append-only operational table would repeat that. Rows are upserted on ``job_name``; adding history later is purely additive. """ __tablename__ = "job_run_state" id: Mapped[int] = mapped_column(primary_key=True) job_name: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) # Scheduler vocabulary: completed | skipped | error | rate_limited | deferred. # Distinct from data_import_runs' statuses, which is one reason this is its # own table rather than a widened column there. status: Mapped[str] = mapped_column(String(32), nullable=False) started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) finished_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) processed: Mapped[int | None] = mapped_column(Integer, nullable=True) total: Mapped[int | None] = mapped_column(Integer, nullable=True) message: Mapped[str | None] = mapped_column(Text, nullable=True) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False )