feat: add internal match detail links

Add internal match detail links for recent matches without safe external URLs while preserving external links when available.
This commit is contained in:
devRaGonSa
2026-05-19 10:37:28 +02:00
committed by GitHub
parent d2de0597b6
commit af4de29a0a
9 changed files with 854 additions and 1 deletions

View File

@@ -816,6 +816,83 @@ def list_recent_historical_matches(
return items
def get_historical_match_detail(
*,
server_slug: str,
match_id: str,
db_path: Path | None = None,
) -> dict[str, object] | None:
"""Return one persisted public-scoreboard match detail for the historical API layer."""
normalized_server_slug = _stringify(server_slug)
normalized_match_id = _stringify(match_id)
if not normalized_server_slug or not normalized_match_id:
return None
resolved_path = initialize_historical_storage(db_path=db_path)
with _connect(resolved_path) as connection:
row = connection.execute(
"""
SELECT
historical_servers.slug AS server_slug,
historical_servers.display_name AS server_name,
historical_matches.external_match_id,
historical_matches.started_at,
historical_matches.ended_at,
historical_matches.map_pretty_name,
historical_matches.map_name,
historical_matches.allied_score,
historical_matches.axis_score,
historical_matches.raw_payload_ref,
historical_servers.scoreboard_base_url,
COUNT(historical_player_match_stats.id) AS player_count,
SUM(COALESCE(historical_player_match_stats.time_seconds, 0)) AS total_time_seconds
FROM historical_matches
INNER JOIN historical_servers
ON historical_servers.id = historical_matches.historical_server_id
LEFT JOIN historical_player_match_stats
ON historical_player_match_stats.historical_match_id = historical_matches.id
WHERE historical_servers.slug = ?
AND historical_matches.external_match_id = ?
GROUP BY historical_matches.id
LIMIT 1
""",
(normalized_server_slug, normalized_match_id),
).fetchone()
if row is None:
return None
started_at = row["started_at"]
ended_at = row["ended_at"]
return {
"server": {
"slug": row["server_slug"],
"name": row["server_name"],
},
"match_id": row["external_match_id"],
"started_at": started_at,
"ended_at": ended_at,
"closed_at": ended_at or started_at,
"duration_seconds": _calculate_match_duration_seconds(started_at, ended_at),
"map": {
"name": row["map_name"],
"pretty_name": row["map_pretty_name"] or row["map_name"],
},
"result": {
"allied_score": _coerce_int(row["allied_score"]),
"axis_score": _coerce_int(row["axis_score"]),
"winner": _resolve_match_winner(
row["allied_score"],
row["axis_score"],
),
},
"player_count": int(row["player_count"] or 0),
"total_time_seconds": _coerce_int(row["total_time_seconds"]),
"capture_basis": "public-scoreboard-match",
"match_url": _resolve_safe_match_url(
row["raw_payload_ref"],
row["scoreboard_base_url"],
),
}
def list_historical_server_summaries(
*,
server_slug: str | None = None,
@@ -3140,6 +3217,18 @@ def _resolve_safe_match_url(raw_payload_ref: object, scoreboard_base_url: object
return candidate
def _calculate_match_duration_seconds(started_at: object, ended_at: object) -> int | None:
start_text = _stringify(started_at)
end_text = _stringify(ended_at)
if not start_text or not end_text:
return None
try:
duration = _parse_timestamp(end_text) - _parse_timestamp(start_text)
except ValueError:
return None
return max(0, int(duration.total_seconds()))
def _start_of_week(value: datetime) -> datetime:
normalized = value.astimezone(timezone.utc)
midnight = normalized.replace(hour=0, minute=0, second=0, microsecond=0)