165 lines
6.5 KiB
Python
165 lines
6.5 KiB
Python
"""Report active PostgreSQL/displayed storage backend and migration parity counts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sqlite3
|
|
from contextlib import closing
|
|
|
|
from .config import get_database_url, get_storage_path, use_postgres_rcon_storage
|
|
from .rcon_admin_log_materialization import summarize_rcon_materialization_status
|
|
from .rcon_admin_log_storage import initialize_rcon_admin_log_storage
|
|
from .sqlite_utils import connect_sqlite_readonly
|
|
|
|
|
|
MIGRATED_RCON_TABLES = (
|
|
"rcon_admin_log_events",
|
|
"rcon_player_profile_snapshots",
|
|
"rcon_materialized_matches",
|
|
"rcon_match_player_stats",
|
|
"rcon_historical_targets",
|
|
"rcon_historical_samples",
|
|
"rcon_historical_competitive_windows",
|
|
"rcon_scoreboard_match_candidates",
|
|
)
|
|
|
|
|
|
def build_storage_diagnostics() -> dict[str, object]:
|
|
"""Return one JSON-safe diagnostic payload for the migrated domains."""
|
|
if use_postgres_rcon_storage():
|
|
from .postgres_rcon_storage import count_migrated_tables
|
|
from .postgres_display_storage import table_counts
|
|
|
|
rcon_counts = count_migrated_tables()
|
|
displayed_counts = table_counts()
|
|
backend = "postgresql"
|
|
else:
|
|
rcon_counts = _count_sqlite_tables()
|
|
displayed_counts = {}
|
|
backend = "sqlite-fallback"
|
|
materialization = summarize_rcon_materialization_status()
|
|
return {
|
|
"active_storage_backend": backend,
|
|
"database_url_configured": bool(get_database_url()),
|
|
"sqlite_fallback_path": str(get_storage_path()),
|
|
"migrated_domains": [
|
|
"rcon-admin-log-events",
|
|
"rcon-player-profile-snapshots",
|
|
"rcon-historical-capture-samples-and-windows",
|
|
"rcon-materialized-matches",
|
|
"rcon-materialized-player-stats",
|
|
"rcon-safe-scoreboard-candidates",
|
|
"public-scoreboard-historical-matches-and-player-stats",
|
|
"weekly-rankings",
|
|
"monthly-rankings",
|
|
"displayed-historical-snapshots",
|
|
"server-summary-and-live-server-cache",
|
|
"player-event-ledger",
|
|
],
|
|
"table_counts": {
|
|
**rcon_counts,
|
|
**displayed_counts,
|
|
"admin_log_events": rcon_counts.get("rcon_admin_log_events", 0),
|
|
"materialized_matches": rcon_counts.get("rcon_materialized_matches", 0),
|
|
"player_stats": rcon_counts.get("rcon_match_player_stats", 0),
|
|
"public_scoreboard_historical_matches": displayed_counts.get(
|
|
"historical_matches", 0
|
|
),
|
|
"weekly_rankings_source_stats": displayed_counts.get(
|
|
"historical_player_match_stats", 0
|
|
),
|
|
"monthly_rankings_source_stats": displayed_counts.get(
|
|
"historical_player_match_stats", 0
|
|
),
|
|
"server_summary_cache": displayed_counts.get("displayed_historical_snapshots", 0),
|
|
"player_event_ledger": displayed_counts.get("player_event_raw_ledger", 0),
|
|
"scoreboard_candidates": rcon_counts.get("rcon_scoreboard_match_candidates", 0),
|
|
},
|
|
"latest_materialized_matches": materialization["latest_materialized_matches"],
|
|
"latest_admin_log_match_end_events": materialization[
|
|
"latest_admin_log_match_end_events"
|
|
],
|
|
"match_end_status": materialization["match_end_status"],
|
|
"remaining_sqlite_or_file_backed_domains": [
|
|
{
|
|
"domain": "public-scoreboard ingestion run and backfill checkpoints",
|
|
"displayed_in_frontend": False,
|
|
"reason": "operational import bookkeeping is not read by visible pages",
|
|
"planned_phase": "phase-3-or-when-scoreboard-import-runs-on-postgresql",
|
|
},
|
|
{
|
|
"domain": "Elo/MMR tables",
|
|
"displayed_in_frontend": False,
|
|
"reason": "Elo/MMR remains paused and hidden from visible pages",
|
|
"planned_phase": "phase-3",
|
|
},
|
|
],
|
|
"sqlite_remaining": [
|
|
"public-scoreboard ingestion run and backfill checkpoints",
|
|
"paused Elo/MMR tables",
|
|
],
|
|
"scoreboard_correlation": "PostgreSQL safe candidates and migrated trusted historical match URLs are used.",
|
|
"external_player_ids": _postgres_external_player_id_diagnostics()
|
|
if backend == "postgresql"
|
|
else {
|
|
"available_in_postgresql": False,
|
|
"reason": "PostgreSQL storage is not active.",
|
|
},
|
|
"migration_parity_summary": {
|
|
"available": backend == "postgresql",
|
|
"source_command": "python -m app.sqlite_to_postgres_migration",
|
|
"displayed_historical_storage": (
|
|
"postgresql" if backend == "postgresql" else "sqlite-or-file-fallback"
|
|
),
|
|
},
|
|
}
|
|
|
|
|
|
def _count_sqlite_tables() -> dict[str, int]:
|
|
resolved_path = initialize_rcon_admin_log_storage()
|
|
counts: dict[str, int] = {}
|
|
with closing(connect_sqlite_readonly(resolved_path)) as connection:
|
|
for table_name in MIGRATED_RCON_TABLES:
|
|
try:
|
|
row = connection.execute(
|
|
f"SELECT COUNT(*) AS count FROM {table_name}"
|
|
).fetchone()
|
|
except sqlite3.Error:
|
|
counts[table_name] = 0
|
|
else:
|
|
counts[table_name] = int(row["count"] or 0)
|
|
return counts
|
|
|
|
|
|
def _postgres_external_player_id_diagnostics() -> dict[str, object]:
|
|
from .postgres_rcon_storage import connect_postgres
|
|
|
|
with connect_postgres() as connection:
|
|
row = connection.execute(
|
|
"""
|
|
SELECT
|
|
(SELECT COUNT(*) FROM rcon_match_player_stats
|
|
WHERE player_id ~ '^[0-9]{17}$') AS rcon_match_steam_id64_rows,
|
|
(SELECT COUNT(*) FROM rcon_player_profile_snapshots
|
|
WHERE player_id ~ '^[0-9]{17}$') AS rcon_profile_steam_id64_rows,
|
|
(SELECT COUNT(*) FROM historical_players
|
|
WHERE steam_id ~ '^[0-9]{17}$') AS scoreboard_player_steam_id64_rows
|
|
"""
|
|
).fetchone()
|
|
return {
|
|
"available_in_postgresql": True,
|
|
"rcon_match_steam_id64_rows": int(row["rcon_match_steam_id64_rows"] or 0),
|
|
"rcon_profile_steam_id64_rows": int(row["rcon_profile_steam_id64_rows"] or 0),
|
|
"scoreboard_player_steam_id64_rows": int(
|
|
row["scoreboard_player_steam_id64_rows"] or 0
|
|
),
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
print(json.dumps(build_storage_diagnostics(), ensure_ascii=False, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|