from datetime import datetime from sqlalchemy import JSON, DateTime, String, func from sqlalchemy.dialects import postgresql from sqlalchemy.orm import Mapped, mapped_column # Portable JSON type (see CONVENTIONS C8): plain JSON on SQLite (tests), # JSONB on PostgreSQL (production). Use this alias for every JSON column. JSONB_V = JSON().with_variant(postgresql.JSONB(), "postgresql") class TimestampMixin: created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class SourceMixin: """Provenance + dedupe columns for every imported/ingested row. source: "manual" | importer id ("foodvisor_csv") | ingest source ("android_bridge") external_id: stable id from the source system, if any content_hash: sha256 of canonical payload, used when no external_id exists """ source: Mapped[str] = mapped_column(String(50), default="manual") external_id: Mapped[str | None] = mapped_column(String(255), default=None) content_hash: Mapped[str | None] = mapped_column(String(64), default=None)