feat(series): pending staging for add-from-post (#789 Phase 2)
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>
This commit is contained in:
@@ -71,30 +71,29 @@ async def test_promote_requires_images(db):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_post_appends_pages_to_the_run(db):
|
||||
async def test_add_post_stages_pending(db):
|
||||
svc = SeriesService(db)
|
||||
artist = await _artist(db, "Story Artist")
|
||||
sid = (await TagService(db).find_or_create("Story", TagKind.series)).id
|
||||
# Seed the series with two existing pages.
|
||||
# Seed the series with two PLACED pages.
|
||||
seed_post = await _post(db, artist, "seed", "pp3a")
|
||||
seed = await _post_images(db, seed_post, artist, 2)
|
||||
await svc.add_images(sid, seed)
|
||||
# A later post's pages append to the END (operator orders afterward).
|
||||
# A later post is STAGED as pending — not in the placed run yet.
|
||||
post = await _post(db, artist, "Story pages 9-11", "pp3b")
|
||||
imgs = await _post_images(db, post, artist, 3)
|
||||
out = await svc.add_post(sid, post.id)
|
||||
assert out["added"] == 3
|
||||
assert out["staged"] == 3
|
||||
|
||||
rows = (
|
||||
await db.execute(
|
||||
select(SeriesPage.image_id, SeriesPage.stated_page)
|
||||
.where(SeriesPage.series_tag_id == sid)
|
||||
.order_by(SeriesPage.page_number)
|
||||
)
|
||||
).all()
|
||||
assert [r.image_id for r in rows] == seed + imgs
|
||||
stated = {r.image_id: r.stated_page for r in rows}
|
||||
assert [stated[i] for i in imgs] == [9, 10, 11]
|
||||
data = await svc.list_pages(sid)
|
||||
# placed run unchanged (just the seed); pending grouped under the post.
|
||||
assert [p["image_id"] for p in data["pages"]] == seed
|
||||
assert len(data["pending"]) == 1
|
||||
grp = data["pending"][0]
|
||||
assert grp["post"]["id"] == post.id
|
||||
assert grp["post"]["title"] == "Story pages 9-11"
|
||||
assert [pg["image_id"] for pg in grp["pages"]] == imgs
|
||||
assert [pg["stated_page"] for pg in grp["pages"]] == [9, 10, 11]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user