All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- seed.py : 3 foyers avec codes fixes (DEVTEST2/3/4, RS/RP/PRO) insérés avant les 363 réels ; existing_codes pré-chargé → zéro collision - page citizen : dev hint mis à jour avec les 3 mêmes codes + profils - CLAUDE.md : reformaté en guide de session Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
101 lines
4.4 KiB
Markdown
101 lines
4.4 KiB
Markdown
# SejeteralO
|
||
|
||
Plateforme de tarification participative de l'eau pour communes françaises.
|
||
Les citoyens votent sur la forme de la courbe tarifaire via des éditeurs Bézier interactifs ;
|
||
le système calcule un tarif à équilibre de recettes en temps réel.
|
||
|
||
## Protocole de début de session
|
||
|
||
1. `git pull --rebase origin main`
|
||
2. Vérifier que `data/` existe à la racine — si absent, signaler avant toute opération
|
||
3. Si l'objectif de la session n'est pas précisé, le demander
|
||
|
||
## Stack
|
||
|
||
- **Frontend** : Nuxt 4 (Vue 3, TypeScript) + Pinia ; package manager : npm ; port dev : 3009
|
||
- **Backend** : Python FastAPI + SQLAlchemy 2.0 async + SQLite (aiosqlite) ; port dev : 8000
|
||
- Déploiement : Docker multi-stage + Traefik (backend + frontend) ; CI Woodpecker
|
||
- Pas d'UnoCSS — CSS vanilla avec variables CSS palettes dans `main.css`
|
||
|
||
## Structure
|
||
|
||
```
|
||
frontend/
|
||
app/
|
||
components/ # composants Vue (dont DisplaySettings.vue — 6 palettes)
|
||
layouts/ # layouts Nuxt
|
||
pages/
|
||
commune/[slug]/index.vue # page principale citoyenne (~1900 lignes)
|
||
composables/
|
||
useApi.ts # wraps fetch avec Bearer token ; usage : api.get<Type>('/path')
|
||
middleware/ # route middleware (auth)
|
||
plugins/
|
||
auth-restore.client.ts # restaure token depuis localStorage au démarrage
|
||
stores/
|
||
auth.ts # token, role, communeSlug
|
||
commune.ts
|
||
utils/
|
||
bezier-math.ts # formule Bézier (Cardano + Newton-Raphson) — miroir du backend
|
||
nuxt.config.ts # port 3009, apiBase via NUXT_PUBLIC_API_BASE (défaut :8000)
|
||
backend/
|
||
app/
|
||
routers/ # 6 routers : auth, communes, tariff, votes, households, content
|
||
engine/
|
||
pricing.py # compute_p0(), compute_tariff(), compute_impacts()
|
||
integrals.py # coefficients α₁, α₂, β₂ (intégrales Bézier)
|
||
median.py # médiane élément par élément des votes
|
||
current_model.py # tarif linéaire de référence
|
||
models/ # Commune, TariffParams, Household, Vote, AdminUser, CommuneContent
|
||
alembic/versions/ # migrations
|
||
tests/
|
||
seed.py # Commune Saoû, 363 foyers, codes auth 8 chars, comptes admin
|
||
data/ # runtime — JAMAIS dans git (voir Données runtime)
|
||
docker/
|
||
docker-compose.yml # backend + frontend (réseau traefik externe)
|
||
backend.Dockerfile
|
||
frontend.Dockerfile
|
||
docker-compose.dev.yml
|
||
Makefile # docker-up, docker-dev
|
||
```
|
||
|
||
## Données runtime (CRITIQUE)
|
||
|
||
- `data/` à la racine : contenu non géré par git, **jamais écrasé par les commits**
|
||
- Volume Docker `backend-data` monté sur `/app` — contient `sejeteralo.db` (SQLite)
|
||
- **Avant toute migration de chemin ou écriture sur data/ ou le volume : demander confirmation**
|
||
- En dev local : la DB SQLite est dans `backend/` (chemin relatif `./sejeteralo.db`)
|
||
- Seed requis après chaque reset DB : `cd backend && python seed.py`
|
||
|
||
## Commandes
|
||
|
||
```bash
|
||
# Backend
|
||
cd backend && . venv/bin/activate
|
||
uvicorn app.main:app --reload --port 8000 --host 0.0.0.0
|
||
python -m pytest tests/ -v
|
||
python seed.py # Saoû, 363 foyers, admin accounts
|
||
|
||
# Frontend
|
||
cd frontend && npm run dev # :3009
|
||
npm run build
|
||
|
||
# Docker
|
||
make docker-up # production
|
||
make docker-dev # dev avec hot-reload
|
||
```
|
||
|
||
## Conventions / pièges
|
||
|
||
- **UI français, code anglais** — "foyer" = household (facturation), "électeur" = voter (vote)
|
||
- **Modèle Bézier deux niveaux** — 6 paramètres citoyens (vinf, a, b, c, d, e) + p0 auto-calculé :
|
||
```
|
||
p0 = (Recettes − Σabo − Σβ₂) / Σ(α₁ + α₂)
|
||
```
|
||
Implémenté deux fois : backend Python (`engine/pricing.py`) et frontend TS (`utils/bezier-math.ts`) — garder synchronisés
|
||
- **Agrégation votes** : médiane élément par élément des votes actifs (pas moyenne)
|
||
- **Graphiques SVG** : axe X inversé (volumes élevés à gauche) — utiliser bindings réactifs `t.*` ou `var(--svg-*)`, jamais de couleurs hex codées en dur dans les SVG
|
||
- **Thème** : 6 palettes via `useState('theme-dark')` ; CSS vars : `--color-primary`, `--color-bg`, `--color-surface`, `--color-text`, `--color-border`, `--svg-plot-bg`, `--svg-grid`, `--svg-text`, `--svg-text-light` ; `.palette-dark` sur `<html>`
|
||
- **Dev hints** : classe `.dev-hint` + `v-if="isDev"` ; les codes auth doivent exister dans la DB seedée
|
||
- **Port backend** : 8000 en local (nuxt.config + uvicorn) — la table globale CLAUDE.md indique 8009 par erreur ; les fichiers de config font foi
|
||
- **Pas de `ssr: true`** — CSR uniquement
|