Files
JKTV-online/web/services/admin_service.py
T

176 lines
5.7 KiB
Python

from datetime import datetime
import json
from pathlib import Path
import sqlite3
from database.job_store import JobStore
from database.maintenance import (
backup_storage_status,
check_managed_databases,
)
from database.paths import BACKUP_ROOT
from web.config import Config
from web.database import query_db
from web.services.integrity_service import IntegrityService
from web.services.roster_version_service import RosterVersionService
from web.services.team_context_service import TeamContextService
class AdminService:
DATABASE_LABELS = {
'l2': 'L2 Facts',
'l3': 'L3 Marts',
'web': 'Web App',
}
@staticmethod
def _format_bytes(value):
value = float(value or 0)
for unit in ('B', 'KB', 'MB', 'GB'):
if value < 1024 or unit == 'GB':
return f'{value:.1f} {unit}'
value /= 1024
@staticmethod
def get_overview():
integrity = IntegrityService.build_report()
store = JobStore(Config.DB_WEB_PATH)
latest_report = query_db(
'l3',
"""
SELECT match_id, match_date, map_name, is_win,
team_avg_rating, summary_text
FROM dm_match_reports
ORDER BY match_date DESC
LIMIT 1
""",
one=True,
)
latest_match = dict(latest_report) if latest_report else None
if latest_match:
latest_match['date_text'] = datetime.fromtimestamp(
latest_match['match_date']
).strftime('%Y-%m-%d %H:%M')
counts = integrity['counts']
return {
'integrity': integrity,
'jobs': store.list_jobs(8),
'job_summary': store.get_summary(),
'latest_match': latest_match,
'roster_size': len(TeamContextService.get_active_roster_ids()),
'roster_versions': len(RosterVersionService.list_versions()),
'metrics': {
'matches': counts.get('matches', 0),
'roster_players': counts.get('active_roster', 0),
'reports': counts.get('l3_match_reports', 0),
'awards': counts.get('l3_awards', 0),
'backup_size': AdminService._format_bytes(
counts.get('backup_bytes', 0)
),
},
}
@staticmethod
def get_jobs(status=None, job_type=None, limit=50):
store = JobStore(Config.DB_WEB_PATH)
return {
'jobs': store.list_jobs(
limit=limit,
status=status,
job_type=job_type,
),
'summary': store.get_summary(),
'status': status,
'job_type': job_type,
}
@staticmethod
def get_database_catalog(db_name):
if db_name not in {'l2', 'l3', 'web'}:
raise ValueError('Unknown database')
rows = query_db(
db_name,
"""
SELECT name, type
FROM sqlite_master
WHERE type IN ('table', 'view')
AND name NOT LIKE 'sqlite_%'
ORDER BY type, name
""",
)
return [dict(row) for row in rows]
@staticmethod
def get_system_status():
managed = check_managed_databases()
web_status = {
'path': Config.DB_WEB_PATH,
'exists': Path(Config.DB_WEB_PATH).exists(),
'size_bytes': (
Path(Config.DB_WEB_PATH).stat().st_size
if Path(Config.DB_WEB_PATH).exists() else 0
),
}
databases = []
for name, item in managed.items():
entry = dict(item)
entry['name'] = name.upper()
entry['size_text'] = AdminService._format_bytes(
entry['size_bytes']
)
databases.append(entry)
web_status.update({
'name': 'WEB',
'quick_check': 'managed by app',
'size_text': AdminService._format_bytes(web_status['size_bytes']),
})
databases.append(web_status)
backup_dirs = sorted(
[path for path in BACKUP_ROOT.glob('*') if path.is_dir()],
key=lambda path: path.stat().st_mtime,
reverse=True,
)
backups = []
for directory in backup_dirs:
manifest_path = directory / 'manifest.json'
manifest = {}
if manifest_path.exists():
try:
manifest = json.loads(
manifest_path.read_text(encoding='utf-8')
)
except (OSError, json.JSONDecodeError):
manifest = {}
size = sum(
file.stat().st_size
for file in directory.rglob('*')
if file.is_file()
)
backups.append({
'name': directory.name,
'created_at': manifest.get('created_at'),
'size_text': AdminService._format_bytes(size),
'databases': manifest.get('databases', {}),
})
return {
'databases': databases,
'backups': backups,
'backup_summary': backup_storage_status(),
'config': {
'web_schema_version': Config.WEB_SCHEMA_VERSION,
'sqlite_timeout': Config.SQLITE_TIMEOUT_SECONDS,
'slow_query_threshold': Config.SLOW_QUERY_THRESHOLD_SECONDS,
'max_upload_mb': Config.MAX_CONTENT_LENGTH / 1024 / 1024,
'secret_key_configured': (
Config.SECRET_KEY != 'yrtv-dev-only-change-me'
),
'admin_token_configured': (
Config.ADMIN_TOKEN != 'yrtv-admin-dev'
),
},
}