From 3208a40f14aa772e42dcf0c9d6c91bfa536d88b0 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Tue, 27 Jan 2026 17:31:48 +1100 Subject: [PATCH] fix forecast data --- cmd/ingestd/web.go | 2 +- internal/db/series.go | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/cmd/ingestd/web.go b/cmd/ingestd/web.go index b44d49a..e676171 100644 --- a/cmd/ingestd/web.go +++ b/cmd/ingestd/web.go @@ -147,7 +147,7 @@ func (s *webServer) handleDashboard(w http.ResponseWriter, r *http.Request) { return } - forecast, err := s.db.ForecastSeriesLatest(r.Context(), s.site.Name, s.model) + forecast, err := s.db.ForecastSeriesRange(r.Context(), s.site.Name, s.model, start, end) if err != nil { http.Error(w, "failed to query forecast", http.StatusInternalServerError) log.Printf("web dashboard forecast error: %v", err) diff --git a/internal/db/series.go b/internal/db/series.go index 10148b5..e62f21c 100644 --- a/internal/db/series.go +++ b/internal/db/series.go @@ -223,6 +223,65 @@ func (d *DB) ForecastSeriesLatest(ctx context.Context, site, model string) (Fore }, nil } +func (d *DB) ForecastSeriesRange(ctx context.Context, site, model string, start, end time.Time) (ForecastSeries, error) { + rows, err := d.Pool.Query(ctx, ` + SELECT DISTINCT ON (ts) + ts, + retrieved_at, + temp_c, + rh, + pressure_msl_hpa, + wind_m_s, + wind_gust_m_s, + wind_dir_deg, + precip_mm, + cloud_cover + FROM forecast_openmeteo_hourly + WHERE site = $1 AND model = $2 AND ts >= $3 AND ts <= $4 + ORDER BY ts ASC, retrieved_at DESC + `, site, model, start, end) + if err != nil { + return ForecastSeries{}, err + } + defer rows.Close() + + points := make([]ForecastPoint, 0, 256) + var maxRetrieved time.Time + for rows.Next() { + var ( + ts time.Time + retrieved time.Time + temp, rh, msl, wind, gust sql.NullFloat64 + dir, precip, cloud sql.NullFloat64 + ) + if err := rows.Scan(&ts, &retrieved, &temp, &rh, &msl, &wind, &gust, &dir, &precip, &cloud); err != nil { + return ForecastSeries{}, err + } + if retrieved.After(maxRetrieved) { + maxRetrieved = retrieved + } + points = append(points, ForecastPoint{ + TS: ts, + TempC: nullFloatPtr(temp), + RH: nullFloatPtr(rh), + PressureMSLH: nullFloatPtr(msl), + WindMS: nullFloatPtr(wind), + WindGustMS: nullFloatPtr(gust), + WindDirDeg: nullFloatPtr(dir), + PrecipMM: nullFloatPtr(precip), + CloudCover: nullFloatPtr(cloud), + }) + } + if rows.Err() != nil { + return ForecastSeries{}, rows.Err() + } + + return ForecastSeries{ + RetrievedAt: maxRetrieved, + Points: points, + }, nil +} + func nullFloatPtr(v sql.NullFloat64) *float64 { if !v.Valid { return nil