156 lines
5.1 KiB
Python
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é"}
|