179 lines
6.0 KiB
Python
179 lines
6.0 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,
|
|
'data_root': Config.DATA_ROOT,
|
|
'import_dir': Config.IMPORT_DIR,
|
|
'site_visibility': Config.SITE_VISIBILITY,
|
|
'brand_primary': Config.BRAND_PRIMARY,
|
|
'secret_key_configured': (
|
|
Config.SECRET_KEY != 'yrtv-dev-only-change-me'
|
|
),
|
|
'admin_token_configured': (
|
|
Config.ADMIN_TOKEN != 'yrtv-admin-dev'
|
|
),
|
|
},
|
|
}
|