"""SeriesSuggestion — a confirm-only "this post may continue this series" hint. The matcher (FC-6.3) scores a (post, candidate series) pair from several weighted signals and, above the configured threshold, records a pending suggestion. The operator confirms (→ the post is added as a chapter) or dismisses it; FC never files a post into a series on its own. status is a plain string (no Postgres ENUM — see the check-existing-enums lesson): pending | added | dismissed. """ from datetime import datetime from sqlalchemy import ( JSON, DateTime, Float, ForeignKey, Integer, String, UniqueConstraint, func, ) from sqlalchemy.orm import Mapped, mapped_column from .base import Base class SeriesSuggestion(Base): __tablename__ = "series_suggestion" __table_args__ = ( UniqueConstraint( "post_id", "series_tag_id", name="uq_series_suggestion_post_series" ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True) post_id: Mapped[int] = mapped_column( ForeignKey("post.id", ondelete="CASCADE"), nullable=False, index=True ) series_tag_id: Mapped[int] = mapped_column( ForeignKey("tag.id", ondelete="CASCADE"), nullable=False, index=True ) score: Mapped[float] = mapped_column(Float, nullable=False) signals: Mapped[dict | None] = mapped_column(JSON, nullable=True) status: Mapped[str] = mapped_column( String(16), nullable=False, server_default="pending", index=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(), )