#!/usr/bin/env python3 """ Predictions routes for API v1. GET /api/v1/predictions/top3 — Top 3 global du jour (free tier, 1/day limit) GET /api/v1/predictions/all — Toutes prédictions (premium+) """ from datetime import datetime, timedelta from flask import Blueprint, jsonify, request from api_v1.utils import ( get_db, table_exists, internal_error, not_found, get_pagination_params, paginate_query, ) from auth import jwt_required_middleware, plan_required, free_daily_limit_check predictions_bp = Blueprint("v1_predictions", __name__, url_prefix="/api/v1/predictions") def _fetch_ml_predictions(conn, date: str, limit: int = None, offset: int = 0): """Shared helper — returns rows from ml_predictions_cache.""" if not table_exists(conn, "ml_predictions_cache"): return [], 0 count_row = conn.execute( "SELECT COUNT(*) as cnt FROM ml_predictions_cache WHERE date = ?", (date,), ).fetchone() total = count_row["cnt"] if count_row else 0 sql = """SELECT race_label, hippodrome, discipline, distance, heure, horse_name, horse_number, odds, prob_top1, prob_top3, ml_score, recommendation, is_value_bet, risque_label, risque_score FROM ml_predictions_cache WHERE date = ? ORDER BY ml_score DESC""" params = [date] if limit is not None: sql += " LIMIT ? OFFSET ?" params += [limit, offset] rows = conn.execute(sql, params).fetchall() return [dict(r) for r in rows], total # ────────────────────────────────────────────────────────────── # GET /api/v1/predictions/top3 # ────────────────────────────────────────────────────────────── @predictions_bp.route("/top3", methods=["GET"]) @jwt_required_middleware @free_daily_limit_check def predictions_top3(): """ Top 3 prédictions du jour --- tags: - Prédictions summary: Top 3 chevaux avec le meilleur score ML du jour (free tier inclus) security: - Bearer: [] parameters: - name: date in: query type: string format: date description: Date au format YYYY-MM-DD (défaut aujourd'hui) responses: 200: description: Top 3 prédictions ML du jour 401: description: Token invalide 429: description: Limite quotidienne free tier atteinte """ date_param = request.args.get("date", datetime.now().strftime("%Y-%m-%d")) conn = get_db() try: predictions, _ = _fetch_ml_predictions(conn, date_param, limit=3, offset=0) return jsonify( { "status": "ok", "date": date_param, "top3": predictions, } ), 200 except Exception as e: return internal_error(str(e)) finally: conn.close() # ────────────────────────────────────────────────────────────── # GET /api/v1/predictions/all # ────────────────────────────────────────────────────────────── @predictions_bp.route("/all", methods=["GET"]) @jwt_required_middleware @plan_required("premium", "pro") def predictions_all(): """ Toutes les prédictions du jour --- tags: - Prédictions summary: Toutes les prédictions ML du jour — accès premium et pro uniquement security: - Bearer: [] parameters: - name: date in: query type: string format: date description: Date au format YYYY-MM-DD (défaut aujourd'hui) - name: limit in: query type: integer default: 20 - name: offset in: query type: integer default: 0 responses: 200: description: Toutes les prédictions ML 401: description: Token invalide 403: description: Plan insuffisant (premium ou pro requis) """ date_param = request.args.get("date", datetime.now().strftime("%Y-%m-%d")) limit, offset = get_pagination_params(default_limit=50, max_limit=500) conn = get_db() try: predictions, total = _fetch_ml_predictions( conn, date_param, limit=limit, offset=offset ) pagination = paginate_query(predictions, total, limit, offset) return jsonify( { "status": "ok", "date": date_param, "predictions": predictions, **pagination, } ), 200 except Exception as e: return internal_error(str(e)) finally: conn.close()