defmodule Microwaveprop.Weather.Iemre do @moduledoc false import Ecto.Query alias Microwaveprop.Radio alias Microwaveprop.Radio.Contact alias Microwaveprop.Repo alias Microwaveprop.Weather.IemreObservation alias Microwaveprop.Weather.ProfileLookup # Delegates to ProfileLookup defdelegate iemre_for_contact(contact), to: ProfileLookup defdelegate iemre_for_path(contact), to: ProfileLookup defdelegate find_nearest_iemre(lat, lon, timestamp), to: ProfileLookup defdelegate round_to_iemre_grid(lat, lon), to: ProfileLookup @spec upsert_iemre_observation(map()) :: {:ok, IemreObservation.t()} | {:error, Ecto.Changeset.t()} def upsert_iemre_observation(attrs) do %IemreObservation{} |> IemreObservation.changeset(attrs) |> Repo.insert( on_conflict: :nothing, conflict_target: [:lat, :lon, :date] ) end @spec has_iemre_observation?(float(), float(), Date.t()) :: boolean() def has_iemre_observation?(lat, lon, date) do IemreObservation |> where([i], i.lat == ^lat and i.lon == ^lon and i.date == ^date) |> Repo.exists?() end @doc """ Flip `contacts.iemre_status` from `:queued` to `:complete` for every contact whose path points all have an `iemre_observations` row at the rounded grid cell for the QSO's UTC date. Per-contact check (via the existing helpers); kept in Elixir because `contact_path_points/1` produces 1–3 points depending on whether `pos2` is set, and rounding to the 0.125° IEMRE grid per point in pure SQL is awkward. Queue sizes are small (<20 stuck at steady state). """ @spec reconcile_iemre_statuses() :: {:ok, non_neg_integer()} def reconcile_iemre_statuses do queued = Contact |> where([c], c.iemre_status == :queued and not is_nil(c.pos1)) |> Repo.all() to_complete = Enum.filter(queued, fn c -> date = DateTime.to_date(c.qso_timestamp) c |> Radio.contact_path_points() |> Enum.all?(fn {lat, lon} -> {rlat, rlon} = round_to_iemre_grid(lat, lon) has_iemre_observation?(rlat, rlon, date) end) end) if to_complete == [] do {:ok, 0} else ids = Enum.map(to_complete, & &1.id) _ = Radio.set_enrichment_status!(ids, :iemre_status, :complete) {:ok, length(to_complete)} end end end