refactor(weather): move hrrr_data_fully_present? from enqueuer to Weather
Pure domain check — "does this contact already have HRRR profiles at every path point for its QSO hour" — has no business living in an Oban worker. Weather already owns has_hrrr_profile?/3 and round_to_hrrr_grid/2, so consolidation keeps HRRR knowledge in one context and leaves ContactWeatherEnqueueWorker focused on job coordination.
This commit is contained in:
parent
a81e55e348
commit
baab3c09f5
3 changed files with 99 additions and 17 deletions
|
|
@ -4,9 +4,11 @@ defmodule Microwaveprop.Weather do
|
|||
import Ecto.Query
|
||||
|
||||
alias Microwaveprop.Propagation.ProfilesFile
|
||||
alias Microwaveprop.Radio
|
||||
alias Microwaveprop.Repo
|
||||
alias Microwaveprop.Weather.GefsProfile
|
||||
alias Microwaveprop.Weather.GridCache
|
||||
alias Microwaveprop.Weather.HrrrClient
|
||||
alias Microwaveprop.Weather.HrrrNativeProfile
|
||||
alias Microwaveprop.Weather.HrrrProfile
|
||||
alias Microwaveprop.Weather.IemClient
|
||||
|
|
@ -865,6 +867,33 @@ defmodule Microwaveprop.Weather do
|
|||
|> Repo.exists?()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns `true` when every path point for `contact` (pos1 → midpoint → pos2,
|
||||
or just pos1 for one-endpoint contacts) already has an HRRR profile at the
|
||||
nearest HRRR hour. Lets the enrichment enqueuer skip :queued → :queued
|
||||
churn when the backing data is already present.
|
||||
|
||||
Returns `false` if `pos1` is nil — a contact with no position can't be
|
||||
looked up.
|
||||
"""
|
||||
@spec hrrr_data_fully_present?(map()) :: boolean()
|
||||
def hrrr_data_fully_present?(%{pos1: nil}), do: false
|
||||
|
||||
def hrrr_data_fully_present?(contact) do
|
||||
rounded = HrrrClient.nearest_hrrr_hour(contact.qso_timestamp)
|
||||
|
||||
case Radio.contact_path_points(contact) do
|
||||
[] ->
|
||||
false
|
||||
|
||||
points ->
|
||||
Enum.all?(points, fn {lat, lon} ->
|
||||
{rlat, rlon} = round_to_hrrr_grid(lat, lon)
|
||||
has_hrrr_profile?(rlat, rlon, rounded)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@spec hrrr_for_contact(map()) :: HrrrProfile.t() | nil
|
||||
def hrrr_for_contact(%{pos1: nil}), do: nil
|
||||
|
||||
|
|
@ -1018,7 +1047,7 @@ defmodule Microwaveprop.Weather do
|
|||
|
||||
def hrrr_profiles_for_path(contact) do
|
||||
contact
|
||||
|> Microwaveprop.Radio.contact_path_points()
|
||||
|> Radio.contact_path_points()
|
||||
|> Enum.map(fn {lat, lon} -> find_nearest_hrrr(lat, lon, contact.qso_timestamp) end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
|
@ -1093,7 +1122,7 @@ defmodule Microwaveprop.Weather do
|
|||
|
||||
def narr_profiles_for_path(contact) do
|
||||
contact
|
||||
|> Microwaveprop.Radio.contact_path_points()
|
||||
|> Radio.contact_path_points()
|
||||
|> Enum.map(fn {lat, lon} -> find_nearest_narr(lat, lon, contact.qso_timestamp) end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
|
@ -1261,7 +1290,7 @@ defmodule Microwaveprop.Weather do
|
|||
|
||||
def iemre_for_path(contact) do
|
||||
contact
|
||||
|> Microwaveprop.Radio.contact_path_points()
|
||||
|> Radio.contact_path_points()
|
||||
|> Enum.map(fn {lat, lon} -> find_nearest_iemre(lat, lon, contact.qso_timestamp) end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -88,25 +88,12 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do
|
|||
cond do
|
||||
is_nil(contact.pos1) -> []
|
||||
NarrClient.in_coverage?(contact.qso_timestamp) -> []
|
||||
hrrr_data_fully_present?(contact) -> []
|
||||
Weather.hrrr_data_fully_present?(contact) -> []
|
||||
true -> [contact.id]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp hrrr_data_fully_present?(contact) do
|
||||
alias Microwaveprop.Weather.HrrrClient
|
||||
|
||||
rounded = HrrrClient.nearest_hrrr_hour(contact.qso_timestamp)
|
||||
|
||||
contact
|
||||
|> Radio.contact_path_points()
|
||||
|> Enum.all?(fn {lat, lon} ->
|
||||
{rlat, rlon} = Weather.round_to_hrrr_grid(lat, lon)
|
||||
Weather.has_hrrr_profile?(rlat, rlon, rounded)
|
||||
end)
|
||||
end
|
||||
|
||||
defp mark_enrichment_statuses(contact, types, jobs_by_type) do
|
||||
ids = [contact.id]
|
||||
|
||||
|
|
|
|||
|
|
@ -656,6 +656,72 @@ defmodule Microwaveprop.WeatherTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "hrrr_data_fully_present?/1" do
|
||||
# nearest_hrrr_hour rounds <:30 down; pick 18:15 so it maps to 18:00 to
|
||||
# match the valid_time in @hrrr_profile_attrs.
|
||||
@contact_time ~U[2026-03-28 18:15:00Z]
|
||||
|
||||
test "returns true when a pos1-only contact's snapped point has a profile" do
|
||||
Weather.upsert_hrrr_profile(@hrrr_profile_attrs)
|
||||
|
||||
contact = %{
|
||||
pos1: %{"lat" => 32.9047, "lon" => -97.0382},
|
||||
pos2: nil,
|
||||
qso_timestamp: @contact_time
|
||||
}
|
||||
|
||||
assert Weather.hrrr_data_fully_present?(contact)
|
||||
end
|
||||
|
||||
test "returns false when a pos1-only contact's snapped point has no profile" do
|
||||
contact = %{
|
||||
pos1: %{"lat" => 40.0, "lon" => -80.0},
|
||||
pos2: nil,
|
||||
qso_timestamp: @contact_time
|
||||
}
|
||||
|
||||
refute Weather.hrrr_data_fully_present?(contact)
|
||||
end
|
||||
|
||||
test "returns true only when every path point (endpoint + midpoint + endpoint) has a profile" do
|
||||
# Contact from (32.9, -97.04) to (33.1, -96.96) → midpoint ≈ (33.0, -97.0)
|
||||
for {lat, lon} <- [{32.9, -97.04}, {33.0, -97.0}, {33.1, -96.96}] do
|
||||
Weather.upsert_hrrr_profile(%{@hrrr_profile_attrs | lat: lat, lon: lon})
|
||||
end
|
||||
|
||||
contact = %{
|
||||
pos1: %{"lat" => 32.9, "lon" => -97.04},
|
||||
pos2: %{"lat" => 33.1, "lon" => -96.96},
|
||||
qso_timestamp: @contact_time
|
||||
}
|
||||
|
||||
assert Weather.hrrr_data_fully_present?(contact)
|
||||
end
|
||||
|
||||
test "returns false when the midpoint profile is missing" do
|
||||
# Only endpoints present — midpoint (33.0, -97.0) missing
|
||||
for {lat, lon} <- [{32.9, -97.04}, {33.1, -96.96}] do
|
||||
Weather.upsert_hrrr_profile(%{@hrrr_profile_attrs | lat: lat, lon: lon})
|
||||
end
|
||||
|
||||
contact = %{
|
||||
pos1: %{"lat" => 32.9, "lon" => -97.04},
|
||||
pos2: %{"lat" => 33.1, "lon" => -96.96},
|
||||
qso_timestamp: @contact_time
|
||||
}
|
||||
|
||||
refute Weather.hrrr_data_fully_present?(contact)
|
||||
end
|
||||
|
||||
test "returns false when pos1 is nil" do
|
||||
refute Weather.hrrr_data_fully_present?(%{
|
||||
pos1: nil,
|
||||
pos2: nil,
|
||||
qso_timestamp: @contact_time
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
describe "round_to_hrrr_grid/2" do
|
||||
test "rounds coordinates to 2 decimal places" do
|
||||
assert Weather.round_to_hrrr_grid(32.9047, -97.0382) == {32.90, -97.04}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue