- Add enqueue_for_qso/1 to directly enqueue weather/HRRR/terrain/IEMRE jobs for a single user-submitted QSO (no cron, no bulk processing) - Submit flow calls enqueue_for_qso instead of generic enqueue worker - Add enrichment queues to prod config for on-demand processing - Guard against HRRR fill values in store_hrrr_profiles (fixes badarith) - Filter QSOs without pos2 in build_terrain_jobs
142 lines
4 KiB
Elixir
142 lines
4 KiB
Elixir
defmodule MicrowavepropWeb.SubmitLive do
|
|
@moduledoc false
|
|
use MicrowavepropWeb, :live_view
|
|
|
|
alias Microwaveprop.Radio
|
|
alias Microwaveprop.Radio.Qso
|
|
alias Microwaveprop.Workers.QsoWeatherEnqueueWorker
|
|
|
|
@band_options [
|
|
{"1296 MHz", "1296"},
|
|
{"2304 MHz", "2304"},
|
|
{"3456 MHz", "3456"},
|
|
{"5760 MHz", "5760"},
|
|
{"10 GHz", "10000"},
|
|
{"24 GHz", "24000"},
|
|
{"47 GHz", "47000"},
|
|
{"76 GHz", "75000"}
|
|
]
|
|
|
|
@mode_options ~w(CW SSB FM FT8 FT4)
|
|
|
|
@impl true
|
|
def mount(_params, _session, socket) do
|
|
changeset = Radio.change_qso(%Qso{})
|
|
|
|
{:ok,
|
|
assign(socket,
|
|
page_title: "Submit QSO",
|
|
form: to_form(changeset),
|
|
band_options: @band_options,
|
|
mode_options: @mode_options,
|
|
submitted_at: nil
|
|
)}
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("validate", %{"qso" => qso_params}, socket) do
|
|
changeset =
|
|
%Qso{}
|
|
|> Radio.change_qso(qso_params)
|
|
|> Map.put(:action, :validate)
|
|
|
|
{:noreply, assign(socket, form: to_form(changeset))}
|
|
end
|
|
|
|
# Minimum milliseconds between submissions per LiveView session.
|
|
# For broader IP-based rate limiting, use a reverse proxy (nginx limit_req, Cloudflare).
|
|
@submission_cooldown_ms 30_000
|
|
|
|
def handle_event("save", %{"qso" => qso_params}, socket) do
|
|
if recently_submitted?(socket) do
|
|
{:noreply, put_flash(socket, :error, "Please wait before submitting another QSO.")}
|
|
else
|
|
case Radio.create_qso(qso_params) do
|
|
{:ok, qso} ->
|
|
QsoWeatherEnqueueWorker.enqueue_for_qso(qso)
|
|
|
|
{:noreply,
|
|
socket
|
|
|> assign(submitted_at: System.monotonic_time(:millisecond))
|
|
|> put_flash(:info, "QSO submitted successfully!")
|
|
|> push_navigate(to: ~p"/qsos/#{qso.id}")}
|
|
|
|
{:error, changeset} ->
|
|
{:noreply, assign(socket, form: to_form(changeset))}
|
|
end
|
|
end
|
|
end
|
|
|
|
defp recently_submitted?(socket) do
|
|
case socket.assigns.submitted_at do
|
|
nil -> false
|
|
ts -> System.monotonic_time(:millisecond) - ts < @submission_cooldown_ms
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def render(assigns) do
|
|
~H"""
|
|
<Layouts.app flash={@flash}>
|
|
<.header>
|
|
Submit QSO
|
|
<:subtitle>Submit a microwave QSO for propagation analysis</:subtitle>
|
|
</.header>
|
|
|
|
<.form for={@form} id="qso-form" phx-change="validate" phx-submit="save" class="space-y-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<.input field={@form[:station1]} type="text" label="Station 1" placeholder="W5XD" />
|
|
<.input
|
|
field={@form[:grid1]}
|
|
type="text"
|
|
label="Grid 1"
|
|
placeholder="EM12kp"
|
|
phx-debounce="blur"
|
|
/>
|
|
<.input field={@form[:station2]} type="text" label="Station 2" placeholder="K5TR" />
|
|
<.input
|
|
field={@form[:grid2]}
|
|
type="text"
|
|
label="Grid 2"
|
|
placeholder="EM00cd"
|
|
phx-debounce="blur"
|
|
/>
|
|
</div>
|
|
|
|
<p class="text-sm text-base-content/60 -mt-2">
|
|
Be as specific as possible with grid squares (6 characters preferred, e.g. EM12kp).
|
|
</p>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<.input
|
|
field={@form[:band]}
|
|
type="select"
|
|
label="Band"
|
|
prompt="Select band"
|
|
options={@band_options}
|
|
/>
|
|
<.input
|
|
field={@form[:mode]}
|
|
type="select"
|
|
label="Mode"
|
|
prompt="Select mode"
|
|
options={@mode_options}
|
|
/>
|
|
<.input field={@form[:qso_timestamp]} type="datetime-local" label="Timestamp (UTC)" />
|
|
</div>
|
|
|
|
<.input
|
|
field={@form[:submitter_email]}
|
|
type="email"
|
|
label="Your Email"
|
|
placeholder="you@example.com"
|
|
/>
|
|
|
|
<div class="mt-6">
|
|
<.button phx-disable-with="Submitting..." class="btn-primary">Submit QSO</.button>
|
|
</div>
|
|
</.form>
|
|
</Layouts.app>
|
|
"""
|
|
end
|
|
end
|