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
40 lines
1.4 KiB
Python
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)
|