prop/lib/microwaveprop/rover_planning/path.ex
Graham McIntire d31e783776
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m35s
fix: resolve 18 bugs across LiveViews, schemas, tests, and logic
- MonitorLive.Show: safe nil-guard on current_scope for anonymous access
- Admin.MonitorLive.Index: add phx-update=stream to enable stream ops
- ImportLive: require owner/admin authorization, not_found redirect
- MapLive: store timer refs in assigns, cancel before reschedule
- 10 schemas: add missing foreign_key_constraint on belongs_to
- Soundings: preload :station to eliminate N+1 in path analysis
- PathAnalysis: defensive preload of :station on soundings
- GridTaskEnqueuer: wrap reclaim_stale_running in Repo.transaction()
- HrdpsClient: replace String.to_atom with compile-time atom literals
- Contacts: fix extract_latlon false return for lon=0.0
- Tests: remove duplicate Mox.defmock, unblock swallowed task exits,
  bump refute_receive timeouts from 50ms to 200ms
2026-07-29 07:46:54 -05:00

62 lines
1.8 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)
|> foreign_key_constraint(:mission_id)
|> foreign_key_constraint(:rover_location_id)
|> foreign_key_constraint(:station_id)
|> unique_constraint([:mission_id, :rover_location_id, :station_id, :band_mhz],
name: :rover_mission_paths_unique
)
end
end