pdf-quiz-generator/backend/scripts
Daniel 3418ed023b fix: WebP figures, the openai SDK removed, and a voice a site can add to
Three things landed together; the message names all of them, because a commit
that mentions one is a commit nobody finds the other two in.

**Figures.** Thirty-four JPEG 2000 files — 21 on questions, the rest unattached
in the media library — are WebP now, with `questions.image_path`,
`questions.explanation_image_path` and `media_assets.path` repointed together.
Serving already converted them on the way out, so nothing was broken; this
removes the step and makes what is stored the same thing that is served. The
originals stay: they are the only copy of what came out of the PDF, they cost a
few megabytes between them, and a conversion nobody can undo is not one to run
against a live bank. Paths are found by what the columns say rather than by
listing a bucket, because three tables record them and updating two would be
worse than none.

**The openai SDK is gone.** Ten call sites — one more than the map said, the
Celery article drafter — every one of them a POST with a JSON body, and not one
reading usage, cost, tool calls or logprobs. Every other call to the same proxy
was already plain httpx: embeddings, the ChromaDB embedding function, speech
both ways, model discovery, the vision probe. So this deletes an abstraction
rather than swapping one for another, and leaves one HTTP client instead of
two. `chat()` and `achat()` return the message content; a `ProxyError` carries
the status and the first 500 characters of the body, which is where the proxy
explains itself.

Behaviour is preserved deliberately, including a 600-second fallback timeout
for the four call sites that were running on the SDK's ten-minute default.
Lowering that is a real change and belongs in its own commit.

Proved against the live proxy on both services rather than only against mocks:
a completion, an async completion, a real 400 the vision probe still classifies
as a refusal, 407 models read from the catalogue, and a word read off an image.

**Voice.** A chosen voice is honoured whatever serves it. The prefix check only
accepted a locally served one, so a site adding a hosted voice would offer it
in Settings, save the learner's choice, and then quietly read every question in
the default voice. The list has always come from the database — adding a voice
is a row in Settings → AI models, never a code change.

And the sign-in page stops offering a locked door: `signup-policy` reports
whether registration is open at all, and the Sign up link goes when it is not.
The switch existed and the only way to discover it was to fill the form in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-12 17:13:05 +02:00
..
abp-2024-outline.json feat: exams are a group you can build, with the board's own blueprint 2026-09-11 20:27:48 +02:00
article_pipeline.py feat: a session prepared for you, and a model that can see when the one on the job cannot 2026-09-12 15:46:04 +02:00
assign_category_systems.py feat: an organ system is a fact about a topic, not about a keyword 2026-09-12 04:32:25 +02:00
backfill_question_figures.py feat: AMBOSS exam chrome, review as a study session, and no silent submissions 2026-09-12 07:37:58 +02:00
bind_exam_tags.py feat: exam-scoped disciplines and exam-scoped facet counts 2026-09-10 02:48:34 +02:00
classify_unfiled_questions.py feat: MinIO-backed media libraries, and file the last 316 questions 2026-09-10 06:45:22 +02:00
convert_jpeg2000_figures.py fix: WebP figures, the openai SDK removed, and a voice a site can add to 2026-09-12 17:13:05 +02:00
convert_tags_to_categories.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
fix_blueprint_collisions.py fix: one topic per idea, and one ABP domain per topic 2026-09-12 07:42:00 +02:00
fix_lab_formatting.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
generate_articles.py feat: article CMS, three reading views, and articles written from the library 2026-09-10 17:13:07 +02:00
index_question_images.py fix: choosing is answering; figures say only what an educator wrote 2026-09-12 00:19:35 +02:00
link_articles.py feat: settings as places with addresses; repair nested cross-references 2026-09-11 12:46:13 +02:00
mdm_pass.py fix: 'No therapy is required' is an answer, not a placeholder 2026-09-11 13:58:02 +02:00
merge_duplicate_categories.py feat: AMBOSS exam chrome, review as a study session, and no silent submissions 2026-09-12 07:37:58 +02:00
merge_duplicate_conditions.py feat: nest the system tree and give each condition one home 2026-09-10 04:26:48 +02:00
migrate_uploads_to_s3.py feat: serve uploads through storage, and move all 3,852 files to MinIO 2026-09-10 10:23:34 +02:00
nest_symptoms_by_system.py feat: real organ systems, symptoms grouped by system, and admin taxonomy CRUD 2026-09-10 10:44:39 +02:00
nest_systems.py feat: nest the system tree and give each condition one home 2026-09-10 04:26:48 +02:00
number_board_reviews.py feat: session analysis after a quiz, and a merged TODO for the rest 2026-09-11 03:07:05 +02:00
parent_keywords_to_systems.py feat: an organ system is a fact about a topic, not about a keyword 2026-09-12 04:32:25 +02:00
promote_primary_categories.py feat: promote primary categories from system down to condition 2026-09-10 04:32:55 +02:00
prose_pass.py fix: the imperative detector only ever saw sentence starts 2026-09-11 13:16:05 +02:00
purge_unused_figures.py feat: a study objective that changes what you see; purge unused figures 2026-09-11 02:47:01 +02:00
reindex_article_search.py feat: search that looks at what an article says 2026-09-12 15:57:42 +02:00
rename_study_plans.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
reorganize_topics.py fix: one topic per idea, and one ABP domain per topic 2026-09-12 07:42:00 +02:00
restore_subject_disease_tags.py fix: put the disciplines and diseases back, and order Performance as shown 2026-09-12 05:39:22 +02:00
retire_question_tags.py feat: retire the tags, and stop settings from hiding from the page that 2026-09-12 05:06:58 +02:00
retitle_figures.py feat: cross-link the articles, and strip what the analysis page replaced 2026-09-11 03:44:44 +02:00
rewrite_catalogue_summaries.py feat: a session prepared for you, and a model that can see when the one on the job cannot 2026-09-12 15:46:04 +02:00
rollback_tag_category_conversion.py feat: system category hierarchy from tags, category editor overlay 2026-09-08 23:50:25 +02:00
sanitize_categories.py fix: category tree cleanup, mixed-content redirect, and category page clarity 2026-09-10 04:09:18 +02:00
sanitize_tags.py feat: facet filters on the question bank, category page, sanitised tags 2026-09-10 00:13:31 +02:00
seed_abp_blueprint.py feat: exams are a group you can build, with the board's own blueprint 2026-09-11 20:27:48 +02:00
seed_illustrations.py feat: one Markdown renderer for the whole site, with LaTeX and highlights intact 2026-09-10 18:12:37 +02:00
seed_lab_references.py feat: PREP as tags, stats toggle, comments redesign, quizzes CTA 2026-09-09 01:30:20 +02:00
seed_sample_content.py feat: a session prepared for you, and a model that can see when the one on the job cannot 2026-09-12 15:46:04 +02:00
seed_showcase_articles.py feat: a session prepared for you, and a model that can see when the one on the job cannot 2026-09-12 15:46:04 +02:00
seed_study_plans.py feat: study-plan blocks as modules, sessions that know their block 2026-09-11 04:31:21 +02:00
settle_topic_questions.py fix: the ear to ENT, gynecology to its own branch, and Surgery back into medicine 2026-09-12 08:06:59 +02:00
tag_source_quizzes.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
triage_question_images.py fix: WebP figures, the openai SDK removed, and a voice a site can add to 2026-09-12 17:13:05 +02:00