""" Leaderboard views for user rankings """ from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response from django.db.models import Count, Sum from django.db.models.functions import Coalesce from django.utils import timezone from datetime import timedelta from apps.accounts.models import User from apps.rides.models import RideCredit from apps.reviews.models import Review from apps.moderation.models import EditSubmission @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, })