mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
* fix(odds): Resolve incorrect sport and league parameters in _fetch_odds calls - Fixed SportsCore._fetch_data() to call _fetch_odds(game) instead of _fetch_odds(game, sport_key, sport_key) - Updated _fetch_odds method signature to accept only game parameter - Added _fetch_odds_with_params helper method for sport-specific implementations - Updated sport-specific managers to use correct sport and league parameters: - NFL: football/nfl - NCAA Football: football/college-football - NCAA Hockey: hockey/mens-college-hockey - Ensures odds are fetched with correct ESPN API endpoints Fixes #79 * Fix odds and other things * update hockey * fix rankings * update imports * Fix Logo Cache * Add show_favorite_team_only attribute --------- Co-authored-by: ChuckBuilds <33324927+ChuckBuilds@users.noreply.github.com> Co-authored-by: Alex Resnick <adr8282@gmail.com>
197 lines
9.6 KiB
Python
197 lines
9.6 KiB
Python
import os
|
|
import time
|
|
import logging
|
|
import requests
|
|
import json
|
|
from typing import Dict, Any, Optional, List
|
|
from datetime import datetime, timedelta
|
|
from src.display_manager import DisplayManager
|
|
from src.cache_manager import CacheManager # Keep CacheManager import
|
|
import pytz
|
|
from src.base_classes.sports import SportsRecent, SportsUpcoming
|
|
from src.base_classes.football import Football, FootballLive
|
|
from pathlib import Path
|
|
# Constants
|
|
ESPN_NCAAFB_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/football/college-football/scoreboard" # Changed URL for NCAA FB
|
|
|
|
class BaseNCAAFBManager(Football): # Renamed class
|
|
"""Base class for NCAA FB 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('NCAAFB') # Changed logger name
|
|
super().__init__(config=config, display_manager=display_manager, cache_manager=cache_manager, logger=self.logger, sport_key="ncaa_fb")
|
|
|
|
# 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("ncaa_fb_recent", False)
|
|
self.upcoming_enabled = display_modes.get("ncaa_fb_upcoming", False)
|
|
self.live_enabled = display_modes.get("ncaa_fb_live", False)
|
|
self.league = "college-football"
|
|
|
|
self.logger.info(f"Initialized NCAAFB 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_fb_api_data(self, use_cache: bool = True) -> Optional[Dict]:
|
|
"""
|
|
Fetches the full season schedule for NCAAFB using week-by-week approach to ensure
|
|
we get all games, then caches the complete dataset.
|
|
|
|
This method now uses background threading to prevent blocking the display.
|
|
"""
|
|
now = datetime.now(pytz.utc)
|
|
season_year = now.year
|
|
if now.month < 8:
|
|
season_year = now.year - 1
|
|
datestring = f"{season_year}0801-{season_year+1}0201"
|
|
cache_key = f"ncaafb_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_fb",
|
|
year=season_year,
|
|
url=ESPN_NCAAFB_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 NFL data when background service is disabled.
|
|
"""
|
|
now = datetime.now(pytz.utc)
|
|
current_year = now.year
|
|
cache_key = f"ncaafb_schedule_{current_year}"
|
|
|
|
self.logger.info(f"Fetching full {current_year} season schedule from ESPN API (sync mode)...")
|
|
try:
|
|
response = self.session.get(ESPN_NCAAFB_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, NCAAFBLiveManager):
|
|
return self._fetch_todays_games()
|
|
else:
|
|
return self._fetch_ncaa_fb_api_data(use_cache=True)
|
|
|
|
class NCAAFBLiveManager(BaseNCAAFBManager, FootballLive): # Renamed class
|
|
"""Manager for live NCAA FB games.""" # Updated docstring
|
|
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
|
super().__init__(config=config, display_manager=display_manager, cache_manager=cache_manager)
|
|
self.logger = logging.getLogger('NCAAFBLiveManager') # Changed logger name
|
|
|
|
if self.test_mode:
|
|
# More detailed test game for NCAA FB
|
|
self.current_game = {
|
|
"id": "testNCAAFB001",
|
|
"home_id": "343", "away_id": "567",
|
|
"home_abbr": "UGA", "away_abbr": "AUB", # NCAA Examples
|
|
"home_score": "28", "away_score": "21",
|
|
"period": 4, "period_text": "Q4", "clock": "01:15",
|
|
"down_distance_text": "2nd & 5",
|
|
"possession": "UGA", # Placeholder ID for home team
|
|
"possession_indicator": "home", # Explicitly set for test
|
|
"home_timeouts": 1, "away_timeouts": 2,
|
|
"home_logo_path": Path(self.logo_dir, "UGA.png"),
|
|
"away_logo_path": Path(self.logo_dir, "AUB.png"),
|
|
"is_live": True, "is_final": False, "is_upcoming": False, "is_halftime": False,
|
|
"status_text": "Q4 01:15"
|
|
}
|
|
self.live_games = [self.current_game]
|
|
logging.info("Initialized NCAAFBLiveManager with test game: AUB vs UGA") # Updated log message
|
|
else:
|
|
logging.info("Initialized NCAAFBLiveManager in live mode") # Updated log message
|
|
|
|
class NCAAFBRecentManager(BaseNCAAFBManager, SportsRecent): # Renamed class
|
|
"""Manager for recently completed NCAA FB games.""" # Updated docstring
|
|
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
|
super().__init__(config, display_manager, cache_manager)
|
|
self.logger = logging.getLogger('NCAAFBRecentManager') # Changed logger name
|
|
self.logger.info(f"Initialized NCAAFBRecentManager with {len(self.favorite_teams)} favorite teams") # Changed log prefix
|
|
|
|
class NCAAFBUpcomingManager(BaseNCAAFBManager, SportsUpcoming): # Renamed class
|
|
"""Manager for upcoming NCAA FB games.""" # Updated docstring
|
|
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
|
super().__init__(config, display_manager, cache_manager)
|
|
self.logger = logging.getLogger('NCAAFBUpcomingManager') # Changed logger name
|
|
self.logger.info(f"Initialized NCAAFBUpcomingManager with {len(self.favorite_teams)} favorite teams") # Changed log prefix
|