Skip to content

Store original uploaded filename (admin-only) for talks/posters/pubs (#1391)#1400

Merged
jonfroehlich merged 1 commit into
masterfrom
1391-store-original-upload-filename
Jun 25, 2026
Merged

Store original uploaded filename (admin-only) for talks/posters/pubs (#1391)#1400
jonfroehlich merged 1 commit into
masterfrom
1391-store-original-upload-filename

Conversation

@jonfroehlich

Copy link
Copy Markdown
Member

Closes #1391.

What & why

Artifact.save() renames uploaded files to the standardized Author_TitleInTitleCase_VenueYear scheme, destroying the one human-recognizable clue in the upload name (e.g. MyTalk_v3_final.pptx). This stores that original name in two new admin-only fields and surfaces it read-only on the change form as a provenance breadcrumb.

Changes

  • Artifact (abstract base): add nullable, editable=False original_pdf_filename / original_raw_filename. A capture block at the top of save() snapshots the basename only on a genuine new upload — the first save, or an edit where the file field is in the incoming update_fields (before the rename logic appends to it). It does not fire on the m2m authors_changed rename re-save or on metadata-only edits, so a standardized name never clobbers a stored original.
  • ArtifactAdmin: read-only "Originally uploaded as" row injected into the Files fieldset on the change form (inherited by Talk/Poster/Publication).
  • backfill_original_filenames command (new): recovers originals for the many bulk-imported prod rows whose files were never renamed (their on-disk name still is the original). If a file's basename ≠ generate_filename() and isn't a -<timestamp> uniquified variant of it, the current basename is recorded as the original. Idempotent (fills nulls only); wired into docker-entrypoint.sh (step 4.7b).
  • Tests: capture-survives-rename, edit-replace, metadata-only-no-clobber, backfill recover/skip/idempotent/uniquified, admin display. Full suite green (551 tests).

Why the backfill matters / ordering

Investigating the prod dump: the unrenamed files are genuine originals (title-derived names + Django's _xxxxxxx collision suffix) from rows that never went through an authored save(); the historical rename one-shots are commented out of the entrypoint. Latent risk: editing any legacy row in admin (even metadata) triggers the rename and destroys its original name — and the in-save() capture won't fire on a metadata edit. The backfill is the only thing that preserves these, so it runs at container start before admin traffic. Do not re-enable any rename one-shot until this has deployed and backfilled. (Re-standardizing those legacy files is a separate follow-up issue.)

Migration

No committed migration (matches repo convention): settings_test builds tables from models; prod/test run makemigrations website at container start, generating the two additive nullable columns.

Notes

  • UI: the only UI surface is a read-only text row on the admin change form (login-gated, not in the Pa11y public scan set). Admin before/after screenshot to be added.
  • No version bump in this PR (leave for the release cut).

🤖 Generated with Claude Code

…1391)

Artifact.save() renames uploaded files to the standardized
Author_TitleInTitleCase_VenueYear scheme, destroying the human-recognizable
upload name (e.g. MyTalk_v3_final.pptx). Capture that name into two new
admin-only fields and surface it read-only on the change form, as a provenance
breadcrumb for confirming/debugging uploads.

- Artifact (abstract base): add nullable, editable=False original_pdf_filename
  and original_raw_filename. Capture block at the top of save() snapshots the
  basename ONLY on a genuine new upload — the first save, or an edit where the
  file field is in the incoming update_fields (before the rename logic appends
  to it). It deliberately does not fire on the m2m authors_changed rename
  re-save or on metadata-only edits, so a standardized name never clobbers a
  stored original.
- ArtifactAdmin: read-only "Originally uploaded as" display injected into the
  Files fieldset on the change form (inherited by Talk/Poster/Publication).
- backfill_original_filenames management command: production has many
  bulk-imported rows whose files were never renamed (their on-disk name still
  IS the original). Recover those: if a file's basename != generate_filename()
  (and isn't a "-<timestamp>" uniquified variant of it), record the current
  basename as the original. Idempotent (fills nulls only); wired into
  docker-entrypoint.sh so it runs at container start before any admin edit can
  rename a legacy row and lose its original name.
- Tests: capture-survives-rename, edit-replace, metadata-only-no-clobber,
  backfill recover/skip/idempotent/uniquified, admin display.

No committed migration (matches repo convention): settings_test builds from
models and prod/test run makemigrations website at container start.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jonfroehlich jonfroehlich merged commit 57c7129 into master Jun 25, 2026
3 checks passed
jonfroehlich added a commit that referenced this pull request Jun 25, 2026
Triggers a fresh -test build that includes #1400 (the prior merge did not
re-fire the deploy webhook) and preps the prod release.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Store original uploaded filename and show it (admin-only) for talks/posters/publications

1 participant