# ================================================================ # services/data/app/api/weather.py - FIXED VERSION # ================================================================ """Weather data API endpoints with improved error handling""" from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from typing import List, Optional from datetime import datetime, timedelta import structlog from app.core.database import get_db from app.core.auth import get_current_user, AuthInfo from app.services.weather_service import WeatherService from app.services.messaging import publish_weather_updated from app.schemas.external import ( WeatherDataResponse, WeatherForecastResponse, LocationRequest, DateRangeRequest ) router = APIRouter() weather_service = WeatherService() logger = structlog.get_logger() @router.get("/current", response_model=WeatherDataResponse) async def get_current_weather( latitude: float = Query(..., description="Latitude"), longitude: float = Query(..., description="Longitude"), current_user: AuthInfo = Depends(get_current_user) ): """Get current weather for location""" try: logger.debug("API: Getting current weather", lat=latitude, lon=longitude) weather = await weather_service.get_current_weather(latitude, longitude) if not weather: logger.warning("No weather data available", lat=latitude, lon=longitude) raise HTTPException(status_code=404, detail="Weather data not available") logger.debug("Successfully returning weather data", temp=weather.temperature) return weather except HTTPException: # Re-raise HTTP exceptions raise except Exception as e: logger.error("Unexpected error in weather API", error=str(e)) import traceback logger.error("Weather API traceback", traceback=traceback.format_exc()) raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") @router.get("/forecast", response_model=List[WeatherForecastResponse]) async def get_weather_forecast( latitude: float = Query(..., description="Latitude"), longitude: float = Query(..., description="Longitude"), days: int = Query(7, description="Number of forecast days", ge=1, le=14), current_user: AuthInfo = Depends(get_current_user) ): """Get weather forecast for location""" try: logger.debug("API: Getting weather forecast", lat=latitude, lon=longitude, days=days) forecast = await weather_service.get_weather_forecast(latitude, longitude, days) if not forecast: logger.warning("No forecast data available", lat=latitude, lon=longitude) raise HTTPException(status_code=404, detail="Weather forecast not available") # Publish event (with error handling) try: await publish_weather_updated({ "type": "forecast_requested", "latitude": latitude, "longitude": longitude, "days": days, "timestamp": datetime.utcnow().isoformat() }) except Exception as pub_error: logger.warning("Failed to publish weather forecast event", error=str(pub_error)) # Continue processing - event publishing failure shouldn't break the API logger.debug("Successfully returning forecast data", count=len(forecast)) return forecast except HTTPException: # Re-raise HTTP exceptions raise except Exception as e: logger.error("Unexpected error in weather forecast API", error=str(e)) import traceback logger.error("Weather forecast API traceback", traceback=traceback.format_exc()) raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") @router.get("/historical", response_model=List[WeatherDataResponse]) async def get_historical_weather( latitude: float = Query(..., description="Latitude"), longitude: float = Query(..., description="Longitude"), start_date: datetime = Query(..., description="Start date"), end_date: datetime = Query(..., description="End date"), db: AsyncSession = Depends(get_db), current_user: AuthInfo = Depends(get_current_user) ): """Get historical weather data""" try: # Validate date range if end_date <= start_date: raise HTTPException(status_code=400, detail="End date must be after start date") if (end_date - start_date).days > 365: raise HTTPException(status_code=400, detail="Date range cannot exceed 365 days") historical_data = await weather_service.get_historical_weather( latitude, longitude, start_date, end_date, db ) # Publish event (with error handling) try: await publish_weather_updated({ "type": "historical_requested", "latitude": latitude, "longitude": longitude, "start_date": start_date.isoformat(), "end_date": end_date.isoformat(), "records_count": len(historical_data), "timestamp": datetime.utcnow().isoformat() }) except Exception as pub_error: logger.warning("Failed to publish historical weather event", error=str(pub_error)) # Continue processing return historical_data except HTTPException: # Re-raise HTTP exceptions raise except Exception as e: logger.error("Unexpected error in historical weather API", error=str(e)) raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") @router.post("/store") async def store_weather_data( latitude: float = Query(..., description="Latitude"), longitude: float = Query(..., description="Longitude"), db: AsyncSession = Depends(get_db), current_user: AuthInfo = Depends(get_current_user) ): """Store current weather data to database""" try: # Get current weather data weather = await weather_service.get_current_weather(latitude, longitude) if not weather: raise HTTPException(status_code=404, detail="No weather data to store") # Convert to dict for storage weather_dict = { "date": weather.date, "temperature": weather.temperature, "precipitation": weather.precipitation, "humidity": weather.humidity, "wind_speed": weather.wind_speed, "pressure": weather.pressure, "description": weather.description, "source": weather.source } success = await weather_service.store_weather_data( latitude, longitude, weather_dict, db ) if success: return {"status": "success", "message": "Weather data stored successfully"} else: raise HTTPException(status_code=500, detail="Failed to store weather data") except HTTPException: raise except Exception as e: logger.error("Error storing weather data", error=str(e)) raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")