From ba232c58b7d07f8bf78ac23201f2c697da8e8a79 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Mon, 21 Jul 2025 21:37:22 -0500 Subject: [PATCH] huge cache refactor, dictonary error updates, and changed from time based game searching to game based for recent and upcoming --- config/config.json | 18 +++--- src/milb_manager.py | 68 +++++++++++++------- src/mlb_manager.py | 65 +++++++++---------- src/ncaa_fb_managers.py | 68 ++++++-------------- src/nfl_managers.py | 137 +++++++++++----------------------------- 5 files changed, 141 insertions(+), 215 deletions(-) diff --git a/config/config.json b/config/config.json index 54c54010..9101d71a 100644 --- a/config/config.json +++ b/config/config.json @@ -119,7 +119,6 @@ "scroll_speed": 1, "scroll_delay": 0.01, "display_duration": 60, - "future_fetch_days": 45, "loop": true }, "calendar": { @@ -138,7 +137,6 @@ "live_update_interval": 30, "recent_update_interval": 3600, "upcoming_update_interval": 3600, - "recent_game_hours": 48, "favorite_teams": ["TB"], "logo_dir": "assets/sports/nhl_logos", "show_records": true, @@ -176,8 +174,8 @@ "live_update_interval": 30, "live_odds_update_interval": 3600, "odds_update_interval": 3600, - "fetch_past_games": 1, - "fetch_future_games": 2, + "recent_games_to_show": 0, + "upcoming_games_to_show": 2, "favorite_teams": ["TB", "DAL"], "logo_dir": "assets/sports/nfl_logos", "show_records": true, @@ -196,8 +194,8 @@ "live_odds_update_interval": 3600, "odds_update_interval": 3600, "season_cache_duration_seconds": 86400, - "fetch_past_games": 1, - "fetch_future_games": 2, + "recent_games_to_show": 0, + "upcoming_games_to_show": 2, "favorite_teams": ["UGA", "AUB"], "logo_dir": "assets/sports/ncaa_fbs_logos", "show_records": true, @@ -255,8 +253,8 @@ "odds_update_interval": 3600, "recent_update_interval": 3600, "upcoming_update_interval": 3600, - "fetch_past_games": 1, - "fetch_future_games": 5, + "recent_games_to_show": 1, + "upcoming_games_to_show": 1, "favorite_teams": ["TB", "TEX"], "logo_dir": "assets/sports/mlb_logos", "show_records": true, @@ -273,7 +271,8 @@ "live_update_interval": 30, "recent_update_interval": 3600, "upcoming_update_interval": 3600, - "recent_game_hours": 48, + "recent_games_to_show": 1, + "upcoming_games_to_show": 1, "favorite_teams": ["TAM"], "logo_dir": "assets/sports/milb_logos", "show_records": true, @@ -303,7 +302,6 @@ "recent_update_interval": 3600, "upcoming_update_interval": 3600, "recent_game_hours": 168, - "upcoming_fetch_days": 7, "favorite_teams": ["LIV"], "leagues": ["eng.1", "esp.1", "ger.1", "ita.1", "fra.1", "uefa.champions", "usa.1"], "logo_dir": "assets/sports/soccer_logos", diff --git a/src/milb_manager.py b/src/milb_manager.py index 2bb23a2c..fe9746f0 100644 --- a/src/milb_manager.py +++ b/src/milb_manager.py @@ -942,7 +942,7 @@ class MiLBRecentManager(BaseMiLBManager): self.current_game_index = 0 self.last_update = 0 self.update_interval = self.milb_config.get('recent_update_interval', 3600) # 1 hour - self.recent_hours = self.milb_config.get('recent_game_hours', 72) # Increased from 48 to 72 hours + self.recent_games_to_show = self.milb_config.get('recent_games_to_show', 5) # Show last 5 games self.last_game_switch = 0 # Track when we last switched games self.game_display_duration = 10 # Display each game for 10 seconds self.last_warning_time = 0 @@ -966,10 +966,8 @@ class MiLBRecentManager(BaseMiLBManager): # Process games new_recent_games = [] - now = datetime.now(timezone.utc) # Make timezone-aware - recent_cutoff = now - timedelta(hours=self.recent_hours) - logger.info(f"[MiLB] Time window: {recent_cutoff} to {now}") + logger.info(f"[MiLB] Processing {len(games)} games for recent games...") for game_id, game in games.items(): # Convert game time to UTC datetime @@ -989,16 +987,19 @@ class MiLBRecentManager(BaseMiLBManager): # Use status_state to determine if game is final is_final = game['status_state'] in ['post', 'final', 'completed'] - is_within_time = recent_cutoff <= game_time <= now - self.logger.info(f"[MiLB] Game Time: {game_time.isoformat()}, Cutoff Time: {recent_cutoff.isoformat()}, Now: {now.isoformat()}") - self.logger.info(f"[MiLB] Is final: {is_final}, Is within time window: {is_within_time}") + self.logger.info(f"[MiLB] Game Time: {game_time.isoformat()}") + self.logger.info(f"[MiLB] Is final: {is_final}") - # Only add favorite team games that are final and within time window - if is_final and is_within_time: + # Only add favorite team games that are final + if is_final: new_recent_games.append(game) logger.info(f"[MiLB] Added favorite team game to recent list: {game['away_team']} @ {game['home_team']}") + # Sort by game time (most recent first) and limit to recent_games_to_show + new_recent_games.sort(key=lambda x: x['start_time'], reverse=True) + new_recent_games = new_recent_games[:self.recent_games_to_show] + if new_recent_games: logger.info(f"[MiLB] Found {len(new_recent_games)} recent games for favorite teams: {self.favorite_teams}") self.recent_games = new_recent_games @@ -1053,6 +1054,7 @@ class MiLBUpcomingManager(BaseMiLBManager): self.current_game_index = 0 self.last_update = 0 self.update_interval = self.milb_config.get('upcoming_update_interval', 3600) # 1 hour + self.upcoming_games_to_show = self.milb_config.get('upcoming_games_to_show', 10) # Show next 10 games self.last_warning_time = 0 self.warning_cooldown = 300 # Only show warning every 5 minutes self.last_game_switch = 0 # Track when we last switched games @@ -1073,24 +1075,44 @@ class MiLBUpcomingManager(BaseMiLBManager): if games: # Process games new_upcoming_games = [] - now = datetime.now(timezone.utc) # Make timezone-aware - upcoming_cutoff = now + timedelta(hours=24) - logger.info(f"Looking for games between {now} and {upcoming_cutoff}") + logger.info(f"[MiLB] Processing {len(games)} games for upcoming games...") - for game in games.get('events', []): - game_data = self._extract_game_details(game) - if game_data: - new_upcoming_games.append(game_data) + for game_id, game in games.items(): + # Convert game time to UTC datetime + game_time_str = game['start_time'].replace('Z', '+00:00') + game_time = datetime.fromisoformat(game_time_str) + if game_time.tzinfo is None: + game_time = game_time.replace(tzinfo=timezone.utc) + + # Check if this is a favorite team game + is_favorite_game = (game['home_team'] in self.favorite_teams or + game['away_team'] in self.favorite_teams) + + if is_favorite_game: + logger.info(f"[MiLB] Checking favorite team game: {game['away_team']} @ {game['home_team']}") + logger.info(f"[MiLB] Game time (UTC): {game_time}") + logger.info(f"[MiLB] Game status: {game['status']}, State: {game['status_state']}") + + # For upcoming games, we'll consider any game that: + # 1. Is not final (not 'post' or 'final' state) + # 2. Has a future start time + is_upcoming = ( + game['status_state'] not in ['post', 'final', 'completed'] and + game_time > datetime.now(timezone.utc) + ) + + if is_upcoming: + new_upcoming_games.append(game) + logger.info(f"[MiLB] Added favorite team game to upcoming list: {game['away_team']} @ {game['home_team']}") - # Filter for favorite teams (though we already filtered above, this is a safety check) - new_team_games = [game for game in new_upcoming_games - if game['home_team'] in self.favorite_teams or - game['away_team'] in self.favorite_teams] + # Sort by game time (soonest first) and limit to upcoming_games_to_show + new_upcoming_games.sort(key=lambda x: x['start_time']) + new_upcoming_games = new_upcoming_games[:self.upcoming_games_to_show] - if new_team_games: - logger.info(f"[MiLB] Found {len(new_team_games)} upcoming games for favorite teams") - self.upcoming_games = new_team_games + if new_upcoming_games: + logger.info(f"[MiLB] Found {len(new_upcoming_games)} upcoming games for favorite teams") + self.upcoming_games = new_upcoming_games if not self.current_game: self.current_game = self.upcoming_games[0] else: diff --git a/src/mlb_manager.py b/src/mlb_manager.py index 10f19152..7d6cf87c 100644 --- a/src/mlb_manager.py +++ b/src/mlb_manager.py @@ -414,6 +414,7 @@ class BaseMLBManager: response.raise_for_status() data = response.json() + self.logger.info(f"Found {len(data.get('events', []))} total games for date {date}") for event in data.get('events', []): game_id = event['id'] @@ -436,6 +437,9 @@ class BaseMLBManager: # Check if this is a favorite team game is_favorite_game = (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams) + # Log all teams found for debugging + self.logger.debug(f"Found game: {away_abbr} @ {home_abbr} (Status: {status}, State: {status_state})") + # Only log detailed information for favorite teams if is_favorite_game: self.logger.info(f"Found favorite team game: {away_abbr} @ {home_abbr} (Status: {status}, State: {status_state})") @@ -1070,7 +1074,7 @@ class MLBRecentManager(BaseMLBManager): self.current_game_index = 0 self.last_update = 0 self.update_interval = self.mlb_config.get('recent_update_interval', 3600) - self.recent_hours = self.mlb_config.get('recent_game_hours', 72) # Increased from 48 to 72 hours + self.recent_games_to_show = self.mlb_config.get('recent_games_to_show', 5) # Show last 5 games self.last_game_switch = 0 # Track when we last switched games self.game_display_duration = 10 # Display each game for 10 seconds self.last_warning_time = 0 @@ -1094,13 +1098,11 @@ class MLBRecentManager(BaseMLBManager): # Process games new_recent_games = [] - now = datetime.now(timezone.utc) # Make timezone-aware - recent_cutoff = now - timedelta(hours=self.recent_hours) - self.logger.info(f"[MLB] Recent games time window: {recent_cutoff} to {now}") + self.logger.info(f"[MLB] Processing {len(games)} games for recent games...") for game_id, game in games.items(): - self.logger.info(f"[MLB] Processing game {game_id} for recent games...") + self.logger.debug(f"[MLB] Processing game {game_id} for recent games...") # Convert game time to UTC datetime game_time_str = game['start_time'].replace('Z', '+00:00') game_time = datetime.fromisoformat(game_time_str) @@ -1112,7 +1114,7 @@ class MLBRecentManager(BaseMLBManager): game['away_team'] in self.favorite_teams) if not is_favorite_game: - self.logger.info(f"[MLB] Skipping game {game_id} - not a favorite team.") + self.logger.debug(f"[MLB] Skipping game {game_id} - not a favorite team.") continue self.logger.info(f"[MLB] Favorite team game found: {game['away_team']} @ {game['home_team']}") @@ -1121,18 +1123,21 @@ class MLBRecentManager(BaseMLBManager): # Use status_state to determine if game is final is_final = game['status_state'] in ['post', 'final', 'completed'] - is_within_time = recent_cutoff <= game_time <= now - self.logger.info(f"[MLB] Game Time: {game_time.isoformat()}, Cutoff Time: {recent_cutoff.isoformat()}, Now: {now.isoformat()}") - self.logger.info(f"[MLB] Is final: {is_final}, Is within time window: {is_within_time}") + self.logger.info(f"[MLB] Game Time: {game_time.isoformat()}") + self.logger.info(f"[MLB] Is final: {is_final}") - # Only add favorite team games that are final and within time window - if is_final and is_within_time: + # Only add favorite team games that are final + if is_final: self.logger.info(f"[MLB] Adding game {game_id} to recent games list.") self._fetch_odds(game) new_recent_games.append(game) else: - self.logger.info(f"[MLB] Skipping game {game_id} - does not meet criteria for recent games.") + self.logger.info(f"[MLB] Skipping game {game_id} - not final.") + + # Sort by game time (most recent first) and limit to recent_games_to_show + new_recent_games.sort(key=lambda x: x['start_time'], reverse=True) + new_recent_games = new_recent_games[:self.recent_games_to_show] if new_recent_games: logger.info(f"[MLB] Found {len(new_recent_games)} recent games for favorite teams: {self.favorite_teams}") @@ -1188,6 +1193,7 @@ class MLBUpcomingManager(BaseMLBManager): self.current_game_index = 0 self.last_update = 0 self.update_interval = self.mlb_config.get('upcoming_update_interval', 3600) + self.upcoming_games_to_show = self.mlb_config.get('upcoming_games_to_show', 10) # Show next 10 games self.last_warning_time = 0 self.warning_cooldown = 300 # Only show warning every 5 minutes self.last_game_switch = 0 # Track when we last switched games @@ -1211,19 +1217,17 @@ class MLBUpcomingManager(BaseMLBManager): # Process games new_upcoming_games = [] - now = datetime.now(timezone.utc) # Make timezone-aware - upcoming_cutoff = now + timedelta(hours=24) - self.logger.info(f"[MLB] Upcoming games time window: {now} to {upcoming_cutoff}") + self.logger.info(f"[MLB] Processing {len(games)} games for upcoming games...") for game_id, game in games.items(): - self.logger.info(f"[MLB] Processing game {game_id} for upcoming games...") + self.logger.debug(f"[MLB] Processing game {game_id} for upcoming games...") # Check if this is a favorite team game first is_favorite_game = (game['home_team'] in self.favorite_teams or game['away_team'] in self.favorite_teams) if not is_favorite_game: - self.logger.info(f"[MLB] Skipping game {game_id} - not a favorite team.") + self.logger.debug(f"[MLB] Skipping game {game_id} - not a favorite team.") continue game_time = datetime.fromisoformat(game['start_time'].replace('Z', '+00:00')) @@ -1234,20 +1238,14 @@ class MLBUpcomingManager(BaseMLBManager): self.logger.info(f"[MLB] Favorite team game found: {game['away_team']} @ {game['home_team']} at {game_time}") self.logger.info(f"[MLB] Game status: {game['status']}, State: {game['status_state']}") - # Check if game is within our time window - is_within_time = now <= game_time <= upcoming_cutoff - # For upcoming games, we'll consider any game that: - # 1. Is within our time window - # 2. Is not final (not 'post' or 'final' state) - # 3. Has a future start time + # 1. Is not final (not 'post' or 'final' state) + # 2. Has a future start time is_upcoming = ( - is_within_time and game['status_state'] not in ['post', 'final', 'completed'] and - game_time > now + game_time > datetime.now(timezone.utc) ) - self.logger.info(f"[MLB] Is within time window: {is_within_time}") self.logger.info(f"[MLB] Is upcoming: {is_upcoming}") if is_upcoming: @@ -1255,16 +1253,15 @@ class MLBUpcomingManager(BaseMLBManager): self._fetch_odds(game) new_upcoming_games.append(game) else: - self.logger.info(f"[MLB] Skipping game {game_id} - does not meet criteria for upcoming games.") + self.logger.info(f"[MLB] Skipping game {game_id} - not upcoming.") - # Filter for favorite teams (though we already filtered above, this is a safety check) - new_team_games = [game for game in new_upcoming_games - if game['home_team'] in self.favorite_teams or - game['away_team'] in self.favorite_teams] + # Sort by game time (soonest first) and limit to upcoming_games_to_show + new_upcoming_games.sort(key=lambda x: x['start_time']) + new_upcoming_games = new_upcoming_games[:self.upcoming_games_to_show] - if new_team_games: - logger.info(f"[MLB] Found {len(new_team_games)} upcoming games for favorite teams") - self.upcoming_games = new_team_games + if new_upcoming_games: + logger.info(f"[MLB] Found {len(new_upcoming_games)} upcoming games for favorite teams") + self.upcoming_games = new_upcoming_games if not self.current_game: self.current_game = self.upcoming_games[0] else: diff --git a/src/ncaa_fb_managers.py b/src/ncaa_fb_managers.py index 2bcd952d..edb17b6e 100644 --- a/src/ncaa_fb_managers.py +++ b/src/ncaa_fb_managers.py @@ -54,6 +54,9 @@ class BaseNCAAFBManager: # Renamed class self.update_interval = self.ncaa_fb_config.get("update_interval_seconds", 60) self.show_records = self.ncaa_fb_config.get('show_records', False) self.season_cache_duration = self.ncaa_fb_config.get("season_cache_duration_seconds", 86400) # 24 hours default + # Number of games to show (instead of time-based windows) + self.recent_games_to_show = self.ncaa_fb_config.get("recent_games_to_show", 5) # Show last 5 games + self.upcoming_games_to_show = self.ncaa_fb_config.get("upcoming_games_to_show", 10) # Show next 10 games # Set up session with retry logic self.session = requests.Session() @@ -79,8 +82,6 @@ class BaseNCAAFBManager: # Renamed class self.current_game = None self.fonts = self._load_fonts() self.favorite_teams = self.ncaa_fb_config.get("favorite_teams", []) - self.fetch_past_games = self.ncaa_fb_config.get("fetch_past_games", 1) - self.fetch_future_games = self.ncaa_fb_config.get("fetch_future_games", 1) # Check display modes to determine what data to fetch display_modes = self.ncaa_fb_config.get("display_modes", {}) @@ -219,46 +220,9 @@ class BaseNCAAFBManager: # Renamed class upcoming_events.sort(key=lambda x: x['date']) past_events.sort(key=lambda x: x['date'], reverse=True) - # Select the correct number of games for favorite teams - selected_upcoming = [] - if self.upcoming_enabled and self.favorite_teams: - games_found = {team: 0 for team in self.favorite_teams} - for game in upcoming_events: - competitors = game.get('competitions', [{}])[0].get('competitors', []) - home_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'home'), '') - away_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'away'), '') - - team_in_game = None - if home_team in self.favorite_teams and games_found[home_team] < self.fetch_future_games: - team_in_game = home_team - elif away_team in self.favorite_teams and games_found[away_team] < self.fetch_future_games: - team_in_game = away_team - - if team_in_game: - selected_upcoming.append(game) - games_found[team_in_game] += 1 - if all(count >= self.fetch_future_games for count in games_found.values()): - break - - selected_past = [] - if self.recent_enabled and self.favorite_teams: - games_found = {team: 0 for team in self.favorite_teams} - for game in past_events: - competitors = game.get('competitions', [{}])[0].get('competitors', []) - home_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'home'), '') - away_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'away'), '') - - team_in_game = None - if home_team in self.favorite_teams and games_found[home_team] < self.fetch_past_games: - team_in_game = home_team - elif away_team in self.favorite_teams and games_found[away_team] < self.fetch_past_games: - team_in_game = away_team - - if team_in_game: - selected_past.append(game) - games_found[team_in_game] += 1 - if all(count >= self.fetch_past_games for count in games_found.values()): - break + # Include all games in shared data - let individual managers filter by count + selected_upcoming = upcoming_events + selected_past = past_events # Combine all relevant events into a single list BaseNCAAFBManager.all_events = live_events + selected_upcoming + selected_past @@ -559,6 +523,9 @@ class BaseNCAAFBManager: # Renamed class home_timeouts = home_team.get("timeouts", 3) # Default to 3 if not specified away_timeouts = away_team.get("timeouts", 3) # Default to 3 if not specified + # For upcoming games, we'll show based on number of games, not time window + # For recent games, we'll show based on number of games, not time window + is_within_window = True # Always include games, let the managers filter by count details = { "id": game_event.get("id"), @@ -586,6 +553,7 @@ class BaseNCAAFBManager: # Renamed class "down_distance_text": down_distance_text, # Added Down/Distance "possession": situation.get("possession") if situation else None, # ID of team with possession "possession_indicator": possession_indicator, # Added for easy home/away check + "is_within_window": is_within_window, # Whether game is within display window } # Basic validation (can be expanded) @@ -999,12 +967,12 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class events = data['events'] # self.logger.info(f"[NCAAFB Recent] Processing {len(events)} events from shared data.") # Changed log prefix - # Process games and filter for final & within window & favorite teams + # Process games and filter for final games & favorite teams processed_games = [] for event in events: game = self._extract_game_details(event) - # Filter criteria: must be final, within time window - if game and game['is_final'] and game.get('is_within_window', True): # Assume within window if key missing, check logic + # Filter criteria: must be final + if game and game['is_final']: processed_games.append(game) # Filter for favorite teams @@ -1017,6 +985,9 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class # Sort by game time, most recent first team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.min.replace(tzinfo=timezone.utc), reverse=True) + + # Limit to the specified number of recent games + team_games = team_games[:self.recent_games_to_show] # Cache the processed games self._cache_processed_games('recent', team_games) @@ -1212,8 +1183,8 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class processed_games = [] for event in events: game = self._extract_game_details(event) - # Filter criteria: must be upcoming ('pre' state) and within time window - if game and game['is_upcoming'] and game.get('is_within_window', True): # Assume within window if key missing, check logic + # Filter criteria: must be upcoming ('pre' state) + if game and game['is_upcoming']: processed_games.append(game) # Debug logging to see what games we have @@ -1246,6 +1217,9 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class # Sort by game time, earliest first team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.max.replace(tzinfo=timezone.utc)) + + # Limit to the specified number of upcoming games + team_games = team_games[:self.upcoming_games_to_show] # Cache the processed games self._cache_processed_games('upcoming', team_games) diff --git a/src/nfl_managers.py b/src/nfl_managers.py index 9e4de744..80d7a0b3 100644 --- a/src/nfl_managers.py +++ b/src/nfl_managers.py @@ -23,56 +23,7 @@ logging.basicConfig( datefmt='%Y-%m-%d %H:%M:%S' ) -# Re-add CacheManager definition temporarily until it's confirmed where it lives -class CacheManager: - """Manages caching of ESPN API responses.""" - _instance = None - _cache = {} - _cache_timestamps = {} - def __new__(cls): - if cls._instance is None: - cls._instance = super(CacheManager, cls).__new__(cls) - return cls._instance - - @classmethod - def get(cls, key: str, max_age: int = 60) -> Optional[Dict]: - """ - Get data from cache if it exists and is not stale. - Args: - key: Cache key (usually the date string) - max_age: Maximum age of cached data in seconds - Returns: - Cached data if valid, None if missing or stale - """ - if key not in cls._cache: - return None - - timestamp = cls._cache_timestamps.get(key, 0) - if time.time() - timestamp > max_age: - # Data is stale, remove it - del cls._cache[key] - del cls._cache_timestamps[key] - return None - - return cls._cache[key] - - @classmethod - def set(cls, key: str, data: Dict) -> None: - """ - Store data in cache with current timestamp. - Args: - key: Cache key (usually the date string) - data: Data to cache - """ - cls._cache[key] = data - cls._cache_timestamps[key] = time.time() - - @classmethod - def clear(cls) -> None: - """Clear all cached data.""" - cls._cache.clear() - cls._cache_timestamps.clear() class BaseNFLManager: # Renamed class @@ -102,8 +53,6 @@ class BaseNFLManager: # Renamed class self.current_game = None self.fonts = self._load_fonts() self.favorite_teams = self.nfl_config.get("favorite_teams", []) - self.fetch_past_games = self.nfl_config.get("fetch_past_games", 1) - self.fetch_future_games = self.nfl_config.get("fetch_future_games", 1) # Check display modes to determine what data to fetch display_modes = self.nfl_config.get("display_modes", {}) @@ -122,6 +71,16 @@ class BaseNFLManager: # Renamed class self._logo_cache = {} + # Set up session with retry logic + self.session = requests.Session() + self.session.mount('http://', requests.adapters.HTTPAdapter(max_retries=3)) + self.session.mount('https://', requests.adapters.HTTPAdapter(max_retries=3)) + + # Set up headers for ESPN API + self.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + } + self.logger.info(f"Initialized NFL manager with display dimensions: {self.display_width}x{self.display_height}") self.logger.info(f"Logo directory: {self.logo_dir}") self.logger.info(f"Display modes - Recent: {self.recent_enabled}, Upcoming: {self.upcoming_enabled}, Live: {self.live_enabled}") @@ -172,7 +131,7 @@ class BaseNFLManager: # Renamed class except Exception as e: self.logger.error(f"Error fetching odds for game {game.get('id', 'N/A')}: {e}") - def _fetch_shared_data(self) -> None: + def _fetch_shared_data(self) -> Optional[Dict]: """ Fetches the full season schedule for NFL, caches it, and then filters for relevant games based on the current configuration. @@ -199,11 +158,11 @@ class BaseNFLManager: # Renamed class self.logger.info(f"[NFL] Successfully fetched and cached {len(events)} events for the {current_year} season.") except requests.exceptions.RequestException as e: self.logger.error(f"[NFL] API error fetching full schedule: {e}") - return + return None if not events: self.logger.warning("[NFL] No events found in the schedule data.") - return + return None # Filter the events for live, upcoming, and recent games live_events = [] @@ -227,52 +186,16 @@ class BaseNFLManager: # Renamed class upcoming_events.sort(key=lambda x: x['date']) past_events.sort(key=lambda x: x['date'], reverse=True) - # Select the correct number of games for favorite teams - selected_upcoming = [] - if self.upcoming_enabled and self.favorite_teams: - games_found = {team: 0 for team in self.favorite_teams} - for game in upcoming_events: - competitors = game.get('competitions', [{}])[0].get('competitors', []) - home_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'home'), '') - away_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'away'), '') - - # Check if this game involves any favorite teams that still need games - team_in_game = None - if home_team in self.favorite_teams and games_found[home_team] < self.fetch_future_games: - team_in_game = home_team - elif away_team in self.favorite_teams and games_found[away_team] < self.fetch_future_games: - team_in_game = away_team - - if team_in_game: - selected_upcoming.append(game) - games_found[team_in_game] += 1 - # Stop if we have found enough games for all teams - if all(count >= self.fetch_future_games for count in games_found.values()): - break - - selected_past = [] - if self.recent_enabled and self.favorite_teams: - games_found = {team: 0 for team in self.favorite_teams} - for game in past_events: - competitors = game.get('competitions', [{}])[0].get('competitors', []) - home_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'home'), '') - away_team = next((c['team']['abbreviation'] for c in competitors if c.get('homeAway') == 'away'), '') - - team_in_game = None - if home_team in self.favorite_teams and games_found[home_team] < self.fetch_past_games: - team_in_game = home_team - elif away_team in self.favorite_teams and games_found[away_team] < self.fetch_past_games: - team_in_game = away_team - - if team_in_game: - selected_past.append(game) - games_found[team_in_game] += 1 - if all(count >= self.fetch_past_games for count in games_found.values()): - break + # Include all games in shared data - let individual managers filter by count + selected_upcoming = upcoming_events + selected_past = past_events # Combine all relevant events into a single list BaseNFLManager.all_events = live_events + selected_upcoming + selected_past self.logger.info(f"[NFL] Processed schedule: {len(live_events)} live, {len(selected_upcoming)} upcoming, {len(selected_past)} recent games.") + + # Return the data in the expected format + return {'events': BaseNFLManager.all_events} def _fetch_data(self, date_str: str = None) -> Optional[Dict]: """Fetch data using shared data mechanism or direct fetch for live.""" @@ -294,7 +217,11 @@ class BaseNFLManager: # Renamed class return None else: # For non-live games, use the shared cache - return self._fetch_shared_data() + shared_data = self._fetch_shared_data() + if shared_data is None: + self.logger.warning("[NFL] No shared data available") + return None + return shared_data def _load_fonts(self): """Load fonts used by the scoreboard.""" @@ -949,8 +876,8 @@ class NFLRecentManager(BaseNFLManager): # Renamed class processed_games = [] for event in events: game = self._extract_game_details(event) - # Filter criteria: must be final, within time window - if game and game['is_final'] and game.get('is_within_window', True): # Assume within window if key missing + # Filter criteria: must be final + if game and game['is_final']: # Fetch odds if enabled if self.show_odds: self._fetch_odds(game) @@ -966,6 +893,10 @@ class NFLRecentManager(BaseNFLManager): # Renamed class # Sort by game time, most recent first team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.min.replace(tzinfo=self._get_timezone()), reverse=True) + + # Limit to the specified number of recent games (default 5) + recent_games_to_show = self.nfl_config.get("recent_games_to_show", 5) + team_games = team_games[:recent_games_to_show] # Check if the list of games to display has changed new_game_ids = {g['id'] for g in team_games} @@ -1152,8 +1083,8 @@ class NFLUpcomingManager(BaseNFLManager): # Renamed class processed_games = [] for event in events: game = self._extract_game_details(event) - # Filter criteria: must be upcoming ('pre' state) and within time window - if game and game['is_upcoming'] and game.get('is_within_window', True): # Assume within window if key missing + # Filter criteria: must be upcoming ('pre' state) + if game and game['is_upcoming']: # Fetch odds if enabled if self.show_odds: self._fetch_odds(game) @@ -1177,6 +1108,10 @@ class NFLUpcomingManager(BaseNFLManager): # Renamed class # Sort by game time, earliest first team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.max.replace(tzinfo=self._get_timezone())) + + # Limit to the specified number of upcoming games (default 10) + upcoming_games_to_show = self.nfl_config.get("upcoming_games_to_show", 10) + team_games = team_games[:upcoming_games_to_show] # Log changes or periodically should_log = (