daniel-saed's picture
Upload 21 files
c2aaace verified
# ===========================
# SISTEMA DE PREDICCIÓN DE CORNERS - OPTIMIZADO PARA APUESTAS (VERSIÓN COMPLETA)
# ===========================
import numpy as np
import pandas as pd
import os
from fastapi.responses import JSONResponse
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security.api_key import APIKeyHeader
from fastapi import Security
from fastapi.responses import JSONResponse
from dotenv import load_dotenv
from src.api.load import USE_MODEL
#from load import USE_MODEL
load_dotenv()
model = USE_MODEL()
app = FastAPI()
# ===========================
# CONFIGURACIÓN API KEY
# ===========================
API_KEY = os.getenv("API_KEY") # ⚠️ CÁMBIALA POR UNA SEGURA
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
async def get_api_key(api_key: str = Security(api_key_header)):
"""Validar API Key"""
if api_key != API_KEY:
raise HTTPException(
status_code=401,
detail="API Key inválida o faltante"
)
return api_key
# ===========================
# HELPER: CONVERTIR NUMPY/PANDAS A TIPOS NATIVOS
# ===========================
def convert_to_native(val):
"""Convierte tipos NumPy/Pandas a tipos nativos de Python"""
if isinstance(val, (np.integer, np.int64, np.int32, np.int16, np.int8)):
return int(val)
elif isinstance(val, (np.floating, np.float64, np.float32, np.float16)):
return float(val)
elif isinstance(val, np.ndarray):
return [convert_to_native(item) for item in val.tolist()]
elif isinstance(val, dict):
return {key: convert_to_native(value) for key, value in val.items()}
elif isinstance(val, (list, tuple)):
return [convert_to_native(item) for item in val]
elif isinstance(val, pd.Series):
return convert_to_native(val.to_dict())
elif isinstance(val, pd.DataFrame):
return convert_to_native(val.to_dict(orient='records'))
elif pd.isna(val):
return None
else:
return val
# ===========================
# ENDPOINTS
# ===========================
@app.get("/")
def read_root():
"""Endpoint raíz con información de la API"""
return {
"api": "Corners Prediction API",
"version": "1.0.0",
"status": "active",
"endpoints": {
"/": "Información de la API",
"/items/": "Predicción de corners (requiere API Key)",
"/health": "Estado de salud"
},
"auth": "Requiere header: X-API-Key"
}
@app.get("/items/")
def predict_corners(
local: str,
visitante: str,
jornada: int,
league_code: str,
temporada: str = "2526",
api_key: str = Depends(get_api_key) # ✅ PROTEGIDO
):
"""
Predecir corners para un partido de fútbol
Args:
local: Nombre del equipo local (requerido)
visitante: Nombre del equipo visitante (requerido)
jornada: Número de jornada (requerido, min: 1)
league_code: Código de liga (requerido: ESP, GER, FRA, ITA, ENG, NED, POR, BEL)
temporada: Temporada en formato AABB (default: "2526")
Returns:
JSON con predicción y análisis completo
Example:
GET /items/?local=Barcelona&visitante=Real%20Madrid&jornada=15&league_code=ESP&temporada=2526
Headers: X-API-Key: tu-clave-secreta-aqui
"""
# ===========================
# VALIDACIONES
# ===========================
# Validar campos obligatorios
if not local or not visitante:
raise HTTPException(
status_code=400,
detail="Los parámetros 'local' y 'visitante' son obligatorios"
)
# Validar jornada
if jornada < 1:
raise HTTPException(
status_code=400,
detail="La jornada debe ser mayor o igual a 1"
)
# Validar liga
valid_leagues = ["ESP", "GER", "FRA", "ITA", "ENG", "NED", "POR", "BEL"]
if league_code not in valid_leagues:
raise HTTPException(
status_code=400,
detail=f"Liga inválida. Ligas válidas: {', '.join(valid_leagues)}"
)
# ===========================
# PREDICCIÓN
# ===========================
try:
resultado = model.consume_model_single(
local=local,
visitante=visitante,
jornada=jornada,
temporada=temporada,
league_code=league_code
)
# Verificar si hubo error en la predicción
if resultado.get("error"):
raise HTTPException(
status_code=422,
detail=f"Error en predicción: {resultado['error']}"
)
# ✅ CONVERTIR TIPOS NUMPY A NATIVOS
resultado_limpio = convert_to_native(resultado)
# Agregar metadata
resultado_limpio["metadata"] = {
"api_version": "1.0.0",
"model_version": "v4",
"timestamp": pd.Timestamp.now().isoformat()
}
return JSONResponse(
status_code=200,
content=resultado_limpio
)
except HTTPException:
# Re-lanzar excepciones HTTP
raise
except Exception as e:
# Capturar cualquier otro error
import traceback
error_detail = {
"error": str(e),
"type": type(e).__name__,
"traceback": traceback.format_exc() if app.debug else None
}
return JSONResponse(
status_code=500,
content=error_detail
)