""" 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({'error': '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, })