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 centralinaday: data inEurope/Rome, formatoYYYY-M-Dsenza 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 conMath.floor, non arrotondati:706ms → .7. I millisecondi sono indipendenti dal timezone e si leggono direttamente dall'oggettoDate. -
formatDayParam(date): restituisce la data locale Europe/Rome nel formatoYYYY-M-Dsenza zero-padding, da usare come parametrodaynelle chiamate API. UsareIntl.DateTimeFormatconmonth: 'numeric'eday: 'numeric'informatToParts— in questo modo i valori non hanno zero-padding. -
getDayRange(startIsoUtc, endIsoUtc): restituisce un array di oggettiDate, uno per ogni giorno dal giorno distartIsoUtcal giorno diendIsoUtc(estremi inclusi), calcolati inEurope/Rome. SeendIsoUtcè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:
- Calcola il range di giorni con
getDayRange. Le centraline attive (ended_at: null) usano oggi come limite superiore; le centraline terminate (ended_atvalorizzato) usanoended_atcome limite superiore. - Itera i giorni sequenzialmente (non in parallelo) per non sovraccaricare l'API.
- Per ogni giorno chiama
fetchDaye accumula le misurazioni. - Aggiorna il contatore di progresso ad ogni giorno completato.
- Supporta la cancellazione: controlla un flag
cancelledad ogni iterazione; se attivo, interrompe il loop senza scaricare nulla. - 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.