69 lines
2.1 KiB
Python
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
|