pdf-quiz-generator/backend/app/services
Daniel 889c40483c feat: link a question to its reading from either end, and a player that reads like a Qbank
**Linking.** A question could be tied to an article only from the article, by
typing the question's number into a box — so opening a question you had just
linked showed no sign of the link, and there was no control to add one. Both
ends now search: find the article by title from the question, find the question
by stem from the article, pick which section of the article the link lands on,
and see what is already linked. One shared finder, so the two ends of one
relationship cannot describe it differently. `GET /questions/{id}/articles`
mirrors the endpoint that already existed the other way, and `GET
/articles/linked` is retired — it answered this question by shipping the whole
prose of every linked article to the quiz player for a list of titles.

"Practise this topic" is a reader's control and no longer appears on an editing
screen.

**The player.** The rail was a bordered card floating in the page with a
scrollbar of its own, so a session had two scrollbars side by side and a
collapse handle tucked inside the card's padding. It is a column now: flush,
full height, its own background rather than its own border, the handle on the
boundary it moves, and a progress bar under the count. The bar at the foot is
the bottom edge of the window — three flush segments, no gaps, no pills —
because Exit as a small grey pill beside a large blue Next made leaving look
like the accident.

Study mode no longer asks whether you are sure. Leaving suspends: every answer
is saved, nothing is graded, and it is waiting where you left it — so the
dialog asked permission for something reversible, under a name for something
that does not happen. An exam still asks once, because a block has a clock, and
it now says what it is: "Leave this block?", not "End Session".

Options are lettered. The explanations already are — a stem extracted from a
board PDF says "Preferred Response: E" — so numbering them 1 to 5 left the
reader translating between two labellings of the same five lines. The tutor is
told the same letters, and the answer key is marked against its own option and
declared authoritative, so a model that would have answered differently cannot
tell a student the marked answer is wrong.

"Preferred response" and "Source page 518" are gone: the first labelled a block
that is obviously the answer, the second named a page of a book the learner
does not have. The clocks moved out of a grey strip across the explanation,
where they read as part of the answer, to the foot of the rail with everything
else about the session.

**AI Mode.** Sources are headed and counted at the end, where evidence belongs,
with the practise button after them rather than above. That button appears only
when there is something to build from and says what it will build — it used to
sit under "how can I help you today?" offering to make a session out of
nothing. A cited question opens in place: `/questions/:id` is the editor, so
following one dropped a learner into a form for changing the question they had
just been told about. And a session built from a chat is named like every other
session, rather than after the chat — asking "hi" produced "hi — practice".

Also: two test questions with raw `<p>&nbsp;</p>` in their stems were live in
the bank; retired. And 36 article summaries were written as a table of contents
with the colons filed off — "Peanut allergy prevention and management: LEAP
guidelines by risk tier, risk stratification, and anaphylaxis treatment" — every
noun phrase sounding informative and none of them saying anything. Rewritten as
claims, with the rule added to the prompt that produced them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-12 15:17:04 +02:00
..
__init__.py Initial commit: PDF Quiz Generator app 2026-03-30 20:04:53 +00:00
ai_mode_service.py feat: link a question to its reading from either end, and a player that reads like a Qbank 2026-09-12 15:17:04 +02:00
ai_service.py feat: link a question to its reading from either end, and a player that reads like a Qbank 2026-09-12 15:17:04 +02:00
article_service.py feat: link a question to its reading from either end, and a player that reads like a Qbank 2026-09-12 15:17:04 +02:00
article_writer.py feat: link a question to its reading from either end, and a player that reads like a Qbank 2026-09-12 15:17:04 +02:00
attempt_expiry.py feat: right after a tip is its own slice 2026-09-12 01:51:03 +02:00
captcha.py feat: Cap replaces hCaptcha, self-hosted beside the app 2026-09-12 06:14:14 +02:00
certificate_service.py Add certificate generation, SCORM support, QTI export/import 2026-04-11 01:30:09 +02:00
clinical_library.py feat: short / long / clinical, and stop broad topics retrieving index lines 2026-09-10 18:21:21 +02:00
draft_questions.py refactor: remove per-question sharing 2026-09-12 08:42:51 +02:00
email_service.py feat: cross-link the articles, and strip what the analysis page replaced 2026-09-11 03:44:44 +02:00
embedding_service.py feat: media as a searchable corpus, ready for a vision embedder 2026-09-10 02:22:52 +02:00
exam_blueprint.py feat: study plans built to the board's content outline 2026-09-11 21:40:17 +02:00
extraction_modes.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
invites.py feat: the player is a box; sharing and sign-up are the administrator's 2026-09-11 19:17:35 +02:00
knowledge_groups.py feat: an organ system is a fact about a topic, not about a keyword 2026-09-12 04:32:25 +02:00
pdf_service.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
question_figures.py fix: choosing is answering; figures say only what an educator wrote 2026-09-12 00:19:35 +02:00
quiz_builder.py refactor: remove per-question sharing 2026-09-12 08:42:51 +02:00
quiz_service.py Junction table for shared questions; verification blocking; semantic bank search; bug fixes 2026-04-01 00:55:13 +02:00
search_service.py feat: media as a searchable corpus, ready for a vision embedder 2026-09-10 02:22:52 +02:00
site_settings.py feat: the tutor is an administrator's to allow, and a handbook explains the rest 2026-09-11 21:13:38 +02:00
storage_service.py feat: serve uploads through storage, and move all 3,852 files to MinIO 2026-09-10 10:23:34 +02:00
study_plan_context.py feat: an exam that runs out is a result; delete and reset go all the way down 2026-09-11 04:47:11 +02:00
thumbnails.py feat: thumbnails for uploaded images, at two widths and no others 2026-09-12 08:49:20 +02:00
vector_service.py fix: gateway-agnostic URL handling for TTS and embeddings, docs cleanup 2026-04-19 02:17:35 +02:00