Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add REST API endpoints to Activities to allow joining and assigning participants #215

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
29 changes: 29 additions & 0 deletions project/activities/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from rest_framework import serializers

from .models import Activity


class ActivitySerializer(serializers.HyperlinkedModelSerializer):
circle = serializers.HyperlinkedRelatedField(
view_name="circle-detail", read_only=True
)
participants = serializers.HyperlinkedRelatedField(
many=True, view_name="companion-detail", read_only=True
)
organizers = serializers.HyperlinkedRelatedField(
many=True, view_name="student-detail", read_only=True
)

class Meta:
model = Activity
fields = (
"url",
"id",
"activity_type",
"activity_date",
"note",
"circle",
"participants",
"done",
"organizers",
)
8 changes: 7 additions & 1 deletion project/activities/urls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.urls import path
from django.urls import include, path
from rest_framework import routers

from .views import (
ActivityAddParticipantView,
Expand All @@ -7,9 +8,14 @@
ActivityRemoveParticipantView,
ActivitySetDoneView,
ActivityUpdateView,
ActivityViewSet,
)

router = routers.SimpleRouter()
router.register(r"activities", ActivityViewSet)

urlpatterns = [
path("", include(router.urls)),
path(
"create",
ActivityCreateView.as_view(),
Expand Down
95 changes: 93 additions & 2 deletions project/activities/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from circles.models import Circle
from circles.models import Circle, Companion
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic import View
from rest_framework import permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from .forms import ActivityModelForm
from .models import Activity
from .models import Activity, User
from .serializers import ActivitySerializer


class ActivityCreateView(UserPassesTestMixin, LoginRequiredMixin, View):
Expand Down Expand Up @@ -219,3 +223,90 @@ def get(self, request, activity_id, *args, **kwargs):
kwargs={"pk": self.activity.circle.id},
)
)


class ActivityViewSet(viewsets.ModelViewSet):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer

@action(detail=True, methods=["post"])
def join_activity(self, request, pk=None):
"""Add current user to participants of an activity"""
activity = self.get_object()
user = request.user
circle = activity.circle

# Check user is Companion in Circle
try:
companion = Companion.objects.get(user=user, circle=circle)
except Companion.DoesNotExist:
return Response(
{"status": "error", "message": "Sorry, you are not in this Circle"}
)

# Check if companion is already a participant of the activity
if user in activity.participants.all():
return Response(
{
"status": "error",
"message": "You are already participating in this activity",
}
)

activity.participants.add(user)
activity.save()

return Response({"status": "success"})

@action(
detail=True, methods=["post"], permission_classes=[permissions.IsAuthenticated]
)
def assign_companion(self, request, pk=None):
# Assign a companion to an activity
activity = self.get_object()
user = request.user
circle = activity.circle
assignee_id = request.data.get("assignee_id")

# Check current user is the Circle organizer
try:
assigner = Companion.objects.get(
user__id=user.id, is_organizer=True, circle=circle
)
except Companion.DoesNotExist:
return Response(
{
"status": "error",
"message": "Sorry, you need to be the organizer of this Circle to "
"add a participant",
}
)

# Check 'assignee' is in the Circle
try:
assignee = Companion.objects.get(user__id=assignee_id, circle=circle)
except Companion.DoesNotExist:
return Response(
{
"error": "The participant you are trying to add is not in this "
"Circle."
},
status=status.HTTP_400_BAD_REQUEST,
)

# Check 'assignee' is not already in the activity
assignee_email = User.objects.get(id=assignee_id)
if assignee_email in activity.participants.all():
return Response(
{
"status": "error",
"message": "This companion is already a participant of this "
"activity",
}
)

# Assign Companion to activity
activity.participants.add(assignee_id)
activity.save()

return Response({"status": "success"})
9 changes: 9 additions & 0 deletions project/circles/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from rest_framework import serializers

from .models import Circle


class CircleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Circle
fields = ("id", "name", "photo")
15 changes: 15 additions & 0 deletions project/circles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated

from .models import Circle, Companion, JoinRequest
from .serializers import CircleSerializer


class CompanionDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
Expand Down Expand Up @@ -241,3 +244,15 @@ def get(self, request, circle_id, join_request_id, *args, **kwargs):
join_request.delete()

return redirect(circle)


# Only returns circles that user is a companion of
class CircleViewSet(viewsets.ModelViewSet):
serializer_class = CircleSerializer
permission_classes = [IsAuthenticated]

def get_queryset(self):
user = self.request.user
circles = Circle.objects.filter(companions_through__user=user)

return circles
7 changes: 7 additions & 0 deletions project/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from circles import views
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path
from django.views.generic.base import TemplateView
from rest_framework.routers import DefaultRouter

media_urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

router = DefaultRouter()
router.register(r"circles", views.CircleViewSet, basename="circle")

api_urlpatterns = [
path(
"accounts/registration/",
Expand All @@ -34,6 +39,8 @@

urlpatterns = [
path("api/v1/", include(api_urlpatterns)),
path("api/v1/", include(router.urls)),
path("api/v1/", include("activities.urls")),
path("__debug__/", include("debug_toolbar.urls")),
path("", TemplateView.as_view(template_name="home.html"), name="home"),
path("admin/", admin.site.urls),
Expand Down