From 4c5f1e73c565e03eda8d280407dfffe15f3aeb8c Mon Sep 17 00:00:00 2001 From: ChuckBuilds <33324927+ChuckBuilds@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:08:02 -0500 Subject: [PATCH] Improve cache functionality. Add max_age parameter to CacheManager, fix WeatherManager API calls, and enhance error handling. --- src/cache_manager.py | 31 ++++++++++++++++++++++++---- src/weather_manager.py | 46 +++++++++++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/cache_manager.py b/src/cache_manager.py index f5c6fa58..bbfb0e86 100644 --- a/src/cache_manager.py +++ b/src/cache_manager.py @@ -125,13 +125,13 @@ class CacheManager: except: pass - def get_cached_data(self, key: str) -> Optional[Dict[str, Any]]: - """Get cached data with memory cache priority.""" + def get_cached_data(self, key: str, max_age: int = 60) -> Optional[Dict[str, Any]]: + """Get cached data with memory cache priority and max age check.""" current_time = time.time() # Check memory cache first if key in self._memory_cache: - if current_time - self._memory_cache_timestamps.get(key, 0) < 60: # 1 minute TTL + if current_time - self._memory_cache_timestamps.get(key, 0) < max_age: # Use provided max_age return self._memory_cache[key] else: # Clear expired memory cache @@ -139,7 +139,30 @@ class CacheManager: del self._memory_cache_timestamps[key] # Fall back to disk cache - return self.load_cache(key) + cache_path = self._get_cache_path(key) + if not os.path.exists(cache_path): + return None + + try: + with self._cache_lock: + with open(cache_path, 'r') as f: + try: + data = json.load(f) + # Check if data is stale + if current_time - data.get('timestamp', 0) > max_age: + return None + # Update memory cache + self._memory_cache[key] = data['data'] + self._memory_cache_timestamps[key] = current_time + return data['data'] + except json.JSONDecodeError as e: + self.logger.error(f"Error parsing cache file for {key}: {e}") + # If the file is corrupted, remove it + os.remove(cache_path) + return None + except Exception as e: + self.logger.error(f"Error loading cache for {key}: {e}") + return None def clear_cache(self, key: Optional[str] = None) -> None: """Clear cache for a specific key or all keys.""" diff --git a/src/weather_manager.py b/src/weather_manager.py index 7bc6e2a7..c8ff7c79 100644 --- a/src/weather_manager.py +++ b/src/weather_manager.py @@ -105,27 +105,55 @@ class WeatherManager: # Check if we need to update if current_time - self._last_update < self._update_interval: - cached_data = self.cache_manager.get_cached_data('weather') + cached_data = self.cache_manager.get_cached_data('weather', max_age=self._update_interval) if cached_data: self.logger.info("Using cached weather data") return self._process_forecast_data(cached_data) return None try: - # Fetch new data - data = self._fetch_from_api() - if data: - self._last_update = current_time - self.cache_manager.save_cache('weather', data) - return self._process_forecast_data(data) + # Fetch new data from OpenWeatherMap API + api_key = self.weather_config.get('api_key') + location = self.weather_config.get('location') + units = self.weather_config.get('units', 'imperial') + + # Fetch current weather + current_url = "https://api.openweathermap.org/data/2.5/weather" + params = { + "q": location, + "appid": api_key, + "units": units + } + + response = requests.get(current_url, params=params, timeout=10) + response.raise_for_status() + current_data = response.json() + + # Fetch forecast + forecast_url = "https://api.openweathermap.org/data/2.5/forecast" + response = requests.get(forecast_url, params=params, timeout=10) + response.raise_for_status() + forecast_data = response.json() + + # Combine the data + data = { + "current": current_data, + "hourly": forecast_data.get("list", []), + "daily": [] # Daily forecast will be processed from hourly data + } + + self._last_update = current_time + self.cache_manager.save_cache('weather', data) + return self._process_forecast_data(data) + except Exception as e: self.logger.error(f"Error fetching weather data: {e}") # Try to use cached data as fallback - cached_data = self.cache_manager.get_cached_data('weather') + cached_data = self.cache_manager.get_cached_data('weather', max_age=self._update_interval) if cached_data: self.logger.info("Using cached weather data as fallback") return self._process_forecast_data(cached_data) - return None + return None def _process_current_conditions(self, current: Dict[str, Any]) -> Dict[str, Any]: """Process current conditions with minimal processing."""