(feature) Show next scheduled run and backup run type in the UI
Add a scheduler helper that calculates the next due time for a cron-style schedule expression and surface that value on the dashboard and host detail pages. Show the latest run type in host summaries and backup trend tables so manual and scheduled backups are distinguishable in the Django UI. Keep the calculation derived from existing ScheduleConfig data without adding a migration.
This commit is contained in:
@@ -32,6 +32,7 @@ from .host_ops import collect_host_checks, ensure_host_directories
|
||||
from .models import BackupRun, GlobalConfig, HostConfig, ScheduleConfig, SnapshotRecord, SshCredential
|
||||
from .retention import run_sql_retention_apply, run_sql_retention_plan
|
||||
from .self_check import collect_self_checks, summarize_self_checks
|
||||
from .scheduler import next_due_after
|
||||
from .snapshot_discovery import discover_snapshots, inspect_snapshot_discovery
|
||||
from .ssh_keys import SshKeyError, delete_generated_key_files, generate_ssh_key, merge_known_hosts, scan_known_host
|
||||
from .stats_summary import collect_dashboard_stats, collect_host_stats
|
||||
@@ -41,7 +42,8 @@ from .stats_summary import collect_dashboard_stats, collect_host_stats
|
||||
def dashboard(request):
|
||||
global_config = GlobalConfig.objects.filter(name="default").first()
|
||||
hosts = list(
|
||||
HostConfig.objects.annotate(snapshot_count=Count("snapshots", distinct=True), run_count=Count("runs", distinct=True))
|
||||
HostConfig.objects.select_related("schedule")
|
||||
.annotate(snapshot_count=Count("snapshots", distinct=True), run_count=Count("runs", distinct=True))
|
||||
.order_by("host")
|
||||
)
|
||||
for host_config in hosts:
|
||||
@@ -50,6 +52,7 @@ def dashboard(request):
|
||||
.order_by("-started_at", "-discovered_at", "-id")
|
||||
.first()
|
||||
)
|
||||
host_config.next_run_at = _next_run_for_host(host_config)
|
||||
stats_summary = collect_dashboard_stats(hosts=hosts, global_config=global_config)
|
||||
context = {
|
||||
"hosts": hosts,
|
||||
@@ -255,6 +258,7 @@ def create_host_config(request):
|
||||
@staff_member_required
|
||||
def host_detail(request, host: str):
|
||||
host_config = get_object_or_404(HostConfig, host=host)
|
||||
schedule = _schedule_for_host(host_config)
|
||||
queued_runs = host_config.runs.filter(status=BackupRun.Status.QUEUED)
|
||||
running_runs = host_config.runs.filter(status=BackupRun.Status.RUNNING)
|
||||
active_run = host_config.runs.filter(
|
||||
@@ -265,7 +269,8 @@ def host_detail(request, host: str):
|
||||
stats_summary = collect_host_stats(host=host_config, limit=10)
|
||||
context = {
|
||||
"host": host_config,
|
||||
"schedule": _schedule_for_host(host_config),
|
||||
"schedule": schedule,
|
||||
"next_run_at": _next_run_for_schedule(schedule, host_config),
|
||||
"discovery": inspect_snapshot_discovery(host=host_config),
|
||||
"host_checks": host_checks,
|
||||
"host_check_summary": summarize_self_checks(host_checks),
|
||||
@@ -548,6 +553,19 @@ def _schedule_for_host(host_config: HostConfig) -> ScheduleConfig | None:
|
||||
return None
|
||||
|
||||
|
||||
def _next_run_for_host(host_config: HostConfig):
|
||||
return _next_run_for_schedule(_schedule_for_host(host_config), host_config)
|
||||
|
||||
|
||||
def _next_run_for_schedule(schedule: ScheduleConfig | None, host_config: HostConfig):
|
||||
if schedule is None or not schedule.enabled or not host_config.enabled:
|
||||
return None
|
||||
try:
|
||||
return next_due_after(schedule.cron_expr, timezone.localtime(timezone.now()))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def _default_schedule_initial() -> dict[str, object]:
|
||||
return {
|
||||
"cron_expr": "15 2 * * *",
|
||||
|
||||
Reference in New Issue
Block a user