# 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.