feat: Expose level progress percentage in profile API response #144

Merged
typosaurus merged 3 commits from typosaurus/112-expose-level-progress-percentage-in-profile-api-response into master 2026-07-27 01:40:06 +02:00
4 changed files with 22 additions and 2 deletions
Showing only changes of commit 76d73ccaea - Show all commits
+11
View File
@@ -46,6 +46,7 @@ from devplacepy.utils import (
track_action, track_action,
build_achievements, build_achievements,
) )
from devplacepy.utils.rewards import LEVEL_XP
from devplacepy.responses import respond, action_result, wants_json from devplacepy.responses import respond, action_result, wants_json
from devplacepy.schemas import ProfileOut from devplacepy.schemas import ProfileOut
from devplacepy.avatar import avatar_url, avatar_seed from devplacepy.avatar import avatar_url, avatar_seed
@@ -139,6 +140,10 @@ async def profile_page(
current_user["uid"], f"/profile/{profile_user['username']}" current_user["uid"], f"/profile/{profile_user['username']}"
) )
profile_user["stars"] = get_user_stars(profile_user["uid"]) profile_user["stars"] = get_user_stars(profile_user["uid"])
xp_raw = profile_user.get("xp") or 0
level_raw = profile_user.get("level") or 1
xp_progress_pct = xp_raw % LEVEL_XP
xp_next_level = level_raw * LEVEL_XP
rank = get_user_rank(profile_user["uid"]) rank = get_user_rank(profile_user["uid"])
follow_counts = get_follow_counts(profile_user["uid"]) follow_counts = get_follow_counts(profile_user["uid"])
@@ -440,6 +445,8 @@ async def profile_page(
"awards_count": awards_count, "awards_count": awards_count,
"prominent_award": prominent_award, "prominent_award": prominent_award,
"can_give_award": can_give, "can_give_award": can_give,
"xp_next_level": xp_next_level,
"xp_progress_pct": xp_progress_pct,
}, },
model=ProfileOut, model=ProfileOut,
) )
@@ -499,3 +506,7 @@ async def regenerate_api_key(request: Request):
links=[audit.target("user", user["uid"], user["username"])], links=[audit.target("user", user["uid"], user["username"])],
) )
return JSONResponse({"api_key": new_key}) return JSONResponse({"api_key": new_key})
+3
View File
@@ -17,6 +17,8 @@ class UserOut(_Out):
website: Optional[str] = None website: Optional[str] = None
level: Optional[int] = None level: Optional[int] = None
xp: Optional[int] = None xp: Optional[int] = None
xp_progress_pct: Optional[int] = None
xp_next_level: Optional[int] = None
stars: Optional[int] = None stars: Optional[int] = None
created_at: Optional[str] = None created_at: Optional[str] = None
last_seen: Optional[str] = None last_seen: Optional[str] = None
@@ -202,3 +204,4 @@ class MessageOut(_Out):
CommentItemOut.model_rebuild() CommentItemOut.model_rebuild()
+3
View File
@@ -72,6 +72,8 @@ class ProfileOut(_Out):
followers_count: Optional[int] = None followers_count: Optional[int] = None
following_count: Optional[int] = None following_count: Optional[int] = None
viewer_is_admin: bool = False viewer_is_admin: bool = False
xp_next_level: int = 0
xp_progress_pct: int = 0
media: list[MediaItemOut] = [] media: list[MediaItemOut] = []
media_pagination: Optional[Any] = None media_pagination: Optional[Any] = None
notification_prefs: list[Any] = [] notification_prefs: list[Any] = []
@@ -88,3 +90,4 @@ class TelegramPairOut(_Out):
code: Optional[str] = None code: Optional[str] = None
expires_at: Optional[str] = None expires_at: Optional[str] = None
ttl_minutes: Optional[int] = None ttl_minutes: Optional[int] = None
+5 -2
View File
@@ -42,10 +42,10 @@
<div class="profile-level-bar"> <div class="profile-level-bar">
<div class="level-label"> <div class="level-label">
<span>Progress to next level</span> <span>Progress to next level</span>
<span>{{ (profile_user.get('xp') or 0) % 100 }}%</span> <span>{{ xp_progress_pct }}%</span>
</div> </div>
<div class="bar"> <div class="bar">
<div class="bar-fill" style="--bar-pct: {{ (profile_user.get('xp') or 0) % 100 }}%;"></div> <div class="bar-fill" style="--bar-pct: {{ xp_progress_pct }}%;"></div>
</div> </div>
</div> </div>
@@ -765,3 +765,6 @@ import { AwardGiver } from "{{ static_url('/static/js/AwardGiver.js') }}";
new AwardGiver(); new AwardGiver();
</script> </script>
{% endblock %} {% endblock %}