77 lines
2 KiB
Elixir
77 lines
2 KiB
Elixir
defmodule Towerops.Workers.WeatherSyncWorker do
|
|
@moduledoc """
|
|
Oban worker that periodically fetches weather data for all sites with coordinates.
|
|
|
|
Runs every 15 minutes. Batches API calls with a small delay between each
|
|
to stay well within OpenWeatherMap's free tier (60 calls/min).
|
|
|
|
Also cleans up observations older than 30 days on each run.
|
|
"""
|
|
use Oban.Worker,
|
|
queue: :weather,
|
|
unique: [period: 600, states: [:available, :scheduled, :executing]]
|
|
|
|
alias Towerops.Sites
|
|
alias Towerops.Weather
|
|
|
|
require Logger
|
|
|
|
@impl Oban.Worker
|
|
def perform(%Oban.Job{}) do
|
|
api_key = Application.get_env(:towerops, :openweathermap_api_key)
|
|
|
|
if is_nil(api_key) or api_key == "" do
|
|
Logger.debug("Weather sync skipped: no OpenWeatherMap API key configured")
|
|
:ok
|
|
else
|
|
sync_all_sites()
|
|
Weather.cleanup_old_observations(30)
|
|
schedule_next()
|
|
:ok
|
|
end
|
|
end
|
|
|
|
defp sync_all_sites do
|
|
sites = Sites.list_sites_with_coordinates()
|
|
total = length(sites)
|
|
|
|
if total > 0 do
|
|
Logger.info("Weather sync: fetching weather for #{total} sites")
|
|
results = fetch_all_sites(sites)
|
|
ok_count = Enum.count(results, &(&1 == :ok))
|
|
Logger.info("Weather sync complete: #{ok_count}/#{total} sites updated")
|
|
end
|
|
end
|
|
|
|
defp fetch_all_sites(sites) do
|
|
sites
|
|
|> Enum.with_index(1)
|
|
|> Enum.map(fn {site, idx} ->
|
|
if idx > 1, do: Process.sleep(1_100)
|
|
fetch_site_weather(site)
|
|
end)
|
|
end
|
|
|
|
defp fetch_site_weather(site) do
|
|
case Weather.fetch_and_store(site) do
|
|
{:ok, _obs} ->
|
|
:ok
|
|
|
|
{:error, reason} ->
|
|
Logger.warning("Weather fetch failed for site #{site.name}: #{inspect(reason)}")
|
|
:error
|
|
end
|
|
end
|
|
|
|
defp schedule_next do
|
|
# Schedule next run in 15 minutes
|
|
case %{} |> new(schedule_in: 900) |> Oban.insert() do
|
|
{:ok, _job} = result ->
|
|
result
|
|
|
|
{:error, changeset} = error ->
|
|
Logger.error("Failed to schedule next weather sync job: #{inspect(changeset)}")
|
|
error
|
|
end
|
|
end
|
|
end
|