P5 — Mode consultation ciblée (Option B, implémentation complète)
Backend : - Nouvelle table `consultations` (slug unique, fenêtre temporelle, webhook, logo) - `ideas.consultation_id` FK nullable (NULL = contexte global home) - `synthesis.consultation_id` FK nullable (synthèse par contexte) - Boucle auto-fermeture (thread daemon, 60 s) — ferme + webhook à l'échéance - Webhook de clôture : POST JSON (synthèse + métadonnées) via urllib.request - Routes publiques : GET/POST /api/consultations/<slug>, synthèse, contributions, export/print - Routes admin : list, create, close (+ webhook), delete (cascade explicite) - CSP ajustée sur /export/print pour autoriser window.print() Frontend : - Nouvelle page /consultation/:slug — formulaire, synthèse live, contributions paginées, PDF - Admin panel : onglet Consultations — liste, formulaire création, fermeture, suppression Docs : DAT.md v1.5, DEX.md v1.7 (section P5, tables, routes, webhook) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+63
-8
@@ -1,7 +1,7 @@
|
||||
# Document d'Architecture Technique — La Voix du Peuple
|
||||
|
||||
**Version** : 1.4
|
||||
**Date** : Avril 2026
|
||||
**Version** : 1.5
|
||||
**Date** : Mai 2026
|
||||
**Statut** : Prêt pour auto-hébergement
|
||||
|
||||
---
|
||||
@@ -15,6 +15,7 @@
|
||||
| 1.2 | Avril 2026 | Palette pétrol neutre, textes de posture (expression vs. vérité) |
|
||||
| 1.3 | Avril 2026 | Dark mode pétrol, panneau d'accessibilité (dyslexie, contraste, zoom) |
|
||||
| 1.4 | Avril 2026 | Synchronisation Gitea sécurisée — `GITEA_TOKEN` + `scripts/push-gitea.sh` |
|
||||
| 1.5 | Mai 2026 | P5 — Mode consultation ciblée (table `consultations`, routes `/consultation/:slug`, panel admin, webhook, auto-fermeture) |
|
||||
|
||||
---
|
||||
|
||||
@@ -88,6 +89,10 @@
|
||||
| `/about` | À propos, fondements juridiques, posture de la démarche |
|
||||
| `/transparence` | Fonctionnement de l'IA, données collectées, limites, posture éditoriale |
|
||||
| `/flyer` | Flyer imprimable avec QR code configurable pour diffusion physique |
|
||||
| `/consultation/:slug` | Page d'une consultation ciblée : formulaire, synthèse, contributions, export PDF |
|
||||
| `/contributions-brutes` | Contributions globales paginées, exports JSON/CSV, transparence éditoriale |
|
||||
| `/mentions-legales` | Mentions légales (LCEN) |
|
||||
| `/politique-confidentialite` | Politique de confidentialité et droits RGPD |
|
||||
|
||||
**Accessibilité** :
|
||||
|
||||
@@ -137,15 +142,26 @@
|
||||
| Rate limiting | flask-limiter (5 req/min par IP sur POST /api/ideas) |
|
||||
| ORM | psycopg2-binary (requêtes SQL directes) |
|
||||
|
||||
**Endpoints** :
|
||||
**Endpoints publics** :
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| `GET` | `/api/ideas` | Liste des contributions acceptées |
|
||||
| `POST` | `/api/ideas` | Soumet une nouvelle contribution |
|
||||
| `GET` | `/api/ideas/stats` | Statistiques (acceptées/refusées) |
|
||||
| `GET` | `/api/synthesis` | Texte de synthèse actuel |
|
||||
| `GET` | `/health` | Health check |
|
||||
| `GET` | `/api/healthz` | Health check |
|
||||
| `GET` | `/api/ideas` | Contributions globales acceptées |
|
||||
| `POST` | `/api/ideas` | Soumet une contribution globale |
|
||||
| `GET` | `/api/synthesis` | Synthèse globale |
|
||||
| `GET` | `/api/stats/public` | Statistiques publiques globales |
|
||||
| `POST` | `/api/consent` | Enregistre le consentement RGPD |
|
||||
| `GET` | `/api/contributions` | Contributions globales paginées (transparence) |
|
||||
| `GET` | `/api/contributions/export/json` | Export JSON contributions globales |
|
||||
| `GET` | `/api/contributions/export/csv` | Export CSV contributions globales |
|
||||
| `POST` | `/api/ideas/<id>/flag` | Signale une contribution |
|
||||
| `GET` | `/api/consultations` | Liste des consultations actives |
|
||||
| `GET` | `/api/consultations/<slug>` | Détails + stats d'une consultation |
|
||||
| `GET` | `/api/consultations/<slug>/synthesis` | Synthèse d'une consultation |
|
||||
| `GET` | `/api/consultations/<slug>/contributions` | Contributions d'une consultation paginées |
|
||||
| `POST` | `/api/consultations/<slug>/ideas` | Soumet une contribution à une consultation |
|
||||
| `GET` | `/api/consultations/<slug>/export/print` | HTML pour impression/PDF |
|
||||
|
||||
**Réponses** : JSON camelCase, statuts HTTP standard.
|
||||
|
||||
@@ -177,6 +193,26 @@ Deux appels distincts à l'API Mistral (compatible OpenAI SDK) :
|
||||
|
||||
---
|
||||
|
||||
### 3.5 Consultations ciblées (P5)
|
||||
|
||||
Le mode consultation permet à un organisateur (mairie, asso, collectif) de créer un espace thématique ciblé, accessible via `/consultation/<slug>`.
|
||||
|
||||
**Caractéristiques** :
|
||||
- Fenêtre temporelle (`starts_at`, `ends_at`) avec fermeture automatique (boucle arrière-plan, 60 s)
|
||||
- Séparation stricte : `consultation_id IS NULL` = contribution globale (page d'accueil)
|
||||
- Synthèse indépendante par consultation (même mécanique IA, contexte isolé)
|
||||
- Webhook de clôture : POST JSON avec synthèse finale vers `webhook_url` configuré
|
||||
- Export HTML/PDF : `GET /api/consultations/<slug>/export/print`
|
||||
- Gestion complète dans le panel admin (onglet Consultations)
|
||||
|
||||
**Boucle auto-fermeture** :
|
||||
```
|
||||
threading.Thread(target=_autoclose_consultations_loop, daemon=True).start()
|
||||
# → time.sleep(60) → get_consultations_to_autoclose() → close_consultation() → _trigger_webhook()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 Base de données — PostgreSQL
|
||||
|
||||
**Table `ideas`** :
|
||||
@@ -190,6 +226,7 @@ Deux appels distincts à l'API Mistral (compatible OpenAI SDK) :
|
||||
| `rejection_reason` | TEXT | Motif de refus (nullable) |
|
||||
| `legal_basis` | TEXT | Base légale du refus (nullable) |
|
||||
| `created_at` | TIMESTAMPTZ | Horodatage de soumission |
|
||||
| `consultation_id` | INTEGER FK | Clé étrangère vers `consultations` (NULL = global) |
|
||||
|
||||
**Table `synthesis`** :
|
||||
|
||||
@@ -199,6 +236,24 @@ Deux appels distincts à l'API Mistral (compatible OpenAI SDK) :
|
||||
| `text` | TEXT | Dernier texte de synthèse |
|
||||
| `idea_count` | INTEGER | Nombre de contributions intégrées |
|
||||
| `updated_at` | TIMESTAMPTZ | Dernière mise à jour |
|
||||
| `consultation_id` | INTEGER FK | Clé étrangère vers `consultations` (NULL = synthèse globale) |
|
||||
|
||||
**Table `consultations`** :
|
||||
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| `id` | SERIAL PK | Identifiant |
|
||||
| `slug` | VARCHAR(100) UNIQUE | Identifiant URL (`/consultation/<slug>`) |
|
||||
| `title` | VARCHAR(200) | Titre affiché |
|
||||
| `subject` | TEXT | Description de la thématique |
|
||||
| `intro_message` | TEXT | Message d'introduction (nullable) |
|
||||
| `organizer_name` | VARCHAR(200) | Nom de l'organisme (nullable) |
|
||||
| `organizer_logo_url` | TEXT | URL logo HTTPS (nullable) |
|
||||
| `starts_at` | TIMESTAMPTZ | Date d'ouverture (défaut : NOW()) |
|
||||
| `ends_at` | TIMESTAMPTZ | Date de fermeture automatique (nullable) |
|
||||
| `closed_at` | TIMESTAMPTZ | Date de fermeture effective (nullable) |
|
||||
| `webhook_url` | TEXT | URL du webhook de clôture (nullable) |
|
||||
| `created_at` | TIMESTAMPTZ | Création |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+57
-4
@@ -1,7 +1,7 @@
|
||||
# Document d'Exploitation — La Voix du Peuple
|
||||
|
||||
**Version** : 1.4
|
||||
**Date** : Avril 2026
|
||||
**Version** : 1.7
|
||||
**Date** : Mai 2026
|
||||
|
||||
---
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| 1.4 | Avril 2026 | Synchronisation Gitea sécurisée — `GITEA_TOKEN` + `scripts/push-gitea.sh` |
|
||||
| 1.5 | Avril 2026 | Droit pénal français intégré dans le filtre IA (16 sections) |
|
||||
| 1.6 | Avril 2026 | Panel admin sécurisé (`/admin`), signalement public, export CSV |
|
||||
| 1.7 | Mai 2026 | P5 — Mode consultation ciblée (table `consultations`, webhook, auto-fermeture, panel admin) |
|
||||
|
||||
---
|
||||
|
||||
@@ -405,14 +406,66 @@ Sur la page d'accueil, chaque contribution affiche un bouton **Signaler** au sur
|
||||
| `POST` | `/api/admin/ideas/<id>/unflag` | Retirer le signalement |
|
||||
| `POST` | `/api/admin/synthesis/regenerate` | Forcer la régénération |
|
||||
| `GET` | `/api/admin/export/csv` | Export CSV (toutes les contributions) |
|
||||
| `GET` | `/api/admin/consultations` | Liste toutes les consultations avec stats |
|
||||
| `POST` | `/api/admin/consultations` | Crée une consultation (`{slug, title, subject, ...}`) |
|
||||
| `POST` | `/api/admin/consultations/<slug>/close` | Ferme manuellement + déclenche webhook |
|
||||
| `DELETE` | `/api/admin/consultations/<slug>` | Supprime une consultation et ses données |
|
||||
|
||||
---
|
||||
|
||||
## 17. Contacts et ressources
|
||||
## 17. Mode consultation ciblée
|
||||
|
||||
### Créer une consultation (panel admin)
|
||||
|
||||
1. Ouvrir `/admin` → onglet **Consultations** → **Nouvelle consultation**
|
||||
2. Remplir : slug (URL-safe, ex. `budget-mairie-2026`), titre, sujet
|
||||
3. Optionnel : message d'introduction, organisateur, logo HTTPS, dates, webhook
|
||||
4. Les dates sont interprétées comme UTC
|
||||
5. La consultation est immédiatement accessible sur `/consultation/<slug>`
|
||||
|
||||
### Fermeture automatique
|
||||
|
||||
La fermeture automatique est gérée par un thread arrière-plan (toutes les 60 secondes).
|
||||
Si `ends_at` est défini et dépassé, la consultation est fermée automatiquement et le webhook déclenché.
|
||||
|
||||
```bash
|
||||
# Vérifier les consultations actives
|
||||
psql "$DATABASE_URL" -c "SELECT slug, title, starts_at, ends_at, closed_at FROM consultations;"
|
||||
```
|
||||
|
||||
### Webhook de clôture
|
||||
|
||||
Si `webhook_url` est configuré, un POST JSON est envoyé à la clôture :
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "consultation_closed",
|
||||
"consultation": {
|
||||
"slug": "budget-mairie-2026",
|
||||
"title": "Budget participatif 2026",
|
||||
"closedAt": "2026-06-30T18:00:00+00:00"
|
||||
},
|
||||
"synthesis": {
|
||||
"text": "...",
|
||||
"ideaCount": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Export PDF d'une consultation
|
||||
|
||||
```
|
||||
GET /api/consultations/<slug>/export/print
|
||||
```
|
||||
|
||||
Retourne une page HTML auto-imprimante. Accessible depuis le bouton **Exporter PDF** sur la page de consultation, ou directement en navigateur.
|
||||
|
||||
---
|
||||
|
||||
## 18. Contacts et ressources
|
||||
|
||||
- Documentation Mistral : https://docs.mistral.ai
|
||||
- PostgreSQL : https://www.postgresql.org/docs/
|
||||
- Flask : https://flask.palletsprojects.com
|
||||
- qrcode.react : https://github.com/zpao/qrcode.react
|
||||
- Architecture : `docs/DAT.md`
|
||||
- Synchronisation Gitea : `docs/GITEA_TUTO.md`
|
||||
|
||||
Reference in New Issue
Block a user