Qualifier : corrections R2/R6 + router + modèle DB + wizard frontend

Corrections moteur (TDD) :
- R2 : within_mandate → record_in_observatory=True (Observatoire des décisions)
- R6 : >50 personnes → collective recommandé, pas obligatoire (confidence=recommended)
- R3 supprimée : affected_count=1 hors périmètre de l'outil
- R9-R12 renommés G1-G4 (garde-fous internes)
- 23 tests, 213/213 verts

Étape 1 — Router /api/v1/qualify :
- POST / → qualify() avec config depuis DB ou defaults
- GET /protocol → protocole actif
- POST /protocol → créer/remplacer (auth requise)

Étape 2 — Modèle QualificationProtocol :
- Table qualification_protocols (seuils configurables via admin)
- Migration Alembic + seed du protocole par défaut

Étape 3 — Wizard frontend decisions/new.vue :
- Étape 1 : formulaire de qualification (mandat, affected_count, structurant, contexte)
- Étape 2 : résultat (type, raisons, modalités, observatoire, on-chain)
- Étape 3 : formulaire de décision (titre, description, protocole si collectif)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-04-23 19:12:01 +02:00
parent 428299c9c8
commit 5c51cffc93
11 changed files with 1060 additions and 519 deletions

View File

@@ -33,6 +33,7 @@ from app.models.document import Document, DocumentItem
from app.models.decision import Decision, DecisionStep
from app.models.mandate import Mandate, MandateStep
from app.models.organization import Organization
from app.models.qualification import QualificationProtocol
from app.models.user import DuniterIdentity
from app.models.vote import VoteSession, Vote
@@ -2459,6 +2460,37 @@ async def seed_organizations(session: AsyncSession) -> dict[str, Organization]:
# Main seed runner
# ---------------------------------------------------------------------------
async def seed_qualification_protocol(session: AsyncSession) -> QualificationProtocol:
"""Seed the default qualification protocol (thresholds for the Décider tunnel)."""
stmt = select(QualificationProtocol).where(QualificationProtocol.is_active == True) # noqa: E712
result = await session.execute(stmt)
existing = result.scalar_one_or_none()
if existing is not None:
print(" [skip] Protocole de qualification déjà présent")
return existing
proto = QualificationProtocol(
name="Protocole de qualification par défaut",
description=(
"Seuils utilisés par le tunnel Décider pour router vers "
"individual/collective et proposer les modalités de vote."
),
small_group_max=5,
collective_wot_min=50,
default_modalities_json=json.dumps([
"vote_wot",
"vote_smith",
"consultation_avis",
"election",
]),
is_active=True,
)
session.add(proto)
await session.flush()
print(" [ok] Protocole de qualification créé")
return proto
async def run_seed():
print("=" * 60)
print("libreDecision - Seed Database")
@@ -2474,6 +2506,9 @@ async def run_seed():
orgs = await seed_organizations(session)
duniter_g1_id = orgs["duniter-g1"].id
print("\n[0b] Protocole de qualification...")
await seed_qualification_protocol(session)
print("\n[1/10] Formula Configs...")
formulas = await seed_formula_configs(session)