Enabled :error_handling, :unknown, :unmatched_returns, :extra_return, :missing_return in an earlier commit and landed a 129-warning baseline. Four parallel agents each fixed a directory slice: - Core contexts (29): Radio, Release, Weather, Beacons, Cache, Backtest.Features, Terrain.Srtm, Ionosphere.GiroClient, Propagation.RunTiming, Accounts.Scope, RepoListener. Fixes were (a) prefix side-effect calls (Task.start, Phoenix.PubSub, Logger, :ets.new) with _ = ; (b) tighten/widen specs that didn't match actual returns; (c) add missing @type t declarations; (d) drop dead parse_int(nil) clause. - Propagation + weather subdirs (15): FreshnessMonitor, NotifyListener, ScoreCache, ScoreCacheReconciler, Weather.FrontalAnalysis, Weather.Grib2.Extractor, Weather.Grib2.Wgrib2, GridCache, HrrrPointEnqueuer, NexradCache. Same patterns — mostly _ = on PubSub / :ets / Repo.insert_all; widened two specs (float -> number) where integer returns were reachable. - Workers (35): BackfillEnqueue, CanadianSoundingFetch, ContactImport, ContactWeatherEnqueue, GefsFetch, IemreFetch, NarrFetch, SolarIndex, TerrainProfile, WeatherFetch. Prefixed Repo.update_all / Radio.set_enrichment_status! / Weather.upsert_* side-effect calls. Fixed one :pattern_match in CanadianSoundingFetch.most_recent_sounding_time/1 where a tautological cond guard generated unreachable code. - Web + Mix tasks + lib_ml (46 of 50): controllers, LiveViews, UserAuth, and 11 mix tasks. Same prefix strategy. 4 remaining warnings originate in LiveTable.LiveResource dep macro expansion and can't be fixed without forking the dep — added .dialyzer_ignore.exs to suppress just those specific file:line pairs. Also wired ignore_warnings in mix.exs dialyzer config. mix dialyzer --format short | grep ^lib/ | wc -l -> 0 mix test: 2163 tests, 3 pre-existing flakes, 0 regressions.
123 lines
4 KiB
Elixir
123 lines
4 KiB
Elixir
defmodule Microwaveprop.Release do
|
|
@moduledoc """
|
|
Release tasks that run in production without Mix installed.
|
|
|
|
Long-running tasks are enqueued as Oban jobs so `eval` returns
|
|
immediately. Check Oban Web at /admin/oban for progress.
|
|
|
|
Usage via `bin/microwaveprop eval`:
|
|
|
|
bin/microwaveprop eval "Microwaveprop.Release.migrate"
|
|
bin/microwaveprop eval "Microwaveprop.Release.backtest_all"
|
|
bin/microwaveprop eval "Microwaveprop.Release.backtest(\"naive_gradient\")"
|
|
bin/microwaveprop eval "Microwaveprop.Release.climatology"
|
|
bin/microwaveprop eval "Microwaveprop.Release.native_backfill(500)"
|
|
bin/microwaveprop eval "Microwaveprop.Release.native_derive"
|
|
bin/microwaveprop eval "Microwaveprop.Release.recalibrate"
|
|
"""
|
|
alias Microwaveprop.Workers.AdminTaskWorker
|
|
alias Microwaveprop.Workers.HrrrNativeGridWorker
|
|
|
|
@app :microwaveprop
|
|
|
|
def migrate do
|
|
_ = load_app()
|
|
|
|
for repo <- repos() do
|
|
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
|
|
end
|
|
end
|
|
|
|
def rollback(repo, version) do
|
|
_ = load_app()
|
|
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
|
|
end
|
|
|
|
def backtest(feature_name) when is_binary(feature_name) do
|
|
_ = start_app()
|
|
{:ok, job} = Oban.insert(AdminTaskWorker.new(%{task: "backtest", feature: feature_name}))
|
|
IO.puts("Enqueued backtest for #{feature_name} (job #{job.id})")
|
|
end
|
|
|
|
def backtest_all do
|
|
_ = start_app()
|
|
{:ok, job} = Oban.insert(AdminTaskWorker.new(%{task: "backtest_all"}))
|
|
IO.puts("Enqueued consolidated backtest (job #{job.id})")
|
|
end
|
|
|
|
def climatology(min_samples \\ 3) do
|
|
_ = start_app()
|
|
{:ok, job} = Oban.insert(AdminTaskWorker.new(%{task: "climatology", min_samples: min_samples}))
|
|
IO.puts("Enqueued climatology build (job #{job.id})")
|
|
end
|
|
|
|
def native_backfill(limit \\ 500) do
|
|
_ = start_app()
|
|
|
|
%{rows: rows} =
|
|
Microwaveprop.Repo.query!(
|
|
"""
|
|
SELECT EXTRACT(YEAR FROM qso_timestamp)::int AS year,
|
|
EXTRACT(MONTH FROM qso_timestamp)::int AS month,
|
|
EXTRACT(DAY FROM qso_timestamp)::int AS day,
|
|
EXTRACT(HOUR FROM qso_timestamp)::int AS hour,
|
|
COUNT(*) AS cnt
|
|
FROM contacts
|
|
WHERE pos1 IS NOT NULL
|
|
AND qso_timestamp >= '2019-01-01'
|
|
GROUP BY 1, 2, 3, 4
|
|
ORDER BY cnt DESC
|
|
LIMIT $1
|
|
""",
|
|
[limit],
|
|
timeout: 60_000
|
|
)
|
|
|
|
IO.puts("Enqueuing #{length(rows)} native backfill jobs...")
|
|
|
|
for [year, month, day, hour, cnt] <- rows do
|
|
args = %{year: year, month: month, day: day, hour: hour}
|
|
|
|
case Oban.insert(HrrrNativeGridWorker.new(args, unique: [period: :infinity])) do
|
|
{:ok, _} -> IO.puts(" #{year}-#{month}-#{day} #{hour}Z (#{cnt} contacts)")
|
|
{:error, _} -> IO.puts(" #{year}-#{month}-#{day} #{hour}Z (skipped)")
|
|
end
|
|
end
|
|
|
|
IO.puts("Done.")
|
|
end
|
|
|
|
def recalibrate do
|
|
_ = start_app()
|
|
{:ok, job} = Oban.insert(AdminTaskWorker.new(%{task: "recalibrate"}))
|
|
IO.puts("Enqueued recalibration (job #{job.id})")
|
|
end
|
|
|
|
# scorer_diff removed — the tool depended on per-cell factors in
|
|
# propagation_scores, which are gone now that scores live as binary
|
|
# files on disk. Left as a stub that tells the operator what
|
|
# happened if they shell into a running release from muscle memory.
|
|
def scorer_diff(_new_weights_json) do
|
|
_ = start_app()
|
|
IO.puts("scorer_diff is disabled: propagation_scores table + factors storage have been dropped.")
|
|
end
|
|
|
|
def native_derive(limit \\ 10_000) do
|
|
_ = start_app()
|
|
{:ok, job} = Oban.insert(AdminTaskWorker.new(%{task: "native_derive", limit: limit}))
|
|
IO.puts("Enqueued native derive (job #{job.id})")
|
|
end
|
|
|
|
defp repos do
|
|
Application.fetch_env!(@app, :ecto_repos)
|
|
end
|
|
|
|
defp load_app do
|
|
{:ok, _} = Application.ensure_all_started(:ssl)
|
|
Application.ensure_loaded(@app)
|
|
end
|
|
|
|
defp start_app do
|
|
Application.ensure_all_started(@app)
|
|
end
|
|
end
|