pdf-quiz-generator/docs/TODO.md
Daniel 0e6c18d886 feat: figures as records, question-centred dashboard, fewer hints mid-quiz
Figures
A question could carry exactly one stem image and one explanation image, each a
bare path with no title, no legend, and no way for the prose to refer to it.
`question_media` makes a figure a row: it points at an image already in the bank,
carries a role, a label the text can name ("Figure 1"), a caption and an order,
and there can be as many as the question needs. The same radiograph can serve two
questions without being stored twice.

The 346 existing paths were backfilled into figure records and retitled —
`page_339_img_0.png` says where a file came from and nothing about what it shows,
so the filename moved into the caption where it is still searchable, and the
title became something a person can read.

On the editor question: no new platform needed. Milkdown is already installed —
ProseMirror-based, MIT, GFM tables, code blocks, LaTeX — and already used for
articles, courses and the quick question modal. Only the question *page* still
has plain textareas, and that swap is written down rather than rushed, because
the stem carries manual-highlight offsets and a WYSIWYG rewrite would move them.

Fewer hints during a quiz
The category trail and the difficulty pill were shown beside every stem. Being
told a question is filed under Neonatology, or that it is "hard", narrows the
answer before the stem has been read. Both now wait until the answer is in,
where the trail becomes a way to more of the same topic.

The dashboard is about questions
Quizzes and attempts describe how the material happens to be packaged. What a
learner is working through is questions: how many of the bank they have seen,
how many they have answered correctly, and their average. The old per-quiz
performance card — which needed two attempts before it showed anything — is
gone, superseded by the session analysis. The greeting sits above "continue your
study" rather than below it, where it read as a heading for the wrong section.

208 backend, 249 frontend green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-11 03:18:49 +02:00

15 KiB

PedsHub — outstanding work

Everything requested and not yet delivered. Ordered roughly by dependency, not priority — say which to take and I'll reorder.

Updated 2026-09-11.


Asked for on 2026-09-11, not yet done

Captured so nothing is lost while the article writing runs.

Sessions and analysis

  • Session analysis after a quiz/analysis/session/:attemptId: rail of latest sessions, the four figures (correct, completed, time per question, total time), a donut, study recommendations, and a paginated performance table with time and peer statistics. Time per question is now recorded (attempt_answers.seconds_spent); answers from before that read "—" rather than claiming zero.
  • Return to a session with Resume, not an immediate start — opening a part-finished session currently restarts it. It should offer Resume and Repeat, as the reference does.
  • An unsuspended exam keeps running — closing an exam-mode session should let the clock continue and show the score when it expires, rather than quietly pausing.
  • Deleting a session removes its data — so it no longer counts towards any statistic. Check the existing delete does this fully.
  • Reset all data, with a warning that says plainly what goes.

Reading and study

  • Study recommendations by Articles / Disciplines / Systems — currently one list by category. Should be three tabs, each linking onward.
  • Adaptive session — questions ordered by what would help most, with an explanation of how it decides.

Editor and figures

  • Rich editing on the question page — no new platform needed: Milkdown is already installed and used for articles, courses and the quick modal.
  • Milkdown on the stem and the options, replacing the plain textareas. Note the stem carries manual-highlight offsets, so check what a WYSIWYG rewrite does to them before switching.
  • Many figures per questionquestion_media links a question to any number of images in the bank, each with a role (stem or explanation), a label the prose can refer to ("Figure 1") and an order. The 346 existing single paths were backfilled and retitled; the filename stays in the caption as provenance.
  • Explanation figures as labelled thumbnails that open a preview, so the prose can say "refer to Figure 2".
  • Figure management in the question editor — add, label, caption, reorder, remove, using the image picker.

Dashboard

  • Stats are about questions, not quizzes — questions seen out of the bank, answered correctly, average score.
  • The old performance card is gone — a per-quiz graph needing two attempts, superseded by the session analysis.
  • Vary the greeting rather than one fixed line.

Questions I owe an answer to

  • What extracted the PDFs? PyMuPDF (fitz) in pdf_service.py, with an MD5 skip list for repeated branding images. It pulled every embedded image from all 18 source PDFs, which is why one 767-page document produced 908.
  • How do the study recommendations work? — walk through the code.
  • How would an adaptive session work? — design before building.

Design agreed, not built

  • AI Mode (RAG chat) — done 2026-09-10. conversations and conversation_messages (migration a5b6c7d8e9f0), retrieval across sections, articles, questions and cards with the curated-link boost, the ID-citation contract enforced server-side, and /ai with a thread rail. Answers cite by number and every source opens; a section citation deep-links into its section. Question sources carry the stem only, so a chat cannot hand away the practice it is meant to prepare for.
  • Global search page — done 2026-09-10. GET /search runs every corpus at once and /search groups the answer by kind, with section hits listed under the article they belong to and linked to that section. A header box offers "go to this article" or "search everything". Each corpus keeps its own visibility rules — bank predicate and exam scope for questions, the draft rule for articles, deck ownership for cards, library grants for images. The Search / AI Mode toggle waits on AI Mode.

UI fixes raised 2026-09-10

  • Quiz/test categories removed — done.
  • Sessions list shows only a few — done, with a link to full history.
  • Analysis session rail full-height — done.
  • Articles page layout — done 2026-09-10. The page is the column browser itself: topics and the articles filed under them share a column, separated by icon, one column per level opened.
  • Systems facet duplicates — done 2026-09-10. 491 redundant "(Parent)" suffixes stripped and 3 sibling pairs merged. Nesting still to do.
  • "⚙ Filters2948 questions" — done. The stylesheet was never imported.
  • Category page relationships — direct vs rolled-up counts, empty-leaf badge, and a desktop hint on small screens.
  • Newly created categories now appear — the bare path 307-redirected to http://, which the browser blocks as mixed content, so the call failed silently. Trailing slash added.
  • Image/media page — done 2026-09-10. Tags and captions show in the picker as well as the bank, libraries have their own rail, and ids sit on every thumbnail rather than only on hover.

Content and editing

  • Study plans have a front end at all — done 2026-09-10. 13 plans were seeded with an API to serve them and no page that called it. /study-plans lists them with progress in blocks; /study-plans/:id is one plan.
  • Admin can edit study plans — done 2026-09-10. Create (as a draft), rename, publish/unpublish, delete; add, rename, reorder and remove blocks; move questions between blocks of the same plan; attach and detach reading. Editing is inline on the learner's own page, so there is no second layout to keep in step.
  • Study plan blocks carry articles — done 2026-09-10. Each block shows Articles with a reversible Mark as read, then Sessions with Study and Exam mode. Reading progress is per learner and separate from article_views: opening an article is not the claim that you finished it.
  • Admin settings page revamp — currently ugly; needs restructuring.
  • Image libraries — done 2026-09-10. Libraries, per-library grants, tags on the shared vocabulary, and MinIO behind a storage service.
  • Media management page (frontend) — done 2026-09-10. /media browses the bank by library, searches by what an image shows, and edits title, caption, alt text, tags and library in place; ids are on every thumbnail because that is what a question refers to, and an image with no caption is called out as one nobody will find. ImagePicker replaces the type-a-filename field on the question editor, and an image uploaded from it lands on the question directly.
  • Question folders — collect questions into folders for assignment and access, alongside category grants.

Article reading

  • Nested sections and per-section collapse — done 2026-09-10. A section may sit under an earlier top-level one (parent_id on the section JSON), the contents rail lists sub-sections under their parent, and an article opens as headings only, each expanding where it sits. Deep links open the target section and its parent.
  • Cross-references with previews — done 2026-09-10. [[slug]] or [[Label|slug]] in article prose becomes an in-app link that shows title, excerpt and section count on hover, from GET /articles/preview/{slug}. One fetch per article per page; no card on touch, where there is no hover.
  • Library browsed column by column — done 2026-09-10. The articles page is now the column browser itself: topics and the articles filed under them share a column, separated by icon. Search still answers with a flat list.
  • References — a section titled "References" is pinned last and styled, but the numbered list with superscript markers linking down to it is not built.
  • Per-section notes and feedback — a learner's own note attached to a section, and a feedback channel to the educator.
  • High-yield / key-exam-info toggles — mark spans and let the reader show or hide them.

Quiz runner

  • Per-question notes in study mode, replacing the global notes tab that is currently on the quiz page.
  • Per-question feedback to the educator.
  • Tutorial mode — first-run coach marks ("Step 2 of 6", Skip / Next).

Analysis

  • Per-question performance table — number, stem excerpt, difficulty, time per question, percentile; sortable, paginated.
  • Session analysis tab — per-session results with study recommendations grouped by Articles / Disciplines / Systems.

Dashboard

  • Overview page for signed-in users — search hero with Search / AI Mode toggle, "Continue your study", and a study-analysis donut. The current dashboard becomes this; a separate signed-out landing page comes later.

Taxonomy

  • Systems subsystems — done 2026-09-10. 69 subsystems created, 305 conditions nested; the tree is now System → Subsystem → Condition.
  • Cross-system duplicate conditions merged — done 2026-09-10. 216 names folded to one home each (347 rows removed, 1075 → 786), keeping the other systems as question_category_links rather than duplicate rows.
  • 114 tied conditions need a human call — an even split across systems has no evidence-based winner, so nothing was merged. Run scripts/merge_duplicate_conditions.py to list them; the largest are Pneumonia, Behavioral Disorder, Intestinal Obstruction, Acute Rheumatic Fever, Hypoxic-Ischemic Encephalopathy.
  • Primary categories pushed down — done 2026-09-10. 1,244 questions promoted from system to condition; 2,730 → 1,486 filed at system level. The system stays as a cross-link so no filter narrows.
  • Unfiled questions classified — done 2026-09-10. 1,170 of 1,486 filed by retrieval + a chooser constrained to the shortlist. 316 remain on a bare system: 301 the chooser declined and 15 with no viable candidate.
  • Every question filed below its system — done 2026-09-10. The last 316 were forced onto their nearest candidate; 0 remain on a bare system.
  • Review the classifier's work — roughly 3 in 4 were right on a spot check, so expect some wrong. The original system is kept as a cross-link, so a wrong pick never loses a question. Worth an editorial pass.
  • 31 questions tie between equally specific conditions — left alone.
  • Real organ systems, and symptoms grouped under them — done 2026-09-10. The 27 "systems" were disciplines; that tree is now the Topics facet, and Systems is a new flat axis of 16 organ systems. question_tags gained parent_id/sort_order (migration e3f4a5b6c7d8), and 726 symptoms are filed under the system they present in. 3,536 stay top-level rather than being forced into an approximate bucket.
  • Taxonomy editing screen — done 2026-09-10. /categories is now Taxonomy: five tabs (Topics, Systems, Symptoms, Diseases, Subjects), each with create / rename / reparent / delete-with-move, and "+ Questions" to attach questions from a bank search. Everything added lands in the question bank and quiz builder immediately.
  • Article section index backfilled — done 2026-09-10. The index was empty because _rebuild_section_index only runs on save, so articles written before it existed had no rows. 10 articles / 28 sections indexed and embedded; section-scoped question links already worked.
  • Grant dimensions — done 2026-09-10. A grant names any combination of exam, discipline and category, and covers what matches all of them.
  • Exam-scoped disciplines — done 2026-09-10. A tag may be shared (exam_id NULL, e.g. Cardiology) or scoped to one exam. Boards Pediatrics and a future Step 1 Pediatrics are separate rows over different content, rather than one label spanning both. Pediatrics is bound to Pediatrics Boards and now covers all 2,948 questions. Facet counts are computed within the active exam and empty facets are hidden.

AI Mode design

Retrieval decides what the model may cite; the model only writes prose.

  1. Embed the learner's message, search every corpus (hybrid_ids already covers questions, articles, sections, cards, media).
  2. Put the retrieved rows in the prompt as the only permitted sources, each with its kind and id.
  3. The model cites by id from that list — [[article:7#features]] — never a URL.
  4. The server rewrites citations to links and drops any id that was not retrieved. A citation the model invented cannot survive.

That last step is the safety property, and it is enforced by the system rather than by the model behaving well — the same discipline as the article page no longer printing answers.

Settled: two kinds of link, handled differently.

Curated links already exist as tables — QuestionArticleLink, FlashcardArticleLink, FlashcardQuestionLink, LabReferenceCardLink — and carry a section id. An educator asserted them, so they are trusted and stored.

Retrieved links are computed per query by hybrid_ids and are not stored at all. They are ranked guesses.

The AI uses both: retrieval finds candidates, and a candidate carrying a curated link to another candidate is boosted, because an educator has already tied those two together. Nothing new is stored for citations. The only new tables AI Mode needs are conversations and messages.


Done this session

Hybrid search (full text + BGE-M3, RRF-fused) · embedding provenance and retry job · articles, cards, sections and media as searchable corpora · exams as real data with a per-user active exam · AI-mode matching from description or upload · category management page · tag vocabulary sanitised · question manager with bulk editing · per-category educator grants · full-page question editor · session rail with gradual reveal · articles read as one page · practise-this-topic · continue-study panel · PREP study plans · question version history · exam-scoped disciplines.