updates
This commit is contained in:
parent
49108423d6
commit
6bd4361ed1
42 changed files with 189 additions and 130 deletions
40
.credo.exs
40
.credo.exs
|
|
@ -189,26 +189,26 @@
|
||||||
#
|
#
|
||||||
## Jump Credo Checks
|
## Jump Credo Checks
|
||||||
#
|
#
|
||||||
{Jump.CredoChecks.AssertElementSelectorCanNeverFail, []},
|
{Jump.CredoChecks.AssertElementSelectorCanNeverFail, []}
|
||||||
{Jump.CredoChecks.AssertReceiveTimeout, []},
|
# {Jump.CredoChecks.AssertReceiveTimeout, []},
|
||||||
{Jump.CredoChecks.AvoidFunctionLevelElse, []},
|
# {Jump.CredoChecks.AvoidFunctionLevelElse, []},
|
||||||
{Jump.CredoChecks.AvoidLoggerConfigureInTest, []},
|
# {Jump.CredoChecks.AvoidLoggerConfigureInTest, []},
|
||||||
{Jump.CredoChecks.AvoidSocketAssignsInTest, []},
|
# {Jump.CredoChecks.AvoidSocketAssignsInTest, []},
|
||||||
{Jump.CredoChecks.ConditionalAssertion, []},
|
# {Jump.CredoChecks.ConditionalAssertion, []},
|
||||||
{Jump.CredoChecks.DoctestIExExamples, []},
|
# {Jump.CredoChecks.DoctestIExExamples, []},
|
||||||
{Jump.CredoChecks.ForbiddenFunction, []},
|
# {Jump.CredoChecks.ForbiddenFunction, []},
|
||||||
{Jump.CredoChecks.LiveViewFormCanBeRehydrated, []},
|
# {Jump.CredoChecks.LiveViewFormCanBeRehydrated, []},
|
||||||
{Jump.CredoChecks.UndeclaredExternalResource, []},
|
# {Jump.CredoChecks.UndeclaredExternalResource, []},
|
||||||
{Jump.CredoChecks.PreferChangeOverUpDownMigrations, [start_after: "0"]},
|
# {Jump.CredoChecks.PreferChangeOverUpDownMigrations, [start_after: "0"]},
|
||||||
{Jump.CredoChecks.PreferTextColumns, [start_after: "0"]},
|
# {Jump.CredoChecks.PreferTextColumns, [start_after: "0"]},
|
||||||
{Jump.CredoChecks.SafeBinaryToTerm, []},
|
# {Jump.CredoChecks.SafeBinaryToTerm, []},
|
||||||
{Jump.CredoChecks.TestHasNoAssertions, []},
|
# {Jump.CredoChecks.TestHasNoAssertions, []},
|
||||||
{Jump.CredoChecks.TooManyAssertions, []},
|
# {Jump.CredoChecks.TooManyAssertions, []},
|
||||||
{Jump.CredoChecks.TopLevelAliasImportRequire, []},
|
# {Jump.CredoChecks.TopLevelAliasImportRequire, []},
|
||||||
{Jump.CredoChecks.UnusedLiveViewAssign, []},
|
# {Jump.CredoChecks.UnusedLiveViewAssign, []},
|
||||||
{Jump.CredoChecks.UseObanProWorker, []},
|
# {Jump.CredoChecks.UseObanProWorker, []},
|
||||||
{Jump.CredoChecks.VacuousTest, []},
|
# {Jump.CredoChecks.VacuousTest, []},
|
||||||
{Jump.CredoChecks.WeakAssertion, []}
|
# {Jump.CredoChecks.WeakAssertion, []}
|
||||||
],
|
],
|
||||||
disabled: [
|
disabled: [
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> get_user_by_email("foo@example.com")
|
get_user_by_email("foo@example.com")
|
||||||
%User{}
|
%User{}
|
||||||
|
|
||||||
iex> get_user_by_email("unknown@example.com")
|
get_user_by_email("unknown@example.com")
|
||||||
nil
|
nil
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -38,10 +38,10 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> get_user_by_callsign("W5ISP")
|
get_user_by_callsign("W5ISP")
|
||||||
%User{}
|
%User{}
|
||||||
|
|
||||||
iex> get_user_by_callsign("unknown")
|
get_user_by_callsign("unknown")
|
||||||
nil
|
nil
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -58,10 +58,10 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> get_user_by_email_and_password("foo@example.com", "correct_password")
|
get_user_by_email_and_password("foo@example.com", "correct_password")
|
||||||
%User{}
|
%User{}
|
||||||
|
|
||||||
iex> get_user_by_email_and_password("foo@example.com", "invalid_password")
|
get_user_by_email_and_password("foo@example.com", "invalid_password")
|
||||||
nil
|
nil
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -78,10 +78,10 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> get_user!(123)
|
get_user!(123)
|
||||||
%User{}
|
%User{}
|
||||||
|
|
||||||
iex> get_user!(456)
|
get_user!(456)
|
||||||
** (Ecto.NoResultsError)
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -121,10 +121,10 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> register_user(%{field: value})
|
register_user(%{field: value})
|
||||||
{:ok, %User{}}
|
{:ok, %User{}}
|
||||||
|
|
||||||
iex> register_user(%{field: bad_value})
|
register_user(%{field: bad_value})
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -209,7 +209,7 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> change_user_email(user)
|
change_user_email(user)
|
||||||
%Ecto.Changeset{data: %User{}}
|
%Ecto.Changeset{data: %User{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -247,7 +247,7 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> change_user_password(user)
|
change_user_password(user)
|
||||||
%Ecto.Changeset{data: %User{}}
|
%Ecto.Changeset{data: %User{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -263,10 +263,10 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> update_user_password(user, %{password: ...})
|
update_user_password(user, %{password: ...})
|
||||||
{:ok, {%User{}, [...]}}
|
{:ok, {%User{}, [...]}}
|
||||||
|
|
||||||
iex> update_user_password(user, %{password: "too short"})
|
update_user_password(user, %{password: "too short"})
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -362,7 +362,7 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> deliver_user_update_email_instructions(user, current_email, &url(~p"/users/settings/confirm-email/#{&1}"))
|
deliver_user_update_email_instructions(user, current_email, &url(~p"/users/settings/confirm-email/#{&1}"))
|
||||||
{:ok, %{to: ..., body: ...}}
|
{:ok, %{to: ..., body: ...}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -386,7 +386,7 @@ defmodule Microwaveprop.Accounts do
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
iex> deliver_user_reset_password_instructions(user, &url(~p"/users/reset-password/#{&1}"))
|
deliver_user_reset_password_instructions(user, &url(~p"/users/reset-password/#{&1}"))
|
||||||
{:ok, %{to: ..., body: ...}}
|
{:ok, %{to: ..., body: ...}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ defmodule Microwaveprop.Commercial.PollWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
links = Commercial.enabled_links()
|
links = Commercial.enabled_links()
|
||||||
|
|
||||||
poll_and_record(links, &SnmpClient.poll/3)
|
poll_and_record(links, &SnmpClient.poll/3)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,36 @@ defmodule Microwaveprop.Propagation.Scorer do
|
||||||
# ── Temperature conversion helpers ────────────────────────────────
|
# ── Temperature conversion helpers ────────────────────────────────
|
||||||
alias Microwaveprop.Propagation.Region
|
alias Microwaveprop.Propagation.Region
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Public entry point: scores propagation conditions for a given band (in MHz).
|
||||||
|
|
||||||
|
Returns `%{score: integer, contributions: [...], breakdown: %{...}, band_mhz: integer}`
|
||||||
|
or `nil` when the band is not configured.
|
||||||
|
"""
|
||||||
|
@spec score(map(), integer()) :: map() | nil
|
||||||
|
def score(conditions, band_mhz) do
|
||||||
|
case BandConfig.get(band_mhz) do
|
||||||
|
nil -> nil
|
||||||
|
band_config -> build_score_result(conditions, band_config, band_mhz)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_score_result(conditions, band_config, band_mhz) do
|
||||||
|
%{score: score, factors: factors} = composite_score(conditions, band_config)
|
||||||
|
|
||||||
|
contributions =
|
||||||
|
factors
|
||||||
|
|> Enum.map(fn {factor, value} -> %{factor: factor, score: value} end)
|
||||||
|
|> Enum.sort_by(& &1.score, :desc)
|
||||||
|
|
||||||
|
%{
|
||||||
|
score: score,
|
||||||
|
contributions: contributions,
|
||||||
|
breakdown: factors,
|
||||||
|
band_mhz: band_mhz
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# Compile-time inverse of the Marshall-Palmer b exponent (1/1.6).
|
# Compile-time inverse of the Marshall-Palmer b exponent (1/1.6).
|
||||||
# Pre-computed to avoid one float division per rain pixel in the
|
# Pre-computed to avoid one float division per rain pixel in the
|
||||||
# dBZ → rain-rate hot path.
|
# dBZ → rain-rate hot path.
|
||||||
|
|
@ -633,7 +663,7 @@ defmodule Microwaveprop.Propagation.Scorer do
|
||||||
defp build_path_conditions(%{dewpoints: []}, _contact), do: nil
|
defp build_path_conditions(%{dewpoints: []}, _contact), do: nil
|
||||||
|
|
||||||
defp build_path_conditions(%{temps: temps, dewpoints: dewpoints} = buckets, contact) do
|
defp build_path_conditions(%{temps: temps, dewpoints: dewpoints} = buckets, contact) do
|
||||||
lon = contact.pos1["lon"] || -97.0
|
lon = path_longitude(contact)
|
||||||
{sum_t, count_t} = Enum.reduce(temps, {0, 0}, fn x, {s, c} -> {s + x, c + 1} end)
|
{sum_t, count_t} = Enum.reduce(temps, {0, 0}, fn x, {s, c} -> {s + x, c + 1} end)
|
||||||
avg_temp_c = sum_t / count_t
|
avg_temp_c = sum_t / count_t
|
||||||
{sum_d, count_d} = Enum.reduce(dewpoints, {0, 0}, fn x, {s, c} -> {s + x, c + 1} end)
|
{sum_d, count_d} = Enum.reduce(dewpoints, {0, 0}, fn x, {s, c} -> {s + x, c + 1} end)
|
||||||
|
|
@ -645,9 +675,9 @@ defmodule Microwaveprop.Propagation.Scorer do
|
||||||
dewpoint_f: c_to_f(avg_dewpoint_c),
|
dewpoint_f: c_to_f(avg_dewpoint_c),
|
||||||
wind_speed_kts: nil,
|
wind_speed_kts: nil,
|
||||||
sky_cover_pct: nil,
|
sky_cover_pct: nil,
|
||||||
utc_hour: contact.qso_timestamp.hour,
|
utc_hour: path_hour(contact),
|
||||||
utc_minute: contact.qso_timestamp.minute,
|
utc_minute: path_minute(contact),
|
||||||
month: contact.qso_timestamp.month,
|
month: path_month(contact),
|
||||||
longitude: lon,
|
longitude: lon,
|
||||||
pressure_mb: Enum.min(buckets.pressures, fn -> nil end),
|
pressure_mb: Enum.min(buckets.pressures, fn -> nil end),
|
||||||
prev_pressure_mb: nil,
|
prev_pressure_mb: nil,
|
||||||
|
|
@ -664,4 +694,21 @@ defmodule Microwaveprop.Propagation.Scorer do
|
||||||
{sum, count} = Enum.reduce(list, {0, 0}, fn x, {s, c} -> {s + x, c + 1} end)
|
{sum, count} = Enum.reduce(list, {0, 0}, fn x, {s, c} -> {s + x, c + 1} end)
|
||||||
sum / count
|
sum / count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Extract longitude from a contact, which may be a schema struct with
|
||||||
|
# `pos1["lon"]` or a plain map with `:longitude`. Falls back to CONUS-centre.
|
||||||
|
defp path_longitude(%{pos1: %{} = pos1}), do: Map.get(pos1, "lon", -97.0)
|
||||||
|
defp path_longitude(%{longitude: lon}) when is_number(lon), do: lon
|
||||||
|
defp path_longitude(_contact), do: -97.0
|
||||||
|
|
||||||
|
# Extract hour/minute/month from a contact, which may be a schema struct
|
||||||
|
# with `qso_timestamp` or a plain map with direct keys.
|
||||||
|
defp path_hour(%{qso_timestamp: %{hour: h}}), do: h
|
||||||
|
defp path_hour(%{utc_hour: h}), do: h
|
||||||
|
|
||||||
|
defp path_minute(%{qso_timestamp: %{minute: m}}), do: m
|
||||||
|
defp path_minute(%{utc_minute: m}), do: m
|
||||||
|
|
||||||
|
defp path_month(%{qso_timestamp: %{month: m}}), do: m
|
||||||
|
defp path_month(%{month: m}), do: m
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -561,7 +561,7 @@ defmodule Microwaveprop.Weather do
|
||||||
length(updates)
|
length(updates)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_values_row({u, idx}, {sql, params}, now) do
|
defp build_values_row({u, _idx}, {sql, params}, now) do
|
||||||
base = Enum.count(params)
|
base = Enum.count(params)
|
||||||
frag = "($#{base + 1}::uuid, $#{base + 2}, $#{base + 3}, $#{base + 4}, $#{base + 5}, $#{base + 6})"
|
frag = "($#{base + 1}::uuid, $#{base + 2}, $#{base + 3}, $#{base + 4}, $#{base + 5}, $#{base + 6})"
|
||||||
sep = if sql == "", do: "", else: ", "
|
sep = if sql == "", do: "", else: ", "
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ defmodule Microwaveprop.Weather.HrrrPointEnqueuer do
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Microwaveprop.Radio
|
||||||
alias Microwaveprop.Repo
|
alias Microwaveprop.Repo
|
||||||
alias Microwaveprop.Weather
|
alias Microwaveprop.Weather
|
||||||
alias Microwaveprop.Weather.HrrrClient
|
alias Microwaveprop.Weather.HrrrClient
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ defmodule Microwaveprop.Workers.AdminTaskWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"task" => "backtest_all"} = args}) do
|
def process(%Oban.Job{args: %{"task" => "backtest_all"} = args}) do
|
||||||
sample_size = Map.get(args, "sample_size", 5000)
|
sample_size = Map.get(args, "sample_size", 5000)
|
||||||
features = Features.all_features()
|
features = Features.all_features()
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ defmodule Microwaveprop.Workers.AdminTaskWorker do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"task" => "backtest", "feature" => feature_name} = args}) do
|
def process(%Oban.Job{args: %{"task" => "backtest", "feature" => feature_name} = args}) do
|
||||||
sample_size = Map.get(args, "sample_size", 5000)
|
sample_size = Map.get(args, "sample_size", 5000)
|
||||||
|
|
||||||
# `function_exported?/3` returns false for any module that hasn't
|
# `function_exported?/3` returns false for any module that hasn't
|
||||||
|
|
@ -65,7 +65,7 @@ defmodule Microwaveprop.Workers.AdminTaskWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"task" => "climatology"} = args}) do
|
def process(%Oban.Job{args: %{"task" => "climatology"} = args}) do
|
||||||
min_samples = Map.get(args, "min_samples", 3)
|
min_samples = Map.get(args, "min_samples", 3)
|
||||||
|
|
||||||
%{rows: combos} =
|
%{rows: combos} =
|
||||||
|
|
@ -124,7 +124,7 @@ defmodule Microwaveprop.Workers.AdminTaskWorker do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"task" => "native_derive"} = args}) do
|
def process(%Oban.Job{args: %{"task" => "native_derive"} = args}) do
|
||||||
limit = Map.get(args, "limit", 10_000)
|
limit = Map.get(args, "limit", 10_000)
|
||||||
|
|
||||||
profiles =
|
profiles =
|
||||||
|
|
@ -142,7 +142,7 @@ defmodule Microwaveprop.Workers.AdminTaskWorker do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"task" => "recalibrate"} = args}) do
|
def process(%Oban.Job{args: %{"task" => "recalibrate"} = args}) do
|
||||||
sample_size = Map.get(args, "sample_size", 5000)
|
sample_size = Map.get(args, "sample_size", 5000)
|
||||||
epochs = Map.get(args, "epochs", 2000)
|
epochs = Map.get(args, "epochs", 2000)
|
||||||
learning_rate = Map.get(args, "learning_rate", 0.01)
|
learning_rate = Map.get(args, "learning_rate", 0.01)
|
||||||
|
|
@ -167,7 +167,7 @@ defmodule Microwaveprop.Workers.AdminTaskWorker do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"task" => task}}) do
|
def process(%Oban.Job{args: %{"task" => task}}) do
|
||||||
Logger.error("AdminTask: unknown task #{task}")
|
Logger.error("AdminTask: unknown task #{task}")
|
||||||
{:error, "unknown task: #{task}"}
|
{:error, "unknown task: #{task}"}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ defmodule Microwaveprop.Workers.BackfillEnqueueWorker do
|
||||||
# exactly what we need as the "given up" signal.
|
# exactly what we need as the "given up" signal.
|
||||||
@stale_queued_cutoff_seconds 3 * 86_400
|
@stale_queued_cutoff_seconds 3 * 86_400
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
types = parse_types(args)
|
types = parse_types(args)
|
||||||
limit = Map.get(args, "limit")
|
limit = Map.get(args, "limit")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ defmodule Microwaveprop.Workers.CanadianSoundingFetchWorker do
|
||||||
# UWYO publishes ~90 minutes after the synoptic hour
|
# UWYO publishes ~90 minutes after the synoptic hour
|
||||||
@publish_delay_seconds 5_400
|
@publish_delay_seconds 5_400
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
sounding_time =
|
sounding_time =
|
||||||
case args do
|
case args do
|
||||||
%{"sounding_time" => iso} when is_binary(iso) ->
|
%{"sounding_time" => iso} when is_binary(iso) ->
|
||||||
|
|
|
||||||
|
|
@ -44,14 +44,14 @@ defmodule Microwaveprop.Workers.CommonVolumeRadarWorker do
|
||||||
# enough to catch thunderstorm cores (typically 10+ km wide).
|
# enough to catch thunderstorm cores (typically 10+ km wide).
|
||||||
@pixel_step 10
|
@pixel_step 10
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"contact_id" => contact_id}}) do
|
def process(%Oban.Job{args: %{"contact_id" => contact_id}}) do
|
||||||
case Repo.get(Contact, contact_id) do
|
case Repo.get(Contact, contact_id) do
|
||||||
nil ->
|
nil ->
|
||||||
:ok
|
:ok
|
||||||
|
|
||||||
%Contact{pos1: p1, pos2: p2} = contact when is_map(p1) and is_map(p2) ->
|
%Contact{pos1: p1, pos2: p2} = contact when is_map(p1) and is_map(p2) ->
|
||||||
process(contact)
|
process_contact(contact)
|
||||||
|
|
||||||
contact ->
|
contact ->
|
||||||
mark_unavailable(contact)
|
mark_unavailable(contact)
|
||||||
|
|
@ -59,7 +59,7 @@ defmodule Microwaveprop.Workers.CommonVolumeRadarWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp process(%Contact{} = contact) do
|
defp process_contact(%Contact{} = contact) do
|
||||||
pos1 = to_latlon(contact.pos1)
|
pos1 = to_latlon(contact.pos1)
|
||||||
pos2 = to_latlon(contact.pos2)
|
pos2 = to_latlon(contact.pos2)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ defmodule Microwaveprop.Workers.ContactImportWorker do
|
||||||
|
|
||||||
@pubsub Microwaveprop.PubSub
|
@pubsub Microwaveprop.PubSub
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"import_run_id" => run_id, "offset" => offset, "limit" => limit}}) do
|
def process(%Oban.Job{args: %{"import_run_id" => run_id, "offset" => offset, "limit" => limit}}) do
|
||||||
case Repo.get(ImportRun, run_id) do
|
case Repo.get(ImportRun, run_id) do
|
||||||
nil ->
|
nil ->
|
||||||
Logger.warning("ContactImportWorker: import_run #{run_id} not found; discarding chunk")
|
Logger.warning("ContactImportWorker: import_run #{run_id} not found; discarding chunk")
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ defmodule Microwaveprop.Workers.ContactPositionBackfillWorker do
|
||||||
|
|
||||||
@default_limit 500
|
@default_limit 500
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
limit = Map.get(args, "limit", @default_limit)
|
limit = Map.get(args, "limit", @default_limit)
|
||||||
|
|
||||||
candidates =
|
candidates =
|
||||||
|
|
|
||||||
|
|
@ -186,8 +186,8 @@ defmodule Microwaveprop.Workers.ContactWeatherEnqueueWorker do
|
||||||
|
|
||||||
defp mark_hrrr_status!(_contact, ids, [_ | _]), do: Radio.set_enrichment_status!(ids, :hrrr_status, :queued)
|
defp mark_hrrr_status!(_contact, ids, [_ | _]), do: Radio.set_enrichment_status!(ids, :hrrr_status, :queued)
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
enqueue_weather_jobs()
|
enqueue_weather_jobs()
|
||||||
enqueue_hrrr_jobs()
|
enqueue_hrrr_jobs()
|
||||||
enqueue_terrain_jobs()
|
enqueue_terrain_jobs()
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ defmodule Microwaveprop.Workers.GefsFetchWorker do
|
||||||
|
|
||||||
@valid_run_hours [0, 6, 12, 18]
|
@valid_run_hours [0, 6, 12, 18]
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"run_time" => run_time_iso, "forecast_hour" => fh}}) when is_integer(fh) do
|
def process(%Oban.Job{args: %{"run_time" => run_time_iso, "forecast_hour" => fh}}) when is_integer(fh) do
|
||||||
with {:ok, run_time, _} <- DateTime.from_iso8601(run_time_iso),
|
with {:ok, run_time, _} <- DateTime.from_iso8601(run_time_iso),
|
||||||
:ok <- validate_run_hour(run_time.hour) do
|
:ok <- validate_run_hour(run_time.hour) do
|
||||||
run_date = DateTime.to_date(run_time)
|
run_date = DateTime.to_date(run_time)
|
||||||
|
|
@ -52,11 +52,11 @@ defmodule Microwaveprop.Workers.GefsFetchWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: args}) when args == %{} do
|
def process(%Oban.Job{args: args}) when args == %{} do
|
||||||
seed_extended_horizon()
|
seed_extended_horizon()
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: _args}), do: {:cancel, :invalid_args}
|
def process(%Oban.Job{args: _args}), do: {:cancel, :invalid_args}
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Forecast hours enqueued per GEFS run: f024 through f168 at the
|
Forecast hours enqueued per GEFS run: f024 through f168 at the
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ defmodule Microwaveprop.Workers.GridCachePruneWorker do
|
||||||
|
|
||||||
@cutoff_hours 3
|
@cutoff_hours 3
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
cutoff = DateTime.add(DateTime.utc_now(), -@cutoff_hours, :hour)
|
cutoff = DateTime.add(DateTime.utc_now(), -@cutoff_hours, :hour)
|
||||||
_removed = GridCache.prune_older_than(cutoff)
|
_removed = GridCache.prune_older_than(cutoff)
|
||||||
:ok
|
:ok
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ defmodule Microwaveprop.Workers.HrdpsGridWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) when args == %{} do
|
def process(%Oban.Job{args: args}) when args == %{} do
|
||||||
Instrument.span([:hrdps, :grid_worker, :perform], %{}, fn ->
|
Instrument.span([:hrdps, :grid_worker, :perform], %{}, fn ->
|
||||||
seed_chain()
|
seed_chain()
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ defmodule Microwaveprop.Workers.HrrrNativeGridWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
%{"year" => year, "month" => month, "day" => day, "hour" => hour} = args
|
%{"year" => year, "month" => month, "day" => day, "hour" => hour} = args
|
||||||
{:ok, date} = Date.new(year, month, day)
|
{:ok, date} = Date.new(year, month, day)
|
||||||
{:ok, valid_time} = DateTime.new(date, Time.new!(hour, 0, 0), "Etc/UTC")
|
{:ok, valid_time} = DateTime.new(date, Time.new!(hour, 0, 0), "Etc/UTC")
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ defmodule Microwaveprop.Workers.IemreFetchWorker do
|
||||||
min(120 * Integer.pow(2, attempt - 1), _six_hours = 21_600)
|
min(120 * Integer.pow(2, attempt - 1), _six_hours = 21_600)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
%{"lat" => lat, "lon" => lon, "date" => date_str} = args
|
%{"lat" => lat, "lon" => lon, "date" => date_str} = args
|
||||||
date = Date.from_iso8601!(date_str)
|
date = Date.from_iso8601!(date_str)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ defmodule Microwaveprop.Workers.IonosphereFetchWorker do
|
||||||
|
|
||||||
@lookback_seconds 2 * 3600
|
@lookback_seconds 2 * 3600
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
now = DateTime.truncate(DateTime.utc_now(), :second)
|
now = DateTime.truncate(DateTime.utc_now(), :second)
|
||||||
from_dt = DateTime.add(now, -@lookback_seconds, :second)
|
from_dt = DateTime.add(now, -@lookback_seconds, :second)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ defmodule Microwaveprop.Workers.MechanismClassifyWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"contact_id" => contact_id}}) do
|
def process(%Oban.Job{args: %{"contact_id" => contact_id}}) do
|
||||||
Microwaveprop.Instrument.span([:worker, :mechanism_classify], %{contact_id: contact_id}, fn ->
|
Microwaveprop.Instrument.span([:worker, :mechanism_classify], %{contact_id: contact_id}, fn ->
|
||||||
case Repo.get(Contact, contact_id) do
|
case Repo.get(Contact, contact_id) do
|
||||||
nil ->
|
nil ->
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ defmodule Microwaveprop.Workers.NarrFetchWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"lat" => lat, "lon" => lon, "valid_time" => valid_time_str}}) do
|
def process(%Oban.Job{args: %{"lat" => lat, "lon" => lon, "valid_time" => valid_time_str}}) do
|
||||||
{:ok, valid_time, _} = DateTime.from_iso8601(valid_time_str)
|
{:ok, valid_time, _} = DateTime.from_iso8601(valid_time_str)
|
||||||
|
|
||||||
rlat = Float.round(lat * 4) / 4
|
rlat = Float.round(lat * 4) / 4
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ defmodule Microwaveprop.Workers.NexradWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
%{"year" => year, "month" => month, "day" => day, "hour" => hour} = args
|
%{"year" => year, "month" => month, "day" => day, "hour" => hour} = args
|
||||||
minute = Map.get(args, "minute", 0)
|
minute = Map.get(args, "minute", 0)
|
||||||
{:ok, date} = Date.new(year, month, day)
|
{:ok, date} = Date.new(year, month, day)
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ defmodule Microwaveprop.Workers.PartitionMaintenanceWorker do
|
||||||
|
|
||||||
@lookahead_quarters 4
|
@lookahead_quarters 4
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
@spec perform(Oban.Job.t()) :: :ok
|
@spec process(Oban.Job.t()) :: :ok
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
lookahead = lookahead(args)
|
lookahead = lookahead(args)
|
||||||
results = PartitionManager.ensure_quarterly_partitions(lookahead)
|
results = PartitionManager.ensure_quarterly_partitions(lookahead)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ defmodule Microwaveprop.Workers.PropagationGridWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) when args == %{} do
|
def process(%Oban.Job{args: args}) when args == %{} do
|
||||||
Instrument.span([:propagation, :grid_worker, :perform], %{}, fn ->
|
Instrument.span([:propagation, :grid_worker, :perform], %{}, fn ->
|
||||||
seed_chain()
|
seed_chain()
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ defmodule Microwaveprop.Workers.PropagationPruneWorker do
|
||||||
|
|
||||||
alias Microwaveprop.Propagation
|
alias Microwaveprop.Propagation
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
Propagation.prune_old_scores()
|
Propagation.prune_old_scores()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -73,14 +73,14 @@ defmodule Microwaveprop.Workers.PskrCalibrationWorker do
|
||||||
# ~1k point batches.
|
# ~1k point batches.
|
||||||
@follow_up_delay_seconds 600
|
@follow_up_delay_seconds 600
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"hour_utc" => iso} = args}) when is_binary(iso) do
|
def process(%Oban.Job{args: %{"hour_utc" => iso} = args}) when is_binary(iso) do
|
||||||
{:ok, hour, _} = DateTime.from_iso8601(iso)
|
{:ok, hour, _} = DateTime.from_iso8601(iso)
|
||||||
process_hour(hour, args)
|
process_hour(hour, args)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
lookback = lookback_hours(args)
|
lookback = lookback_hours(args)
|
||||||
now = truncate_to_hour(DateTime.utc_now())
|
now = truncate_to_hour(DateTime.utc_now())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ defmodule Microwaveprop.Workers.PskrRecalibrationWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
run = Recalibrator.run()
|
run = Recalibrator.run()
|
||||||
Logger.info("PskrRecalibrationWorker: status=#{run.status} sample_count=#{run.sample_count}")
|
Logger.info("PskrRecalibrationWorker: status=#{run.status} sample_count=#{run.sample_count}")
|
||||||
:ok
|
:ok
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ defmodule Microwaveprop.Workers.RadarFrameWorker do
|
||||||
@terminal_statuses [:complete, :unavailable]
|
@terminal_statuses [:complete, :unavailable]
|
||||||
@frame_window_seconds 5 * 60
|
@frame_window_seconds 5 * 60
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"frame_ts" => frame_iso}}) do
|
def process(%Oban.Job{args: %{"frame_ts" => frame_iso}}) do
|
||||||
{:ok, frame_ts, _} = DateTime.from_iso8601(frame_iso)
|
{:ok, frame_ts, _} = DateTime.from_iso8601(frame_iso)
|
||||||
rounded = NexradClient.round_to_5min(frame_ts)
|
rounded = NexradClient.round_to_5min(frame_ts)
|
||||||
bucket_end = DateTime.add(rounded, @frame_window_seconds, :second)
|
bucket_end = DateTime.add(rounded, @frame_window_seconds, :second)
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ defmodule Microwaveprop.Workers.RoverMissionBackfillWorker do
|
||||||
|
|
||||||
alias Microwaveprop.RoverPlanning
|
alias Microwaveprop.RoverPlanning
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
RoverPlanning.backfill_paths()
|
RoverPlanning.backfill_paths()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ defmodule Microwaveprop.Workers.RoverMissionReconcileWorker do
|
||||||
alias Microwaveprop.RoverPlanning.Mission
|
alias Microwaveprop.RoverPlanning.Mission
|
||||||
alias Microwaveprop.RoverPlanning.Station
|
alias Microwaveprop.RoverPlanning.Station
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"mission_id" => mission_id}}) do
|
def process(%Oban.Job{args: %{"mission_id" => mission_id}}) do
|
||||||
case Repo.get(Mission, mission_id) do
|
case Repo.get(Mission, mission_id) do
|
||||||
nil ->
|
nil ->
|
||||||
:ok
|
:ok
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ defmodule Microwaveprop.Workers.RoverPathProfileWorker do
|
||||||
min(60 * Integer.pow(2, attempt - 1), 3600)
|
min(60 * Integer.pow(2, attempt - 1), 3600)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: args}) do
|
def process(%Oban.Job{args: args}) do
|
||||||
case load_path(args) do
|
case load_path(args) do
|
||||||
nil ->
|
nil ->
|
||||||
:ok
|
:ok
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ defmodule Microwaveprop.Workers.SolarIndexWorker do
|
||||||
alias Microwaveprop.Weather
|
alias Microwaveprop.Weather
|
||||||
alias Microwaveprop.Weather.SolarClient
|
alias Microwaveprop.Weather.SolarClient
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"date" => date_str}}) do
|
def process(%Oban.Job{args: %{"date" => date_str}}) do
|
||||||
{:ok, target_date} = Date.from_iso8601(date_str)
|
{:ok, target_date} = Date.from_iso8601(date_str)
|
||||||
|
|
||||||
case SolarClient.fetch_solar_indices() do
|
case SolarClient.fetch_solar_indices() do
|
||||||
|
|
@ -33,7 +33,7 @@ defmodule Microwaveprop.Workers.SolarIndexWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
since_date = Date.add(Date.utc_today(), -7)
|
since_date = Date.add(Date.utc_today(), -7)
|
||||||
|
|
||||||
case SolarClient.fetch_solar_indices() do
|
case SolarClient.fetch_solar_indices() do
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ defmodule Microwaveprop.Workers.SpaceWeatherFetchWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def process(%Oban.Job{}) do
|
||||||
fetch_and_upsert(:kp, &SwpcClient.fetch_kp/0, &SpaceWeather.upsert_kp/1)
|
fetch_and_upsert(:kp, &SwpcClient.fetch_kp/0, &SpaceWeather.upsert_kp/1)
|
||||||
fetch_and_upsert(:f107, &SwpcClient.fetch_f107/0, &SpaceWeather.upsert_solar_flux/1)
|
fetch_and_upsert(:f107, &SwpcClient.fetch_f107/0, &SpaceWeather.upsert_solar_flux/1)
|
||||||
fetch_and_upsert(:xrays, &SwpcClient.fetch_xrays/0, &SpaceWeather.upsert_xray/1)
|
fetch_and_upsert(:xrays, &SwpcClient.fetch_xrays/0, &SpaceWeather.upsert_xray/1)
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ defmodule Microwaveprop.Workers.StationElevationWorker do
|
||||||
alias Microwaveprop.Rover.FixedStation
|
alias Microwaveprop.Rover.FixedStation
|
||||||
alias Microwaveprop.Terrain.Srtm
|
alias Microwaveprop.Terrain.Srtm
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"id" => id}}) do
|
def process(%Oban.Job{args: %{"id" => id}}) do
|
||||||
case Repo.get(FixedStation, id) do
|
case Repo.get(FixedStation, id) do
|
||||||
nil ->
|
nil ->
|
||||||
:ok
|
:ok
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ defmodule Microwaveprop.Workers.TerrainProfileWorker do
|
||||||
min(120 * Integer.pow(2, attempt - 1), _six_hours = 21_600)
|
min(120 * Integer.pow(2, attempt - 1), _six_hours = 21_600)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"contact_id" => contact_id}}) do
|
def process(%Oban.Job{args: %{"contact_id" => contact_id}}) do
|
||||||
Microwaveprop.Instrument.span([:worker, :terrain_profile], %{contact_id: contact_id}, fn ->
|
Microwaveprop.Instrument.span([:worker, :terrain_profile], %{contact_id: contact_id}, fn ->
|
||||||
if Terrain.has_terrain_profile?(contact_id) do
|
if Terrain.has_terrain_profile?(contact_id) do
|
||||||
_ = Radio.set_enrichment_status!([contact_id], :terrain_status, :complete)
|
_ = Radio.set_enrichment_status!([contact_id], :terrain_status, :complete)
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ defmodule Microwaveprop.Workers.UserHomeQthLookupWorker do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"user_id" => user_id}}) do
|
def process(%Oban.Job{args: %{"user_id" => user_id}}) do
|
||||||
case Repo.get(User, user_id) do
|
case Repo.get(User, user_id) do
|
||||||
nil ->
|
nil ->
|
||||||
:ok
|
:ok
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ defmodule Microwaveprop.Workers.WeatherFetchWorker do
|
||||||
min(120 * Integer.pow(2, attempt - 1), _six_hours = 21_600)
|
min(120 * Integer.pow(2, attempt - 1), _six_hours = 21_600)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Pro.Worker
|
||||||
def perform(%Oban.Job{args: %{"fetch_type" => "asos"} = args}) do
|
def process(%Oban.Job{args: %{"fetch_type" => "asos"} = args}) do
|
||||||
%{
|
%{
|
||||||
"station_id" => station_id,
|
"station_id" => station_id,
|
||||||
"station_code" => station_code,
|
"station_code" => station_code,
|
||||||
|
|
@ -41,7 +41,7 @@ defmodule Microwaveprop.Workers.WeatherFetchWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"fetch_type" => "asos_day"} = args}) do
|
def process(%Oban.Job{args: %{"fetch_type" => "asos_day"} = args}) do
|
||||||
%{
|
%{
|
||||||
"station_id" => station_id,
|
"station_id" => station_id,
|
||||||
"station_code" => station_code,
|
"station_code" => station_code,
|
||||||
|
|
@ -65,7 +65,7 @@ defmodule Microwaveprop.Workers.WeatherFetchWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"fetch_type" => "asos_batch"} = args}) do
|
def process(%Oban.Job{args: %{"fetch_type" => "asos_batch"} = args}) do
|
||||||
%{
|
%{
|
||||||
"station_ids" => station_ids,
|
"station_ids" => station_ids,
|
||||||
"station_codes" => station_codes,
|
"station_codes" => station_codes,
|
||||||
|
|
@ -87,7 +87,7 @@ defmodule Microwaveprop.Workers.WeatherFetchWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Oban.Job{args: %{"fetch_type" => "raob"} = args}) do
|
def process(%Oban.Job{args: %{"fetch_type" => "raob"} = args}) do
|
||||||
%{
|
%{
|
||||||
"station_id" => station_id,
|
"station_id" => station_id,
|
||||||
"station_code" => station_code,
|
"station_code" => station_code,
|
||||||
|
|
|
||||||
|
|
@ -161,11 +161,8 @@ defmodule MicrowavepropWeb.ContactLive.Index do
|
||||||
defp visible_query(_), do: from(c in Contact, where: c.private == false)
|
defp visible_query(_), do: from(c in Contact, where: c.private == false)
|
||||||
|
|
||||||
# The data_provider MFA is serialized into the socket; a raw %Scope{}
|
# The data_provider MFA is serialized into the socket; a raw %Scope{}
|
||||||
# with associations isn't safe to store there. Collapse to the minimum
|
# with associations isn't safe to store there. Rehydrate the minimal
|
||||||
# needed to rebuild visibility and rehydrate on each invocation.
|
# scope from the persisted token on each invocation.
|
||||||
defp scope_token(%Scope{user: %User{id: id, is_admin: is_admin}}), do: {id, is_admin}
|
|
||||||
defp scope_token(_), do: nil
|
|
||||||
|
|
||||||
defp scope_from_token({id, true}), do: %Scope{user: %User{id: id, is_admin: true}}
|
defp scope_from_token({id, true}), do: %Scope{user: %User{id: id, is_admin: true}}
|
||||||
defp scope_from_token({id, false}), do: %Scope{user: %User{id: id, is_admin: false}}
|
defp scope_from_token({id, false}), do: %Scope{user: %User{id: id, is_admin: false}}
|
||||||
defp scope_from_token(nil), do: nil
|
defp scope_from_token(nil), do: nil
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ defmodule MicrowavepropWeb.RoverLive do
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
{fixed_stations, persisted?} = load_stations(socket)
|
{fixed_stations, persisted?} = load_stations(socket)
|
||||||
home = home_for(socket)
|
home = home_for(socket)
|
||||||
valid_times = Propagation.available_valid_times(@default_band)
|
|
||||||
current_valid_time = Propagation.latest_valid_time(@default_band)
|
current_valid_time = Propagation.latest_valid_time(@default_band)
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ defmodule Microwaveprop.Propagation.RegionTest do
|
||||||
:southwest,
|
:southwest,
|
||||||
:southeentral,
|
:southeentral,
|
||||||
:midwest,
|
:midwest,
|
||||||
|
:mountain_west,
|
||||||
:other
|
:other
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,17 @@ defmodule Microwaveprop.Propagation.ScorerTest do
|
||||||
utc_minute: 30,
|
utc_minute: 30,
|
||||||
month: 6,
|
month: 6,
|
||||||
longitude: -97.0,
|
longitude: -97.0,
|
||||||
latitude: 32.9
|
latitude: 32.9,
|
||||||
|
sky_cover_pct: 25.0,
|
||||||
|
wind_speed_kts: 5.0,
|
||||||
|
pressure_mb: 1013.0,
|
||||||
|
prev_pressure_mb: 1015.0,
|
||||||
|
abs_humidity: 10.0,
|
||||||
|
temp_f: 80.0,
|
||||||
|
dewpoint_f: 60.0,
|
||||||
|
min_refractivity_gradient: -150.0,
|
||||||
|
bl_depth_m: 500.0,
|
||||||
|
rain_rate_mmhr: 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
describe "score/2" do
|
describe "score/2" do
|
||||||
|
|
@ -22,8 +32,11 @@ defmodule Microwaveprop.Propagation.ScorerTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "score increases with favorable conditions" do
|
test "score increases with favorable conditions" do
|
||||||
good_contact = Map.merge(@contact, %{utc_hour: 6, utc_minute: 0, month: 1})
|
# Same month, same location — dawn inversion peak scores higher than afternoon mixing.
|
||||||
bad_contact = Map.merge(@contact, %{utc_hour: 14, utc_minute: 0, month: 7})
|
# At longitude -97.0 the solar offset is ~-6.5 h, so 13 UTC ≈ 6:30 local (peak inversion)
|
||||||
|
# and 21 UTC ≈ 14:30 local (deep convective mixing).
|
||||||
|
good_contact = Map.merge(@contact, %{utc_hour: 13, utc_minute: 0})
|
||||||
|
bad_contact = Map.merge(@contact, %{utc_hour: 21, utc_minute: 0})
|
||||||
|
|
||||||
good_score = Scorer.score(good_contact, 10_000)
|
good_score = Scorer.score(good_contact, 10_000)
|
||||||
bad_score = Scorer.score(bad_contact, 10_000)
|
bad_score = Scorer.score(bad_contact, 10_000)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ defmodule Microwaveprop.Pskr.AggregatorTest do
|
||||||
}
|
}
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
name = Module.safe_concat([Aggregator, to_string(:erlang.unique_integer([:positive]))])
|
int_part = [:positive] |> :erlang.unique_integer() |> Integer.to_string() |> String.to_atom()
|
||||||
|
name = Module.concat([Aggregator, int_part])
|
||||||
|
|
||||||
pid =
|
pid =
|
||||||
start_supervised!({Aggregator, name: name, flush_ms: 0})
|
start_supervised!({Aggregator, name: name, flush_ms: 0})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue