pdf-quiz-generator/backend/scripts
Daniel 3980ecb7f8 feat: one Markdown renderer for the whole site, with LaTeX and highlights intact
Every piece of educator prose the platform stores is Markdown, and until now
only articles rendered it. A lab panel written as a table reached the quiz player
as a row of literal pipes, which is why the table conversion had to be held back.

`RichText` is now the single renderer: GFM tables, `$…$` maths through KaTeX,
images resolved through the uploads helper, external links opened safely, and
raw HTML escaped rather than executed — a stem can never inject markup into the
page around it. The question bank's `dangerouslySetInnerHTML` is gone with it.

Highlights were the hard part
Manual highlights and the read-aloud cursor are stored as character offsets into
the raw stem, and rendering Markdown destroys the one-to-one map a plain string
gave us. A rehype plugin puts it back: each text node in the output carries the
source offsets it was parsed from, so a highlight saved before this change still
lands exactly where it was drawn, and the selection arithmetic that reads
`data-start` needs no change at all.

Inside an inline-formatted run the rendered text is shorter than its source by
the marker characters, so an offset picked mid-run can be out by a few. Splitting
per text node bounds that to one node and keeps every node boundary exact —
stated in the code, because it is a real limit rather than an oversight.

With that in place the lab tables are applied: 79 stems, 82 panels. Question 3333
now reads as two tables with `3.5 × 10⁹/L` instead of `3.5 x 109/L`, and the
`inEq/L` and `mrnol/L` scanning damage repaired. Each change was snapshotted
first, so it is reversible from the question editor.

Six schematic illustrations
Drawn from scratch as SVG in `scripts/seed_illustrations.py` — bilirubin risk
zones, airway narrowing by level, dehydration bands, the fluid pathway, the
target sign, growth velocity. Each is captioned, tagged and searchable in the
image bank, and each says on its face that it is schematic and not a clinical
reference. They exist so the media library, picker and article figures can be
exercised against real files, and because an article with no figure looks
unfinished even when its prose is not.

234 frontend tests green, 11 of them new on the renderer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-10 18:12:37 +02:00
..
backfill_section_index.py feat: real organ systems, symptoms grouped by system, and admin taxonomy CRUD 2026-09-10 10:44:39 +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_tags_to_categories.py feat: PREP as tags, stats toggle, comments redesign, quizzes CTA 2026-09-09 01:30:20 +02:00
fix_lab_formatting.py feat: article CMS, three reading views, and articles written from the library 2026-09-10 17:13:07 +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
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
promote_primary_categories.py feat: promote primary categories from system down to condition 2026-09-10 04:32:55 +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_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_prep_study_plans.py feat: PREP study plans of numbered blocks 2026-09-10 02:25:29 +02:00
seed_sample_content.py feat: sample smart-links quiz, bank feedback polish, performance hierarchy 2026-09-09 03:31:54 +02:00
seed_showcase_articles.py feat: AMBOSS-style session management, question manager, showcase articles 2026-09-09 18:27:53 +02:00
tag_prep_quizzes.py feat: PREP as tags, stats toggle, comments redesign, quizzes CTA 2026-09-09 01:30:20 +02:00
triage_question_images.py fix: MinIO name collision, and refuse to write an article from fragments 2026-09-10 17:31:53 +02:00