"""Weather data router -- primary + secondary locations and hourly forecast.""" from __future__ import annotations import asyncio import logging from typing import Any, Dict, List from fastapi import APIRouter from server.cache import cache from server.config import get_settings from server.services.weather_service import fetch_hourly_forecast, fetch_weather logger = logging.getLogger(__name__) router = APIRouter(prefix="/api", tags=["weather"]) CACHE_KEY = "weather" @router.get("/weather") async def get_weather() -> Dict[str, Any]: """Return weather for both configured locations plus an hourly forecast. The response shape is:: { "primary": { ... weather dict or error stub }, "secondary": { ... weather dict or error stub }, "hourly": [ ... forecast entries or empty list ], } """ # --- cache hit? ----------------------------------------------------------- cached = await cache.get(CACHE_KEY) if cached is not None: return cached # --- cache miss -- fetch all three in parallel ---------------------------- primary_data: Dict[str, Any] = {} secondary_data: Dict[str, Any] = {} hourly_data: List[Dict[str, Any]] = [] results = await asyncio.gather( _safe_fetch_weather(get_settings().weather_location), _safe_fetch_weather(get_settings().weather_location_secondary), _safe_fetch_hourly(get_settings().weather_location), return_exceptions=False, # we handle errors inside the helpers ) primary_data = results[0] secondary_data = results[1] hourly_data = results[2] payload: Dict[str, Any] = { "primary": primary_data, "secondary": secondary_data, "hourly": hourly_data, } await cache.set(CACHE_KEY, payload, get_settings().weather_cache_ttl) return payload # -- internal helpers --------------------------------------------------------- async def _safe_fetch_weather(location: str) -> Dict[str, Any]: """Fetch weather for *location*, returning an error stub on failure.""" try: data = await fetch_weather(location) return data except Exception as exc: logger.exception("Failed to fetch weather for %s", location) return {"error": True, "message": str(exc), "location": location} async def _safe_fetch_hourly(location: str) -> List[Dict[str, Any]]: """Fetch hourly forecast for *location*, returning ``[]`` on failure.""" try: data = await fetch_hourly_forecast(location) return data except Exception as exc: logger.exception("Failed to fetch hourly forecast for %s", location) return []