Files
bakery-ia/services/data/app/api/weather.py
2025-07-21 13:09:30 +02:00

208 lines
7.7 KiB
Python

# services/data/app/api/weather.py - UPDATED WITH UNIFIED AUTH
"""Weather data API endpoints with unified authentication"""
from fastapi import APIRouter, Depends, HTTPException, Query, BackgroundTasks
from typing import List, Optional, Dict, Any
from datetime import datetime, date
import structlog
from app.schemas.weather import (
WeatherDataResponse,
WeatherForecastResponse,
WeatherSummaryResponse
)
from app.services.weather_service import WeatherService
from app.services.messaging import publish_weather_updated
# Import unified authentication from shared library
from shared.auth.decorators import (
get_current_user_dep,
get_current_tenant_id_dep
)
router = APIRouter(prefix="/weather", tags=["weather"])
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"),
tenant_id: str = Depends(get_current_tenant_id_dep),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
):
"""Get current weather data for location"""
try:
logger.debug("Getting current weather",
lat=latitude,
lon=longitude,
tenant_id=tenant_id,
user_id=current_user["user_id"])
weather_service = WeatherService()
weather = await weather_service.get_current_weather(latitude, longitude)
if not weather:
raise HTTPException(status_code=404, detail="Weather data not available")
# Publish event
try:
await publish_weather_updated({
"type": "current_weather_requested",
"tenant_id": tenant_id,
"latitude": latitude,
"longitude": longitude,
"requested_by": current_user["user_id"],
"timestamp": datetime.utcnow().isoformat()
})
except Exception as e:
logger.warning("Failed to publish weather event", error=str(e))
return weather
except HTTPException:
raise
except Exception as e:
logger.error("Failed to get current weather", error=str(e))
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),
tenant_id: str = Depends(get_current_tenant_id_dep),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
):
"""Get weather forecast for location"""
try:
logger.debug("Getting weather forecast",
lat=latitude,
lon=longitude,
days=days,
tenant_id=tenant_id)
weather_service = WeatherService()
forecast = await weather_service.get_weather_forecast(latitude, longitude, days)
if not forecast:
raise HTTPException(status_code=404, detail="Weather forecast not available")
# Publish event
try:
await publish_weather_updated({
"type": "forecast_requested",
"tenant_id": tenant_id,
"latitude": latitude,
"longitude": longitude,
"days": days,
"requested_by": current_user["user_id"],
"timestamp": datetime.utcnow().isoformat()
})
except Exception as e:
logger.warning("Failed to publish forecast event", error=str(e))
return forecast
except HTTPException:
raise
except Exception as e:
logger.error("Failed to get weather forecast", error=str(e))
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
@router.get("/history", response_model=List[WeatherDataResponse])
async def get_weather_history(
start_date: date = Query(..., description="Start date"),
end_date: date = Query(..., description="End date"),
latitude: float = Query(..., description="Latitude"),
longitude: float = Query(..., description="Longitude"),
tenant_id: str = Depends(get_current_tenant_id_dep),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
):
"""Get historical weather data"""
try:
logger.debug("Getting weather history",
start_date=start_date,
end_date=end_date,
tenant_id=tenant_id)
weather_service = WeatherService()
history = await weather_service.get_weather_history(
latitude, longitude, start_date, end_date
)
return history
except Exception as e:
logger.error("Failed to get weather history", error=str(e))
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
@router.get("/summary", response_model=WeatherSummaryResponse)
async def get_weather_summary(
location_id: Optional[str] = Query(None, description="Location ID"),
days: int = Query(30, description="Number of days to summarize"),
tenant_id: str = Depends(get_current_tenant_id_dep),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
):
"""Get weather summary for tenant's location"""
try:
logger.debug("Getting weather summary",
location_id=location_id,
days=days,
tenant_id=tenant_id)
weather_service = WeatherService()
# If no location_id provided, use tenant's default location
if not location_id:
# This would typically fetch from tenant service
location_id = tenant_id # Simplified for example
summary = await weather_service.get_weather_summary(location_id, days)
return summary
except Exception as e:
logger.error("Failed to get weather summary", error=str(e))
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
@router.post("/sync")
async def sync_weather_data(
background_tasks: BackgroundTasks,
force: bool = Query(False, description="Force sync even if recently synced"),
tenant_id: str = Depends(get_current_tenant_id_dep),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
):
"""Manually trigger weather data synchronization"""
try:
logger.info("Weather sync requested",
tenant_id=tenant_id,
user_id=current_user["user_id"],
force=force)
weather_service = WeatherService()
# Check if user has permission to sync (could be admin only)
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(
status_code=403,
detail="Insufficient permissions to sync weather data"
)
# Schedule background sync
background_tasks.add_task(
weather_service.sync_weather_data,
tenant_id=tenant_id,
force=force
)
return {
"message": "Weather sync initiated",
"status": "processing",
"initiated_by": current_user["user_id"]
}
except HTTPException:
raise
except Exception as e:
logger.error("Failed to initiate weather sync", error=str(e))
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")