""" 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, } )