feat: Add badge earned info to user profile badges API response #159

Merged
retoor merged 4 commits from typosaurus/157-add-badge-earned-info-to-user-profile-badges-api-response into master 2026-08-05 02:21:37 +02:00
Collaborator

What was built

Summary

The user profile badges API response is missing information about how each badge was earned. In the webapp, hovering over a badge shows a message such as "Reacted 50 times" for the "Cheerleader" badge, but this message is absent from the API response.

Example current API response:

{
 "name" : "AI Whisperer",
 "icon" : "✨",
 "created_at" : "2026-07-04T00:01:31.598424+00:00"
},
{
 "name" : "Patron",
 "icon" : "🀝",
 "created_at" : "2026-07-24T18:38:25.683152+00:00"
},
{
 "name" : "Cheerleader",
 "icon" : "πŸŽ‰",
 "created_at" : "2026-07-31T17:45:40.325029+00:00"
}

Steps to Reproduce

Not provided.

Expected Behaviour

The API response for badges should include the earned-by information (e.g., "Reacted 50 times" for the Cheerleader badge), matching what is shown in the webapp hover tooltip.

Actual Behaviour

The API response only includes name, icon, and created_at. The message describing how the badge was earned is missing.

Environment

Not provided.


Reported by Lensflare via DevPlace.

Acceptance criteria

  • The ticket objective is satisfied: Add badge earned info to user profile badges API response

Commits

Commit Summary Node
3a0f6820525a test(sveta): Add profile badges description tests to the API tier -
68c403747c16 test(sveta): Extend the profile badges JSON test with description assertions b827a89016b5
f72f2edf6b32 feat(nadia): Add earned-by description to the profile badges API response 974d049e1b1e
4bd420f38b5d feat(nadia): Add description to the badge dict and declare it on BadgeOut 527d1bad2be4

Run

Cost: 0.1444 USD Β· Nodes: 10 Β· Verification: make test

Closes #157

## What was built ## Summary The user profile badges API response is missing information about how each badge was earned. In the webapp, hovering over a badge shows a message such as "Reacted 50 times" for the "Cheerleader" badge, but this message is absent from the API response. Example current API response: ```json { "name" : "AI Whisperer", "icon" : "✨", "created_at" : "2026-07-04T00:01:31.598424+00:00" }, { "name" : "Patron", "icon" : "🀝", "created_at" : "2026-07-24T18:38:25.683152+00:00" }, { "name" : "Cheerleader", "icon" : "πŸŽ‰", "created_at" : "2026-07-31T17:45:40.325029+00:00" } ``` ## Steps to Reproduce Not provided. ## Expected Behaviour The API response for badges should include the earned-by information (e.g., "Reacted 50 times" for the Cheerleader badge), matching what is shown in the webapp hover tooltip. ## Actual Behaviour The API response only includes `name`, `icon`, and `created_at`. The message describing how the badge was earned is missing. ## Environment Not provided. --- *Reported by **Lensflare** via DevPlace.* ## Acceptance criteria - The ticket objective is satisfied: Add badge earned info to user profile badges API response ## Commits | Commit | Summary | Node | |---|---|---| | `3a0f6820525a` | test(sveta): Add profile badges description tests to the API tier | `-` | | `68c403747c16` | test(sveta): Extend the profile badges JSON test with description assertions | `b827a89016b5` | | `f72f2edf6b32` | feat(nadia): Add earned-by description to the profile badges API response | `974d049e1b1e` | | `4bd420f38b5d` | feat(nadia): Add description to the badge dict and declare it on BadgeOut | `527d1bad2be4` | ## Run Cost: 0.1444 USD Β· Nodes: 10 Β· Verification: make test Closes #157
typosaurus added 4 commits 2026-08-05 01:11:26 +02:00
Outcome: done
Changed: devplacepy/routers/profile/index.py:204-208, devplacepy/schemas/content.py:68-73
Verified by: verify() β€” py_compile OK, pyflakes clean, `from devplacepy.main import app` imports clean, pytest tests/api/profile/index.py tests/unit/utils.py β†’ 42 passed
Findings:
- Badge loop (devplacepy/routers/profile/index.py:205-208) sets both `icon` and `description` from a single `get_badge(b["badge_name"])` lookup; `get_badge` always returns a dict with `description` (devplacepy/utils/badges.py:105-108).
- `BadgeOut` (devplacepy/schemas/content.py:71) declares `description: Optional[str] = None`; without it the dict key is dropped by `extra="ignore"` (devplacepy/schemas/base.py:10-11). BadgeOut is consumed only by ProfileOut (devplacepy/schemas/profile.py:32).
- Serialization verified: dict with `description` emits it; without one emits null; ProfileOut passes it through unchanged.
- Environment: workspace Python is 3.11.2, pyproject requires >=3.12, so full `make test` (e2e tier) could not run here; import + targeted tests pass on 3.11.
- 25 pre-existing tests/unit failures (e.g. zip_service KeyError `local_path`) reproduce identically on the stashed clean tree β€” not caused by this change.
- Direct pytest writes `__pycache__` (make exports PYTHONDONTWRITEBYTECODE=1); after a byte-level edit this caused a transient `cannot import name 'AttachmentOut'` in the uvicorn subprocess, gone after removing `__pycache__` β€” run tests via make targets.
Open: test extension asse

Typosaurus-Run: 3828c0c223934696842a70e9d9efb9e0
Typosaurus-Node: 527d1bad2be4464b886a71af0247378e
Typosaurus-Agent: @nadia
Refs: #157
Outcome: done
Changed: devplacepy/routers/profile/index.py:205-208; devplacepy/schemas/content.py:71
Verified by: `python -c "from devplacepy.main import app"` clean; pyflakes clean on both touched files; `python -m pytest tests/api/profile/search.py` β†’ 7 passed; disposable TestClient check β†’ JSON badges each carry `description` equal to BADGE_CATALOG (Cheerleader β†’ "Reacted 50 times") and HTML tooltip intact. Full `make test` not runnable here: only Python 3.11 installed, project requires >=3.12; CI runs the full suite.
Findings: index.py:205-208 enriches each badge dict with `icon` and `description` from `get_badge(b["badge_name"])`; BadgeOut (content.py:71) declares `description: Optional[str] = None`, required because `_Out` uses `extra="ignore"` (schemas/base.py:7). BadgeOut feeds only ProfileOut.badges (schemas/profile.py:32). profile.html:55 tooltips read only `badge_name` from the dict, so HTML is unchanged. tests/api/profile/awards_tab.py:81 fails on base state too (patch round-trip) β€” pre-existing, unrelated.
Open: testwriter may extend test_profile_badges_json_has_non_null_names with a description assertion; awards_tab failure has its own owner.
Confidence: high - both criteria implemented and verified end-to-end; full suite blocked by environment Python version.

Typosaurus-Run: 3828c0c223934696842a70e9d9efb9e0
Typosaurus-Node: 974d049e1b1e4a01923bdf9f583d07cd
Typosaurus-Agent: @nadia
Refs: #157
Outcome: done
Changed: tests/api/profile/search.py:312-319 (8 lines added); stray previous-attempt tests in tests/api/profile/index.py reverted to HEAD
Verified by: verify() β€” py_compile OK; pyflakes shows no new findings (9β†’8, the committed unused BADGE_CATALOG import finding removed); `from devplacepy.main import app` imports clean; pytest tests/api/profile/search.py β†’ 7 passed; red/green demonstrated (FAILED "badge missing description key" against pre-change 13f9fb5, PASSED against HEAD)
Findings:
- tests/api/profile/search.py:312-319 asserts per badge: "description" present, not None, str, non-empty, and == BADGE_CATALOG[badge["name"]]["description"] (BADGE_CATALOG exported at devplacepy/utils/__init__.py:102).
- Awarded badges "First Post"/"Member" exist in BADGE_CATALOG (devplacepy/utils/badges.py:17-18); award_badge inserts only the named badge (badges.py:139-151), so the lookup cannot KeyError.
- Previous attempt's duplicate tests in tests/api/profile/index.py removed; the badge JSON test lives only in tests/api/profile/search.py:276.
- HEAD f72f2ed already carried the implementation (index.py:208, content.py:71) and the unused BADGE_CATALOG import; the addition makes it used.
Open: full `make test` (e2e tier) still requires Python >=3.12; workspace runs 3.11.2 (same limitation as sibling). API tier + import pass here.
Confidence: high - red/green proven against the pre-change implementation; diff additive-only

Typosaurus-Run: 3828c0c223934696842a70e9d9efb9e0
Typosaurus-Node: b827a89016b54505832d8529fbe887cd
Typosaurus-Agent: @sveta
Refs: #157
test(sveta): Add profile badges description tests to the API tier
Some checks failed
DevPlace CI / test (pull_request) Failing after 1h7m34s
3a0f682052
Outcome: done
Changed: tests/api/profile/index.py:501-578 (helper + two tests)
Verified by: py_compile OK; pyflakes clean; pytest (2 new tests) passed; full tests/api/profile/index.py + tests/api/profile/search.py -> 23 passed; e2e test_profile_badges passed
Findings:
- test_profile_badges_json_description_matches_catalog asserts every badge entry carries a non-null non-empty description equal to BADGE_CATALOG[badge['name']]['description'].
- test_profile_badge_description_exact_string awards Cheerleader and asserts description == 'Reacted 50 times'.
Open: none
Confidence: high - new tests pass against committed implementation
retoor merged commit e02919a1db into master 2026-08-05 02:21:37 +02:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: retoor/devplacepy#159
No description provided.