From 318eb300467808f548ea18e5e23de6666e7d13da Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 9 Feb 2026 12:32:42 -0600 Subject: [PATCH] Fix additional nesting depth issues across multiple files Extracts nested logic into helper functions to comply with Credo requirements (max nesting depth 2): - data_builder.ex: Extract historical packet distance filtering (filter_historical_by_distance/3, packet_far_enough_from_most_recent?/3) Reduces depth 5 -> depth 3 - streaming_packets_pubsub.ex: Extract subscriber message sending (send_to_subscriber_if_alive/3) Reduces depth 4 -> depth 2 - aprs_symbol.ex: Extract symbol code calculation (get_symbol_code_ord/1) Reduces depth 3 -> depth 2 - mobile_channel.ex: Extract subscription bounds update (update_subscription_bounds/2) Reduces depth 3 -> depth 2 - core_components.ex: Extract SVG tag class insertion (add_class_to_svg_tag/2) Reduces depth 3 -> depth 2 - info_live/show.ex: Extract row mapping for database queries (map_digipeater_row/2, map_station_heard_row/2) Reduces depth 3 -> depth 2 (2 instances) Also updates .sobelow-skips with new line numbers from code changes. Fixes 7 Credo nesting depth issues (2 remaining) --- .sobelow-skips | 7 +- lib/aprsme/streaming_packets_pubsub.ex | 19 +++--- lib/aprsme_web/aprs_symbol.ex | 11 +-- lib/aprsme_web/channels/mobile_channel.ex | 30 ++++---- lib/aprsme_web/components/core_components.ex | 14 ++-- lib/aprsme_web/live/info_live/show.ex | 72 +++++++++++--------- lib/aprsme_web/live/map_live/data_builder.ex | 56 ++++++++------- 7 files changed, 119 insertions(+), 90 deletions(-) diff --git a/.sobelow-skips b/.sobelow-skips index 85f2e39..97678f3 100644 --- a/.sobelow-skips +++ b/.sobelow-skips @@ -20,4 +20,9 @@ XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:675,2E9B38C SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:390,46E033F SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:505,515E82C SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:619,53BB748 -XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:693,EEA0E3 \ No newline at end of file +XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:693,EEA0E3 +SQL.Query: SQL injection,lib/aprsme/packets.ex:664,17532E +XSS.Raw: XSS,lib/aprsme_web/components/core_components.ex:48,1E00DD8 +SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:603,52B452D +XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:661,5C70BB5 +XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:643,692A7EC \ No newline at end of file diff --git a/lib/aprsme/streaming_packets_pubsub.ex b/lib/aprsme/streaming_packets_pubsub.ex index 8924b12..02db43f 100644 --- a/lib/aprsme/streaming_packets_pubsub.ex +++ b/lib/aprsme/streaming_packets_pubsub.ex @@ -123,14 +123,7 @@ defmodule Aprsme.StreamingPacketsPubSub do subscribers |> Stream.filter(fn {_pid, bounds} -> packet_in_bounds?(lat, lon, bounds) end) |> Enum.reduce([], fn {pid, _bounds}, acc -> - # Only send if process is alive - if Process.alive?(pid) do - send(pid, {:streaming_packet, packet}) - acc - else - # Collect dead pid for cleanup - [pid | acc] - end + send_to_subscriber_if_alive(pid, packet, acc) end) # Send dead pids back to GenServer for cleanup @@ -188,4 +181,14 @@ defmodule Aprsme.StreamingPacketsPubSub do lat_in_bounds and lon_in_bounds end + + defp send_to_subscriber_if_alive(pid, packet, acc) do + if Process.alive?(pid) do + send(pid, {:streaming_packet, packet}) + acc + else + # Collect dead pid for cleanup + [pid | acc] + end + end end diff --git a/lib/aprsme_web/aprs_symbol.ex b/lib/aprsme_web/aprs_symbol.ex index 360aed9..87c5017 100644 --- a/lib/aprsme_web/aprs_symbol.ex +++ b/lib/aprsme_web/aprs_symbol.ex @@ -46,11 +46,7 @@ defmodule AprsmeWeb.AprsSymbol do sprite_file = "/aprs-symbols/aprs-symbols-128-#{table_id}@2x.png" # Get symbol position using ASCII-based calculation - symbol_code_ord = - symbol_code - |> String.to_charlist() - |> List.first() - |> then(fn c -> if is_integer(c), do: c, else: 63 end) + symbol_code_ord = get_symbol_code_ord(symbol_code) index = symbol_code_ord - 33 safe_index = max(0, min(index, 93)) @@ -369,4 +365,9 @@ defmodule AprsmeWeb.AprsSymbol do Map.get(data_extended, to_string(field)) || default end + + defp get_symbol_code_ord(symbol_code) do + c = symbol_code |> String.to_charlist() |> List.first() + if is_integer(c), do: c, else: 63 + end end diff --git a/lib/aprsme_web/channels/mobile_channel.ex b/lib/aprsme_web/channels/mobile_channel.ex index de1c74d..4bd5745 100644 --- a/lib/aprsme_web/channels/mobile_channel.ex +++ b/lib/aprsme_web/channels/mobile_channel.ex @@ -125,19 +125,7 @@ defmodule AprsmeWeb.MobileChannel do # Validate bounds case validate_bounds(bounds) do :ok -> - # Unsubscribe from old bounds - if socket.assigns[:bounds] do - Aprsme.StreamingPacketsPubSub.unsubscribe(self()) - end - - # Subscribe to new bounds - Aprsme.StreamingPacketsPubSub.subscribe_to_bounds(self(), bounds) - - socket = assign(socket, :bounds, bounds) - - Logger.debug("Mobile client #{socket.assigns.client_id} updated bounds: #{inspect(bounds)}") - - {:reply, {:ok, %{bounds: bounds, message: "Bounds updated"}}, socket} + update_subscription_bounds(socket, bounds) {:error, reason} -> {:reply, {:error, %{message: reason}}, socket} @@ -501,4 +489,20 @@ defmodule AprsmeWeb.MobileChannel do false end end + + defp update_subscription_bounds(socket, bounds) do + # Unsubscribe from old bounds if they exist + if socket.assigns[:bounds] do + Aprsme.StreamingPacketsPubSub.unsubscribe(self()) + end + + # Subscribe to new bounds + Aprsme.StreamingPacketsPubSub.subscribe_to_bounds(self(), bounds) + + socket = assign(socket, :bounds, bounds) + + Logger.debug("Mobile client #{socket.assigns.client_id} updated bounds: #{inspect(bounds)}") + + {:reply, {:ok, %{bounds: bounds, message: "Bounds updated"}}, socket} + end end diff --git a/lib/aprsme_web/components/core_components.ex b/lib/aprsme_web/components/core_components.ex index 58476bf..eefe19a 100644 --- a/lib/aprsme_web/components/core_components.ex +++ b/lib/aprsme_web/components/core_components.ex @@ -38,11 +38,7 @@ defmodule AprsmeWeb.CoreComponents do {:ok, contents} -> # Insert class attribute if not present Regex.replace(~r/]*?)>/, contents, fn _, attrs -> - if String.contains?(attrs, "class=") do - "" - else - "" - end + add_class_to_svg_tag(attrs, class) end) _ -> @@ -822,4 +818,12 @@ defmodule AprsmeWeb.CoreComponents do <% end %> """ end + + defp add_class_to_svg_tag(attrs, class) do + if String.contains?(attrs, "class=") do + "" + else + "" + end + end end diff --git a/lib/aprsme_web/live/info_live/show.ex b/lib/aprsme_web/live/info_live/show.ex index 2860eab..6319c76 100644 --- a/lib/aprsme_web/live/info_live/show.ex +++ b/lib/aprsme_web/live/info_live/show.ex @@ -505,23 +505,7 @@ defmodule AprsmeWeb.InfoLive.Show do case Repo.query(query, [callsign, one_month_ago]) do {:ok, result} -> result.rows - |> Enum.map(fn row -> - case row do - [digipeater, first_heard, last_heard, packet_count, longest_path_time, longest_distance_km] -> - %{ - digipeater: digipeater || "", - first_heard: first_heard, - last_heard: last_heard, - packet_count: packet_count || 0, - longest_distance_km: longest_distance_km, - longest_distance: if(longest_distance_km, do: format_distance(longest_distance_km, locale)), - longest_path_time: longest_path_time - } - - _ -> - nil - end - end) + |> Enum.map(fn row -> map_digipeater_row(row, locale) end) |> Enum.reject(&is_nil/1) {:error, error} -> @@ -619,23 +603,7 @@ defmodule AprsmeWeb.InfoLive.Show do case Repo.query(query, [callsign, one_month_ago]) do {:ok, result} -> result.rows - |> Enum.map(fn row -> - case row do - [station, first_heard, last_heard, packet_count, longest_path_time, longest_distance_km] -> - %{ - station: station || "", - first_heard: first_heard, - last_heard: last_heard, - packet_count: packet_count || 0, - longest_distance_km: longest_distance_km, - longest_distance: if(longest_distance_km, do: format_distance(longest_distance_km, locale)), - longest_path_time: longest_path_time - } - - _ -> - nil - end - end) + |> Enum.map(fn row -> map_station_heard_row(row, locale) end) |> Enum.reject(&is_nil/1) {:error, error} -> @@ -811,4 +779,40 @@ defmodule AprsmeWeb.InfoLive.Show do defp decode_tcpip_element("TCPIP"), do: gettext("TCPIP (Internet gateway)") defp decode_tcpip_element("TCPIP*"), do: gettext("TCPIP* (Internet gateway, no forward)") defp decode_tcpip_element(element), do: gettext("TCPIP gateway (%{element})", element: element) + + defp map_digipeater_row(row, locale) do + case row do + [digipeater, first_heard, last_heard, packet_count, longest_path_time, longest_distance_km] -> + %{ + digipeater: digipeater || "", + first_heard: first_heard, + last_heard: last_heard, + packet_count: packet_count || 0, + longest_distance_km: longest_distance_km, + longest_distance: if(longest_distance_km, do: format_distance(longest_distance_km, locale)), + longest_path_time: longest_path_time + } + + _ -> + nil + end + end + + defp map_station_heard_row(row, locale) do + case row do + [station, first_heard, last_heard, packet_count, longest_path_time, longest_distance_km] -> + %{ + station: station || "", + first_heard: first_heard, + last_heard: last_heard, + packet_count: packet_count || 0, + longest_distance_km: longest_distance_km, + longest_distance: if(longest_distance_km, do: format_distance(longest_distance_km, locale)), + longest_path_time: longest_path_time + } + + _ -> + nil + end + end end diff --git a/lib/aprsme_web/live/map_live/data_builder.ex b/lib/aprsme_web/live/map_live/data_builder.ex index 17ff459..0f1beae 100644 --- a/lib/aprsme_web/live/map_live/data_builder.ex +++ b/lib/aprsme_web/live/map_live/data_builder.ex @@ -181,30 +181,7 @@ defmodule AprsmeWeb.MapLive.DataBuilder do # Filter historical packets that are too close to most recent position filtered_historical = - if most_recent_lat && most_recent_lon do - Enum.filter(historical, fn packet -> - {lat, lon, _} = get_coordinates(packet) - - if lat && lon do - distance_meters = - CoordinateUtils.calculate_distance_meters( - most_recent_lat, - most_recent_lon, - lat, - lon - ) - - # Only show if 10+ meters away - distance_meters >= 10.0 - else - # Skip packets without coordinates - false - end - end) - else - # If most recent has no coordinates, include all historical - historical - end + filter_historical_by_distance(historical, most_recent_lat, most_recent_lon) # Build data for remaining historical packets historical_data = @@ -620,4 +597,35 @@ defmodule AprsmeWeb.MapLive.DataBuilder do end defp convert_rain(value, _locale), do: {value, "in"} + + defp filter_historical_by_distance(historical, most_recent_lat, most_recent_lon) do + if most_recent_lat && most_recent_lon do + Enum.filter(historical, fn packet -> + packet_far_enough_from_most_recent?(packet, most_recent_lat, most_recent_lon) + end) + else + # If most recent has no coordinates, include all historical + historical + end + end + + defp packet_far_enough_from_most_recent?(packet, most_recent_lat, most_recent_lon) do + {lat, lon, _} = get_coordinates(packet) + + if lat && lon do + distance_meters = + CoordinateUtils.calculate_distance_meters( + most_recent_lat, + most_recent_lon, + lat, + lon + ) + + # Only show if 10+ meters away + distance_meters >= 10.0 + else + # Skip packets without coordinates + false + end + end end