Skip to content

feat(stats): time per chapter — TOC chapter maps, fixed-layout page counts, book-page layout#124

Merged
bndct-devops merged 4 commits into
mainfrom
feat/reading-chapters
Jul 5, 2026
Merged

feat(stats): time per chapter — TOC chapter maps, fixed-layout page counts, book-page layout#124
bndct-devops merged 4 commits into
mainfrom
feat/reading-chapters

Conversation

@bndct-devops

Copy link
Copy Markdown
Owner

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.
  • Both wired into all five creation paths and into the Admin → Word Counts job, which now backfills chapter maps and page counts in the same pass (existing libraries get chapter data with one run).
  • Time per chapter on BookDetailPage: line/area chart of dwell across chapters (hover snaps to a chapter: name · time · read dates), full bar table behind "Show details" with row hover showing the actual clock ranges — clustered into sittings with the same 30-minute-gap rule as the session clustering, so a chapter read across two evenings never renders as a 28-hour session.
  • Book-page layout: description now sits above the stats cluster (the page leads with what the book is), and the Reading Stats collapse persists in localStorage.

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).

bndct-devops and others added 4 commits July 5, 2026 22:28
…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 bndct-devops merged commit bccefe4 into main Jul 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant