147 lines
5 KiB
Python
147 lines
5 KiB
Python
"""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,
|
|
)
|