Coverage for website/events/views.py: 83.14%
152 statements
« prev ^ index » next coverage.py v7.6.12, created at 2026-06-21 23:59 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2026-06-21 23:59 +0000
1from django.conf import settings
2from django.contrib import messages
3from django.contrib.auth.decorators import login_required
4from django.http import Http404
5from django.shortcuts import get_object_or_404, redirect
6from django.urls import reverse
7from django.utils import timezone
8from django.utils.decorators import method_decorator
9from django.utils.translation import gettext_lazy as _
10from django.views import View
11from django.views.generic import DetailView, FormView, TemplateView
13from events import services
14from events.exceptions import RegistrationError
15from events.models import categories
16from events.models.feed_token import FeedToken
17from events.services import is_user_registered
18from payments.models import Payment
19from utils.media.services import fetch_thumbnails
21from .forms import FieldsForm
22from .models import Event, EventRegistration
25class EventIndex(TemplateView):
26 """Render the events calendar overview."""
28 template_name = "events/index.html"
30 def get_context_data(self, **kwargs):
31 context = super().get_context_data(**kwargs)
33 upcoming_activity = (
34 Event.objects.filter(published=True, end__gte=timezone.now())
35 .order_by("end")
36 .first()
37 )
38 context["upcoming_activity"] = upcoming_activity
40 return context
43class EventDetail(DetailView):
44 """Render a single event detail page."""
46 model = Event
47 queryset = Event.objects.filter(published=True).prefetch_related("organisers")
48 template_name = "events/event.html"
49 context_object_name = "event"
51 def get_context_data(self, **kwargs):
52 context = super().get_context_data(**kwargs)
53 context["user"] = self.request.user
54 context["payment_method_tpay"] = Payment.TPAY
56 event = context["event"]
57 if event.max_participants:
58 perc = 100.0 * len(event.participants) / event.max_participants
59 context["registration_percentage"] = perc
61 try:
62 context["registration"] = EventRegistration.objects.get(
63 event=event, member=self.request.member
64 )
65 except (EventRegistration.DoesNotExist, TypeError):
66 pass
68 registration_status = services.registration_status(
69 event, context.get("registration"), self.request.member
70 )
71 context["registration_status"] = services.registration_status_string(
72 registration_status, event, context.get("registration")
73 )
75 context["show_cancel_status"] = services.show_cancel_status(registration_status)
76 if context["show_cancel_status"]:
77 cancel_status = services.cancel_status(event, context.get("registration"))
78 context["cancel_info"] = services.cancel_info_string(
79 event, cancel_status, registration_status
80 )
82 context["permissions"] = services.event_permissions(self.request.member, event)
84 context["date_now"] = timezone.now()
86 context["slide_size"] = "slide"
88 context["participants"] = event.participants.select_related(
89 "member", "member__profile"
90 )
92 context["shifts"] = event.shift_set.filter(selforder=True)
94 fetch_thumbnails(
95 [p.member.profile.photo for p in context["participants"] if p.member]
96 )
98 return context
101class AlumniEventsView(TemplateView):
102 """Render the alumni events page."""
104 template_name = "events/alumni.html"
106 def get_context_data(self, **kwargs):
107 context = super().get_context_data(**kwargs)
109 events = Event.objects.filter(
110 published=True, category=categories.CATEGORY_ALUMNI, end__gte=timezone.now()
111 ).order_by("end")[:3]
112 context["events"] = events
114 return context
117@method_decorator(login_required, name="dispatch")
118class EventRegisterView(View):
119 """Define a view that allows the user to register for an event using a POST request.
121 The user should be authenticated.
122 """
124 def get(self, request, *args, **kwargs):
125 return redirect("events:event", pk=kwargs["pk"])
127 def post(self, request, *args, **kwargs):
128 event = get_object_or_404(Event, pk=kwargs["pk"])
129 try:
130 services.create_registration(request.member, event)
132 if event.has_fields:
133 return redirect("events:registration", event.pk)
135 messages.success(request, _("Registration successful."))
136 except RegistrationError as e:
137 messages.error(request, e)
139 return redirect(event)
142@method_decorator(login_required, name="dispatch")
143class EventCancelView(View):
144 """Define a view that allows the user to cancel their event registration using a POST request.
146 The user should be authenticated.
147 """
149 def get(self, request, *args, **kwargs):
150 return redirect("events:event", pk=kwargs["pk"])
152 def post(self, request, *args, **kwargs):
153 event = get_object_or_404(Event, pk=kwargs["pk"])
154 try:
155 services.cancel_registration(request.member, event)
156 messages.success(request, _("Registration successfully cancelled."))
157 except RegistrationError as e:
158 messages.error(request, e)
160 return redirect(event)
163@method_decorator(login_required, name="dispatch")
164class RegistrationView(FormView):
165 """Render a form that allows the user to change the details of their registration.
167 The user should be authenticated.
168 """
170 form_class = FieldsForm
171 template_name = "events/registration.html"
172 event = None
174 def get_context_data(self, **kwargs):
175 context = super().get_context_data(**kwargs)
176 context["event"] = self.event
177 return context
179 def get_form_kwargs(self):
180 kwargs = super().get_form_kwargs()
181 kwargs["fields"] = services.registration_fields(
182 self.request, self.request.member, self.event
183 )
184 return kwargs
186 def form_valid(self, form):
187 values = form.field_values()
188 try:
189 services.update_registration(
190 self.request.member, self.event, field_values=values
191 )
192 messages.success(self.request, _("Registration successfully saved."))
193 return redirect(self.event)
194 except RegistrationError as e:
195 messages.error(self.request, e)
196 return self.render_to_response(self.get_context_data(form=form))
198 def dispatch(self, request, *args, **kwargs):
199 self.event = get_object_or_404(Event, pk=self.kwargs["pk"])
200 try:
201 if self.event.has_fields: 201 ↛ 205line 201 didn't jump to line 205 because the condition on line 201 was always true
202 return super().dispatch(request, *args, **kwargs)
203 except RegistrationError:
204 pass
205 return redirect(self.event)
208@method_decorator(login_required, name="dispatch")
209class MarkPresentView(View):
210 """A view that allows uses to mark their presence at an event using a secret token."""
212 def get(self, request, *args, **kwargs):
213 """Mark a user as present.
215 Checks if the url is correct, the event has not ended yet, and the user is registered.
216 """
217 event = get_object_or_404(Event, pk=kwargs["pk"])
218 if kwargs["token"] != event.mark_present_url_token:
219 messages.error(request, _("Invalid url."))
220 elif not request.member or not is_user_registered(request.member, event):
221 messages.error(request, _("You are not registered for this event."))
222 else:
223 registration = event.registrations.get(
224 member=request.member, date_cancelled=None
225 )
227 if registration.present:
228 messages.info(request, _("You were already marked as present."))
229 elif event.end < timezone.now():
230 messages.error(request, _("This event has already ended."))
231 else:
232 registration.present = True
233 registration.save()
234 messages.success(request, _("You have been marked as present."))
236 return redirect(event)
239class NextEventView(View):
240 def get(self, request, *args, **kwargs):
241 """HTTP redirect to the next event.
243 Checks if there is an upcoming event. Raise a 404 if none exists.
244 """
245 upcoming_activity = (
246 Event.objects.filter(published=True, end__gte=timezone.now())
247 .order_by("end")
248 .first()
249 )
250 if not upcoming_activity:
251 raise Http404("There is no upcoming event.")
253 return redirect(upcoming_activity)
256class ICalHelpView(TemplateView):
257 """Render the iCal feed help page."""
259 template_name = "events/ical_help.html"
261 def get_context_data(self, **kwargs):
262 context = super().get_context_data(**kwargs)
263 context["all_events_feed"] = settings.BASE_URL + reverse("events:ical-en")
264 if self.request.member:
265 token = FeedToken.objects.get_or_create(member=self.request.member)[0].token
266 context["personal_feed"] = (
267 f"{settings.BASE_URL}{reverse('events:ical-en')}?u={token}"
268 )
270 return context