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:
2025-12-30 12:44:16 +01:00
co-authored by Claude Opus 4.5
parent 68eb1add3d
commit 46b9bcd7a0
4 changed files with 141 additions and 33 deletions
+86 -22
View File
@@ -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)
)