190 lines
6.0 KiB
Python
190 lines
6.0 KiB
Python
"""
|
|
Public API endpoints for predictions.
|
|
|
|
This module provides public endpoints for retrieving predictions without authentication.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
from fastapi import APIRouter, Query, Depends
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database import get_db
|
|
from app.services.prediction_service import PredictionService
|
|
from app.schemas.public import (
|
|
PublicPredictionResponse,
|
|
SuccessResponse,
|
|
SuccessMeta
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/public/v1", tags=["public-predictions"])
|
|
|
|
|
|
@router.get("/predictions", response_model=SuccessResponse)
|
|
def get_public_predictions(
|
|
limit: int = Query(20, ge=1, le=100, description="Maximum number of predictions to return (max 100)"),
|
|
offset: int = Query(0, ge=0, description="Number of predictions to skip"),
|
|
league: Optional[str] = Query(None, description="Filter by league name (case-insensitive)"),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Get public predictions with pagination and filters.
|
|
|
|
This endpoint provides publicly accessible predictions without authentication.
|
|
Data is limited to non-sensitive information only.
|
|
|
|
Args:
|
|
limit: Maximum number of predictions to return (1-100, default: 20)
|
|
offset: Number of predictions to skip (default: 0)
|
|
league: Optional filter by league name (case-insensitive partial match)
|
|
db: Database session (injected)
|
|
|
|
Returns:
|
|
Paginated list of public predictions with match details
|
|
|
|
Example Requests:
|
|
GET /api/public/v1/predictions
|
|
GET /api/public/v1/predictions?limit=10&offset=0
|
|
GET /api/public/v1/predictions?league=Ligue%201
|
|
|
|
Example Response:
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 1,
|
|
"match_id": 1,
|
|
"match": {
|
|
"id": 1,
|
|
"home_team": "PSG",
|
|
"away_team": "Olympique de Marseille",
|
|
"date": "2026-01-18T20:00:00Z",
|
|
"league": "Ligue 1",
|
|
"status": "scheduled"
|
|
},
|
|
"energy_score": "high",
|
|
"confidence": "65.0%",
|
|
"predicted_winner": "PSG",
|
|
"created_at": "2026-01-17T12:00:00Z"
|
|
}
|
|
],
|
|
"meta": {
|
|
"total": 45,
|
|
"limit": 20,
|
|
"offset": 0,
|
|
"timestamp": "2026-01-17T14:30:00Z",
|
|
"version": "v1"
|
|
}
|
|
}
|
|
"""
|
|
service = PredictionService(db)
|
|
predictions, total = service.get_predictions_with_pagination(
|
|
limit=limit,
|
|
offset=offset,
|
|
team_id=None, # No team filter for public API
|
|
league=league,
|
|
date_min=None, # No date filter for public API
|
|
date_max=None
|
|
)
|
|
|
|
# Build response with match details
|
|
prediction_responses = []
|
|
for prediction in predictions:
|
|
match = prediction.match
|
|
prediction_responses.append({
|
|
"id": prediction.id,
|
|
"match_id": prediction.match_id,
|
|
"match": {
|
|
"id": match.id,
|
|
"home_team": match.home_team,
|
|
"away_team": match.away_team,
|
|
"date": match.date.isoformat() if match.date else None,
|
|
"league": match.league,
|
|
"status": match.status
|
|
},
|
|
"energy_score": prediction.energy_score,
|
|
"confidence": prediction.confidence,
|
|
"predicted_winner": prediction.predicted_winner,
|
|
"created_at": prediction.created_at.isoformat() if prediction.created_at else None
|
|
})
|
|
|
|
return {
|
|
"data": prediction_responses,
|
|
"meta": {
|
|
"total": total,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
"version": "v1"
|
|
}
|
|
}
|
|
|
|
|
|
@router.get("/predictions/{prediction_id}", response_model=PublicPredictionResponse)
|
|
def get_public_prediction(
|
|
prediction_id: int,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Get a specific public prediction by ID.
|
|
|
|
This endpoint provides a publicly accessible prediction without authentication.
|
|
|
|
Args:
|
|
prediction_id: ID of the prediction
|
|
db: Database session (injected)
|
|
|
|
Returns:
|
|
Public prediction with match details
|
|
|
|
Raises:
|
|
404: If prediction doesn't exist
|
|
|
|
Example Request:
|
|
GET /api/public/v1/predictions/1
|
|
|
|
Example Response:
|
|
{
|
|
"id": 1,
|
|
"match_id": 1,
|
|
"match": {
|
|
"id": 1,
|
|
"home_team": "PSG",
|
|
"away_team": "Olympique de Marseille",
|
|
"date": "2026-01-18T20:00:00Z",
|
|
"league": "Ligue 1",
|
|
"status": "scheduled"
|
|
},
|
|
"energy_score": "high",
|
|
"confidence": "65.0%",
|
|
"predicted_winner": "PSG",
|
|
"created_at": "2026-01-17T12:00:00Z"
|
|
}
|
|
"""
|
|
service = PredictionService(db)
|
|
prediction = service.get_prediction_by_id(prediction_id)
|
|
|
|
if not prediction:
|
|
from fastapi import HTTPException, status
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Prediction with id {prediction_id} not found"
|
|
)
|
|
|
|
match = prediction.match
|
|
return {
|
|
"id": prediction.id,
|
|
"match_id": prediction.match_id,
|
|
"match": {
|
|
"id": match.id,
|
|
"home_team": match.home_team,
|
|
"away_team": match.away_team,
|
|
"date": match.date.isoformat() if match.date else None,
|
|
"league": match.league,
|
|
"status": match.status
|
|
},
|
|
"energy_score": prediction.energy_score,
|
|
"confidence": prediction.confidence,
|
|
"predicted_winner": prediction.predicted_winner,
|
|
"created_at": prediction.created_at.isoformat() if prediction.created_at else None
|
|
}
|