Files
turf_saas/api_v1/routes/export.py
DevOps Engineer b8ef1ed35d feat: Sprint 3-4 — Refacto API /v1/ (HRT-29)
- Blueprint Flask api_v1 avec prefix /api/v1/
- GET /api/v1/health — healthcheck public
- GET /api/v1/courses/today — courses du jour (paginé, filtré)
- GET /api/v1/courses/{id}/predictions — prédictions ML pour une course
- GET /api/v1/predictions/top3 — top 3 global (free tier)
- GET /api/v1/predictions/all — toutes prédictions (premium+)
- GET /api/v1/valuebets — value bets du jour (premium+)
- GET /api/v1/backtest — résultats backtest historiques (pro)
- GET /api/v1/export/csv — export CSV prédictions/paris (pro)
- GET /api/v1/metrics — métriques perf ML (premium+)
- Swagger/OpenAPI via flasgger à /api/v1/docs
- Erreurs uniformes {status, message, code}
- Pagination limit/offset sur toutes les listes
- 42 tests d'intégration passants

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 18:00:54 +02:00

186 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""
Export route for API v1.
GET /api/v1/export/csv — Export CSV des prédictions ou paris (pro)
"""
import csv
import io
from datetime import datetime, timedelta
from flask import Blueprint, jsonify, request, Response
from api_v1.utils import (
get_db,
table_exists,
internal_error,
bad_request,
forbidden,
)
from auth import jwt_required_middleware, plan_required
export_bp = Blueprint("v1_export", __name__, url_prefix="/api/v1/export")
# Maximum rows exportable in one request
EXPORT_MAX_ROWS = 5000
@export_bp.route("/csv", methods=["GET"])
@jwt_required_middleware
@plan_required("pro")
def export_csv():
"""
Export CSV
---
tags:
- Export
summary: Export CSV des prédictions ML ou des paris historiques — accès pro uniquement
security:
- Bearer: []
parameters:
- name: type
in: query
type: string
enum: [predictions, bets]
default: predictions
description: Type de données à exporter
- name: start
in: query
type: string
format: date
description: Date de début (YYYY-MM-DD)
- name: end
in: query
type: string
format: date
description: Date de fin (YYYY-MM-DD)
- name: date
in: query
type: string
format: date
description: Date unique (YYYY-MM-DD), ignoré si start/end fournis
responses:
200:
description: Fichier CSV
content:
text/csv:
schema:
type: string
400:
description: Paramètre invalide
401:
description: Token invalide
403:
description: Plan insuffisant (pro requis)
"""
export_type = request.args.get("type", "predictions").lower()
if export_type not in ("predictions", "bets"):
return bad_request(
"Paramètre 'type' invalide. Valeurs acceptées: predictions, bets"
)
start = request.args.get("start")
end = request.args.get("end")
date = request.args.get("date", datetime.now().strftime("%Y-%m-%d"))
for label, val in [("start", start), ("end", end), ("date", date)]:
if val:
try:
datetime.strptime(val, "%Y-%m-%d")
except ValueError:
return bad_request(
f"Paramètre '{label}' invalide, format attendu: YYYY-MM-DD"
)
# Build date range
if start and end:
date_cond = "date BETWEEN ? AND ?"
date_params = [start, end]
elif start:
date_cond = "date >= ?"
date_params = [start]
else:
date_cond = "date = ?"
date_params = [date]
conn = get_db()
try:
output = io.StringIO()
if export_type == "predictions":
if not table_exists(conn, "ml_predictions_cache"):
return bad_request("Table ml_predictions_cache introuvable")
rows = conn.execute(
f"""SELECT date, race_label, hippodrome, discipline, distance, heure,
horse_name, horse_number, odds, prob_top1, prob_top3,
ml_score, recommendation, is_value_bet, risque_label
FROM ml_predictions_cache
WHERE {date_cond}
ORDER BY date DESC, ml_score DESC
LIMIT {EXPORT_MAX_ROWS}""",
date_params,
).fetchall()
fieldnames = [
"date",
"race_label",
"hippodrome",
"discipline",
"distance",
"heure",
"horse_name",
"horse_number",
"odds",
"prob_top1",
"prob_top3",
"ml_score",
"recommendation",
"is_value_bet",
"risque_label",
]
else: # bets
if not table_exists(conn, "bet_results"):
return bad_request("Table bet_results introuvable")
rows = conn.execute(
f"""SELECT date, race_name, type_pari, horse_name, horse_number,
COALESCE(cote, 0) AS cote, mise, resultat, gain
FROM bet_results
WHERE {date_cond}
ORDER BY date DESC
LIMIT {EXPORT_MAX_ROWS}""",
date_params,
).fetchall()
fieldnames = [
"date",
"race_name",
"type_pari",
"horse_name",
"horse_number",
"cote",
"mise",
"resultat",
"gain",
]
writer = csv.DictWriter(output, fieldnames=fieldnames, extrasaction="ignore")
writer.writeheader()
for row in rows:
writer.writerow(dict(row))
filename = f"turf_{export_type}_{date_params[0]}.csv"
return Response(
output.getvalue(),
status=200,
mimetype="text/csv",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
except Exception as e:
return internal_error(str(e))
finally:
conn.close()