Vai al contenuto

(04) Sistemi / Caso di studio

aria-er

I numeri coincidono con quelli di ARPAE.

aria-er è una piattaforma dati sulla qualità dell'aria in Emilia-Romagna: open data ARPAE e meteo di Open-Meteo, dall'ingestion alla dashboard. Questa pagina mostra come è fatta e come sono stati verificati i risultati.

Inizio
Settembre 2026
Versione
1.2.0, ottobre 2026
Team
Uno sviluppatore
Codice
Pubblico, MIT

Rust · dbt · DuckDB · BigQuery · Dagster · Streamlit · Terraform · Docker

Il problema

ARPAE pubblica le misure della qualità dell'aria dell'Emilia-Romagna come open data, ma non in una forma a cui si possano fare domande: valori orari e giornalieri sparsi su più servizi, provvisori per settimane e poi corretti, codici delle stazioni che cambiano da un file all'altro.

Le domande sono semplici: quante volte ogni stazione ha superato i limiti di legge, se l'aria sta migliorando, quanto aggiunge il traffico all'inquinamento di fondo, come vento e pioggia muovono il PM10. Rispondere con numeri di cui fidarsi è il lavoro.

Vincoli

  • I valori recenti vengono corretti per settimane: ogni esecuzione rielabora una finestra di 30 giorni e aggiorna per stazione, inquinante e ora. Rieseguire la stessa finestra produce gli stessi file.
  • I timestamp sono salvati in UTC; giorni e anni si contano in UTC+1 tutto l'anno, come fa ARPAE.
  • I limiti di legge stanno in un seed, mai nell'SQL di un modello.
  • I valori negativi o impossibili vengono segnalati, non scartati.
  • Gli stessi modelli girano su DuckDB in locale e su BigQuery nel cloud.

Architettura

Quattro componenti, ognuno con un compito, collegati da file e tabelle invece che da chiamate: un binario Rust scrive Parquet partizionato, dbt ne ricava mart testati, Dagster pianifica e controlla entrambi, una dashboard legge soltanto i mart. Ogni riga dipende solo da quelle sotto.

  1. dashboard

    Streamlit: una query filtrata per mart, nessuna logica di business.

    orchestration

    Dagster: pianificazione giornaliera, partizioni mensili, backfill, controlli su freschezza e completezza.

  2. transform

    dbt: staging, intermediate e mart, con più di cento test.

  3. aq-ingest

    Workspace Rust: client ARPAE e Open-Meteo dietro un trait di sorgente, retry con backoff.

  4. raw/

    Parquet partizionato per anno e mese, ogni partizione riscritta in modo atomico, su disco o su Cloud Storage.

Un comando avvia tutto con Docker su un portatile; il target cloud scrive su Cloud Storage e costruisce gli stessi modelli su BigQuery.

Un giorno di dati, dall'inizio alla fine

L'aggiornamento giornaliero:

  1. 01Dagster

    All'ora prevista avvia l'aggiornamento del mese corrente, un'esecuzione alla volta.

  2. 02Ingestor

    Scarica gli ultimi 30 giorni dall'archivio ARPAE e il meteo da Open-Meteo, e riscrive ogni mese toccato come un unico file Parquet.

  3. 03dbt

    Normalizza le unità, toglie i duplicati, unisce le righe nuove e ricostruisce i mart. Un test che fallisce ferma l'esecuzione.

  4. 04Dagster

    I suoi controlli avvisano quando l'ultimo valore ha più di 48 ore o quando la completezza cala.

  5. 05Dashboard

    Legge i mart: superamenti, andamenti, traffico contro fondo, meteo, completezza.

Scelte e compromessi

  1. 01

    Riscrivere una partizione, mai accodare

    Ogni mese è un file Parquet, riscritto per intero con le righe unite e sostituito in modo atomico. Rieseguire una finestra dà gli stessi byte; il prezzo è riscrivere un mese per cambiare un'ora, poco a queste dimensioni.

  2. 02

    Le regole di legge, non una media qualsiasi

    Un giorno conta solo con 18 ore valide, il valore si arrotonda prima del confronto con il limite, il giorno è in UTC+1. Togliendo anche una sola di queste regole i conteggi non coincidevano più con quelli di ARPAE.

  3. 03

    Segnalare, non scartare

    I valori negativi, nulli o non plausibili restano nei dati con un segnale e fuori dagli aggregati. Due test di avviso li tengono visibili invece di farli sparire in silenzio.

  4. 04

    Un solo insieme di modelli, due warehouse

    L'SQL che cambia tra DuckDB e BigQuery sta in poche macro. Un emulatore verifica la versione BigQuery a ogni pull request, e l'esecuzione su un vero progetto Google Cloud ha dato mart identici.

  5. 05

    Niente streaming

    Le fonti pubblicano ore dopo la misura e correggono per mesi, e ogni domanda è giornaliera o più ampia. Basta un batch giornaliero; lo streaming aggiungerebbe solo parti in movimento.

Verificato con ARPAE

Ogni conteggio di superamenti è stato confrontato con quello che pubblica ARPAE. Due verifiche hanno cambiato il codice: il PM10 coincideva solo arrotondando il valore giornaliero prima del confronto, e l'ozono coincideva per 29 stazioni su 43 finché le finestre di 8 ore non sono state calcolate su ogni ora, anche quelle mancanti.

Report annuale sui dati 2025 della rete di Bologna e bollettino giornaliero del 12 ottobre 2026.
IndicatoreARPAEaria-er
Giorni di PM10 oltre 50 µg/m³, Porta San Felice, 2025mart_exceedances_yearly2020
Giorni di ozono oltre 120 µg/m³, Via Chiarini, 2025mart_exceedances_yearly5454
Stazioni con gli stessi giorni di superamento del PM10 nel 2026mart_exceedances_yearly6464
Stazioni con gli stessi giorni di superamento dell'ozono nel 2026mart_exceedances_yearly4343

Risultati dal vivo

I grafici qui sotto leggono l'export che aria-er pubblica ogni mattina su GitHub Pages: gli stessi mart della dashboard, aggiornati dalla pipeline stessa.

Caricamento degli ultimi dati…

Dashboard e pipeline

Pagina della dashboard con un grafico a barre dei giorni di superamento del PM10 per anno, dal 2016 al 2026, per tre stazioni di Bologna, e una linea tratteggiata sul massimo di legge di 35.
Superamenti: giorni di PM10 oltre il limite per anno a Bologna, rispetto ai 35 ammessi dalla legge.
Pagina della dashboard con due linee delle medie mensili di NO2 a Bologna dal 2016 al 2026, la stazione da traffico sempre sopra quelle di fondo.
Traffico contro fondo: NO2 mensile a Bologna, la quota di inquinamento che viene dal traffico.
Grafo degli asset in Dagster: l'asset di ingestion alimenta i modelli di staging, poi quelli intermedi, poi cinque mart.
La lineage in Dagster, dall'ingestion ai cinque mart.

Cosa viene dopo

  • Un export giornaliero dei mart pubblicato dalla pipeline, per mostrare i risultati aggiornati in questa pagina.
  • Superamenti dell'ozono come media su tre anni, come chiede la legge, quando saranno caricati tre anni completi.
  • Caricare l'archivio in una tabella nativa di BigQuery, se dovesse superare la quota gratuita di letture.

Codice

Il repository è pubblico, con licenza MIT. Un solo comando avvia tutto con Docker e carica dieci anni di dati.

Leggi il codice su GitHubTorna a Sistemi