From 3406234e00d8526beca1a18bc1563782f2d449a9 Mon Sep 17 00:00:00 2001 From: Alex Resnick Date: Sun, 5 Oct 2025 14:19:03 -0500 Subject: [PATCH] Add NCAA Women's Hockey (#96) * Add NCAA Womens Hockey * Fix status text --------- Co-authored-by: Alex Resnick --- assets/sports/ncaa_logos/NE.png | Bin 0 -> 45786 bytes config/config.template.json | 26 ++++ src/base_classes/hockey.py | 1 + src/base_classes/sports.py | 11 +- src/display_controller.py | 50 +++++- src/logo_downloader.py | 1 + src/ncaaw_hockey_managers.py | 267 ++++++++++++++++++++++++++++++++ 7 files changed, 351 insertions(+), 5 deletions(-) create mode 100644 assets/sports/ncaa_logos/NE.png create mode 100644 src/ncaaw_hockey_managers.py diff --git a/assets/sports/ncaa_logos/NE.png b/assets/sports/ncaa_logos/NE.png new file mode 100644 index 0000000000000000000000000000000000000000..9f672ecea4ecbf39f1dda76535c235e59243f45b GIT binary patch literal 45786 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 QbntinoQZznS;ss72Md 0: home_shots = round(away_team_saves / away_team_saves_per) + status_short = status["type"].get("shortDetail", "") if situation and status["type"]["state"] == "in": # Detect scoring events from status detail diff --git a/src/base_classes/sports.py b/src/base_classes/sports.py index 02c15b9b..3c0d0585 100644 --- a/src/base_classes/sports.py +++ b/src/base_classes/sports.py @@ -419,9 +419,14 @@ class SportsCore(ABC): if not home_team or not away_team: self.logger.warning(f"Could not find home or away team in event: {game_event.get('id')}") return None, None, None, None, None - - home_abbr = home_team["team"]["abbreviation"] - away_abbr = away_team["team"]["abbreviation"] + try: + home_abbr = home_team["team"]["abbreviation"] + except KeyError: + home_abbr = home_team["team"]["name"][:3] + try: + away_abbr = away_team["team"]["abbreviation"] + except KeyError: + away_abbr = away_team["team"]["name"][:3] # Check if this is a favorite team game BEFORE doing expensive logging is_favorite_game = (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams) diff --git a/src/display_controller.py b/src/display_controller.py index 151499b6..b7777a88 100644 --- a/src/display_controller.py +++ b/src/display_controller.py @@ -31,6 +31,7 @@ from src.ncaa_fb_managers import NCAAFBLiveManager, NCAAFBRecentManager, NCAAFBU from src.ncaa_baseball_managers import NCAABaseballLiveManager, NCAABaseballRecentManager, NCAABaseballUpcomingManager from src.ncaam_basketball_managers import NCAAMBasketballLiveManager, NCAAMBasketballRecentManager, NCAAMBasketballUpcomingManager from src.ncaam_hockey_managers import NCAAMHockeyLiveManager, NCAAMHockeyRecentManager, NCAAMHockeyUpcomingManager +from src.ncaaw_hockey_managers import NCAAWHockeyLiveManager, NCAAWHockeyRecentManager, NCAAWHockeyUpcomingManager from src.youtube_display import YouTubeDisplay from src.calendar_manager import CalendarManager from src.text_display import TextDisplay @@ -255,6 +256,21 @@ class DisplayController: self.ncaam_hockey_recent = None self.ncaam_hockey_upcoming = None logger.info("NCAA Men's Hockey managers initialized in %.3f seconds", time.time() - ncaam_hockey_time) + + # Initialize NCAA Men's Hockey managers if enabled + ncaaw_hockey_time = time.time() + ncaaw_hockey_enabled = self.config.get('ncaaw_hockey_scoreboard', {}).get('enabled', False) + ncaaw_hockey_display_modes = self.config.get('ncaaw_hockey_scoreboard', {}).get('display_modes', {}) + + if ncaaw_hockey_enabled: + self.ncaaw_hockey_live = NCAAWHockeyLiveManager(self.config, self.display_manager, self.cache_manager) if ncaaw_hockey_display_modes.get('ncaaw_hockey_live', True) else None + self.ncaaw_hockey_recent = NCAAWHockeyRecentManager(self.config, self.display_manager, self.cache_manager) if ncaaw_hockey_display_modes.get('ncaaw_hockey_recent', True) else None + self.ncaaw_hockey_upcoming = NCAAWHockeyUpcomingManager(self.config, self.display_manager, self.cache_manager) if ncaaw_hockey_display_modes.get('ncaaw_hockey_upcoming', True) else None + else: + self.ncaaw_hockey_live = None + self.ncaaw_hockey_recent = None + self.ncaaw_hockey_upcoming = None + logger.info("NCAA Men's Hockey managers initialized in %.3f seconds", time.time() - ncaaw_hockey_time) # Track MLB rotation state self.mlb_current_team_index = 0 @@ -273,6 +289,7 @@ class DisplayController: self.ncaa_baseball_live_priority = self.config.get('ncaa_baseball_scoreboard', {}).get('live_priority', True) self.ncaam_basketball_live_priority = self.config.get('ncaam_basketball_scoreboard', {}).get('live_priority', True) self.ncaam_hockey_live_priority = self.config.get('ncaam_hockey_scoreboard', {}).get('live_priority', True) + self.ncaaw_hockey_live_priority = self.config.get('ncaaw_hockey_scoreboard', {}).get('live_priority', True) # List of available display modes (adjust order as desired) self.available_modes = [] @@ -322,6 +339,9 @@ class DisplayController: if ncaam_hockey_enabled: if self.ncaam_hockey_recent: self.available_modes.append('ncaam_hockey_recent') if self.ncaam_hockey_upcoming: self.available_modes.append('ncaam_hockey_upcoming') + if ncaaw_hockey_enabled: + if self.ncaaw_hockey_recent: self.available_modes.append('ncaaw_hockey_recent') + if self.ncaaw_hockey_upcoming: self.available_modes.append('ncaaw_hockey_upcoming') # Add live modes to rotation if live_priority is False and there are live games self._update_live_modes_in_rotation() @@ -427,7 +447,10 @@ class DisplayController: 'ncaam_basketball_upcoming': 15, 'ncaam_hockey_live': 30, # Added NCAA Men's Hockey durations 'ncaam_hockey_recent': 15, - 'ncaam_hockey_upcoming': 15 + 'ncaam_hockey_upcoming': 15, + 'ncaaw_hockey_live': 30, # Added NCAA Men's Hockey durations + 'ncaaw_hockey_recent': 15, + 'ncaaw_hockey_upcoming': 15 } # Merge loaded durations with defaults for key, value in default_durations.items(): @@ -683,6 +706,10 @@ class DisplayController: if self.ncaam_hockey_live: self.ncaam_hockey_live.update() if self.ncaam_hockey_recent: self.ncaam_hockey_recent.update() if self.ncaam_hockey_upcoming: self.ncaam_hockey_upcoming.update() + elif current_sport == 'ncaaw_hockey': + if self.ncaaw_hockey_live: self.ncaaw_hockey_live.update() + if self.ncaaw_hockey_recent: self.ncaaw_hockey_recent.update() + if self.ncaaw_hockey_upcoming: self.ncaaw_hockey_upcoming.update() else: # If no specific sport is active, update all managers (fallback behavior) # This ensures data is available when switching to a sport @@ -726,6 +753,10 @@ class DisplayController: if self.ncaam_hockey_recent: self.ncaam_hockey_recent.update() if self.ncaam_hockey_upcoming: self.ncaam_hockey_upcoming.update() + if self.ncaaw_hockey_live: self.ncaaw_hockey_live.update() + if self.ncaaw_hockey_recent: self.ncaaw_hockey_recent.update() + if self.ncaaw_hockey_upcoming: self.ncaaw_hockey_upcoming.update() + def _check_live_games(self) -> tuple: """ Check if there are any live games available. @@ -755,6 +786,8 @@ class DisplayController: live_checks['ncaam_basketball'] = self.ncaam_basketball_live and self.ncaam_basketball_live.live_games if 'ncaam_hockey_scoreboard' in self.config and self.config['ncaam_hockey_scoreboard'].get('enabled', False): live_checks['ncaam_hockey'] = self.ncaam_hockey_live and self.ncaam_hockey_live.live_games + if 'ncaaw_hockey_scoreboard' in self.config and self.config['ncaaw_hockey_scoreboard'].get('enabled', False): + live_checks['ncaaw_hockey'] = self.ncaaw_hockey_live and self.ncaaw_hockey_live.live_games for sport, has_live_games in live_checks.items(): if has_live_games: @@ -1006,6 +1039,7 @@ class DisplayController: ncaa_baseball_enabled = self.config.get('ncaa_baseball_scoreboard', {}).get('enabled', False) ncaam_basketball_enabled = self.config.get('ncaam_basketball_scoreboard', {}).get('enabled', False) ncaam_hockey_enabled = self.config.get('ncaam_hockey_scoreboard', {}).get('enabled', False) + ncaaw_hockey_enabled = self.config.get('ncaaw_hockey_scoreboard', {}).get('enabled', False) update_mode('nhl_live', getattr(self, 'nhl_live', None), self.nhl_live_priority, nhl_enabled) update_mode('nba_live', getattr(self, 'nba_live', None), self.nba_live_priority, nba_enabled) @@ -1017,6 +1051,7 @@ class DisplayController: update_mode('ncaa_baseball_live', getattr(self, 'ncaa_baseball_live', None), self.ncaa_baseball_live_priority, ncaa_baseball_enabled) update_mode('ncaam_basketball_live', getattr(self, 'ncaam_basketball_live', None), self.ncaam_basketball_live_priority, ncaam_basketball_enabled) update_mode('ncaam_hockey_live', getattr(self, 'ncaam_hockey_live', None), self.ncaam_hockey_live_priority, ncaam_hockey_enabled) + update_mode('ncaaw_hockey_live', getattr(self, 'ncaaw_hockey_live', None), self.ncaaw_hockey_live_priority, ncaaw_hockey_enabled) def run(self): """Run the display controller, switching between displays.""" @@ -1066,7 +1101,8 @@ class DisplayController: ('ncaa_fb', 'ncaa_fb_live', self.ncaa_fb_live_priority), ('ncaa_baseball', 'ncaa_baseball_live', self.ncaa_baseball_live_priority), ('ncaam_basketball', 'ncaam_basketball_live', self.ncaam_basketball_live_priority), - ('ncaam_hockey', 'ncaam_hockey_live', self.ncaam_hockey_live_priority) + ('ncaam_hockey', 'ncaam_hockey_live', self.ncaam_hockey_live_priority), + ('ncaaw_hockey', 'ncaaw_hockey_live', self.ncaaw_hockey_live_priority) ]: manager = getattr(self, attr, None) # Only consider sports that are enabled (manager is not None) and have actual live games @@ -1276,6 +1312,12 @@ class DisplayController: manager_to_display = self.ncaam_hockey_recent elif self.current_display_mode == 'ncaam_hockey_upcoming' and self.ncaam_hockey_upcoming: manager_to_display = self.ncaam_hockey_upcoming + elif self.current_display_mode == 'ncaaw_hockey_live' and self.ncaaw_hockey_live: + manager_to_display = self.ncaaw_hockey_live + elif self.current_display_mode == 'ncaaw_hockey_recent' and self.ncaaw_hockey_recent: + manager_to_display = self.ncaaw_hockey_recent + elif self.current_display_mode == 'ncaaw_hockey_upcoming' and self.ncaaw_hockey_upcoming: + manager_to_display = self.ncaaw_hockey_upcoming elif self.current_display_mode == 'mlb_live' and self.mlb_live: manager_to_display = self.mlb_live elif self.current_display_mode == 'milb_live' and self.milb_live: @@ -1349,6 +1391,10 @@ class DisplayController: self.ncaam_hockey_recent.display(force_clear=self.force_clear) elif self.current_display_mode == 'ncaam_hockey_upcoming' and self.ncaam_hockey_upcoming: self.ncaam_hockey_upcoming.display(force_clear=self.force_clear) + elif self.current_display_mode == 'ncaaw_hockey_recent' and self.ncaaw_hockey_recent: + self.ncaaw_hockey_recent.display(force_clear=self.force_clear) + elif self.current_display_mode == 'ncaaw_hockey_upcoming' and self.ncaaw_hockey_upcoming: + self.ncaaw_hockey_upcoming.display(force_clear=self.force_clear) elif self.current_display_mode == 'milb_live' and self.milb_live and len(self.milb_live.live_games) > 0: logger.debug(f"[DisplayController] Calling MiLB live display with {len(self.milb_live.live_games)} live games") # Update data before displaying for live managers diff --git a/src/logo_downloader.py b/src/logo_downloader.py index 1756e9c6..218b3057 100644 --- a/src/logo_downloader.py +++ b/src/logo_downloader.py @@ -58,6 +58,7 @@ class LogoDownloader: 'ncaam_basketball': 'assets/sports/ncaa_logos', 'ncaa_baseball': 'assets/sports/ncaa_logos', 'ncaam_hockey': 'assets/sports/ncaa_logos', + 'ncaaw_hockey': 'assets/sports/ncaa_logos', # Soccer leagues - all use the same soccer_logos directory 'soccer_eng.1': 'assets/sports/soccer_logos', 'soccer_esp.1': 'assets/sports/soccer_logos', diff --git a/src/ncaaw_hockey_managers.py b/src/ncaaw_hockey_managers.py new file mode 100644 index 00000000..9ff82c09 --- /dev/null +++ b/src/ncaaw_hockey_managers.py @@ -0,0 +1,267 @@ +import logging +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, Optional + +import pytz +import requests + +from src.base_classes.hockey import Hockey, HockeyLive +from src.base_classes.sports import SportsRecent, SportsUpcoming +from src.cache_manager import CacheManager # Keep CacheManager import +from src.display_manager import DisplayManager + +# Constants +ESPN_NCAAWH_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/hockey/womens-college-hockey/scoreboard" + + +class BaseNCAAWHockeyManager(Hockey): # Renamed class + """Base class for NCAA Womens Hockey managers with common functionality.""" # Updated docstring + + # Class variables for warning tracking + _no_data_warning_logged = False + _last_warning_time = 0 + _warning_cooldown = 60 # Only log warnings once per minute + _shared_data = None + _last_shared_update = 0 + _processed_games_cache = {} # Cache for processed game data + _processed_games_timestamp = 0 + + def __init__( + self, + config: Dict[str, Any], + display_manager: DisplayManager, + cache_manager: CacheManager, + ): + self.logger = logging.getLogger("NCAAWH") # Changed logger name + super().__init__( + config=config, + display_manager=display_manager, + cache_manager=cache_manager, + logger=self.logger, + sport_key="ncaaw_hockey", + ) + + # Configuration is already set in base class + # self.logo_dir and self.update_interval are already configured + + # Check display modes to determine what data to fetch + display_modes = self.mode_config.get("display_modes", {}) + self.recent_enabled = display_modes.get("ncaaw_hockey_recent", False) + self.upcoming_enabled = display_modes.get("ncaaw_hockey_upcoming", False) + self.live_enabled = display_modes.get("ncaaw_hockey_live", False) + self.league = "womens-college-hockey" + + self.logger.info( + f"Initialized NCAAWHockey 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}" + ) + + def _fetch_ncaa_hockey_api_data(self, use_cache: bool = True) -> Optional[Dict]: + """ + Fetches the full season schedule for NCAAWH, caches it, and then filters + for relevant games based on the current configuration. + """ + now = datetime.now(pytz.utc) + season_year = now.year + if now.month < 8: + season_year = now.year - 1 + datestring = f"{season_year}0901-{season_year+1}0501" + cache_key = f"ncaa_womens_hockey_schedule_{season_year}" + + if use_cache: + cached_data = self.cache_manager.get(cache_key) + if cached_data: + # Validate cached data structure + if isinstance(cached_data, dict) and "events" in cached_data: + self.logger.info(f"Using cached schedule for {season_year}") + return cached_data + elif isinstance(cached_data, list): + # Handle old cache format (list of events) + self.logger.info( + f"Using cached schedule for {season_year} (legacy format)" + ) + return {"events": cached_data} + else: + self.logger.warning( + f"Invalid cached data format for {season_year}: {type(cached_data)}" + ) + # Clear invalid cache + self.cache_manager.clear_cache(cache_key) + + # If background service is disabled, fall back to synchronous fetch + if not self.background_enabled or not self.background_service: + return self._fetch_ncaa_api_data_sync(use_cache) + + self.logger.info( + f"Fetching full {season_year} season schedule from ESPN API..." + ) + + # Start background fetch + self.logger.info( + f"Starting background fetch for {season_year} season schedule..." + ) + + def fetch_callback(result): + """Callback when background fetch completes.""" + if result.success: + self.logger.info( + f"Background fetch completed for {season_year}: {len(result.data.get('events'))} events" + ) + else: + self.logger.error( + f"Background fetch failed for {season_year}: {result.error}" + ) + + # Clean up request tracking + if season_year in self.background_fetch_requests: + del self.background_fetch_requests[season_year] + + # Get background service configuration + background_config = self.mode_config.get("background_service", {}) + timeout = background_config.get("request_timeout", 30) + max_retries = background_config.get("max_retries", 3) + priority = background_config.get("priority", 2) + + # Submit background fetch request + request_id = self.background_service.submit_fetch_request( + sport="ncaa_womens_hockey", + year=season_year, + url=ESPN_NCAAWH_SCOREBOARD_URL, + cache_key=cache_key, + params={"dates": datestring, "limit": 1000}, + headers=self.headers, + timeout=timeout, + max_retries=max_retries, + priority=priority, + callback=fetch_callback, + ) + + # Track the request + self.background_fetch_requests[season_year] = request_id + + # For immediate response, try to get partial data + partial_data = self._get_weeks_data() + if partial_data: + return partial_data + return None + + def _fetch_ncaa_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]: + """ + Synchronous fallback for fetching NCAA Womens Hockey data when background service is disabled. + """ + now = datetime.now(pytz.utc) + current_year = now.year + cache_key = f"ncaa_womens_hockey_schedule_{current_year}" + + self.logger.info( + f"Fetching full {current_year} season schedule from ESPN API (sync mode)..." + ) + try: + response = self.session.get( + ESPN_NCAAWH_SCOREBOARD_URL, + params={"dates": current_year, "limit": 1000}, + headers=self.headers, + timeout=15, + ) + response.raise_for_status() + data = response.json() + events = data.get("events", []) + + if use_cache: + self.cache_manager.set(cache_key, events) + + self.logger.info( + f"Successfully fetched {len(events)} events for the {current_year} season." + ) + return {"events": events} + except requests.exceptions.RequestException as e: + self.logger.error(f"[API error fetching full schedule: {e}") + return None + + def _fetch_data(self) -> Optional[Dict]: + """Fetch data using shared data mechanism or direct fetch for live.""" + if isinstance(self, NCAAWHockeyLiveManager): + return self._fetch_todays_games() + else: + return self._fetch_ncaa_hockey_api_data(use_cache=True) + + +class NCAAWHockeyLiveManager(BaseNCAAWHockeyManager, HockeyLive): # Renamed class + """Manager for live NCAA Mens Hockey games.""" + + def __init__( + self, + config: Dict[str, Any], + display_manager: DisplayManager, + cache_manager: CacheManager, + ): + super().__init__(config, display_manager, cache_manager) + self.logger = logging.getLogger("NCAAWHockeyLiveManager") # Changed logger name + + # Initialize with test game only if test mode is enabled + if self.test_mode: + self.current_game = { + "id": "401596361", + "home_abbr": "RIT", + "away_abbr": "CLAR ", + "home_score": "3", + "away_score": "2", + "period": 2, + "period_text": "1st", + "home_id": "178", + "away_id": "2137", + "clock": "12:34", + "home_logo_path": Path(self.logo_dir, "RIT.png"), + "away_logo_path": Path(self.logo_dir, "CLAR .png"), + "game_time": "7:30 PM", + "game_date": "Apr 17", + "is_live": True, + "is_final": False, + "is_upcoming": False, + } + self.live_games = [self.current_game] + self.logger.info( + "Initialized NCAAWHockeyLiveManager with test game: RIT vs CLAR " + ) + else: + self.logger.info("Initialized NCAAWHockeyLiveManager in live mode") + + +class NCAAWHockeyRecentManager(BaseNCAAWHockeyManager, SportsRecent): + """Manager for recently completed NCAAWH games.""" + + def __init__( + self, + config: Dict[str, Any], + display_manager: DisplayManager, + cache_manager: CacheManager, + ): + super().__init__(config, display_manager, cache_manager) + self.logger = logging.getLogger( + "NCAAWHockeyRecentManager" + ) # Changed logger name + self.logger.info( + f"Initialized NCAAWHockeyRecentManager with {len(self.favorite_teams)} favorite teams" + ) + + +class NCAAWHockeyUpcomingManager(BaseNCAAWHockeyManager, SportsUpcoming): + """Manager for upcoming NCAA Womens Hockey games.""" + + def __init__( + self, + config: Dict[str, Any], + display_manager: DisplayManager, + cache_manager: CacheManager, + ): + super().__init__(config, display_manager, cache_manager) + self.logger = logging.getLogger( + "NCAAWHockeyUpcomingManager" + ) # Changed logger name + self.logger.info( + f"Initialized NCAAWHockeyUpcomingManager with {len(self.favorite_teams)} favorite teams" + )