forked from yvv/decision
Typo Plus Jakarta Sans + renommage libreDecision + mode démo prod + seed mandats
- Fonte : Nunito → Plus Jakarta Sans (moderne, ronde sans être toy) - Logo : ğ(Decision) → libreDecision (libre italic/muted + Decision bold) - Footer et watermark DocumentPreview mis à jour - Mode démo : DEMO_MODE flag dans config.py + auth.py (profils rapides en prod) - docker-compose : ENVIRONMENT=production explicite + DEMO_MODE=true par défaut - Seed : +décision Licence G1 v0.4.0, +3 mandats (ComTech, Admin Forgerons, Modération) runner 7→10 étapes, import Mandate/MandateStep ajouté Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
289
backend/seed.py
289
backend/seed.py
@@ -31,6 +31,7 @@ from app.database import async_session, engine, Base, init_db
|
||||
from app.models.protocol import FormulaConfig, VotingProtocol
|
||||
from app.models.document import Document, DocumentItem
|
||||
from app.models.decision import Decision, DecisionStep
|
||||
from app.models.mandate import Mandate, MandateStep
|
||||
from app.models.user import DuniterIdentity
|
||||
from app.models.vote import VoteSession, Vote
|
||||
|
||||
@@ -2143,6 +2144,270 @@ async def seed_votes_on_items(
|
||||
print(f" VoteSession for '{item.title}': created (10 pour, 1 contre)")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed: Additional decisions (demo content)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def seed_decision_licence_evolution(session: AsyncSession) -> Decision:
|
||||
"""Seed a community decision: evolution of the G1 monetary license."""
|
||||
decision, created = await get_or_create(
|
||||
session,
|
||||
Decision,
|
||||
"title",
|
||||
"Évolution Licence G1 v0.4.0",
|
||||
description=(
|
||||
"Proposition d'évolution de la Licence G1 vers la version 0.4.0. "
|
||||
"Intègre les retours du forum, clarifie les engagements de certification "
|
||||
"et précise le processus de vote nuancé."
|
||||
),
|
||||
context=(
|
||||
"La Licence G1 v0.3.0 est en vigueur depuis l'origine de la monnaie libre. "
|
||||
"Des discussions communautaires approfondies (topics 31066, 32375, 32409, 32412) "
|
||||
"ont permis d'identifier des clarifications nécessaires. "
|
||||
"Cette décision lance le processus de vote communautaire pour l'adoption "
|
||||
"de la v0.4.0 selon le protocole Vote WoT standard."
|
||||
),
|
||||
decision_type="community",
|
||||
status="draft",
|
||||
)
|
||||
print(f" Decision 'Évolution Licence G1 v0.4.0': {'created' if created else 'exists'}")
|
||||
|
||||
if created:
|
||||
steps = [
|
||||
{
|
||||
"step_order": 1,
|
||||
"step_type": "qualification",
|
||||
"title": "Rédaction de la proposition",
|
||||
"description": (
|
||||
"Co-rédaction de la v0.4.0 en intégrant les retours des discussions "
|
||||
"forum. Coordination entre 1000i100, Natha, Pini et la communauté."
|
||||
),
|
||||
},
|
||||
{
|
||||
"step_order": 2,
|
||||
"step_type": "review",
|
||||
"title": "Période de commentaires",
|
||||
"description": (
|
||||
"Publication de la proposition sur le forum pendant 30 jours. "
|
||||
"Recueil des amendements et objections. Intégration des retours "
|
||||
"dans la version finale soumise au vote."
|
||||
),
|
||||
},
|
||||
{
|
||||
"step_order": 3,
|
||||
"step_type": "vote",
|
||||
"title": "Vote WoT (nuancé)",
|
||||
"description": (
|
||||
"Vote nuancé à 6 niveaux ouvert à tous les membres de la WoT. "
|
||||
"Durée : 30 jours. Protocole : Vote WoT standard avec formule inertie."
|
||||
),
|
||||
},
|
||||
{
|
||||
"step_order": 4,
|
||||
"step_type": "execution",
|
||||
"title": "Mise à jour du dépôt officiel",
|
||||
"description": (
|
||||
"Si le seuil est atteint : mise à jour du dépôt git officiel, "
|
||||
"calcul du hash IPFS, ancrage on-chain via system.remark."
|
||||
),
|
||||
},
|
||||
]
|
||||
for step_data in steps:
|
||||
step = DecisionStep(decision_id=decision.id, **step_data)
|
||||
session.add(step)
|
||||
await session.flush()
|
||||
print(f" -> {len(steps)} steps created")
|
||||
|
||||
return decision
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed: Mandates (Comité Technique + Admin Forgerons)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def seed_mandates(session: AsyncSession, voters: list[DuniterIdentity]) -> None:
|
||||
"""Seed example mandates: TechComm and Smith Admin."""
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# Find Charlie (techcomm voter) as mandatee, or use first voter
|
||||
mandatee_techcomm = next(
|
||||
(v for v in voters if "Cgeek" in v.display_name or "Elois" in v.display_name),
|
||||
voters[0] if voters else None,
|
||||
)
|
||||
mandatee_smith = next(
|
||||
(v for v in voters if "Moul" in v.display_name or "Tuxmain" in v.display_name),
|
||||
voters[1] if len(voters) > 1 else None,
|
||||
)
|
||||
|
||||
mandates_data = [
|
||||
{
|
||||
"title": "Mandat Comité Technique — Session 2025-2026",
|
||||
"description": (
|
||||
"Le Comité Technique (ComTech) est mandaté par la communauté pour "
|
||||
"assurer la validation technique des propositions on-chain : "
|
||||
"runtime upgrades, modifications de paramètres réseau, audits de code. "
|
||||
"Composition : 5 membres élus pour 12 mois."
|
||||
),
|
||||
"mandate_type": "techcomm",
|
||||
"status": "active",
|
||||
"mandatee_id": mandatee_techcomm.id if mandatee_techcomm else None,
|
||||
"starts_at": now - timedelta(days=90),
|
||||
"ends_at": now + timedelta(days=275),
|
||||
"steps": [
|
||||
{
|
||||
"step_order": 1,
|
||||
"step_type": "candidacy",
|
||||
"title": "Appel à candidatures",
|
||||
"status": "completed",
|
||||
"description": "Période de candidatures ouvertes sur le forum Duniter. Durée : 14 jours.",
|
||||
"outcome": "5 candidats retenus : Elois, Cgeek, Maaltir, Hugo, Tuxmain",
|
||||
},
|
||||
{
|
||||
"step_order": 2,
|
||||
"step_type": "vote",
|
||||
"title": "Élection par vote nuancé",
|
||||
"status": "completed",
|
||||
"description": "Vote WoT ouvert à tous les membres. Protocole Vote Nuancé (6 niveaux).",
|
||||
"outcome": "Quorum atteint. 5 membres élus à plus de 70% de soutien.",
|
||||
},
|
||||
{
|
||||
"step_order": 3,
|
||||
"step_type": "assignment",
|
||||
"title": "Prise de fonction",
|
||||
"status": "completed",
|
||||
"description": "Mise en place du ComTech, définition des processus internes de décision.",
|
||||
"outcome": "ComTech opérationnel depuis le 2025-09-15.",
|
||||
},
|
||||
{
|
||||
"step_order": 4,
|
||||
"step_type": "reporting",
|
||||
"title": "Rapport de mi-mandat",
|
||||
"status": "active",
|
||||
"description": "Rapport public d'activité à mi-parcours du mandat.",
|
||||
},
|
||||
{
|
||||
"step_order": 5,
|
||||
"step_type": "completion",
|
||||
"title": "Fin de mandat et bilan",
|
||||
"status": "pending",
|
||||
"description": "Rapport final, transmission aux successeurs, renouvellement ou dissolution.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "Mandat Administrateur des Forgerons — Rotation 2026-Q1",
|
||||
"description": (
|
||||
"L'Administrateur des Forgerons coordonne l'onboarding des nouveaux "
|
||||
"forgerons, maintient la liste des nœuds actifs et anime les "
|
||||
"discussions techniques de la sous-WoT Smith. "
|
||||
"Mandat tournant de 6 mois, renouvelable une fois."
|
||||
),
|
||||
"mandate_type": "smith",
|
||||
"status": "voting",
|
||||
"mandatee_id": mandatee_smith.id if mandatee_smith else None,
|
||||
"starts_at": None,
|
||||
"ends_at": None,
|
||||
"steps": [
|
||||
{
|
||||
"step_order": 1,
|
||||
"step_type": "formulation",
|
||||
"title": "Définition du rôle",
|
||||
"status": "completed",
|
||||
"description": "Rédaction de la fiche de rôle et des responsabilités de l'Administrateur.",
|
||||
"outcome": "Fiche de rôle validée par consensus sur le forum Duniter (topic 34201).",
|
||||
},
|
||||
{
|
||||
"step_order": 2,
|
||||
"step_type": "candidacy",
|
||||
"title": "Appel à candidatures forgerons",
|
||||
"status": "completed",
|
||||
"description": "Candidatures ouvertes aux forgerons actifs depuis plus de 6 mois.",
|
||||
"outcome": "2 candidats : Moul (Forgeron senior), Tuxmain (Forgeron actif).",
|
||||
},
|
||||
{
|
||||
"step_order": 3,
|
||||
"step_type": "vote",
|
||||
"title": "Vote forgeron (Smith)",
|
||||
"status": "active",
|
||||
"description": (
|
||||
"Vote Smith à double critère : quorum WoT + quorum forgerons. "
|
||||
"Durée : 30 jours. En cours."
|
||||
),
|
||||
},
|
||||
{
|
||||
"step_order": 4,
|
||||
"step_type": "assignment",
|
||||
"title": "Prise de fonction",
|
||||
"status": "pending",
|
||||
"description": "Passation avec l'administrateur sortant. Accès aux outils de coordination.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "Mandat Modération Forum — Duniter V2",
|
||||
"description": (
|
||||
"Équipe de modération élue pour maintenir la qualité des discussions "
|
||||
"sur forum.duniter.org et forum.monnaie-libre.fr. "
|
||||
"3 modérateurs, mandat de 12 mois."
|
||||
),
|
||||
"mandate_type": "custom",
|
||||
"status": "draft",
|
||||
"mandatee_id": None,
|
||||
"starts_at": None,
|
||||
"ends_at": None,
|
||||
"steps": [
|
||||
{
|
||||
"step_order": 1,
|
||||
"step_type": "formulation",
|
||||
"title": "Élaboration de la charte de modération",
|
||||
"status": "active",
|
||||
"description": "Définition des règles, outils et périmètre de la modération communautaire.",
|
||||
},
|
||||
{
|
||||
"step_order": 2,
|
||||
"step_type": "candidacy",
|
||||
"title": "Appel à candidatures",
|
||||
"status": "pending",
|
||||
"description": "Ouvert à tous les membres de la WoT G1.",
|
||||
},
|
||||
{
|
||||
"step_order": 3,
|
||||
"step_type": "vote",
|
||||
"title": "Élection",
|
||||
"status": "pending",
|
||||
"description": "Vote WoT standard.",
|
||||
},
|
||||
{
|
||||
"step_order": 4,
|
||||
"step_type": "assignment",
|
||||
"title": "Prise de fonction",
|
||||
"status": "pending",
|
||||
"description": "Formation aux outils Discourse et mise en place de la rotation.",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
for m_data in mandates_data:
|
||||
steps_data = m_data.pop("steps")
|
||||
mandate, created = await get_or_create(
|
||||
session,
|
||||
Mandate,
|
||||
"title",
|
||||
m_data["title"],
|
||||
**{k: v for k, v in m_data.items() if k != "title"},
|
||||
)
|
||||
status_str = "created" if created else "exists"
|
||||
print(f" Mandate '{mandate.title[:50]}': {status_str}")
|
||||
|
||||
if created:
|
||||
for step_data in steps_data:
|
||||
step = MandateStep(mandate_id=mandate.id, **step_data)
|
||||
session.add(step)
|
||||
await session.flush()
|
||||
print(f" -> {len(steps_data)} steps created")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main seed runner
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -2154,29 +2419,32 @@ async def run_seed():
|
||||
|
||||
# Ensure tables exist
|
||||
await init_db()
|
||||
print("[0/8] Tables created.\n")
|
||||
print("[0/10] Tables created.\n")
|
||||
|
||||
async with async_session() as session:
|
||||
async with session.begin():
|
||||
print("\n[1/8] Formula Configs...")
|
||||
print("\n[1/10] Formula Configs...")
|
||||
formulas = await seed_formula_configs(session)
|
||||
|
||||
print("\n[2/8] Voting Protocols...")
|
||||
print("\n[2/10] Voting Protocols...")
|
||||
protocols = await seed_voting_protocols(session, formulas)
|
||||
|
||||
print("\n[3/8] Document: Acte d'engagement Certification...")
|
||||
print("\n[3/10] Document: Acte d'engagement Certification...")
|
||||
await seed_document_engagement_certification(session, protocols)
|
||||
|
||||
print("\n[4/8] Document: Acte d'engagement forgeron v2.0.0...")
|
||||
print("\n[4/10] Document: Acte d'engagement forgeron v2.0.0...")
|
||||
doc_forgeron = await seed_document_engagement_forgeron(session, protocols)
|
||||
|
||||
print("\n[5/7] Decision: Runtime Upgrade...")
|
||||
print("\n[5/10] Decision: Runtime Upgrade...")
|
||||
await seed_decision_runtime_upgrade(session)
|
||||
|
||||
print("\n[6/7] Simulated voters...")
|
||||
print("\n[6/10] Decision: Évolution Licence G1 v0.4.0...")
|
||||
await seed_decision_licence_evolution(session)
|
||||
|
||||
print("\n[7/10] Simulated voters...")
|
||||
voters = await seed_voters(session)
|
||||
|
||||
print("\n[7/7] Votes on first 3 engagements forgeron...")
|
||||
print("\n[8/10] Votes on first 3 engagements forgeron...")
|
||||
await seed_votes_on_items(
|
||||
session,
|
||||
doc_forgeron,
|
||||
@@ -2184,6 +2452,11 @@ async def run_seed():
|
||||
voters,
|
||||
)
|
||||
|
||||
print("\n[9/10] Mandates...")
|
||||
await seed_mandates(session, voters)
|
||||
|
||||
print("\n[10/10] Done.")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Seed complete.")
|
||||
print("=" * 60)
|
||||
|
||||
Reference in New Issue
Block a user