mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-01-01 22:07:03 -05:00
159 lines
4.7 KiB
Python
159 lines
4.7 KiB
Python
"""
|
|
Leaderboard views for user rankings
|
|
"""
|
|
|
|
from datetime import timedelta
|
|
|
|
from django.db.models import Count, Sum
|
|
from django.db.models.functions import Coalesce
|
|
from django.utils import timezone
|
|
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework.permissions import AllowAny
|
|
from rest_framework.response import Response
|
|
|
|
from apps.moderation.models import EditSubmission
|
|
from apps.reviews.models import Review
|
|
from apps.rides.models import RideCredit
|
|
|
|
|
|
@api_view(["GET"])
|
|
@permission_classes([AllowAny])
|
|
def leaderboard(request):
|
|
"""
|
|
Get user leaderboard data.
|
|
|
|
Query params:
|
|
- category: 'credits' | 'reviews' | 'contributions' (default: credits)
|
|
- period: 'all' | 'monthly' | 'weekly' (default: all)
|
|
- limit: int (default: 25, max: 100)
|
|
"""
|
|
category = request.query_params.get("category", "credits")
|
|
period = request.query_params.get("period", "all")
|
|
limit = min(int(request.query_params.get("limit", 25)), 100)
|
|
|
|
# Calculate date filter based on period
|
|
date_filter = None
|
|
if period == "weekly":
|
|
date_filter = timezone.now() - timedelta(days=7)
|
|
elif period == "monthly":
|
|
date_filter = timezone.now() - timedelta(days=30)
|
|
|
|
if category == "credits":
|
|
return _get_credits_leaderboard(date_filter, limit)
|
|
elif category == "reviews":
|
|
return _get_reviews_leaderboard(date_filter, limit)
|
|
elif category == "contributions":
|
|
return _get_contributions_leaderboard(date_filter, limit)
|
|
else:
|
|
return Response({"detail": "Invalid category"}, status=400)
|
|
|
|
|
|
def _get_credits_leaderboard(date_filter, limit):
|
|
"""Top users by total ride credits."""
|
|
queryset = RideCredit.objects.all()
|
|
|
|
if date_filter:
|
|
queryset = queryset.filter(created_at__gte=date_filter)
|
|
|
|
# Aggregate credits per user
|
|
users_data = (
|
|
queryset.values("user_id", "user__username", "user__display_name")
|
|
.annotate(
|
|
total_credits=Coalesce(Sum("count"), 0),
|
|
unique_rides=Count("ride", distinct=True),
|
|
)
|
|
.order_by("-total_credits")[:limit]
|
|
)
|
|
|
|
results = []
|
|
for rank, entry in enumerate(users_data, 1):
|
|
results.append(
|
|
{
|
|
"rank": rank,
|
|
"user_id": entry["user_id"],
|
|
"username": entry["user__username"],
|
|
"display_name": entry["user__display_name"] or entry["user__username"],
|
|
"total_credits": entry["total_credits"],
|
|
"unique_rides": entry["unique_rides"],
|
|
}
|
|
)
|
|
|
|
return Response(
|
|
{
|
|
"category": "credits",
|
|
"results": results,
|
|
}
|
|
)
|
|
|
|
|
|
def _get_reviews_leaderboard(date_filter, limit):
|
|
"""Top users by review count."""
|
|
queryset = Review.objects.all()
|
|
|
|
if date_filter:
|
|
queryset = queryset.filter(created_at__gte=date_filter)
|
|
|
|
# Count reviews per user
|
|
users_data = (
|
|
queryset.values("user_id", "user__username", "user__display_name")
|
|
.annotate(
|
|
review_count=Count("id"),
|
|
)
|
|
.order_by("-review_count")[:limit]
|
|
)
|
|
|
|
results = []
|
|
for rank, entry in enumerate(users_data, 1):
|
|
results.append(
|
|
{
|
|
"rank": rank,
|
|
"user_id": entry["user_id"],
|
|
"username": entry["user__username"],
|
|
"display_name": entry["user__display_name"] or entry["user__username"],
|
|
"review_count": entry["review_count"],
|
|
}
|
|
)
|
|
|
|
return Response(
|
|
{
|
|
"category": "reviews",
|
|
"results": results,
|
|
}
|
|
)
|
|
|
|
|
|
def _get_contributions_leaderboard(date_filter, limit):
|
|
"""Top users by approved contributions."""
|
|
queryset = EditSubmission.objects.filter(status="approved")
|
|
|
|
if date_filter:
|
|
queryset = queryset.filter(created_at__gte=date_filter)
|
|
|
|
# Count contributions per user
|
|
users_data = (
|
|
queryset.values("submitted_by_id", "submitted_by__username", "submitted_by__display_name")
|
|
.annotate(
|
|
contribution_count=Count("id"),
|
|
)
|
|
.order_by("-contribution_count")[:limit]
|
|
)
|
|
|
|
results = []
|
|
for rank, entry in enumerate(users_data, 1):
|
|
results.append(
|
|
{
|
|
"rank": rank,
|
|
"user_id": entry["submitted_by_id"],
|
|
"username": entry["submitted_by__username"],
|
|
"display_name": entry["submitted_by__display_name"] or entry["submitted_by__username"],
|
|
"contribution_count": entry["contribution_count"],
|
|
}
|
|
)
|
|
|
|
return Response(
|
|
{
|
|
"category": "contributions",
|
|
"results": results,
|
|
}
|
|
)
|