defmodule Microwaveprop.Radio.ImportRun do @moduledoc """ Tracks an async CSV import: the full set of rows to process, running counters the `ContactImportWorker` increments as it processes chunks, and a status that moves from `"pending"` → `"running"` → `"complete"` (or `"failed"`). The `rows` field is a JSONB blob shaped as `%{"rows" => [...], "refinement_ids" => [...]}` so workers can tell which rows are new-contact inserts vs refinements of an existing contact. """ use Ecto.Schema import Ecto.Changeset @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id @statuses ~w(pending running complete failed) schema "import_runs" do field :submitter_email, :string field :rows, :map field :total_rows, :integer field :processed_rows, :integer, default: 0 field :imported_count, :integer, default: 0 field :refined_count, :integer, default: 0 field :error_count, :integer, default: 0 field :errors, :map, default: %{} field :status, :string, default: "pending" field :started_at, :utc_datetime_usec field :completed_at, :utc_datetime_usec timestamps(type: :utc_datetime_usec) end @type t :: %__MODULE__{} @cast_fields ~w(submitter_email rows total_rows processed_rows imported_count refined_count error_count errors status started_at completed_at)a @required_fields ~w(submitter_email rows total_rows)a @spec statuses() :: [String.t()] def statuses, do: @statuses @spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t() def changeset(run, attrs) do run |> cast(attrs, @cast_fields) |> validate_required(@required_fields) |> validate_inclusion(:status, @statuses) |> validate_number(:total_rows, greater_than_or_equal_to: 0) |> validate_number(:processed_rows, greater_than_or_equal_to: 0) |> validate_number(:imported_count, greater_than_or_equal_to: 0) |> validate_number(:refined_count, greater_than_or_equal_to: 0) |> validate_number(:error_count, greater_than_or_equal_to: 0) end end