clean up status page
This commit is contained in:
parent
400e444eaa
commit
534bd8df8a
4 changed files with 112 additions and 26 deletions
|
|
@ -133,7 +133,8 @@ defmodule Aprsme.Is do
|
||||||
login_id: Application.get_env(:aprsme, :aprsme_is_login_id, "W5ISP"),
|
login_id: Application.get_env(:aprsme, :aprsme_is_login_id, "W5ISP"),
|
||||||
filter: Application.get_env(:aprsme, :aprsme_is_default_filter, "r/33/-96/100"),
|
filter: Application.get_env(:aprsme, :aprsme_is_default_filter, "r/33/-96/100"),
|
||||||
packet_stats: default_packet_stats(),
|
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 ->
|
_pid ->
|
||||||
|
|
@ -154,7 +155,8 @@ defmodule Aprsme.Is do
|
||||||
login_id: Application.get_env(:aprsme, :aprsme_is_login_id, "W5ISP"),
|
login_id: Application.get_env(:aprsme, :aprsme_is_login_id, "W5ISP"),
|
||||||
filter: Application.get_env(:aprsme, :aprsme_is_default_filter, "r/33/-96/100"),
|
filter: Application.get_env(:aprsme, :aprsme_is_default_filter, "r/33/-96/100"),
|
||||||
packet_stats: default_packet_stats(),
|
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
|
||||||
end
|
end
|
||||||
|
|
@ -259,7 +261,8 @@ defmodule Aprsme.Is do
|
||||||
login_id: state.login_params.user_id,
|
login_id: state.login_params.user_id,
|
||||||
filter: state.login_params.filter,
|
filter: state.login_params.filter,
|
||||||
packet_stats: state.packet_stats,
|
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}
|
{:reply, status, state}
|
||||||
|
|
|
||||||
|
|
@ -638,6 +638,18 @@ defmodule Aprsme.Packets do
|
||||||
0
|
0
|
||||||
end
|
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 """
|
@doc """
|
||||||
Configure packet retention policy.
|
Configure packet retention policy.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ defmodule AprsmeWeb.StatusLive.Index do
|
||||||
assign(socket,
|
assign(socket,
|
||||||
page_title: "System Status",
|
page_title: "System Status",
|
||||||
aprs_status: aprs_status,
|
aprs_status: aprs_status,
|
||||||
version: get_app_version(),
|
|
||||||
current_time: DateTime.utc_now(),
|
current_time: DateTime.utc_now(),
|
||||||
health_score: calculate_health_score(aprs_status),
|
health_score: calculate_health_score(aprs_status),
|
||||||
loading: false
|
loading: false
|
||||||
|
|
@ -83,23 +82,7 @@ defmodule AprsmeWeb.StatusLive.Index do
|
||||||
<span class="font-mono">{Calendar.strftime(@current_time, "%H:%M:%S UTC")}</span>
|
<span class="font-mono">{Calendar.strftime(@current_time, "%H:%M:%S UTC")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Overall Status Alert -->
|
||||||
<!-- 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 -->
|
|
||||||
<%= if not @aprs_status.connected do %>
|
<%= if not @aprs_status.connected do %>
|
||||||
<div class="alert alert-error mb-6">
|
<div class="alert alert-error mb-6">
|
||||||
<svg
|
<svg
|
||||||
|
|
@ -244,6 +227,29 @@ defmodule AprsmeWeb.StatusLive.Index do
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
|
|
||||||
<!-- Health Score -->
|
<!-- Health Score -->
|
||||||
|
|
@ -320,14 +326,11 @@ defmodule AprsmeWeb.StatusLive.Index do
|
||||||
packets_per_second: 0,
|
packets_per_second: 0,
|
||||||
last_packet_at: nil
|
last_packet_at: nil
|
||||||
},
|
},
|
||||||
stored_packet_count: 0
|
stored_packet_count: 0,
|
||||||
|
oldest_packet_timestamp: nil
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_app_version do
|
|
||||||
:aprsme |> Application.spec(:vsn) |> List.to_string()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp schedule_refresh do
|
defp schedule_refresh do
|
||||||
Process.send_after(self(), :refresh_status, @refresh_interval)
|
Process.send_after(self(), :refresh_status, @refresh_interval)
|
||||||
end
|
end
|
||||||
|
|
@ -373,6 +376,8 @@ defmodule AprsmeWeb.StatusLive.Index do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_time_ago(nil), do: gettext("Never")
|
||||||
|
|
||||||
def format_time_ago(datetime) do
|
def format_time_ago(datetime) do
|
||||||
diff_seconds = DateTime.diff(DateTime.utc_now(), datetime)
|
diff_seconds = DateTime.diff(DateTime.utc_now(), datetime)
|
||||||
|
|
||||||
|
|
|
||||||
66
test/aprsme/packets_oldest_test.exs
Normal file
66
test/aprsme/packets_oldest_test.exs
Normal 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
|
||||||
Loading…
Add table
Reference in a new issue