(04) Systèmes / Étude de cas
aria-er
Les chiffres concordent avec ceux de l'ARPAE.
aria-er est une plateforme de données sur la qualité de l'air en Émilie-Romagne : open data de l'ARPAE et météo d'Open-Meteo, de l'ingestion au tableau de bord. Cette page montre comment elle est construite et comment ses résultats ont été vérifiés.
- Début
- Septembre 2026
- Version
- 1.2.0, octobre 2026
- Équipe
- Un développeur
- Code
- Public, MIT
Rust · dbt · DuckDB · BigQuery · Dagster · Streamlit · Terraform · Docker
Le problème
L'ARPAE publie les mesures de la qualité de l'air de l'Émilie-Romagne en open data, mais pas sous une forme qu'on peut interroger : valeurs horaires et journalières réparties sur plusieurs services, provisoires pendant des semaines puis corrigées, identifiants de stations qui changent d'un fichier à l'autre.
Les questions sont simples : combien de fois chaque station a dépassé les limites légales, si l'air s'améliore, combien le trafic ajoute à la pollution de fond, comment le vent et la pluie agissent sur les PM10. Y répondre avec des chiffres fiables, c'est le travail.
Contraintes
- Les valeurs récentes sont corrigées pendant des semaines : chaque exécution retraite une fenêtre de 30 jours et met à jour par station, polluant et heure. Relancer la même fenêtre produit les mêmes fichiers.
- Les horodatages sont stockés en UTC ; jours et années se comptent en UTC+1 toute l'année, comme le fait l'ARPAE.
- Les limites légales sont dans un seed, jamais dans le SQL d'un modèle.
- Les valeurs négatives ou impossibles sont signalées, pas supprimées.
- Les mêmes modèles tournent sur DuckDB en local et sur BigQuery dans le cloud.
Architecture
Quatre composants, chacun avec une seule tâche, reliés par des fichiers et des tables plutôt que par des appels : un binaire Rust écrit du Parquet partitionné, dbt en tire des marts testés, Dagster planifie et contrôle les deux, un tableau de bord ne fait que lire les marts. Chaque ligne ne dépend que de celles du dessous.
dashboard
Streamlit : une requête filtrée par mart, aucune logique métier.
orchestration
Dagster : planification quotidienne, partitions mensuelles, backfills, contrôles de fraîcheur et de complétude.
transform
dbt : staging, intermediate et marts, avec plus de cent tests.
aq-ingest
Workspace Rust : clients ARPAE et Open-Meteo derrière un trait de source, nouvelles tentatives avec backoff.
raw/
Parquet partitionné par année et par mois, chaque partition réécrite de façon atomique, sur disque ou dans Cloud Storage.
Une commande lance tout avec Docker sur un portable ; la cible cloud écrit dans Cloud Storage et construit les mêmes modèles dans BigQuery.
Une journée de données, de bout en bout
La mise à jour quotidienne :
- 01Dagster
À l'heure prévue, il lance la mise à jour du mois en cours, une exécution à la fois.
- 02Ingestor
Il télécharge les 30 derniers jours depuis l'archive de l'ARPAE et la météo depuis Open-Meteo, et réécrit chaque mois touché en un seul fichier Parquet.
- 03dbt
Il normalise les unités, supprime les doublons, fusionne les nouvelles lignes et reconstruit les marts. Un test en échec arrête l'exécution.
- 04Dagster
Ses contrôles avertissent quand la dernière valeur a plus de 48 heures ou quand la complétude baisse.
- 05Tableau de bord
Il lit les marts : dépassements, tendances, trafic face au fond, météo, complétude.
Choix et compromis
- 01
Réécrire une partition, jamais ajouter
Chaque mois est un fichier Parquet, réécrit en entier avec les lignes fusionnées et remplacé de façon atomique. Relancer une fenêtre donne les mêmes octets ; le prix est de réécrire un mois pour changer une heure, peu de chose à cette taille.
- 02
Les règles légales, pas une simple moyenne
Un jour ne compte qu'avec 18 heures valides, la valeur est arrondie avant la comparaison avec la limite, le jour est en UTC+1. Sans l'une de ces règles, les décomptes s'écartaient de ceux de l'ARPAE.
- 03
Signaler, pas supprimer
Les valeurs négatives, nulles ou invraisemblables restent dans les données avec un indicateur, et hors des agrégats. Deux tests d'avertissement les gardent visibles au lieu de les faire disparaître en silence.
- 04
Un seul jeu de modèles, deux entrepôts
Le SQL qui diffère entre DuckDB et BigQuery tient dans quelques macros. Un émulateur vérifie la version BigQuery à chaque pull request, et l'exécution sur un vrai projet Google Cloud a donné des marts identiques.
- 05
Pas de streaming
Les sources publient des heures après la mesure et corrigent pendant des mois, et chaque question est quotidienne ou plus large. Un traitement par lot quotidien suffit ; le streaming n'ajouterait que des pièces mobiles.
Vérifié avec l'ARPAE
Chaque décompte de dépassements a été comparé à ce que publie l'ARPAE. Deux vérifications ont changé le code : les PM10 ne concordaient qu'en arrondissant la valeur journalière avant la comparaison, et l'ozone concordait pour 29 stations sur 43 tant que les fenêtres de 8 heures n'étaient pas calculées sur chaque heure, y compris les heures manquantes.
| Indicateur | ARPAE | aria-er |
|---|---|---|
| Jours de PM10 au-dessus de 50 µg/m³, Porta San Felice, 2025mart_exceedances_yearly | 20 | 20 |
| Jours d'ozone au-dessus de 120 µg/m³, Via Chiarini, 2025mart_exceedances_yearly | 54 | 54 |
| Stations avec les mêmes jours de dépassement des PM10 en 2026mart_exceedances_yearly | 64 | 64 |
| Stations avec les mêmes jours de dépassement de l'ozone en 2026mart_exceedances_yearly | 43 | 43 |
Résultats en direct
Les graphiques ci-dessous lisent l'export qu'aria-er publie chaque matin sur GitHub Pages : les mêmes marts que le tableau de bord, mis à jour par le pipeline lui-même.
Chargement des dernières données…
Tableau de bord et pipeline



La suite
- Un export quotidien des marts publié par le pipeline, pour montrer des résultats à jour sur cette page.
- Les dépassements d'ozone en moyenne sur trois ans, comme le demande la loi, dès que trois années complètes seront chargées.
- Charger l'archive dans une table native BigQuery, si elle dépassait le quota gratuit de lectures.
Code
Le dépôt est public, sous licence MIT. Une seule commande lance tout avec Docker et charge dix ans de données.