#!/usr/bin/env python3 """ SaaS API v1 Blueprint — /api/v1/* Stats, prédictions, résumés pour le dashboard SaaS. Sprint 4-5 — HRT-30 """ from flask import Blueprint, request, jsonify, Response import sqlite3 import csv import io import os from datetime import datetime from saas_auth import require_auth DB_PATH = os.environ.get("TURF_SAAS_DB", "/home/h3r7/turf_saas/turf_saas.db") api_v1_bp = Blueprint("api_v1", __name__, url_prefix="/api/v1") def get_db(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def plan_allows(user_plan, required): order = {"free": 0, "premium": 1, "pro": 2} return order.get(user_plan, 0) >= order.get(required, 0) @api_v1_bp.route("/stats/summary", methods=["GET"]) @require_auth def stats_summary(): today = datetime.now().strftime("%Y-%m-%d") conn = get_db() try: courses_today = conn.execute( "SELECT COUNT(DISTINCT num_reunion||'-'||num_course) FROM ml_predictions_cache WHERE date=?", (today,) ).fetchone()[0] or 0 value_bets_today = conn.execute( "SELECT COUNT(*) FROM ml_predictions_cache WHERE date=? AND is_value_bet=1", (today,) ).fetchone()[0] or 0 acc_row = conn.execute(""" SELECT CAST(SUM(CASE WHEN p.ordre_arrivee BETWEEN 1 AND 3 AND m.recommendation='top3' THEN 1 ELSE 0 END) AS FLOAT) / NULLIF(COUNT(CASE WHEN m.recommendation='top3' THEN 1 END), 0) * 100 AS acc FROM ml_predictions_cache m JOIN pmu_partants p ON m.horse_name=p.nom AND m.date=p.date_programme WHERE m.date >= date('now', '-30 days') """).fetchone() accuracy_top3 = round(acc_row[0], 1) if acc_row and acc_row[0] else None next_race = conn.execute( "SELECT heure, hippodrome FROM ml_predictions_cache WHERE date=? AND heure IS NOT NULL ORDER BY heure LIMIT 1", (today,) ).fetchone() conn.close() return jsonify({ "courses_today": courses_today, "value_bets_today": value_bets_today, "accuracy_top3": accuracy_top3, "next_race_time": next_race["heure"] if next_race else None, "next_race_hippodrome": next_race["hippodrome"] if next_race else None, }), 200 except Exception as e: conn.close() return jsonify({"error": str(e), "courses_today": 0, "value_bets_today": 0}), 200 @api_v1_bp.route("/predictions/today", methods=["GET"]) @require_auth def predictions_today(): user = request.current_user plan = user.get("plan", "free") today = datetime.now().strftime("%Y-%m-%d") conn = get_db() try: rows = conn.execute(""" SELECT horse_name, horse_number, odds, prob_top1, prob_top3, ml_score, recommendation, is_value_bet, is_outlier, race_label, race_name, hippodrome, discipline, distance, heure, risque_label, risque_score, num_reunion, num_course FROM ml_predictions_cache WHERE date=? ORDER BY num_reunion, num_course, ml_score DESC """, (today,)).fetchall() conn.close() predictions = [dict(r) for r in rows] if plan == "free" and predictions: first = predictions[0] first_key = (first["num_reunion"], first["num_course"]) predictions = [p for p in predictions if (p["num_reunion"], p["num_course"]) == first_key] for p in predictions: p["is_value_bet"] = 0 return jsonify({"date": today, "plan": plan, "count": len(predictions), "predictions": predictions}), 200 except Exception as e: conn.close() return jsonify({"error": str(e), "predictions": []}), 200 @api_v1_bp.route("/value-bets/today", methods=["GET"]) @require_auth def value_bets_today(): user = request.current_user plan = user.get("plan", "free") if not plan_allows(plan, "premium"): return jsonify({"error": "Cette fonctionnalité requiert un plan Premium ou Pro.", "upgrade_required": True}), 403 today = datetime.now().strftime("%Y-%m-%d") conn = get_db() rows = conn.execute(""" SELECT horse_name, race_label, race_name, hippodrome, odds, prob_top3, ml_score, risque_label, heure FROM ml_predictions_cache WHERE date=? AND is_value_bet=1 ORDER BY ml_score DESC """, (today,)).fetchall() conn.close() return jsonify({"value_bets": [dict(r) for r in rows], "count": len(rows)}), 200 @api_v1_bp.route("/export/csv", methods=["GET"]) @require_auth def export_csv(): user = request.current_user plan = user.get("plan", "free") if not plan_allows(plan, "pro"): return jsonify({"error": "L'export CSV requiert un plan Pro.", "upgrade_required": True}), 403 date_param = request.args.get("date", datetime.now().strftime("%Y-%m-%d")) conn = get_db() rows = conn.execute( "SELECT * FROM ml_predictions_cache WHERE date=? ORDER BY num_reunion, num_course, ml_score DESC", (date_param,) ).fetchall() conn.close() output = io.StringIO() if rows: writer = csv.DictWriter(output, fieldnames=rows[0].keys()) writer.writeheader() writer.writerows([dict(r) for r in rows]) return Response( output.getvalue(), mimetype="text/csv", headers={"Content-Disposition": f"attachment; filename=turf_ia_{date_param}.csv"} )