Skip to content

Task: Show real earned badges on the v3 user profile (and honour hide_badges) #2558

Description

@herzog0

Context

The v3 profile page shipped with a mocked badges card (demo_badges) and a hardcoded featured
badge ({"name": "Bug Catcher", "badge": TIER_5}). Meanwhile story #2447 / PR #2517 added a
hide_badges privacy field to the edit-profile page - a field that is stored but read nowhere, so
a user who opts out still has badges rendered publicly.

This ticket connects the real badge data to the existing v3 components and closes that privacy gap.

Scope

  1. User.get_earned_badges(include_hidden=False) - active badges, highest rank first.
    Ordering must use TierRank.db_order() and only then threshold: thresholds are not comparable
    across badges (a reviewer diamond needs 5, a commits silver needs 12), so sorting by threshold
    would feature the wrong badge.
  2. highest_badge, featured_badge, badges_for_display(), badge_url, to_v3_profile_dict()
    fed from real data.
  3. Gate hide_badges at the query source so every downstream accessor fails closed rather than
    each call site remembering. Because cached_property cannot take arguments, the accessors that
    need an owner-facing variant split into a method plus a cached property.
  4. The owner's own profile page passes include_hidden=True.
  5. Rename the demo_badges context key to profile_badges on the real profile path (the V3
    examples-page fixtures in core/views.py keep the demo_* name - those are intentional).
  6. Fix users/profile_cards.py, which emits a badge_url key while _user_profile.html reads
    author.badge, so news post-author cards render no badge at all.
  7. Tests for the ordering rule, all accessors, the hide_badges suppression, the owner bypass, and an
    end-to-end profile-page render.

Out of scope

Decision to confirm with product

hide_badges semantics. Implemented as: the toggle governs what visitors see; the owner still
sees their own badges on their own profile page, because they need them visible to manage them and the
adjacent "Pick Badge" section is meaningless otherwise.

This follows the field's help_text ("Hide badges from the public profile") over its form label
("Hide badges on your profile"), which are slightly at odds. If product wants the owner's view hidden
too, it is a one-line change (drop include_hidden=True in CurrentUserProfileView), and the form
label should then win and the help_text be corrected.

Acceptance criteria

  • The profile badges card renders the user's real earned badges, highest rank first
  • The featured badge on the user card is the user's highest rank, even when another badge has
    a larger raw threshold
  • A user with hide_badges=True shows no badges to visitors, on every accessor
  • That same user still sees their own badges on their own profile page
  • badge_url returns "" (not a placeholder medal) for a user with no badges
  • News post-author cards render the author's badge
  • No mock-era demo_* naming remains on the real profile path
  • No new CSS or template components introduced - existing _badges_card.html, _badge_v3.html
    and _user_profile.html are reused
  • Renders without JavaScript
  • Full suite green (1184 passed / 43 skipped), pre-commit clean
  • Screenshots: light, dark, mobile; owner vs. visitor with hide_badges on; news author card
    before / after

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions