import json import time from pathlib import Path from django.http import StreamingHttpResponse, JsonResponse from django.conf import settings from django.core.validators import validate_email from django.core.exceptions import ValidationError from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.csrf import csrf_exempt def send_progress_update(step, message, progress): """Helper to send a progress update as JSON""" return json.dumps({"step": step, "message": message, "progress": progress}) + "\n" @xframe_options_exempt @csrf_exempt def submit_quote(request): """ Handle quote submission with progress updates. Steps: 1. Upload files 2. Save files 3. Send email 4. Verify email sent """ if request.method != "POST": return JsonResponse({"error": "Method not allowed"}, status=405) # Read form data first (files need to be read from request) email = request.POST.get("email", "").strip() notes = request.POST.get("notes", "").strip() files = request.FILES.getlist("files") def process_quote(): try: # Step 1: Validate email yield send_progress_update(1, "Validating email address...", 5) if not email: raise ValueError("Email is required") try: validate_email(email) except ValidationError: raise ValueError(f"Invalid email address: {email}") yield send_progress_update(1, "Email validated successfully", 10) # Step 2: Create submission & upload files from quotes.models import Submission, SubmissionFile # Create a Submission entry submission = Submission.objects.create( email=email, description=notes, ) yield send_progress_update(2, "Created submission entry...", 15) # Save each file and register in the model total_files = len(files) file_objs = [] for idx, file in enumerate(files, start=1): # Use the original filename and save to MEDIA_ROOT/submission__filename submission_dir = ( Path(settings.MEDIA_ROOT) / f"submission_{submission.id}" ) submission_dir.mkdir(parents=True, exist_ok=True) target_path = submission_dir / file.name with open(target_path, "wb") as f: for chunk in file.chunks(): f.write(chunk) # Register file in SubmissionFile model rel_path = (Path(f"submission_{submission.id}") / file.name).as_posix() submission_file = SubmissionFile.objects.create( submission=submission, original_filename=file.name, path=rel_path, file_size=file.size, ) file_objs.append(submission_file) yield send_progress_update( 2, f"Uploaded file {idx} of {total_files}: {file.name}", 15 + int((idx / total_files) * 30), ) yield send_progress_update(3, "Files saved...", 50) # Step 4: Send emails from quotes.email_utils import send_submission_emails yield send_progress_update(4, "Sending notification email...", 70) owner_sent, submitter_sent = send_submission_emails(submission) time.sleep(0.3) yield send_progress_update(4, "Sending confirmation email...", 75) time.sleep(0.3) # Verify emails were sent if not owner_sent and getattr(settings, "OWNER_EMAIL", ""): yield send_progress_update( "warning", "Warning: Owner notification email may not have been sent", 75, ) if not submitter_sent: yield send_progress_update( "warning", "Warning: Confirmation email may not have been sent", 75 ) # Complete yield send_progress_update( 5, "Quote request submitted successfully! Check your email for confirmation.", 100, ) except Exception as e: yield send_progress_update("error", f"An error occurred: {str(e)}", 0) response = StreamingHttpResponse(process_quote(), content_type="text/event-stream") response["Cache-Control"] = "no-cache" response["X-Accel-Buffering"] = "no" return response