""" Volatile memory models for Library Desk. Provides models for ephemeral cached data with TTL - weather, news, financial data, transit schedules, and other time-sensitive external information. """ from pydantic import BaseModel, Field from typing import Dict, Any, Optional, List from datetime import datetime from enum import Enum class VolatileNamespace(str, Enum): """ Predefined namespaces for volatile data. Each namespace can have different default TTLs and refresh schedules. """ # Real-time external data WEATHER = "weather" # Current conditions (temperature, humidity, wind) FORECAST = "forecast" # Multi-day weather outlook SUN = "sun" # Sunrise, sunset, daylight duration NEWS = "news" # Headlines, breaking news FINANCIAL = "financial" # Stock prices, exchange rates, crypto TRANSIT = "transit" # Train/bus schedules, delays, disruptions TRAFFIC = "traffic" # Commute times, road conditions AIR_QUALITY = "air_quality" # Pollution levels, pollen counts SPORTS = "sports" # Live scores, upcoming matches # System/integration data SOCIAL = "social" # Social media mentions, notifications SYSTEM = "system" # Service health, infrastructure status # Ephemeral context CONTEXT = "context" # Conversation context, session state CUSTOM = "custom" # User-defined volatile data # Default TTLs per namespace (in seconds) # TTL = 2x refresh interval to ensure data survives missed/delayed refreshes NAMESPACE_DEFAULT_TTL: Dict[str, int] = { VolatileNamespace.WEATHER: 7200, # 2 hours (hourly refresh) VolatileNamespace.FORECAST: 86400, # 24 hours (12hr refresh) VolatileNamespace.SUN: 172800, # 48 hours (daily refresh) VolatileNamespace.NEWS: 7200, # 2 hours (hourly refresh) VolatileNamespace.FINANCIAL: 600, # 10 min (5 min refresh) VolatileNamespace.TRANSIT: 600, # 10 min (5 min refresh) VolatileNamespace.TRAFFIC: 1200, # 20 min (10 min refresh) VolatileNamespace.AIR_QUALITY: 7200, # 2 hours (hourly refresh) VolatileNamespace.SPORTS: 120, # 2 min (1 min refresh) VolatileNamespace.SOCIAL: 1200, # 20 min (10 min refresh) VolatileNamespace.SYSTEM: 120, # 2 min (1 min refresh) VolatileNamespace.CONTEXT: 7200, # 2 hours - session context VolatileNamespace.CUSTOM: 7200, # 2 hours - default for custom } class VolatileRecord(BaseModel): """ A volatile cache record with TTL. Volatile records are ephemeral data stored in Redis with automatic expiration. Used for weather, news, financial data, and other time-sensitive information. """ key: str = Field(..., description="Record key (e.g., 'rotterdam', 'nos-headlines')") namespace: str = Field(..., description="Namespace (e.g., 'weather', 'news', 'financial')") data: Dict[str, Any] = Field(..., description="Actual content/payload") source: Optional[str] = Field(None, description="Origin API/service (e.g., 'openweathermap', 'nos.nl')") created_at: datetime = Field(default_factory=datetime.utcnow, description="When record was created") updated_at: datetime = Field(default_factory=datetime.utcnow, description="When record was last updated") ttl: int = Field(..., ge=60, le=604800, description="Time-to-live in seconds (max 7 days)") refresh_schedule: Optional[str] = Field(None, description="Cron expression for scheduled refresh") user: str = Field(..., description="User identifier for multi-tenancy") class VolatileRecordCreate(BaseModel): """Request model for creating/updating a volatile record.""" data: Dict[str, Any] = Field(..., description="Content to store") source: Optional[str] = Field(None, description="Origin API/service") ttl: Optional[int] = Field(None, ge=60, le=604800, description="TTL in seconds (uses namespace default if not set)") refresh_schedule: Optional[str] = Field(None, description="Cron expression for scheduled refresh") class VolatileRecordResponse(BaseModel): """Response model for a volatile record.""" key: str = Field(..., description="Record key") namespace: str = Field(..., description="Namespace") data: Dict[str, Any] = Field(..., description="Stored content") source: Optional[str] = Field(None, description="Origin API/service") created_at: datetime = Field(..., description="Creation timestamp") updated_at: datetime = Field(..., description="Last update timestamp") ttl: int = Field(..., description="TTL in seconds") ttl_remaining: int = Field(..., description="Seconds until expiration") refresh_schedule: Optional[str] = Field(None, description="Cron expression if scheduled") user: str = Field(..., description="User identifier") class VolatileListResponse(BaseModel): """Response model for listing volatile records.""" namespace: str = Field(..., description="Namespace queried") keys: List[str] = Field(..., description="List of keys in namespace") count: int = Field(..., description="Number of keys") user: str = Field(..., description="User identifier") class VolatileScheduledResponse(BaseModel): """Response model for records needing refresh.""" records: List[VolatileRecordResponse] = Field(..., description="Records with refresh schedules") count: int = Field(..., description="Number of scheduled records") user: str = Field(..., description="User identifier") class VolatileStatsResponse(BaseModel): """Response model for volatile cache statistics.""" total_records: int = Field(..., description="Total volatile records for user") by_namespace: Dict[str, int] = Field(..., description="Record count per namespace") scheduled_count: int = Field(..., description="Records with refresh schedules") total_memory_bytes: Optional[int] = Field(None, description="Approximate memory usage") user: str = Field(..., description="User identifier") class VolatileDeleteResponse(BaseModel): """Response model for delete operation.""" key: str = Field(..., description="Deleted key") namespace: str = Field(..., description="Namespace") deleted: bool = Field(..., description="Whether record was found and deleted") user: str = Field(..., description="User identifier") class VolatileBulkDeleteResponse(BaseModel): """Response model for bulk delete operations.""" namespace: Optional[str] = Field(None, description="Namespace if namespace-wide delete") deleted_count: int = Field(..., description="Number of records deleted") user: str = Field(..., description="User identifier")