improve charts and logs
This commit is contained in:
@@ -429,7 +429,6 @@ function renderDashboard(data) {
|
|||||||
const fcWinds = forecast.map((p) => p.wind_m_s);
|
const fcWinds = forecast.map((p) => p.wind_m_s);
|
||||||
const fcGusts = forecast.map((p) => p.wind_gust_m_s);
|
const fcGusts = forecast.map((p) => p.wind_gust_m_s);
|
||||||
const fcRH = forecast.map((p) => p.rh);
|
const fcRH = forecast.map((p) => p.rh);
|
||||||
const fcPrecip = forecast.map((p) => p.precip_mm);
|
|
||||||
|
|
||||||
const obsTempSummary = minMax(obsTemps);
|
const obsTempSummary = minMax(obsTemps);
|
||||||
const obsWindSummary = minMax(obsWinds);
|
const obsWindSummary = minMax(obsWinds);
|
||||||
@@ -580,16 +579,6 @@ function renderDashboard(data) {
|
|||||||
};
|
};
|
||||||
upsertChart("chart-rain", rainChart);
|
upsertChart("chart-rain", rainChart);
|
||||||
|
|
||||||
const precipChart = {
|
|
||||||
type: "bar",
|
|
||||||
data: {
|
|
||||||
datasets: [
|
|
||||||
{ label: "forecast precip mm", data: series(forecast, "precip_mm"), backgroundColor: colors.precip },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
options: baseOptions(range),
|
|
||||||
};
|
|
||||||
upsertChart("chart-precip", precipChart);
|
|
||||||
|
|
||||||
updateSingleChartMode();
|
updateSingleChartMode();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,15 +155,6 @@
|
|||||||
<canvas id="chart-rain"></canvas>
|
<canvas id="chart-rain"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart-card wide" data-chart="chart-precip">
|
|
||||||
<div class="chart-header">
|
|
||||||
<div class="chart-title">Precipitation (forecast)</div>
|
|
||||||
<button class="chart-link" data-chart="chart-precip" title="Copy chart link">Share</button>
|
|
||||||
</div>
|
|
||||||
<div class="chart-canvas">
|
|
||||||
<canvas id="chart-precip"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type ForecastPoint struct {
|
|||||||
WindGustMS *float64 `json:"wind_gust_m_s,omitempty"`
|
WindGustMS *float64 `json:"wind_gust_m_s,omitempty"`
|
||||||
WindDirDeg *float64 `json:"wind_dir_deg,omitempty"`
|
WindDirDeg *float64 `json:"wind_dir_deg,omitempty"`
|
||||||
PrecipMM *float64 `json:"precip_mm,omitempty"`
|
PrecipMM *float64 `json:"precip_mm,omitempty"`
|
||||||
|
PrecipProb *float64 `json:"precip_prob,omitempty"`
|
||||||
CloudCover *float64 `json:"cloud_cover,omitempty"`
|
CloudCover *float64 `json:"cloud_cover,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +196,7 @@ func (d *DB) ForecastSeriesLatest(ctx context.Context, site, model string) (Fore
|
|||||||
wind_gust_m_s,
|
wind_gust_m_s,
|
||||||
wind_dir_deg,
|
wind_dir_deg,
|
||||||
precip_mm,
|
precip_mm,
|
||||||
|
precip_prob,
|
||||||
cloud_cover
|
cloud_cover
|
||||||
FROM forecast_openmeteo_hourly
|
FROM forecast_openmeteo_hourly
|
||||||
WHERE site = $1 AND model = $2 AND retrieved_at = $3
|
WHERE site = $1 AND model = $2 AND retrieved_at = $3
|
||||||
@@ -210,9 +212,9 @@ func (d *DB) ForecastSeriesLatest(ctx context.Context, site, model string) (Fore
|
|||||||
var (
|
var (
|
||||||
ts time.Time
|
ts time.Time
|
||||||
temp, rh, msl, wind, gust sql.NullFloat64
|
temp, rh, msl, wind, gust sql.NullFloat64
|
||||||
dir, precip, cloud sql.NullFloat64
|
dir, precip, prob, cloud sql.NullFloat64
|
||||||
)
|
)
|
||||||
if err := rows.Scan(&ts, &temp, &rh, &msl, &wind, &gust, &dir, &precip, &cloud); err != nil {
|
if err := rows.Scan(&ts, &temp, &rh, &msl, &wind, &gust, &dir, &precip, &prob, &cloud); err != nil {
|
||||||
return ForecastSeries{}, err
|
return ForecastSeries{}, err
|
||||||
}
|
}
|
||||||
points = append(points, ForecastPoint{
|
points = append(points, ForecastPoint{
|
||||||
@@ -224,6 +226,7 @@ func (d *DB) ForecastSeriesLatest(ctx context.Context, site, model string) (Fore
|
|||||||
WindGustMS: nullFloatPtr(gust),
|
WindGustMS: nullFloatPtr(gust),
|
||||||
WindDirDeg: nullFloatPtr(dir),
|
WindDirDeg: nullFloatPtr(dir),
|
||||||
PrecipMM: nullFloatPtr(precip),
|
PrecipMM: nullFloatPtr(precip),
|
||||||
|
PrecipProb: nullFloatPtr(prob),
|
||||||
CloudCover: nullFloatPtr(cloud),
|
CloudCover: nullFloatPtr(cloud),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -249,6 +252,7 @@ func (d *DB) ForecastSeriesRange(ctx context.Context, site, model string, start,
|
|||||||
wind_gust_m_s,
|
wind_gust_m_s,
|
||||||
wind_dir_deg,
|
wind_dir_deg,
|
||||||
precip_mm,
|
precip_mm,
|
||||||
|
precip_prob,
|
||||||
cloud_cover
|
cloud_cover
|
||||||
FROM forecast_openmeteo_hourly
|
FROM forecast_openmeteo_hourly
|
||||||
WHERE site = $1 AND model = $2 AND ts >= $3 AND ts <= $4
|
WHERE site = $1 AND model = $2 AND ts >= $3 AND ts <= $4
|
||||||
@@ -266,9 +270,9 @@ func (d *DB) ForecastSeriesRange(ctx context.Context, site, model string, start,
|
|||||||
ts time.Time
|
ts time.Time
|
||||||
retrieved time.Time
|
retrieved time.Time
|
||||||
temp, rh, msl, wind, gust sql.NullFloat64
|
temp, rh, msl, wind, gust sql.NullFloat64
|
||||||
dir, precip, cloud sql.NullFloat64
|
dir, precip, prob, cloud sql.NullFloat64
|
||||||
)
|
)
|
||||||
if err := rows.Scan(&ts, &retrieved, &temp, &rh, &msl, &wind, &gust, &dir, &precip, &cloud); err != nil {
|
if err := rows.Scan(&ts, &retrieved, &temp, &rh, &msl, &wind, &gust, &dir, &precip, &prob, &cloud); err != nil {
|
||||||
return ForecastSeries{}, err
|
return ForecastSeries{}, err
|
||||||
}
|
}
|
||||||
if retrieved.After(maxRetrieved) {
|
if retrieved.After(maxRetrieved) {
|
||||||
@@ -283,6 +287,7 @@ func (d *DB) ForecastSeriesRange(ctx context.Context, site, model string, start,
|
|||||||
WindGustMS: nullFloatPtr(gust),
|
WindGustMS: nullFloatPtr(gust),
|
||||||
WindDirDeg: nullFloatPtr(dir),
|
WindDirDeg: nullFloatPtr(dir),
|
||||||
PrecipMM: nullFloatPtr(precip),
|
PrecipMM: nullFloatPtr(precip),
|
||||||
|
PrecipProb: nullFloatPtr(prob),
|
||||||
CloudCover: nullFloatPtr(cloud),
|
CloudCover: nullFloatPtr(cloud),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,23 +77,34 @@ func (o *OpenMeteo) Fetch(ctxDone <-chan struct{}, site Site, model string) (*Fo
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
const maxLogBody = int64(65536)
|
||||||
|
bodyBuf, _ := io.ReadAll(io.LimitReader(resp.Body, maxLogBody+1))
|
||||||
|
truncated := int64(len(bodyBuf)) > maxLogBody
|
||||||
|
if truncated {
|
||||||
|
bodyBuf = bodyBuf[:maxLogBody]
|
||||||
|
}
|
||||||
|
if truncated {
|
||||||
|
log.Printf("open-meteo response status=%d bytes=%d truncated=true body=%s", resp.StatusCode, len(bodyBuf), string(bodyBuf))
|
||||||
|
} else {
|
||||||
|
log.Printf("open-meteo response status=%d bytes=%d body=%s", resp.StatusCode, len(bodyBuf), string(bodyBuf))
|
||||||
|
}
|
||||||
|
|
||||||
if resp.StatusCode/100 != 2 {
|
if resp.StatusCode/100 != 2 {
|
||||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
|
||||||
var apiErr struct {
|
var apiErr struct {
|
||||||
Error bool `json:"error"`
|
Error bool `json:"error"`
|
||||||
Reason string `json:"reason"`
|
Reason string `json:"reason"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(body, &apiErr); err == nil && apiErr.Reason != "" {
|
if err := json.Unmarshal(bodyBuf, &apiErr); err == nil && apiErr.Reason != "" {
|
||||||
return nil, fmt.Errorf("open-meteo HTTP %d: %s", resp.StatusCode, apiErr.Reason)
|
return nil, fmt.Errorf("open-meteo HTTP %d: %s", resp.StatusCode, apiErr.Reason)
|
||||||
}
|
}
|
||||||
if len(body) > 0 {
|
if len(bodyBuf) > 0 {
|
||||||
return nil, fmt.Errorf("open-meteo HTTP %d: %s", resp.StatusCode, string(body))
|
return nil, fmt.Errorf("open-meteo HTTP %d: %s", resp.StatusCode, string(bodyBuf))
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("open-meteo HTTP %d", resp.StatusCode)
|
return nil, fmt.Errorf("open-meteo HTTP %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
var raw map[string]any
|
var raw map[string]any
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil {
|
if err := json.Unmarshal(bodyBuf, &raw); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user