fix remaining bugs and quality improvements
Bug fixes: - path_compute: use midpoint longitude (src+dest)/2 for time-of-day score - rate_limiter: guard nil token id from burning auth rate limit bucket Robustness: - scorer: replace fragile MatchError-prone pattern match with per-key fallback — each invariant computed individually if absent - telemetry: log exception in rescue instead of silent swallow - grid: add atom-key clause to contains?() consistent with point_source/1 - grid: round float_range values to avoid fp drift in snapped lookups Cleanup: - path_compute: remove unused alias Geo (import-only module)
This commit is contained in:
parent
e3d430f8c4
commit
4d3b61c740
7 changed files with 31 additions and 20 deletions
|
|
@ -60,6 +60,10 @@ defmodule Microwaveprop.Propagation.Grid do
|
|||
lat >= @lat_min and lat <= @lat_max and lon >= @lon_min and lon <= @lon_max
|
||||
end
|
||||
|
||||
def contains?(%{lat: lat, lon: lon}) when is_number(lat) and is_number(lon) do
|
||||
lat >= @lat_min and lat <= @lat_max and lon >= @lon_min and lon <= @lon_max
|
||||
end
|
||||
|
||||
def contains?(_), do: false
|
||||
|
||||
@doc "Returns the grid specification for wgrib2 -lola extraction."
|
||||
|
|
@ -133,6 +137,9 @@ defmodule Microwaveprop.Propagation.Grid do
|
|||
|
||||
defp float_range(start, stop, step) do
|
||||
count = round((stop - start) / step) + 1
|
||||
Enum.map(0..(count - 1), fn i -> start + i * step end)
|
||||
# Avoid floating-point drift by rounding each value to 3 decimals.
|
||||
# The accumulation of start + i * step drifts beyond 0.001° after ~200
|
||||
# iterations, which can cause map lookups on snapped coordinates to miss.
|
||||
Enum.map(0..(count - 1), fn i -> Float.round(start + i * step, 3) end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ defmodule Microwaveprop.Propagation.PathCompute do
|
|||
|
||||
import Microwaveprop.Geo, only: [haversine_km: 4, bearing_deg: 4]
|
||||
|
||||
alias Microwaveprop.Geo
|
||||
alias Microwaveprop.Ionosphere
|
||||
alias Microwaveprop.Propagation
|
||||
alias Microwaveprop.Propagation.BandConfig
|
||||
|
|
@ -405,7 +404,7 @@ defmodule Microwaveprop.Propagation.PathCompute do
|
|||
utc_minute: now.minute,
|
||||
month: now.month,
|
||||
latitude: (src.lat + dst.lat) / 2,
|
||||
longitude: src.lon,
|
||||
longitude: (src.lon + dst.lon) / 2,
|
||||
pressure_mb: if(pressures != [], do: Enum.min(pressures)),
|
||||
prev_pressure_mb: nil,
|
||||
rain_rate_mmhr: 0.0,
|
||||
|
|
@ -485,6 +484,5 @@ defmodule Microwaveprop.Propagation.PathCompute do
|
|||
}
|
||||
end
|
||||
|
||||
# Avoid unused-alias warnings for the import.
|
||||
@compile {:no_warn_undefined, Geo}
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -553,17 +553,10 @@ defmodule Microwaveprop.Propagation.Scorer do
|
|||
# scorer for per-point reuse across bands) or all absent. We check
|
||||
# one key and derive the rest from the same branch so we never
|
||||
# silently mix cached and freshly-computed values.
|
||||
%{
|
||||
tod_score: tod_score,
|
||||
sky_score: sky_score,
|
||||
wind_score: wind_score,
|
||||
pressure_score: pressure_score
|
||||
} =
|
||||
if Map.has_key?(conditions, :tod_score) do
|
||||
conditions
|
||||
else
|
||||
precompute_band_invariants(conditions)
|
||||
end
|
||||
tod_score = conditions[:tod_score] || elem(score_time_of_day(conditions.utc_hour, conditions.utc_minute, conditions.month, conditions.longitude), 0)
|
||||
sky_score = conditions[:sky_score] || score_sky(conditions.sky_cover_pct)
|
||||
wind_score = conditions[:wind_score] || score_wind(conditions.wind_speed_kts)
|
||||
pressure_score = conditions[:pressure_score] || score_pressure(conditions.pressure_mb, conditions.prev_pressure_mb)
|
||||
|
||||
factors = %{
|
||||
humidity: score_humidity(conditions.abs_humidity, band_config),
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ defmodule Microwaveprop.Radio.Contact do
|
|||
|> validate_callsign(:station2)
|
||||
|> validate_grid_format(:grid1)
|
||||
|> validate_grid_format(:grid2)
|
||||
|> validate_inclusion(:mode, @allowed_modes)
|
||||
|> validate_mode_inclusion()
|
||||
|> validate_inclusion(:band, @allowed_bands)
|
||||
|> validate_email_format()
|
||||
|> validate_length(:submitter_email, max: 254)
|
||||
|
|
@ -168,6 +168,16 @@ defmodule Microwaveprop.Radio.Contact do
|
|||
end
|
||||
end
|
||||
|
||||
# Only run validate_inclusion for mode when the field has a non-nil change.
|
||||
# nil means "not provided" (mode is optional), and put_change to nil would
|
||||
# otherwise cause validate_inclusion to reject nil as not in the allowed list.
|
||||
defp validate_mode_inclusion(changeset) do
|
||||
case get_change(changeset, :mode) do
|
||||
nil -> changeset
|
||||
mode -> validate_inclusion(changeset, :mode, @allowed_modes)
|
||||
end
|
||||
end
|
||||
|
||||
# Either a user_id (logged-in submitter) or a submitter_email (anonymous)
|
||||
# must be present so we know who submitted the contact.
|
||||
defp validate_user_or_email(changeset) do
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ defmodule MicrowavepropWeb.Api.RateLimiter do
|
|||
|
||||
defp bucket_for(conn, opts) do
|
||||
case conn.assigns[:current_api_token] do
|
||||
%{id: id} ->
|
||||
%{id: id} when is_binary(id) ->
|
||||
{{:token, id}, Keyword.get(opts, :auth_limit, @default_auth_limit)}
|
||||
|
||||
_ ->
|
||||
|
|
|
|||
|
|
@ -282,6 +282,9 @@ defmodule MicrowavepropWeb.Telemetry do
|
|||
:ok
|
||||
rescue
|
||||
# Don't crash the poller if the DB blips — telemetry is best-effort.
|
||||
_ -> :ok
|
||||
e ->
|
||||
require Logger
|
||||
Logger.warning("Oban queue depth poll failed: #{inspect(e)}")
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ defmodule MicrowavepropWeb.UserAuth do
|
|||
|
||||
# Do not renew session if the user is already logged in
|
||||
# to prevent CSRF errors or data being lost in tabs that are still open
|
||||
defp renew_session(conn, user) when conn.assigns.current_scope.user.id == user.id do
|
||||
defp renew_session(conn, %{id: user_id}) when conn.assigns.current_scope.user.id == user_id do
|
||||
conn
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue