feat(reports): add localized workspace reports and exports
This commit is contained in:
110
apps/reports/api/views.py
Normal file
110
apps/reports/api/views.py
Normal file
@@ -0,0 +1,110 @@
|
||||
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,
|
||||
load_report_filters,
|
||||
)
|
||||
from apps.reports.tasks import generate_report_export_task
|
||||
|
||||
|
||||
class ReportChartView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(responses=dict)
|
||||
def get(self, request):
|
||||
return Response(build_chart_report(request.user, request.query_params))
|
||||
|
||||
|
||||
class ReportTableView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(responses=dict)
|
||||
def get(self, request):
|
||||
return Response(build_table_report(request.user, request.query_params))
|
||||
|
||||
|
||||
class ReportDayDetailsView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(responses=dict)
|
||||
def get(self, request):
|
||||
return Response(build_day_details_report(request.user, request.query_params))
|
||||
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user