#!/usr/bin/env python3 """ Value bets route for API v1. GET /api/v1/valuebets — Value bets du jour (premium+) """ from datetime import datetime from flask import Blueprint, jsonify, request from api_v1.utils import ( get_db, table_exists, internal_error, get_pagination_params, paginate_query, ) from auth import jwt_required_middleware, plan_required valuebets_bp = Blueprint("v1_valuebets", __name__, url_prefix="/api/v1") @valuebets_bp.route("/valuebets", methods=["GET"]) @jwt_required_middleware @plan_required("premium", "pro") def valuebets(): """ Value bets du jour --- tags: - Value Bets summary: Value bets du jour — chevaux à cote surévaluée par le marché (premium+) security: - Bearer: [] parameters: - name: date in: query type: string format: date description: Date YYYY-MM-DD (défaut aujourd'hui) - name: min_odds in: query type: number default: 2.0 description: Cote minimale pour filtrer les value bets - name: limit in: query type: integer default: 20 - name: offset in: query type: integer default: 0 responses: 200: description: Value bets du jour avec météo et terrain (HRT-83) 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=20, max_limit=100) try: min_odds = float(request.args.get("min_odds", 2.0)) except (ValueError, TypeError): min_odds = 2.0 conn = get_db() try: rows_raw = [] total = 0 if table_exists(conn, "ml_predictions_cache"): count_row = conn.execute( """SELECT COUNT(*) as cnt FROM ml_predictions_cache WHERE date = ? AND is_value_bet = 1 AND odds >= ?""", (date_param, min_odds), ).fetchone() total = count_row["cnt"] if count_row else 0 # LEFT JOIN pmu_courses (terrain) + pmu_meteo (météo) — HRT-83 has_courses = table_exists(conn, "pmu_courses") has_meteo = table_exists(conn, "pmu_meteo") if has_courses and has_meteo: rows_raw = conn.execute( """SELECT m.race_label, m.hippodrome, m.discipline, m.distance, m.heure, m.horse_name, m.horse_number, m.odds, m.prob_top1, m.prob_top3, m.ml_score, m.recommendation, m.risque_label, m.risque_score, c.penetrometre_intitule, mt.nebulositecode, mt.nebulosite_court, mt.temperature, mt.force_vent FROM ml_predictions_cache m LEFT JOIN pmu_courses c ON c.date_programme = m.date AND c.num_reunion = m.num_reunion AND c.num_course = m.num_course LEFT JOIN pmu_meteo mt ON mt.date_programme = m.date AND mt.num_reunion = m.num_reunion WHERE m.date = ? AND m.is_value_bet = 1 AND m.odds >= ? ORDER BY m.ml_score DESC LIMIT ? OFFSET ?""", (date_param, min_odds, limit, offset), ).fetchall() else: rows_raw = conn.execute( """SELECT race_label, hippodrome, discipline, distance, heure, horse_name, horse_number, odds, prob_top1, prob_top3, ml_score, recommendation, risque_label, risque_score FROM ml_predictions_cache WHERE date = ? AND is_value_bet = 1 AND odds >= ? ORDER BY ml_score DESC LIMIT ? OFFSET ?""", (date_param, min_odds, limit, offset), ).fetchall() from scoring_v2 import get_terrain_condition, compute_weather_impact valuebets_list = [] for r in rows_raw: row_dict = dict(r) penetrometre = row_dict.pop("penetrometre_intitule", None) or "" terrain_condition = ( get_terrain_condition(penetrometre) if penetrometre else "inconnu" ) weather_data = None if ( row_dict.get("nebulositecode") is not None or row_dict.get("temperature") is not None ): weather_data = { "nebulositecode": row_dict.pop("nebulositecode", None), "nebulosite_court": row_dict.pop("nebulosite_court", None), "temperature": row_dict.pop("temperature", None), "force_vent": row_dict.pop("force_vent", None), } else: row_dict.pop("nebulositecode", None) row_dict.pop("nebulosite_court", None) row_dict.pop("temperature", None) row_dict.pop("force_vent", None) weather_impact = compute_weather_impact(weather_data, terrain_condition) row_dict["terrain_condition"] = terrain_condition row_dict["weather_impact"] = weather_impact valuebets_list.append(row_dict) pagination = paginate_query(valuebets_list, total, limit, offset) return jsonify( { "status": "ok", "date": date_param, "min_odds": min_odds, "valuebets": valuebets_list, **pagination, } ), 200 except Exception as e: return internal_error(str(e)) finally: conn.close()