Add Notes Hub, transcript search, stale nudges, study guide export, Next Up queue, activity heatmap
Six more dashboard features, all built on the per-course progress-file
scanning infrastructure from earlier sessions:
- Notes Hub (/notes): every lesson note across the library in one place,
newest first, reusing the existing /recent/open cross-course jump route.
- Transcript search: opt-in checkbox on the Library search box, searching
inside .srt/.vtt files. Kept cheap by doing a raw substring match as the
filter step and only extracting a snippet for files that actually match.
- Stale course nudges ("Pick Back Up"): courses with progress that haven't
been touched in 14+ days.
- Study guide export: compiles a course's notes into one downloadable
markdown file, section/lesson structure preserved.
- Next Up queue: an ordered, persisted "what to tackle next" list with
up/down reordering and a queue button on every course row.
- Activity heatmap: a 90-day contribution-style calendar in the Library
Stats card.
_scan_library_activity() now does one walk over every course's progress
file per dashboard load; library stats, stale courses, and the heatmap all
derive from that single scan instead of three independent ones.
Also refactored the duplicated lesson-progress-key lookup (used in three
places now) into _resolve_lesson_progress_key(), and flagged a pre-existing
bug found along the way (not fixed here, kept out of scope): subtitle files
never actually attach to a Lesson object, so the video player's caption
track has never had anything to render.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -275,6 +275,55 @@ before.
|
||||
- App is unauthenticated by design (matches upstream) — settings and hidden-path
|
||||
curation apply app-wide, not per-browser/per-user.
|
||||
|
||||
## Six more features (this session)
|
||||
|
||||
Building on `iter_all_courses`/per-course-progress-file scanning from
|
||||
earlier: Notes Hub, transcript search, stale-course nudges, study guide
|
||||
export, a Next Up queue, and an activity heatmap.
|
||||
|
||||
- **One shared library scan** — `_scan_library_activity()` walks every
|
||||
course's progress file once; `format_library_stats()`,
|
||||
`format_stale_courses()`, and `format_activity_heatmap()` all derive from
|
||||
a single call in `index()`, instead of three separate full-library reads
|
||||
on the same dashboard load.
|
||||
- **Notes Hub** (`/notes`, `templates/notes_hub.html`) — every lesson note
|
||||
across the whole library in one place, newest first, since a note was
|
||||
otherwise only visible from its own lesson page. Reuses the existing
|
||||
`/recent/open` cross-course jump route for navigation - no new route
|
||||
needed there.
|
||||
- **Transcript search** — opt-in checkbox on the existing Library search
|
||||
box ("Also search transcripts"). `search_transcripts()` reads subtitle
|
||||
file *contents* for a raw substring match as the cheap filter step, only
|
||||
extracting a cleaned snippet for files that actually match - deliberately
|
||||
scoped to avoid reintroducing the exact O(every file) perf problem fixed
|
||||
earlier this session. Found along the way (flagged as a separate task,
|
||||
not fixed here): subtitle files never actually attach to a Lesson object
|
||||
today - `DynamicCourseParser._create_lesson_from_file` sets
|
||||
`subtitle_file` then immediately returns `None` before ever constructing
|
||||
the `Lesson`, so the `<track kind="subtitles">` element in
|
||||
`lesson_view.html` has never had anything to show. Transcript search
|
||||
works around this by matching a subtitle file to its lesson via same-stem
|
||||
video/audio file lookup instead of depending on `lesson.subtitle_file`.
|
||||
- **Stale course nudges** ("⏰ Pick Back Up") — courses with some progress,
|
||||
not fully done, untouched 14+ days. Known limitation: "not fully done" is
|
||||
judged only from progress-file entries (lessons never opened at all
|
||||
aren't in that file), so a course with several never-touched lessons
|
||||
alongside one marked-complete lesson can look falsely "finished" - getting
|
||||
a true completion count would mean re-scanning every course's full file
|
||||
tree, the exact cost this whole scanning approach exists to avoid.
|
||||
- **Study guide export** (`/course/study-guide`) — compiles every note
|
||||
written for the loaded course into one markdown file, section/lesson
|
||||
structure preserved, skipping anything without a note. Stdlib only,
|
||||
matching how `/api/backup` already works.
|
||||
- **Next Up queue** — ordered, persisted list of courses to tackle next
|
||||
(`next_up.json`, mirrors `hidden_paths.json`'s pattern but as an ordered
|
||||
list, since order matters here). Reorder via ▲▼ buttons rather than
|
||||
drag-and-drop - more reliable on mobile. "📌" button added to Library
|
||||
browser course rows and the loaded-course stats card.
|
||||
- **Activity heatmap** — GitHub-style 90-day contribution calendar folded
|
||||
into the existing Library Stats card, using the same per-day counts the
|
||||
shared scan already computes for the streak stat.
|
||||
|
||||
## Workflow that's been in use
|
||||
Edit locally → `git add . && git commit -m "..." && git push` to the private
|
||||
Gitea repo → redeploy the stack in Dockhand (which builds from the fresh
|
||||
|
||||
Reference in New Issue
Block a user