Skip to content
Snippets Groups Projects
views.py 4.78 KiB
"""Views for the file_validator app."""
import csv
import io
from typing import ClassVar

from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
from django.urls import reverse_lazy
from django.utils import timezone
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView

from sage_validation.accounts.models import UserActivityLog
from sage_validation.file_validator.forms import CSVUploadForm
from sage_validation.file_validator.models import MeoCostCentres, XxData


def index_view(request: HttpRequest) -> HttpResponse:
    """Render the index page."""
    return render(request, "index.html")


def upload_page_view(request: HttpRequest) -> HttpResponse:
    """Render the file upload page."""
    return render(request, "upload.html")


class CSVUploadAPIView(APIView):
    """API view for uploading a CSV file."""

    permission_classes: ClassVar[list] = [IsAuthenticated]

    def post(self, request: Request) -> Response:
        """Handle CSV upload and validation."""
        form = CSVUploadForm(data=request.data, files=request.FILES)

        if not form.is_valid():
            return Response({"status": "error", "errors": form.errors}, status=status.HTTP_400_BAD_REQUEST)

        csv_file = form.cleaned_data["file"]
        csv_file.seek(0)
        decoded_file = csv_file.read().decode("utf-8").strip()

        if not decoded_file:
            return Response({"status": "error", "message": "Uploaded file is empty."},
                            status=status.HTTP_400_BAD_REQUEST)

        reader = csv.DictReader(io.StringIO(decoded_file))
        csv_data: list[dict[str, str]] = list(reader)

        updated_data = self.update_fields(csv_data)
        request.session["validated_csv"] = updated_data
        request.session["input_file_hash"] = UserActivityLog.generate_file_hash(csv_file)
        request.session.modified = True

        return Response({
            "status": "success",
            "message": "File successfully uploaded and processed.",
            "download_url": reverse_lazy("export-file")
        }, status=status.HTTP_200_OK)

    @staticmethod
    def update_fields(csv_data: list[dict[str, str]]) -> list[dict[str, str]]:
        """Automatically update specific fields before export."""
        current_date: str = timezone.now().strftime("%d/%m/%Y")

        xx_data_map: dict[str, tuple] = {
            obj.xx_value: (obj.project, obj.overhead) for obj in XxData.objects.using("meo").all()
        }
        cost_centre_map: dict[str, str] = {
            obj.cc: obj.cc_type for obj in MeoCostCentres.objects.using("meo").all()
        }

        for row in csv_data:
            row["TransactionDate"] = current_date

            repeat = 1
            while f"NominalAnalysisNominalCostCentre/{repeat}" in row:
                cc = row.get(f"NominalAnalysisNominalCostCentre/{repeat}", "")
                nominal_account_name = row.get(f"NominalAnalysisNominalAccountNumber/{repeat}", "")

                if not cc or not nominal_account_name:
                    repeat += 1
                    continue

                cc_type = cost_centre_map.get(cc, "")
                xx_data = xx_data_map.get(nominal_account_name)

                if xx_data:
                    row[f"NominalAnalysisNominalAccountNumber/{repeat}"] = (
                        xx_data[0] if cc_type == "Project" else xx_data[1]
                    )
                repeat += 1

        return csv_data


class CSVExportAPIView(APIView):
    """API view for exporting the updated CSV file."""

    def get(self, request: Request) -> Response:
        """Return processed CSV as a downloadable response."""
        csv_data: list[dict[str, str]] = request.session.get("validated_csv", [])
        input_file_hash: str = request.session.get("input_file_hash", "")

        if not csv_data:
            return Response({"status": "error", "message": "No data available for export."},
                            status=status.HTTP_400_BAD_REQUEST)
        file_name = f"Validated_{csv_data[0].get('TransactionReference', 'file')}.csv"
        response = HttpResponse(content_type="text/csv")
        response["Content-Disposition"] = f"attachment; filename={file_name}"

        writer = csv.DictWriter(response, fieldnames=csv_data[0].keys())
        writer.writeheader()
        writer.writerows(csv_data)
        # Log the user activity
        UserActivityLog.objects.create(
            user=request.user,
            action="download",
            name=file_name,
            input_file_hash=input_file_hash,
            output_file_hash=UserActivityLog.generate_file_hash(csv_data),
            timestamp=timezone.now()
        )
        return response