Add OpenWeatherMap weather forecasts. Closes #48

Show 8-day Bristol home weather on the index and weekends pages.
Show destination weather per day on the trip list and trip page.
Cache forecasts in ~/lib/data/weather/ and refresh via update.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2026-02-21 20:27:25 +00:00
parent 61e17d9c96
commit b4f0a5bf5d
7 changed files with 217 additions and 0 deletions

View file

@ -19,9 +19,11 @@ import agenda.geomob
import agenda.gwr
import agenda.mail
import agenda.thespacedevs
import agenda.trip
import agenda.types
import agenda.uk_holiday
import agenda.uk_school_holiday
import agenda.weather
from agenda.event import Event
from agenda.types import StrDict
from web_view import app
@ -406,6 +408,40 @@ def update_gandi(config: flask.config.Config) -> None:
out.write(r.text)
def update_weather(config: flask.config.Config) -> None:
"""Refresh weather cache for all upcoming trips."""
from datetime import date, timedelta
today = date.today()
forecast_window = today + timedelta(days=8)
trips = agenda.trip.build_trip_list()
upcoming = [
t
for t in trips
if (t.end or t.start) >= today and t.start <= forecast_window
]
seen: set[tuple[float, float]] = set()
count = 0
for trip in upcoming:
latlon = agenda.weather.trip_latlon(trip)
if not latlon or latlon in seen:
continue
seen.add(latlon)
lat, lon = latlon
try:
agenda.weather.get_forecast(
config["DATA_DIR"], config["OPENWEATHERMAP_API_KEY"], lat, lon
)
count += 1
except Exception as exc:
print(f"weather update failed for {lat},{lon}: {exc}")
if sys.stdin.isatty():
print(f"updated weather for {count} location(s)")
def check_birthday_reminders(config: flask.config.Config) -> None:
"""Send at most one grouped birthday reminder email per day.
@ -497,6 +533,7 @@ def main() -> None:
agenda.fx.get_rates(app.config)
update_weather(app.config)
update_thespacedevs(app.config)
# Check for birthday reminders daily at 9 AM