Auto-fetch solar index data on contact page when missing

- SolarIndexWorker accepts date arg for specific date fetch
- Contact show page enqueues solar fetch when data missing
- PubSub broadcast on solar data arrival, live-updates page
- Spinner shown while fetching
This commit is contained in:
Graham McIntire 2026-04-02 16:07:38 -05:00
parent 9fe53b4e7b
commit 0e089e8493
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
2 changed files with 50 additions and 3 deletions

View file

@ -1,11 +1,34 @@
defmodule Microwaveprop.Workers.SolarIndexWorker do defmodule Microwaveprop.Workers.SolarIndexWorker do
@moduledoc false @moduledoc false
use Oban.Worker, queue: :solar, max_attempts: 3 use Oban.Worker, queue: :solar, max_attempts: 3, unique: [period: 3600]
alias Microwaveprop.Weather alias Microwaveprop.Weather
alias Microwaveprop.Weather.SolarClient alias Microwaveprop.Weather.SolarClient
@impl Oban.Worker @impl Oban.Worker
def perform(%Oban.Job{args: %{"date" => date_str}}) do
{:ok, target_date} = Date.from_iso8601(date_str)
case SolarClient.fetch_solar_indices() do
{:ok, all_records} ->
matched = Enum.filter(all_records, fn r -> r.date == target_date end)
Enum.each(matched, &Weather.upsert_solar_index/1)
if matched != [] do
Phoenix.PubSub.broadcast(
Microwaveprop.PubSub,
"contact_enrichment:solar",
{:solar_ready, date_str}
)
end
:ok
{:error, reason} ->
{:error, reason}
end
end
def perform(%Oban.Job{}) do def perform(%Oban.Job{}) do
since_date = Date.add(Date.utc_today(), -7) since_date = Date.add(Date.utc_today(), -7)

View file

@ -12,6 +12,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do
alias Microwaveprop.Weather.HrrrClient alias Microwaveprop.Weather.HrrrClient
alias Microwaveprop.Workers.ContactWeatherEnqueueWorker alias Microwaveprop.Workers.ContactWeatherEnqueueWorker
alias Microwaveprop.Workers.HrrrFetchWorker alias Microwaveprop.Workers.HrrrFetchWorker
alias Microwaveprop.Workers.SolarIndexWorker
alias Microwaveprop.Workers.TerrainProfileWorker alias Microwaveprop.Workers.TerrainProfileWorker
require Logger require Logger
@ -26,10 +27,11 @@ defmodule MicrowavepropWeb.ContactLive.Show do
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:#{contact.id}") Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:#{contact.id}")
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:hrrr") Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:hrrr")
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:weather") Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:weather")
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:solar")
end end
weather = load_weather(contact) weather = load_weather(contact)
solar = load_solar(contact) solar = maybe_enqueue_solar(contact)
hrrr_path = Weather.hrrr_profiles_for_path(contact) hrrr_path = Weather.hrrr_profiles_for_path(contact)
hrrr = List.first(hrrr_path) hrrr = List.first(hrrr_path)
{hrrr, contact, hrrr_jobs_ahead} = maybe_enqueue_hrrr(hrrr, contact) {hrrr, contact, hrrr_jobs_ahead} = maybe_enqueue_hrrr(hrrr, contact)
@ -182,6 +184,11 @@ defmodule MicrowavepropWeb.ContactLive.Show do
)} )}
end end
def handle_info({:solar_ready, _date}, socket) do
solar = load_solar(socket.assigns.contact)
{:noreply, assign(socket, solar: solar)}
end
defp maybe_enqueue_weather(weather, contact) do defp maybe_enqueue_weather(weather, contact) do
has_data = weather.surface_observations != [] or weather.soundings != [] has_data = weather.surface_observations != [] or weather.soundings != []
@ -353,6 +360,18 @@ defmodule MicrowavepropWeb.ContactLive.Show do
|> Weather.get_solar_index() |> Weather.get_solar_index()
end end
defp maybe_enqueue_solar(contact) do
solar = load_solar(contact)
if solar do
solar
else
date = DateTime.to_date(contact.qso_timestamp)
Oban.insert(SolarIndexWorker.new(%{"date" => Date.to_iso8601(date)}))
nil
end
end
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
@ -665,7 +684,12 @@ defmodule MicrowavepropWeb.ContactLive.Show do
<div><span class="font-semibold">Kp:</span> {format_kp(@solar.kp_values)}</div> <div><span class="font-semibold">Kp:</span> {format_kp(@solar.kp_values)}</div>
</div> </div>
<% else %> <% else %>
<p class="text-sm text-base-content/50 italic">No solar data available for this date.</p> <div class="text-sm text-base-content/50 italic">
<span class="flex items-center gap-2">
<span class="loading loading-spinner loading-sm"></span>
Fetching solar index data...
</span>
</div>
<% end %> <% end %>
<div class="divider" /> <div class="divider" />