svthalia/concrexit

View on GitHub
website/events/views.py

Summary

Maintainability
A
2 hrs
Test Coverage
"""Views provided by the events package."""

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic import DetailView, FormView, TemplateView

from events import services
from events.exceptions import RegistrationError
from events.models import categories
from events.services import is_user_registered
from payments.models import Payment
from utils.media.services import fetch_thumbnails

from .forms import FieldsForm
from .models import Event, EventRegistration


class EventIndex(TemplateView):
    """Render the events calendar overview."""

    template_name = "events/index.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        upcoming_activity = (
            Event.objects.filter(published=True, end__gte=timezone.now())
            .order_by("end")
            .first()
        )
        context["upcoming_activity"] = upcoming_activity

        return context


class EventDetail(DetailView):
    """Render a single event detail page."""

    model = Event
    queryset = Event.objects.filter(published=True).prefetch_related("organisers")
    template_name = "events/event.html"
    context_object_name = "event"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["user"] = self.request.user
        context["payment_method_tpay"] = Payment.TPAY

        event = context["event"]
        if event.max_participants:
            perc = 100.0 * len(event.participants) / event.max_participants
            context["registration_percentage"] = perc

        try:
            context["registration"] = EventRegistration.objects.get(
                event=event, member=self.request.member
            )
        except (EventRegistration.DoesNotExist, TypeError):
            pass

        registration_status = services.registration_status(
            event, context.get("registration"), self.request.member
        )
        context["registration_status"] = services.registration_status_string(
            registration_status, event, context.get("registration")
        )

        context["show_cancel_status"] = services.show_cancel_status(registration_status)
        if context["show_cancel_status"]:
            cancel_status = services.cancel_status(event, context.get("registration"))
            context["cancel_info"] = services.cancel_info_string(
                event, cancel_status, registration_status
            )

        context["permissions"] = services.event_permissions(self.request.member, event)

        context["date_now"] = timezone.now()

        context["slide_size"] = "slide"

        context["participants"] = event.participants.select_related(
            "member", "member__profile"
        )

        fetch_thumbnails(
            [p.member.profile.photo for p in context["participants"] if p.member]
        )

        return context


class AlumniEventsView(TemplateView):
    """Render the alumni events page."""

    template_name = "events/alumni.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        events = Event.objects.filter(
            published=True, category=categories.CATEGORY_ALUMNI, end__gte=timezone.now()
        ).order_by("end")[:3]
        context["events"] = events

        return context


@method_decorator(login_required, name="dispatch")
class EventRegisterView(View):
    """Define a view that allows the user to register for an event using a POST request.

    The user should be authenticated.
    """

    def get(self, request, *args, **kwargs):
        return redirect("events:event", pk=kwargs["pk"])

    def post(self, request, *args, **kwargs):
        event = get_object_or_404(Event, pk=kwargs["pk"])
        try:
            services.create_registration(request.member, event)

            if event.has_fields:
                return redirect("events:registration", event.pk)

            messages.success(request, _("Registration successful."))
        except RegistrationError as e:
            messages.error(request, e)

        return redirect(event)


@method_decorator(login_required, name="dispatch")
class EventCancelView(View):
    """Define a view that allows the user to cancel their event registration using a POST request.

    The user should be authenticated.
    """

    def get(self, request, *args, **kwargs):
        return redirect("events:event", pk=kwargs["pk"])

    def post(self, request, *args, **kwargs):
        event = get_object_or_404(Event, pk=kwargs["pk"])
        try:
            services.cancel_registration(request.member, event)
            messages.success(request, _("Registration successfully cancelled."))
        except RegistrationError as e:
            messages.error(request, e)

        return redirect(event)


@method_decorator(login_required, name="dispatch")
class RegistrationView(FormView):
    """Render a form that allows the user to change the details of their registration.

    The user should be authenticated.
    """

    form_class = FieldsForm
    template_name = "events/registration.html"
    event = None

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["event"] = self.event
        return context

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["fields"] = services.registration_fields(
            self.request, self.request.member, self.event
        )
        return kwargs

    def form_valid(self, form):
        values = form.field_values()
        try:
            services.update_registration(
                self.request.member, self.event, field_values=values
            )
            messages.success(self.request, _("Registration successfully saved."))
            return redirect(self.event)
        except RegistrationError as e:
            messages.error(self.request, e)
            return self.render_to_response(self.get_context_data(form=form))

    def dispatch(self, request, *args, **kwargs):
        self.event = get_object_or_404(Event, pk=self.kwargs["pk"])
        try:
            if self.event.has_fields:
                return super().dispatch(request, *args, **kwargs)
        except RegistrationError:
            pass
        return redirect(self.event)


@method_decorator(login_required, name="dispatch")
class MarkPresentView(View):
    """A view that allows uses to mark their presence at an event using a secret token."""

    def get(self, request, *args, **kwargs):
        """Mark a user as present.

        Checks if the url is correct, the event has not ended yet, and the user is registered.
        """
        event = get_object_or_404(Event, pk=kwargs["pk"])
        if kwargs["token"] != event.mark_present_url_token:
            messages.error(request, _("Invalid url."))
        elif not request.member or not is_user_registered(request.member, event):
            messages.error(request, _("You are not registered for this event."))
        else:
            registration = event.registrations.get(
                member=request.member, date_cancelled=None
            )

            if registration.present:
                messages.info(request, _("You were already marked as present."))
            elif event.end < timezone.now():
                messages.error(request, _("This event has already ended."))
            else:
                registration.present = True
                registration.save()
                messages.success(request, _("You have been marked as present."))

        return redirect(event)


class NextEventView(View):
    def get(self, request, *args, **kwargs):
        """HTTP redirect to the next event.

        Checks if there is an upcoming event. Raise a 404 if none exists.
        """
        upcoming_activity = (
            Event.objects.filter(published=True, end__gte=timezone.now())
            .order_by("end")
            .first()
        )
        if not upcoming_activity:
            raise Http404("There is no upcoming event.")

        return redirect(upcoming_activity)