Files
fundi-scraper-api/api/datasource.py
T
2026-09-02 20:20:01 +02:00

69 lines
2.1 KiB
Python

"""Loads the scraped events JSON file and keeps it in memory.
The file is treated as read-only. Call ``reload()`` to pick up a fresh
scrape without restarting the server.
"""
import json
import os
from datetime import date
from pathlib import Path
from threading import Lock
from typing import List, Optional
from models import Event
# Default: the fundi-scraped-output.json sitting in the repo root, one
# directory above this file. Override with the DATA_FILE env var.
_DEFAULT_DATA_FILE = Path(__file__).resolve().parent.parent / "fundi-scraped-output.json"
class EventStore:
def __init__(self, path: Optional[os.PathLike] = None):
self.path = Path(path or os.environ.get("DATA_FILE") or _DEFAULT_DATA_FILE)
self._lock = Lock()
self._events: List[Event] = []
self.reload()
def reload(self) -> int:
"""Re-read the JSON file from disk. Returns the number of events loaded."""
if not self.path.exists():
raise FileNotFoundError(f"Data file not found: {self.path}")
with self.path.open(encoding="utf-8") as fh:
raw = json.load(fh)
events = [Event.model_validate(item) for item in raw]
with self._lock:
self._events = events
return len(events)
def all(self) -> List[Event]:
with self._lock:
return list(self._events)
def get(self, index: int) -> Optional[Event]:
with self._lock:
if 0 <= index < len(self._events):
return self._events[index]
return None
def parse_event_date(value: str) -> Optional[date]:
"""Parse the scraper's ``dd.mm.yy`` date strings. Returns None if unparseable."""
try:
day, month, year = (int(part) for part in value.strip().split("."))
except (ValueError, AttributeError):
return None
if year < 100:
year += 2000
try:
return date(year, month, day)
except ValueError:
return None
def iter_artist_names(event: Event):
for group in event.event_artists:
for artist in group:
if artist.artist_name:
yield artist.artist_name