- endpoint.ex log_level/1 now filters by conn.request_path instead of
conn.path_info. Plug.Router.forward/2 (deps/plug/lib/plug.ex:170)
rewrites path_info to the unmatched remainder and extends script_name
with the matched prefix before dispatching to the forwarded plug.
/metrics is routed via forward "/metrics", MetricsPlug; by the time
Plug.Telemetry's before_send callback fires inside MetricsPlug.call/2
the conn it observes has path_info: [] and script_name: ["metrics"],
so the old log_level(%{path_info: ["metrics" | _]}) clause never
matched and the default :info level fired. request_path is set by
the adapter at entry and is never rewritten, making it the correct
discriminator. New regression test in metrics_log_suppression_test.exs
captures both the direct shape and the integration path via Plug.Test.
- Scorer.dbz_to_rain_rate_mmhr/1 now uses a compile-time @mp_inv_b
constant (1 / 1.6) instead of dividing on every rain pixel.
composite_score/2's band-invariant fallback switched from four
separate short-circuits (which silently
mixed cached and freshly-computed values if only some keys were
passed) to a single Map.has_key?/2 branch that honors the "all or
none" contract. Dropped unused band_invariant_tod/1 helper.
- Propagation.replace_scores span scope tightened: the
Instrument.span([:db, :replace_scores]) now wraps only the per-band
ScoresFile.write! loop, not the upstream Enum.group_by grouping
phase. The span name + metadata stay unchanged so Grafana panels
keep working, but the fixed telemetry dispatch cost (~100µs x 2)
is no longer paid for trivially small result sets.
- Added @type t :: %__MODULE__{...} to Accounts.UserToken and
Weather.HrrrClimatology — the last two schemas that lacked one.
Elixir 1.19's set-theoretic inference benefits from every struct
having an explicit t/0 so callers can flow through tightly.
mix dialyzer --format short | grep ^lib/ | wc -l -> 0
mix test: 2165 tests, 3 pre-existing flakes, 0 new regressions.
48 lines
1.6 KiB
Elixir
48 lines
1.6 KiB
Elixir
defmodule Microwaveprop.Weather.HrrrClimatology do
|
|
@moduledoc """
|
|
Pre-computed mean and stddev of surface temperature from
|
|
`hrrr_profiles`, keyed on `(lat, lon, month, hour)`.
|
|
|
|
Used by Phase 6 to compute temperature anomaly: how much hotter
|
|
(or cooler) a given hour is compared to the same grid cell's
|
|
typical value for that month and time of day. The meteorologist
|
|
noted that extremely hot days (~10°F above normal in summer)
|
|
produce enhanced ducting even in the afternoon when the
|
|
time-of-day factor normally suppresses the score.
|
|
"""
|
|
use Ecto.Schema
|
|
|
|
import Ecto.Changeset
|
|
|
|
@primary_key {:id, :binary_id, autogenerate: true}
|
|
@foreign_key_type :binary_id
|
|
|
|
schema "hrrr_climatology" do
|
|
field :lat, :float
|
|
field :lon, :float
|
|
field :month, :integer
|
|
field :hour, :integer
|
|
field :mean_surface_temp_c, :float
|
|
field :stddev_surface_temp_c, :float
|
|
field :sample_count, :integer
|
|
end
|
|
|
|
@type t :: %__MODULE__{
|
|
id: Ecto.UUID.t() | nil,
|
|
lat: float() | nil,
|
|
lon: float() | nil,
|
|
month: 1..12 | nil,
|
|
hour: 0..23 | nil,
|
|
mean_surface_temp_c: float() | nil,
|
|
stddev_surface_temp_c: float() | nil,
|
|
sample_count: non_neg_integer() | nil
|
|
}
|
|
|
|
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
|
def changeset(record, attrs) do
|
|
record
|
|
|> cast(attrs, [:lat, :lon, :month, :hour, :mean_surface_temp_c, :stddev_surface_temp_c, :sample_count])
|
|
|> validate_required([:lat, :lon, :month, :hour])
|
|
|> unique_constraint([:lat, :lon, :month, :hour])
|
|
end
|
|
end
|