From 5bb9241e9a4a7e5cb403ae27c38283ce94cbce8c Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 24 Apr 2026 09:11:42 -0700 Subject: [PATCH] Enhancement: show small task summary in system status (#12634) --- .../system-status-dialog.component.html | 25 +++++++++++++ .../system-status-dialog.component.spec.ts | 7 ++++ src-ui/src/app/data/system-status.ts | 7 ++++ src/documents/tests/test_api_status.py | 36 +++++++++++++++++++ src/documents/views.py | 35 ++++++++++++++++++ 5 files changed, 110 insertions(+) diff --git a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html index d57485214..d9194fd2c 100644 --- a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html +++ b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html @@ -142,6 +142,31 @@ } +
Recent Task Activity ({{status.tasks.summary.days}} days)
+
+ @if (status.tasks.summary.total_count > 0) { + + } @else { + No recent tasks + } +
diff --git a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.spec.ts b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.spec.ts index 29bad431e..4c69feed4 100644 --- a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.spec.ts @@ -71,6 +71,13 @@ const status: SystemStatus = { llmindex_status: SystemStatusItemStatus.OK, llmindex_last_modified: new Date().toISOString(), llmindex_error: null, + summary: { + days: 30, + total_count: 12, + pending_count: 1, + success_count: 10, + failure_count: 1, + }, }, } diff --git a/src-ui/src/app/data/system-status.ts b/src-ui/src/app/data/system-status.ts index 7dcbffa20..41cc738cc 100644 --- a/src-ui/src/app/data/system-status.ts +++ b/src-ui/src/app/data/system-status.ts @@ -47,6 +47,13 @@ export interface SystemStatus { llmindex_status: SystemStatusItemStatus llmindex_last_modified: string // ISO date string llmindex_error: string + summary: { + days: number + total_count: number + pending_count: number + success_count: number + failure_count: number + } } websocket_connected?: SystemStatusItemStatus // added client-side } diff --git a/src/documents/tests/test_api_status.py b/src/documents/tests/test_api_status.py index bb0039e67..72f1e269b 100644 --- a/src/documents/tests/test_api_status.py +++ b/src/documents/tests/test_api_status.py @@ -1,12 +1,14 @@ import os import shutil import tempfile +from datetime import timedelta from pathlib import Path from unittest import mock from django.contrib.auth.models import Permission from django.contrib.auth.models import User from django.test import override_settings +from django.utils import timezone from rest_framework import status from rest_framework.test import APITestCase @@ -76,6 +78,11 @@ class TestSystemStatus(APITestCase): self.assertEqual(response.data["tasks"]["redis_url"], "redis://localhost:6379") self.assertEqual(response.data["tasks"]["redis_status"], "ERROR") self.assertIsNotNone(response.data["tasks"]["redis_error"]) + self.assertEqual(response.data["tasks"]["summary"]["days"], 30) + self.assertEqual(response.data["tasks"]["summary"]["total_count"], 0) + self.assertEqual(response.data["tasks"]["summary"]["success_count"], 0) + self.assertEqual(response.data["tasks"]["summary"]["failure_count"], 0) + self.assertEqual(response.data["tasks"]["summary"]["pending_count"], 0) def test_system_status_insufficient_permissions(self) -> None: """ @@ -436,3 +443,32 @@ class TestSystemStatus(APITestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["tasks"]["llmindex_status"], "ERROR") self.assertIsNotNone(response.data["tasks"]["llmindex_error"]) + + def test_system_status_includes_recent_task_summary(self) -> None: + PaperlessTaskFactory( + task_type=PaperlessTask.TaskType.CONSUME_FILE, + status=PaperlessTask.Status.SUCCESS, + ) + PaperlessTaskFactory( + task_type=PaperlessTask.TaskType.CONSUME_FILE, + status=PaperlessTask.Status.FAILURE, + ) + PaperlessTaskFactory( + task_type=PaperlessTask.TaskType.SANITY_CHECK, + status=PaperlessTask.Status.PENDING, + ) + PaperlessTaskFactory( + task_type=PaperlessTask.TaskType.MAIL_FETCH, + status=PaperlessTask.Status.SUCCESS, + date_created=timezone.now() - timedelta(days=45), + ) + + self.client.force_login(self.user) + response = self.client.get(self.ENDPOINT) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["tasks"]["summary"]["days"], 30) + self.assertEqual(response.data["tasks"]["summary"]["total_count"], 3) + self.assertEqual(response.data["tasks"]["summary"]["success_count"], 1) + self.assertEqual(response.data["tasks"]["summary"]["failure_count"], 1) + self.assertEqual(response.data["tasks"]["summary"]["pending_count"], 1) diff --git a/src/documents/views.py b/src/documents/views.py index 217550634..74c7733cf 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -4597,6 +4597,16 @@ class CustomFieldViewSet(PermissionsAwareDocumentCountMixin, ModelViewSet[Custom "redis_status": serializers.CharField(), "redis_error": serializers.CharField(), "celery_status": serializers.CharField(), + "summary": inline_serializer( + name="TasksSummaryOverview", + fields={ + "days": serializers.IntegerField(), + "total_count": serializers.IntegerField(), + "pending_count": serializers.IntegerField(), + "success_count": serializers.IntegerField(), + "failure_count": serializers.IntegerField(), + }, + ), }, ), "index": inline_serializer( @@ -4630,6 +4640,7 @@ class CustomFieldViewSet(PermissionsAwareDocumentCountMixin, ModelViewSet[Custom ) class SystemStatusView(PassUserMixin): permission_classes = (IsAuthenticated,) + TASK_SUMMARY_DAYS = 30 def get(self, request, format=None): if not has_system_status_permission(request.user): @@ -4796,6 +4807,29 @@ class SystemStatusView(PassUserMixin): last_llmindex_update.date_done if last_llmindex_update else None ) + summary_cutoff = timezone.now() - timedelta(days=self.TASK_SUMMARY_DAYS) + task_summary_agg = PaperlessTask.objects.filter( + date_created__gte=summary_cutoff, + ).aggregate( + total_count=Count("id"), + pending_count=Count( + "id", + filter=Q(status=PaperlessTask.Status.PENDING), + ), + success_count=Count( + "id", + filter=Q(status=PaperlessTask.Status.SUCCESS), + ), + failure_count=Count( + "id", + filter=Q(status=PaperlessTask.Status.FAILURE), + ), + ) + task_summary = { + "days": self.TASK_SUMMARY_DAYS, + **task_summary_agg, + } + return Response( { "pngx_version": current_version, @@ -4836,6 +4870,7 @@ class SystemStatusView(PassUserMixin): "llmindex_status": llmindex_status, "llmindex_last_modified": llmindex_last_modified, "llmindex_error": llmindex_error, + "summary": task_summary, }, }, )