Files
bakery-ia/services/data/app/services/weather_service.py
2025-07-18 19:55:57 +02:00

156 lines
7.3 KiB
Python

# ================================================================
# services/data/app/services/weather_service.py - FIXED VERSION
# ================================================================
"""Weather data service with improved error handling"""
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_
import structlog
from app.models.weather import WeatherData, WeatherForecast
from app.external.aemet import AEMETClient
from app.schemas.external import WeatherDataResponse, WeatherForecastResponse
logger = structlog.get_logger()
class WeatherService:
def __init__(self):
self.aemet_client = AEMETClient()
async def get_current_weather(self, latitude: float, longitude: float) -> Optional[WeatherDataResponse]:
"""Get current weather for location"""
try:
logger.debug("Getting current weather", lat=latitude, lon=longitude)
weather_data = await self.aemet_client.get_current_weather(latitude, longitude)
if weather_data:
logger.debug("Weather data received", source=weather_data.get('source'))
return WeatherDataResponse(**weather_data)
else:
logger.warning("No weather data received from AEMET client")
return None
except Exception as e:
logger.error("Failed to get current weather", error=str(e), lat=latitude, lon=longitude)
return None
async def get_weather_forecast(self, latitude: float, longitude: float, days: int = 7) -> List[WeatherForecastResponse]:
"""Get weather forecast for location"""
try:
logger.debug("Getting weather forecast", lat=latitude, lon=longitude, days=days)
forecast_data = await self.aemet_client.get_forecast(latitude, longitude, days)
if forecast_data:
logger.debug("Forecast data received", count=len(forecast_data))
# Validate each forecast item before creating response
valid_forecasts = []
for item in forecast_data:
try:
if isinstance(item, dict):
# Ensure required fields are present
forecast_item = {
"forecast_date": item.get("forecast_date", datetime.now()),
"generated_at": item.get("generated_at", datetime.now()),
"temperature": float(item.get("temperature", 15.0)),
"precipitation": float(item.get("precipitation", 0.0)),
"humidity": float(item.get("humidity", 50.0)),
"wind_speed": float(item.get("wind_speed", 10.0)),
"description": str(item.get("description", "Variable")),
"source": str(item.get("source", "unknown"))
}
valid_forecasts.append(WeatherForecastResponse(**forecast_item))
else:
logger.warning("Invalid forecast item type", item_type=type(item))
except Exception as item_error:
logger.warning("Error processing forecast item", error=str(item_error), item=item)
continue
logger.debug("Valid forecasts processed", count=len(valid_forecasts))
return valid_forecasts
else:
logger.warning("No forecast data received from AEMET client")
return []
except Exception as e:
logger.error("Failed to get weather forecast", error=str(e), lat=latitude, lon=longitude)
return []
async def get_historical_weather(self,
latitude: float,
longitude: float,
start_date: datetime,
end_date: datetime,
db: AsyncSession) -> List[WeatherDataResponse]:
"""Get historical weather data"""
try:
logger.debug("Getting historical weather",
lat=latitude, lon=longitude,
start=start_date, end=end_date)
# First check database
location_id = f"{latitude:.4f},{longitude:.4f}"
stmt = select(WeatherData).where(
and_(
WeatherData.location_id == location_id,
WeatherData.date >= start_date,
WeatherData.date <= end_date
)
).order_by(WeatherData.date)
result = await db.execute(stmt)
db_records = result.scalars().all()
if db_records:
logger.debug("Historical data found in database", count=len(db_records))
return [WeatherDataResponse(
date=record.date,
temperature=record.temperature,
precipitation=record.precipitation,
humidity=record.humidity,
wind_speed=record.wind_speed,
pressure=record.pressure,
description=record.description,
source=record.source
) for record in db_records]
# If not in database, fetch from API and store
logger.debug("Fetching historical data from AEMET API")
weather_data = await self.aemet_client.get_historical_weather(
latitude, longitude, start_date, end_date
)
if weather_data:
# Store in database for future use
try:
for data in weather_data:
weather_record = WeatherData(
location_id=location_id,
date=data.get('date', datetime.now()),
temperature=data.get('temperature'),
precipitation=data.get('precipitation'),
humidity=data.get('humidity'),
wind_speed=data.get('wind_speed'),
pressure=data.get('pressure'),
description=data.get('description'),
source="aemet",
raw_data=str(data)
)
db.add(weather_record)
await db.commit()
logger.debug("Historical data stored in database", count=len(weather_data))
except Exception as db_error:
logger.warning("Failed to store historical data in database", error=str(db_error))
await db.rollback()
return [WeatherDataResponse(**item) for item in weather_data]
else:
logger.warning("No historical weather data received")
return []
except Exception as e:
logger.error("Failed to get historical weather", error=str(e))
return []