Mission now carries bands_mhz ({:array, :integer}) — operator picks
one or more bands as multi-checkboxes. enqueue_paths_for builds the
cross product (rover x station x band) and persists each tuple as its
own Path row keyed by (mission_id, rover_location_id, station_id,
band_mhz). The path-profile worker reads band_mhz from the path
itself (legacy single-band jobs without band_mhz in args still resolve
to their unique row).
replace_mission_paths/1 is now a thin alias for reconcile_mission_paths/1
which diffs desired vs actual: stale tuples (old band that the user
unchecked, station they removed, rover-site they deleted) get dropped,
new tuples become :pending and enqueue, surviving :complete rows are
left in place — no more wholesale destruction of already-computed
paths on every edit.
The show table gains a Band column, and band_label() in the mission
summary becomes bands_label() (joins the list with commas).
59 lines
1.7 KiB
Elixir
59 lines
1.7 KiB
Elixir
defmodule Microwaveprop.RoverPlanning.Path do
|
|
@moduledoc """
|
|
A computed (or pending) path-profile result for one rover-location
|
|
→ station pairing inside a mission. `result` holds the same shape
|
|
produced by `MicrowavepropWeb.PathLive`'s synchronous compute, so
|
|
the show page can render it identically.
|
|
"""
|
|
use Ecto.Schema
|
|
|
|
import Ecto.Changeset
|
|
|
|
alias Microwaveprop.Rover.Location
|
|
alias Microwaveprop.RoverPlanning.Mission
|
|
alias Microwaveprop.RoverPlanning.Station
|
|
|
|
@statuses [:pending, :computing, :complete, :failed]
|
|
|
|
@primary_key {:id, :binary_id, autogenerate: true}
|
|
@foreign_key_type :binary_id
|
|
schema "rover_mission_paths" do
|
|
field :band_mhz, :integer
|
|
field :status, Ecto.Enum, values: @statuses, default: :pending
|
|
field :result, :map
|
|
field :error, :string
|
|
field :computed_at, :utc_datetime
|
|
|
|
belongs_to :mission, Mission
|
|
belongs_to :rover_location, Location
|
|
belongs_to :station, Station
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
@type t :: %__MODULE__{}
|
|
|
|
@spec statuses() :: [atom()]
|
|
def statuses, do: @statuses
|
|
|
|
@spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
|
|
def changeset(path, attrs) do
|
|
path
|
|
|> cast(attrs, [
|
|
:band_mhz,
|
|
:status,
|
|
:result,
|
|
:error,
|
|
:computed_at,
|
|
:mission_id,
|
|
:rover_location_id,
|
|
:station_id
|
|
])
|
|
|> validate_required([:mission_id, :rover_location_id, :station_id, :band_mhz, :status])
|
|
|> validate_inclusion(:status, @statuses)
|
|
|> validate_number(:band_mhz, greater_than: 0)
|
|
|> unique_constraint([:mission_id, :rover_location_id, :station_id, :band_mhz],
|
|
name: :rover_mission_paths_unique
|
|
)
|
|
end
|
|
end
|