defmodule Microwaveprop.Ionosphere do @moduledoc """ Context for ionospheric observations (GIRO ionosonde data). Used by the propagation scorer for VHF sporadic-E and HF MUF predictions. """ import Ecto.Query alias Microwaveprop.Ionosphere.Observation alias Microwaveprop.Repo @doc """ Upsert a list of parsed `GiroClient` observations for a station. Conflicts on `(station_code, valid_time)` replace every column except the primary key and inserted_at. Returns `{:ok, count}`. """ @spec upsert_observations(String.t(), [map()]) :: {:ok, non_neg_integer()} def upsert_observations(_station_code, []), do: {:ok, 0} def upsert_observations(station_code, observations) when is_list(observations) do now = DateTime.truncate(DateTime.utc_now(), :second) rows = Enum.map(observations, fn obs -> obs |> Map.put(:station_code, station_code) |> Map.put(:inserted_at, now) |> Map.put(:updated_at, now) end) {count, _} = Repo.insert_all(Observation, rows, on_conflict: {:replace_all_except, [:id, :inserted_at]}, conflict_target: [:station_code, :valid_time] ) {:ok, count} end @doc """ Returns the most recent `Observation` row for a station, or nil. """ @spec latest_observation(String.t()) :: Observation.t() | nil def latest_observation(station_code) do Repo.one( from o in Observation, where: o.station_code == ^station_code, order_by: [desc: o.valid_time], limit: 1 ) end end