diff --git a/config/config.json b/config/config.json index 7367c2cf..7f90d7fc 100644 --- a/config/config.json +++ b/config/config.json @@ -177,7 +177,7 @@ "live_odds_update_interval": 3600, "odds_update_interval": 3600, "fetch_past_games": 1, - "fetch_future_games": 1, + "fetch_future_games": 5, "favorite_teams": ["TB", "DAL"], "logo_dir": "assets/sports/nfl_logos", "show_records": true, @@ -196,7 +196,7 @@ "live_odds_update_interval": 3600, "odds_update_interval": 3600, "fetch_past_games": 1, - "fetch_future_games": 1, + "fetch_future_games": 5, "favorite_teams": ["UGA", "AUB"], "logo_dir": "assets/sports/ncaa_fbs_logos", "show_records": true, diff --git a/src/nba_managers.py b/src/nba_managers.py index 033fb040..f88cc3b4 100644 --- a/src/nba_managers.py +++ b/src/nba_managers.py @@ -370,22 +370,16 @@ class BaseNBAManager: """Fetch odds for a specific game if conditions are met.""" self.logger.debug(f"Checking odds for game: {game.get('id', 'N/A')}") - # Ensure the API key is set in the secrets config - if not self.config_manager.get_secret("the_odds_api_key"): - if self._should_log('no_api_key', cooldown=3600): # Log once per hour - self.logger.warning("Odds API key not found. Skipping odds fetch.") - return - # Check if odds should be shown for this sport if not self.show_odds: self.logger.debug("Odds display is disabled for NBA.") return - # Fetch odds using OddsManager + # Fetch odds using OddsManager (ESPN API) try: # Determine update interval based on game state - is_live = game.get('is_live', False) - update_interval = self.nba_config.get("live_odds_update_interval", 3600) if is_live \ + is_live = game.get('status', '').lower() == 'in' + update_interval = self.nba_config.get("live_odds_update_interval", 60) if is_live \ else self.nba_config.get("odds_update_interval", 3600) odds_data = self.odds_manager.get_odds( diff --git a/src/ncaa_fb_managers.py b/src/ncaa_fb_managers.py index 716fb263..42f0eb3e 100644 --- a/src/ncaa_fb_managers.py +++ b/src/ncaa_fb_managers.py @@ -132,22 +132,24 @@ class BaseNCAAFBManager: # Renamed class except pytz.UnknownTimeZoneError: return pytz.utc + def _should_log(self, warning_type: str, cooldown: int = 60) -> bool: + """Check if we should log a warning based on cooldown period.""" + current_time = time.time() + if current_time - self._last_warning_time > cooldown: + self._last_warning_time = current_time + return True + return False + def _fetch_odds(self, game: Dict) -> None: """Fetch odds for a specific game if conditions are met.""" self.logger.debug(f"Checking odds for game: {game.get('id', 'N/A')}") - # Ensure the API key is set in the secrets config - if not self.config_manager.get_secret("the_odds_api_key"): - if self._should_log('no_api_key', cooldown=3600): # Log once per hour - self.logger.warning("Odds API key not found. Skipping odds fetch.") - return - # Check if odds should be shown for this sport if not self.show_odds: self.logger.debug("Odds display is disabled for NCAAFB.") return - # Fetch odds using OddsManager + # Fetch odds using OddsManager (ESPN API) try: # Determine update interval based on game state is_live = game.get('status', '').lower() == 'in' @@ -156,7 +158,7 @@ class BaseNCAAFBManager: # Renamed class odds_data = self.odds_manager.get_odds( sport="football", - league="college-football", + league="ncaa_fb", event_id=game['id'], update_interval_seconds=update_interval ) @@ -198,6 +200,15 @@ class BaseNCAAFBManager: # Renamed class actual_past_games = fetch_past_games if need_past_games else 0 actual_future_games = fetch_future_games if need_future_games else 0 + # For upcoming games, we need to find games for each favorite team + if need_future_games and self.favorite_teams: + # Calculate how many games we need to find for favorite teams + games_needed_per_team = actual_future_games + total_favorite_games_needed = len(self.favorite_teams) * games_needed_per_team + BaseNCAAFBManager.logger.info(f"[NCAAFB] Need to find {games_needed_per_team} games for each of {len(self.favorite_teams)} favorite teams ({total_favorite_games_needed} total)") + else: + total_favorite_games_needed = actual_future_games + BaseNCAAFBManager.logger.info(f"[NCAAFB] Fetching data - Past games: {actual_past_games}, Future games: {actual_future_games}") # Smart game-based fetching with range caching @@ -206,6 +217,9 @@ class BaseNCAAFBManager: # Renamed class past_events = [] future_events = [] + # Track games found for each favorite team + favorite_team_games = {team: [] for team in self.favorite_teams} if self.favorite_teams else {} + # Check for cached search ranges range_cache_key = f"search_ranges_ncaafb_{actual_past_games}_{actual_future_games}" cached_ranges = BaseNCAAFBManager.cache_manager.get(range_cache_key, max_age=86400) # Cache for 24 hours @@ -222,7 +236,9 @@ class BaseNCAAFBManager: # Renamed class days_to_check = max(past_days_needed, future_days_needed) max_days_to_check = 365 # Limit to 1 year to prevent infinite loops - while (len(past_events) < actual_past_games or len(future_events) < actual_future_games) and days_to_check <= max_days_to_check: + while (len(past_events) < actual_past_games or + (need_future_games and self.favorite_teams and + not all(len(games) >= actual_future_games for games in favorite_team_games.values()))) and days_to_check <= max_days_to_check: # Check dates in both directions dates_to_check = [] @@ -290,6 +306,24 @@ class BaseNCAAFBManager: # Renamed class past_events.append(event) else: future_events.append(event) + + # Track games for favorite teams + if self.favorite_teams and need_future_games: + competition = event.get('competitions', [{}])[0] + competitors = competition.get('competitors', []) + home_team = next((c for c in competitors if c.get('homeAway') == 'home'), None) + away_team = next((c for c in competitors if c.get('homeAway') == 'away'), None) + + if home_team and away_team: + home_abbr = home_team['team']['abbreviation'] + away_abbr = away_team['team']['abbreviation'] + + # Check if this game involves a favorite team + for team in self.favorite_teams: + if team in [home_abbr, away_abbr]: + if len(favorite_team_games[team]) < actual_future_games: + favorite_team_games[team].append(event) + BaseNCAAFBManager.logger.debug(f"[NCAAFB] Found game for {team}: {away_abbr}@{home_abbr}") except Exception as e: BaseNCAAFBManager.logger.warning(f"[NCAAFB] Could not parse event date: {e}") continue @@ -313,7 +347,17 @@ class BaseNCAAFBManager: # Renamed class # Take the specified number of games selected_past_events = past_events[-actual_past_games:] if past_events else [] - selected_future_events = future_events[:actual_future_games] if future_events else [] + + # For future games, use favorite team games if available + if self.favorite_teams and need_future_games: + selected_future_events = [] + for team in self.favorite_teams: + team_games = favorite_team_games.get(team, []) + selected_future_events.extend(team_games[:actual_future_games]) + BaseNCAAFBManager.logger.info(f"[NCAAFB] Selected {len(selected_past_events)} past games and {len(selected_future_events)} favorite team future games") + else: + selected_future_events = future_events[:actual_future_games] if future_events else [] + BaseNCAAFBManager.logger.info(f"[NCAAFB] Selected {len(selected_past_events)} past games and {len(selected_future_events)} future games after checking {days_to_check} days") # Combine selected events selected_events = selected_past_events + selected_future_events @@ -1229,6 +1273,13 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class for game in processed_games: self.logger.debug(f"[NCAAFB Upcoming] Game: {game['away_abbr']}@{game['home_abbr']} - Upcoming: {game['is_upcoming']}") + # Log all unique teams found for debugging + all_teams = set() + for game in processed_games: + all_teams.add(game['away_abbr']) + all_teams.add(game['home_abbr']) + self.logger.debug(f"[NCAAFB Upcoming] All teams found in API: {sorted(all_teams)}") + # Filter for favorite teams if self.favorite_teams: team_games = [game for game in processed_games diff --git a/src/nfl_managers.py b/src/nfl_managers.py index b35eb960..fd2beb7c 100644 --- a/src/nfl_managers.py +++ b/src/nfl_managers.py @@ -132,22 +132,24 @@ class BaseNFLManager: # Renamed class except pytz.UnknownTimeZoneError: return pytz.utc + def _should_log(self, warning_type: str, cooldown: int = 60) -> bool: + """Check if we should log a warning based on cooldown period.""" + current_time = time.time() + if current_time - self._last_warning_time > cooldown: + self._last_warning_time = current_time + return True + return False + def _fetch_odds(self, game: Dict) -> None: """Fetch odds for a specific game if conditions are met.""" self.logger.debug(f"Checking odds for game: {game.get('id', 'N/A')}") - # Ensure the API key is set in the secrets config - if not self.config_manager.get_secret("the_odds_api_key"): - if self._should_log('no_api_key', cooldown=3600): # Log once per hour - self.logger.warning("Odds API key not found. Skipping odds fetch.") - return - # Check if odds should be shown for this sport if not self.show_odds: self.logger.debug("Odds display is disabled for NFL.") return - # Fetch odds using OddsManager + # Fetch odds using OddsManager (ESPN API) try: # Determine update interval based on game state is_live = game.get('status', '').lower() == 'in' @@ -198,6 +200,15 @@ class BaseNFLManager: # Renamed class actual_past_games = fetch_past_games if need_past_games else 0 actual_future_games = fetch_future_games if need_future_games else 0 + # For upcoming games, we need to find games for each favorite team + if need_future_games and self.favorite_teams: + # Calculate how many games we need to find for favorite teams + games_needed_per_team = actual_future_games + total_favorite_games_needed = len(self.favorite_teams) * games_needed_per_team + BaseNFLManager.logger.info(f"[NFL] Need to find {games_needed_per_team} games for each of {len(self.favorite_teams)} favorite teams ({total_favorite_games_needed} total)") + else: + total_favorite_games_needed = actual_future_games + BaseNFLManager.logger.info(f"[NFL] Fetching data - Past games: {actual_past_games}, Future games: {actual_future_games}") # Smart game-based fetching with range caching @@ -206,6 +217,9 @@ class BaseNFLManager: # Renamed class past_events = [] future_events = [] + # Track games found for each favorite team + favorite_team_games = {team: [] for team in self.favorite_teams} if self.favorite_teams else {} + # Check for cached search ranges range_cache_key = f"search_ranges_nfl_{actual_past_games}_{actual_future_games}" cached_ranges = BaseNFLManager.cache_manager.get(range_cache_key, max_age=86400) # Cache for 24 hours @@ -222,7 +236,9 @@ class BaseNFLManager: # Renamed class days_to_check = max(past_days_needed, future_days_needed) max_days_to_check = 365 # Limit to 1 year to prevent infinite loops - while (len(past_events) < actual_past_games or len(future_events) < actual_future_games) and days_to_check <= max_days_to_check: + while (len(past_events) < actual_past_games or + (need_future_games and self.favorite_teams and + not all(len(games) >= actual_future_games for games in favorite_team_games.values()))) and days_to_check <= max_days_to_check: # Check dates in both directions dates_to_check = [] @@ -290,6 +306,24 @@ class BaseNFLManager: # Renamed class past_events.append(event) else: future_events.append(event) + + # Track games for favorite teams + if self.favorite_teams and need_future_games: + competition = event.get('competitions', [{}])[0] + competitors = competition.get('competitors', []) + home_team = next((c for c in competitors if c.get('homeAway') == 'home'), None) + away_team = next((c for c in competitors if c.get('homeAway') == 'away'), None) + + if home_team and away_team: + home_abbr = home_team['team']['abbreviation'] + away_abbr = away_team['team']['abbreviation'] + + # Check if this game involves a favorite team + for team in self.favorite_teams: + if team in [home_abbr, away_abbr]: + if len(favorite_team_games[team]) < actual_future_games: + favorite_team_games[team].append(event) + BaseNFLManager.logger.debug(f"[NFL] Found game for {team}: {away_abbr}@{home_abbr}") except Exception as e: BaseNFLManager.logger.warning(f"[NFL] Could not parse event date: {e}") continue @@ -313,7 +347,17 @@ class BaseNFLManager: # Renamed class # Take the specified number of games selected_past_events = past_events[-actual_past_games:] if past_events else [] - selected_future_events = future_events[:actual_future_games] if future_events else [] + + # For future games, use favorite team games if available + if self.favorite_teams and need_future_games: + selected_future_events = [] + for team in self.favorite_teams: + team_games = favorite_team_games.get(team, []) + selected_future_events.extend(team_games[:actual_future_games]) + BaseNFLManager.logger.info(f"[NFL] Selected {len(selected_past_events)} past games and {len(selected_future_events)} favorite team future games") + else: + selected_future_events = future_events[:actual_future_games] if future_events else [] + BaseNFLManager.logger.info(f"[NFL] Selected {len(selected_past_events)} past games and {len(selected_future_events)} future games after checking {days_to_check} days") # Combine selected events selected_events = selected_past_events + selected_future_events