"""SeriesChapter — a cosmetic chapter DIVIDER within a series (FC-6.x reframe). A series is ONE flat, series-global ordered run of SeriesPages. A chapter is NOT a container — it owns no pages. It is a labeled divider anchored to the page that BEGINS the chapter (anchor_page_id → series_page): "a new chapter starts here." A page's chapter is derived at read time as the nearest preceding divider. Dividers never affect page ordering or the series-global page numbers; they stay pinned to their anchor page across reorders. anchor_page_id is UNIQUE — at most one chapter begins at a given page — and FK-cascades, so removing the anchor page from the series drops the divider (the chapter merges into the preceding run). title is the optional chapter name; stated_part is the optional operator-facing "Part N" label (shown instead of a derived ordinal when set). """ from datetime import datetime from sqlalchemy import ( DateTime, ForeignKey, Integer, Text, UniqueConstraint, func, ) from sqlalchemy.orm import Mapped, mapped_column from .base import Base class SeriesChapter(Base): __tablename__ = "series_chapter" __table_args__ = ( # alembic 0047 named the UNIQUE `uq_series_chapter_anchor_page`, not # the `uq_series_chapter_anchor_page_id` a bare `unique=True` would # render (#3275). UniqueConstraint("anchor_page_id", name="uq_series_chapter_anchor_page"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True) series_tag_id: Mapped[int] = mapped_column( ForeignKey("tag.id", ondelete="CASCADE"), nullable=False, index=True ) # Both the UNIQUE (above) and the FK carry the names 0047 gave them; the # convention would render the FK `fk_series_chapter_anchor_page_id_series_page`. anchor_page_id: Mapped[int] = mapped_column( ForeignKey( "series_page.id", ondelete="CASCADE", name="fk_series_chapter_anchor_page", ), nullable=False, ) title: Mapped[str | None] = mapped_column(Text, nullable=True) stated_part: Mapped[int | None] = mapped_column(Integer, nullable=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(), )