"""Submit form view (plan.md §7.4 + §7). The view persists a `Submission` and, depending on whether the request is authenticated, sets the initial state machine state per plan.md §7.3: OAuth user -> processing (email already verified) guest with email -> identifying (waiting for confirmation link click) Slug generation, email sending, and validation cron are out of scope here and deferred to plan.md §7.5 (`processor` sidecar) and §7.5 confirmation flow. """ from __future__ import annotations import secrets import namesgenerator from django.contrib import messages from django.db import IntegrityError, transaction from django.urls import reverse_lazy from django.utils import timezone from django.views.generic import CreateView from .forms import SubmissionForm from .models import Submission def _generate_unique_slug(max_attempts: int = 16) -> str: """`namesgenerator.get_random_name` with collision retries. The slug column is `unique=True`; we retry instead of looping in SQL so the rare collision path stays observable. """ for _ in range(max_attempts): candidate = namesgenerator.get_random_name() if not Submission.objects.filter(slug=candidate).exists(): return candidate # Fall through: extremely unlikely, but caller will see IntegrityError # if the next save() still collides. return namesgenerator.get_random_name() class SubmitView(CreateView): """Public submit form. GET renders, POST creates a Submission.""" form_class = SubmissionForm template_name = "submissions/submit.html" success_url = reverse_lazy("dashboard:index") def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["user"] = self.request.user return kwargs @transaction.atomic def form_valid(self, form): submission: Submission = form.save(commit=False) submission.slug = _generate_unique_slug() if self.request.user.is_authenticated: submission.submitted_by = self.request.user submission.guest_email = None submission.email_confirmed = True submission.status = Submission.Status.PROCESSING else: submission.submitted_by = None # guest_email is already on the form's cleaned_data, ModelForm # populated it onto the instance. submission.email_confirmed = False submission.status = Submission.Status.IDENTIFYING submission.confirmation_token = secrets.token_urlsafe(32) submission.confirmation_sent_at = timezone.now() # TODO(plan.md §7.4 step 6): send the confirmation email here. try: submission.save() except IntegrityError: # Extremely rare slug collision on the unique index; one more try. submission.slug = _generate_unique_slug() submission.save() if submission.status == Submission.Status.IDENTIFYING: messages.info( self.request, f"Submission {submission.slug} created. Check your email " f"({submission.guest_email}) for a confirmation link -- you " f"have 24 hours.", ) else: messages.success( self.request, f"Submission {submission.slug} accepted. We'll start " f"validating it shortly.", ) self.object = submission return super().form_valid(form)