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

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 

12 

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 

20 

21from .forms import FieldsForm 

22from .models import Event, EventRegistration 

23 

24 

25class EventIndex(TemplateView): 

26 """Render the events calendar overview.""" 

27 

28 template_name = "events/index.html" 

29 

30 def get_context_data(self, **kwargs): 

31 context = super().get_context_data(**kwargs) 

32 

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 

39 

40 return context 

41 

42 

43class EventDetail(DetailView): 

44 """Render a single event detail page.""" 

45 

46 model = Event 

47 queryset = Event.objects.filter(published=True).prefetch_related("organisers") 

48 template_name = "events/event.html" 

49 context_object_name = "event" 

50 

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 

55 

56 event = context["event"] 

57 if event.max_participants: 

58 perc = 100.0 * len(event.participants) / event.max_participants 

59 context["registration_percentage"] = perc 

60 

61 try: 

62 context["registration"] = EventRegistration.objects.get( 

63 event=event, member=self.request.member 

64 ) 

65 except (EventRegistration.DoesNotExist, TypeError): 

66 pass 

67 

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 ) 

74 

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 ) 

81 

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

83 

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

85 

86 context["slide_size"] = "slide" 

87 

88 context["participants"] = event.participants.select_related( 

89 "member", "member__profile" 

90 ) 

91 

92 context["shifts"] = event.shift_set.filter(selforder=True) 

93 

94 fetch_thumbnails( 

95 [p.member.profile.photo for p in context["participants"] if p.member] 

96 ) 

97 

98 return context 

99 

100 

101class AlumniEventsView(TemplateView): 

102 """Render the alumni events page.""" 

103 

104 template_name = "events/alumni.html" 

105 

106 def get_context_data(self, **kwargs): 

107 context = super().get_context_data(**kwargs) 

108 

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 

113 

114 return context 

115 

116 

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. 

120 

121 The user should be authenticated. 

122 """ 

123 

124 def get(self, request, *args, **kwargs): 

125 return redirect("events:event", pk=kwargs["pk"]) 

126 

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) 

131 

132 if event.has_fields: 

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

134 

135 messages.success(request, _("Registration successful.")) 

136 except RegistrationError as e: 

137 messages.error(request, e) 

138 

139 return redirect(event) 

140 

141 

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. 

145 

146 The user should be authenticated. 

147 """ 

148 

149 def get(self, request, *args, **kwargs): 

150 return redirect("events:event", pk=kwargs["pk"]) 

151 

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) 

159 

160 return redirect(event) 

161 

162 

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. 

166 

167 The user should be authenticated. 

168 """ 

169 

170 form_class = FieldsForm 

171 template_name = "events/registration.html" 

172 event = None 

173 

174 def get_context_data(self, **kwargs): 

175 context = super().get_context_data(**kwargs) 

176 context["event"] = self.event 

177 return context 

178 

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 

185 

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)) 

197 

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) 

206 

207 

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.""" 

211 

212 def get(self, request, *args, **kwargs): 

213 """Mark a user as present. 

214 

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 ) 

226 

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.")) 

235 

236 return redirect(event) 

237 

238 

239class NextEventView(View): 

240 def get(self, request, *args, **kwargs): 

241 """HTTP redirect to the next event. 

242 

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.") 

252 

253 return redirect(upcoming_activity) 

254 

255 

256class ICalHelpView(TemplateView): 

257 """Render the iCal feed help page.""" 

258 

259 template_name = "events/ical_help.html" 

260 

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 ) 

269 

270 return context