feat(cache): add targeted server-side response caching

This commit is contained in:
2026-04-30 16:13:12 +03:30
parent 08e1793765
commit 054bb5a582
8 changed files with 432 additions and 16 deletions

View File

@@ -23,6 +23,10 @@ from apps.reports.services import (
load_report_filters,
)
from apps.reports.tasks import generate_report_export_task
from core.services.cache import CACHE_NAMESPACE_REPORTS, get_or_set_cache_payload
REPORT_CACHE_TTL_SECONDS = 90
class ReportChartView(APIView):
@@ -30,7 +34,17 @@ class ReportChartView(APIView):
@extend_schema(responses=dict)
def get(self, request):
return Response(build_chart_report(request.user, request.query_params))
workspace_id = request.query_params.get("workspace")
payload = get_or_set_cache_payload(
CACHE_NAMESPACE_REPORTS,
ttl_seconds=REPORT_CACHE_TTL_SECONDS,
builder=lambda: build_chart_report(request.user, request.query_params),
resource="chart",
user_id=request.user.id,
workspace_id=workspace_id,
params=request.query_params,
)
return Response(payload)
class ReportTableView(APIView):
@@ -38,7 +52,17 @@ class ReportTableView(APIView):
@extend_schema(responses=dict)
def get(self, request):
return Response(build_table_report(request.user, request.query_params))
workspace_id = request.query_params.get("workspace")
payload = get_or_set_cache_payload(
CACHE_NAMESPACE_REPORTS,
ttl_seconds=REPORT_CACHE_TTL_SECONDS,
builder=lambda: build_table_report(request.user, request.query_params),
resource="table",
user_id=request.user.id,
workspace_id=workspace_id,
params=request.query_params,
)
return Response(payload)
class ReportDayDetailsView(APIView):
@@ -46,7 +70,17 @@ class ReportDayDetailsView(APIView):
@extend_schema(responses=dict)
def get(self, request):
return Response(build_day_details_report(request.user, request.query_params))
workspace_id = request.query_params.get("workspace")
payload = get_or_set_cache_payload(
CACHE_NAMESPACE_REPORTS,
ttl_seconds=REPORT_CACHE_TTL_SECONDS,
builder=lambda: build_day_details_report(request.user, request.query_params),
resource="day-details",
user_id=request.user.id,
workspace_id=workspace_id,
params=request.query_params,
)
return Response(payload)
class ReportExportJobViewSet(

View File

@@ -2,6 +2,7 @@ from datetime import date, timedelta
from decimal import Decimal
from unittest.mock import patch
from django.core.cache import cache
from rest_framework.test import APITestCase
from apps.clients.models import Client
@@ -82,6 +83,9 @@ class ReportViewTests(APITestCase):
)
entry_member.tags.add(cls.tag)
def setUp(self):
cache.clear()
def test_member_only_sees_own_chart_report(self):
self.client.force_authenticate(user=self.member)
@@ -208,3 +212,27 @@ class ReportViewTests(APITestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["summary"]["total_duration"], "02:00:00")
self.assertEqual(response.data["scope"]["from_date"], "2026-04-21")
def test_table_report_cache_stays_until_time_entry_invalidation(self):
self.client.force_authenticate(user=self.owner)
url = "/api/reports/table/"
params = {"workspace": str(self.workspace.id), "period": "this_month"}
first_response = self.client.get(url, params)
self.assertEqual(first_response.status_code, 200)
self.assertEqual(first_response.data["summary"]["total_duration"], "03:00:00")
member_entry = TimeEntry.objects.get(description="Member work")
TimeEntry.objects.filter(id=member_entry.id).update(duration=timedelta(hours=5))
cached_response = self.client.get(url, params)
self.assertEqual(cached_response.status_code, 200)
self.assertEqual(cached_response.data["summary"]["total_duration"], "03:00:00")
member_entry.refresh_from_db()
member_entry.description = "Member work updated"
member_entry.save(update_fields=["description"])
fresh_response = self.client.get(url, params)
self.assertEqual(fresh_response.status_code, 200)
self.assertEqual(fresh_response.data["summary"]["total_duration"], "07:00:00")