Phases 6 et 7 : application SvelteKit, PWA, systemd, documentation
Application — rendu serveur sur adapter-node, conforme au §3bis : - tableau de bord, recherche à facettes, fiche détaillée, calendrier - /a-propos et /methode prérendues ; tout le reste en rendu serveur - API JSON ouverte : /api/textes, /api/textes/[slug], /api/echeances - recherche FTS5 avec surlignage et classement bm25 pondéré (le titre pèse dix fois plus que les points clés) - facettes recalculées sur le résultat filtré par les AUTRES facettes : cocher un thème doit recompter les statuts encore disponibles, sinon les compteurs mentent Identité OKI sobrifiée : tokens en CSS vanilla, thème sombre par défaut et clair opt-in, or comme seule couleur d'action, flag-bar une fois par écran, polices Archivo/Inter auto-hébergées. Politique de sécurité de contenu interdisant toute requête tierce. Les statuts ne reposent jamais sur la seule couleur : libellé en toutes lettres et pastille de forme distincte. Budget respecté très largement : 6,6 Ko de JavaScript sur l'accueil (3 Ko compressés) pour un plafond de 100 Ko. Les faits clés — AFD à 500 €, saisine 2026-915 DC, statut — sont lisibles sans JavaScript. Trois défauts trouvés en testant l'application, pas en relisant le code : - v_textes est une vue, et une vue SQLite n'a pas de rowid : la jointure avec l'index plein texte échouait (migration 005) - ppl-montagne remontait en « pertinence forte » alors que le corpus dit « sans portée pour la Guadeloupe » — la cotation comptait le mot sans lire la négation. Idem pour pjl-logement et accord-globe. Corrigé par une lecture du voisinage, avec quatre tests de non-régression. - les libellés d'affichage étaient importés depuis /server dans un composant, ce que SvelteKit interdit à raison PWA : service worker réseau-d'abord pour les pages, cache-d'abord pour le coffre du build. Une veille législative ne doit pas servir une page périmée quand le réseau répond — une date de promulgation change tout. Les réponses issues du cache portent un en-tête qui le dit. systemd : timer du pipeline (6 h, 18 h, dimanche 9 h, avec dispersion et rattrapage), application activée par socket avec mise en sommeil à 300 s. Unités durcies, validées par systemd-analyze. Documentation : README d'installation sur Debian/Ubuntu vierge, README-pipeline avec le piège des deux couples d'identifiants PISTE, et scripts/verifier-conformite.sh qui contrôle mécaniquement le §3bis et le §8. 172 tests, npm run check à 0 erreur, make verifier au vert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
feb5544599
commit
34d81c66a8
@@ -1,49 +1,259 @@
|
||||
# Veille législative Guadeloupe
|
||||
# Veille législative Gwadloup
|
||||
|
||||
Plateforme de veille sur la production législative française — lois, projets et
|
||||
propositions de loi, ordonnances, décisions du Conseil constitutionnel — analysée
|
||||
sous l'angle des **libertés des individus, des associations et des entreprises**,
|
||||
avec un zoom Guadeloupe et outre-mer.
|
||||
|
||||
Deux composantes indissociables :
|
||||
|
||||
1. **`pipeline/`** — automatisation Python. Ingère le corpus de recherche
|
||||
(date d'arrêté : 25 juillet 2026, périmètre 1ᵉʳ mai → 30 septembre 2026) puis
|
||||
met la base à jour depuis les sources officielles.
|
||||
2. **`web/`** — application SvelteKit rendue côté serveur : recherche plein
|
||||
texte, filtres à facettes partageables, calendrier des échéances, fiches
|
||||
détaillées avec sources et extraits verbatim.
|
||||
propositions de loi, ordonnances, décisions du Conseil constitutionnel — lue
|
||||
sous l'angle des **libertés des individus, des associations et des
|
||||
entreprises**, avec un zoom Guadeloupe et outre-mer.
|
||||
|
||||
> **Aucune donnée législative n'est inventée.** Chaque fait porte sa source
|
||||
> (URL, date, extrait), son niveau de confiance et son fichier d'origine. Ce qui
|
||||
> n'est pas établi est marqué « à vérifier » plutôt que deviné.
|
||||
> (adresse, date, extrait verbatim), son niveau de confiance et son fichier
|
||||
> d'origine. Ce qui n'est pas établi est marqué « à vérifier », avec son motif
|
||||
> affiché sur la fiche.
|
||||
|
||||
## État d'avancement
|
||||
---
|
||||
|
||||
| Phase | Objet | Statut |
|
||||
|---|---|---|
|
||||
| 0 | Lecture du corpus, plan d'implémentation | ✅ `PLAN-IMPLEMENTATION.md` |
|
||||
| 1 | Socle : schéma SQLite + FTS5, modèles | ✅ |
|
||||
| 2 | Parseur des tableaux récapitulatifs | ✅ 52 textes |
|
||||
| 3 | Parseurs des rapports de dimension, citations | ✅ 360 sources |
|
||||
| 4 | Enrichissement et tests d'acceptation | ✅ 7/7 critères §7 |
|
||||
| 5 | Collecteurs officiels, run de mise à jour | 🚧 |
|
||||
| 6 | Application web | ⏳ |
|
||||
| 7 | PWA, systemd, documentation d'installation | ⏳ |
|
||||
|
||||
## Ce que contient la base après `make seed`
|
||||
## Ce que contient la base
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Textes législatifs | **52** (27 promulgués, 7 devant le Conseil constitutionnel, 10 en navette, 5 déposés, 2 annoncés, 1 validé) |
|
||||
| Sources vérifiables | **360**, toutes avec URL — aucun texte sans lien |
|
||||
| Événements de timeline | 83 |
|
||||
| Textes législatifs | **52** — 27 promulgués, 7 devant le Conseil constitutionnel, 10 en navette, 5 déposés, 2 annoncés, 1 validé |
|
||||
| Sources vérifiables | **360**, toutes avec adresse — aucun texte sans lien |
|
||||
| Événements de parcours | 83 |
|
||||
| Affaires du Conseil constitutionnel | 12 |
|
||||
| Analyses transversales | 7 |
|
||||
| Échéances du calendrier | 9 |
|
||||
| Marqueurs de citation résolus | 100 % (332/332), dont 93 % avec un lien |
|
||||
| Échéances de calendrier | 9 |
|
||||
| Marqueurs de citation résolus | **100 %** (332/332), dont 93 % avec un lien |
|
||||
|
||||
Les valeurs déduites — 16 cotations Guadeloupe, 17 ventilations d'impact — sont
|
||||
marquées « à vérifier » avec leur motif, et affichées comme telles.
|
||||
signalées comme telles, avec leur motif.
|
||||
|
||||
L'installation complète sur Debian/Ubuntu vierge est documentée en phase 7.
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
veille_legislative_971/
|
||||
├── data/input/ corpus de recherche — LECTURE SEULE, jamais modifié
|
||||
├── pipeline/ Python 3.12 : parseurs, collecteurs, run de veille
|
||||
├── web/ SvelteKit 2 + Svelte 5, rendu serveur sur adapter-node
|
||||
├── systemd/ 4 unités : timer du pipeline, socket + service de l'app
|
||||
├── tests/ pytest, fixtures HTML figées — aucun test ne va au réseau
|
||||
└── scripts/ contrôles de conformité
|
||||
```
|
||||
|
||||
Une seule base SQLite, `data/veille.db`. La recherche plein texte est assurée
|
||||
par SQLite lui-même (FTS5). Aucun service externe n'est nécessaire au
|
||||
fonctionnement : ni moteur de recherche, ni réseau de diffusion, ni traceur.
|
||||
|
||||
---
|
||||
|
||||
## Installation sur Debian ou Ubuntu vierge
|
||||
|
||||
Testé sur Debian 13 et Ubuntu 24.04.
|
||||
|
||||
### 1. Dépendances système
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y git curl build-essential sqlite3 ca-certificates
|
||||
```
|
||||
|
||||
### 2. Node.js ≥ 20.6
|
||||
|
||||
La version 20.6 est le minimum : l'application utilise `node --env-file`, qui
|
||||
n'existe pas avant.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt install -y nodejs
|
||||
node --version # doit afficher v20.6 ou plus
|
||||
```
|
||||
|
||||
### 3. Python 3.12 et uv
|
||||
|
||||
```bash
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
uv python install 3.12
|
||||
```
|
||||
|
||||
### 4. Le projet
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/veille-legislative
|
||||
sudo chown "$USER:$USER" /opt/veille-legislative
|
||||
git clone <adresse-du-depot> /opt/veille-legislative
|
||||
cd /opt/veille-legislative
|
||||
|
||||
make install # environnement Python + dépendances npm
|
||||
cp .env.example .env
|
||||
$EDITOR .env # renseigner les identifiants PISTE — voir README-pipeline.md
|
||||
```
|
||||
|
||||
### 5. Remplir la base et construire l'application
|
||||
|
||||
```bash
|
||||
make seed # → 52 textes, 360 sources
|
||||
cd web && npm run polices && cd .. # copie les polices depuis npm
|
||||
make build
|
||||
make verifier # contrôles de conformité
|
||||
```
|
||||
|
||||
### 6. Vérifier localement
|
||||
|
||||
```bash
|
||||
cd web
|
||||
HOST=127.0.0.1 PORT=3971 VEILLE_DB=/opt/veille-legislative/data/veille.db node build
|
||||
```
|
||||
|
||||
Puis, dans un autre terminal :
|
||||
|
||||
```bash
|
||||
curl -sI http://127.0.0.1:3971/ | head -1 # HTTP/1.1 200 OK
|
||||
curl -s http://127.0.0.1:3971/api/textes | head -c 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mise en service
|
||||
|
||||
### Compte dédié
|
||||
|
||||
```bash
|
||||
sudo useradd --system --home /opt/veille-legislative --shell /usr/sbin/nologin veille
|
||||
sudo chown -R veille:veille /opt/veille-legislative
|
||||
sudo chmod 600 /opt/veille-legislative/.env
|
||||
```
|
||||
|
||||
### Unités systemd
|
||||
|
||||
```bash
|
||||
sudo cp systemd/*.service systemd/*.timer systemd/*.socket /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now veille-web.socket # l'app se réveille à la demande
|
||||
sudo systemctl enable --now veille-legislative.timer # collecte 6 h, 18 h, dimanche 9 h
|
||||
```
|
||||
|
||||
L'application est **activée par socket** : systemd tient le port ouvert et ne
|
||||
démarre Node qu'à la première requête ; sans trafic pendant `IDLE_TIMEOUT`
|
||||
(300 s), elle s'arrête. Sur un site de veille peu fréquenté, l'empreinte mémoire
|
||||
tombe à zéro entre deux visites — ce qui compte sur un serveur à 5 € par mois.
|
||||
|
||||
Avant de démarrer, ajustez `ORIGIN` dans `veille-web.service` : sans lui, les
|
||||
soumissions de formulaire sont refusées derrière un reverse proxy.
|
||||
|
||||
### Reverse proxy
|
||||
|
||||
**Caddy** (le plus court, HTTPS automatique) :
|
||||
|
||||
```caddy
|
||||
veille.example.org {
|
||||
encode gzip zstd
|
||||
reverse_proxy 127.0.0.1:3971
|
||||
}
|
||||
```
|
||||
|
||||
**nginx** :
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name veille.example.org;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript application/json image/svg+xml;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3971;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Avec nginx, remplacez `ORIGIN` par `PROTOCOL_HEADER=x-forwarded-proto` et
|
||||
`HOST_HEADER=x-forwarded-host` dans le service.
|
||||
|
||||
### Vérifier
|
||||
|
||||
```bash
|
||||
systemctl status veille-web.socket
|
||||
systemctl list-timers veille-legislative.timer
|
||||
journalctl -u veille-legislative -n 40
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commandes
|
||||
|
||||
```
|
||||
make install installe pipeline Python et application web
|
||||
make seed remplit la base depuis data/input/
|
||||
make reseed repart d'une base vierge
|
||||
make update-dry collecte réelle, aucune écriture, rapport des écarts
|
||||
make update collecte et applique
|
||||
make test pytest avec couverture
|
||||
make lint ruff
|
||||
make dev serveur de développement (port 5971)
|
||||
make build build de production
|
||||
make check vérification TypeScript
|
||||
make verifier contrôles de conformité au cahier des charges
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dégradations documentées
|
||||
|
||||
| Situation | Conséquence |
|
||||
|---|---|
|
||||
| Sans identifiants PISTE | Le collecteur Légifrance se désactive et l'annonce dans le rapport. Le Sénat et le Conseil constitutionnel prennent le relais : les promulgations et les décisions restent suivies. Voir `README-pipeline.md` §3. |
|
||||
| Une source indisponible | Le collecteur concerné échoue seul ; les autres poursuivent. L'erreur figure au rapport de run. |
|
||||
| Réseau coupé | `make update --hors-ligne` travaille sur le cache HTTP. Côté navigateur, le service worker sert les pages déjà consultées, en signalant qu'elles peuvent être périmées. |
|
||||
| Base absente | L'application refuse de démarrer avec un message indiquant les emplacements essayés. |
|
||||
|
||||
**Deux adresses du cahier des charges sont mortes** et ont été remplacées :
|
||||
`senat.fr/lois/index.html` et
|
||||
`assemblee-nationale.fr/dyn/actualites-accueil/promulgations-de-lois` renvoient
|
||||
toutes deux 404. La liste `senat.fr/dossiers-legislatifs/lois-promulguees.html`
|
||||
couvre l'ensemble des promulgations et tient lieu de source unique.
|
||||
|
||||
---
|
||||
|
||||
## Sauvegarde
|
||||
|
||||
La base est un fichier unique :
|
||||
|
||||
```bash
|
||||
sqlite3 data/veille.db ".backup /sauvegardes/veille-$(date +%F).db"
|
||||
```
|
||||
|
||||
Ne copiez jamais le fichier pendant un passage du pipeline : le mode WAL laisse
|
||||
des fichiers annexes. `.backup` s'en charge correctement.
|
||||
|
||||
---
|
||||
|
||||
## Interface de programmation
|
||||
|
||||
Ouverte, sans clé :
|
||||
|
||||
| Route | Objet |
|
||||
|---|---|
|
||||
| `GET /api/textes` | liste filtrable — `q`, `statut`, `theme`, `type`, `impact`, `guadeloupe`, `confiance`, `from`, `to`, `tri`, `page` |
|
||||
| `GET /api/textes/[identifiant]` | fiche complète, sources et parcours compris |
|
||||
| `GET /api/echeances` | calendrier, avec `?jours=N` en option |
|
||||
|
||||
```bash
|
||||
curl -s 'https://veille.example.org/api/textes?statut=saisie_cc&guadeloupe=forte' | jq '.total'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
AGPL-3.0-or-later. Le corpus de `data/input/` reste la propriété de ses auteurs.
|
||||
|
||||
Si vous réutilisez ces données, citez la source primaire — Journal officiel,
|
||||
assemblées, Conseil constitutionnel — plutôt que cette plateforme : elle n'est
|
||||
qu'un intermédiaire.
|
||||
|
||||
Reference in New Issue
Block a user