{"openapi":"3.1.0","info":{"title":"Chartbastan API","description":"\n API publique pour les prédictions de matchs basées sur l'énergie collective.\n\n ## Endpoints Publics\n - **Prédictions**: Accès aux prédictions de matchs avec filtres\n - **Matchs**: Liste des matchs avec filtres par ligue et statut\n\n ## Authentification\n Les endpoints publics ne nécessitent pas d'authentification.\n Pour un usage intensif, générez une clé API via le dashboard développeur.\n ","version":"1.0.0"},"paths":{"/":{"get":{"summary":"Read Root","description":"Endpoint racine de l'API.","operationId":"read_root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health Check","description":"Endpoint de vérification de santé.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/users/":{"post":{"tags":["users"],"summary":"Create User","description":"Créer un nouvel utilisateur.\n\nArgs:\n user: Données utilisateur à créer.\n db: Session de base de données.\n \nReturns:\n UserResponse: L'utilisateur créé.","operationId":"create_user_api_v1_users__post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["users"],"summary":"Read Users","description":"Récupérer la liste des utilisateurs.\n\nArgs:\n skip: Nombre d'éléments à sauter.\n limit: Nombre maximum d'éléments à retourner.\n db: Session de base de données.\n \nReturns:\n List[UserResponse]: Liste des utilisateurs.","operationId":"read_users_api_v1_users__get","parameters":[{"name":"skip","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserResponse"},"title":"Response Read Users Api V1 Users Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}":{"get":{"tags":["users"],"summary":"Read User","description":"Récupérer un utilisateur par son ID.\n\nArgs:\n user_id: ID de l'utilisateur.\n db: Session de base de données.\n \nReturns:\n UserResponse: L'utilisateur trouvé.","operationId":"read_user_api_v1_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/predictions":{"get":{"tags":["predictions"],"summary":"Get Predictions","description":"Get all predictions with pagination and filters.\n\nThis endpoint retrieves predictions joined with match data, applies optional filters,\nand returns paginated results sorted by match date (upcoming matches first).\n\nArgs:\n limit: Maximum number of predictions to return (1-100, default: 20)\n offset: Number of predictions to skip (default: 0)\n team_id: Optional filter by team ID (matches where team is home or away)\n league: Optional filter by league name (case-insensitive partial match)\n date_min: Optional filter for matches after this date\n date_max: Optional filter for matches before this date\n db: Database session (injected)\n\nReturns:\n Paginated list of predictions with match details and metadata\n\nExample Requests:\n GET /api/v1/predictions\n GET /api/v1/predictions?limit=10&offset=0\n GET /api/v1/predictions?league=Ligue%201\n GET /api/v1/predictions?team_id=1&limit=5\n GET /api/v1/predictions?date_min=2026-01-15T00:00:00Z&date_max=2026-01-20T23:59:59Z\n\nExample Response:\n {\n \"data\": [\n {\n \"id\": 1,\n \"match_id\": 1,\n \"match\": {\n \"id\": 1,\n \"home_team\": \"PSG\",\n \"away_team\": \"Olympique de Marseille\",\n \"date\": \"2026-01-18T20:00:00Z\",\n \"league\": \"Ligue 1\",\n \"status\": \"scheduled\"\n },\n \"energy_score\": \"high\",\n \"confidence\": \"65.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T12:00:00Z\"\n }\n ],\n \"meta\": {\n \"total\": 45,\n \"limit\": 20,\n \"offset\": 0,\n \"timestamp\": \"2026-01-17T14:30:00Z\",\n \"version\": \"v1\"\n }\n }","operationId":"get_predictions_api_v1_predictions_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of predictions to return (max 100)","default":20,"title":"Limit"},"description":"Maximum number of predictions to return (max 100)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of predictions to skip","default":0,"title":"Offset"},"description":"Number of predictions to skip"},{"name":"team_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter by team ID","title":"Team Id"},"description":"Filter by team ID"},{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by league name (case-insensitive)","title":"League"},"description":"Filter by league name (case-insensitive)"},{"name":"date_min","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Filter for matches after this date (ISO 8601)","title":"Date Min"},"description":"Filter for matches after this date (ISO 8601)"},{"name":"date_max","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Filter for matches before this date (ISO 8601)","title":"Date Max"},"description":"Filter for matches before this date (ISO 8601)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/predictions/match/{match_id}":{"get":{"tags":["predictions"],"summary":"Get Prediction By Match Id","description":"Get prediction details for a specific match.\n\nThis endpoint retrieves the latest prediction for a match and includes\nfull match details, energy score information, and historical predictions.\n\nArgs:\n match_id: ID of the match\n db: Database session (injected)\n\nReturns:\n Prediction with full details including match info and history\n\nRaises:\n 404: If match or prediction not found\n\nExample Request:\n GET /api/v1/predictions/match/1\n\nExample Response:\n {\n \"id\": 3,\n \"match_id\": 1,\n \"match\": {\n \"id\": 1,\n \"home_team\": \"PSG\",\n \"away_team\": \"Olympique de Marseille\",\n \"date\": \"2026-01-18T20:00:00Z\",\n \"league\": \"Ligue 1\",\n \"status\": \"scheduled\",\n \"actual_winner\": null\n },\n \"energy_score\": \"high\",\n \"confidence\": \"70.5%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T14:00:00Z\",\n \"history\": [\n {\n \"id\": 3,\n \"energy_score\": \"high\",\n \"confidence\": \"70.5%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T14:00:00Z\"\n },\n {\n \"id\": 2,\n \"energy_score\": \"medium\",\n \"confidence\": \"60.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T12:00:00Z\"\n }\n ]\n }","operationId":"get_prediction_by_match_id_api_v1_predictions_match__match_id__get","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"integer","title":"Match Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/predictions/matches/{match_id}/predict":{"post":{"tags":["predictions"],"summary":"Create Prediction","description":"Create a prediction for a specific match.\n\nThis endpoint calculates a prediction based on energy scores for both teams\nand stores it in the database.\n\nArgs:\n match_id: ID of the match to predict\n home_energy: Energy score of the home team (0.0+)\n away_energy: Energy score of the away team (0.0+)\n energy_score_label: Optional label for energy score (e.g., \"high\", \"medium\", \"low\")\n db: Database session (injected)\n\nReturns:\n Created prediction object\n\nRaises:\n 404: If match doesn't exist\n 400: If energy scores are invalid\n 422: If validation fails\n\nExample Request:\n POST /api/v1/predictions/matches/1/predict?home_energy=65.0&away_energy=45.0\n\nExample Response:\n {\n \"id\": 1,\n \"match_id\": 1,\n \"energy_score\": \"high\",\n \"confidence\": \"40.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T12:00:00Z\"\n }","operationId":"create_prediction_api_v1_predictions_matches__match_id__predict_post","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"integer","title":"Match Id"}},{"name":"home_energy","in":"query","required":true,"schema":{"type":"number","title":"Home Energy"}},{"name":"away_energy","in":"query","required":true,"schema":{"type":"number","title":"Away Energy"}},{"name":"energy_score_label","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Energy Score Label"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/predictions/{prediction_id}":{"get":{"tags":["predictions"],"summary":"Get Prediction","description":"Get a prediction by its ID.\n\nArgs:\n prediction_id: ID of the prediction to retrieve\n db: Database session (injected)\n\nReturns:\n Prediction object\n\nRaises:\n 404: If prediction doesn't exist\n\nExample Request:\n GET /api/v1/predictions/1\n\nExample Response:\n {\n \"id\": 1,\n \"match_id\": 1,\n \"energy_score\": \"high\",\n \"confidence\": \"40.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T12:00:00Z\"\n }","operationId":"get_prediction_api_v1_predictions__prediction_id__get","parameters":[{"name":"prediction_id","in":"path","required":true,"schema":{"type":"integer","title":"Prediction Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["predictions"],"summary":"Delete Prediction","description":"Delete a prediction by its ID.\n\nArgs:\n prediction_id: ID of the prediction to delete\n db: Database session (injected)\n\nReturns:\n No content (204)\n\nRaises:\n 404: If prediction doesn't exist\n\nExample Request:\n DELETE /api/v1/predictions/1\n\nExample Response:\n (HTTP 204 No Content)","operationId":"delete_prediction_api_v1_predictions__prediction_id__delete","parameters":[{"name":"prediction_id","in":"path","required":true,"schema":{"type":"integer","title":"Prediction Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/predictions/matches/{match_id}":{"get":{"tags":["predictions"],"summary":"Get Predictions For Match","description":"Get all predictions for a specific match.\n\nArgs:\n match_id: ID of the match\n db: Database session (injected)\n\nReturns:\n List of predictions for the match\n\nExample Request:\n GET /api/v1/predictions/matches/1\n\nExample Response:\n {\n \"data\": [\n {\n \"id\": 1,\n \"match_id\": 1,\n \"energy_score\": \"high\",\n \"confidence\": \"40.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T12:00:00Z\"\n }\n ],\n \"count\": 1,\n \"meta\": {}\n }","operationId":"get_predictions_for_match_api_v1_predictions_matches__match_id__get","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"integer","title":"Match Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/predictions/matches/{match_id}/latest":{"get":{"tags":["predictions"],"summary":"Get Latest Prediction For Match","description":"Get the most recent prediction for a specific match.\n\nArgs:\n match_id: ID of the match\n db: Database session (injected)\n\nReturns:\n Latest prediction object\n\nRaises:\n 404: If no predictions exist for the match\n\nExample Request:\n GET /api/v1/predictions/matches/1/latest\n\nExample Response:\n {\n \"id\": 3,\n \"match_id\": 1,\n \"energy_score\": \"high\",\n \"confidence\": \"45.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T14:00:00Z\"\n }","operationId":"get_latest_prediction_for_match_api_v1_predictions_matches__match_id__latest_get","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"integer","title":"Match Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backtesting/run":{"post":{"tags":["backtesting"],"summary":"Run Backtesting","description":"Run backtesting on historical matches.\n\nAnalyzes historical match predictions against actual results to calculate\naccuracy metrics and validate prediction system performance.\n\n- **leagues**: Optional list of leagues to filter by (e.g., ['Ligue 1', 'Premier League'])\n- **start_date**: Optional start date for filtering matches (ISO 8601 format)\n- **end_date**: Optional end date for filtering matches (ISO 8601 format)\n- **export_format**: Optional export format ('json', 'csv', 'html')\n\nReturns:\n Comprehensive backtesting report including:\n - Total matches analyzed\n - Correct/incorrect predictions\n - Overall accuracy percentage\n - Validation status (VALIDATED, REVISION_REQUIRED, BELOW_TARGET)\n - Detailed results per match\n - Metrics breakdown by league\n\nExample:\n ```json\n {\n \"leagues\": [\"Ligue 1\", \"Premier League\"],\n \"start_date\": \"2025-01-01T00:00:00Z\",\n \"end_date\": \"2025-12-31T23:59:59Z\",\n \"export_format\": \"html\"\n }\n ```\n\nRaises:\n 400: If no matches found with specified filters\n 500: If internal server error occurs","operationId":"run_backtesting_backtesting_run_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BacktestingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BacktestingResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BacktestingErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backtesting/status":{"get":{"tags":["backtesting"],"summary":"Get Backtesting Status","description":"Get backtesting service status.\n\nReturns information about the backtesting system configuration\nand validation thresholds.\n\nReturns:\n Status information including:\n - Available leagues\n - Validation thresholds\n - Service health status","operationId":"get_backtesting_status_backtesting_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/leaderboard":{"get":{"tags":["leaderboard"],"summary":"Get Leaderboard","description":"Get leaderboard with top 100 users sorted by accuracy.\n\nThis endpoint retrieves the top users based on their prediction accuracy\nand includes personal rank data if user_id is provided.\n\nRanking criteria:\n- Primary: Accuracy percentage (higher is better)\n- Secondary: Number of predictions viewed (more is better, used as tie-breaker)\n\nArgs:\n limit: Maximum number of users to return (1-100, default: 100)\n user_id: Optional current user ID to include personal rank data\n db: Database session (injected)\n\nReturns:\n Leaderboard with top users and optional personal rank data\n\nRaises:\n 404: If user_id provided but user doesn't exist\n\nExample Request:\n GET /api/v1/leaderboard\n GET /api/v1/leaderboard?user_id=1\n\nExample Response:\n {\n \"data\": [\n {\n \"user_id\": 1,\n \"username\": \"JohnDoe\",\n \"accuracy\": 95.5,\n \"predictions_count\": 100\n },\n {\n \"user_id\": 2,\n \"username\": \"JaneSmith\",\n \"accuracy\": 90.0,\n \"predictions_count\": 85\n }\n ],\n \"personal_data\": {\n \"rank\": 42,\n \"accuracy\": 75.5,\n \"predictions_count\": 25\n },\n \"meta\": {\n \"total\": 2,\n \"limit\": 100,\n \"timestamp\": \"2026-01-18T10:30:00Z\",\n \"version\": \"v1\"\n }\n }","operationId":"get_leaderboard_api_v1_leaderboard_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of users to return (max 100)","default":100,"title":"Limit"},"description":"Maximum number of users to return (max 100)"},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Current user ID to include personal rank data","title":"User Id"},"description":"Current user ID to include personal rank data"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaderboardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/leaderboard/personal/{user_id}":{"get":{"tags":["leaderboard"],"summary":"Get Personal Rank","description":"Get personal rank data for a specific user.\n\nArgs:\n user_id: ID of the user\n db: Database session (injected)\n\nReturns:\n Personal rank data with rank, accuracy, and predictions count\n\nRaises:\n 404: If user doesn't exist or has no completed predictions\n\nExample Request:\n GET /api/v1/leaderboard/personal/1\n\nExample Response:\n {\n \"rank\": 42,\n \"accuracy\": 75.5,\n \"predictions_count\": 25\n }","operationId":"get_personal_rank_api_v1_leaderboard_personal__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonalRankData"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/badges/":{"get":{"tags":["badges"],"summary":"Get All Badges","description":"Récupère tous les badges disponibles\n- Indique les badges débloqués par l'utilisateur si user_id est fourni\n- Retourne les critères de débloquage","operationId":"get_all_badges_api_v1_badges__get","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadgeListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/badges/check":{"post":{"tags":["badges"],"summary":"Check Badges","description":"Vérifie et débloque les nouveaux badges pour un utilisateur\n- Vérifie les critères de badges de l'utilisateur\n- Débloque les nouveaux badges atteints\n- Retourne les badges débloqués\n- Envoie les notifications","operationId":"check_badges_api_v1_badges_check_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadgeUnlockRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadgeCheckResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/badges/users/{user_id}":{"get":{"tags":["badges"],"summary":"Get User Badges","description":"Récupère tous les badges débloqués par un utilisateur","operationId":"get_user_badges_api_v1_badges_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserBadgeListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/public/v1/predictions":{"get":{"tags":["public-predictions"],"summary":"Get Public Predictions","description":"Get public predictions with pagination and filters.\n\nThis endpoint provides publicly accessible predictions without authentication.\nData is limited to non-sensitive information only.\n\nArgs:\n limit: Maximum number of predictions to return (1-100, default: 20)\n offset: Number of predictions to skip (default: 0)\n league: Optional filter by league name (case-insensitive partial match)\n db: Database session (injected)\n\nReturns:\n Paginated list of public predictions with match details\n\nExample Requests:\n GET /api/public/v1/predictions\n GET /api/public/v1/predictions?limit=10&offset=0\n GET /api/public/v1/predictions?league=Ligue%201\n\nExample Response:\n {\n \"data\": [\n {\n \"id\": 1,\n \"match_id\": 1,\n \"match\": {\n \"id\": 1,\n \"home_team\": \"PSG\",\n \"away_team\": \"Olympique de Marseille\",\n \"date\": \"2026-01-18T20:00:00Z\",\n \"league\": \"Ligue 1\",\n \"status\": \"scheduled\"\n },\n \"energy_score\": \"high\",\n \"confidence\": \"65.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T12:00:00Z\"\n }\n ],\n \"meta\": {\n \"total\": 45,\n \"limit\": 20,\n \"offset\": 0,\n \"timestamp\": \"2026-01-17T14:30:00Z\",\n \"version\": \"v1\"\n }\n }","operationId":"get_public_predictions_api_public_v1_predictions_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of predictions to return (max 100)","default":20,"title":"Limit"},"description":"Maximum number of predictions to return (max 100)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of predictions to skip","default":0,"title":"Offset"},"description":"Number of predictions to skip"},{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by league name (case-insensitive)","title":"League"},"description":"Filter by league name (case-insensitive)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/public/v1/predictions/{prediction_id}":{"get":{"tags":["public-predictions"],"summary":"Get Public Prediction","description":"Get a specific public prediction by ID.\n\nThis endpoint provides a publicly accessible prediction without authentication.\n\nArgs:\n prediction_id: ID of the prediction\n db: Database session (injected)\n\nReturns:\n Public prediction with match details\n\nRaises:\n 404: If prediction doesn't exist\n\nExample Request:\n GET /api/public/v1/predictions/1\n\nExample Response:\n {\n \"id\": 1,\n \"match_id\": 1,\n \"match\": {\n \"id\": 1,\n \"home_team\": \"PSG\",\n \"away_team\": \"Olympique de Marseille\",\n \"date\": \"2026-01-18T20:00:00Z\",\n \"league\": \"Ligue 1\",\n \"status\": \"scheduled\"\n },\n \"energy_score\": \"high\",\n \"confidence\": \"65.0%\",\n \"predicted_winner\": \"PSG\",\n \"created_at\": \"2026-01-17T12:00:00Z\"\n }","operationId":"get_public_prediction_api_public_v1_predictions__prediction_id__get","parameters":[{"name":"prediction_id","in":"path","required":true,"schema":{"type":"integer","title":"Prediction Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPredictionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/public/v1/matches":{"get":{"tags":["public-matches"],"summary":"Get Public Matches","description":"Get public matches with pagination and filters.\n\nThis endpoint provides publicly accessible matches without authentication.\nData is limited to non-sensitive information only.\n\nArgs:\n limit: Maximum number of matches to return (1-100, default: 20)\n offset: Number of matches to skip (default: 0)\n league: Optional filter by league name (case-insensitive)\n status: Optional filter by match status (e.g., \"scheduled\", \"completed\", \"ongoing\")\n db: Database session (injected)\n\nReturns:\n Paginated list of public matches\n\nExample Requests:\n GET /api/public/v1/matches\n GET /api/public/v1/matches?limit=10&offset=0\n GET /api/public/v1/matches?league=Ligue%201\n GET /api/public/v1/matches?status=scheduled\n\nExample Response:\n {\n \"data\": [\n {\n \"id\": 1,\n \"home_team\": \"PSG\",\n \"away_team\": \"Olympique de Marseille\",\n \"date\": \"2026-01-18T20:00:00Z\",\n \"league\": \"Ligue 1\",\n \"status\": \"scheduled\"\n }\n ],\n \"meta\": {\n \"total\": 45,\n \"limit\": 20,\n \"offset\": 0,\n \"timestamp\": \"2026-01-17T14:30:00Z\",\n \"version\": \"v1\"\n }\n }","operationId":"get_public_matches_api_public_v1_matches_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of matches to return (max 100)","default":20,"title":"Limit"},"description":"Maximum number of matches to return (max 100)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of matches to skip","default":0,"title":"Offset"},"description":"Number of matches to skip"},{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by league name (case-insensitive)","title":"League"},"description":"Filter by league name (case-insensitive)"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by match status","title":"Status"},"description":"Filter by match status"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/public/v1/matches/{match_id}":{"get":{"tags":["public-matches"],"summary":"Get Public Match","description":"Get a specific public match by ID.\n\nThis endpoint provides a publicly accessible match without authentication.\n\nArgs:\n match_id: ID of match\n db: Database session (injected)\n\nReturns:\n Public match details\n\nRaises:\n 404: If match doesn't exist\n\nExample Request:\n GET /api/public/v1/matches/1\n\nExample Response:\n {\n \"id\": 1,\n \"home_team\": \"PSG\",\n \"away_team\": \"Olympique de Marseille\",\n \"date\": \"2026-01-18T20:00:00Z\",\n \"league\": \"Ligue 1\",\n \"status\": \"scheduled\"\n }","operationId":"get_public_match_api_public_v1_matches__match_id__get","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"integer","title":"Match Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicMatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"BacktestingData":{"properties":{"total_matches":{"type":"integer","title":"Total Matches"},"correct_predictions":{"type":"integer","title":"Correct Predictions"},"incorrect_predictions":{"type":"integer","title":"Incorrect Predictions"},"accuracy":{"type":"number","title":"Accuracy"},"status":{"type":"string","title":"Status"},"results":{"items":{"$ref":"#/components/schemas/MatchResultSchema"},"type":"array","title":"Results"},"metrics_by_league":{"additionalProperties":{"$ref":"#/components/schemas/LeagueMetricsSchema"},"type":"object","title":"Metrics By League"},"timestamp":{"type":"string","title":"Timestamp"},"validation_thresholds":{"$ref":"#/components/schemas/ValidationThresholdsSchema"},"export":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Export"}},"type":"object","required":["total_matches","correct_predictions","incorrect_predictions","accuracy","status","results","metrics_by_league","timestamp","validation_thresholds"],"title":"BacktestingData","description":"Schema for backtesting result data."},"BacktestingErrorResponse":{"properties":{"error":{"$ref":"#/components/schemas/ErrorDetail"},"meta":{"type":"object","title":"Meta"}},"type":"object","required":["error","meta"],"title":"BacktestingErrorResponse","description":"Response schema for backtesting API errors.","example":{"error":{"code":"NO_MATCHES_FOUND","details":{"filters":{"end_date":"2025-12-31T23:59:59Z","leagues":["Ligue 1"],"start_date":"2025-01-01T00:00:00Z"}},"message":"No historical matches found matching the specified filters"},"meta":{"request_id":"req-abc123","timestamp":"2026-01-17T10:30:00Z"}}},"BacktestingRequest":{"properties":{"leagues":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Leagues","description":"List of leagues to filter by (e.g., ['Ligue 1', 'Premier League'])","example":["Ligue 1","Premier League"]},"start_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date","description":"Start date for filtering matches (ISO 8601 format)","example":"2025-01-01T00:00:00Z"},"end_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date","description":"End date for filtering matches (ISO 8601 format)","example":"2025-12-31T23:59:59Z"},"export_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Export Format","description":"Export format for results: 'json', 'csv', or 'html'","example":"html"}},"type":"object","title":"BacktestingRequest","description":"Request schema for running backtesting.","example":{"end_date":"2025-12-31T23:59:59Z","export_format":"html","leagues":["Ligue 1","Premier League"],"start_date":"2025-01-01T00:00:00Z"}},"BacktestingResponse":{"properties":{"data":{"$ref":"#/components/schemas/BacktestingData"},"meta":{"type":"object","title":"Meta"}},"type":"object","required":["data","meta"],"title":"BacktestingResponse","description":"Response schema for backtesting API.","example":{"data":{"accuracy":65.0,"correct_predictions":65,"incorrect_predictions":35,"metrics_by_league":{"Ligue 1":{"accuracy":66.0,"correct":33,"total":50},"Premier League":{"accuracy":64.0,"correct":32,"total":50}},"results":[{"actual_winner":"home","away_energy":45.0,"away_team":"OM","correct":true,"date":"2025-01-15T20:00:00Z","home_energy":65.0,"home_team":"PSG","league":"Ligue 1","match_id":1,"prediction":{"away_energy":45.0,"confidence":40.0,"home_energy":65.0,"predicted_winner":"home"}}],"status":"VALIDATED","timestamp":"2026-01-17T10:30:00Z","total_matches":100,"validation_thresholds":{"alert":55.0,"validated":60.0}},"meta":{"timestamp":"2026-01-17T10:30:00Z","version":"v1"}}},"BadgeCheckResponse":{"properties":{"data":{"type":"object","title":"Data"},"meta":{"type":"object","title":"Meta"}},"type":"object","required":["data","meta"],"title":"BadgeCheckResponse","description":"Réponse après vérification des badges"},"BadgeListResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/BadgeResponse"},"type":"array","title":"Data"},"meta":{"type":"object","title":"Meta"}},"type":"object","required":["data","meta"],"title":"BadgeListResponse","description":"Réponse avec une liste de badges"},"BadgeResponse":{"properties":{"name":{"type":"string","title":"Name","description":"Nom du badge"},"description":{"type":"string","title":"Description","description":"Description du badge"},"icon":{"type":"string","title":"Icon","description":"Icône ou emoji du badge"},"category":{"type":"string","title":"Category","description":"Catégorie du badge (predictions, accuracy, engagement, social)"},"criteriaType":{"type":"string","title":"Criteriatype","description":"Type de critère"},"criteriaValue":{"type":"integer","title":"Criteriavalue","description":"Valeur du critère"},"criteriaDescription":{"type":"string","title":"Criteriadescription","description":"Description du critère"},"rarity":{"type":"string","title":"Rarity","description":"Rareté du badge (common, rare, epic, legendary)"},"points":{"type":"integer","title":"Points","description":"Points attribués pour le badge"},"id":{"type":"integer","title":"Id"},"badgeId":{"type":"string","title":"Badgeid"},"createdAt":{"type":"string","title":"Createdat"},"unlocked":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Unlocked"}},"type":"object","required":["name","description","icon","category","criteriaType","criteriaValue","criteriaDescription","rarity","points","id","badgeId","createdAt"],"title":"BadgeResponse","description":"Réponse avec un badge"},"BadgeUnlockRequest":{"properties":{"userId":{"type":"integer","title":"Userid","description":"ID de l'utilisateur"},"badgeId":{"type":"string","title":"Badgeid","description":"ID du badge à débloquer"}},"type":"object","required":["userId","badgeId"],"title":"BadgeUnlockRequest","description":"Requête pour débloquer un badge"},"ErrorDetail":{"properties":{"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"},"details":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Details"}},"type":"object","required":["code","message"],"title":"ErrorDetail","description":"Schema for error details."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LeaderboardEntry":{"properties":{"user_id":{"type":"integer","title":"User Id"},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"accuracy":{"type":"number","maximum":100.0,"minimum":0.0,"title":"Accuracy","description":"Accuracy percentage (0-100)"},"predictions_count":{"type":"integer","minimum":0.0,"title":"Predictions Count","description":"Total number of predictions viewed"}},"type":"object","required":["user_id","accuracy","predictions_count"],"title":"LeaderboardEntry","description":"Represents a single entry in the leaderboard.","example":{"accuracy":85.5,"predictions_count":42,"user_id":1,"username":"JohnDoe"}},"LeaderboardResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/LeaderboardEntry"},"type":"array","title":"Data"},"personal_data":{"anyOf":[{"$ref":"#/components/schemas/PersonalRankData"},{"type":"null"}]},"meta":{"type":"object","title":"Meta"}},"type":"object","required":["data","meta"],"title":"LeaderboardResponse","description":"Response model for leaderboard endpoint.","example":{"data":[{"accuracy":95.5,"predictions_count":100,"user_id":1,"username":"JohnDoe"},{"accuracy":90.0,"predictions_count":85,"user_id":2,"username":"JaneSmith"}],"meta":{"limit":100,"timestamp":"2026-01-18T10:30:00Z","total":2,"version":"v1"},"personal_data":{"accuracy":75.5,"predictions_count":25,"rank":42}}},"LeagueMetricsSchema":{"properties":{"total":{"type":"integer","title":"Total"},"correct":{"type":"integer","title":"Correct"},"accuracy":{"type":"number","title":"Accuracy"}},"type":"object","required":["total","correct","accuracy"],"title":"LeagueMetricsSchema","description":"Schema for league-specific metrics."},"MatchInfo":{"properties":{"id":{"type":"integer","title":"Id","description":"Match ID"},"home_team":{"type":"string","title":"Home Team","description":"Home team name"},"away_team":{"type":"string","title":"Away Team","description":"Away team name"},"date":{"type":"string","format":"date-time","title":"Date","description":"Match date and time"},"league":{"type":"string","title":"League","description":"League name"},"status":{"type":"string","title":"Status","description":"Match status"}},"type":"object","required":["id","home_team","away_team","date","league","status"],"title":"MatchInfo","description":"Schema for match information included in prediction response."},"MatchResultSchema":{"properties":{"match_id":{"type":"integer","title":"Match Id"},"league":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"League"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"home_team":{"type":"string","title":"Home Team"},"away_team":{"type":"string","title":"Away Team"},"home_energy":{"type":"number","title":"Home Energy"},"away_energy":{"type":"number","title":"Away Energy"},"prediction":{"type":"object","title":"Prediction"},"actual_winner":{"type":"string","title":"Actual Winner"},"correct":{"type":"boolean","title":"Correct"}},"type":"object","required":["match_id","home_team","away_team","home_energy","away_energy","prediction","actual_winner","correct"],"title":"MatchResultSchema","description":"Schema for single match backtesting result."},"PersonalRankData":{"properties":{"rank":{"type":"integer","minimum":1.0,"title":"Rank","description":"User's rank in the leaderboard"},"accuracy":{"type":"number","maximum":100.0,"minimum":0.0,"title":"Accuracy","description":"User's accuracy percentage"},"predictions_count":{"type":"integer","minimum":0.0,"title":"Predictions Count","description":"User's total predictions viewed"}},"type":"object","required":["rank","accuracy","predictions_count"],"title":"PersonalRankData","description":"Represents personal rank data for the current user.","example":{"accuracy":75.5,"predictions_count":25,"rank":42}},"PredictionListMeta":{"properties":{"total":{"type":"integer","title":"Total","description":"Total number of predictions matching filters"},"limit":{"type":"integer","title":"Limit","description":"Number of predictions returned"},"offset":{"type":"integer","title":"Offset","description":"Number of predictions skipped"},"timestamp":{"type":"string","title":"Timestamp","description":"ISO 8601 timestamp of response"},"version":{"type":"string","title":"Version","description":"API version","default":"v1"}},"type":"object","required":["total","limit","offset","timestamp"],"title":"PredictionListMeta","description":"Schema for pagination metadata."},"PredictionListResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/PredictionResponse"},"type":"array","title":"Data","description":"List of predictions"},"meta":{"allOf":[{"$ref":"#/components/schemas/PredictionListMeta"}],"description":"Pagination and metadata"}},"type":"object","required":["data","meta"],"title":"PredictionListResponse","description":"Schema for a list of predictions with standardized metadata."},"PredictionResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Primary key"},"match_id":{"type":"integer","title":"Match Id","description":"Foreign key to matches table"},"match":{"allOf":[{"$ref":"#/components/schemas/MatchInfo"}],"description":"Match details"},"energy_score":{"type":"string","title":"Energy Score","description":"Energy score for the prediction"},"confidence":{"type":"string","title":"Confidence","description":"Confidence level of the prediction"},"predicted_winner":{"type":"string","title":"Predicted Winner","description":"Predicted winner team name"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Timestamp when prediction was created"}},"type":"object","required":["id","match_id","match","energy_score","confidence","predicted_winner","created_at"],"title":"PredictionResponse","description":"Schema for prediction response with match details."},"PublicMatchInfo":{"properties":{"id":{"type":"integer","title":"Id","description":"Match ID"},"home_team":{"type":"string","title":"Home Team","description":"Home team name"},"away_team":{"type":"string","title":"Away Team","description":"Away team name"},"date":{"type":"string","title":"Date","description":"Match date and time (ISO 8601)"},"league":{"type":"string","title":"League","description":"League name"},"status":{"type":"string","title":"Status","description":"Match status"}},"type":"object","required":["id","home_team","away_team","date","league","status"],"title":"PublicMatchInfo","description":"Schema for match information in public API."},"PublicMatchResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Match ID"},"home_team":{"type":"string","title":"Home Team","description":"Home team name"},"away_team":{"type":"string","title":"Away Team","description":"Away team name"},"date":{"type":"string","title":"Date","description":"Match date and time (ISO 8601)"},"league":{"type":"string","title":"League","description":"League name"},"status":{"type":"string","title":"Status","description":"Match status"}},"type":"object","required":["id","home_team","away_team","date","league","status"],"title":"PublicMatchResponse","description":"Schema for match response in public API."},"PublicPredictionResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Prediction ID"},"match_id":{"type":"integer","title":"Match Id","description":"Match ID"},"match":{"allOf":[{"$ref":"#/components/schemas/PublicMatchInfo"}],"description":"Match details"},"energy_score":{"type":"string","title":"Energy Score","description":"Energy score for the prediction"},"confidence":{"type":"string","title":"Confidence","description":"Confidence level of the prediction"},"predicted_winner":{"type":"string","title":"Predicted Winner","description":"Predicted winner team name"},"created_at":{"type":"string","title":"Created At","description":"Timestamp when prediction was created"}},"type":"object","required":["id","match_id","match","energy_score","confidence","predicted_winner","created_at"],"title":"PublicPredictionResponse","description":"Schema for prediction response in public API."},"SuccessMeta":{"properties":{"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total","description":"Total number of items"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","description":"Number of items returned"},"offset":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Offset","description":"Number of items skipped"},"timestamp":{"type":"string","title":"Timestamp","description":"ISO 8601 timestamp of response"},"version":{"type":"string","title":"Version","description":"API version","default":"v1"}},"type":"object","required":["timestamp"],"title":"SuccessMeta","description":"Schema for success response metadata."},"SuccessResponse":{"properties":{"data":{"title":"Data","description":"Response data"},"meta":{"allOf":[{"$ref":"#/components/schemas/SuccessMeta"}],"description":"Response metadata"}},"type":"object","required":["data","meta"],"title":"SuccessResponse","description":"Schema for standardized success response."},"UserBadgeListResponse":{"properties":{"data":{"type":"object","title":"Data"},"meta":{"type":"object","title":"Meta"}},"type":"object","required":["data","meta"],"title":"UserBadgeListResponse","description":"Réponse avec une liste de badges d'utilisateur"},"UserCreate":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["email"],"title":"UserCreate","description":"Schéma pour la création d'utilisateur."},"UserResponse":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"id":{"type":"integer","title":"Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["email","id","created_at","updated_at"],"title":"UserResponse","description":"Schéma pour la réponse utilisateur."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"ValidationThresholdsSchema":{"properties":{"validated":{"type":"number","title":"Validated"},"alert":{"type":"number","title":"Alert"}},"type":"object","required":["validated","alert"],"title":"ValidationThresholdsSchema","description":"Schema for validation thresholds."}}}}