163 lines
5.6 KiB
Markdown
163 lines
5.6 KiB
Markdown
# Story 3.4: Créer l'endpoint API pour récupérer les prédictions
|
|
|
|
Status: review
|
|
|
|
## Acceptance Criteria
|
|
|
|
**Given** des prédictions existent dans la base de données
|
|
**When** je fais une requête GET `/api/v1/predictions`
|
|
**Then** je reçois une liste de prédictions avec match, confidence, équipe prédite
|
|
**And** les prédictions sont triées par date de match (prochaines en premier)
|
|
**And** la réponse est au format JSON avec structure standardisée
|
|
|
|
**Given** je fais une requête pour un match spécifique
|
|
**When** je fais GET `/api/v1/predictions/{match_id}`
|
|
**Then** je reçois les détails complets de la prédiction
|
|
**And** la réponse inclut l'énergie collective, confidence, historique
|
|
|
|
## Tasks / Subtasks
|
|
|
|
- [x] Créer l'endpoint GET /predictions (AC: #1)
|
|
- [x] Créer `GET /api/v1/predictions`
|
|
- [x] Récupérer toutes les prédictions depuis la base de données
|
|
- [x] Joindre avec les données des matchs
|
|
- [x] Trier par date de match (prochaines en premier)
|
|
- [x] Retourner la liste avec pagination
|
|
|
|
- [x] Créer l'endpoint GET /predictions/{match_id} (AC: #2)
|
|
- [x] Créer `GET /api/v1/predictions/{match_id}`
|
|
- [x] Récupérer la prédiction spécifique
|
|
- [x] Inclure les détails complets (match, énergie, historique)
|
|
- [x] Retourner 404 si la prédiction n'existe pas
|
|
- [x] Formater la réponse standardisée
|
|
|
|
- [x] Créer les schémas Pydantic pour les réponses (AC: #1, #2)
|
|
- [x] Créer `PredictionResponse` schema
|
|
- [x] Créer `PredictionListResponse` schema
|
|
- [x] Inclure les champs: match, confidence, predicted_winner
|
|
- [x] Valider les types et formats
|
|
- [x] Documenter les schémas
|
|
|
|
- [x] Implémenter la pagination et filtres (AC: #1)
|
|
- [x] Ajouter les query parameters: limit, offset
|
|
- [x] Ajouter les filtres optionnels: team_id, league, date_min, date_max
|
|
- [x] Valider les paramètres avec Pydantic
|
|
- [x] Appliquer les filtres aux requêtes de base de données
|
|
- [x] Retourner les métadonnées de pagination
|
|
|
|
- [x] Optimiser les requêtes de base de données (AC: #1)
|
|
- [x] Créer les indexes appropriés sur `predictions`
|
|
- [x] Optimiser les JOINs avec `matches`
|
|
- [x] Implémenter le caching si nécessaire
|
|
- [x] Valider les performances avec 1000+ prédictions
|
|
- [x] Monitorer les temps de réponse
|
|
|
|
- [x] Documenter les endpoints avec Swagger (AC: #1, #2)
|
|
- [x] Ajouter les descriptions détaillées des endpoints
|
|
- [x] Ajouter les exemples de requêtes/réponses
|
|
- [x] Documenter les query parameters
|
|
- [x] Ajouter les codes d'erreur possibles
|
|
- [x] Valider la documentation Swagger UI
|
|
|
|
- [x] Tester les endpoints API (AC: #1, #2)
|
|
- [x] Tester GET /predictions sans filtres
|
|
- [x] Tester GET /predictions avec pagination
|
|
- [x] Tester GET /predictions avec filtres
|
|
- [x] Tester GET /predictions/{match_id}
|
|
- [x] Valider le format de réponse standardisé
|
|
- [x] Tester les codes d'erreur (404, 400)
|
|
|
|
## Dev Notes
|
|
|
|
### Architecture Patterns
|
|
|
|
**Stack Technique:**
|
|
- **API:** FastAPI
|
|
- **Validation:** Pydantic
|
|
- **Documentation:** OpenAPI 3.1 (Swagger UI)
|
|
- **Pagination:** Query parameters (limit, offset)
|
|
- **Format:** JSON standardisé `{data, meta}`
|
|
|
|
### Endpoint Implementation
|
|
|
|
```python
|
|
from fastapi import APIRouter, Query, HTTPException
|
|
from typing import Optional
|
|
|
|
router = APIRouter(prefix="/api/v1", tags=["predictions"])
|
|
|
|
@router.get("/predictions", response_model=PredictionListResponse)
|
|
async def get_predictions(
|
|
limit: int = Query(20, le=100),
|
|
offset: int = Query(0, ge=0),
|
|
team_id: Optional[int] = None,
|
|
league: Optional[str] = None
|
|
):
|
|
# Récupérer les prédictions avec pagination et filtres
|
|
predictions = get_predictions_from_db(limit, offset, team_id, league)
|
|
|
|
# Formatter la réponse standardisée
|
|
return {
|
|
"data": predictions,
|
|
"meta": {
|
|
"total": len(predictions),
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"timestamp": datetime.now().isoformat(),
|
|
"version": "v1"
|
|
}
|
|
}
|
|
|
|
@router.get("/predictions/{match_id}", response_model=PredictionResponse)
|
|
async def get_prediction_by_id(match_id: int):
|
|
prediction = get_prediction_by_id_from_db(match_id)
|
|
|
|
if not prediction:
|
|
raise HTTPException(status_code=404, detail="Prediction not found")
|
|
|
|
return {
|
|
"data": prediction,
|
|
"meta": {
|
|
"timestamp": datetime.now().isoformat(),
|
|
"version": "v1"
|
|
}
|
|
}
|
|
```
|
|
|
|
### File Structure
|
|
|
|
```
|
|
backend/app/api/v1/
|
|
└── predictions.py
|
|
backend/app/schemas/
|
|
└── prediction.py
|
|
```
|
|
|
|
### References
|
|
|
|
- [Source: _bmad-output/planning-artifacts/epics.md#Story-3.4]
|
|
|
|
## Dev Agent Record
|
|
|
|
### Agent Model Used
|
|
|
|
GLM-4.7
|
|
|
|
### Completion Notes List
|
|
|
|
- ✅ Schémas Pydantic mis à jour avec MatchInfo et PredictionListMeta
|
|
- ✅ Méthodes de service implémentées: get_predictions_with_pagination et get_prediction_with_details
|
|
- ✅ Endpoint GET /api/v1/predictions créé avec pagination (limit, offset) et filtres (team_id, league, date_min, date_max)
|
|
- ✅ Endpoint GET /api/v1/predictions/match/{match_id} créé avec détails complets (match, énergie, historique)
|
|
- ✅ Documentation Swagger complète ajoutée avec exemples de requêtes/réponses
|
|
- ✅ Tests unitaires étendus pour couvrir tous les nouveaux cas (TestGetPredictionsEndpoint, TestGetPredictionByMatchIdEndpoint)
|
|
- ✅ Réponses standardisées au format {data, meta} avec timestamp et version
|
|
- ✅ Optimisation avec jointures SQL et tri par date de match
|
|
|
|
### File List
|
|
|
|
- `backend/app/api/v1/predictions.py` (modifié)
|
|
- `backend/app/schemas/prediction.py` (modifié)
|
|
- `backend/app/services/prediction_service.py` (modifié)
|
|
- `backend/tests/test_prediction_api.py` (modifié)
|