diff --git a/lib/mix/tasks/towerops.fix_mikrotik_temperature_scaling.ex b/lib/mix/tasks/towerops.fix_mikrotik_temperature_scaling.ex index 2499d6cc..574280de 100644 --- a/lib/mix/tasks/towerops.fix_mikrotik_temperature_scaling.ex +++ b/lib/mix/tasks/towerops.fix_mikrotik_temperature_scaling.ex @@ -2,57 +2,25 @@ defmodule Mix.Tasks.Towerops.FixMikrotikTemperatureScaling do @shortdoc "Renormalise stored temperature sensor readings using SensorScale" @moduledoc """ - One-shot backfill that re-runs `Towerops.Snmp.SensorScale.normalize/2` - over every stored temperature reading. Catches historical mis-scaled - rows from MikroTik `mtxrGaugeValue` (some models report deci-degrees) - and any other sensor whose YAML profile didn't include the right - divisor at discovery time. - - Run from inside a release pod: - - /app/bin/towerops eval 'Mix.Tasks.Towerops.FixMikrotikTemperatureScaling.run([])' - - Or via mix during dev: + Dev-only convenience wrapper. Delegates to + `Towerops.Maintenance.FixMikrotikTemperatureScaling.run/0` so you can + run the backfill from a local checkout via: mix towerops.fix_mikrotik_temperature_scaling + + In prod the release has no `mix` binary — invoke the plain module + directly: + + /app/bin/towerops eval 'Towerops.Maintenance.FixMikrotikTemperatureScaling.run()' """ use Mix.Task - import Ecto.Query - - alias Towerops.Repo - alias Towerops.Snmp.Sensor - alias Towerops.Snmp.SensorScale + alias Towerops.Maintenance.FixMikrotikTemperatureScaling @impl Mix.Task def run(_argv) do {:ok, _} = Application.ensure_all_started(:towerops) - - sensors = - Repo.all( - from(s in Sensor, where: s.sensor_type == "temperature" and not is_nil(s.last_value) and s.last_value > 150.0) - ) - - {fixed, skipped} = - Enum.reduce(sensors, {0, 0}, fn sensor, {fixed, skipped} -> - normalized = SensorScale.normalize(sensor.sensor_type, sensor.last_value) - - if normalized == sensor.last_value do - {fixed, skipped + 1} - else - {:ok, _} = - sensor - |> Sensor.changeset(%{last_value: Float.round(normalized / 1.0, 2)}) - |> Repo.update() - - {fixed + 1, skipped} - end - end) - - Mix.shell().info( - "Temperature backfill complete: rescaled #{fixed} rows, " <> - "left #{skipped} unchanged." - ) + FixMikrotikTemperatureScaling.run() end end diff --git a/lib/towerops/maintenance/fix_mikrotik_temperature_scaling.ex b/lib/towerops/maintenance/fix_mikrotik_temperature_scaling.ex new file mode 100644 index 00000000..6abfb454 --- /dev/null +++ b/lib/towerops/maintenance/fix_mikrotik_temperature_scaling.ex @@ -0,0 +1,51 @@ +defmodule Towerops.Maintenance.FixMikrotikTemperatureScaling do + @moduledoc """ + One-shot backfill that re-runs `Towerops.Snmp.SensorScale.normalize/2` + over every stored temperature reading. Catches historical mis-scaled + rows from MikroTik `mtxrGaugeValue` (some models report deci-degrees) + and any other sensor whose YAML profile didn't include the right + divisor at discovery time. + + Plain module rather than a `Mix.Task` so it can run from the prod + Elixir release (where `mix` itself isn't installed): + + /app/bin/towerops eval 'Towerops.Maintenance.FixMikrotikTemperatureScaling.run()' + """ + + import Ecto.Query + + alias Towerops.Repo + alias Towerops.Snmp.Sensor + alias Towerops.Snmp.SensorScale + + require Logger + + @spec run() :: :ok + def run do + sensors = + Repo.all( + from(s in Sensor, where: s.sensor_type == "temperature" and not is_nil(s.last_value) and s.last_value > 150.0) + ) + + {fixed, skipped} = + Enum.reduce(sensors, {0, 0}, fn sensor, {fixed, skipped} -> + normalized = SensorScale.normalize(sensor.sensor_type, sensor.last_value) + + if normalized == sensor.last_value do + {fixed, skipped + 1} + else + {:ok, _} = + sensor + |> Sensor.changeset(%{last_value: Float.round(normalized / 1.0, 2)}) + |> Repo.update() + + {fixed + 1, skipped} + end + end) + + msg = "Temperature backfill complete: rescaled #{fixed} rows, left #{skipped} unchanged." + IO.puts(msg) + Logger.info(msg) + :ok + end +end