"""Routes S4 — Rapport de réunion.""" from datetime import datetime, timezone from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, desc, extract from app.core.database import get_db from app.core.security import get_current_membre from app.models.reunion import Reunion from app.models.groupe import Groupe from app.models.membre import Membre from app.models.affectation import Affectation from app.models.poste import Poste from app.schemas.phase1 import ReunionCreate, ReunionOut router = APIRouter(prefix="/reunions", tags=["S4 — Rapport de réunion"]) JOURS_SEMAINE = { "lundi": 0, "mardi": 1, "mercredi": 2, "jeudi": 3, "vendredi": 4, "samedi": 5, "dimanche": 6, } async def require_animateur(membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db)): """Vérifie que le membre est animateur ou exécutif.""" result = await db.execute( select(Affectation).join(Poste).where( Affectation.membre_id == membre.id, Affectation.statut == "actif", (Poste.nom.ilike("%animateur%") | (Poste.categorie == "executif")), ) ) if not result.scalars().first(): raise HTTPException(status_code=403, detail="Réservé à l'animateur ou à l'exécutif") return membre @router.post("/", response_model=ReunionOut) async def creer_rapport_reunion( req: ReunionCreate, membre=Depends(require_animateur), db: AsyncSession = Depends(get_db), ): """S4 — L'animateur note le thème et les annonces.""" groupe = await db.get(Groupe, membre.groupe_id) # ── Validation : réunion régulière ── if req.type == "reguliere" and groupe.jour_reunion: jour_attendu = JOURS_SEMAINE.get(groupe.jour_reunion.lower()) jour_soumis = req.date_reunion.weekday() if jour_attendu is not None and jour_soumis != jour_attendu: raise HTTPException( status_code=400, detail=f"La réunion régulière doit avoir lieu le {groupe.jour_reunion}", ) # Une seule régulière par semaine ISO iso_year, iso_week, _ = req.date_reunion.isocalendar() result = await db.execute( select(Reunion).where( Reunion.groupe_id == membre.groupe_id, Reunion.type == "reguliere", extract("isoyear", Reunion.date_reunion) == iso_year, extract("week", Reunion.date_reunion) == iso_week, ) ) if result.scalars().first(): raise HTTPException( status_code=400, detail=f"Il y a déjà une réunion régulière pour la semaine {iso_week}", ) reunion = Reunion( groupe_id=membre.groupe_id, type=req.type, date_reunion=req.date_reunion, animateur_id=membre.id, theme=req.theme, annonces=req.annonces, note=req.note, ) db.add(reunion) await db.flush() return ReunionOut( id=reunion.id, type=reunion.type, date_reunion=reunion.date_reunion, animateur_prenom=membre.prenom, theme=reunion.theme, annonces=reunion.annonces, note=reunion.note, cree_le=reunion.cree_le, ) @router.get("/", response_model=list[ReunionOut]) async def liste_reunions( limite: int = 20, membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db), ): """Lister les réunions récentes du groupe.""" result = await db.execute( select(Reunion) .where(Reunion.groupe_id == membre.groupe_id) .order_by(desc(Reunion.date_reunion)) .limit(limite) ) reunions = result.scalars().all() out = [] for r in reunions: anim = await db.get(Membre, r.animateur_id) if r.animateur_id else None out.append(ReunionOut( id=r.id, type=r.type, date_reunion=r.date_reunion, animateur_prenom=anim.prenom if anim else None, theme=r.theme, annonces=r.annonces, note=r.note, cree_le=r.cree_le, )) return out @router.get("/{reunion_id}", response_model=ReunionOut) async def detail_reunion( reunion_id: str, membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db), ): result = await db.execute( select(Reunion).where(Reunion.id == reunion_id, Reunion.groupe_id == membre.groupe_id) ) reunion = result.scalar_one_or_none() if not reunion: raise HTTPException(status_code=404, detail="Réunion introuvable") anim = await db.get(Membre, reunion.animateur_id) if reunion.animateur_id else None return ReunionOut( id=reunion.id, type=reunion.type, date_reunion=reunion.date_reunion, animateur_prenom=anim.prenom if anim else None, theme=reunion.theme, annonces=reunion.annonces, note=reunion.note, cree_le=reunion.cree_le, )