fix(weather): default selected time to current hour, not clock-closest
Past minute 30 of the wall clock the next forecast hour is closer to utc_now() than the current hour, so the picker would land on (e.g.) 19:00Z when the user is at 18:43Z. HRRR has 19:00Z, but the HRDPS chain only seeds the *current* hour (18:00Z), so the dual-source map showed an empty Canadian half whenever we crossed the half-hour mark. Switch to "most-recent valid_time <= now", with a fallback to the closest future when nothing on disk is yet at-or-before now (cold start). Hoist the picker into a public function so it can be unit- tested deterministically — anchoring the integration test on DateTime.utc_now() truncated to the hour would have only verified the new behavior past minute 30. /weather-ca delegates to the same picker so the two maps agree on "current."
This commit is contained in:
parent
040ebf9d3d
commit
b37692592b
3 changed files with 49 additions and 8 deletions
|
|
@ -97,11 +97,11 @@ defmodule MicrowavepropWeb.WeatherCaMapLive do
|
|||
|
||||
defp parse_float_param(_value, _min, _max, default), do: default
|
||||
|
||||
defp pick_initial_valid_time([]), do: nil
|
||||
|
||||
# Delegate to WeatherMapLive's picker so the two maps agree on what
|
||||
# "current" means: most-recent <=now, with a fall-through to the
|
||||
# closest future when nothing is yet at-or-before now.
|
||||
defp pick_initial_valid_time(valid_times) do
|
||||
now = DateTime.utc_now()
|
||||
Enum.min_by(valid_times, fn vt -> abs(DateTime.diff(vt, now, :second)) end)
|
||||
MicrowavepropWeb.WeatherMapLive.pick_initial_valid_time(valid_times)
|
||||
end
|
||||
|
||||
# Don't filter by recency. HRDPS publishes 4×/day with a 3-4h
|
||||
|
|
|
|||
|
|
@ -113,11 +113,25 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
|||
|
||||
defp parse_float_param(_value, _min, _max, default), do: default
|
||||
|
||||
defp pick_initial_valid_time([]), do: nil
|
||||
@doc false
|
||||
# Public for direct unit testing: anchoring an integration test on a
|
||||
# truncated `utc_now()` would only assert the new behavior past minute
|
||||
# 30 of the wall clock.
|
||||
def pick_initial_valid_time(valid_times, now \\ DateTime.utc_now())
|
||||
def pick_initial_valid_time([], _now), do: nil
|
||||
|
||||
defp pick_initial_valid_time(valid_times) do
|
||||
now = DateTime.utc_now()
|
||||
Enum.min_by(valid_times, fn vt -> abs(DateTime.diff(vt, now, :second)) end)
|
||||
def pick_initial_valid_time(valid_times, now) do
|
||||
# Prefer the most recent valid_time that is <= now (the "current
|
||||
# hour" — what HRRR analysis just delivered and what the HRDPS chain
|
||||
# seeds when fired this hour). Fall back to the closest absolute
|
||||
# match only when nothing on disk is yet at-or-before now.
|
||||
past_or_equal =
|
||||
Enum.filter(valid_times, fn vt -> DateTime.compare(vt, now) != :gt end)
|
||||
|
||||
case past_or_equal do
|
||||
[] -> Enum.min_by(valid_times, fn vt -> abs(DateTime.diff(vt, now, :second)) end)
|
||||
list -> Enum.max_by(list, &DateTime.to_unix/1)
|
||||
end
|
||||
end
|
||||
|
||||
# Drop valid_times older than one hour ago. The HRRR scalar cache can
|
||||
|
|
|
|||
|
|
@ -131,6 +131,33 @@ defmodule MicrowavepropWeb.WeatherMapLiveTest do
|
|||
refute html =~ ~s(data-selected-time="#{future_iso}")
|
||||
end
|
||||
|
||||
test "pick_initial_valid_time prefers the most-recent <=now over a clock-closer future hour" do
|
||||
# Deterministic unit test against the picker (the integration
|
||||
# version would only fail past minute 30 of the wall clock).
|
||||
now = ~U[2026-04-30 18:43:00Z]
|
||||
current_hour = ~U[2026-04-30 18:00:00Z]
|
||||
next_hour = ~U[2026-04-30 19:00:00Z]
|
||||
|
||||
# next_hour is closer to `now` (17 min vs 43 min), but the user
|
||||
# wants the *current hour* so HRRR-analysis-now and HRDPS-seeded-
|
||||
# for-now align on the dual-source map.
|
||||
assert MicrowavepropWeb.WeatherMapLive.pick_initial_valid_time(
|
||||
[current_hour, next_hour],
|
||||
now
|
||||
) == current_hour
|
||||
end
|
||||
|
||||
test "pick_initial_valid_time falls back to the closest future when nothing is <=now" do
|
||||
# Cold-start case: no analysis-or-past data on disk yet, only
|
||||
# forecast hours. The picker should still return something so the
|
||||
# map is non-blank.
|
||||
now = ~U[2026-04-30 18:00:00Z]
|
||||
f1 = ~U[2026-04-30 19:00:00Z]
|
||||
f6 = ~U[2026-05-01 00:00:00Z]
|
||||
|
||||
assert MicrowavepropWeb.WeatherMapLive.pick_initial_valid_time([f1, f6], now) == f1
|
||||
end
|
||||
|
||||
test "select_time pushes an overlay refresh for the selected hour", %{conn: conn} do
|
||||
base =
|
||||
DateTime.utc_now()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue