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