Coverage for website/events/views.py: 83.04%

151 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +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 fetch_thumbnails( 

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

94 ) 

95 

96 return context 

97 

98 

99class AlumniEventsView(TemplateView): 

100 """Render the alumni events page.""" 

101 

102 template_name = "events/alumni.html" 

103 

104 def get_context_data(self, **kwargs): 

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

106 

107 events = Event.objects.filter( 

108 published=True, category=categories.CATEGORY_ALUMNI, end__gte=timezone.now() 

109 ).order_by("end")[:3] 

110 context["events"] = events 

111 

112 return context 

113 

114 

115@method_decorator(login_required, name="dispatch") 

116class EventRegisterView(View): 

117 """Define a view that allows the user to register for an event using a POST request. 

118 

119 The user should be authenticated. 

120 """ 

121 

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

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

124 

125 def post(self, request, *args, **kwargs): 

126 event = get_object_or_404(Event, pk=kwargs["pk"]) 

127 try: 

128 services.create_registration(request.member, event) 

129 

130 if event.has_fields: 

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

132 

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

134 except RegistrationError as e: 

135 messages.error(request, e) 

136 

137 return redirect(event) 

138 

139 

140@method_decorator(login_required, name="dispatch") 

141class EventCancelView(View): 

142 """Define a view that allows the user to cancel their event registration using a POST request. 

143 

144 The user should be authenticated. 

145 """ 

146 

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

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

149 

150 def post(self, request, *args, **kwargs): 

151 event = get_object_or_404(Event, pk=kwargs["pk"]) 

152 try: 

153 services.cancel_registration(request.member, event) 

154 messages.success(request, _("Registration successfully cancelled.")) 

155 except RegistrationError as e: 

156 messages.error(request, e) 

157 

158 return redirect(event) 

159 

160 

161@method_decorator(login_required, name="dispatch") 

162class RegistrationView(FormView): 

163 """Render a form that allows the user to change the details of their registration. 

164 

165 The user should be authenticated. 

166 """ 

167 

168 form_class = FieldsForm 

169 template_name = "events/registration.html" 

170 event = None 

171 

172 def get_context_data(self, **kwargs): 

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

174 context["event"] = self.event 

175 return context 

176 

177 def get_form_kwargs(self): 

178 kwargs = super().get_form_kwargs() 

179 kwargs["fields"] = services.registration_fields( 

180 self.request, self.request.member, self.event 

181 ) 

182 return kwargs 

183 

184 def form_valid(self, form): 

185 values = form.field_values() 

186 try: 

187 services.update_registration( 

188 self.request.member, self.event, field_values=values 

189 ) 

190 messages.success(self.request, _("Registration successfully saved.")) 

191 return redirect(self.event) 

192 except RegistrationError as e: 

193 messages.error(self.request, e) 

194 return self.render_to_response(self.get_context_data(form=form)) 

195 

196 def dispatch(self, request, *args, **kwargs): 

197 self.event = get_object_or_404(Event, pk=self.kwargs["pk"]) 

198 try: 

199 if self.event.has_fields: 199 ↛ 203line 199 didn't jump to line 203 because the condition on line 199 was always true

200 return super().dispatch(request, *args, **kwargs) 

201 except RegistrationError: 

202 pass 

203 return redirect(self.event) 

204 

205 

206@method_decorator(login_required, name="dispatch") 

207class MarkPresentView(View): 

208 """A view that allows uses to mark their presence at an event using a secret token.""" 

209 

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

211 """Mark a user as present. 

212 

213 Checks if the url is correct, the event has not ended yet, and the user is registered. 

214 """ 

215 event = get_object_or_404(Event, pk=kwargs["pk"]) 

216 if kwargs["token"] != event.mark_present_url_token: 

217 messages.error(request, _("Invalid url.")) 

218 elif not request.member or not is_user_registered(request.member, event): 

219 messages.error(request, _("You are not registered for this event.")) 

220 else: 

221 registration = event.registrations.get( 

222 member=request.member, date_cancelled=None 

223 ) 

224 

225 if registration.present: 

226 messages.info(request, _("You were already marked as present.")) 

227 elif event.end < timezone.now(): 

228 messages.error(request, _("This event has already ended.")) 

229 else: 

230 registration.present = True 

231 registration.save() 

232 messages.success(request, _("You have been marked as present.")) 

233 

234 return redirect(event) 

235 

236 

237class NextEventView(View): 

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

239 """HTTP redirect to the next event. 

240 

241 Checks if there is an upcoming event. Raise a 404 if none exists. 

242 """ 

243 upcoming_activity = ( 

244 Event.objects.filter(published=True, end__gte=timezone.now()) 

245 .order_by("end") 

246 .first() 

247 ) 

248 if not upcoming_activity: 

249 raise Http404("There is no upcoming event.") 

250 

251 return redirect(upcoming_activity) 

252 

253 

254class ICalHelpView(TemplateView): 

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

256 

257 template_name = "events/ical_help.html" 

258 

259 def get_context_data(self, **kwargs): 

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

261 context["all_events_feed"] = settings.BASE_URL + reverse("events:ical-en") 

262 if self.request.member: 

263 token = FeedToken.objects.get_or_create(member=self.request.member)[0].token 

264 context["personal_feed"] = ( 

265 f"{settings.BASE_URL}{reverse('events:ical-en')}?u={token}" 

266 ) 

267 

268 return context