Add diskcache finctionality for caching sports api fixtures.

This commit is contained in:
2026-03-22 12:22:31 +01:00
parent 0cbb772dc4
commit ebf8c78c79
3 changed files with 25 additions and 13 deletions

View File

@@ -14,7 +14,8 @@ dependencies = [
"openpyxl>=3.1.0", "openpyxl>=3.1.0",
"PyYaml==6.0.3", "PyYaml==6.0.3",
"playwright==1.58.0", "playwright==1.58.0",
"requests>=2.32.0" "requests>=2.32.0",
"diskcache>=5.6",
] ]
[project.optional-dependencies] [project.optional-dependencies]

View File

@@ -5,3 +5,4 @@ from pydantic.dataclasses import dataclass
class ResolverConfig: class ResolverConfig:
api_key: str api_key: str
league_map: dict[str, int] league_map: dict[str, int]
cache_path: str = "data/fixture_cache"

View File

@@ -5,6 +5,7 @@ from difflib import SequenceMatcher
from enum import Enum from enum import Enum
from typing import Any from typing import Any
import diskcache
import requests import requests
from beaky import _ansi from beaky import _ansi
@@ -83,6 +84,7 @@ class TicketResolver:
def __init__(self, config: ResolverConfig): def __init__(self, config: ResolverConfig):
self._headers = {"x-apisports-key": config.api_key} self._headers = {"x-apisports-key": config.api_key}
self._league_map = config.league_map self._league_map = config.league_map
self._disk_cache: diskcache.Cache = diskcache.Cache(config.cache_path)
# Cache maps (center_date_str, league_id | None) -> list of fixture dicts # Cache maps (center_date_str, league_id | None) -> list of fixture dicts
self._fixture_cache: dict[tuple[str, int | None], list[dict[str, Any]]] = {} self._fixture_cache: dict[tuple[str, int | None], list[dict[str, Any]]] = {}
# Cache maps league name -> (league_id, confidence) # Cache maps league name -> (league_id, confidence)
@@ -150,19 +152,27 @@ class TicketResolver:
cache_key = (date_str, league_id) cache_key = (date_str, league_id)
if cache_key not in self._fixture_cache: if cache_key not in self._fixture_cache:
date_from = (center - timedelta(days=_DATE_WINDOW)).strftime("%Y-%m-%d") if cache_key in self._disk_cache:
date_to = (center + timedelta(days=_DATE_WINDOW)).strftime("%Y-%m-%d") self._fixture_cache[cache_key] = self._disk_cache[cache_key]
params: dict[str, str | int] = {"from": date_from, "to": date_to} print(_ansi.gray(f" │ /fixtures served from disk cache ({len(self._fixture_cache[cache_key])} fixtures)"))
if league_id is not None: else:
params["league"] = league_id date_from = (center - timedelta(days=_DATE_WINDOW)).strftime("%Y-%m-%d")
params["season"] = center.year if center.month >= 7 else center.year - 1 date_to = (center + timedelta(days=_DATE_WINDOW)).strftime("%Y-%m-%d")
print(_ansi.gray(f" │ GET /fixtures {params}")) params: dict[str, str | int] = {"from": date_from, "to": date_to}
resp = _get(f"{_API_BASE}/fixtures", headers=self._headers, params=params) if league_id is not None:
resp.raise_for_status() params["league"] = league_id
self._fixture_cache[cache_key] = resp.json().get("response", []) params["season"] = center.year if center.month >= 7 else center.year - 1
print(_ansi.gray(f" {len(self._fixture_cache[cache_key])} fixtures returned (cached)")) print(_ansi.gray(f"GET /fixtures {params}"))
resp = _get(f"{_API_BASE}/fixtures", headers=self._headers, params=params)
resp.raise_for_status()
self._fixture_cache[cache_key] = resp.json().get("response", [])
print(_ansi.gray(f"{len(self._fixture_cache[cache_key])} fixtures returned"))
finished = [f for f in self._fixture_cache[cache_key] if _is_finished(f) == 1.0]
if finished:
self._disk_cache[cache_key] = finished
print(_ansi.gray(f"{len(finished)} finished fixture(s) written to disk cache"))
else: else:
print(_ansi.gray(f" │ /fixtures (±{_DATE_WINDOW}d of {date_str}, league={league_id}) served from cache")) print(_ansi.gray(f" │ /fixtures (±{_DATE_WINDOW}d of {date_str}, league={league_id}) served from memory"))
fixture, name_match, date_prox = _best_fixture_match( fixture, name_match, date_prox = _best_fixture_match(
self._fixture_cache[cache_key], bet.team1Name, bet.team2Name, center self._fixture_cache[cache_key], bet.team1Name, bet.team2Name, center