-
Neda Moeini authoredNeda Moeini authored
views.py 4.58 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.modified = True
# Log the user activity
UserActivityLog.objects.create(
user=request.user,
action="upload",
name=csv_file.name,
input_file_hash=UserActivityLog.generate_file_hash(csv_file),
output_file_hash=UserActivityLog.generate_file_hash(updated_data),
timestamp=timezone.now()
)
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", [])
if not csv_data:
return Response({"status": "error", "message": "No data available for export."},
status=status.HTTP_400_BAD_REQUEST)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="updated_file.csv"'
writer = csv.DictWriter(response, fieldnames=csv_data[0].keys())
writer.writeheader()
writer.writerows(csv_data)
return response