Task 9.3 - Weight recalibration via gradient descent:
- Recalibrator module fits logistic regression weights using Nx
- Trains on QSO positives vs random baseline negatives
- Cross-validates by month, normalizes weights to sum to 1.0
- Mix task: mix recalibrate_scorer --sample 5000 --epochs 2000
Task 9.4 - Side-by-side scorer comparison:
- ScorerDiff.compare/3 re-scores grid with old vs new weights
- Reports mean diff, regressions, improvements, per-band breakdown
- Mix task: mix scorer_diff --new-weights '{...}'
Phase 3 - NEXRAD ingestion pipeline:
- NexradClient fetches IEM n0q composite PNGs, extracts per-point
box statistics (mean/max dBZ, texture variance)
- NexradObservation schema with unique (lat, lon, observed_at)
- NexradWorker on :nexrad queue for background processing
- nexrad_texture backtest feature in Features module
- mix nexrad_backfill --limit 200
All tasks added to AdminTaskWorker and Release for production use.
1116 tests, 0 failures.
63 lines
1.9 KiB
Elixir
63 lines
1.9 KiB
Elixir
defmodule Microwaveprop.Weather.NexradObservationTest do
|
|
use Microwaveprop.DataCase, async: true
|
|
|
|
alias Microwaveprop.Weather.NexradObservation
|
|
|
|
@valid_attrs %{
|
|
observed_at: ~U[2022-08-20 20:00:00Z],
|
|
lat: 32.875,
|
|
lon: -97.0,
|
|
mean_reflectivity_dbz: 12.5,
|
|
max_reflectivity_dbz: 35.0,
|
|
texture_variance: 48.7,
|
|
pixel_count: 180
|
|
}
|
|
|
|
describe "changeset/2" do
|
|
test "valid attributes produce a valid changeset" do
|
|
changeset = NexradObservation.changeset(%NexradObservation{}, @valid_attrs)
|
|
assert changeset.valid?
|
|
end
|
|
|
|
test "requires observed_at, lat, and lon" do
|
|
changeset = NexradObservation.changeset(%NexradObservation{}, %{})
|
|
|
|
assert %{
|
|
observed_at: ["can't be blank"],
|
|
lat: ["can't be blank"],
|
|
lon: ["can't be blank"]
|
|
} = errors_on(changeset)
|
|
end
|
|
|
|
test "numeric fields are optional" do
|
|
attrs = Map.take(@valid_attrs, [:observed_at, :lat, :lon])
|
|
changeset = NexradObservation.changeset(%NexradObservation{}, attrs)
|
|
assert changeset.valid?
|
|
end
|
|
|
|
test "persists to the database" do
|
|
changeset = NexradObservation.changeset(%NexradObservation{}, @valid_attrs)
|
|
assert {:ok, obs} = Repo.insert(changeset)
|
|
assert obs.lat == 32.875
|
|
assert obs.lon == -97.0
|
|
assert obs.mean_reflectivity_dbz == 12.5
|
|
assert obs.max_reflectivity_dbz == 35.0
|
|
assert obs.texture_variance == 48.7
|
|
assert obs.pixel_count == 180
|
|
end
|
|
|
|
test "enforces unique constraint on lat, lon, observed_at" do
|
|
assert {:ok, _} =
|
|
%NexradObservation{}
|
|
|> NexradObservation.changeset(@valid_attrs)
|
|
|> Repo.insert()
|
|
|
|
assert {:error, changeset} =
|
|
%NexradObservation{}
|
|
|> NexradObservation.changeset(@valid_attrs)
|
|
|> Repo.insert()
|
|
|
|
assert %{lat: ["has already been taken"]} = errors_on(changeset)
|
|
end
|
|
end
|
|
end
|