2.0.0-rc2: Achievements Refactored & Admin improved.
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
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'
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user