refactor
This commit is contained in:
parent
7ef3f59026
commit
7f06d3200a
4 changed files with 120 additions and 164 deletions
|
|
@ -160,18 +160,20 @@ defmodule Aprsme.DeviceIdentification do
|
|||
|
||||
defp upsert_device_group(group, now) do
|
||||
Enum.each(group, fn {identifier, attrs} ->
|
||||
attrs =
|
||||
attrs
|
||||
|> Map.put("identifier", identifier)
|
||||
|> Map.update("features", nil, fn f ->
|
||||
if is_list(f), do: f, else: [f]
|
||||
end)
|
||||
|> Map.put("updated_at", now)
|
||||
|
||||
%Devices{} |> Devices.changeset(attrs) |> Repo.insert!()
|
||||
processed_attrs = process_device_attrs(attrs, identifier, now)
|
||||
%Devices{} |> Devices.changeset(processed_attrs) |> Repo.insert!()
|
||||
end)
|
||||
end
|
||||
|
||||
defp process_device_attrs(attrs, identifier, now) do
|
||||
attrs
|
||||
|> Map.put("identifier", identifier)
|
||||
|> Map.update("features", nil, fn f ->
|
||||
if is_list(f), do: f, else: [f]
|
||||
end)
|
||||
|> Map.put("updated_at", now)
|
||||
end
|
||||
|
||||
# Helper to enqueue the job
|
||||
def enqueue_refresh_job do
|
||||
# Oban.insert!(Worker.new(%{}))
|
||||
|
|
@ -214,7 +216,8 @@ defmodule Aprsme.DeviceIdentification do
|
|||
|
||||
# Converts a pattern with ? wildcards to a regex
|
||||
defp wildcard_pattern_to_regex(pattern) when is_binary(pattern) do
|
||||
# Replace ? with a placeholder, escape all regex metacharacters except the placeholder, then replace placeholder with .
|
||||
# Replace ? with a placeholder, escape all regex metacharacters except the placeholder,
|
||||
# then replace placeholder with .
|
||||
pattern
|
||||
|> String.replace("?", "__WILDCARD__")
|
||||
# Escape all regex metacharacters
|
||||
|
|
|
|||
|
|
@ -302,70 +302,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@spec handle_bounds_update(map(), Socket.t()) :: {:noreply, Socket.t()}
|
||||
defp handle_bounds_update(bounds, socket) do
|
||||
# Update the map bounds from the client, converting to atom keys
|
||||
map_bounds = %{
|
||||
north: bounds["north"],
|
||||
south: bounds["south"],
|
||||
east: bounds["east"],
|
||||
west: bounds["west"]
|
||||
}
|
||||
|
||||
# Validate bounds to prevent invalid coordinates
|
||||
if map_bounds.north > 90 or map_bounds.south < -90 or
|
||||
map_bounds.north <= map_bounds.south do
|
||||
# Invalid bounds, skip update
|
||||
{:noreply, socket}
|
||||
else
|
||||
# Only schedule a bounds update if the bounds have actually changed (with rounding)
|
||||
if compare_bounds(map_bounds, socket.assigns.map_bounds) do
|
||||
{:noreply, socket}
|
||||
else
|
||||
if socket.assigns[:bounds_update_timer] do
|
||||
Process.cancel_timer(socket.assigns.bounds_update_timer)
|
||||
end
|
||||
|
||||
timer_ref = Process.send_after(self(), {:process_bounds_update, map_bounds}, 250)
|
||||
socket = assign(socket, bounds_update_timer: timer_ref, pending_bounds: map_bounds)
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@spec process_bounds_update(map(), Socket.t()) :: Socket.t()
|
||||
defp process_bounds_update(map_bounds, socket) do
|
||||
# Remove out-of-bounds packets and markers immediately
|
||||
new_visible_packets =
|
||||
socket.assigns.visible_packets
|
||||
|> Enum.filter(fn {_k, packet} -> within_bounds?(packet, map_bounds) end)
|
||||
|> Map.new()
|
||||
|
||||
packets_to_remove =
|
||||
socket.assigns.visible_packets
|
||||
|> Enum.reject(fn {_k, packet} -> within_bounds?(packet, map_bounds) end)
|
||||
|> Enum.map(fn {k, _} -> k end)
|
||||
|
||||
# Remove markers for out-of-bounds packets
|
||||
socket =
|
||||
if packets_to_remove == [] do
|
||||
socket
|
||||
else
|
||||
Enum.reduce(packets_to_remove, socket, fn k, acc ->
|
||||
push_event(acc, "remove_marker", %{id: k})
|
||||
end)
|
||||
end
|
||||
|
||||
# Remove only out-of-bounds historical packets instead of clearing all
|
||||
socket = push_event(socket, "filter_markers_by_bounds", %{bounds: map_bounds})
|
||||
|
||||
# Load additional historical packets for the new bounds if needed
|
||||
socket = load_historical_packets_for_bounds(socket, map_bounds)
|
||||
|
||||
# Update map bounds and visible packets
|
||||
assign(socket, map_bounds: map_bounds, visible_packets: new_visible_packets)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:process_bounds_update, map_bounds}, socket), do: handle_info_process_bounds_update(map_bounds, socket)
|
||||
|
||||
|
|
@ -1015,29 +951,11 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
|
||||
# Check if a packet is within the time threshold (not too old)
|
||||
@spec packet_within_time_threshold?(map(), DateTime.t()) :: boolean()
|
||||
@spec packet_within_time_threshold?(struct(), any()) :: boolean()
|
||||
defp packet_within_time_threshold?(packet, threshold) do
|
||||
case packet do
|
||||
%{received_at: received_at} when not is_nil(received_at) ->
|
||||
threshold_dt =
|
||||
cond do
|
||||
is_integer(threshold) ->
|
||||
# Assume seconds since epoch
|
||||
DateTime.from_unix!(threshold)
|
||||
|
||||
is_binary(threshold) ->
|
||||
case DateTime.from_iso8601(threshold) do
|
||||
{:ok, dt, _} -> dt
|
||||
_ -> DateTime.utc_now()
|
||||
end
|
||||
|
||||
match?(%DateTime{}, threshold) ->
|
||||
threshold
|
||||
|
||||
true ->
|
||||
DateTime.utc_now()
|
||||
end
|
||||
|
||||
threshold_dt = convert_threshold_to_datetime(threshold)
|
||||
DateTime.compare(received_at, threshold_dt) in [:gt, :eq]
|
||||
|
||||
_ ->
|
||||
|
|
@ -1046,48 +964,33 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
end
|
||||
|
||||
defp convert_threshold_to_datetime(threshold) do
|
||||
cond do
|
||||
is_integer(threshold) ->
|
||||
# Assume seconds since epoch
|
||||
DateTime.from_unix!(threshold)
|
||||
|
||||
is_binary(threshold) ->
|
||||
case DateTime.from_iso8601(threshold) do
|
||||
{:ok, dt, _} -> dt
|
||||
_ -> DateTime.utc_now()
|
||||
end
|
||||
|
||||
match?(%DateTime{}, threshold) ->
|
||||
threshold
|
||||
|
||||
true ->
|
||||
DateTime.utc_now()
|
||||
end
|
||||
end
|
||||
|
||||
# Helper functions
|
||||
|
||||
# Fetch historical packets from the database
|
||||
defp process_historical_packets(socket, historical_packets) do
|
||||
socket = push_event(socket, "clear_historical_packets", %{})
|
||||
|
||||
packet_data_list =
|
||||
historical_packets
|
||||
|> Enum.group_by(&PacketUtils.generate_callsign/1)
|
||||
|> Enum.flat_map(fn {callsign, packets} ->
|
||||
sorted_packets =
|
||||
Enum.sort_by(
|
||||
packets,
|
||||
fn packet ->
|
||||
case packet.inserted_at do
|
||||
%NaiveDateTime{} = naive_dt -> DateTime.from_naive!(naive_dt, "Etc/UTC")
|
||||
%DateTime{} = dt -> dt
|
||||
_other -> DateTime.utc_now()
|
||||
end
|
||||
end,
|
||||
{:desc, DateTime}
|
||||
)
|
||||
|
||||
unique_position_packets = filter_unique_positions(sorted_packets)
|
||||
|
||||
unique_position_packets
|
||||
|> Enum.with_index()
|
||||
|> Enum.map(fn {packet, index} ->
|
||||
# The first packet (index 0) is the most recent for this callsign
|
||||
# Only show as red dot if it's not the most recent position
|
||||
is_most_recent = index == 0
|
||||
|
||||
packet_data = PacketUtils.build_packet_data(packet, is_most_recent)
|
||||
|
||||
if packet_data do
|
||||
packet_data
|
||||
|> Map.put(:callsign, callsign)
|
||||
|> Map.put(:historical, true)
|
||||
end
|
||||
end)
|
||||
|> Enum.filter(& &1)
|
||||
end)
|
||||
packet_data_list = build_packet_data_list(historical_packets)
|
||||
|
||||
if Enum.any?(packet_data_list) do
|
||||
push_event(socket, "add_historical_packets", %{packets: packet_data_list})
|
||||
|
|
@ -1096,6 +999,50 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
end
|
||||
|
||||
defp build_packet_data_list(historical_packets) do
|
||||
historical_packets
|
||||
|> Enum.group_by(&PacketUtils.generate_callsign/1)
|
||||
|> Enum.flat_map(&process_callsign_packets/1)
|
||||
end
|
||||
|
||||
defp process_callsign_packets({callsign, packets}) do
|
||||
sorted_packets = sort_packets_by_inserted_at(packets)
|
||||
unique_position_packets = filter_unique_positions(sorted_packets)
|
||||
|
||||
unique_position_packets
|
||||
|> Enum.with_index()
|
||||
|> Enum.map(&build_packet_data_with_index(&1, callsign))
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
|
||||
defp sort_packets_by_inserted_at(packets) do
|
||||
Enum.sort_by(
|
||||
packets,
|
||||
fn packet ->
|
||||
case packet.inserted_at do
|
||||
%NaiveDateTime{} = naive_dt -> DateTime.from_naive!(naive_dt, "Etc/UTC")
|
||||
%DateTime{} = dt -> dt
|
||||
_other -> DateTime.utc_now()
|
||||
end
|
||||
end,
|
||||
{:desc, DateTime}
|
||||
)
|
||||
end
|
||||
|
||||
defp build_packet_data_with_index({packet, index}, callsign) do
|
||||
# The first packet (index 0) is the most recent for this callsign
|
||||
# Only show as red dot if it's not the most recent position
|
||||
is_most_recent = index == 0
|
||||
|
||||
packet_data = PacketUtils.build_packet_data(packet, is_most_recent)
|
||||
|
||||
if packet_data do
|
||||
packet_data
|
||||
|> Map.put(:callsign, callsign)
|
||||
|> Map.put(:historical, true)
|
||||
end
|
||||
end
|
||||
|
||||
defp filter_unique_positions(packets) do
|
||||
packets
|
||||
|> Enum.reduce([], fn packet, acc ->
|
||||
|
|
|
|||
|
|
@ -66,26 +66,33 @@ defmodule AprsmeWeb.MapLive.MapHelpers do
|
|||
if is_nil(lat) or is_nil(lon) do
|
||||
false
|
||||
else
|
||||
lat = to_float(lat)
|
||||
lon = to_float(lon)
|
||||
south = to_float(bounds.south)
|
||||
north = to_float(bounds.north)
|
||||
west = to_float(bounds.west)
|
||||
east = to_float(bounds.east)
|
||||
lat_in_bounds = lat >= south && lat <= north
|
||||
|
||||
lng_in_bounds =
|
||||
if west <= east do
|
||||
lon >= west && lon <= east
|
||||
else
|
||||
lon >= west || lon <= east
|
||||
end
|
||||
|
||||
lat_in_bounds && lng_in_bounds
|
||||
check_bounds(lat, lon, bounds)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp check_bounds(lat, lon, bounds) do
|
||||
lat = to_float(lat)
|
||||
lon = to_float(lon)
|
||||
south = to_float(bounds.south)
|
||||
north = to_float(bounds.north)
|
||||
west = to_float(bounds.west)
|
||||
east = to_float(bounds.east)
|
||||
|
||||
lat_in_bounds = lat >= south && lat <= north
|
||||
lng_in_bounds = check_longitude_bounds(lon, west, east)
|
||||
|
||||
lat_in_bounds && lng_in_bounds
|
||||
end
|
||||
|
||||
defp check_longitude_bounds(lon, west, east) do
|
||||
if west <= east do
|
||||
lon >= west && lon <= east
|
||||
else
|
||||
lon >= west || lon <= east
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_lat_lon(%{lat: lat, lon: lon}), do: extract_lat_lon_atom(%{lat: lat, lon: lon})
|
||||
|
||||
defp extract_lat_lon(%{"lat" => lat, "lon" => lon}), do: extract_lat_lon_string(%{"lat" => lat, "lon" => lon})
|
||||
|
|
|
|||
|
|
@ -149,28 +149,27 @@ defmodule AprsmeWeb.PacketsLive.CallsignView do
|
|||
defp get_all_packets_list(stored, live) do
|
||||
# Combine and sort by received_at timestamp (newest first)
|
||||
(live ++ stored)
|
||||
|> Enum.sort_by(
|
||||
fn packet ->
|
||||
case packet.received_at do
|
||||
%DateTime{} = dt ->
|
||||
DateTime.to_unix(dt, :microsecond)
|
||||
|
||||
dt when is_binary(dt) ->
|
||||
case DateTime.from_iso8601(dt) do
|
||||
{:ok, parsed_dt, _} -> DateTime.to_unix(parsed_dt, :microsecond)
|
||||
_ -> 0
|
||||
end
|
||||
|
||||
_ ->
|
||||
0
|
||||
end
|
||||
end,
|
||||
:desc
|
||||
)
|
||||
|> Enum.sort_by(&get_timestamp_microseconds/1, :desc)
|
||||
# Ensure we never exceed 100 total
|
||||
|> Enum.take(100)
|
||||
end
|
||||
|
||||
defp get_timestamp_microseconds(packet) do
|
||||
case packet.received_at do
|
||||
%DateTime{} = dt ->
|
||||
DateTime.to_unix(dt, :microsecond)
|
||||
|
||||
dt when is_binary(dt) ->
|
||||
case DateTime.from_iso8601(dt) do
|
||||
{:ok, parsed_dt, _} -> DateTime.to_unix(parsed_dt, :microsecond)
|
||||
_ -> 0
|
||||
end
|
||||
|
||||
_ ->
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
# Helper to update stored and live packet lists, keeping total <= 100
|
||||
defp update_packet_lists(current_stored, current_live, sanitized_payload) do
|
||||
total_count = length(current_live) + length(current_stored)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue