Files

164 lines
5.8 KiB
Python

from __future__ import annotations
from django.conf import settings
from django.http import FileResponse, Http404
from django.urls import reverse
from django.utils import timezone
from drf_spectacular.utils import extend_schema
from rest_framework import mixins, status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from apps.reports.api.serializers import (
ReportExportCreateSerializer,
ReportExportJobSerializer,
)
from apps.reports.models import ReportExportJob
from apps.reports.services import (
build_chart_report,
build_day_details_report,
build_table_report,
build_user_summary_report,
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):
permission_classes = [IsAuthenticated]
@extend_schema(responses=dict)
def get(self, request):
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):
permission_classes = [IsAuthenticated]
@extend_schema(responses=dict)
def get(self, request):
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):
permission_classes = [IsAuthenticated]
@extend_schema(responses=dict)
def get(self, request):
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 ReportUserSummaryView(APIView):
permission_classes = [IsAuthenticated]
@extend_schema(responses=dict)
def get(self, request):
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_user_summary_report(request.user, request.query_params),
resource="user-summary",
user_id=request.user.id,
workspace_id=workspace_id,
params=request.query_params,
)
return Response(payload)
class ReportExportJobViewSet(
mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [IsAuthenticated]
serializer_class = ReportExportJobSerializer
def get_queryset(self):
return ReportExportJob.objects.filter(requesting_user=self.request.user, is_deleted=False)
@extend_schema(request=ReportExportCreateSerializer, responses=ReportExportJobSerializer)
def create(self, request, *args, **kwargs):
serializer = ReportExportCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
filters = load_report_filters(request.user, serializer.validated_data)
job = ReportExportJob.objects.create(
requesting_user=request.user,
workspace=filters.workspace,
export_type=serializer.validated_data["export_type"],
filters={
"workspace": str(filters.workspace.id),
"period": filters.period,
"from_date": filters.from_date.isoformat(),
"to_date": filters.to_date.isoformat(),
"user": filters.user_id,
"client": filters.client_id,
"project": filters.project_id,
"tags": filters.tag_ids,
"language": serializer.validated_data.get("language", "en"),
},
status=ReportExportJob.Status.PENDING,
)
generate_report_export_task.delay(str(job.id))
output = ReportExportJobSerializer(job)
return Response(output.data, status=status.HTTP_202_ACCEPTED)
@action(detail=True, methods=["get"], url_path="download")
def download(self, request, pk=None):
job = self.get_object()
if job.status != ReportExportJob.Status.COMPLETED or not job.file:
raise Http404("Export file is not available.")
if job.expires_at and job.expires_at <= timezone.now():
raise Http404("Export file has expired.")
response = FileResponse(
job.file.open("rb"),
as_attachment=True,
filename=job.file_name or job.file.name.split("/")[-1],
)
return response
def build_export_action_url(job: ReportExportJob) -> str:
path = reverse("report-export-job-download", kwargs={"pk": job.id})
if settings.BASE_URL:
return f"{settings.BASE_URL.rstrip('/')}{path}"
return path