432 lines
23 KiB
Python
432 lines
23 KiB
Python
"""Operations sysadmin de remise a zero et de donnees de demonstration."""
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
from datetime import date, datetime, time, timedelta, timezone
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import text
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.database import Base
|
|
from app.core.security import hash_pin
|
|
from app.models.affectation import Affectation
|
|
from app.models.candidature import Candidature
|
|
from app.models.collecte import Collecte
|
|
from app.models.config_repartition import ConfigRepartition
|
|
from app.models.depense import Depense
|
|
from app.models.destinataire_contribution import DestinataireContribution
|
|
from app.models.envoi_contribution import EnvoiContribution
|
|
from app.models.evenement import Evenement
|
|
from app.models.groupe import Groupe
|
|
from app.models.jeton import Jeton
|
|
from app.models.litterature import Litterature
|
|
from app.models.membre import Membre
|
|
from app.models.mouvement_reserve import MouvementReserve
|
|
from app.models.operation_bancaire import OperationBancaire
|
|
from app.models.poste import Poste
|
|
from app.models.poste_module import PosteModule
|
|
from app.models.presence import Presence
|
|
from app.models.proposition import Proposition
|
|
from app.models.pv_reunion import PvReunion
|
|
from app.models.rapport_adopte import RapportAdopte
|
|
from app.models.rapport_rsg import RapportRsg
|
|
from app.models.reserve import Reserve
|
|
from app.models.reunion import Reunion
|
|
from app.models.transaction_comptable import TransactionComptable
|
|
from app.models.vente_litterature import VenteLitterature
|
|
|
|
|
|
POSTES = [
|
|
("RSG", "executif", "Représente le groupe auprès du district et rapporte les informations de service.", "2 ans de stabilité", 1, ["M3", "C2", "C5"]),
|
|
("RSG Substitut(e)", "executif", "Appuie le RSG et assure la relève lorsque nécessaire.", "1 an de stabilité", 2, ["M3", "C2", "C5"]),
|
|
("Trésorier(ière)", "executif", "Tient les registres, reçoit les sommes et présente les rapports mensuels.", "1 an de stabilité", 3, ["S1T", "S5", "M4", "G8"]),
|
|
("Secrétaire", "executif", "Prépare les procès-verbaux, les présences et la correspondance.", "6 mois de stabilité", 4, ["M0", "M1", "M2", "S0"]),
|
|
("Animateur(trice)", "service", "Anime les rencontres hebdomadaires.", "Disponibilité régulière", 5, ["S0"]),
|
|
("Responsable accueil", "physique", "Accueille les nouveaux et veille à l'atmosphère de la salle.", "Fiabilité", 6, ["C9"]),
|
|
("Responsable café", "physique", "Prépare le café et remet les petites dépenses au trésorier.", "Aucun", 7, ["S5"]),
|
|
("Responsable littérature", "service", "Maintient l'inventaire et remet les ventes au trésorier.", "Connaissance de la littérature", 8, ["S3", "C4", "M5"]),
|
|
("Responsable jetons", "service", "Prépare les jetons et les anniversaires.", "Aucun", 9, ["S2", "M6"]),
|
|
("Responsable clés", "physique", "Ouvre et ferme le local.", "Fiabilité", 10, []),
|
|
]
|
|
|
|
MEMBRES_DEMO = [
|
|
("Sysadmin", None, "sysadmin", "0000", "514-555-0000"),
|
|
("Claire", "B.", "claire.demo@example.test", "1111", "514-555-0101"),
|
|
("Marc", "L.", "marc.demo@example.test", "2222", "514-555-0102"),
|
|
("Nadia", "R.", "nadia.demo@example.test", "3333", "514-555-0103"),
|
|
("Simon", "T.", "simon.demo@example.test", "4444", "514-555-0104"),
|
|
("Julie", "P.", "julie.demo@example.test", "5555", "514-555-0105"),
|
|
("Karim", "A.", "karim.demo@example.test", "6666", "514-555-0106"),
|
|
("Mélanie", "S.", "melanie.demo@example.test", "7777", "514-555-0107"),
|
|
("Robert", "G.", "robert.demo@example.test", "8888", "514-555-0108"),
|
|
("Anne", "C.", "anne.demo@example.test", "9999", "514-555-0109"),
|
|
("Luc", "D.", "luc.demo@example.test", "1212", "514-555-0110"),
|
|
("Sophie", "M.", "sophie.demo@example.test", "3434", "514-555-0111"),
|
|
]
|
|
|
|
|
|
def _catalogue_litterature() -> list[dict]:
|
|
chemin = Path(__file__).resolve().parents[3] / "data" / "import" / "Liste-de-prix-rev.-Fevrier-2026.csv"
|
|
if chemin.exists():
|
|
with chemin.open(newline="", encoding="utf-8") as fichier:
|
|
return [
|
|
{
|
|
"code_bsg": row["code_bsg"].strip() or None,
|
|
"titre": row["titre"].strip(),
|
|
"categorie": row["categorie"].strip() or "brochure",
|
|
"prix_coutant": float(row["prix_coutant"]) if row["prix_coutant"].strip() else 0,
|
|
}
|
|
for row in csv.DictReader(fichier)
|
|
if row.get("titre", "").strip()
|
|
]
|
|
return [
|
|
{"code_bsg": "B-1", "titre": "Les Alcooliques anonymes", "categorie": "livre", "prix_coutant": 12.0},
|
|
{"code_bsg": "B-2", "titre": "Les Douze Étapes et les Douze Traditions", "categorie": "livre", "prix_coutant": 11.0},
|
|
{"code_bsg": "P-3", "titre": "Les AA sont-ils pour vous?", "categorie": "brochure", "prix_coutant": 0.0},
|
|
]
|
|
|
|
|
|
async def remettre_a_zero(db: AsyncSession) -> dict:
|
|
tables = ", ".join(table.name for table in reversed(Base.metadata.sorted_tables))
|
|
if tables:
|
|
await db.execute(text(f"TRUNCATE TABLE {tables} RESTART IDENTITY CASCADE"))
|
|
groupe, sysadmin, postes, litterature = await _creer_base(db)
|
|
await db.flush()
|
|
return {
|
|
"message": "Base remise à zéro",
|
|
"groupe_id": groupe.id,
|
|
"sysadmin_id": sysadmin.id,
|
|
"postes": len(postes),
|
|
"litterature": len(litterature),
|
|
}
|
|
|
|
|
|
async def initialiser_demo(db: AsyncSession) -> dict:
|
|
await remettre_a_zero(db)
|
|
groupe = await _premier_groupe(db)
|
|
membres = await _membres_par_courriel(db)
|
|
postes = await _postes_par_nom(db)
|
|
litterature = await _litterature(db)
|
|
|
|
await _ajouter_membres_demo(db, groupe, membres, postes)
|
|
membres = await _membres_par_courriel(db)
|
|
await _ajouter_historique_demo(db, groupe, membres, postes, litterature)
|
|
await db.flush()
|
|
return {
|
|
"message": "Base remise à zéro et données de démonstration installées",
|
|
"groupe_id": groupe.id,
|
|
"membres": len(membres),
|
|
"periode_mois": 12,
|
|
}
|
|
|
|
|
|
async def _creer_base(db: AsyncSession):
|
|
groupe = Groupe(
|
|
nom="Groupe Méditation",
|
|
district="87-16",
|
|
region="87",
|
|
adresse_local="Salle communautaire, Montréal",
|
|
jour_reunion="mardi",
|
|
heure_reunion=time(19, 30),
|
|
assemblee_affaires_ordre="premiere",
|
|
format_reunion="Discussion ouverte",
|
|
lien_aa87="https://aa87.org",
|
|
)
|
|
db.add(groupe)
|
|
await db.flush()
|
|
|
|
postes = {}
|
|
for nom, categorie, description, prerequis, ordre, modules in POSTES:
|
|
poste = Poste(
|
|
nom=nom,
|
|
categorie=categorie,
|
|
description=description,
|
|
prerequis=prerequis,
|
|
ordre_affichage=ordre,
|
|
actif=True,
|
|
)
|
|
db.add(poste)
|
|
await db.flush()
|
|
postes[nom] = poste
|
|
for module_code in modules:
|
|
db.add(PosteModule(poste_id=poste.id, module_code=module_code))
|
|
|
|
sysadmin = Membre(
|
|
groupe_id=groupe.id,
|
|
prenom="Sysadmin",
|
|
nom=None,
|
|
telephone="514-555-0000",
|
|
courriel="sysadmin",
|
|
pin_hash=hash_pin("0000"),
|
|
actif=True,
|
|
)
|
|
db.add(sysadmin)
|
|
await db.flush()
|
|
|
|
litterature = []
|
|
for item in _catalogue_litterature():
|
|
lit = Litterature(
|
|
groupe_id=groupe.id,
|
|
titre=item["titre"][:200],
|
|
categorie=item["categorie"] if item["categorie"] in {"livre", "brochure", "depliant", "pamphlet"} else "brochure",
|
|
prix_coutant=item["prix_coutant"],
|
|
quantite_stock=8,
|
|
seuil_commande=2,
|
|
code_bsg=item["code_bsg"],
|
|
)
|
|
db.add(lit)
|
|
litterature.append(lit)
|
|
|
|
db.add(DestinataireContribution(groupe_id=groupe.id, nom="District 87-16", protege=True, actif=True))
|
|
db.add(DestinataireContribution(groupe_id=groupe.id, nom="Région 87", protege=False, actif=True))
|
|
db.add(DestinataireContribution(groupe_id=groupe.id, nom="Bureau des services généraux", protege=False, actif=True))
|
|
for destination, pct in [("district", 50), ("region", 20), ("bsg", 20), ("intergroupe", 10)]:
|
|
db.add(ConfigRepartition(groupe_id=groupe.id, destination=destination, pourcentage=pct, actif_depuis=date.today().replace(day=1)))
|
|
return groupe, sysadmin, postes, litterature
|
|
|
|
|
|
async def _ajouter_membres_demo(db: AsyncSession, groupe: Groupe, membres: dict, postes: dict) -> None:
|
|
for prenom, nom, courriel, pin, telephone in MEMBRES_DEMO[1:]:
|
|
membre = Membre(
|
|
groupe_id=groupe.id,
|
|
prenom=prenom,
|
|
nom=nom,
|
|
telephone=telephone,
|
|
courriel=courriel,
|
|
date_abstinence=date.today() - timedelta(days=365 * (2 + len(membres) % 12)),
|
|
pin_hash=hash_pin(pin),
|
|
actif=True,
|
|
)
|
|
db.add(membre)
|
|
await db.flush()
|
|
membres[courriel] = membre
|
|
|
|
affectations = [
|
|
("claire.demo@example.test", "RSG"),
|
|
("marc.demo@example.test", "RSG Substitut(e)"),
|
|
("nadia.demo@example.test", "Trésorier(ière)"),
|
|
("simon.demo@example.test", "Secrétaire"),
|
|
("julie.demo@example.test", "Animateur(trice)"),
|
|
("karim.demo@example.test", "Responsable littérature"),
|
|
("melanie.demo@example.test", "Responsable jetons"),
|
|
("robert.demo@example.test", "Responsable accueil"),
|
|
("anne.demo@example.test", "Responsable café"),
|
|
]
|
|
debut = date.today() - timedelta(days=240)
|
|
for courriel, poste_nom in affectations:
|
|
membre = membres.get(courriel)
|
|
if membre and poste_nom in postes:
|
|
db.add(Affectation(groupe_id=groupe.id, membre_id=membre.id, poste_id=postes[poste_nom].id, date_debut=debut, statut="actif"))
|
|
|
|
|
|
async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: dict, postes: dict, litterature: list[Litterature]) -> None:
|
|
tresorier = membres["nadia.demo@example.test"]
|
|
secretaire = membres["simon.demo@example.test"]
|
|
rsg = membres["claire.demo@example.test"]
|
|
membres_actifs = [m for m in membres.values() if m.courriel != "sysadmin"]
|
|
reserves = {
|
|
"Loyer": Reserve(groupe_id=groupe.id, nom="Loyer", montant=0, note="Prévoir trois mois de salle"),
|
|
"Littérature": Reserve(groupe_id=groupe.id, nom="Littérature", montant=0, note="Réapprovisionnement"),
|
|
"Prudentielle": Reserve(groupe_id=groupe.id, nom="Prudentielle", montant=0, note="Réserve de sécurité"),
|
|
}
|
|
for reserve in reserves.values():
|
|
db.add(reserve)
|
|
await db.flush()
|
|
|
|
debut = _premier_mardi(date.today() - timedelta(days=365))
|
|
dates_reunions = [debut + timedelta(days=7 * i) for i in range(52)]
|
|
mois_vus = set()
|
|
banque = 0.0
|
|
encaisse = 0.0
|
|
compteurs = {"RES": 0, "NOM": 0, "DON": 0}
|
|
|
|
for index, jour in enumerate(dates_reunions):
|
|
type_reunion = "affaires" if jour.month not in mois_vus else "reguliere"
|
|
mois_vus.add(jour.month)
|
|
reunion = Reunion(
|
|
groupe_id=groupe.id,
|
|
type=type_reunion,
|
|
date_reunion=datetime.combine(jour, groupe.heure_reunion or time(19, 30)).replace(tzinfo=timezone.utc),
|
|
animateur_id=membres_actifs[index % len(membres_actifs)].id,
|
|
theme=["Acceptation", "Service", "Gratitude", "Honnêteté"][index % 4],
|
|
annonces={"district": index % 5 == 0, "anniversaire": index % 8 == 0},
|
|
note="Réunion de démonstration réaliste.",
|
|
)
|
|
db.add(reunion)
|
|
await db.flush()
|
|
|
|
for membre in membres_actifs:
|
|
db.add(Presence(reunion_id=reunion.id, membre_id=membre.id, present=(index + len(membre.prenom)) % 7 != 0))
|
|
|
|
montant_collecte = round(82 + (index % 6) * 9 + (index % 3) * 4, 2)
|
|
collecte = Collecte(
|
|
groupe_id=groupe.id,
|
|
date_collecte=jour,
|
|
montant=montant_collecte,
|
|
reunion_id=reunion.id,
|
|
detenteur_id=membres_actifs[index % len(membres_actifs)].id,
|
|
statut="recu",
|
|
saisi_par=secretaire.id,
|
|
confirme_par=secretaire.id,
|
|
date_confirmation=datetime.combine(jour, time(21, 15)).replace(tzinfo=timezone.utc),
|
|
recu_par=tresorier.id,
|
|
date_reception=jour,
|
|
)
|
|
db.add(collecte)
|
|
await db.flush()
|
|
encaisse += montant_collecte
|
|
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="collecte", compte="encaisse", montant=montant_collecte, sens="debit", source_table="collectes", source_id=collecte.id, note="Collecte 7e Tradition", cree_par=tresorier.id))
|
|
|
|
if index % 4 == 0:
|
|
don = 40 + (index % 5) * 10
|
|
banque += don
|
|
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="don", compte="banque", montant=don, sens="debit", note="Don direct au compte bancaire", cree_par=tresorier.id))
|
|
|
|
if index % 4 == 3:
|
|
depot = round(encaisse - 25, 2) if encaisse > 50 else round(encaisse, 2)
|
|
if depot > 0:
|
|
encaisse -= depot
|
|
banque += depot
|
|
db.add(OperationBancaire(groupe_id=groupe.id, type="depot", montant=depot, date_operation=jour, effectue_par=tresorier.id, note="Dépôt mensuel de l'encaisse"))
|
|
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="banque_depot", compte="banque", montant=depot, sens="debit", note="Dépôt de l'encaisse", cree_par=tresorier.id))
|
|
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="banque_depot", compte="encaisse", montant=depot, sens="credit", note="Dépôt de l'encaisse", cree_par=tresorier.id))
|
|
|
|
if type_reunion == "affaires":
|
|
await _decision(db, groupe, reunion, compteurs, "RES", jour, secretaire, rsg, "Adopter l'ordre du jour", "L'assemblée doit traiter les affaires courantes.", "L'ordre du jour est adopté tel que présenté.")
|
|
await _decision(db, groupe, reunion, compteurs, "RES", jour, rsg, secretaire, f"Orientation du mois {jour.month:02d}", "Le groupe souhaite garder une conscience de groupe active.", "Le groupe retient les priorités présentées pour le mois.")
|
|
pv = PvReunion(
|
|
reunion_id=reunion.id,
|
|
redacteur_id=secretaire.id,
|
|
contenu=f"Procès-verbal de l'assemblée du {jour.isoformat()}.\nPrésences vérifiées. Rapports reçus. Décisions consignées.",
|
|
decisions=[{"sujet": "Adoption du procès-verbal précédent", "decision": "Adopté", "proposeur": rsg.prenom, "secondeur": secretaire.prenom, "statut": "complete"}],
|
|
approuve=index > 4,
|
|
)
|
|
db.add(pv)
|
|
db.add(RapportAdopte(
|
|
groupe_id=groupe.id,
|
|
adopte_par_id=tresorier.id,
|
|
type_rapport="tresorerie",
|
|
titre=f"Rapport du trésorier — {jour.strftime('%Y-%m')}",
|
|
annee=jour.year,
|
|
mois=jour.month,
|
|
contenu_html=f"<html><body><h1>Rapport du trésorier</h1><p>Rapport adopté pour {jour.strftime('%Y-%m')}.</p><p>Banque: {banque:.2f} $</p><p>Encaisse: {encaisse:.2f} $</p></body></html>",
|
|
donnees={"banque": round(banque, 2), "encaisse": round(encaisse, 2)},
|
|
))
|
|
|
|
if index % 6 == 1:
|
|
montant = 32 + (index % 4) * 6
|
|
methode = "COLLECTE" if encaisse >= montant else "VIREMENT"
|
|
if methode == "COLLECTE":
|
|
encaisse -= montant
|
|
compte = "encaisse"
|
|
else:
|
|
banque -= montant
|
|
compte = "banque"
|
|
depense = Depense(groupe_id=groupe.id, date_depense=jour, montant=montant, qui=membres_actifs[index % len(membres_actifs)].id, quoi=["Café et lait", "Papeterie", "Location de salle", "Fournitures accueil"][index % 4], paye=True, methode=methode, saisi_par=membres_actifs[index % len(membres_actifs)].id, confirme_par=tresorier.id, date_confirmation=datetime.combine(jour, time(20, 45)).replace(tzinfo=timezone.utc))
|
|
db.add(depense)
|
|
await db.flush()
|
|
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="depense", compte=compte, montant=montant, sens="credit", source_table="depenses", source_id=depense.id, note=depense.quoi, cree_par=tresorier.id))
|
|
|
|
if index % 8 == 2 and litterature:
|
|
item = litterature[index % len(litterature)]
|
|
montant = float(item.prix_coutant or 2) * 2
|
|
item.quantite_stock = max(0, item.quantite_stock - 2)
|
|
vente = VenteLitterature(groupe_id=groupe.id, litterature_id=item.id, quantite=2, montant=montant, date_vente=jour, vendu_par=membres["karim.demo@example.test"].id, remis_tresorier=True, recu_par=tresorier.id, date_remise=jour)
|
|
db.add(vente)
|
|
await db.flush()
|
|
encaisse += montant
|
|
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="vente_litterature", compte="encaisse", montant=montant, sens="debit", source_table="ventes_litterature", source_id=vente.id, note="Vente de littérature", cree_par=tresorier.id))
|
|
|
|
if index % 13 == 4:
|
|
montant = 125
|
|
if banque >= montant:
|
|
banque -= montant
|
|
decision = await _decision(db, groupe, reunion, compteurs, "DON", jour, rsg, secretaire, "Contribution au district", "La conscience de groupe prévoit soutenir les services.", "Le groupe décide d'envoyer une contribution au District 87-16.")
|
|
contribution = EnvoiContribution(groupe_id=groupe.id, date_envoi=jour, destination="District 87-16", montant=montant, methode="VIREMENT", resolution_id=decision.id, engagee=True, debitee=True, saisi_par=tresorier.id)
|
|
db.add(contribution)
|
|
await db.flush()
|
|
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="contribution", compte="banque", montant=montant, sens="credit", source_table="envois_contributions", source_id=contribution.id, note="Contribution au district", cree_par=tresorier.id))
|
|
|
|
if index in {9, 22, 35}:
|
|
candidat = membres_actifs[index % len(membres_actifs)]
|
|
poste = postes[["Responsable accueil", "Responsable café", "Responsable clés"][index % 3]]
|
|
candidature = Candidature(groupe_id=groupe.id, poste_id=poste.id, membre_id=candidat.id, type="nomination", statut="acceptee", motivation="Disponibilité confirmée.", nomme_par=rsg.id, acceptee_par_membre=True)
|
|
db.add(candidature)
|
|
await db.flush()
|
|
decision = await _decision(db, groupe, reunion, compteurs, "NOM", jour, rsg, secretaire, f"Nomination de {candidat.prenom}", "Un poste doit être pourvu pour soutenir le groupe.", f"Le groupe nomme {candidat.prenom} au poste de {poste.nom}.")
|
|
decision.candidature_id = candidature.id
|
|
candidature.proposition_id = decision.id
|
|
|
|
if index % 10 == 5:
|
|
db.add(Jeton(groupe_id=groupe.id, membre_id=membres_actifs[index % len(membres_actifs)].id, type_jeton="multiple", date_remise=jour, annees=1 + index % 5, remis_par=membres["melanie.demo@example.test"].id))
|
|
|
|
if index % 17 == 7:
|
|
reserve = reserves[["Loyer", "Littérature", "Prudentielle"][index % 3]]
|
|
montant = 75
|
|
if banque >= montant:
|
|
reserve.montant = float(reserve.montant or 0) + montant
|
|
db.add(MouvementReserve(groupe_id=groupe.id, source_id=None, destination_id=reserve.id, montant=montant, type="reserve_virement", note="Affectation au solde réservé", cree_par=tresorier.id, cree_le=datetime.combine(jour, time(21, 0)).replace(tzinfo=timezone.utc)))
|
|
|
|
db.add(Collecte(groupe_id=groupe.id, date_collecte=dates_reunions[-1] + timedelta(days=7), montant=96, detenteur_id=membres["luc.demo@example.test"].id, statut="en_attente", saisi_par=membres["luc.demo@example.test"].id))
|
|
db.add(Depense(groupe_id=groupe.id, date_depense=date.today(), montant=48, qui=membres["anne.demo@example.test"].id, quoi="Fournitures à confirmer", paye=False, methode=None, saisi_par=membres["anne.demo@example.test"].id))
|
|
db.add(Proposition(groupe_id=groupe.id, sujet="Réviser le format de discussion", description="Proposition laissée en réflexion pour la prochaine assemblée.", proposeur_id=membres["julie.demo@example.test"].id, secondeur_id=membres["marc.demo@example.test"].id, statut="secondee"))
|
|
db.add(Proposition(groupe_id=groupe.id, sujet="Acheter un projecteur", description="La dépense n'est pas jugée prioritaire.", proposeur_id=membres["luc.demo@example.test"].id, secondeur_id=membres["sophie.demo@example.test"].id, statut="rejetee", etant_donne_que="La réserve prudente doit d'abord être consolidée."))
|
|
db.add(Evenement(groupe_id=groupe.id, titre="Atelier Étapes et Traditions", description="Atelier régional ouvert aux membres.", date_evenement=date.today() + timedelta(days=24), lieu="Centre communautaire", cree_par=rsg.id))
|
|
db.add(Evenement(groupe_id=groupe.id, titre="Anniversaire du groupe", description="Souper partage et réunion ouverte.", date_evenement=date.today() - timedelta(days=40), lieu="Local habituel", cree_par=secretaire.id))
|
|
db.add(RapportRsg(groupe_id=groupe.id, reunion_dist=date.today() - timedelta(days=18), redacteur_id=rsg.id, decisions=["Le district prépare l'inventaire annuel.", "Besoin de bénévoles pour l'accueil."], infos_region="La région sollicite les groupes pour les ateliers.", sujets_groupe="Questions transmises par la conscience de groupe."))
|
|
|
|
|
|
async def _decision(db, groupe, reunion, compteurs, prefixe, jour, proposeur, secondeur, sujet, attendu, decide):
|
|
compteurs[prefixe] += 1
|
|
numero = f"{prefixe}-{jour.year}-{compteurs[prefixe]:03d}"
|
|
proposition = Proposition(
|
|
groupe_id=groupe.id,
|
|
reunion_id=reunion.id if reunion else None,
|
|
sujet=sujet,
|
|
description=attendu,
|
|
proposeur_id=proposeur.id,
|
|
secondeur_id=secondeur.id,
|
|
statut="adoptee",
|
|
numero_resolution=numero,
|
|
date_vote=jour,
|
|
date_adoption=jour,
|
|
etant_donne_que=attendu,
|
|
groupe_a_decide_de=decide,
|
|
)
|
|
db.add(proposition)
|
|
await db.flush()
|
|
return proposition
|
|
|
|
|
|
def _premier_mardi(jour: date) -> date:
|
|
delta = (1 - jour.weekday()) % 7
|
|
return jour + timedelta(days=delta)
|
|
|
|
|
|
async def _premier_groupe(db: AsyncSession) -> Groupe:
|
|
from sqlalchemy import select
|
|
|
|
result = await db.execute(select(Groupe).limit(1))
|
|
return result.scalar_one()
|
|
|
|
|
|
async def _membres_par_courriel(db: AsyncSession) -> dict[str, Membre]:
|
|
from sqlalchemy import select
|
|
|
|
result = await db.execute(select(Membre))
|
|
return {m.courriel: m for m in result.scalars().all()}
|
|
|
|
|
|
async def _postes_par_nom(db: AsyncSession) -> dict[str, Poste]:
|
|
from sqlalchemy import select
|
|
|
|
result = await db.execute(select(Poste))
|
|
return {p.nom: p for p in result.scalars().all()}
|
|
|
|
|
|
async def _litterature(db: AsyncSession) -> list[Litterature]:
|
|
from sqlalchemy import select
|
|
|
|
result = await db.execute(select(Litterature))
|
|
return result.scalars().all()
|