"""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é"}