feat: separate current weather from forecast into distinct namespaces
- Add FORECAST namespace for multi-day outlook (12hr TTL)
- WEATHER namespace now stores only current conditions (1hr TTL)
- Split fetch_weather into fetch_current_weather + fetch_forecast
- Add POST /volatile/fetch/forecast/{city} endpoint
- Different update frequencies for efficient caching
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -67,12 +67,86 @@ class VolatileFetchService:
|
||||
self.news = news_provider
|
||||
self.financial = financial_provider
|
||||
|
||||
async def fetch_weather(
|
||||
async def fetch_current_weather(
|
||||
self,
|
||||
user: str,
|
||||
city: str,
|
||||
ttl: int = 3600, # 1 hour
|
||||
) -> FetchResult:
|
||||
"""
|
||||
Fetch current weather conditions for a city and store in volatile cache.
|
||||
|
||||
Args:
|
||||
user: User identifier
|
||||
city: City name (will be geocoded)
|
||||
ttl: Time-to-live in seconds (default 1 hour)
|
||||
|
||||
Returns:
|
||||
FetchResult with success status and stored record
|
||||
"""
|
||||
try:
|
||||
# Geocode city and get current conditions
|
||||
location = await self.weather.geocode(city)
|
||||
if not location:
|
||||
return FetchResult(
|
||||
success=False,
|
||||
namespace="weather",
|
||||
key=city.lower(),
|
||||
error=f"Could not geocode city: {city}"
|
||||
)
|
||||
|
||||
current = await self.weather.get_current(location)
|
||||
|
||||
# Generate natural language summary
|
||||
text = current.to_text()
|
||||
|
||||
# Convert to storage format
|
||||
data = {
|
||||
"temperature": current.temperature,
|
||||
"feels_like": current.feels_like,
|
||||
"humidity": current.humidity,
|
||||
"wind_speed": current.wind_speed,
|
||||
"wind_direction": current.wind_direction,
|
||||
"conditions": current.condition_text,
|
||||
"condition_code": current.condition.value,
|
||||
"uv_index": current.uv_index,
|
||||
"location": current.location,
|
||||
"text": text,
|
||||
}
|
||||
|
||||
# Store in volatile cache
|
||||
record = await self.volatile.store(
|
||||
user=user,
|
||||
namespace=VolatileNamespace.WEATHER,
|
||||
key=city.lower(),
|
||||
data=data,
|
||||
source="openmeteo",
|
||||
ttl=ttl,
|
||||
)
|
||||
|
||||
logger.info(f"Stored current weather for {city} (user={user})")
|
||||
return FetchResult(
|
||||
success=True,
|
||||
namespace="weather",
|
||||
key=city.lower(),
|
||||
record=record
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch current weather for {city}: {e}")
|
||||
return FetchResult(
|
||||
success=False,
|
||||
namespace="weather",
|
||||
key=city.lower(),
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
async def fetch_forecast(
|
||||
self,
|
||||
user: str,
|
||||
city: str,
|
||||
days: int = 7,
|
||||
ttl: int = 86400, # 24 hours
|
||||
ttl: int = 43200, # 12 hours
|
||||
) -> FetchResult:
|
||||
"""
|
||||
Fetch weather forecast for a city and store in volatile cache.
|
||||
@@ -81,7 +155,7 @@ class VolatileFetchService:
|
||||
user: User identifier
|
||||
city: City name (will be geocoded)
|
||||
days: Number of forecast days (1-16)
|
||||
ttl: Time-to-live in seconds
|
||||
ttl: Time-to-live in seconds (default 12 hours)
|
||||
|
||||
Returns:
|
||||
FetchResult with success status and stored record
|
||||
@@ -92,13 +166,12 @@ class VolatileFetchService:
|
||||
if not location:
|
||||
return FetchResult(
|
||||
success=False,
|
||||
namespace="weather",
|
||||
namespace="forecast",
|
||||
key=city.lower(),
|
||||
error=f"Could not geocode city: {city}"
|
||||
)
|
||||
|
||||
forecast = await self.weather.get_forecast(location, days=days)
|
||||
current = forecast.current
|
||||
|
||||
# Build daily forecast array
|
||||
daily_forecasts = []
|
||||
@@ -116,32 +189,23 @@ class VolatileFetchService:
|
||||
})
|
||||
|
||||
# Generate natural language summary
|
||||
forecast_lines = [current.to_text()]
|
||||
for day in forecast.daily[:5]: # First 5 days
|
||||
forecast_lines = [f"{city} {days}-day forecast:"]
|
||||
for day in forecast.daily:
|
||||
forecast_lines.append(day.to_text())
|
||||
text = "\n".join(forecast_lines)
|
||||
|
||||
# Convert to storage format
|
||||
data = {
|
||||
"current": {
|
||||
"temperature": current.temperature,
|
||||
"feels_like": current.feels_like,
|
||||
"humidity": current.humidity,
|
||||
"wind_speed": current.wind_speed,
|
||||
"wind_direction": current.wind_direction,
|
||||
"conditions": current.condition_text,
|
||||
"condition_code": current.condition.value,
|
||||
"uv_index": current.uv_index,
|
||||
},
|
||||
"days": days,
|
||||
"daily": daily_forecasts,
|
||||
"location": current.location,
|
||||
"location": forecast.current.location,
|
||||
"text": text,
|
||||
}
|
||||
|
||||
# Store in volatile cache
|
||||
record = await self.volatile.store(
|
||||
user=user,
|
||||
namespace=VolatileNamespace.WEATHER,
|
||||
namespace=VolatileNamespace.FORECAST,
|
||||
key=city.lower(),
|
||||
data=data,
|
||||
source="openmeteo",
|
||||
@@ -151,16 +215,16 @@ class VolatileFetchService:
|
||||
logger.info(f"Stored {days}-day forecast for {city} (user={user})")
|
||||
return FetchResult(
|
||||
success=True,
|
||||
namespace="weather",
|
||||
namespace="forecast",
|
||||
key=city.lower(),
|
||||
record=record
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch weather for {city}: {e}")
|
||||
logger.error(f"Failed to fetch forecast for {city}: {e}")
|
||||
return FetchResult(
|
||||
success=False,
|
||||
namespace="weather",
|
||||
namespace="forecast",
|
||||
key=city.lower(),
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user