drop leftovers from the live leaderboard: no fake model rows, no second sort

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 07:27:45 +03:30
parent ca6b2ff4c9
commit 81348cee5a
2 changed files with 11 additions and 21 deletions

View File

@@ -77,14 +77,8 @@ class Command(BaseCommand):
from progress.models import LeaderboardEntry from progress.models import LeaderboardEntry
days = opts['days'] days = opts['days']
scores = self._collect(period, days, include_zero=opts['include_zero']) # مرتب‌سازی و کنار گذاشتنِ XPی صفر را خودِ compute_scores انجام می‌دهد.
if not opts['include_zero']: scores = self._collect(period, days, include_zero=opts['include_zero'])[:opts['limit']]
scores = [(uid, xp) for uid, xp in scores if xp > 0]
# ترتیبِ قطعی: امتیازِ بیشتر جلوتر، و در تساوی user_id کوچک‌تر جلوتر —
# وگرنه دو اجرای پشت‌سرهم روی داده‌ی یکسان رتبه‌های متفاوت می‌دهند.
scores.sort(key=lambda p: (-p[1], p[0]))
scores = scores[:opts['limit']]
label = period if period == 'all_time' else f'{period} ({days} روزِ اخیر)' label = period if period == 'all_time' else f'{period} ({days} روزِ اخیر)'
if opts['dry_run']: if opts['dry_run']:

View File

@@ -7,7 +7,7 @@ from rest_framework.response import Response
from rest_framework.throttling import ScopedRateThrottle from rest_framework.throttling import ScopedRateThrottle
from rest_framework.views import APIView from rest_framework.views import APIView
from .models import Character, LeaderboardEntry, StreakRecord, UserHearts, UserStats from .models import Character, StreakRecord, UserHearts, UserStats
from .services import effective_current_streak from .services import effective_current_streak
@@ -129,26 +129,22 @@ class LeaderboardView(APIView):
scores = cache.get_or_set( scores = cache.get_or_set(
f'leaderboard:{period}', lambda: compute_scores(period), LEADERBOARD_CACHE_TTL, f'leaderboard:{period}', lambda: compute_scores(period), LEADERBOARD_CACHE_TTL,
) )
entries = [
LeaderboardEntry(user_id=uid, period=period, xp_in_period=xp, rank=rank)
for rank, (uid, xp) in enumerate(scores, 1)
]
# نام کاربر از accounts.User خونده می‌شه (فقط برای نمایش، بدون FK واقعی). # نام کاربر از accounts.User خونده می‌شه (فقط برای نمایش، بدون FK واقعی).
from accounts.models import User as AccountUser from accounts.models import User as AccountUser
user_ids = [e.user_id for e in entries] names = dict(
names = {u.id: u.name for u in AccountUser.objects.filter(id__in=user_ids)} AccountUser.objects.filter(id__in=[uid for uid, _ in scores]).values_list('id', 'name')
)
data = { data = {
'period': period, 'period': period,
'entries': [ 'entries': [
{ {
'rank': e.rank, 'rank': rank,
'userId': str(e.user_id), 'userId': str(uid),
'name': names.get(e.user_id, ''), 'name': names.get(uid, ''),
'xpInPeriod': e.xp_in_period, 'xpInPeriod': xp,
} }
for e in entries for rank, (uid, xp) in enumerate(scores, 1)
], ],
} }
return Response(LeaderboardResponseSerializer(data).data) return Response(LeaderboardResponseSerializer(data).data)