From ea69712bd961fba6543b4a4368f62b3f2b76caa7 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 24 Apr 2026 15:37:26 -0500 Subject: [PATCH] fix(weather): reconcile_hrrr_statuses also flips OCONUS path points to :unavailable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of the "81,980 / 81,994" display that wouldn't advance: 14 contacts had pos1 inside the HRRR CONUS grid but pos2 or the great-circle midpoint landed outside it (50°N+ into Canada, mid- Atlantic, Pacific, Caribbean, Alaska). The Rust hrrr-point-worker silently returns no profile for OCONUS points, so `hrrr_data_fully_present?/1` never evaluated true and the contact sat `:queued` forever. The existing ContactWeatherEnqueueWorker.mark_hrrr_status!/3 already flips to `:unavailable` on empty job lists via NarrClient.in_coverage? + Grid.contains?(pos1), but those checks only considered pos1 — not the full contact_path_points list. When pos1 was in-grid but a downstream point was not, the contact entered `:queued` and the reconciler couldn't rescue it. `reconcile_hrrr_statuses/0` now sweeps `:queued` contacts into two buckets: - ANY path point OCONUS → `:unavailable` (matches what the enqueuer would emit had it known about the downstream points); - else, all points present → `:complete`. Applied once in prod to 14 stuck contacts; cron path picks up any new occurrences going forward. --- lib/microwaveprop/weather.ex | 49 +++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/lib/microwaveprop/weather.ex b/lib/microwaveprop/weather.ex index bab5bed9..36bc4638 100644 --- a/lib/microwaveprop/weather.ex +++ b/lib/microwaveprop/weather.ex @@ -3,6 +3,7 @@ defmodule Microwaveprop.Weather do import Ecto.Query + alias Microwaveprop.Propagation.Grid alias Microwaveprop.Propagation.ProfilesFile alias Microwaveprop.Radio alias Microwaveprop.Radio.Contact @@ -302,10 +303,19 @@ defmodule Microwaveprop.Weather do end @doc """ - Flip `contacts.hrrr_status` from `:queued` to `:complete` for every - contact whose HRRR data is fully present (all path points at the - rounded HRRR hour and grid cell). Same shape as - `reconcile_iemre_statuses/0`. + Reconcile `contacts.hrrr_status` for every `:queued` contact: + + * flips to `:complete` when every path point has an hrrr_profiles + row at the rounded HRRR hour; + * flips to `:unavailable` when any path point falls outside the + HRRR CONUS grid (pos2 or the great-circle midpoint can drift + into Canada, the mid-Atlantic, or the Pacific even when pos1 is + stateside — the Rust hrrr-point-worker returns no profile for + those points and the contact would otherwise sit `:queued` + forever). + + Returns `{:ok, n}` where `n` is the total number of contacts advanced + (completed + oconus). """ @spec reconcile_hrrr_statuses() :: {:ok, non_neg_integer()} def reconcile_hrrr_statuses do @@ -316,15 +326,30 @@ defmodule Microwaveprop.Weather do |> where([c], c.hrrr_status == :queued and not is_nil(c.pos1)) |> Repo.all() - to_complete = Enum.filter(queued, &hrrr_data_fully_present?/1) + {oconus, rest} = Enum.split_with(queued, &contact_has_oconus_path_point?/1) + to_complete = Enum.filter(rest, &hrrr_data_fully_present?/1) - if to_complete == [] do - {:ok, 0} - else - ids = Enum.map(to_complete, & &1.id) - _ = Radio.set_enrichment_status!(ids, :hrrr_status, :complete) - {:ok, length(to_complete)} - end + _ = + if oconus != [] do + ids = Enum.map(oconus, & &1.id) + Radio.set_enrichment_status!(ids, :hrrr_status, :unavailable) + end + + _ = + if to_complete != [] do + ids = Enum.map(to_complete, & &1.id) + Radio.set_enrichment_status!(ids, :hrrr_status, :complete) + end + + {:ok, length(oconus) + length(to_complete)} + end + + defp contact_has_oconus_path_point?(contact) do + contact + |> Radio.contact_path_points() + |> Enum.any?(fn {lat, lon} -> + not Grid.contains?(%{"lat" => lat, "lon" => lon}) + end) end @doc """