feat(dashboard): revamp with at-a-glance strip, sparkline, modal, disk bar
Replace the four stat cards with a compact three-card strip: 7-day activity with inline SVG sparkline (new ActivitySparkline), Running Now / Next Check with per-source countdown list, and System with credential health + disk usage bar. - Add GET /downloads/activity-timeline — per-day completed/failed/files counts, pre-filled with zero buckets so the sparkline always has N points. - Report filesystem-level usage via shutil.disk_usage in storage rollup, plus a live fallback in GET /settings so the capacity bar works before the first Celery rollup runs and for cached rows that predate the field. - Extract Download Details into a reusable DownloadDetailsModal component and wire Recent Activity rows to open it (previously Downloads-page only). - Compute next scheduled check per source from global schedule_interval; surface credential expiration/missing alerts scoped to platforms that actually have enabled sources. - Two-speed polling (5s when downloads are active, 30s idle) with Page Visibility awareness so background tabs don't churn the API. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
@@ -38,6 +39,29 @@ def format_size(size_bytes: int) -> str:
|
||||
return f"{size_bytes:.1f} PB"
|
||||
|
||||
|
||||
def _disk_usage_stats(path: Path) -> dict:
|
||||
"""Return filesystem-level usage for the volume hosting `path`.
|
||||
|
||||
Uses shutil.disk_usage so we report the actual mount's free space (the
|
||||
number that matters for capacity planning), not just what our downloads
|
||||
subtree consumes.
|
||||
"""
|
||||
try:
|
||||
usage = shutil.disk_usage(path)
|
||||
return {
|
||||
"disk_total": usage.total,
|
||||
"disk_used": usage.used,
|
||||
"disk_free": usage.free,
|
||||
"disk_total_formatted": format_size(usage.total),
|
||||
"disk_used_formatted": format_size(usage.used),
|
||||
"disk_free_formatted": format_size(usage.free),
|
||||
"disk_percent_used": round(usage.used / usage.total * 100, 1) if usage.total else 0,
|
||||
}
|
||||
except OSError as e:
|
||||
logger.warning(f"Could not read disk usage for {path}: {e}")
|
||||
return {}
|
||||
|
||||
|
||||
def calculate_storage_stats() -> dict:
|
||||
"""Calculate storage statistics for the downloads directory.
|
||||
|
||||
@@ -80,6 +104,7 @@ def calculate_storage_stats() -> dict:
|
||||
"total_size_formatted": format_size(total_size),
|
||||
"file_count": file_count,
|
||||
"calculated_at": datetime.utcnow().isoformat(),
|
||||
**_disk_usage_stats(downloads_path),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user