Files
download-dati-centraline/CLAUDE.md
T
2026-05-07 21:18:13 +02:00

8.6 KiB

CLAUDE.md — Portale Centraline 37100lab

Lingua

Tutto il progetto deve essere in italiano: commenti nel codice, messaggi all'utente, testo dell'interfaccia, documentazione interna, messaggi di errore, label dei pulsanti, intestazioni CSV.

Fanno eccezione — e restano in inglese — i nomi di variabili, funzioni, componenti, file, classi CSS, e qualsiasi termine tecnico consolidato (es. fetch, endpoint, build, download, timestamp).


Panoramica

Portale web statico (solo frontend, nessun backend) per visualizzare le centraline di monitoraggio ambientale di 37100lab e scaricare i dati storici in formato CSV.

Stack: Svelte + Vite. Output finale: cartella dist/ da servire con qualsiasi web server statico.


Setup iniziale

Usare il template Svelte ufficiale di Vite. I comandi di sviluppo standard sono dev, build e preview.


Struttura del progetto

portale-centraline/
├── CLAUDE.md
├── package.json
├── vite.config.js
├── index.html
└── src/
    ├── App.svelte
    ├── lib/
    │   ├── api.js          # fetch verso l'API
    │   ├── timezone.js     # conversione UTC → Europe/Rome, iterazione giorni
    │   └── csv.js          # costruzione CSV e download
    └── components/
        ├── StationList.svelte
        ├── StationRow.svelte
        └── DownloadProgress.svelte

API

Base URL: http://37100lab.it:8101

GET /api/campagne_map_data — lista centraline

Risposta: GeoJSON. Le centraline si trovano in .features[].properties.

Campi rilevanti di ogni .properties:

Campo Tipo Note
title string Nome della centralina
pk string ID numerico, arriva come stringa
created_at ISO 8601 UTC Data/ora di creazione
ended_at string o null Data/ora di fine; null se ancora attiva

Il campo pk va convertito a numero intero. Le centraline vanno ordinate per ID decrescente.

GET /api/get_measurements/{id}?day=YYYY-M-D — misurazioni giornaliere

  • {id}: ID numerico della centralina
  • day: data in Europe/Rome, formato YYYY-M-D senza zero-padding
    • 2026-5-6
    • 2026-05-06

La risposta contiene una chiave misure con un array di oggetti. Ogni oggetto ha:

Campo Tipo Descrizione
temp number Temperatura (°C)
umid number Umidità relativa (%)
pm10 number PM10 (µg/m³)
time ISO UTC Timestamp misurazione

misure può essere un array vuoto se il giorno non ha dati.


Formato CSV

"Data","Temperatura","Umidità","PM10"
"05/05/2026 22:00:53.7",13.45,89.22,16.3
"05/05/2026 22:01:53.9",13.45,89.02,16.1
"05/05/2026 22:02:54.1",13.47,88.88,15.5
Caratteristica Valore
Encoding UTF-8 con BOM (\uFEFF) — necessario per Excel italiano
Separatore virgola
Intestazioni tra doppi apici, prima riga
Colonna Data stringa tra doppi apici, formato DD/MM/YYYY HH:MM:SS.s
Colonne numeriche senza apici, valori originali (non arrotondare)
Timestamp convertito da UTC a Europe/Rome (gestisce ora legale)

Nome file: {NomeCentralina}_({id})_{YYYY-MM-DD}.csv Esempio: FabLab_Mantova_(178)_2026-05-07.csv Sostituire spazi con _, rimuovere caratteri non alfanumerici eccetto _.


Moduli

lib/api.js

Contiene due funzioni: una per caricare la lista delle centraline e una per caricare le misurazioni di un singolo giorno. La seconda non deve mai lanciare eccezioni — in caso di errore HTTP o di rete restituisce un array vuoto, così da non interrompere il loop di download.

lib/timezone.js

Gestisce tutto ciò che riguarda date e timezone. Non usare librerie esterne: Intl.DateTimeFormat con timeZone: 'Europe/Rome' è sufficiente e gestisce automaticamente il cambio ora legale (CET/CEST).

Contiene tre funzioni:

  • formatTimestampRome(isoString): converte un timestamp ISO UTC nella stringa formattata per il CSV (DD/MM/YYYY HH:MM:SS.s). I millisecondi vengono troncati al primo decimale con Math.floor, non arrotondati: 706ms → .7. I millisecondi sono indipendenti dal timezone e si leggono direttamente dall'oggetto Date.

  • formatDayParam(date): restituisce la data locale Europe/Rome nel formato YYYY-M-D senza zero-padding, da usare come parametro day nelle chiamate API. Usare Intl.DateTimeFormat con month: 'numeric' e day: 'numeric' in formatToParts — in questo modo i valori non hanno zero-padding.

  • getDayRange(startIsoUtc, endIsoUtc): restituisce un array di oggetti Date, uno per ogni giorno dal giorno di startIsoUtc al giorno di endIsoUtc (estremi inclusi), calcolati in Europe/Rome. Se endIsoUtc è null, il limite superiore è oggi. Usare mezzogiorno UTC (T12:00:00Z) come ancora per ogni cursore giornaliero: mezzogiorno UTC corrisponde alle 13:00 o 14:00 a Roma, quindi non attraversa mai un cambio di data locale neanche al cambio ora legale.

lib/csv.js

Contiene tre funzioni: una per costruire la stringa CSV da un array di misurazioni (con BOM), una per triggerare il download nel browser, e una per costruire il nome del file.


Componenti

App.svelte

Componente radice. Al montaggio carica la lista delle centraline e gestisce i tre stati: caricamento, errore, lista pronta.

StationList.svelte

Riceve l'array di centraline e le mostra in una tabella con colonne: ID, nome, data di creazione (formattata in Europe/Rome), stato (attiva/terminata), pulsante download.

StationRow.svelte

Gestisce la riga di una singola centralina e contiene tutta la logica di download:

  1. Calcola il range di giorni con getDayRange. Le centraline attive (ended_at: null) usano oggi come limite superiore; le centraline terminate (ended_at valorizzato) usano ended_at come limite superiore.
  2. Itera i giorni sequenzialmente (non in parallelo) per non sovraccaricare l'API.
  3. Per ogni giorno chiama fetchDay e accumula le misurazioni.
  4. Aggiorna il contatore di progresso ad ogni giorno completato.
  5. Supporta la cancellazione: controlla un flag cancelled ad ogni iterazione; se attivo, interrompe il loop senza scaricare nulla.
  6. Al termine, se ci sono misurazioni e il download non è stato annullato, genera e scarica il CSV.

DownloadProgress.svelte

Mostra il progresso durante il download: giorno corrente, totale giorni e percentuale. Ha un pulsante "Annulla" che emette un evento verso StationRow.


Edge case e comportamenti attesi

Situazione Comportamento
Giorno senza misurazioni (misure: []) Ignorare, continuare con il giorno successivo
Errore HTTP su un singolo giorno fetchDay restituisce [], ignorare e continuare
Download annullato dall'utente Il loop si interrompe al controllo del flag, nessun file scaricato
Nessuna misurazione dopo tutti i giorni Non generare il CSV, nessun download
Centralina attiva (ended_at: null) Range: da created_at a oggi in Europe/Rome
Centralina terminata (ended_at valorizzato) Range: da created_at a ended_at in Europe/Rome
Nome centralina con caratteri speciali Sanitizzare per il nome file: spazi → _, resto rimosso
Errore nel caricamento della lista App.svelte mostra un messaggio di errore all'utente

Design

Il portale riguarda dati ambientali e scientifici raccolti sul campo da stazioni fisiche. L'estetica deve riflettere questo contesto: strumentale, precisa, leggibile. Scegliere una direzione stilistica netta — non generica. Evitare palette pastello, font di sistema e layout cookie-cutter. Curare tipografia, spaziatura e la gerarchia visiva della tabella.