Files
veye-lalwa/README-pipeline.md
T
Cyber MawonajandClaude Opus 5 34d81c66a8 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>
2026-07-25 22:34:22 -04:00

7.3 KiB

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

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, 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 :

.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

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é)

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é)

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 :

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 :

sqlite3 data/veille.db ".backup data/veille-$(date +%F).db"

7. Tests

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.