groupe-meditation/backend/app/routers/evenements.py
2026-04-17 04:04:04 -04:00

156 lines
5.1 KiB
Python

"""Routes — Événements à venir (anniversaires + ad hoc + résolutions)."""
from datetime import date, timedelta
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, desc
from app.core.database import get_db
from app.core.security import get_current_membre, require_executif
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.models.evenement import Evenement
from app.models.proposition import Proposition
router = APIRouter(prefix="/evenements", tags=["Événements à venir"])
def _prochain_anniversaire(date_abstinence: date, aujourdhui: date) -> tuple[int, date, int]:
"""Retourne (années, prochaine date, jours restants)."""
try:
anniv = date_abstinence.replace(year=aujourdhui.year)
except ValueError:
anniv = date(aujourdhui.year, 3, 1)
if anniv < aujourdhui:
try:
anniv = date_abstinence.replace(year=aujourdhui.year + 1)
except ValueError:
anniv = date(aujourdhui.year + 1, 3, 1)
annees = anniv.year - date_abstinence.year
jours = (anniv - aujourdhui).days
return max(0, annees), anniv, jours
@router.get("/")
async def evenements_a_venir(
jours: int = 90,
membre=Depends(get_current_membre),
db: AsyncSession = Depends(get_db),
):
"""Tous les événements à venir : réunions, anniversaires, ad hoc."""
aujourdhui = date.today()
limite = aujourdhui + timedelta(days=jours)
groupe = await db.get(Groupe, membre.groupe_id)
district = groupe.district if groupe else "87-16"
evenements = []
# ── 1. Anniversaires de sobriété du district ──
result = await db.execute(
select(Membre, Groupe.nom)
.join(Groupe, Membre.groupe_id == Groupe.id)
.where(
Groupe.district == district,
Membre.actif == True,
Membre.date_abstinence.isnot(None),
)
)
for m, groupe_nom in result.all():
annees, prochain, jours_restants = _prochain_anniversaire(m.date_abstinence, aujourdhui)
if 0 <= jours_restants <= jours:
evenements.append({
"type": "anniversaire",
"date": str(prochain),
"titre": f"🎂 {m.prenom} — {annees} an{'s' if annees > 1 else ''}",
"detail": f"Anniversaire de sobriété · {groupe_nom}",
"lieu": None,
"groupe_nom": groupe_nom,
})
# ── 2. Événements ad hoc ──
result = await db.execute(
select(Evenement, Groupe.nom)
.join(Groupe, Evenement.groupe_id == Groupe.id)
.where(
Groupe.district == district,
Evenement.date_evenement >= aujourdhui,
Evenement.date_evenement <= limite,
)
)
for ev, groupe_nom in result.all():
evenements.append({
"type": "adhoc",
"date": str(ev.date_evenement),
"titre": ev.titre,
"detail": ev.description,
"lieu": ev.lieu,
"groupe_nom": groupe_nom,
})
# ── 3. Résolutions adoptées du groupe ──
result = await db.execute(
select(Proposition).where(
Proposition.groupe_id == membre.groupe_id,
Proposition.statut == "adoptee",
).order_by(desc(Proposition.date_vote))
)
for p in result.scalars().all():
date_res = str(p.date_vote) if p.date_vote else str(p.cree_le.date())
evenements.append({
"type": "resolution",
"date": date_res,
"titre": f"📜 {p.numero_resolution or 'RES'} — {p.sujet}",
"detail": p.description or p.note,
"lieu": None,
"groupe_nom": groupe.nom if groupe else "?",
})
# Trier par date
evenements.sort(key=lambda e: e["date"])
return evenements
# ── CRUD événements ad hoc ──
@router.post("/")
async def creer_evenement(
titre: str,
date_evenement: date,
description: str = "",
lieu: str = "",
executif=Depends(require_executif),
db: AsyncSession = Depends(get_db),
):
"""Créer un événement ad hoc (exécutif)."""
ev = Evenement(
groupe_id=executif.groupe_id,
titre=titre.strip(),
description=description.strip() or None,
date_evenement=date_evenement,
lieu=lieu.strip() or None,
cree_par=executif.id,
)
db.add(ev)
await db.flush()
return {"message": "Événement créé", "id": str(ev.id)}
@router.delete("/{evenement_id}")
async def supprimer_evenement(
evenement_id: str,
executif=Depends(require_executif),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(Evenement).where(
Evenement.id == evenement_id,
Evenement.groupe_id == executif.groupe_id,
)
)
ev = result.scalar_one_or_none()
if not ev:
raise HTTPException(status_code=404, detail="Événement introuvable")
await db.delete(ev)
await db.flush()
return {"message": "Événement supprimé"}