clean up status page

This commit is contained in:
Graham McIntire 2025-07-15 13:56:01 -05:00
parent 400e444eaa
commit 534bd8df8a
No known key found for this signature in database
4 changed files with 112 additions and 26 deletions

View file

@ -133,7 +133,8 @@ defmodule Aprsme.Is do
login_id: Application.get_env(:aprsme, :aprsme_is_login_id, "W5ISP"),
filter: Application.get_env(:aprsme, :aprsme_is_default_filter, "r/33/-96/100"),
packet_stats: default_packet_stats(),
stored_packet_count: Aprsme.Packets.get_total_packet_count()
stored_packet_count: Aprsme.Packets.get_total_packet_count(),
oldest_packet_timestamp: Aprsme.Packets.get_oldest_packet_timestamp()
}
_pid ->
@ -154,7 +155,8 @@ defmodule Aprsme.Is do
login_id: Application.get_env(:aprsme, :aprsme_is_login_id, "W5ISP"),
filter: Application.get_env(:aprsme, :aprsme_is_default_filter, "r/33/-96/100"),
packet_stats: default_packet_stats(),
stored_packet_count: Aprsme.Packets.get_total_packet_count()
stored_packet_count: Aprsme.Packets.get_total_packet_count(),
oldest_packet_timestamp: Aprsme.Packets.get_oldest_packet_timestamp()
}
end
end
@ -259,7 +261,8 @@ defmodule Aprsme.Is do
login_id: state.login_params.user_id,
filter: state.login_params.filter,
packet_stats: state.packet_stats,
stored_packet_count: Aprsme.Packets.get_total_packet_count()
stored_packet_count: Aprsme.Packets.get_total_packet_count(),
oldest_packet_timestamp: Aprsme.Packets.get_oldest_packet_timestamp()
}
{:reply, status, state}

View file

@ -638,6 +638,18 @@ defmodule Aprsme.Packets do
0
end
@doc """
Gets the timestamp of the oldest stored packet in the database.
Returns nil if no packets exist.
"""
@spec get_oldest_packet_timestamp() :: DateTime.t() | nil
def get_oldest_packet_timestamp do
Repo.one(
from p in Packet,
select: min(p.received_at)
)
end
@doc """
Configure packet retention policy.

View file

@ -16,7 +16,6 @@ defmodule AprsmeWeb.StatusLive.Index do
assign(socket,
page_title: "System Status",
aprs_status: aprs_status,
version: get_app_version(),
current_time: DateTime.utc_now(),
health_score: calculate_health_score(aprs_status),
loading: false
@ -83,23 +82,7 @@ defmodule AprsmeWeb.StatusLive.Index do
<span class="font-mono">{Calendar.strftime(@current_time, "%H:%M:%S UTC")}</span>
</div>
</div>
<!-- Application Information -->
<div class="mb-8">
<h2 class="text-xl font-semibold mb-4">{gettext("Application Information")}</h2>
<div class="card bg-base-200">
<div class="card-body">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<span class="text-sm font-medium opacity-70 mr-2">{gettext("Version:")}</span>
<span class="text-sm font-mono">{@version}</span>
</div>
</div>
</div>
</div>
</div>
<!-- Overall Status Alert -->
<!-- Overall Status Alert -->
<%= if not @aprs_status.connected do %>
<div class="alert alert-error mb-6">
<svg
@ -244,6 +227,29 @@ defmodule AprsmeWeb.StatusLive.Index do
</span>
</div>
</div>
<div class="mt-2">
<div class="flex items-center">
<span class="text-sm font-medium opacity-70 mr-2">
{gettext("Oldest Packet:")}
</span>
<span class="text-sm">
<%= if @aprs_status[:oldest_packet_timestamp] do %>
<span class="font-mono">
{Calendar.strftime(
@aprs_status.oldest_packet_timestamp,
"%Y-%m-%d %H:%M:%S UTC"
)}
</span>
<span class="opacity-70">
({format_time_ago(@aprs_status.oldest_packet_timestamp)})
</span>
<% else %>
<span class="opacity-50">{gettext("No packets stored")}</span>
<% end %>
</span>
</div>
</div>
</div>
<!-- Health Score -->
@ -320,14 +326,11 @@ defmodule AprsmeWeb.StatusLive.Index do
packets_per_second: 0,
last_packet_at: nil
},
stored_packet_count: 0
stored_packet_count: 0,
oldest_packet_timestamp: nil
}
end
defp get_app_version do
:aprsme |> Application.spec(:vsn) |> List.to_string()
end
defp schedule_refresh do
Process.send_after(self(), :refresh_status, @refresh_interval)
end
@ -373,6 +376,8 @@ defmodule AprsmeWeb.StatusLive.Index do
end
end
def format_time_ago(nil), do: gettext("Never")
def format_time_ago(datetime) do
diff_seconds = DateTime.diff(DateTime.utc_now(), datetime)

View file

@ -0,0 +1,66 @@
defmodule Aprsme.PacketsOldestTest do
use Aprsme.DataCase
alias Aprsme.Packets
alias Aprsme.Repo
describe "get_oldest_packet_timestamp/0" do
test "returns nil when no packets exist" do
assert is_nil(Packets.get_oldest_packet_timestamp())
end
test "returns the timestamp of the oldest packet" do
# Create packets with different timestamps
oldest_time = ~U[2023-01-01 00:00:00Z]
middle_time = ~U[2023-06-15 12:30:00Z]
newest_time = ~U[2023-12-31 23:59:59Z]
# Insert packets with different timestamps
{:ok, _} = create_test_packet("OLD-1", oldest_time)
{:ok, _} = create_test_packet("MID-1", middle_time)
{:ok, _} = create_test_packet("NEW-1", newest_time)
# Should return the oldest timestamp
assert Packets.get_oldest_packet_timestamp() == oldest_time
end
test "handles packets with microsecond precision" do
# Create a packet with microsecond precision
timestamp_with_microseconds = ~U[2023-07-15 14:30:45.123456Z]
{:ok, _} = create_test_packet("TEST-1", timestamp_with_microseconds)
result = Packets.get_oldest_packet_timestamp()
# Database might truncate microseconds, so we check the timestamp is close
assert DateTime.diff(result, timestamp_with_microseconds, :microsecond) < 1000
end
end
# Helper function to create a test packet with specific timestamp
defp create_test_packet(callsign, received_at) do
# Extract SSID from callsign (e.g., "TEST-1" -> ssid = "1")
{base_callsign, ssid} =
case String.split(callsign, "-", parts: 2) do
[base] -> {base, "0"}
[base, ssid_part] -> {base, ssid_part}
end
packet_data = %{
sender: callsign,
base_callsign: base_callsign,
ssid: ssid,
destination: "APRS",
data_type: "position",
path: "WIDE1-1",
information_field: "!3216.46N/09647.82W>Test packet",
raw_packet: "#{callsign}>APRS,WIDE1-1:!3216.46N/09647.82W>Test packet",
lat: 32.274333,
lon: -96.797,
has_position: true,
received_at: received_at
}
%Aprsme.Packet{}
|> Aprsme.Packet.changeset(packet_data)
|> Repo.insert()
end
end