Commit graph

15 commits

Author SHA1 Message Date
Florian
9db5d4c204 fix(climato): fiabiliser les jours récents à couverture station partielle
Some checks failed
Deploy info-canicule / deploy (push) Failing after 4s
Les 1-2 derniers jours du flux climato `latest` ont souvent 0-1 station
publiée (parfois TN sans TX), produisant des agrégats département nuls ou
non représentatifs. Symptômes côté page dept : TX/TN vides ou tx==tn à
J-1/J-2 sur le graphe, et moyenne 3j/7j figée (computeAnomaly lisait
climato.days brut et retombait sur les mêmes vieux jours complets).

- climato.ts : exposer la couverture réelle par champ (txN/tnN, optionnels,
  rétro-compatibles avec le cache 24h).
- [code].astro : `_mergedDay` choisit par champ la source fiable —
  climato si couverture >= 50% de la médiane station du dept, sinon SYNOP
  (garde >= 3 obs/jour pour éviter tx==tn), sinon valeur fine, sinon null.
  Le badge anomalie utilise désormais les 7 derniers jours complets
  (finissant J-1) au lieu de climato.days brut, recollant graphe et badge.

Vérifié par simulation sur données réelles (depts 31/75/13) : jours pleins
inchangés (zéro régression), jours récents partiels comblés proprement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 16:04:34 +02:00
Florian
e247005d34 fix(climato): combler le lag de publication via SYNOP 48h
All checks were successful
Deploy info-canicule / deploy (push) Successful in 1m33s
Fetcher 48h d'obs horaires (au lieu de 24h) pour avoir le jour J-1 complet.
Dériver TX/TN journaliers (max/min des températures horaires) et les injecter
dans les fenêtres 7j/30j/1an pour les jours absents de la climato CSV.
Le tab "24h" du graphique reste filtré sur les 24 dernières heures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 12:47:24 +02:00
Florian
a1c6002756 fix(climato): remettre TX en moyenne + fenêtres calendaires jusqu'à aujourd'hui
All checks were successful
Deploy info-canicule / deploy (push) Successful in 1m33s
TX revient en moyenne inter-stations (comme TN/TM) ; la version MAX était
une mauvaise piste. Les vues 7j/30j/1an sont désormais construites sur une
plage calendaire fixe se terminant à aujourd'hui (heure Paris) : les jours
sans données source (lag publication ~1-4j) apparaissent comme points nuls
plutôt que de décaler la fenêtre vers le passé.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 12:40:43 +02:00
Florian
759cda8ea9 fix(vigilance): désactiver onglet J+1 si absent du bulletin
All checks were successful
Deploy info-canicule / deploy (push) Successful in 1m29s
- Ajoute `hasJ1Period()` — source de vérité réelle au lieu de
  s'appuyer sur l'heure de publication (fixe 6h/16h non fiable,
  bulletins constatés à 23h et 00h01).
- Home : `tomorrowAvailable` conditionné à `hasJ1Period(snapshot)`
  en plus de `currentEcheance === 'J'`.
- Dept : `j1Available` pilote la colonne Demain du tableau de
  comparaison et le calcul `tomorrowColor`/`changed`.
- Tooltip J+1 désactivé : suppression de la mention "vers 6h".
- cache.ts / vigilance.ts : `bulletinIsSuperseded` + refresh sync
  via `shouldRevalidateSync` (pré-existant, inclus dans le push).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 01:57:25 +02:00
Florian
29c1151fea fix(dept): numéros urgence en 2×3 (grid-cols-2 mobile, sm:grid-cols-3)
Some checks failed
Deploy info-canicule / deploy (push) Failing after 4s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 23:39:33 +02:00
Florian
003b49c297 feat(design): refonte page département + AnomalyBadge, paliers soutenir
All checks were successful
Deploy info-canicule / deploy (push) Successful in 1m31s
- Page département : nouveau design issu du mockup hi-fi (hero v-block
  coloré, "Que faire ?", autres alertes compactes, section repliable
  "tous au vert", tableau aujourd'hui/demain, actions rapides)
- AnomalyBadge : refonte avec CSS tokens (v-block coloré, grand nombre,
  inline mode supprimé)
- /soutenir : 3 paliers progressifs (10 €, 40 €, 150 €) avec barres de
  progression individuelles et indicateur d'objectif actif

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:29:17 +02:00
Florian
e72f25b33d fix(cache): Cache-Control must-revalidate sur home + dept
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
Le navigateur cachait l ancienne version HTML → utilisateurs voyaient le
bulletin Vigilance précédent (carte du 25 mai 6h) malgré le bon HTML
servi par le serveur (carte du 25 mai 16h). max-age=60 + must-revalidate
force une revalidation conditionnelle à chaque visite > 1 min.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 02:46:20 +02:00
Florian
b342ea7375 perf: fetches parallèles + /api/health expose vigilance freshness
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
- /departement/[code] : Promise.allSettled sur les 3 fetches externes
  (vigilance MF, climato data.gouv, hourly SYNOP). Avant : ~15-20s
  sériel cold-fetch. Après : ~10s max (= temps du plus lent = climato).

- normales (3 ranges) : Promise.all aussi, économise ~30 ms.

- /api/health enrichi avec vigilance.productDatetime + ageSeconds
  pour permettre au cron freshness de checker sans /api/vigilance
  (qui a été supprimé en public).

Pré-requis pour le cron warmup côté infra repo (cf. scripts/cron-warmup-info-canicule.sh).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 02:32:49 +02:00
Florian
0b418bd031 fix(vigilance): écheance dynamique selon l'heure courante
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
Le bulletin MF est publié 2× par jour (~6h et ~16h Paris). Pendant la nuit
(de minuit au prochain bulletin ~6h), on est techniquement dans le J+1 du
bulletin courant, qui correspond pour l'utilisateur à "aujourd'hui réel".

Avant : on affichait toujours J → la nuit, la carte montrait les alertes
"d'hier" alors que l'utilisateur cherche "celles d'aujourd'hui réel".
Symptôme : Florian voit du orange sur vigilance.meteofrance.fr (qui prend
la bonne écheance) mais pas chez nous (qui restait collé sur J).

Fix :
- currentEcheance(snapshot) compare now au end_time du J : si dépassé, J1.
- index.astro + departement.astro utilisent cette écheance pour
  "aujourd'hui". Page dept : "demain" = J1 si on est sur J, vide sinon.
- Labels rendus dynamiques : "Niveau par département — mardi 26 mai" au
  lieu de "(aujourd'hui)" générique.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 02:22:19 +02:00
Florian
5f8b765d79 feat: Vigilance API officielle + tab 1 an + logo SVG + map à 5xl
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
1. lib/vigilance.ts : provider Météo France officielle via DPVigilance/v1
   /cartevigilance/encours. Map vers VigilanceAlert (1 par dept × phen × J/J1)
   en prenant phenomenon_max_color_id (pire de la journée). Filtre les domains
   non-dept (FRA national, sub-côtes XX10, etc).
   - Auto-pick : MF si key dispo, sinon opendatasoft. Override VIGILANCE_PROVIDER.
   - Fallback opendatasoft si MF échoue (cache key différent pour pas polluer).
   - Devrait fixer le lag de plusieurs heures observé sur Opendatasoft.

2. lib/climato.ts : fetch latest + previous (~4 MB compressé), garder 365j max
   en cache 24h. Permet l'onglet "1 an" sur la page dept.

3. TemperatureChartInteractive : onglet "1 an" (apparaît si > 30j dispos),
   série days365 + normales365 sérialisés au SSR.

4. Base.astro : logo header et footer utilisent <img src="/favicon.svg">
   au lieu d'un emoji 🌡️.

5. index.astro : wrapper map repassé à container-tight (max-w-5xl) — la version
   1400px était trop grande, on revient à la largeur du reste du site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 01:57:35 +02:00
Florian
cb8d111a12 feat: normales journalières + dedupe SYNOP + carte plus large
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
3 fixes en un :

1. lib/observations.ts : dedupe par validity_time sur le parse hourly.
   L API MF SYNOP retourne chaque obs en doublon exact sur les ranges
   multi-step (bug gateway WSO2). Constaté 7/8 paires identiques sur 24h.

2. Normales 1991-2020 passées de mensuelles à journalières (lissées 7j).
   - scripts/build-normales.mjs : agrégation par day-of-year (1..366)
     avec moving average ±3j pour stabiliser le bruit jour-à-jour.
   - src/data/normales.json : 2.28 MB (vs 78 KB), 96 × 366 entrées.
   - lib/normales.ts : normaleForDay/Date + normalesForRange, computeAnomaly
     compare maintenant chaque jour observé à SA normale (pas à la moyenne
     du mois) → bien plus précis sur les jours de transition mensuelle.
   - TemperatureChartInteractive : overlay normales en COURBE qui suit
     la saison (7j/30j) au lieu d une ligne horizontale unique.
     24h reste ligne horizontale (normale du jour courant).
   - Tooltip 7j/30j ajoute "↳ normale TX X°C (+Y)" pour montrer l écart
     par point.

3. Carte sur la home libérée du container-tight (max-w-5xl = 1024px) :
   wrapper dédié max-w-[1400px] → carte ~37% plus grande sur PC large.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 01:43:10 +02:00
Florian
2c4d91ce2f feat: graph T° interactif + widget iframe + MF auth + E2E Playwright
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
Graph T° (TemperatureChartInteractive.astro) :
- Onglets 24 h / 7 j / 30 j (toggle JS, séries serialisées au SSR)
- Hover vertical line + tooltip valeurs
- Overlay normales mois en pointillé (TX orange, TN bleu)
- Onglet 24 h dispo seulement si l'API MF a répondu (best-effort)

Météo France OAuth2 (lib/meteofrance-auth.ts + observations.ts) :
- client_credentials avec refresh auto, cache token Valkey
- Fallback METEOFRANCE_STATIC_TOKEN pour debug
- /synop endpoint pour 24h horaires par station SYNOP du dept
- Mapping dept → station SYNOP la plus proche (src/data/stations-synop.json)
- En attente de creds : SDK skip silencieusement, l'onglet 24h n'apparaît pas

Widget iframe (/embed/dept/[code] + /embed doc) :
- Layout minimal sans header/footer global
- Réutilisable via iframe avec une ligne
- Page /embed avec snippet copier-coller + aperçu live

Tests E2E Playwright (tests/e2e/) :
- home (carte 96 paths, tooltip dept, navigation)
- api (health, vigilance, vigilance/dept)
- departement (tabs période, DROM notice, 404)
- static pages (a-propos, mentions, dependances, soutenir, conseils, embed)
- embed widget (rendu minimal, headers X-Frame OK)
- 20+ tests, run via pnpm test:e2e (live) ou test:e2e:local

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 00:14:05 +02:00
Florian
c2b489f9b9 feat: normales saisonnières 1991-2020 + AnomalyBadge
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
- scripts/build-normales.mjs : agrégation TN/TX mensuelles par dept sur
  la période WMO 1991-2020 depuis les fichiers Q_<DEPT>_previous-1950-2024.
  Output src/data/normales.json (78 KB, committé). Run annuel max.
- Corse : Météo France utilise le code historique "20" (avant split 2A/2B
  en 1976), donc 2A et 2B partagent la même normale issue de Q_20_*.
- src/lib/normales.ts : computeAnomaly() qui moyenne TX/TN des 7 derniers
  jours, compare à la normale du mois, calcule l'écart en °C et en σ,
  catégorise (normal / warm / cool / anomaly_warm / anomaly_cool /
  extreme_warm / extreme_cool / unknown).
- src/components/AnomalyBadge.astro : badge coloré (vert/jaune/orange/rouge)
  visible sur /departement/[code] juste au-dessus du graphe T°.
  Différencie "il fait chaud" de "il fait anormalement chaud pour ce mois".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 21:34:04 +02:00
Florian
87d173684c feat: OG png + sentry + dept api + drom notice + registre canicule
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
Quick wins :
- public/og-image.png (1200x630, via sharp depuis le SVG, build via pnpm build)
  SVG ne fonctionne pas pour Open Graph (Slack/Discord/X/FB).
- @sentry/astro intégré conditionnellement (skip si SENTRY_DSN absent → no-op).
  GIT_COMMIT_SHA en var pour le release tag dans GlitchTip si voulu.
- /api/vigilance/dept/[code] : JSON enrichi (phenomenon label + color name)
  pour J et J1, CORS *, Cache-Control 5min. 404 si code unknown.
- JSON-LD enrichi : @graph WebSite + Service avec isBasedOn Dataset + license LOv2.
- Lien retour vigilance.meteofrance.fr visible sous la carte.

DROM (97x / 976) :
- 5 entrées ajoutées dans departements.ts (région "DROM").
- /departement/[code] DROM : bannière "Vigilance Outre-mer non couverte par
  cette source open data" + bouton vers vigilance.meteofrance.fr.
- Home : ligne sous la carte listant les 5 DROM + lien retour.
- L'API /api/vigilance/dept/<DROM> retourne quand même un JSON 200 (arrays vides).

Registre canicule :
- Page /conseils/registre-canicule : qui, quoi, comment s'inscrire au CCAS.
- Numéro vert 0 800 06 66 66.
- Bannière mise en avant en haut de /conseils.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 20:59:11 +02:00
Florian
e075d963bc init: info-canicule MVP (Vigilance + climato + conseils)
Astro 5 SSR + ioredis cache Valkey, déployable sur shared-net.
- Vigilance temps réel via Opendatasoft (no-auth, LOv2)
- Carte SVG des 96 départements (gregoiredavid/france-geojson)
- Climato T° 30j par dept (CSV.GZ Météo France, cache 24h)
- Conseils officiels par phénomène (7 types Vigilance)
- /api/health (UptimeRobot) + /api/vigilance (JSON public CORS *)
- Dockerfile multi-stage, CI Forgejo deploy.yml (pattern Reteno)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 18:17:56 +02:00