"""Archive location persistence (US06-01). An archive location is a *medium*, not a path. External disks get mounted at different mountpoints, and a different disk can be mounted at the same one, so a recorded root alone can never prove "these bytes went to that volume". Each location therefore owns a marker file written onto the medium itself; its ``media_id`` is the stable identity, and the root is only where it was last seen. ``capabilities`` and ``state`` are the last probe result, kept so the UI can list locations without touching a sleeping disk. Preflight always re-probes — a stored state is a hint, never evidence. """ from __future__ import annotations from datetime import datetime from sqlalchemy import DateTime, String, func from sqlalchemy.orm import Mapped, mapped_column from photo_pipeline.db import Base class ArchiveLocation(Base): __tablename__ = "archive_locations" id: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False, unique=True) root: Mapped[str] = mapped_column(String, nullable=False) # Written into the marker file on the medium; proves the right volume is mounted. media_id: Mapped[str] = mapped_column(String, nullable=False, unique=True) capabilities: Mapped[str | None] = mapped_column(String) # JSON, last probe # online | offline | wrong_volume | unwritable — the last probe's verdict. state: Mapped[str] = mapped_column(String, nullable=False, default="offline") last_seen_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() )