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:
@@ -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']:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user