diff --git a/lib/microwaveprop_web/live/contact_live/show.ex b/lib/microwaveprop_web/live/contact_live/show.ex index 2f1392de..db0b8512 100644 --- a/lib/microwaveprop_web/live/contact_live/show.ex +++ b/lib/microwaveprop_web/live/contact_live/show.ex @@ -11,6 +11,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do alias Microwaveprop.Weather alias Microwaveprop.Weather.HrrrClient alias Microwaveprop.Workers.ContactWeatherEnqueueWorker + alias Microwaveprop.Workers.Era5FetchWorker alias Microwaveprop.Workers.HrrrFetchWorker alias Microwaveprop.Workers.SolarIndexWorker alias Microwaveprop.Workers.TerrainProfileWorker @@ -40,6 +41,8 @@ defmodule MicrowavepropWeb.ContactLive.Show do solar: nil, hrrr: nil, hrrr_path: [], + era5: nil, + era5_path: [], iemre: nil, terrain: nil, elevation_profile: nil, @@ -83,6 +86,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do |> start_async(:weather, fn -> load_weather(contact) end) |> start_async(:solar, fn -> load_solar(contact) end) |> start_async(:hrrr_path, fn -> Weather.hrrr_profiles_for_path(contact) end) + |> start_async(:era5_path, fn -> Weather.era5_profiles_for_path(contact) end) |> start_async(:terrain, fn -> Terrain.get_terrain_profile(contact.id) end) |> start_async(:iemre, fn -> load_iemre(contact) end) end @@ -308,6 +312,16 @@ defmodule MicrowavepropWeb.ContactLive.Show do {:noreply, socket} end + def handle_async(:era5_path, {:ok, era5_path}, socket) do + era5 = List.first(era5_path) + contact = socket.assigns.contact + + contact = + if socket.assigns.can_enqueue, do: maybe_enqueue_era5(era5, contact), else: contact + + {:noreply, assign(socket, contact: contact, era5: era5, era5_path: era5_path)} + end + def handle_async(:terrain, {:ok, terrain}, socket) do contact = socket.assigns.contact @@ -326,7 +340,8 @@ defmodule MicrowavepropWeb.ContactLive.Show do {:noreply, assign(socket, :iemre, iemre)} end - def handle_async(slot, {:exit, reason}, socket) when slot in [:weather, :solar, :hrrr_path, :terrain, :iemre] do + def handle_async(slot, {:exit, reason}, socket) + when slot in [:weather, :solar, :hrrr_path, :era5_path, :terrain, :iemre] do Logger.warning("contact hydrate task #{slot} exited: #{inspect(reason)}") {:noreply, socket} end @@ -626,6 +641,33 @@ defmodule MicrowavepropWeb.ContactLive.Show do end end + # ERA5 is a fallback for contacts where HRRR is unavailable (pre-2014 or + # missing from the archive). Only enqueue when HRRR is known-unavailable AND + # we don't already have an ERA5 profile — otherwise we'd waste CDS quota on + # contacts the HRRR path will cover. + defp maybe_enqueue_era5(era5, contact) when not is_nil(era5), do: contact + + defp maybe_enqueue_era5(nil, %{hrrr_status: :unavailable} = contact) do + lat = contact.pos1 && contact.pos1["lat"] + lon = contact.pos1 && (contact.pos1["lon"] || contact.pos1["lng"]) + + if lat && lon do + valid_time = %{DateTime.truncate(contact.qso_timestamp, :second) | minute: 0, second: 0} + + %{ + "lat" => lat, + "lon" => lon, + "valid_time" => DateTime.to_iso8601(valid_time) + } + |> Era5FetchWorker.new() + |> Oban.insert() + end + + contact + end + + defp maybe_enqueue_era5(_nil, contact), do: contact + defp queue_info(counts, queue) do case Map.get(counts, queue, 0) do 0 -> "" @@ -1077,23 +1119,31 @@ defmodule MicrowavepropWeb.ContactLive.Show do
-

HRRR Model Profile

- <%= if @hrrr do %> +

Atmospheric Profile

+ <% profile = @hrrr || @era5 %> + <% profile_source = + cond do + @hrrr -> "HRRR (3 km)" + @era5 -> "ERA5 (0.25°)" + true -> nil + end %> + <%= if profile do %>