huge cache refactor, dictonary error updates, and changed from time based game searching to game based for recent and upcoming

This commit is contained in:
Chuck
2025-07-21 21:37:22 -05:00
parent bf44d7b55b
commit ba232c58b7
5 changed files with 141 additions and 215 deletions

View File

@@ -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",

View File

@@ -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:

View File

@@ -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:

View File

@@ -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)

View File

@@ -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 = (