Files
comunidadhll/backend/app/data_sources.py
devRaGonSa c70073dbe1 RCON model
2026-03-25 11:46:28 +01:00

141 lines
4.8 KiB
Python

"""Data source selection and contracts for live and historical backend flows."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol
from .collector import collect_server_snapshots
from .config import get_historical_data_source_kind, get_live_data_source_kind
from .providers.public_scoreboard_provider import PublicScoreboardHistoricalDataSource
from .providers.rcon_provider import RconLiveDataSource
from .rcon_historical_read_model import (
describe_rcon_historical_read_model,
list_rcon_historical_recent_activity,
list_rcon_historical_server_summaries,
)
from .server_targets import A2SServerTarget, load_a2s_targets
LIVE_SOURCE_A2S = "a2s"
SOURCE_KIND_RCON = "rcon"
class HistoricalDataSource(Protocol):
"""Contract for historical providers used by ingestion flows."""
source_kind: str
def fetch_public_info(self, *, base_url: str) -> dict[str, object]:
"""Fetch provider metadata for one historical source."""
def fetch_match_page(self, *, base_url: str, page: int, limit: int) -> dict[str, object]:
"""Fetch one page of historical matches."""
def fetch_match_details(
self,
*,
base_url: str,
match_ids: list[str],
max_workers: int,
) -> list[dict[str, object]]:
"""Fetch detailed payloads for one batch of matches."""
class LiveDataSource(Protocol):
"""Contract for live providers used by API payload builders."""
source_kind: str
def collect_snapshots(self, *, persist: bool) -> dict[str, object]:
"""Collect one live snapshot batch."""
def build_target_index(self) -> dict[str | None, A2SServerTarget]:
"""Return optional server connection metadata keyed by external id."""
@dataclass(frozen=True, slots=True)
class A2SLiveDataSource:
"""Live provider backed by the existing A2S collector flow."""
source_kind: str = LIVE_SOURCE_A2S
def collect_snapshots(self, *, persist: bool) -> dict[str, object]:
return collect_server_snapshots(
source_mode="a2s",
allow_controlled_fallback=False,
persist=persist,
)
def build_target_index(self) -> dict[str | None, A2SServerTarget]:
return {
target.external_server_id: target
for target in load_a2s_targets()
if target.external_server_id
}
@dataclass(frozen=True, slots=True)
class RconHistoricalDataSource:
"""Minimal persisted historical read model over prospective RCON capture."""
source_kind: str = SOURCE_KIND_RCON
def fetch_public_info(self, *, base_url: str) -> dict[str, object]:
raise RuntimeError(
"RCON historical read mode does not support CRCON ingestion operations."
)
def fetch_match_page(self, *, base_url: str, page: int, limit: int) -> dict[str, object]:
raise RuntimeError(
"RCON historical read mode does not support CRCON ingestion operations."
)
def fetch_match_details(
self,
*,
base_url: str,
match_ids: list[str],
max_workers: int,
) -> list[dict[str, object]]:
raise RuntimeError(
"RCON historical read mode does not support CRCON ingestion operations."
)
def list_server_summaries(self, *, server_key: str | None = None) -> list[dict[str, object]]:
"""Return coverage and freshness from persisted prospective RCON samples."""
return list_rcon_historical_server_summaries(server_key=server_key)
def list_recent_activity(
self,
*,
server_key: str | None = None,
limit: int = 20,
) -> list[dict[str, object]]:
"""Return recent persisted RCON activity without on-demand network calls."""
return list_rcon_historical_recent_activity(server_key=server_key, limit=limit)
def describe_capabilities(self) -> dict[str, object]:
"""Describe the supported RCON historical read surface."""
return describe_rcon_historical_read_model()
def get_historical_data_source() -> HistoricalDataSource:
"""Select the historical provider configured for the current environment."""
source_kind = get_historical_data_source_kind()
if source_kind == "public-scoreboard":
return PublicScoreboardHistoricalDataSource()
if source_kind == SOURCE_KIND_RCON:
return RconHistoricalDataSource()
raise ValueError(f"Unsupported historical data source: {source_kind}")
def get_live_data_source() -> LiveDataSource:
"""Select the live provider configured for the current environment."""
source_kind = get_live_data_source_kind()
if source_kind == LIVE_SOURCE_A2S:
return A2SLiveDataSource()
if source_kind == SOURCE_KIND_RCON:
return RconLiveDataSource()
raise ValueError(f"Unsupported live data source: {source_kind}")