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)
This commit is contained in:
Graham McIntire 2026-02-09 12:32:42 -06:00
parent 74a19b0640
commit 318eb30046
No known key found for this signature in database
7 changed files with 119 additions and 90 deletions

View file

@ -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
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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -38,11 +38,7 @@ defmodule AprsmeWeb.CoreComponents do
{:ok, contents} ->
# Insert class attribute if not present
Regex.replace(~r/<svg([^>]*?)>/, contents, fn _, attrs ->
if String.contains?(attrs, "class=") do
"<svg#{attrs}>"
else
"<svg class=\"#{class}\"#{attrs}>"
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
"<svg#{attrs}>"
else
"<svg class=\"#{class}\"#{attrs}>"
end
end
end

View file

@ -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

View file

@ -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