7bb765b6ed
Add-from-post no longer appends straight into the run — it STAGES the post's
pages as pending (per-page status; page_number NULL), grouped by source post,
so the operator drops junk (text-free alts, bumpers) and places the keepers
into the sequence with clean series-global numbering.
- migration 0048: series_page.status ('placed' default | 'pending') + nullable
page_number.
- series_service: placed/pending split everywhere (list_pages returns the
placed run + a `pending` section grouped by source post; reorder/cover/
list_series operate on placed only); add_post stages pending; new
place_pending(image_ids, before_image_id=None) flips pending→placed spliced
before a page (or appended) and renumbers; junk removal reuses remove_images.
- api/tags: /add-post now returns staged count; new POST /series/<id>/pending/
place.
- frontend: PostSeriesMenu navigates to the series after staging; seriesManage
store surfaces `pending` + placePending; SeriesManageView gains a pending
tray (per-post groups, place-all / place-one / drop-junk).
- tests: pending staging, place (append + insert-before), ignore-already-
placed, drop-junk, route guard; updated add_post + match-accept expectations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
51 lines
2.0 KiB
Python
51 lines
2.0 KiB
Python
"""SeriesPage — ordered image membership for a series-kind Tag.
|
|
|
|
A series IS a Tag with kind='series'; series_page gives it a SINGLE flat,
|
|
series-global ordered run of pages (FC-6.x divider reframe). An image belongs to
|
|
at most one series (UNIQUE image_id). Reading order is `page_number` alone — a
|
|
series-wide ordering key (not unique), rewritten 1..N wholesale on reorder so a
|
|
reorder can't transiently collide on an index.
|
|
|
|
Chapters are cosmetic DIVIDERS anchored to a page (see SeriesChapter); they do
|
|
NOT own pages, so there is no chapter_id here — a page's chapter is derived at
|
|
read time as the nearest preceding divider. stated_page carries the printed page
|
|
number parsed from the source post, nullable when unknown.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Integer, String, func
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from .base import Base
|
|
|
|
|
|
class SeriesPage(Base):
|
|
__tablename__ = "series_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
|
|
)
|
|
image_id: Mapped[int] = mapped_column(
|
|
ForeignKey("image_record.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
unique=True,
|
|
)
|
|
# 'placed' = in the series-global run (page_number set); 'pending' = staged
|
|
# from a post awaiting the operator's sort (page_number NULL). (#789 P2)
|
|
status: Mapped[str] = mapped_column(
|
|
String(16), nullable=False, server_default="placed"
|
|
)
|
|
page_number: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
stated_page: 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(),
|
|
)
|