feat(stats): time per chapter — TOC chapter maps, fixed-layout page counts, book-page layout#124
Merged
Merged
Conversation
…counts
Stats-plan Phase 3 plumbing plus the stat it unlocks.
- BookChapter: the EPUB TOC extracted at ingest into device-independent
fraction-of-book boundaries (word-offset of each chapter's spine item;
front matter folds into chapter one). Reuses the already-open ebooklib
book — no second parse. No usable TOC (or a single entry) stores nothing.
- Book.page_count: intrinsic page count for PDF/CBZ/CBR at ingest (fitz
page count / archive image count, ComicInfo PageCount as fallback). EPUB
deliberately stays NULL — reflowable pagination is not a book property.
- Both wired into all five creation paths (upload, ingest, scan, bindery
accept, auto-import) and into the admin Word Counts job, which now
backfills chapter maps and page counts in the same pass (EPUBs with words
already counted only do the chapter extraction).
- compute_book_chapter_times: KOReader page-stat dwell mapped into chapter
buckets via each row's own pagination ((page-0.5)/total_pages, bisect on
chapter starts) — robust across repagination. `chapters` block on
GET /books/{id}/reading-stats; ChapterTimesBlock on BookDetailPage next to
the intensity curve (top 10 + expander, em-dash for unread chapters).
Verification: full suite green (11 new tests: extraction fractions +
no-TOC/single-entry edges, CBZ counting, bucketing math incl. mixed
paginations and front-matter folding, endpoint block). Live on dev: the
extended backfill processed 270 files in 9.5s (141 chapter maps, 122 page
counts, 0 failures) and High Gloom renders 6h 43m across 95 chapters —
matching its known device total exactly. Frontend build clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The stats tower (hero + intensity + time-per-chapter) grew tall enough to push the book's own description below the fold. The page now leads with what the book IS (description above the stats cluster), and the existing Reading Stats collapse persists in localStorage — closing it once keeps it closed across books and visits. Verified with Playwright on dev: collapse survives reload; collapsed page is title → status → description → "Reading Stats ›" → details. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, spacing Iteration on the time-per-chapter tile from live review: - The line/area chart is now the tile's face (all chapters visible in fixed height); the bar table moved behind a "Show details" toggle. Hovering the line snaps to the nearest chapter with a styled tooltip (name · time · read dates) and a marker dot — HTML overlays in percent coordinates, since the stretched SVG would distort anything drawn inside it. - WHEN a chapter was read: per-chapter dwells cluster into sittings (same 30-minute-gap rule as the session clustering). Detail rows highlight on hover and tooltip the actual clock ranges — "read Jun 2, 17:40 – 18:54" for one sitting, per-sitting ranges up to three, "N sittings · Jun 3 – Jun 4" beyond. A naive min–max range was rejected: real data showed Chapter 2's 15 minutes spanning two sittings 7 hours apart. - Reading Stats section spacing joins the Description/Details rhythm (mt-6) now that the description sits above it. 12 tests green (incl. the sitting-split case); verified against real imported Kindle data on dev. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bndct-devops
pushed a commit
that referenced
this pull request
Jul 5, 2026
bndct-devops
pushed a commit
that referenced
this pull request
Jul 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stats-plan Phase 3 plumbing plus the stat it unlocks, iterated on live review.
BookChapter: EPUB TOC extracted at ingest into device-independent fraction-of-book boundaries (word-offset of each chapter's spine item; front matter folds into chapter one). Reuses the already-open ebooklib book — no second parse.Book.page_count: intrinsic page count for PDF/CBZ/CBR at ingest. EPUB deliberately stays NULL — reflowable pagination is not a book property.Verification: suite green (12 new tests: extraction fractions, no-TOC/single-entry edges, CBZ counting, bucketing math incl. mixed paginations, sitting-splitting, endpoint block). Live on dev with real imported Kindle data: backfill processed 270 files in 9.5s (141 chapter maps, 122 page counts, 0 failures); High Gloom renders 6h 43m across 95 chapters — matching its known device total — and real data validated the sitting design (Chapter 2's 15m = two sittings 7h apart).