Live-update soundings and surface observations on contact page

- WeatherFetchWorker broadcasts on ASOS and RAOB completion
- Contact show page subscribes and reloads weather data on receipt
- Loading spinners for soundings and surface observations when queued
- Propagation analysis recomputed with new sounding duct data
This commit is contained in:
Graham McIntire 2026-04-02 08:22:43 -05:00
parent a3ac8cff8d
commit 17b66d6903
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
2 changed files with 73 additions and 2 deletions

View file

@ -39,6 +39,12 @@ defmodule Microwaveprop.Workers.WeatherFetchWorker do
if row.observed_at, do: Weather.upsert_surface_observation(station, row)
end)
Phoenix.PubSub.broadcast(
Microwaveprop.PubSub,
"contact_enrichment:weather",
{:weather_ready, %{station_id: station_id}}
)
:ok
{:error, reason} ->
@ -96,6 +102,13 @@ defmodule Microwaveprop.Workers.WeatherFetchWorker do
end
Weather.upsert_sounding(station, sounding_attrs)
Phoenix.PubSub.broadcast(
Microwaveprop.PubSub,
"contact_enrichment:weather",
{:weather_ready, %{station_id: station_id}}
)
:ok
{:ok, []} ->

View file

@ -24,6 +24,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do
if connected?(socket) do
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:#{contact.id}")
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:hrrr")
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:weather")
end
weather = load_weather(contact)
@ -42,6 +43,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do
contact: contact,
surface_observations: weather.surface_observations,
soundings: weather.soundings,
weather_status: weather_status(weather, contact),
solar: solar,
hrrr: hrrr,
hrrr_status: hrrr_status,
@ -154,6 +156,44 @@ defmodule MicrowavepropWeb.ContactLive.Show do
end
end
def handle_info({:weather_ready, _info}, socket) do
contact = socket.assigns.contact
weather = load_weather(contact)
soundings = weather.soundings
hrrr_path = Weather.hrrr_profiles_for_path(contact)
hrrr = List.first(hrrr_path)
terrain = socket.assigns.terrain
elevation_profile = socket.assigns.elevation_profile
# Recompute propagation analysis with new sounding data (duct detection)
propagation_analysis =
if elevation_profile || hrrr do
build_propagation_analysis(contact, hrrr, terrain, elevation_profile, soundings)
else
socket.assigns.propagation_analysis
end
{:noreply,
assign(socket,
surface_observations: weather.surface_observations,
soundings: soundings,
weather_status: :loaded,
propagation_analysis: propagation_analysis
)}
end
defp weather_status(weather, contact) do
has_data = weather.surface_observations != [] or weather.soundings != []
cond do
has_data -> :loaded
contact.weather_queued -> :queued
contact.pos1 -> :queued
true -> :unavailable
end
end
defp toggle_sort(current_field, current_order, new_field) do
if current_field == new_field && current_order == "asc",
do: {new_field, "desc"},
@ -494,7 +534,16 @@ defmodule MicrowavepropWeb.ContactLive.Show do
<h2 class="text-base font-semibold mb-2">Soundings</h2>
<%= if @soundings == [] do %>
<p class="text-sm text-base-content/50 italic">No soundings found nearby.</p>
<div class="text-sm text-base-content/50 italic">
<%= if @weather_status == :queued do %>
<span class="flex items-center gap-2">
<span class="loading loading-spinner loading-sm"></span>
Fetching sounding data from nearby stations...
</span>
<% else %>
No soundings found nearby.
<% end %>
</div>
<% else %>
<%= for s <- @soundings do %>
<div class="mb-6 border border-base-300 rounded-lg">
@ -660,7 +709,16 @@ defmodule MicrowavepropWeb.ContactLive.Show do
<h2 class="text-base font-semibold mb-2">Surface Observations</h2>
<%= if @surface_observations == [] do %>
<p class="text-sm text-base-content/50 italic">No surface observations found nearby.</p>
<div class="text-sm text-base-content/50 italic">
<%= if @weather_status == :queued do %>
<span class="flex items-center gap-2">
<span class="loading loading-spinner loading-sm"></span>
Fetching surface observations from nearby stations...
</span>
<% else %>
No surface observations found nearby.
<% end %>
</div>
<% else %>
<div class="overflow-x-auto">
<table class="table table-xs table-zebra">