pdf-quiz-generator/backend/alembic/versions/l2c3d4e5f6a7_media_source.py
Daniel 809ac2fca6 feat: figures are thumbnails that open into a viewer, with source and overlay
A 2,000px radiograph written into an article rendered at whatever width it
happened to be — a wall of greyscale in the middle of a sentence, four
megabytes to draw it, and no way to look at it properly. Every image in prose
is now a 256px thumbnail with the author's label under it, and a click gives it
the screen.

The viewer puts what is known about the figure beside it: its title, what it
shows, and where it came from. `media_assets` gains `source` and `source_url`
for that — a citation belongs to the file, because the same figure used in
three articles is cited the same way in all three, and a licence that turns out
to be wrong is one row to fix rather than three paragraphs to find. Asked for
when the figure is opened, not when the page is drawn.

And `overlay`: the regions an educator has marked, as vector shapes in
normalised coordinates on the unit square, so one drawing is correct in a
thumbnail, in the viewer and on a projector. Off until the learner turns it on
— marks shown before they have looked answer the question for them. Vectors
rather than a second burnt-in picture, for four reasons written down in
docs/image-overlays.md. The tool that draws them is next; this is the storage,
the contract and the reader's half.

On a narrow screen the description stacks above the image rather than beside
it, where it can be read before scrolling to the picture.

Also here: `classify_question_difficulty`, which labelled all 2,924 questions
in batches of twenty-five against a written rubric — 622 easy, 1,634 medium,
668 hard, no failed batches. The column had been NULL on every row since it
existed. Migration l2c3d4e5f6a7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-12 20:33:00 +02:00

40 lines
1.4 KiB
Python

"""Where an image came from, and what has been marked on it
`source` is the citation as it should be read — figure, authors, publication,
licence — and `source_url` is where to check it. `overlay` holds the regions an
educator has marked on the image, as vector shapes in normalised coordinates so
they land in the right place at any size. Both on the asset rather than
typed into each caption: the same figure used in three articles is cited the
same way in all three, and a licence that turns out to be wrong is one row to
fix rather than three paragraphs to find.
Revision ID: l2c3d4e5f6a7
Revises: k1b2c3d4e5f6
"""
import sqlalchemy as sa
from alembic import op
revision = "l2c3d4e5f6a7"
down_revision = "k1b2c3d4e5f6"
branch_labels = None
depends_on = None
COLUMNS = {
"source": sa.Column("source", sa.Text(), nullable=True),
"source_url": sa.Column("source_url", sa.String(length=600), nullable=True),
# Vector shapes in normalised coordinates, not a second raster: an overlay
# has to be switchable, correctable and correct at any size.
"overlay": sa.Column("overlay", sa.JSON(), nullable=True),
}
def upgrade() -> None:
existing = {column["name"] for column in sa.inspect(op.get_bind()).get_columns("media_assets")}
for name, column in COLUMNS.items():
if name not in existing:
op.add_column("media_assets", column)
def downgrade() -> None:
for name in COLUMNS:
op.drop_column("media_assets", name)