mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-12 21:43:00 +00:00
cache rework
This commit is contained in:
@@ -195,6 +195,7 @@
|
|||||||
"live_update_interval": 30,
|
"live_update_interval": 30,
|
||||||
"live_odds_update_interval": 3600,
|
"live_odds_update_interval": 3600,
|
||||||
"odds_update_interval": 3600,
|
"odds_update_interval": 3600,
|
||||||
|
"season_cache_duration_seconds": 86400,
|
||||||
"fetch_past_games": 1,
|
"fetch_past_games": 1,
|
||||||
"fetch_future_games": 2,
|
"fetch_future_games": 2,
|
||||||
"favorite_teams": ["UGA", "AUB"],
|
"favorite_teams": ["UGA", "AUB"],
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ from src.cache_manager import CacheManager # Keep CacheManager import
|
|||||||
from src.config_manager import ConfigManager
|
from src.config_manager import ConfigManager
|
||||||
from src.odds_manager import OddsManager
|
from src.odds_manager import OddsManager
|
||||||
import pytz
|
import pytz
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
from urllib3.util.retry import Retry
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
ESPN_NCAAFB_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/football/college-football/scoreboard" # Changed URL for NCAA FB
|
ESPN_NCAAFB_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/football/college-football/scoreboard" # Changed URL for NCAA FB
|
||||||
@@ -23,56 +25,7 @@ logging.basicConfig(
|
|||||||
datefmt='%Y-%m-%d %H:%M:%S'
|
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 BaseNCAAFBManager: # Renamed class
|
class BaseNCAAFBManager: # Renamed class
|
||||||
@@ -83,6 +36,8 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
_warning_cooldown = 60 # Only log warnings once per minute
|
_warning_cooldown = 60 # Only log warnings once per minute
|
||||||
_shared_data = None
|
_shared_data = None
|
||||||
_last_shared_update = 0
|
_last_shared_update = 0
|
||||||
|
_processed_games_cache = {} # Cache for processed game data
|
||||||
|
_processed_games_timestamp = 0
|
||||||
cache_manager = CacheManager()
|
cache_manager = CacheManager()
|
||||||
odds_manager = OddsManager(cache_manager, ConfigManager())
|
odds_manager = OddsManager(cache_manager, ConfigManager())
|
||||||
logger = logging.getLogger('NCAAFB') # Changed logger name
|
logger = logging.getLogger('NCAAFB') # Changed logger name
|
||||||
@@ -98,6 +53,28 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
self.logo_dir = self.ncaa_fb_config.get("logo_dir", "assets/sports/ncaa_fbs_logos") # Changed logo dir
|
self.logo_dir = self.ncaa_fb_config.get("logo_dir", "assets/sports/ncaa_fbs_logos") # Changed logo dir
|
||||||
self.update_interval = self.ncaa_fb_config.get("update_interval_seconds", 60)
|
self.update_interval = self.ncaa_fb_config.get("update_interval_seconds", 60)
|
||||||
self.show_records = self.ncaa_fb_config.get('show_records', False)
|
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
|
||||||
|
|
||||||
|
# Set up session with retry logic
|
||||||
|
self.session = requests.Session()
|
||||||
|
retry_strategy = Retry(
|
||||||
|
total=5, # increased number of retries
|
||||||
|
backoff_factor=1, # increased backoff factor
|
||||||
|
status_forcelist=[429, 500, 502, 503, 504], # added 429 to retry list
|
||||||
|
allowed_methods=["GET", "HEAD", "OPTIONS"]
|
||||||
|
)
|
||||||
|
adapter = HTTPAdapter(max_retries=retry_strategy)
|
||||||
|
self.session.mount("https://", adapter)
|
||||||
|
self.session.mount("http://", adapter)
|
||||||
|
|
||||||
|
# Set up headers
|
||||||
|
self.headers = {
|
||||||
|
'User-Agent': 'LEDMatrix/1.0 (https://github.com/yourusername/LEDMatrix; contact@example.com)',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Accept-Language': 'en-US,en;q=0.9',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
}
|
||||||
self.last_update = 0
|
self.last_update = 0
|
||||||
self.current_game = None
|
self.current_game = None
|
||||||
self.fonts = self._load_fonts()
|
self.fonts = self._load_fonts()
|
||||||
@@ -172,10 +149,16 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error fetching odds for game {game.get('id', 'N/A')}: {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 NCAAFB, caches it, and then filters
|
Fetches the full season schedule for NCAAFB, caches it, and then filters
|
||||||
for relevant games based on the current configuration.
|
for relevant games based on the current configuration.
|
||||||
|
|
||||||
|
Caching Strategy:
|
||||||
|
- Season schedules: Cached for 24 hours (configurable) - schedules rarely change
|
||||||
|
- Live games: Cached for 60 seconds - scores update frequently
|
||||||
|
- Processed data: Cached for 5 minutes - avoids re-processing
|
||||||
|
- Recent/Upcoming: Use shared season data + local processing cache
|
||||||
"""
|
"""
|
||||||
now = datetime.now(pytz.utc)
|
now = datetime.now(pytz.utc)
|
||||||
current_year = now.year
|
current_year = now.year
|
||||||
@@ -187,7 +170,9 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
all_events = []
|
all_events = []
|
||||||
for year in years_to_check:
|
for year in years_to_check:
|
||||||
cache_key = f"ncaafb_schedule_{year}"
|
cache_key = f"ncaafb_schedule_{year}"
|
||||||
cached_data = BaseNCAAFBManager.cache_manager.get(cache_key)
|
# Use much longer cache duration for season schedules (configurable, default 24 hours)
|
||||||
|
# Season schedules rarely change and can be cached for days
|
||||||
|
cached_data = BaseNCAAFBManager.cache_manager.get(cache_key, max_age=self.season_cache_duration)
|
||||||
|
|
||||||
if cached_data:
|
if cached_data:
|
||||||
self.logger.info(f"[NCAAFB] Using cached schedule for {year}")
|
self.logger.info(f"[NCAAFB] Using cached schedule for {year}")
|
||||||
@@ -201,7 +186,7 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
events = data.get('events', [])
|
events = data.get('events', [])
|
||||||
BaseNCAAFBManager.cache_manager.set(cache_key, events, expiration_seconds=86400) # Cache for 24 hours
|
BaseNCAAFBManager.cache_manager.update_cache(cache_key, events)
|
||||||
self.logger.info(f"[NCAAFB] Successfully fetched and cached {len(events)} events for the {year} season.")
|
self.logger.info(f"[NCAAFB] Successfully fetched and cached {len(events)} events for the {year} season.")
|
||||||
all_events.extend(events)
|
all_events.extend(events)
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
@@ -210,7 +195,7 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
|
|
||||||
if not all_events:
|
if not all_events:
|
||||||
self.logger.warning("[NCAAFB] No events found in the schedule data for checked years.")
|
self.logger.warning("[NCAAFB] No events found in the schedule data for checked years.")
|
||||||
return
|
return None
|
||||||
|
|
||||||
# Filter the events for live, upcoming, and recent games
|
# Filter the events for live, upcoming, and recent games
|
||||||
live_events = []
|
live_events = []
|
||||||
@@ -278,11 +263,41 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
# Combine all relevant events into a single list
|
# Combine all relevant events into a single list
|
||||||
BaseNCAAFBManager.all_events = live_events + selected_upcoming + selected_past
|
BaseNCAAFBManager.all_events = live_events + selected_upcoming + selected_past
|
||||||
self.logger.info(f"[NCAAFB] Processed schedule: {len(live_events)} live, {len(selected_upcoming)} upcoming, {len(selected_past)} recent games.")
|
self.logger.info(f"[NCAAFB] Processed schedule: {len(live_events)} live, {len(selected_upcoming)} upcoming, {len(selected_past)} recent games.")
|
||||||
|
|
||||||
|
# Return the data in the expected format
|
||||||
|
return {'events': BaseNCAAFBManager.all_events}
|
||||||
|
|
||||||
|
def _get_cached_processed_games(self, manager_type: str) -> Optional[List[Dict]]:
|
||||||
|
"""Get cached processed games for a specific manager type."""
|
||||||
|
current_time = time.time()
|
||||||
|
cache_key = f"processed_games_{manager_type}"
|
||||||
|
|
||||||
|
# Cache processed games for 5 minutes
|
||||||
|
if (current_time - BaseNCAAFBManager._processed_games_timestamp < 300 and
|
||||||
|
cache_key in BaseNCAAFBManager._processed_games_cache):
|
||||||
|
return BaseNCAAFBManager._processed_games_cache[cache_key]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _cache_processed_games(self, manager_type: str, games: List[Dict]) -> None:
|
||||||
|
"""Cache processed games for a specific manager type."""
|
||||||
|
cache_key = f"processed_games_{manager_type}"
|
||||||
|
BaseNCAAFBManager._processed_games_cache[cache_key] = games
|
||||||
|
BaseNCAAFBManager._processed_games_timestamp = time.time()
|
||||||
|
|
||||||
def _fetch_data(self, date_str: str = None) -> Optional[Dict]:
|
def _fetch_data(self, date_str: str = None) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
# Check if the instance is NCAAFBLiveManager
|
# Check if the instance is NCAAFBLiveManager
|
||||||
if isinstance(self, NCAAFBLiveManager): # Changed class name
|
if isinstance(self, NCAAFBLiveManager): # Changed class name
|
||||||
|
# For live games, use shorter cache duration (60 seconds)
|
||||||
|
# Live scores can be fetched more frequently if needed
|
||||||
|
cache_key = f"ncaafb_live_{date_str or 'current'}"
|
||||||
|
cached_data = self.cache_manager.get(cache_key, max_age=60)
|
||||||
|
|
||||||
|
if cached_data:
|
||||||
|
self.logger.debug(f"[NCAAFB] Using cached live data")
|
||||||
|
return cached_data
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = ESPN_NCAAFB_SCOREBOARD_URL # Use NCAA FB URL
|
url = ESPN_NCAAFB_SCOREBOARD_URL # Use NCAA FB URL
|
||||||
params = {}
|
params = {}
|
||||||
@@ -292,6 +307,8 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
response = requests.get(url, params=params)
|
response = requests.get(url, params=params)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
# Cache live data for 60 seconds
|
||||||
|
self.cache_manager.update_cache(cache_key, data)
|
||||||
self.logger.info(f"[NCAAFB] Successfully fetched live game data from ESPN API")
|
self.logger.info(f"[NCAAFB] Successfully fetched live game data from ESPN API")
|
||||||
return data
|
return data
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
@@ -299,7 +316,11 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# For non-live games, use the shared cache
|
# 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("[NCAAFB] No shared data available")
|
||||||
|
return None
|
||||||
|
return shared_data
|
||||||
|
|
||||||
def _load_fonts(self):
|
def _load_fonts(self):
|
||||||
"""Load fonts used by the scoreboard."""
|
"""Load fonts used by the scoreboard."""
|
||||||
@@ -961,70 +982,79 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.last_update = current_time # Update time even if fetch fails
|
self.last_update = current_time # Update time even if fetch fails
|
||||||
try:
|
|
||||||
data = self._fetch_data() # Uses shared cache
|
# Check for cached processed games first
|
||||||
if not data or 'events' not in data:
|
cached_games = self._get_cached_processed_games('recent')
|
||||||
self.logger.warning("[NCAAFB Recent] No events found in shared data.") # Changed log prefix
|
if cached_games:
|
||||||
if not self.games_list: self.current_game = None # Clear display if no games were showing
|
self.logger.debug("[NCAAFB Recent] Using cached processed games")
|
||||||
return
|
team_games = cached_games
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
data = self._fetch_data() # Uses shared cache
|
||||||
|
if not data or 'events' not in data:
|
||||||
|
self.logger.warning("[NCAAFB Recent] No events found in shared data.") # Changed log prefix
|
||||||
|
if not self.games_list: self.current_game = None # Clear display if no games were showing
|
||||||
|
return
|
||||||
|
|
||||||
events = data['events']
|
events = data['events']
|
||||||
# self.logger.info(f"[NCAAFB Recent] Processing {len(events)} events from shared data.") # Changed log prefix
|
# 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 & within window & favorite teams
|
||||||
processed_games = []
|
processed_games = []
|
||||||
for event in events:
|
for event in events:
|
||||||
game = self._extract_game_details(event)
|
game = self._extract_game_details(event)
|
||||||
# Filter criteria: must be final, within time window
|
# 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
|
if game and game['is_final'] and game.get('is_within_window', True): # Assume within window if key missing, check logic
|
||||||
processed_games.append(game)
|
processed_games.append(game)
|
||||||
|
|
||||||
# Filter for favorite teams
|
# Filter for favorite teams
|
||||||
if self.favorite_teams:
|
if self.favorite_teams:
|
||||||
team_games = [game for game in processed_games
|
team_games = [game for game in processed_games
|
||||||
if game['home_abbr'] in self.favorite_teams or
|
if game['home_abbr'] in self.favorite_teams or
|
||||||
game['away_abbr'] in self.favorite_teams]
|
game['away_abbr'] in self.favorite_teams]
|
||||||
else:
|
|
||||||
team_games = processed_games # Show all recent games if no favorites defined
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Check if the list of games to display has changed
|
|
||||||
new_game_ids = {g['id'] for g in team_games}
|
|
||||||
current_game_ids = {g['id'] for g in self.games_list}
|
|
||||||
|
|
||||||
if new_game_ids != current_game_ids:
|
|
||||||
self.logger.info(f"[NCAAFB Recent] Found {len(team_games)} final games within window for display.") # Changed log prefix
|
|
||||||
self.games_list = team_games
|
|
||||||
# Reset index if list changed or current game removed
|
|
||||||
if not self.current_game or not self.games_list or self.current_game['id'] not in new_game_ids:
|
|
||||||
self.current_game_index = 0
|
|
||||||
self.current_game = self.games_list[0] if self.games_list else None
|
|
||||||
self.last_game_switch = current_time # Reset switch timer
|
|
||||||
else:
|
else:
|
||||||
# Try to maintain position if possible
|
team_games = processed_games # Show all recent games if no favorites defined
|
||||||
try:
|
|
||||||
self.current_game_index = next(i for i, g in enumerate(self.games_list) if g['id'] == self.current_game['id'])
|
|
||||||
self.current_game = self.games_list[self.current_game_index] # Update data just in case
|
|
||||||
except StopIteration:
|
|
||||||
self.current_game_index = 0
|
|
||||||
self.current_game = self.games_list[0]
|
|
||||||
self.last_game_switch = current_time
|
|
||||||
|
|
||||||
elif self.games_list:
|
# Sort by game time, most recent first
|
||||||
# List content is same, just update data for current game
|
team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.min.replace(tzinfo=timezone.utc), reverse=True)
|
||||||
self.current_game = self.games_list[self.current_game_index]
|
|
||||||
|
|
||||||
|
# Cache the processed games
|
||||||
|
self._cache_processed_games('recent', team_games)
|
||||||
|
|
||||||
if not self.games_list:
|
# Check if the list of games to display has changed
|
||||||
self.logger.info("[NCAAFB Recent] No relevant recent games found to display.") # Changed log prefix
|
new_game_ids = {g['id'] for g in team_games}
|
||||||
self.current_game = None # Ensure display clears if no games
|
current_game_ids = {g['id'] for g in self.games_list}
|
||||||
|
|
||||||
except Exception as e:
|
if new_game_ids != current_game_ids:
|
||||||
self.logger.error(f"[NCAAFB Recent] Error updating recent games: {e}", exc_info=True) # Changed log prefix
|
self.logger.info(f"[NCAAFB Recent] Found {len(team_games)} final games within window for display.") # Changed log prefix
|
||||||
# Don't clear current game on error, keep showing last known state
|
self.games_list = team_games
|
||||||
# self.current_game = None # Decide if we want to clear display on error
|
# Reset index if list changed or current game removed
|
||||||
|
if not self.current_game or not self.games_list or self.current_game['id'] not in new_game_ids:
|
||||||
|
self.current_game_index = 0
|
||||||
|
self.current_game = self.games_list[0] if self.games_list else None
|
||||||
|
self.last_game_switch = current_time # Reset switch timer
|
||||||
|
else:
|
||||||
|
# Try to maintain position if possible
|
||||||
|
try:
|
||||||
|
self.current_game_index = next(i for i, g in enumerate(self.games_list) if g['id'] == self.current_game['id'])
|
||||||
|
self.current_game = self.games_list[self.current_game_index] # Update data just in case
|
||||||
|
except StopIteration:
|
||||||
|
self.current_game_index = 0
|
||||||
|
self.current_game = self.games_list[0]
|
||||||
|
self.last_game_switch = current_time
|
||||||
|
|
||||||
|
elif self.games_list:
|
||||||
|
# List content is same, just update data for current game
|
||||||
|
self.current_game = self.games_list[self.current_game_index]
|
||||||
|
|
||||||
|
if not self.games_list:
|
||||||
|
self.logger.info("[NCAAFB Recent] No relevant recent games found to display.") # Changed log prefix
|
||||||
|
self.current_game = None # Ensure display clears if no games
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"[NCAAFB Recent] Error updating recent games: {e}", exc_info=True) # Changed log prefix
|
||||||
|
# Don't clear current game on error, keep showing last known state
|
||||||
|
# self.current_game = None # Decide if we want to clear display on error
|
||||||
|
|
||||||
def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
|
def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
|
||||||
"""Draw the layout for a recently completed NCAA FB game.""" # Updated docstring
|
"""Draw the layout for a recently completed NCAA FB game.""" # Updated docstring
|
||||||
@@ -1162,101 +1192,110 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.last_update = current_time
|
self.last_update = current_time
|
||||||
try:
|
|
||||||
data = self._fetch_data() # Uses shared cache
|
# Check for cached processed games first
|
||||||
if not data or 'events' not in data:
|
cached_games = self._get_cached_processed_games('upcoming')
|
||||||
self.logger.warning("[NCAAFB Upcoming] No events found in shared data.") # Changed log prefix
|
if cached_games:
|
||||||
if not self.games_list: self.current_game = None
|
self.logger.debug("[NCAAFB Upcoming] Using cached processed games")
|
||||||
return
|
team_games = cached_games
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
data = self._fetch_data() # Uses shared cache
|
||||||
|
if not data or 'events' not in data:
|
||||||
|
self.logger.warning("[NCAAFB Upcoming] No events found in shared data.") # Changed log prefix
|
||||||
|
if not self.games_list: self.current_game = None
|
||||||
|
return
|
||||||
|
|
||||||
events = data['events']
|
events = data['events']
|
||||||
# self.logger.info(f"[NCAAFB Upcoming] Processing {len(events)} events from shared data.") # Changed log prefix
|
# self.logger.info(f"[NCAAFB Upcoming] Processing {len(events)} events from shared data.") # Changed log prefix
|
||||||
|
|
||||||
processed_games = []
|
processed_games = []
|
||||||
for event in events:
|
for event in events:
|
||||||
game = self._extract_game_details(event)
|
game = self._extract_game_details(event)
|
||||||
# Filter criteria: must be upcoming ('pre' state) and within time window
|
# 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
|
if game and game['is_upcoming'] and game.get('is_within_window', True): # Assume within window if key missing, check logic
|
||||||
processed_games.append(game)
|
processed_games.append(game)
|
||||||
|
|
||||||
# Debug logging to see what games we have
|
# Debug logging to see what games we have
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] Processed {len(processed_games)} upcoming games")
|
self.logger.debug(f"[NCAAFB Upcoming] Processed {len(processed_games)} upcoming games")
|
||||||
for game in processed_games:
|
for game in processed_games:
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] Game: {game['away_abbr']}@{game['home_abbr']} - Upcoming: {game['is_upcoming']}")
|
self.logger.debug(f"[NCAAFB Upcoming] Game: {game['away_abbr']}@{game['home_abbr']} - Upcoming: {game['is_upcoming']}")
|
||||||
|
|
||||||
# Log all unique teams found for debugging
|
# Log all unique teams found for debugging
|
||||||
all_teams = set()
|
all_teams = set()
|
||||||
for game in processed_games:
|
for game in processed_games:
|
||||||
all_teams.add(game['away_abbr'])
|
all_teams.add(game['away_abbr'])
|
||||||
all_teams.add(game['home_abbr'])
|
all_teams.add(game['home_abbr'])
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] All teams found in API: {sorted(all_teams)}")
|
self.logger.debug(f"[NCAAFB Upcoming] All teams found in API: {sorted(all_teams)}")
|
||||||
|
|
||||||
# Debug: Log what events we received and what we extracted
|
# Debug: Log what events we received and what we extracted
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] Received {len(events)} events from shared data")
|
self.logger.debug(f"[NCAAFB Upcoming] Received {len(events)} events from shared data")
|
||||||
for i, event in enumerate(events):
|
for i, event in enumerate(events):
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] Event {i}: ID={event.get('id')}, Status={event.get('competitions', [{}])[0].get('status', {}).get('type', {}).get('name', 'unknown')}")
|
self.logger.debug(f"[NCAAFB Upcoming] Event {i}: ID={event.get('id')}, Status={event.get('competitions', [{}])[0].get('status', {}).get('type', {}).get('name', 'unknown')}")
|
||||||
|
|
||||||
# Filter for favorite teams
|
# Filter for favorite teams
|
||||||
if self.favorite_teams:
|
if self.favorite_teams:
|
||||||
team_games = [game for game in processed_games
|
team_games = [game for game in processed_games
|
||||||
if game['home_abbr'] in self.favorite_teams or
|
if game['home_abbr'] in self.favorite_teams or
|
||||||
game['away_abbr'] in self.favorite_teams]
|
game['away_abbr'] in self.favorite_teams]
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] After favorite team filtering: {len(team_games)} games")
|
self.logger.debug(f"[NCAAFB Upcoming] After favorite team filtering: {len(team_games)} games")
|
||||||
for game in team_games:
|
for game in team_games:
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] Favorite game: {game['away_abbr']}@{game['home_abbr']}")
|
self.logger.debug(f"[NCAAFB Upcoming] Favorite game: {game['away_abbr']}@{game['home_abbr']}")
|
||||||
else:
|
else:
|
||||||
team_games = processed_games # Show all upcoming if no favorites
|
team_games = processed_games # Show all upcoming if no favorites
|
||||||
|
|
||||||
# Sort by game time, earliest first
|
# Sort by game time, earliest first
|
||||||
team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.max.replace(tzinfo=timezone.utc))
|
team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.max.replace(tzinfo=timezone.utc))
|
||||||
|
|
||||||
# Log changes or periodically
|
# Cache the processed games
|
||||||
should_log = (
|
self._cache_processed_games('upcoming', team_games)
|
||||||
current_time - self.last_log_time >= self.log_interval or
|
|
||||||
len(team_games) != len(self.games_list) or
|
|
||||||
any(g1['id'] != g2.get('id') for g1, g2 in zip(self.games_list, team_games)) or
|
|
||||||
(not self.games_list and team_games)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check if the list of games to display has changed
|
# Log changes or periodically
|
||||||
new_game_ids = {g['id'] for g in team_games}
|
should_log = (
|
||||||
current_game_ids = {g['id'] for g in self.games_list}
|
current_time - self.last_log_time >= self.log_interval or
|
||||||
|
len(team_games) != len(self.games_list) or
|
||||||
|
any(g1['id'] != g2.get('id') for g1, g2 in zip(self.games_list, team_games)) or
|
||||||
|
(not self.games_list and team_games)
|
||||||
|
)
|
||||||
|
|
||||||
if new_game_ids != current_game_ids:
|
# Check if the list of games to display has changed
|
||||||
self.logger.info(f"[NCAAFB Upcoming] Found {len(team_games)} upcoming games within window for display.") # Changed log prefix
|
new_game_ids = {g['id'] for g in team_games}
|
||||||
self.games_list = team_games
|
current_game_ids = {g['id'] for g in self.games_list}
|
||||||
if not self.current_game or not self.games_list or self.current_game['id'] not in new_game_ids:
|
|
||||||
self.current_game_index = 0
|
|
||||||
self.current_game = self.games_list[0] if self.games_list else None
|
|
||||||
self.last_game_switch = current_time
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self.current_game_index = next(i for i, g in enumerate(self.games_list) if g['id'] == self.current_game['id'])
|
|
||||||
self.current_game = self.games_list[self.current_game_index]
|
|
||||||
except StopIteration:
|
|
||||||
self.current_game_index = 0
|
|
||||||
self.current_game = self.games_list[0]
|
|
||||||
self.last_game_switch = current_time
|
|
||||||
|
|
||||||
elif self.games_list:
|
if new_game_ids != current_game_ids:
|
||||||
self.current_game = self.games_list[self.current_game_index] # Update data
|
self.logger.info(f"[NCAAFB Upcoming] Found {len(team_games)} upcoming games within window for display.") # Changed log prefix
|
||||||
|
self.games_list = team_games
|
||||||
|
if not self.current_game or not self.games_list or self.current_game['id'] not in new_game_ids:
|
||||||
|
self.current_game_index = 0
|
||||||
|
self.current_game = self.games_list[0] if self.games_list else None
|
||||||
|
self.last_game_switch = current_time
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.current_game_index = next(i for i, g in enumerate(self.games_list) if g['id'] == self.current_game['id'])
|
||||||
|
self.current_game = self.games_list[self.current_game_index]
|
||||||
|
except StopIteration:
|
||||||
|
self.current_game_index = 0
|
||||||
|
self.current_game = self.games_list[0]
|
||||||
|
self.last_game_switch = current_time
|
||||||
|
|
||||||
if not self.games_list:
|
elif self.games_list:
|
||||||
self.logger.info("[NCAAFB Upcoming] No relevant upcoming games found to display.") # Changed log prefix
|
self.current_game = self.games_list[self.current_game_index] # Update data
|
||||||
self.current_game = None
|
|
||||||
|
|
||||||
if should_log and not self.games_list:
|
if not self.games_list:
|
||||||
# Log favorite teams only if no games are found and logging is needed
|
self.logger.info("[NCAAFB Upcoming] No relevant upcoming games found to display.") # Changed log prefix
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] Favorite teams: {self.favorite_teams}") # Changed log prefix
|
self.current_game = None
|
||||||
self.logger.debug(f"[NCAAFB Upcoming] Total upcoming games before filtering: {len(processed_games)}") # Changed log prefix
|
|
||||||
self.last_log_time = current_time
|
|
||||||
elif should_log:
|
|
||||||
self.last_log_time = current_time
|
|
||||||
|
|
||||||
|
if should_log and not self.games_list:
|
||||||
|
# Log favorite teams only if no games are found and logging is needed
|
||||||
|
self.logger.debug(f"[NCAAFB Upcoming] Favorite teams: {self.favorite_teams}") # Changed log prefix
|
||||||
|
self.logger.debug(f"[NCAAFB Upcoming] Total upcoming games before filtering: {len(processed_games)}") # Changed log prefix
|
||||||
|
self.last_log_time = current_time
|
||||||
|
elif should_log:
|
||||||
|
self.last_log_time = current_time
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"[NCAAFB Upcoming] Error updating upcoming games: {e}", exc_info=True) # Changed log prefix
|
self.logger.error(f"[NCAAFB Upcoming] Error updating upcoming games: {e}", exc_info=True) # Changed log prefix
|
||||||
# self.current_game = None # Decide if clear on error
|
# self.current_game = None # Decide if clear on error
|
||||||
|
|
||||||
def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
|
def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
|
||||||
"""Draw the layout for an upcoming NCAA FB game.""" # Updated docstring
|
"""Draw the layout for an upcoming NCAA FB game.""" # Updated docstring
|
||||||
|
|||||||
Reference in New Issue
Block a user