chartbastan/backend/app/api/v1/user_predictions.py
2026-02-01 09:31:38 +01:00

242 lines
6.9 KiB
Python

"""
User Prediction API Routes.
This module provides REST endpoints for tracking user predictions
and retrieving user statistics.
"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from app.database import get_db
from app.services.user_prediction_service import UserPredictionService
from app.schemas.user_prediction import (
UserPredictionResponse,
UserPredictionListResponse,
UserStatsResponse
)
router = APIRouter(prefix="/api/v1/user-predictions", tags=["user-predictions"])
@router.post("", status_code=status.HTTP_201_CREATED)
def record_prediction_view(
user_id: int,
prediction_id: int,
db: Session = Depends(get_db)
):
"""
Record that a user viewed a prediction.
This endpoint tracks when a user views a prediction for ROI and accuracy calculations.
Duplicate views are ignored (unique constraint on user_id + prediction_id).
Args:
user_id: ID of the user
prediction_id: ID of the prediction viewed
db: Database session (injected)
Returns:
Created user prediction record
Raises:
404: If user or prediction doesn't exist
422: If validation fails
Example Request:
POST /api/v1/user-predictions?user_id=1&prediction_id=5
Example Response:
{
"id": 1,
"user_id": 1,
"prediction_id": 5,
"viewed_at": "2026-01-18T10:00:00Z",
"was_correct": null
}
"""
try:
service = UserPredictionService(db)
user_prediction = service.record_prediction_view(user_id, prediction_id)
return user_prediction.to_dict()
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(e)
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to record prediction view: {str(e)}"
)
@router.get("/history/{user_id}", response_model=UserPredictionListResponse)
def get_prediction_history(
user_id: int,
limit: int = Query(50, ge=1, le=100, description="Maximum number of records to return (max 100)"),
offset: int = Query(0, ge=0, description="Number of records to skip"),
db: Session = Depends(get_db)
):
"""
Get a user's prediction viewing history.
This endpoint retrieves all predictions a user has viewed, sorted by most recent.
Includes full prediction and match details.
Args:
user_id: ID of the user
limit: Maximum number of records to return (1-100, default: 50)
offset: Number of records to skip (default: 0)
db: Database session (injected)
Returns:
Paginated list of user predictions with full details
Example Request:
GET /api/v1/user-predictions/history/1?limit=10&offset=0
Example Response:
{
"data": [
{
"id": 5,
"user_id": 1,
"prediction_id": 10,
"viewed_at": "2026-01-18T10:00:00Z",
"was_correct": true,
"prediction": {
"id": 10,
"match_id": 3,
"energy_score": "high",
"confidence": "75.0%",
"predicted_winner": "PSG",
"created_at": "2026-01-17T12:00:00Z"
},
"match": {
"id": 3,
"home_team": "PSG",
"away_team": "Marseille",
"date": "2026-01-18T20:00:00Z",
"league": "Ligue 1",
"status": "completed",
"actual_winner": "PSG"
}
}
],
"meta": {
"total": 25,
"limit": 10,
"offset": 0,
"timestamp": "2026-01-18T15:30:00Z"
}
}
"""
from datetime import datetime
service = UserPredictionService(db)
predictions, total = service.get_user_prediction_history(
user_id=user_id,
limit=limit,
offset=offset
)
return {
"data": predictions,
"meta": {
"total": total,
"limit": limit,
"offset": offset,
"timestamp": datetime.utcnow().isoformat() + "Z"
}
}
@router.get("/stats/{user_id}", response_model=UserStatsResponse)
def get_user_statistics(
user_id: int,
db: Session = Depends(get_db)
):
"""
Get user statistics including accuracy and ROI.
This endpoint calculates:
- Total predictions viewed
- Number of correct/incorrect predictions
- Accuracy rate percentage
- ROI (Return on Investment) in EUR
Args:
user_id: ID of the user
db: Database session (injected)
Returns:
User statistics
Raises:
404: If user doesn't exist
Example Request:
GET /api/v1/user-predictions/stats/1
Example Response:
{
"total_predictions_viewed": 25,
"correct_predictions": 18,
"incorrect_predictions": 5,
"accuracy_rate": 78.3,
"roi": 1550.0
}
"""
service = UserPredictionService(db)
stats = service.get_user_stats(user_id)
return UserStatsResponse(**stats)
@router.put("/result/{prediction_id}")
def update_prediction_result(
prediction_id: int,
actual_winner: Optional[str] = Query(None, description="Actual winner: home, away, draw, or null"),
db: Session = Depends(get_db)
):
"""
Update prediction result when match completes.
This endpoint is called when a match finishes to update all user
predictions that referenced this prediction.
Args:
prediction_id: ID of the prediction
actual_winner: Actual winner of the match (home/away/draw or null)
db: Database session (injected)
Returns:
Success message
Example Request:
PUT /api/v1/user-predictions/result/10?actual_winner=home
Example Response:
{
"message": "Prediction results updated successfully",
"prediction_id": 10,
"actual_winner": "home"
}
"""
try:
service = UserPredictionService(db)
service.update_prediction_result(prediction_id, actual_winner)
return {
"message": "Prediction results updated successfully",
"prediction_id": prediction_id,
"actual_winner": actual_winner
}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to update prediction result: {str(e)}"
)