diff --git a/src/pobsync_backend/stats_summary.py b/src/pobsync_backend/stats_summary.py index b6802f7..ab13149 100644 --- a/src/pobsync_backend/stats_summary.py +++ b/src/pobsync_backend/stats_summary.py @@ -54,23 +54,23 @@ def collect_dashboard_stats(*, hosts: Iterable[HostConfig], global_config: Globa def collect_host_stats(*, host: HostConfig, limit: int = 8) -> dict[str, Any]: runs = list(host.runs.select_related("snapshot").filter(status=BackupRun.Status.SUCCESS).order_by("-started_at", "-created_at")[:50]) real_runs = [_run_summary(run) for run in runs if _is_real_run(run)] - real_runs = [run for run in real_runs if run["has_stats"]][:limit] + trend_runs = [run for run in real_runs if run["has_stats"]][:limit] latest_snapshot = host.snapshots.order_by("-started_at", "-discovered_at", "-id").first() latest_snapshot_stats = _snapshot_summary(latest_snapshot) if latest_snapshot else {} - literal_values = [_int_at(run, "rsync", "literal_data_bytes") for run in real_runs] + literal_values = [_int_at(run, "rsync", "literal_data_bytes") for run in trend_runs] literal_values = [value for value in literal_values if value is not None] - matched_values = [_int_at(run, "rsync", "matched_data_bytes") for run in real_runs] + matched_values = [_int_at(run, "rsync", "matched_data_bytes") for run in trend_runs] matched_values = [value for value in matched_values if value is not None] max_literal = max(literal_values) if literal_values else 0 max_matched = max(matched_values) if matched_values else 0 return { - "runs": [_with_bar_percentages(run, max_literal=max_literal, max_matched=max_matched) for run in real_runs], + "runs": [_with_bar_percentages(run, max_literal=max_literal, max_matched=max_matched) for run in trend_runs], "latest_run": real_runs[0] if real_runs else {}, "latest_snapshot": latest_snapshot_stats, "avg_literal_data_bytes": _average(literal_values), - "avg_daily_literal_data_bytes": _average_daily_literal(real_runs), + "avg_daily_literal_data_bytes": _average_daily_literal(trend_runs), "total_literal_data_bytes": sum(literal_values), "total_matched_data_bytes": sum(matched_values), } diff --git a/src/pobsync_backend/templates/pobsync_backend/dashboard.html b/src/pobsync_backend/templates/pobsync_backend/dashboard.html index e12fcfe..5daff1c 100644 --- a/src/pobsync_backend/templates/pobsync_backend/dashboard.html +++ b/src/pobsync_backend/templates/pobsync_backend/dashboard.html @@ -90,7 +90,14 @@ none {% endif %} - {% if host.next_run_at %}{{ host.next_run_at }}{% else %}none{% endif %} + + {% if host.next_run_at %} + {{ host.next_run_at|date:"Y-m-d H:i T" }} +
{{ scheduler_timezone }}
+ {% else %} + none + {% endif %} + {{ host.stats_summary.latest_run.rsync.literal_data_bytes|filesizeformat }} {{ host.run_count }} d{{ host.retention_daily }} w{{ host.retention_weekly }} m{{ host.retention_monthly }} y{{ host.retention_yearly }} diff --git a/src/pobsync_backend/templates/pobsync_backend/host_detail.html b/src/pobsync_backend/templates/pobsync_backend/host_detail.html index b176998..6fc1932 100644 --- a/src/pobsync_backend/templates/pobsync_backend/host_detail.html +++ b/src/pobsync_backend/templates/pobsync_backend/host_detail.html @@ -52,7 +52,7 @@
Schedule expression: {{ schedule.cron_expr }}
Evaluated by the pobsync scheduler service.
Enabled: {{ schedule.enabled|yesno:"yes,no" }}
-
Next run: {{ next_run_at|default:"" }}
+
Next run: {% if next_run_at %}{{ next_run_at|date:"Y-m-d H:i T" }} {{ scheduler_timezone }}{% endif %}
Prune: {{ schedule.prune|yesno:"yes,no" }}
Last status: {{ schedule.last_status|default:"" }}
Last started: {{ schedule.last_started_at|default:"" }}
diff --git a/src/pobsync_backend/tests/test_views.py b/src/pobsync_backend/tests/test_views.py index e5bc391..76569ee 100644 --- a/src/pobsync_backend/tests/test_views.py +++ b/src/pobsync_backend/tests/test_views.py @@ -50,6 +50,8 @@ class ViewTests(TestCase): self.assertContains(response, "web-01") self.assertContains(response, "20260519-021500Z__ABCDEFGH") self.assertContains(response, "success") + self.assertContains(response, f"Run {run.id}") + self.assertContains(response, "manual") def test_dashboard_renders_backup_trend_summary(self) -> None: self.client.force_login(self.staff_user) @@ -91,6 +93,7 @@ class ViewTests(TestCase): self.assertContains(response, "Avg Daily New") self.assertContains(response, "Days Until Full") self.assertContains(response, "Next Run") + self.assertContains(response, "UTC") self.assertContains(response, "10") self.assertContains(response, f"Run {run.id}") self.assertContains(response, "manual") @@ -554,6 +557,7 @@ class ViewTests(TestCase): self.assertContains(response, "Schedule expression") self.assertContains(response, "Evaluated by the pobsync scheduler service.") self.assertContains(response, "Next run:") + self.assertContains(response, "UTC") self.assertContains(response, "20260519-021500Z__ABCDEFGH") self.assertContains(response, "Discover snapshots") self.assertContains(response, "Edit schedule") diff --git a/src/pobsync_backend/views.py b/src/pobsync_backend/views.py index aae4720..8bbce78 100644 --- a/src/pobsync_backend/views.py +++ b/src/pobsync_backend/views.py @@ -58,6 +58,7 @@ def dashboard(request): "hosts": hosts, "global_config": global_config, "stats_summary": stats_summary, + "scheduler_timezone": timezone.get_current_timezone_name(), "latest_runs": BackupRun.objects.select_related("host", "snapshot").order_by("-created_at")[:10], "counts": { "global_configs": GlobalConfig.objects.count(), @@ -271,6 +272,7 @@ def host_detail(request, host: str): "host": host_config, "schedule": schedule, "next_run_at": _next_run_for_schedule(schedule, host_config), + "scheduler_timezone": timezone.get_current_timezone_name(), "discovery": inspect_snapshot_discovery(host=host_config), "host_checks": host_checks, "host_check_summary": summarize_self_checks(host_checks),