Skip to content

WIP: Add backfill_program_certificate_titles management command#3697

Open
zamanafzal wants to merge 3 commits into
mainfrom
zafzal/backfill-program-certificate-title
Open

WIP: Add backfill_program_certificate_titles management command#3697
zamanafzal wants to merge 3 commits into
mainfrom
zafzal/backfill-program-certificate-title

Conversation

@zamanafzal

@zamanafzal zamanafzal commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

What are the relevant tickets?

https://github.com/mitodl/hq/issues/11938

Description

Issued program certificates freeze their "Certificate Title" (product_name) into the Wagtail page revision in effect at issue time (ProgramCertificate.certificate_page_revision), and the display path reads certificate_page_revision.as_object().product_name. So editing the live "Certificate Title" in the CMS does not update certificates already issued — they keep their old frozen title until a one-time backfill is run.

This adds a backfill_program_certificate_titles management command (workstream 1 of hq#11938, follow-up to hq#11907 / #3695) that rewrites each issued program certificate's frozen product_name to the live CMS page value.

python manage.py backfill_program_certificate_titles \
    (--program <readable_id> [--program <readable_id> ...] | --all-programs) \
    [--commit]

Behavior & safety

  • Dry-run by default — reports planned changes; --commit is required to write.
  • Mode validation — exactly one of --program (repeatable) / --all-programs; neither or both raises CommandError, so a bare run touches nothing.
  • Placeholder/empty guard — skips any program whose live title is empty or still a "PLACEHOLDER - " default; placeholder text is never stamped onto real certificates.
  • Idempotent — revisions already matching the live title are reported [skipped]; a second --commit run is a no-op.
  • Distinct-revision dedup — certs issued at the same page revision share one Revision row, so each shared revision is edited once (report shows (N certs)).
  • Tolerant skips (never crash) — unknown readable_id, missing CMS page, missing certificate page, and null-revision (revoked) certs are reported and skipped.
  • Per-program writes run inside transaction.atomic(); a final Summary: line tallies programs processed/skipped, revisions changed, and certs affected.

Write strategy

Edits the existing Revision.content["product_name"] in place (per design): simplest, fewest writes, fixes all certs sharing a revision at once. Trade-off — the page's revision-history view will retroactively show the corrected title, which is acceptable for a one-time correction.

Test plan

  • --commit rewrites frozen product_name to the live title (asserted via the display path revision.as_object().product_name)
  • Dry-run (default) makes no DB writes and reports the planned change
  • Idempotent: second --commit reports zero changes
  • Placeholder / empty live title → program skipped, no writes
  • Null-revision (revoked) cert → not touched
  • Shared revision: multiple certs → single edit, all updated
  • Mode validation: neither flag → CommandError; both flags → CommandError
  • --all-programs processes clean programs and skips placeholder / page-less ones
  • Unknown --program readable_id reported and skipped; other programs still processed
  • 14 tests pass; ruff format + ruff check clean

Issued program certificates freeze their "Certificate Title"
(product_name) into the CMS page revision in effect at issue time, so
editing the live title later does not update already-issued certs. This
one-time backfill rewrites each issued program certificate's frozen
product_name to the live CMS page value.

Safety: dry-run by default (--commit to write), requires exactly one of
--program/--all-programs, skips programs whose live title is empty or
still a "PLACEHOLDER - " default, is idempotent, edits each shared
revision once, and never touches null-revision (revoked) certs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

OpenAPI Changes

Show/hide ## Changes for v0.yaml:
## Changes for v0.yaml:
2 changes: 1 error, 0 warning, 1 info
error	[api-path-removed-without-deprecation] at base/openapi/specs/v0.yaml
	in API POST /api/v0/b2b/manager/organizations/{parent_lookup_organization}/contracts/{id}/codes/send_test_email/
		api path removed without deprecation

info	[api-schema-removed]
	in components/schemas
		removed the schema `SendTestEmailRequest`



## Changes for v1.yaml:
2 changes: 1 error, 0 warning, 1 info
error	[api-path-removed-without-deprecation] at base/openapi/specs/v1.yaml
	in API POST /api/v0/b2b/manager/organizations/{parent_lookup_organization}/contracts/{id}/codes/send_test_email/
		api path removed without deprecation

info	[api-schema-removed]
	in components/schemas
		removed the schema `SendTestEmailRequest`



## Changes for v2.yaml:
2 changes: 1 error, 0 warning, 1 info
error	[api-path-removed-without-deprecation] at base/openapi/specs/v2.yaml
	in API POST /api/v0/b2b/manager/organizations/{parent_lookup_organization}/contracts/{id}/codes/send_test_email/
		api path removed without deprecation

info	[api-schema-removed]
	in components/schemas
		removed the schema `SendTestEmailRequest`



Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

Comment thread courses/management/commands/backfill_program_certificate_titles.py Outdated
@zamanafzal zamanafzal changed the title Add backfill_program_certificate_titles management command WIP: Add backfill_program_certificate_titles management command Jun 24, 2026
Address Sentry review feedback: reassign the JSONField value and save with
update_fields=["content"] instead of mutating revision.content in place, so
the write is robust to any save()/dirty-tracking behavior and only touches
the content column (no incidental field updates). Behavior is unchanged;
all 14 command tests still pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread courses/management/commands/backfill_program_certificate_titles.py
Restructure the command's output so each targeted program prints a full
block (readable_id, program title, current frozen title, target live
title, and a YES/NO/SKIPPED change flag). Already-correct and skipped
programs now appear too, so nothing falls through silently. This lets the
dry-run be shared directly with admins to verify titles before --commit.

Co-Authored-By: Claude Opus 4.8 <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.

1 participant