""" Leaderboard API Routes. This module provides REST endpoints for retrieving user rankings and leaderboards based on prediction accuracy. """ from datetime import datetime from typing import Optional from fastapi import APIRouter, Depends, HTTPException, status, Query from sqlalchemy.orm import Session from app.database import get_db from app.services.leaderboard_service import LeaderboardService from app.schemas.leaderboard import LeaderboardResponse, LeaderboardEntry, PersonalRankData router = APIRouter(prefix="/api/v1/leaderboard", tags=["leaderboard"]) @router.get("", response_model=LeaderboardResponse) def get_leaderboard( limit: int = Query(100, ge=1, le=100, description="Maximum number of users to return (max 100)"), user_id: Optional[int] = Query(None, description="Current user ID to include personal rank data"), db: Session = Depends(get_db) ): """ Get leaderboard with top 100 users sorted by accuracy. This endpoint retrieves the top users based on their prediction accuracy and includes personal rank data if user_id is provided. Ranking criteria: - Primary: Accuracy percentage (higher is better) - Secondary: Number of predictions viewed (more is better, used as tie-breaker) Args: limit: Maximum number of users to return (1-100, default: 100) user_id: Optional current user ID to include personal rank data db: Database session (injected) Returns: Leaderboard with top users and optional personal rank data Raises: 404: If user_id provided but user doesn't exist Example Request: GET /api/v1/leaderboard GET /api/v1/leaderboard?user_id=1 Example Response: { "data": [ { "user_id": 1, "username": "JohnDoe", "accuracy": 95.5, "predictions_count": 100 }, { "user_id": 2, "username": "JaneSmith", "accuracy": 90.0, "predictions_count": 85 } ], "personal_data": { "rank": 42, "accuracy": 75.5, "predictions_count": 25 }, "meta": { "total": 2, "limit": 100, "timestamp": "2026-01-18T10:30:00Z", "version": "v1" } } """ service = LeaderboardService(db) leaderboard = service.get_leaderboard(limit=limit) # Get personal rank if user_id provided personal_data = None if user_id: personal_rank = service.get_personal_rank(user_id) if personal_rank: personal_data = PersonalRankData(**personal_rank) # Format leaderboard entries entries = [LeaderboardEntry(**entry) for entry in leaderboard] return { "data": entries, "personal_data": personal_data, "meta": { "total": len(entries), "limit": limit, "timestamp": datetime.utcnow().isoformat() + "Z", "version": "v1" } } @router.get("/personal/{user_id}", response_model=PersonalRankData) def get_personal_rank( user_id: int, db: Session = Depends(get_db) ): """ Get personal rank data for a specific user. Args: user_id: ID of the user db: Database session (injected) Returns: Personal rank data with rank, accuracy, and predictions count Raises: 404: If user doesn't exist or has no completed predictions Example Request: GET /api/v1/leaderboard/personal/1 Example Response: { "rank": 42, "accuracy": 75.5, "predictions_count": 25 } """ service = LeaderboardService(db) personal_rank = service.get_personal_rank(user_id) if not personal_rank: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"User {user_id} not found or has no completed predictions" ) return PersonalRankData(**personal_rank)