defmodule Microwaveprop.Workers.PropagationGridWorker do @moduledoc """ Hourly Oban worker that downloads the latest HRRR data and computes propagation scores across the CONUS grid for all bands. """ use Oban.Worker, queue: :propagation, max_attempts: 3, unique: [period: 3600, states: [:available, :scheduled, :executing]] alias Microwaveprop.Propagation alias Microwaveprop.Propagation.Grid alias Microwaveprop.Weather.HrrrClient require Logger @impl Oban.Worker def perform(%Oban.Job{}) do valid_time = HrrrClient.nearest_hrrr_hour(DateTime.utc_now()) points = Grid.conus_points() Logger.info("PropagationGrid: fetching HRRR for #{valid_time}, #{length(points)} points") with {:ok, grid_data} <- HrrrClient.fetch_grid(points, valid_time) do scores = grid_data |> Task.async_stream( fn {{lat, lon}, profile} -> band_scores = Propagation.score_grid_point(profile, valid_time) Enum.map(band_scores, fn result -> %{ lat: lat, lon: lon, valid_time: valid_time, band_mhz: result.band_mhz, score: result.score, factors: result.factors } end) end, max_concurrency: System.schedulers_online() * 2, timeout: 30_000 ) |> Enum.flat_map(fn {:ok, results} -> results {:exit, _reason} -> [] end) Logger.info("PropagationGrid: computed #{length(scores)} scores, upserting") case Propagation.upsert_scores(scores) do {:ok, count} -> Logger.info("PropagationGrid: upserted #{count} scores for #{valid_time}") :ok error -> Logger.error("PropagationGrid: upsert failed: #{inspect(error)}") error end end end end