diff --git a/src/apps/parsers/views.py b/src/apps/parsers/views.py index 0e6ca7b..6d4e556 100644 --- a/src/apps/parsers/views.py +++ b/src/apps/parsers/views.py @@ -2064,6 +2064,10 @@ class ParserDashboardDataView(APIView): def get(self, request: Request): sources = ParserSourceSerializer(PARSER_SOURCES.values(), many=True).data + api_sources = [ + source for source in sources if not source["supports_file_upload"] + ] + file_sources = [source for source in sources if source["supports_file_upload"]] jobs = BackgroundJobService.get_user_jobs(user_id=request.user.id, limit=30) source_counts = dict( GenericParserRecord.objects.values("source") @@ -2083,12 +2087,12 @@ class ParserDashboardDataView(APIView): return api_response( { "sources": sources, - "api_sources": [ - source for source in sources if not source["supports_file_upload"] - ], - "file_sources": [ - source for source in sources if source["supports_file_upload"] - ], + "api_sources": api_sources, + "file_sources": file_sources, + "groups": { + "api": api_sources, + "uploads": file_sources, + }, "schedules": ParserScheduleSerializer(schedules, many=True).data, "jobs": BackgroundJobListSerializer(jobs, many=True).data, "source_counts": source_counts, diff --git a/src/templates/dashboard.html b/src/templates/dashboard.html index f7eb54b..b79ef92 100644 --- a/src/templates/dashboard.html +++ b/src/templates/dashboard.html @@ -1378,6 +1378,14 @@ return map; } + function sourceGroups() { + const sources = dashboardData?.sources || []; + return dashboardData?.groups || { + api: dashboardData?.api_sources || sources.filter((source) => !source.supports_file_upload), + uploads: dashboardData?.file_sources || sources.filter((source) => source.supports_file_upload), + }; + } + function latestLoadForSource(source) { return latestLoadsBySource().get(source.source) || null; } @@ -1398,7 +1406,7 @@ const other = Math.max(latestLoads.length - success - failed, 0); const activeSchedules = (dashboardData?.schedules || []).filter((schedule) => schedule.enabled).length; const runningJobs = (dashboardData?.jobs || []).filter((job) => ["pending", "started", "retry"].includes(job.status)).length; - const uploadCount = (dashboardData?.groups?.uploads || []).length; + const uploadCount = (sourceGroups().uploads || []).length; $("analyticsKpis").innerHTML = [ ["Источников", sources.length], @@ -1708,14 +1716,15 @@ function renderDashboard(data) { dashboardData = data.data; const sources = dashboardData.sources || []; + const groups = sourceGroups(); const options = sources.map((source) => ``).join(""); $("runSource").innerHTML = options; $("scheduleSource").innerHTML = options; $("sourceCount").textContent = `${sources.length} источников`; - $("uploadCount").textContent = `${(dashboardData.groups.uploads || []).length} источника`; + $("uploadCount").textContent = `${(groups.uploads || []).length} источника`; renderAnalytics(); renderSourceCards(sources); - renderUploadSources(dashboardData.groups.uploads || [], "uploadSources"); + renderUploadSources(groups.uploads || [], "uploadSources"); $("jobsBody").innerHTML = (dashboardData.jobs || []).map((job) => { const canRevoke = ["pending", "started", "retry"].includes(job.status); diff --git a/tests/apps/parsers/test_dashboard_page.py b/tests/apps/parsers/test_dashboard_page.py index 4fa87d0..c3c14fc 100644 --- a/tests/apps/parsers/test_dashboard_page.py +++ b/tests/apps/parsers/test_dashboard_page.py @@ -21,3 +21,11 @@ class ParserDashboardPageTest(TestCase): content = response.content.decode() self.assertIn("refreshRegisters().catch(renderRegistersUnavailable)", content) self.assertIn("isAuthError(error)", content) + + def test_dashboard_has_group_fallback_for_current_api_shape(self): + response = self.client.get("/dashboard") + + self.assertEqual(response.status_code, 200) + content = response.content.decode() + self.assertIn("function sourceGroups()", content) + self.assertIn("dashboardData?.file_sources", content) diff --git a/tests/apps/parsers/test_views.py b/tests/apps/parsers/test_views.py index 2519c6e..bf9d671 100644 --- a/tests/apps/parsers/test_views.py +++ b/tests/apps/parsers/test_views.py @@ -120,6 +120,19 @@ class ParsersViewSetTest(APITestCase): ) self.assertEqual(detail.status_code, status.HTTP_200_OK) + def test_dashboard_data_exposes_source_groups_for_page(self): + self.client.force_authenticate(self.user) + + response = self.client.get("/api/v1/parsers/dashboard/") + + self.assertEqual(response.status_code, status.HTTP_200_OK) + payload = response.data["data"] + self.assertIn("groups", payload) + self.assertIn("api", payload["groups"]) + self.assertIn("uploads", payload["groups"]) + self.assertEqual(payload["api_sources"], payload["groups"]["api"]) + self.assertEqual(payload["file_sources"], payload["groups"]["uploads"]) + def test_financial_reports_list_and_retrieve(self): report = FinancialReport.objects.create( external_id=_digits(5),