Skip to content
Snippets Groups Projects
Commit 917f8856 authored by Neda Moeini's avatar Neda Moeini
Browse files

Replace FormView with APIView

parent e9504346
No related branches found
No related tags found
1 merge request!2Feature/unit test for validations
...@@ -40,21 +40,18 @@ ...@@ -40,21 +40,18 @@
</div> </div>
</div> </div>
<script> <script>
const form = document.getElementById('uploadForm'); document.getElementById('uploadForm').addEventListener('submit', async function (e) {
const fileInput = document.getElementById('fileInput');
const errorSection = document.getElementById('errorSection');
const errorList = document.getElementById('errorList');
const successSection = document.getElementById('successSection');
const successMessage = document.getElementById('successMessage');
const downloadSection = document.getElementById('downloadSection');
const downloadLink = document.getElementById('downloadLink');
form.addEventListener('submit', async function (e) {
e.preventDefault(); e.preventDefault();
// Clear previous messages const fileInput = document.getElementById('fileInput');
const errorSection = document.getElementById('errorSection');
const errorList = document.getElementById('errorList');
const successSection = document.getElementById('successSection');
const successMessage = document.getElementById('successMessage');
const downloadSection = document.getElementById('downloadSection');
const downloadLink = document.getElementById('downloadLink');
errorList.innerHTML = ''; errorList.innerHTML = '';
successMessage.innerHTML = ''; successMessage.innerHTML = '';
errorSection.classList.add('hidden'); errorSection.classList.add('hidden');
...@@ -65,7 +62,7 @@ ...@@ -65,7 +62,7 @@
formData.append('file', fileInput.files[0]); formData.append('file', fileInput.files[0]);
try { try {
const response = await fetch('', { const response = await fetch("{% url 'upload-file' %}", {
method: 'POST', method: 'POST',
body: formData, body: formData,
headers: { headers: {
...@@ -81,38 +78,13 @@ ...@@ -81,38 +78,13 @@
downloadLink.href = result.download_url; downloadLink.href = result.download_url;
downloadSection.classList.remove('hidden'); downloadSection.classList.remove('hidden');
} else if (response.status === 400 && result.status === 'error') { } else if (response.status === 400 && result.status === 'error') {
errorList.innerHTML = ''; for (const [field, messages] of Object.entries(result.errors)) {
messages.forEach(message => {
if (Array.isArray(result.errors)) { const li = document.createElement('li');
result.errors.forEach(errorObj => { li.textContent = `${field}: ${message}`;
if (typeof errorObj === 'string') { errorList.appendChild(li);
const li = document.createElement('li');
li.textContent = errorObj;
errorList.appendChild(li);
} else {
for (const [field, messages] of Object.entries(errorObj)) {
messages.forEach(message => {
const li = document.createElement('li');
li.textContent = `${field}: ${message}`;
errorList.appendChild(li);
});
}
}
}); });
} else if (typeof result.errors === 'object') {
for (const [field, messages] of Object.entries(result.errors)) {
messages.forEach(message => {
const li = document.createElement('li');
li.textContent = `${field}: ${message}`;
errorList.appendChild(li);
});
}
} else {
const li = document.createElement('li');
li.textContent = result.errors;
errorList.appendChild(li);
} }
errorSection.classList.remove('hidden'); errorSection.classList.remove('hidden');
} }
} catch (error) { } catch (error) {
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
from django.urls import path from django.urls import path
from sage_validation.file_validator.views import CSVExportView, CSVUploadView from sage_validation.file_validator.views import CSVExportAPIView, CSVUploadAPIView, upload_page_view
urlpatterns = [ urlpatterns = [
path("upload/", CSVUploadView.as_view(), name="upload-file"), path("upload-page/", upload_page_view, name="upload-page"),
path("export/", CSVExportView.as_view(), name="export-file"), path("api/upload/", CSVUploadAPIView.as_view(), name="upload-file"),
path("api/export/", CSVExportAPIView.as_view(), name="export-file"),
] ]
"""Views for the file_validator app.""" """Views for the file_validator app."""
import csv import csv
import io import io
from typing import Any
from django.http import HttpRequest, HttpResponse, JsonResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.views.generic.base import View from rest_framework import status
from django.views.generic.edit import FormView from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from sage_validation.file_validator.forms import CSVUploadForm from sage_validation.file_validator.forms import CSVUploadForm
from sage_validation.file_validator.models import MeoCostCentres, XxData from sage_validation.file_validator.models import MeoCostCentres, XxData
...@@ -19,46 +20,41 @@ def index_view(request: HttpRequest) -> HttpResponse: ...@@ -19,46 +20,41 @@ def index_view(request: HttpRequest) -> HttpResponse:
return render(request, "index.html") return render(request, "index.html")
class CSVUploadView(FormView): def upload_page_view(request: HttpRequest) -> HttpResponse:
"""View for uploading a CSV file.""" """Render the file upload page."""
return render(request, "upload.html")
template_name = "upload.html"
form_class = CSVUploadForm
success_url = reverse_lazy("upload-file")
def get_context_data(self, **kwargs: dict[str, Any]) -> dict[str, Any]: class CSVUploadAPIView(APIView):
"""Render the form with no error message on GET request.""" """API view for uploading a CSV file."""
context = super().get_context_data(**kwargs)
context["error"] = None def post(self, request: Request) -> Response:
context["message"] = None """Handle CSV upload and validation."""
return context 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)
def form_valid(self, form: CSVUploadForm) -> JsonResponse:
"""Handle the CSV validation, store valid data, and prepare for export."""
csv_file = form.cleaned_data["file"] csv_file = form.cleaned_data["file"]
csv_file.seek(0) csv_file.seek(0)
decoded_file = csv_file.read().decode("utf-8").strip() decoded_file = csv_file.read().decode("utf-8").strip()
if not decoded_file: if not decoded_file:
return JsonResponse({"status": "error", "message": "Uploaded file is empty."}, status=400) return Response({"status": "error", "message": "Uploaded file is empty."},
status=status.HTTP_400_BAD_REQUEST)
reader = csv.DictReader(io.StringIO(decoded_file)) reader = csv.DictReader(io.StringIO(decoded_file))
csv_data: list[dict[str, str]] = list(reader) csv_data: list[dict[str, str]] = list(reader)
updated_data = self.update_fields(csv_data) updated_data = self.update_fields(csv_data)
request.session["validated_csv"] = updated_data
request.session.modified = True
self.request.session["validated_csv"] = updated_data return Response({
self.request.session.modified = True
return JsonResponse({
"status": "success", "status": "success",
"message": "File successfully uploaded and processed.", "message": "File successfully uploaded and processed.",
"download_url": reverse_lazy("export-file") "download_url": reverse_lazy("export-file")
}) }, status=status.HTTP_200_OK)
def form_invalid(self, form: CSVUploadForm) -> JsonResponse:
"""Handle the form when it is invalid."""
return JsonResponse({"status": "error", "errors": form.errors}, status=400)
@staticmethod @staticmethod
def update_fields(csv_data: list[dict[str, str]]) -> list[dict[str, str]]: def update_fields(csv_data: list[dict[str, str]]) -> list[dict[str, str]]:
...@@ -91,21 +87,21 @@ class CSVUploadView(FormView): ...@@ -91,21 +87,21 @@ class CSVUploadView(FormView):
row[f"NominalAnalysisNominalAccountNumber/{repeat}"] = ( row[f"NominalAnalysisNominalAccountNumber/{repeat}"] = (
xx_data[0] if cc_type == "Project" else xx_data[1] xx_data[0] if cc_type == "Project" else xx_data[1]
) )
repeat += 1 repeat += 1
return csv_data return csv_data
class CSVExportView(View): class CSVExportAPIView(APIView):
"""View for exporting the updated CSV file.""" """API view for exporting the updated CSV file."""
def get(self, request: HttpRequest) -> HttpResponse: def get(self, request: Request) -> Response:
"""Generate a downloadable CSV file with updated values.""" """Return processed CSV as a downloadable response."""
csv_data: list[dict[str, str]] = request.session.get("validated_csv", []) csv_data: list[dict[str, str]] = request.session.get("validated_csv", [])
if not csv_data: if not csv_data:
return HttpResponse("No data available for export.", status=400) return Response({"status": "error", "message": "No data available for export."},
status=status.HTTP_400_BAD_REQUEST)
response = HttpResponse(content_type="text/csv") response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="updated_file.csv"' response["Content-Disposition"] = 'attachment; filename="updated_file.csv"'
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<h1 class="text-5xl md:text-6xl font-bold mb-12 text-blue-900">Welcome to Sage Validation</h1> <h1 class="text-5xl md:text-6xl font-bold mb-12 text-blue-900">Welcome to Sage Validation</h1>
<p class="text-xl md:text-2xl mb-16 text-gray-700">Click the button below to upload your file for validation.</p> <p class="text-xl md:text-2xl mb-16 text-gray-700">Click the button below to upload your file for validation.</p>
<a href="{% url "upload-file" %}" <a href="{% url "upload-page" %}"
class="inline-flex py-4 px-16 bg-blue-600 text-white text-lg md:text-xl font-bold rounded-full shadow-lg transition-transform transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-50"> class="inline-flex py-4 px-16 bg-blue-600 text-white text-lg md:text-xl font-bold rounded-full shadow-lg transition-transform transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-50">
Upload File Upload File
</a> </a>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment