diff --git a/src/nhl_managers.py b/src/nhl_managers.py index e029a560..3ee1866d 100644 --- a/src/nhl_managers.py +++ b/src/nhl_managers.py @@ -262,15 +262,15 @@ class BaseNHLManager: logging.error(f"[NHL] Error extracting game details: {e}") return None - def _draw_scorebug_layout(self): + def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None: """Draw the scorebug layout for the current game.""" try: # Create a new black image for the main display main_img = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 255)) # Load logos once - home_logo = self._load_and_resize_logo(self.current_game["home_abbr"]) - away_logo = self._load_and_resize_logo(self.current_game["away_abbr"]) + home_logo = self._load_and_resize_logo(game["home_abbr"]) + away_logo = self._load_and_resize_logo(game["away_abbr"]) if not home_logo or not away_logo: self.logger.error("Failed to load one or both team logos") @@ -304,12 +304,12 @@ class BaseNHLManager: draw = ImageDraw.Draw(main_img) # Check if this is an upcoming game - is_upcoming = self.current_game.get("is_upcoming", False) + is_upcoming = game.get("is_upcoming", False) if is_upcoming: # For upcoming games, show date and time - game_date = self.current_game.get("game_date", "") - game_time = self.current_game.get("game_time", "") + game_date = game.get("game_date", "") + game_time = game.get("game_time", "") date_time_text = f"{game_date} {game_time}" # Calculate position for the date/time text (centered at the bottom) @@ -326,8 +326,8 @@ class BaseNHLManager: draw.text((status_x, status_y), status_text, font=self.fonts['status'], fill=(255, 255, 255)) else: # For live/final games, show scores and period/time - home_score = str(self.current_game.get("home_score", "0")) - away_score = str(self.current_game.get("away_score", "0")) + home_score = str(game.get("home_score", "0")) + away_score = str(game.get("away_score", "0")) score_text = f"{away_score} - {home_score}" # Calculate position for the score text (centered at the bottom) @@ -337,11 +337,11 @@ class BaseNHLManager: draw.text((score_x, score_y), score_text, font=self.fonts['score'], fill=(255, 255, 255)) # Draw period and time or Final - if self.current_game.get("is_final", False): + if game.get("is_final", False): status_text = "Final" else: - period = self.current_game.get("period", 0) - clock = self.current_game.get("clock", "0:00") + period = game.get("period", 0) + clock = game.get("clock", "0:00") # Format period text if period > 3: @@ -375,7 +375,7 @@ class BaseNHLManager: self._last_warning_time = current_time return - self._draw_scorebug_layout() + self._draw_scorebug_layout(self.current_game, force_clear) class NHLLiveManager(BaseNHLManager): """Manager for live NHL games.""" @@ -485,198 +485,143 @@ class NHLRecentManager(BaseNHLManager): """Manager for recently completed NHL games.""" def __init__(self, config: Dict[str, Any], display_manager: DisplayManager): super().__init__(config, display_manager) - self.update_interval = self.nhl_config.get("recent_update_interval", 300) # 5 minutes - self.no_data_interval = 900 # 15 minutes when no recent games + self.recent_games = [] + self.current_game_index = 0 self.last_update = 0 - self.logger.info("Initialized NHL Recent Manager") - self.current_game = None - self.games_list = [] # List to store all recent games - # Override test_mode to always use real data for recent games - self.test_mode = False - logging.info("[NHL] Initialized NHLRecentManager in live mode") - + self.update_interval = 300 # 5 minutes + self.recent_hours = self.nhl_config.get("recent_game_hours", 48) + self.logger.info(f"Initialized NHLRecentManager with {len(self.favorite_teams)} favorite teams") + def update(self): - """Update recent game data.""" + """Update recent games data.""" current_time = time.time() - if current_time - self.last_update >= self.update_interval: - self.logger.debug("Updating recent game data") + if current_time - self.last_update < self.update_interval: + return + + try: + # Fetch data from ESPN API + data = self._fetch_data() + if not data or 'events' not in data: + self.logger.warning("[NHL] No events found in ESPN API response") + return + + events = data['events'] + self.logger.info(f"[NHL] Successfully fetched {len(events)} events from ESPN API") + + # Process games + self.recent_games = [] + for event in events: + game = self._extract_game_details(event) + if game: + self.recent_games.append(game) + self.logger.debug(f"Processing game: {game['away_abbr']} vs {game['home_abbr']} - Final: {game['is_final']}, Within window: {game['is_within_window']}") + + # Filter for favorite teams + team_games = [game for game in self.recent_games + if game['home_abbr'] in self.favorite_teams or + game['away_abbr'] in self.favorite_teams] + + self.logger.info(f"[NHL] Found {len(team_games)} recent games for favorite teams") + if not team_games: + self.logger.info("[NHL] No recent games found for favorite teams") + return + + self.games_list = team_games + self.current_game = team_games[0] self.last_update = current_time - if self.test_mode: - # For testing, we'll just update the score to show it's working - if self.current_game: - self.current_game["home_score"] += 1 - logging.debug(f"[NHL] Updated test game score: {self.current_game['home_score']}") - else: - # Fetch recent game data from ESPN API - data = self._fetch_data() - if data and "events" in data: - logging.info(f"[NHL] Fetched {len(data['events'])} events from ESPN API") - # Find all completed games involving favorite teams - new_recent_games = [] - for event in data["events"]: - details = self._extract_game_details(event) - if details: - self.logger.info(f"[NHL] Processing game: {details['away_abbr']} vs {details['home_abbr']}") - self.logger.info(f"[NHL] Game status: is_final={details['is_final']}, is_within_window={details['is_within_window']}") - self.logger.info(f"[NHL] Game time: {details['start_time_utc']}, Cutoff time: {datetime.now(timezone.utc) - timedelta(hours=self.recent_hours)}") - if details["is_final"] and details["is_within_window"]: - if not self.favorite_teams or ( - details["home_abbr"] in self.favorite_teams or - details["away_abbr"] in self.favorite_teams - ): - new_recent_games.append(details) - self.logger.info(f"[NHL] Found recent game: {details['away_abbr']} vs {details['home_abbr']}") - else: - self.logger.info(f"[NHL] Game not involving favorite teams: {details['away_abbr']} vs {details['home_abbr']}") - else: - self.logger.info(f"[NHL] Game not meeting criteria: is_final={details['is_final']}, is_within_window={details['is_within_window']}") - - if new_recent_games: - logging.info(f"[NHL] Found {len(new_recent_games)} recent games for favorite teams") - # Log all games in the list - for i, game in enumerate(new_recent_games): - logging.info(f"[NHL] Game {i+1}: {game['away_abbr']} vs {game['home_abbr']}") - - # Sort games by start time (most recent first) - new_recent_games.sort(key=lambda x: x["start_time_utc"], reverse=True) - logging.info("[NHL] Games after sorting:") - for i, game in enumerate(new_recent_games): - logging.info(f"[NHL] Game {i+1}: {game['away_abbr']} vs {game['home_abbr']}") - - # Only update the games list if we have new games - current_games_set = set(game["away_abbr"] + game["home_abbr"] for game in self.games_list) - new_games_set = set(game["away_abbr"] + game["home_abbr"] for game in new_recent_games) - logging.info(f"[NHL] Current games set: {current_games_set}") - logging.info(f"[NHL] New games set: {new_games_set}") - - if not self.games_list or current_games_set != new_games_set: - self.games_list = new_recent_games - # If we don't have a current game or it's not in the new list, start from the beginning - if not self.current_game or self.current_game not in self.games_list: - self.current_game = self.games_list[0] - logging.info(f"[NHL] Starting with recent game: {self.current_game['away_abbr']} vs {self.current_game['home_abbr']}") - else: - # No recent games found - self.games_list = [] - self.current_game = None - logging.info("[NHL] No recent games found for favorite teams") - else: - logging.warning("[NHL] No events found in ESPN API response") + except Exception as e: + self.logger.error(f"[NHL] Error updating recent games: {e}", exc_info=True) - def display(self, force_clear: bool = False): - """Display recent game information.""" - if not self.current_game: - # Clear the display when there are no games + def display(self, force_clear=False): + """Display recent games.""" + if not self.games_list: + self.logger.info("[NHL] No recent games to display") self.display_manager.clear() return - super().display(force_clear) # Call parent class's display method + + try: + # Draw the scorebug layout + self._draw_scorebug_layout(self.current_game, force_clear) + + # Update display + self.display_manager.update() + + # Move to next game + self.current_game_index = (self.current_game_index + 1) % len(self.games_list) + self.current_game = self.games_list[self.current_game_index] + + except Exception as e: + self.logger.error(f"[NHL] Error displaying recent game: {e}", exc_info=True) class NHLUpcomingManager(BaseNHLManager): """Manager for upcoming NHL games.""" def __init__(self, config: Dict[str, Any], display_manager: DisplayManager): super().__init__(config, display_manager) - self.update_interval = self.nhl_config.get("upcoming_update_interval", 300) # 5 minutes - self.no_data_interval = 900 # 15 minutes when no upcoming games + self.upcoming_games = [] + self.current_game_index = 0 self.last_update = 0 - self.logger.info("Initialized NHL Upcoming Manager") - self.current_game = None - self.games_list = [] # List to store all upcoming games - - def update(self): - """Update upcoming game data.""" - current_time = time.time() - # Use longer interval if no game data - interval = self.no_data_interval if not self.games_list else self.update_interval + self.update_interval = 300 # 5 minutes + self.logger.info(f"Initialized NHLUpcomingManager with {len(self.favorite_teams)} favorite teams") - if current_time - self.last_update >= interval: - self.logger.debug("Updating upcoming game data") + def update(self): + """Update upcoming games data.""" + current_time = time.time() + if current_time - self.last_update < self.update_interval: + return + + try: + # Fetch data from ESPN API + data = self._fetch_data() + if not data or 'events' not in data: + self.logger.warning("[NHL] No events found in ESPN API response") + return + + events = data['events'] + self.logger.info(f"[NHL] Successfully fetched {len(events)} events from ESPN API") + + # Process games + self.upcoming_games = [] + for event in events: + game = self._extract_game_details(event) + if game and not game['is_final'] and game['is_within_window']: + self.upcoming_games.append(game) + self.logger.debug(f"Processing upcoming game: {game['away_abbr']} vs {game['home_abbr']}") + + # Filter for favorite teams + team_games = [game for game in self.upcoming_games + if game['home_abbr'] in self.favorite_teams or + game['away_abbr'] in self.favorite_teams] + + self.logger.info(f"[NHL] Found {len(team_games)} upcoming games for favorite teams") + if not team_games: + self.logger.info("[NHL] No upcoming games found for favorite teams") + return + + self.games_list = team_games + self.current_game = team_games[0] self.last_update = current_time - # Fetch today's and tomorrow's data - today = datetime.now(timezone.utc).date() - tomorrow = today + timedelta(days=1) - - # Format dates for API (YYYYMMDD) - today_str = today.strftime('%Y%m%d') - tomorrow_str = tomorrow.strftime('%Y%m%d') - - # Fetch data for both days - today_data = self._fetch_data(today_str) - tomorrow_data = self._fetch_data(tomorrow_str) - - # Combine events from both days - all_events = [] - if today_data and "events" in today_data: - all_events.extend(today_data["events"]) - if tomorrow_data and "events" in tomorrow_data: - all_events.extend(tomorrow_data["events"]) - - # Find all upcoming games involving favorite teams - upcoming_games = [] - - for event in all_events: - details = self._extract_game_details(event) - if details and details["is_upcoming"] and details["start_time_utc"]: - # Check if it involves favorite teams (if any are configured) - if not self.favorite_teams or ( - details["home_abbr"] in self.favorite_teams or - details["away_abbr"] in self.favorite_teams - ): - # Verify logo files exist for both teams - home_logo_path = os.path.join(self.logo_dir, f"{details['home_abbr']}.png") - away_logo_path = os.path.join(self.logo_dir, f"{details['away_abbr']}.png") - - if not os.path.exists(home_logo_path): - logging.warning(f"[NHL] Home logo not found: {home_logo_path}") - continue - if not os.path.exists(away_logo_path): - logging.warning(f"[NHL] Away logo not found: {away_logo_path}") - continue - - upcoming_games.append(details) - - # Sort games by start time - upcoming_games.sort(key=lambda x: x["start_time_utc"]) - - if upcoming_games: - # Group games by team - team_games = {} - for game in upcoming_games: - for team in self.favorite_teams: - if game["home_abbr"] == team or game["away_abbr"] == team: - if team not in team_games: - team_games[team] = [] - team_games[team].append(game) - - # Debug: Print all favorite team games we found - print("\nDEBUG - Favorite team games found:") - for team, games in team_games.items(): - print(f"\n{team} games:") - for game in games: - print(f" {game['away_abbr']} vs {game['home_abbr']}") - - # Find the first team that has games - first_team_with_games = None - for team in self.favorite_teams: - if team in team_games and team_games[team]: - first_team_with_games = team - break - - if first_team_with_games: - # If we don't have a current game or it's not in the new list, start from the beginning - if not self.current_game or self.current_game not in upcoming_games: - self.current_game = team_games[first_team_with_games][0] - logging.info(f"[NHL] Starting with upcoming game: {self.current_game['away_abbr']} vs {self.current_game['home_abbr']}") - else: - logging.info("[NHL] No upcoming games found for favorite teams") - self.current_game = None - else: - logging.info("[NHL] No upcoming games found") - self.current_game = None + except Exception as e: + self.logger.error(f"[NHL] Error updating upcoming games: {e}", exc_info=True) - def display(self, force_clear: bool = False): - """Display upcoming game information.""" - if not self.current_game: + def display(self, force_clear=False): + """Display upcoming games.""" + if not self.games_list: + self.logger.info("[NHL] No upcoming games to display") + self.display_manager.clear() return - super().display(force_clear) # Call parent class's display method \ No newline at end of file + + try: + # Draw the scorebug layout + self._draw_scorebug_layout(self.current_game, force_clear) + + # Update display + self.display_manager.update() + + # Move to next game + self.current_game_index = (self.current_game_index + 1) % len(self.games_list) + self.current_game = self.games_list[self.current_game_index] + + except Exception as e: + self.logger.error(f"[NHL] Error displaying upcoming game: {e}", exc_info=True) \ No newline at end of file