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
@@ -0,0 +1,209 @@
|
||||
# Pipeline de veille — installation, clés, dépannage
|
||||
|
||||
Le pipeline fait deux choses : **remplir** la base depuis le corpus de recherche
|
||||
(`make seed`), puis la **tenir à jour** depuis les sources officielles
|
||||
(`make update`). Il n'écrit jamais dans `data/input/`, qui est en lecture seule.
|
||||
|
||||
---
|
||||
|
||||
## 1. Installation
|
||||
|
||||
```bash
|
||||
uv venv --python 3.12
|
||||
uv pip install -e ".[dev]"
|
||||
cp .env.example .env # puis renseigner les clés, voir §2
|
||||
make seed # remplit data/veille.db
|
||||
```
|
||||
|
||||
`make seed` est idempotent : le relancer ne duplique rien. Pour repartir d'une
|
||||
base vierge : `make reseed`.
|
||||
|
||||
Le compte-rendu affiché en fin de seed est le vrai livrable : il dit combien de
|
||||
textes sont entrés, combien de citations ont été résolues, et — surtout — ce qui
|
||||
manque.
|
||||
|
||||
---
|
||||
|
||||
## 2. Clés d'API
|
||||
|
||||
### Légifrance, via PISTE
|
||||
|
||||
Créez un compte sur [piste.gouv.fr](https://piste.gouv.fr), déclarez une
|
||||
application, et **abonnez-la à l'API « Légifrance »**.
|
||||
|
||||
> **Le piège à connaître.** Une application PISTE affiche *deux* couples de
|
||||
> valeurs. Seul le couple **OAuth** fonctionne ici :
|
||||
>
|
||||
> | Ce que PISTE affiche | À mettre dans `.env` |
|
||||
> |---|---|
|
||||
> | **Client ID** / **Client secret** | ✅ `LEGIFRANCE_CLIENT_ID` / `LEGIFRANCE_CLIENT_SECRET` |
|
||||
> | API key / API key secret | ❌ produit `invalid_client` à la demande de jeton |
|
||||
>
|
||||
> Vérifié le 25 juillet 2026 sur un compte réel : la clé d'API est refusée par
|
||||
> le point d'accès OAuth, quelle que soit la méthode d'authentification tentée
|
||||
> (corps de requête, en-tête Basic, avec ou sans `scope`).
|
||||
|
||||
Second piège : **les identifiants de production ne fonctionnent pas en bac à
|
||||
sable.** `sandbox` exige une application distincte, déclarée sur l'environnement
|
||||
de test. Laissez `LEGIFRANCE_ENV=prod` sauf si vous avez explicitement créé
|
||||
cette seconde application.
|
||||
|
||||
Vérification en une commande :
|
||||
|
||||
```bash
|
||||
.venv/bin/python -c "
|
||||
from pipeline.collecteurs.http import ClientHttp
|
||||
from pipeline.collecteurs.legifrance import CollecteurLegifrance
|
||||
with ClientHttp() as c:
|
||||
col = CollecteurLegifrance(c, numeros_a_verifier=['2026-491'])
|
||||
print(col.est_disponible())
|
||||
print(col.collecter().textes)
|
||||
"
|
||||
```
|
||||
|
||||
### Aucune autre clé n'est nécessaire
|
||||
|
||||
Le Sénat et le Conseil constitutionnel sont interrogés sans authentification.
|
||||
|
||||
---
|
||||
|
||||
## 3. Ce qui marche sans clé PISTE
|
||||
|
||||
C'est le point important : **le pipeline reste utile sans Légifrance.**
|
||||
|
||||
| Collecteur | Sans clé PISTE | Avec clé PISTE |
|
||||
|---|---|---|
|
||||
| `legifrance` | ⛔ désactivé, motif affiché dans le rapport | ✅ vérifie chaque numéro de loi en base |
|
||||
| `senat` | ✅ liste chronologique des lois promulguées | ✅ idem, sert de contrôle croisé |
|
||||
| `conseil_constitutionnel` | ✅ affaires en instance + décisions rendues | ✅ idem |
|
||||
|
||||
Ce qui est **perdu** sans clé : la confirmation de l'intitulé officiel et de la
|
||||
date de signature directement au Journal officiel, et l'identifiant JORF de
|
||||
chaque texte. Ce qui est **conservé** : la détection des promulgations
|
||||
nouvelles (par le Sénat) et le suivi des décisions constitutionnelles — soit
|
||||
l'essentiel de ce qui fait bouger un statut.
|
||||
|
||||
Le rapport de run indique explicitement quand un collecteur est ignoré :
|
||||
|
||||
```
|
||||
Collecteurs
|
||||
legifrance ignoré — LEGIFRANCE_CLIENT_ID absent de .env — repli sur
|
||||
l'Assemblée nationale, le Sénat et le Conseil constitutionnel
|
||||
senat 31 texte(s), 0 décision(s) (0.4 s)
|
||||
```
|
||||
|
||||
### Une URL du cahier des charges est morte
|
||||
|
||||
Le §5.1 indique `senat.fr/lois/index.html` : cette adresse renvoie **404** depuis
|
||||
la refonte du site. L'équivalent fonctionnel, utilisé par le collecteur, est
|
||||
`senat.fr/dossiers-legislatifs/lois-promulguees.html`.
|
||||
|
||||
De même, `assemblee-nationale.fr/dyn/actualites-accueil/promulgations-de-lois`
|
||||
renvoie 404. La liste du Sénat couvrant l'intégralité des lois promulguées, elle
|
||||
tient lieu de source unique pour les promulgations.
|
||||
|
||||
---
|
||||
|
||||
## 4. Utilisation
|
||||
|
||||
```bash
|
||||
make update-dry # collecte réelle, aucune écriture, rapport des écarts
|
||||
make update # collecte et applique
|
||||
```
|
||||
|
||||
**Toujours commencer par `--dry-run`.** Le rapport liste chaque écart avec sa
|
||||
nature, son ancienne et sa nouvelle valeur :
|
||||
|
||||
```
|
||||
Écarts détectés : 11
|
||||
ajouts : 7
|
||||
modifications : 4
|
||||
signalements : 0
|
||||
|
||||
[ajout ] (nouveau) 2026-103 · LOI n° 2026-103 du 19 février 2026 de finances
|
||||
[decision_cc ] ppl-aide-a-mourir 2026-910 DC · date_saisine : — → 2026-07-16
|
||||
[decision_cc ] loi-2026-650 2026-908 DC · date_decision : — → 2026-07-23
|
||||
```
|
||||
|
||||
Options utiles :
|
||||
|
||||
| Option | Effet |
|
||||
|---|---|
|
||||
| `--dry-run` | collecte réelle, aucune écriture |
|
||||
| `--hors-ligne` | n'utilise que le cache HTTP, sans accès réseau |
|
||||
| `--verbeux` | journalisation détaillée, requête par requête |
|
||||
|
||||
---
|
||||
|
||||
## 5. Automatisation
|
||||
|
||||
### systemd (recommandé)
|
||||
|
||||
```bash
|
||||
sudo cp systemd/veille-legislative.{service,timer} /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now veille-legislative.timer
|
||||
systemctl list-timers veille-legislative.timer
|
||||
```
|
||||
|
||||
Cadence : 6 h et 18 h tous les jours, plus un passage le dimanche à 9 h pour
|
||||
rattraper les publications de fin de semaine. `RandomizedDelaySec=900` évite de
|
||||
frapper Légifrance à la seconde ronde. `Persistent=true` rattrape le passage
|
||||
manqué si la machine était éteinte.
|
||||
|
||||
### cron (repli documenté)
|
||||
|
||||
```cron
|
||||
0 6,18 * * * cd /opt/veille-legislative && .venv/bin/python -m pipeline.update >> /var/log/veille.log 2>&1
|
||||
0 9 * * 0 cd /opt/veille-legislative && .venv/bin/python -m pipeline.update >> /var/log/veille.log 2>&1
|
||||
```
|
||||
|
||||
Le repli cron perd le durcissement de sécurité des unités systemd et la reprise
|
||||
des passages manqués.
|
||||
|
||||
---
|
||||
|
||||
## 6. Dépannage
|
||||
|
||||
| Symptôme | Cause probable | Remède |
|
||||
|---|---|---|
|
||||
| `invalid_client` sur le jeton | clé d'API utilisée au lieu du couple OAuth | voir §2 |
|
||||
| `invalid_client` avec le bon couple | application non abonnée à l'API Légifrance | demander l'abonnement sur piste.gouv.fr, compter 24-48 h |
|
||||
| 403 sur `/search` alors que le jeton est obtenu | abonnement en cours de propagation | réessayer plus tard |
|
||||
| `Base introuvable` | seed jamais lancé | `make seed` |
|
||||
| `no such column: t.rowid` | base créée avant la migration 005 | `make db-init` (les migrations s'appliquent seules) |
|
||||
| Collecte lente | premier passage, cache vide | normal : ~25 s pour 27 numéros, quasi instantané ensuite |
|
||||
| Un collecteur en échec | site indisponible ou structure changée | le run continue ; vérifier le rapport et les fixtures de `tests/fixtures/` |
|
||||
|
||||
### Le cache HTTP
|
||||
|
||||
Les réponses sont mises en cache dans `data/cache_http/` pendant
|
||||
`VEILLE_CACHE_TTL_H` heures (6 par défaut). Pour forcer une collecte fraîche :
|
||||
|
||||
```bash
|
||||
rm -rf data/cache_http
|
||||
```
|
||||
|
||||
### Sauvegarde
|
||||
|
||||
La base est un fichier unique. Sauvegarder revient à le copier — mais **jamais
|
||||
pendant un passage**, le mode WAL laissant des fichiers annexes :
|
||||
|
||||
```bash
|
||||
sqlite3 data/veille.db ".backup data/veille-$(date +%F).db"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Tests
|
||||
|
||||
```bash
|
||||
make test # pytest avec couverture
|
||||
make lint # ruff
|
||||
```
|
||||
|
||||
Aucun test ne touche au réseau : les collecteurs sont éprouvés sur des fixtures
|
||||
HTML figées dans `tests/fixtures/`, capturées le 25 juillet 2026. **Si un test
|
||||
de fixture échoue après une mise à jour, c'est le site officiel qui a changé de
|
||||
structure** — c'est exactement ce que ces tests servent à détecter. Recapturez
|
||||
alors la fixture et adaptez le parseur.
|
||||
Reference in New Issue
Block a user