pdf-quiz-generator/backend/scripts
Daniel 1f77d421c7 feat: cross-link the articles, and strip what the analysis page replaced
The linking was the gap
The marker system was built weeks ago — resolves by id, survives a rename, shows
a preview on hover — and not one of 333 articles used it. Every article was
written in isolation, so a piece on croup named stridor and epiglottitis and
offered no way to reach either. `scripts/link_articles.py` reads what is written
and links it: 3,718 cross-references across 307 articles, by id, so a later
rename cannot break them.

Conservative on purpose, because a wrong link is worse than a missing one: only
the first mention in a section, whole words, longest title first so "Otitis media
with effusion" beats "Otitis media", never inside an existing link, marker,
heading, code span or table, and never an article to itself.

That exposed a second thing: the reading view had its own Markdown pipeline with
its own cross-reference regex, and it only understood the old slug form. It would
have printed every one of those 3,718 links as literal brackets. Article prose
now goes through the same renderer as the rest of the site.

Short and Clinical looked empty
Both are usually a single section, and everything starts collapsed, so the tab
showed one heading over blank space. A view of one section is not a contents
page; it opens.

Removed
Quiz reminders — emailed nudges to retake anything under 75%, with a scheduler
that existed solely to send them: the model, the service, the scheduler, the
email, the table. Article comments. The dashboard's in-progress list and its
stat cards, both of which the analysis page now answers better.

One mistake worth recording: the first pass at removing the reminder cleanup used
a regex that took 109 lines with it, including an unrelated endpoint. The test
suite caught it (`/attempts/quiz/{id}/in-progress` returning 404 instead of 403),
and the file was restored and edited by exact match instead.

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:44:44 +02:00
..
article_pipeline.py feat: a study objective that changes what you see; purge unused figures 2026-09-11 02:47:01 +02:00
backfill_question_figures.py feat: figures as records, question-centred dashboard, fewer hints mid-quiz 2026-09-11 03:18:49 +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 refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +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 feat: every figure in the image bank, bigger editing boxes, footer links 2026-09-11 02:32:06 +02:00
link_articles.py feat: cross-link the articles, and strip what the analysis page replaced 2026-09-11 03:44:44 +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
promote_primary_categories.py feat: promote primary categories from system down to condition 2026-09-10 04:32:55 +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
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
retitle_figures.py feat: cross-link the articles, and strip what the analysis page replaced 2026-09-11 03:44:44 +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_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
seed_study_plans.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +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 refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00