""" Script d'initialisation — Groupe Méditation Crée les postes, le groupe initial et le premier membre. Usage: cd /opt/groupe-meditation/backend && venv/bin/python ../deploy/seed.py """ import asyncio import sys import os import uuid import secrets from datetime import date, datetime, timedelta, timezone # Ajouter le backend au path sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend")) from app.core.database import engine, Base, async_session from app.core.security import hash_pin # Importer TOUS les modèles pour que create_all crée toutes les tables from app.models.groupe import Groupe from app.models.membre import Membre from app.models.poste import Poste from app.models.affectation import Affectation from app.models.invitation import Invitation from app.models.collecte import Collecte from app.models.depense import Depense from app.models.reunion import Reunion from app.models.presence import Presence from app.models.pv_reunion import PvReunion from app.models.decision import Decision from app.models.rotation import Rotation from app.models.rapport_rsg import RapportRsg from app.models.jeton import Jeton from app.models.litterature import Litterature from app.models.vente_litterature import VenteLitterature from app.models.envoi_contribution import EnvoiContribution from app.models.config_repartition import ConfigRepartition from app.models.sondage import Sondage from app.models.option_sondage import OptionSondage from app.models.vote import Vote from app.models.sync_log import SyncLog from app.models.proposition import Proposition from app.models.operation_bancaire import OperationBancaire from app.models.evenement import Evenement from app.models.candidature import Candidature from app.models.poste_module import PosteModule from app.models.reserve import Reserve POSTES = [ ("RSG", "executif", "Représentant aux Services du Groupe", "730 days", "2 ans sobriété", 1), ("RSG Substitut(e)", "executif", "Remplace le RSG, se prépare à assumer le rôle", "730 days", "1 an sobriété", 2), ("Trésorier(ière)", "executif", "Gestion financière, 7e Tradition", "548 days", "1 an sobriété", 3), ("Secrétaire", "executif", "PV, correspondance, mémoire du groupe", "548 days", "6 mois sobriété", 4), ("Animateur(trice)", "service", "Anime les réunions régulières", None, "6 mois sobriété", 5), ("Responsable accueil", "physique", "Accueil des membres et nouveaux", "365 days", "Sobriété stable", 6), ("Responsable café", "physique", "Café, entretien de la salle", "365 days", "Aucun", 7), ("Responsable littérature", "service", "Inventaire et vente de littérature AA", "365 days", "Connaissance littérature AA", 8), ("Responsable jetons", "service", "Jetons et anniversaires de sobriété", "365 days", "Aucun", 9), ("Responsable clés", "physique", "Ouverture et fermeture du local", None, "Fiabilité", 10), ] async def seed(): # Créer les tables async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) async with async_session() as db: # ── Créer le groupe ── print("\n═══ Initialisation du Groupe Méditation ═══\n") groupe = Groupe( nom="Groupe Méditation", district="87-16", region="87", adresse_local="", jour_reunion="mardi", heure_reunion=None, format_reunion="Discussion ouverte", lien_aa87="https://aa87.org", ) db.add(groupe) await db.flush() print(f"✓ Groupe créé: {groupe.nom} (district {groupe.district})") # ── Créer les postes ── postes_map = {} for nom, cat, desc, duree, prereq, ordre in POSTES: poste = Poste( nom=nom, categorie=cat, description=desc, prerequis=prereq, ordre_affichage=ordre, ) db.add(poste) await db.flush() postes_map[nom] = poste print(f" ✓ Poste: {nom} ({cat})") # ── Premier membre ── print("\n── Premier membre (vous) ──") prenom = input(" Prénom: ").strip().title() nom_membre = input(" Nom (optionnel, Entrée pour passer): ").strip() nom_membre = nom_membre.title() if nom_membre else None courriel = input(" Courriel: ").strip().lower() while not courriel or "@" not in courriel: courriel = input(" Courriel invalide. Réessayer: ").strip().lower() telephone = input(" Téléphone: ").strip() while not telephone: telephone = input(" Téléphone obligatoire: ").strip() pin = input(" PIN (4 chiffres): ").strip() while not pin.isdigit() or len(pin) != 4: pin = input(" PIN invalide. 4 chiffres: ").strip() print("\n Postes disponibles:") for i, (nom_poste, _, _, _, _, _) in enumerate(POSTES[:4], 1): print(f" {i}. {nom_poste}") choix = input(" Votre poste (1-4): ").strip() poste_idx = int(choix) - 1 if choix.isdigit() and 1 <= int(choix) <= 4 else 0 poste_choisi = POSTES[poste_idx][0] membre = Membre( groupe_id=groupe.id, prenom=prenom, nom=nom_membre, courriel=courriel, telephone=telephone, pin_hash=hash_pin(pin), ) db.add(membre) await db.flush() # ── Affectation ── affectation = Affectation( groupe_id=groupe.id, membre_id=membre.id, poste_id=postes_map[poste_choisi].id, date_debut=date.today(), statut="actif", ) db.add(affectation) # ── Première invitation ── code = secrets.token_hex(6).upper() invitation = Invitation( groupe_id=groupe.id, code=code, cree_par=membre.id, expire_le=datetime.now(timezone.utc) + timedelta(days=7), ) db.add(invitation) # ── Littérature de base ── for titre, cat, prix, code_bsg in [ ("Les Alcooliques anonymes (Gros Livre)", "livre", 12.00, "B-1"), ("Les Douze Étapes et les Douze Traditions", "livre", 11.00, "B-2"), ("Vivre sans alcool", "livre", 9.00, "B-7"), ("Les AA sont-ils pour vous?", "brochure", 0.00, "P-3"), ("Un nouveau arrive", "depliant", 0.00, "P-16"), ]: db.add(Litterature( groupe_id=groupe.id, titre=titre, categorie=cat, prix_coutant=prix, quantite_stock=0, seuil_commande=2, code_bsg=code_bsg, )) print(" ✓ Littérature de base : 5 titres") await db.commit() print(f"\n═══ Initialisation terminée ═══") print(f" Membre: {prenom} ({courriel})") print(f" Groupe: Groupe Méditation") print(f" Poste: {poste_choisi}") print(f"\n Code d'invitation pour les prochains membres: {code}") print(f" (Expire dans 7 jours)") print(f"\n Connectez-vous sur l'app avec:") print(f" Courriel: {courriel}") print(f" PIN: {pin}") if __name__ == "__main__": asyncio.run(seed())