mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
feat(fetch): one ESPN scoreboard cache key and a max-age response cache (fetch service stage 2) (#728)
Fetch service stage 2: one ESPN scoreboard cache key shared across the sports base classes (legacy keys still read), and a max-age response cache in the fetch service. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+46
-13
@@ -10,7 +10,12 @@ import logging
|
||||
import time
|
||||
from datetime import datetime
|
||||
from types import MappingProxyType
|
||||
from src.common.espn_dates import ESPN_MAX_LIMIT
|
||||
from src.common.espn_dates import (
|
||||
ESPN_MAX_LIMIT,
|
||||
espn_scoreboard_cache_key,
|
||||
read_espn_scoreboard_cache,
|
||||
store_espn_scoreboard_cache,
|
||||
)
|
||||
from src.common.fetch_service import fetch_get, fetch_post, share_connection_pool
|
||||
from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, cast
|
||||
|
||||
@@ -117,6 +122,14 @@ class APIHelper:
|
||||
Returns:
|
||||
Response data as dictionary or None if request fails
|
||||
"""
|
||||
return self._get(url, params, headers, timeout, cache_key, cache_ttl,
|
||||
cache_ttl if cache_key else None)
|
||||
|
||||
def _get(self, url: str, params: Optional[Dict], headers: Optional[Dict],
|
||||
timeout: Optional[int], cache_key: Optional[str], cache_ttl: int,
|
||||
cache_max_age: Optional[float]) -> Optional[Dict]:
|
||||
""":meth:`get`, saying how old a response the fetch service's short
|
||||
response cache may hand back (``cache_max_age``, the caller's TTL)."""
|
||||
if cache_key and self.cache_manager:
|
||||
cached = self._get_from_cache(cache_key, cache_ttl)
|
||||
if cached is not None:
|
||||
@@ -138,7 +151,8 @@ class APIHelper:
|
||||
url,
|
||||
params=params,
|
||||
headers=request_headers,
|
||||
timeout=timeout or self.default_timeout
|
||||
timeout=timeout or self.default_timeout,
|
||||
cache_max_age=cache_max_age,
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
@@ -167,22 +181,23 @@ class APIHelper:
|
||||
sport: Sport name (e.g., 'basketball', 'football')
|
||||
league: League name (e.g., 'nba', 'nfl')
|
||||
date: Date in YYYYMMDD format (defaults to today)
|
||||
cache_key: Cache key for response
|
||||
cache_ttl: Cache time-to-live in seconds
|
||||
|
||||
cache_key: Cache key for response. By default the canonical
|
||||
``espn_scoreboard_cache_key(sport, league, date)``, shared
|
||||
with every other consumer of this scoreboard, with the key
|
||||
this used before (``espn_{sport}_{league}_{date}``) read as a
|
||||
fallback for one release. An explicit key works as before.
|
||||
cache_ttl: Cache time-to-live in seconds. A shared entry is
|
||||
returned only while it is at most this old.
|
||||
|
||||
Returns:
|
||||
ESPN API response data or None if request fails
|
||||
"""
|
||||
if date is None:
|
||||
date = datetime.now().strftime('%Y%m%d')
|
||||
|
||||
|
||||
# Build URL
|
||||
url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard"
|
||||
|
||||
# Build cache key if not provided
|
||||
if cache_key is None:
|
||||
cache_key = f"espn_{sport}_{league}_{date}"
|
||||
|
||||
|
||||
# Set parameters
|
||||
# limit above 500 makes ESPN truncate instead of erroring: college
|
||||
# football came back with 25 of 68 games. See src/common/espn_dates.py.
|
||||
@@ -190,8 +205,26 @@ class APIHelper:
|
||||
'dates': date,
|
||||
'limit': ESPN_MAX_LIMIT
|
||||
}
|
||||
|
||||
return self.get(url, params=params, cache_key=cache_key, cache_ttl=cache_ttl)
|
||||
|
||||
if cache_key is not None:
|
||||
return self.get(url, params=params, cache_key=cache_key, cache_ttl=cache_ttl)
|
||||
|
||||
legacy_key = f"espn_{sport}_{league}_{date}"
|
||||
try:
|
||||
shared_key = espn_scoreboard_cache_key(sport, league, date)
|
||||
except ValueError:
|
||||
# Not a path or date the canonical key covers: the old key.
|
||||
return self.get(url, params=params, cache_key=legacy_key, cache_ttl=cache_ttl)
|
||||
if self.cache_manager:
|
||||
cached = read_espn_scoreboard_cache(
|
||||
self.cache_manager, shared_key, cache_ttl, legacy_keys=(legacy_key,))
|
||||
if cached is not None:
|
||||
self.logger.debug(f"Using cached response for {shared_key}")
|
||||
return cast(Dict[Any, Any], cached)
|
||||
data = self._get(url, params, None, None, None, cache_ttl, cache_ttl)
|
||||
if data is not None and self.cache_manager:
|
||||
store_espn_scoreboard_cache(self.cache_manager, shared_key, data)
|
||||
return data
|
||||
|
||||
def fetch_espn_standings(self, sport: str, league: str,
|
||||
cache_key: Optional[str] = None,
|
||||
|
||||
Reference in New Issue
Block a user