Skip to content

Commit

Permalink
Add mark-present api (#2610)
Browse files Browse the repository at this point in the history
* Add mark-present api

Adds an api view basically identical to that at the mark-present-url,
but using PATCH. And adds the mark_present_url to the event admin serializer.

* Fix linting
  • Loading branch information
DeD1rk authored Oct 26, 2022
1 parent b9e3594 commit 92a31b7
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 1 deletion.
1 change: 1 addition & 0 deletions website/events/api/v2/admin/serializers/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Meta:
description = CleanedHTMLSerializer()
price = PaymentAmountSerializer()
fine = PaymentAmountSerializer()
mark_present_url = serializers.ReadOnlyField()

def to_internal_value(self, data):
self.fields["organisers"] = serializers.PrimaryKeyRelatedField(
Expand Down
6 changes: 6 additions & 0 deletions website/events/api/v2/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
EventRegistrationsView,
ExternalEventDetailView,
ExternalEventListView,
MarkPresentAPIView,
)

app_name = "events"
Expand All @@ -35,6 +36,11 @@
EventRegistrationFieldsView.as_view(),
name="event-registration-fields",
),
path(
"events/<int:pk>/mark-present/<uuid:token>/",
MarkPresentAPIView.as_view(),
name="mark-present",
),
path(
"events/external/", ExternalEventListView.as_view(), name="external-events-list"
),
Expand Down
39 changes: 39 additions & 0 deletions website/events/api/v2/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.db.models import Count, Q
from django.utils import timezone

from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope
from rest_framework import filters as framework_filters
Expand All @@ -21,6 +22,7 @@
from events.exceptions import RegistrationError
from events.models import Event, EventRegistration
from events.models.external_event import ExternalEvent
from events.services import is_user_registered
from thaliawebsite.api.v2.permissions import IsAuthenticatedOrTokenHasScopeForMethod
from thaliawebsite.api.v2.serializers import EmptySerializer

Expand Down Expand Up @@ -275,3 +277,40 @@ class ExternalEventDetailView(RetrieveAPIView):
queryset = ExternalEvent.objects.filter(published=True)
permission_classes = [IsAuthenticatedOrTokenHasScope]
required_scopes = ["events:read"]


class MarkPresentAPIView(APIView):
"""A view that allows uses to mark their presence at an event using a secret token."""

def patch(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:
raise PermissionDenied(detail="Invalid url.")

if not request.member or not is_user_registered(request.member, event):
raise PermissionDenied(detail="You are not registered for this event.")

registration = event.registrations.get(
member=request.member, date_cancelled=None
)

if registration.present:
return Response(
data={"detail": "You were already marked as present."},
status=status.HTTP_200_OK,
)
if event.end < timezone.now():
raise PermissionDenied(
detail="This event has already ended.",
)

registration.present = True
registration.save()
return Response(
data={"detail": "You have been marked as present."},
status=status.HTTP_200_OK,
)
74 changes: 74 additions & 0 deletions website/events/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime

from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils import timezone

from rest_framework.test import APIClient
Expand Down Expand Up @@ -40,6 +41,14 @@ def setUpTestData(cls):
cls.event.organisers.add(Committee.objects.get(pk=1))
cls.member = Member.objects.filter(last_name="Wiggers").first()

cls.mark_present_api_url = reverse(
"api:v2:events:mark-present",
kwargs={
"pk": cls.event.pk,
"token": cls.event.mark_present_url_token,
},
)

def setUp(self):
self.client = APIClient()
self.client.force_login(self.member)
Expand Down Expand Up @@ -354,3 +363,68 @@ def test_registration_organiser(self):
reg2.refresh_from_db()
self.assertEqual(reg2.payment.type, "cash_payment")
self.assertFalse(reg2.present)

def test_mark_present_url_registered(self):
registration = EventRegistration.objects.create(
event=self.event,
member=self.member,
date=timezone.now() - datetime.timedelta(hours=1),
)

response = self.client.patch(self.mark_present_api_url, follow=True)
self.assertContains(response, "You have been marked as present.")
registration.refresh_from_db()
self.assertTrue(registration.present)

def test_mark_present_url_already_present(self):
registration = EventRegistration.objects.create(
event=self.event,
member=self.member,
date=timezone.now() - datetime.timedelta(hours=1),
present=True,
)

response = self.client.patch(self.mark_present_api_url, follow=True)
self.assertContains(response, "You were already marked as present.")
registration.refresh_from_db()
self.assertTrue(registration.present)

def test_mark_present_url_not_registered(self):
response = self.client.patch(self.mark_present_api_url, follow=True)
self.assertContains(
response, "You are not registered for this event.", status_code=403
)

def test_mark_present_url_wrong_token(self):
registration = EventRegistration.objects.create(
event=self.event,
member=self.member,
date=timezone.now() - datetime.timedelta(hours=3),
)
response = self.client.patch(
reverse(
"api:v2:events:mark-present",
kwargs={
"pk": self.event.pk,
"token": "11111111-2222-3333-4444-555555555555",
},
),
follow=True,
)

self.assertContains(response, "Invalid url.", status_code=403)
self.assertFalse(registration.present)

def test_mark_present_url_past_event(self):
registration = EventRegistration.objects.create(
event=self.event,
member=self.member,
date=timezone.now() - datetime.timedelta(hours=3),
)
self.event.start = timezone.now() - datetime.timedelta(hours=2)
self.event.end = timezone.now() - datetime.timedelta(hours=1)
self.event.save()
response = self.client.patch(self.mark_present_api_url, follow=True)

self.assertContains(response, "This event has already ended.", status_code=403)
self.assertFalse(registration.present)
4 changes: 3 additions & 1 deletion website/sales/api/v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ def get_request_serializer(self, path, method):

def patch(self, request, *args, **kwargs):
if request.member is None:
raise PermissionDenied("You need to be a member to pay for an order.")
raise PermissionDenied(
detail="You need to be a member to pay for an order."
)

order = self.get_object()
if order.payment:
Expand Down

0 comments on commit 92a31b7

Please sign in to comment.