From 4088807c72fdaab2c5772a8b3b23ccd7289e1674 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sat, 9 Aug 2025 10:22:56 -0500 Subject: [PATCH] milb_manager cache test --- src/milb_manager.py | 79 +++++++++++++++++++++--- test/test_milb_cache_debug.py | 113 ++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 8 deletions(-) create mode 100644 test/test_milb_cache_debug.py diff --git a/src/milb_manager.py b/src/milb_manager.py index 6c21af12..c0ff093a 100644 --- a/src/milb_manager.py +++ b/src/milb_manager.py @@ -379,6 +379,11 @@ class BaseMiLBManager: for event in data['dates'][0]['games']: game_pk = event['gamePk'] + # Debug: Check game_pk type + if not isinstance(game_pk, (int, str)): + self.logger.warning(f"[MiLB] Unexpected game_pk type: {type(game_pk)}, value: {game_pk}") + # Convert to string to ensure it's usable as a key + game_pk = str(game_pk) home_team_name = event['teams']['home']['team']['name'] away_team_name = event['teams']['away']['team']['name'] @@ -409,14 +414,9 @@ class BaseMiLBManager: else: home_record_str = '' - # --- TEMP: Comprehensive Debugging --- - self.logger.info(f"[MiLB DEBUG] Raw event data for game {game_pk}:\n{json.dumps(event, indent=2)}") - - game_date = event.get('gameDate') - if not game_date: + if not event.get('gameDate'): self.logger.warning(f"Skipping game {game_pk} due to missing 'gameDate'.") continue - # --- End of TEMP Debugging --- is_favorite_game = (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams) @@ -468,10 +468,38 @@ class BaseMiLBManager: # For non-live games, set defaults game_data.update({'inning': 1, 'inning_half': 'top', 'balls': 0, 'strikes': 0, 'outs': 0, 'bases_occupied': [False]*3}) - all_games[game_pk] = game_data + # Validate game_data before adding to all_games + if isinstance(game_data, dict): + all_games[game_pk] = game_data + else: + self.logger.error(f"[MiLB] Invalid game_data type for game {game_pk}: {type(game_data)}") + # Filter out any invalid games before returning + if isinstance(all_games, dict): + valid_games = {} + for game_id, game_data in all_games.items(): + if isinstance(game_data, dict): + valid_games[game_id] = game_data + else: + self.logger.warning(f"[MiLB] Skipping invalid game {game_id} with type {type(game_data)}") + all_games = valid_games + if use_cache: - self.cache_manager.set(cache_key, all_games) + # Validate that all_games is a dictionary before caching + if isinstance(all_games, dict): + # Validate that all values in the dictionary are also dictionaries + invalid_games = [] + for game_id, game_data in all_games.items(): + if not isinstance(game_data, dict): + invalid_games.append((game_id, type(game_data))) + + if invalid_games: + self.logger.error(f"[MiLB] Found invalid game data types: {invalid_games}") + # Don't cache corrupted data + else: + self.cache_manager.set(cache_key, all_games) + else: + self.logger.error(f"[MiLB] Cannot cache invalid data type: {type(all_games)}") return all_games except Exception as e: @@ -1235,12 +1263,42 @@ class MiLBUpcomingManager(BaseMiLBManager): try: # Fetch data from MiLB API games = self._fetch_milb_api_data(use_cache=True) + + # Debug: Check the structure of returned data + if games is not None: + self.logger.debug(f"[MiLB] Games data type: {type(games)}") + if isinstance(games, dict): + self.logger.debug(f"[MiLB] Number of games: {len(games)}") + if games: + sample_key = next(iter(games)) + sample_value = games[sample_key] + self.logger.debug(f"[MiLB] Sample game key: {sample_key}, type: {type(sample_value)}, value: {sample_value}") + + # Check if the data structure is corrupted + if not isinstance(sample_value, dict): + self.logger.error(f"[MiLB] Cache data appears corrupted. Clearing cache and refetching.") + self.cache_manager.clear_cache("milb_live_api_data") + games = self._fetch_milb_api_data(use_cache=False) + else: + self.logger.error(f"[MiLB] Games is not a dictionary: {type(games)}, value: {games}") + # Clear cache and try again without cache + self.cache_manager.clear_cache("milb_live_api_data") + games = self._fetch_milb_api_data(use_cache=False) + if not games: self.logger.warning("[MiLB] No games returned from API for upcoming games update.") if self.upcoming_games: # Clear games if API returns nothing self.upcoming_games = [] self.current_game = None return + + # Final validation that games is a dictionary + if not isinstance(games, dict): + self.logger.error(f"[MiLB] Final validation failed - games is not a dictionary: {type(games)}") + if self.upcoming_games: # Clear games if data is invalid + self.upcoming_games = [] + self.current_game = None + return # --- Optimization: Filter for favorite teams before processing --- if self.milb_config.get("show_favorite_teams_only", False) and self.favorite_teams: @@ -1259,6 +1317,11 @@ class MiLBUpcomingManager(BaseMiLBManager): for game_id, game in games.items(): self.logger.debug(f"[MiLB] Processing game {game_id} for upcoming games...") + # Debug: Check the type of game data + if not isinstance(game, dict): + self.logger.error(f"[MiLB] Game {game_id} is not a dictionary. Type: {type(game)}, Value: {game}") + continue + # Ensure start_time exists before processing if 'start_time' not in game or not game['start_time']: self.logger.warning(f"Skipping game {game_id} due to missing or empty 'start_time'.") diff --git a/test/test_milb_cache_debug.py b/test/test_milb_cache_debug.py new file mode 100644 index 00000000..7b76ecff --- /dev/null +++ b/test/test_milb_cache_debug.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +""" +Test script to debug MiLB cache issues. +This script will check the cache data structure and identify any corrupted data. +""" + +import sys +import os +import json +import logging +from datetime import datetime + +# Add the src directory to the path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from cache_manager import CacheManager +from config_manager import ConfigManager + +# Set up logging +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +def check_milb_cache(): + """Check the MiLB cache data structure.""" + try: + # Initialize managers + config_manager = ConfigManager() + cache_manager = CacheManager() + + # Check the MiLB cache key + cache_key = "milb_live_api_data" + + logger.info(f"Checking cache for key: {cache_key}") + + # Try to get cached data + cached_data = cache_manager.get_with_auto_strategy(cache_key) + + if cached_data is None: + logger.info("No cached data found") + return + + logger.info(f"Cached data type: {type(cached_data)}") + + if isinstance(cached_data, dict): + logger.info(f"Number of games in cache: {len(cached_data)}") + + # Check each game + for game_id, game_data in cached_data.items(): + logger.info(f"Game ID: {game_id} (type: {type(game_id)})") + logger.info(f"Game data type: {type(game_data)}") + + if isinstance(game_data, dict): + logger.info(f" - Valid game data with {len(game_data)} fields") + # Check for required fields + required_fields = ['away_team', 'home_team', 'start_time'] + for field in required_fields: + if field in game_data: + logger.info(f" - {field}: {game_data[field]} (type: {type(game_data[field])})") + else: + logger.warning(f" - Missing required field: {field}") + else: + logger.error(f" - INVALID: Game data is not a dictionary: {type(game_data)}") + logger.error(f" - Value: {game_data}") + + # Try to understand what this value is + if isinstance(game_data, (int, float)): + logger.error(f" - This appears to be a numeric value: {game_data}") + elif isinstance(game_data, str): + logger.error(f" - This appears to be a string: {game_data}") + else: + logger.error(f" - Unknown type: {type(game_data)}") + else: + logger.error(f"Cache data is not a dictionary: {type(cached_data)}") + logger.error(f"Value: {cached_data}") + + # Try to understand what this value is + if isinstance(cached_data, (int, float)): + logger.error(f"This appears to be a numeric value: {cached_data}") + elif isinstance(cached_data, str): + logger.error(f"This appears to be a string: {cached_data}") + else: + logger.error(f"Unknown type: {type(cached_data)}") + + except Exception as e: + logger.error(f"Error checking MiLB cache: {e}", exc_info=True) + +def clear_milb_cache(): + """Clear the MiLB cache.""" + try: + config_manager = ConfigManager() + cache_manager = CacheManager() + + cache_key = "milb_live_api_data" + logger.info(f"Clearing cache for key: {cache_key}") + + cache_manager.clear_cache(cache_key) + logger.info("Cache cleared successfully") + + except Exception as e: + logger.error(f"Error clearing MiLB cache: {e}", exc_info=True) + +if __name__ == "__main__": + print("MiLB Cache Debug Tool") + print("=====================") + print() + + if len(sys.argv) > 1 and sys.argv[1] == "clear": + clear_milb_cache() + else: + check_milb_cache() + + print() + print("Debug complete.")