diff --git a/lib/aprsme/packets.ex b/lib/aprsme/packets.ex index 0d8c191..ecd3675 100644 --- a/lib/aprsme/packets.ex +++ b/lib/aprsme/packets.ex @@ -404,6 +404,34 @@ defmodule Aprsme.Packets do get_packets_for_replay(opts_with_time) end + @doc """ + Gets weather packets for a specific callsign within a time range. + This is optimized for weather queries by filtering at the database level. + """ + @impl true + @spec get_weather_packets(String.t(), DateTime.t(), DateTime.t(), map()) :: [struct()] + def get_weather_packets(callsign, start_time, end_time, opts \\ %{}) do + limit = Map.get(opts, :limit, 500) + + base_query = from(p in Packet, order_by: [asc: p.received_at]) + + query = + base_query + |> filter_by_time(%{start_time: start_time, end_time: end_time}) + |> filter_by_callsign(%{callsign: callsign}) + |> filter_weather_packets() + |> limit_results(%{limit: limit}) + |> select_with_virtual_coordinates() + + Repo.all(query) + end + + # Filter for weather packets at the database level + defp filter_weather_packets(query) do + from p in query, + where: p.data_type == "weather" or (p.symbol_table_id == "/" and p.symbol_code == "_") + end + @doc """ Retrieves a continuous stream of stored packets for replay in chronological order. diff --git a/lib/aprsme/packets_behaviour.ex b/lib/aprsme/packets_behaviour.ex index 9d21bed..df817fc 100644 --- a/lib/aprsme/packets_behaviour.ex +++ b/lib/aprsme/packets_behaviour.ex @@ -7,6 +7,7 @@ defmodule Aprsme.PacketsBehaviour do @callback stream_packets_for_replay(map()) :: Enumerable.t() @callback get_packets_for_replay(map()) :: list() @callback get_recent_packets(map()) :: list() + @callback get_weather_packets(String.t(), DateTime.t(), DateTime.t(), map()) :: list() @callback clean_old_packets() :: {:ok, non_neg_integer()} | {:error, any()} @callback clean_packets_older_than(pos_integer()) :: {:ok, non_neg_integer()} | {:error, any()} end diff --git a/lib/aprsme_web/live/weather_live/callsign_view.ex b/lib/aprsme_web/live/weather_live/callsign_view.ex index efa82a2..cc808b6 100644 --- a/lib/aprsme_web/live/weather_live/callsign_view.ex +++ b/lib/aprsme_web/live/weather_live/callsign_view.ex @@ -2,8 +2,6 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do @moduledoc false use AprsmeWeb, :live_view - import Jason - alias Aprsme.Packets alias AprsmeWeb.MapLive.PacketUtils @@ -57,59 +55,12 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do end defp get_weather_history(callsign, start_time, end_time) do - opts = %{callsign: callsign, start_time: start_time, end_time: end_time, limit: 1000} - - opts - |> Packets.get_packets_for_replay() - |> Enum.filter(&PacketUtils.weather_packet?/1) - |> Enum.sort_by(& &1.received_at) + Packets.get_weather_packets(callsign, start_time, end_time, %{limit: 500}) end defp default_time_range do now = DateTime.utc_now() - {DateTime.add(now, -48 * 3600, :second), now} - end - - defp build_temp_chart_svg(history) do - data = - history - |> Enum.map(fn pkt -> - [pkt.received_at, pkt.temperature, pkt.dew_point] - end) - |> Enum.filter(fn [_, t, d] -> not is_nil(t) and not is_nil(d) end) - - if data == [] do - "No data" - else - dataset = Dataset.new(data, ["Time", "Temperature", "Dew Point"]) - - dataset - |> Plot.new(LinePlot, 600, 300, smoothed: false) - |> Plot.titles("Temperature & Dew Point (°F)", nil) - |> Plot.axis_labels("Time", "°F") - |> Plot.to_svg() - end - end - - defp build_humidity_chart_svg(history) do - data = - history - |> Enum.map(fn pkt -> - [pkt.received_at, pkt.humidity] - end) - |> Enum.filter(fn [_, h] -> not is_nil(h) end) - - if data == [] do - "No data" - else - dataset = Dataset.new(data, ["Time", "Humidity"]) - - dataset - |> Plot.new(LinePlot, 600, 300, smoothed: false) - |> Plot.titles("Humidity (%)", nil) - |> Plot.axis_labels("Time", "%") - |> Plot.to_svg() - end + {DateTime.add(now, -24 * 3600, :second), now} end defp calc_dew_point(temp, humidity) when is_number(temp) and is_number(humidity) do diff --git a/priv/repo/migrations/20250703180500_add_sender_index_to_packets.exs b/priv/repo/migrations/20250703180500_add_sender_index_to_packets.exs new file mode 100644 index 0000000..be505f2 --- /dev/null +++ b/priv/repo/migrations/20250703180500_add_sender_index_to_packets.exs @@ -0,0 +1,14 @@ +defmodule Aprsme.Repo.Migrations.AddSenderIndexToPackets do + use Ecto.Migration + + def change do + # Add index on sender field for efficient callsign filtering + create index(:packets, [:sender]) + + # Add compound index for sender + received_at for efficient time-based callsign queries + create index(:packets, [:sender, :received_at]) + + # Add compound index for sender + has_position for weather packet queries + create index(:packets, [:sender, :has_position]) + end +end