refactor: FP patterns in packets_live/callsign_view
- mount: extract subscribe_if_connected/2, split assign_valid/assign_invalid
to eliminate inline if/else for validation + subscription logic.
- enrich_packet_with_device_identifier: extract resolve_canonical_identifier/1
as multi-clause function (binary guard + catch-all) instead of nested if.
- update_packet_lists: cond → multi-clause do_update/4 with guards.
Three explicit paths: at-cap no-stored, at-cap with-stored, under-cap.
- get_timestamp_microseconds: case → multi-clause (%DateTime{}, binary,
catch-all) for cleaner dispatch.
This commit is contained in:
parent
80983e6223
commit
088fd20711
1 changed files with 64 additions and 75 deletions
|
|
@ -13,42 +13,43 @@ defmodule AprsmeWeb.PacketsLive.CallsignView do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(%{"callsign" => callsign}, _session, socket) do
|
def mount(%{"callsign" => callsign}, _session, socket) do
|
||||||
# Validate and normalize callsign
|
normalized = Callsign.normalize(callsign)
|
||||||
normalized_callsign = Callsign.normalize(callsign)
|
|
||||||
|
|
||||||
if Callsign.valid?(normalized_callsign) do
|
if Callsign.valid?(normalized) do
|
||||||
# Subscribe to live packet updates for this callsign only
|
socket = subscribe_if_connected(socket, normalized)
|
||||||
_ =
|
stored = get_stored_packets(normalized, 100)
|
||||||
if connected?(socket) do
|
{:ok, assign_valid(socket, normalized, stored)}
|
||||||
Phoenix.PubSub.subscribe(Aprsme.PubSub, "packets:#{normalized_callsign}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get stored packets for this callsign (up to 100)
|
|
||||||
stored_packets = get_stored_packets(normalized_callsign, 100)
|
|
||||||
all_packets = stored_packets
|
|
||||||
|
|
||||||
socket =
|
|
||||||
socket
|
|
||||||
|> assign(:callsign, normalized_callsign)
|
|
||||||
|> assign(:packets, stored_packets)
|
|
||||||
|> assign(:live_packets, [])
|
|
||||||
|> assign(:all_packets, all_packets)
|
|
||||||
|> assign(:error, nil)
|
|
||||||
|
|
||||||
{:ok, socket}
|
|
||||||
else
|
else
|
||||||
socket =
|
{:ok, assign_invalid(socket, normalized)}
|
||||||
socket
|
|
||||||
|> assign(:callsign, normalized_callsign)
|
|
||||||
|> assign(:packets, [])
|
|
||||||
|> assign(:live_packets, [])
|
|
||||||
|> assign(:all_packets, [])
|
|
||||||
|> assign(:error, "Invalid callsign format")
|
|
||||||
|
|
||||||
{:ok, socket}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp subscribe_if_connected(socket, callsign) do
|
||||||
|
if connected?(socket) do
|
||||||
|
Phoenix.PubSub.subscribe(Aprsme.PubSub, "packets:#{callsign}")
|
||||||
|
end
|
||||||
|
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assign_valid(socket, callsign, stored) do
|
||||||
|
socket
|
||||||
|
|> assign(:callsign, callsign)
|
||||||
|
|> assign(:packets, stored)
|
||||||
|
|> assign(:live_packets, [])
|
||||||
|
|> assign(:all_packets, stored)
|
||||||
|
|> assign(:error, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assign_invalid(socket, callsign) do
|
||||||
|
socket
|
||||||
|
|> assign(:callsign, callsign)
|
||||||
|
|> assign(:packets, [])
|
||||||
|
|> assign(:live_packets, [])
|
||||||
|
|> assign(:all_packets, [])
|
||||||
|
|> assign(:error, "Invalid callsign format")
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:postgres_packet, payload}, socket) do
|
def handle_info({:postgres_packet, payload}, socket) do
|
||||||
SharedPacketHandler.handle_packet_update(payload, socket,
|
SharedPacketHandler.handle_packet_update(payload, socket,
|
||||||
|
|
@ -60,7 +61,6 @@ defmodule AprsmeWeb.PacketsLive.CallsignView do
|
||||||
def handle_info(_message, socket), do: {:noreply, socket}
|
def handle_info(_message, socket), do: {:noreply, socket}
|
||||||
|
|
||||||
defp process_matching_packet(enriched_payload, socket) do
|
defp process_matching_packet(enriched_payload, socket) do
|
||||||
# Device identifier enrichment specific to this module
|
|
||||||
enriched_payload = enrich_packet_with_device_identifier(enriched_payload)
|
enriched_payload = enrich_packet_with_device_identifier(enriched_payload)
|
||||||
|
|
||||||
current_live = socket.assigns.live_packets
|
current_live = socket.assigns.live_packets
|
||||||
|
|
@ -69,13 +69,11 @@ defmodule AprsmeWeb.PacketsLive.CallsignView do
|
||||||
{updated_stored, updated_live} = update_packet_lists(current_stored, current_live, enriched_payload)
|
{updated_stored, updated_live} = update_packet_lists(current_stored, current_live, enriched_payload)
|
||||||
all_packets = get_all_packets_list(updated_stored, updated_live)
|
all_packets = get_all_packets_list(updated_stored, updated_live)
|
||||||
|
|
||||||
socket =
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|> assign(:packets, updated_stored)
|
|> assign(:packets, updated_stored)
|
||||||
|> assign(:live_packets, updated_live)
|
|> assign(:live_packets, updated_live)
|
||||||
|> assign(:all_packets, all_packets)
|
|> assign(:all_packets, all_packets)}
|
||||||
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp enrich_packet_with_device_identifier(sanitized_payload) do
|
defp enrich_packet_with_device_identifier(sanitized_payload) do
|
||||||
|
|
@ -84,18 +82,18 @@ defmodule AprsmeWeb.PacketsLive.CallsignView do
|
||||||
Map.get(sanitized_payload, "device_identifier") ||
|
Map.get(sanitized_payload, "device_identifier") ||
|
||||||
DeviceParser.extract_device_identifier(sanitized_payload)
|
DeviceParser.extract_device_identifier(sanitized_payload)
|
||||||
|
|
||||||
canonical_identifier =
|
canonical_identifier = resolve_canonical_identifier(device_identifier)
|
||||||
if is_binary(device_identifier) do
|
|
||||||
matched_device = Aprsme.DeviceIdentification.lookup_device_by_identifier(device_identifier)
|
|
||||||
if matched_device, do: matched_device.identifier, else: device_identifier
|
|
||||||
else
|
|
||||||
device_identifier
|
|
||||||
end
|
|
||||||
|
|
||||||
Map.put(sanitized_payload, :device_identifier, canonical_identifier)
|
Map.put(sanitized_payload, :device_identifier, canonical_identifier)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Private helper functions
|
defp resolve_canonical_identifier(identifier) when is_binary(identifier) do
|
||||||
|
case Aprsme.DeviceIdentification.lookup_device_by_identifier(identifier) do
|
||||||
|
%{identifier: canonical} -> canonical
|
||||||
|
nil -> identifier
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp resolve_canonical_identifier(identifier), do: identifier
|
||||||
|
|
||||||
# Get recent packets for this callsign from the database (all packets, not just position)
|
# Get recent packets for this callsign from the database (all packets, not just position)
|
||||||
# Returns the latest packets regardless of age, filtered by callsign
|
# Returns the latest packets regardless of age, filtered by callsign
|
||||||
|
|
@ -133,37 +131,28 @@ defmodule AprsmeWeb.PacketsLive.CallsignView do
|
||||||
|> Enum.take(100)
|
|> Enum.take(100)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_timestamp_microseconds(packet) do
|
defp get_timestamp_microseconds(%DateTime{} = dt), do: DateTime.to_unix(dt, :microsecond)
|
||||||
case packet.received_at do
|
|
||||||
%DateTime{} = dt ->
|
|
||||||
DateTime.to_unix(dt, :microsecond)
|
|
||||||
|
|
||||||
dt when is_binary(dt) ->
|
defp get_timestamp_microseconds(dt) when is_binary(dt) do
|
||||||
case DateTime.from_iso8601(dt) do
|
case DateTime.from_iso8601(dt) do
|
||||||
{:ok, parsed_dt, _} -> DateTime.to_unix(parsed_dt, :microsecond)
|
{:ok, parsed_dt, _} -> DateTime.to_unix(parsed_dt, :microsecond)
|
||||||
_ -> 0
|
_ -> 0
|
||||||
end
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
0
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Helper to update stored and live packet lists, keeping total <= 100
|
defp get_timestamp_microseconds(_), do: 0
|
||||||
|
|
||||||
|
# Helper to update stored and live packet lists, keeping total <= 100.
|
||||||
|
# Dispatches on capacity: at-cap with no stored, at-cap with stored, under-cap.
|
||||||
defp update_packet_lists(current_stored, current_live, sanitized_payload) do
|
defp update_packet_lists(current_stored, current_live, sanitized_payload) do
|
||||||
total_count = length(current_live) + length(current_stored)
|
total = length(current_live) + length(current_stored)
|
||||||
|
do_update(total, current_stored, current_live, sanitized_payload)
|
||||||
cond do
|
|
||||||
total_count >= 100 and current_stored == [] ->
|
|
||||||
live_without_oldest = Enum.drop(current_live, -1)
|
|
||||||
{current_stored, [sanitized_payload | live_without_oldest]}
|
|
||||||
|
|
||||||
total_count >= 100 ->
|
|
||||||
stored_without_oldest = Enum.drop(current_stored, -1)
|
|
||||||
{stored_without_oldest, [sanitized_payload | current_live]}
|
|
||||||
|
|
||||||
true ->
|
|
||||||
{current_stored, [sanitized_payload | current_live]}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_update(total, [], current_live, payload) when total >= 100, do: {[], [payload | Enum.drop(current_live, -1)]}
|
||||||
|
|
||||||
|
defp do_update(total, current_stored, current_live, payload) when total >= 100,
|
||||||
|
do: {Enum.drop(current_stored, -1), [payload | current_live]}
|
||||||
|
|
||||||
|
defp do_update(_total, current_stored, current_live, payload), do: {current_stored, [payload | current_live]}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue