diff --git a/backend/config/settings.py b/backend/config/settings.py index 9076bd9..2d361a7 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -126,3 +126,8 @@ # Custom User Model AUTH_USER_MODEL = "hoagiecalendar.User" + +# Default primary key field type +# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/backend/config/urls.py b/backend/config/urls.py index 09c71db..8a4af92 100644 --- a/backend/config/urls.py +++ b/backend/config/urls.py @@ -20,10 +20,12 @@ from hoagiecalendar.api.event_views import EventDetailView, EventView from hoagiecalendar.api.user_events_view import UserEventsView +from hoagiecalendar.api.user_saved_events_view import UserSavedEventsView urlpatterns = [ path("admin/", admin.site.urls), path("event/", EventView.as_view(), name="event"), path("event//", EventDetailView.as_view(), name="event-detail"), - path("user/events/", UserEventsView.as_view(), name="user-events"), + path("user/events/created", UserEventsView.as_view(), name="user-events"), + path("user/events/saved", UserSavedEventsView.as_view(), name="user-events-saved"), ] diff --git a/backend/hoagiecalendar/api/event_views.py b/backend/hoagiecalendar/api/event_views.py index 02ab148..b09e45b 100644 --- a/backend/hoagiecalendar/api/event_views.py +++ b/backend/hoagiecalendar/api/event_views.py @@ -37,6 +37,13 @@ class EventSerializer(serializers.ModelSerializer): }, ) + is_saved = serializers.SerializerMethodField() + + def get_is_saved(self, obj: Event) -> bool: + user = self.context["request"].user + + return obj.saved_by.contains(user) + class Meta: model = Event fields = [ @@ -51,8 +58,9 @@ class Meta: "category", "from_mail", "ordering", + "is_saved", ] - read_only_fields = ["id", "owner", "created_at", "updated_at"] + read_only_fields = ["id", "owner", "created_at", "updated_at", "is_saved"] class EventView(APIView): diff --git a/backend/hoagiecalendar/api/user_events_view.py b/backend/hoagiecalendar/api/user_events_view.py index f6adabd..5ca618c 100644 --- a/backend/hoagiecalendar/api/user_events_view.py +++ b/backend/hoagiecalendar/api/user_events_view.py @@ -10,5 +10,5 @@ class UserEventsView(APIView): # Logic to get events user created def get(self, request) -> Response: events = Event.objects.filter(owner=request.user) - serializer = EventSerializer(events, many=True) + serializer = EventSerializer(events, many=True, context={"request": request}) return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/backend/hoagiecalendar/api/user_saved_events_view.py b/backend/hoagiecalendar/api/user_saved_events_view.py new file mode 100644 index 0000000..69a9264 --- /dev/null +++ b/backend/hoagiecalendar/api/user_saved_events_view.py @@ -0,0 +1,60 @@ +from typing import TypedDict, cast + +from rest_framework import serializers, status +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.views import APIView + +from hoagiecalendar.api.user_events_view import EventSerializer +from hoagiecalendar.models import Event + + +class SaveEventRequestSerializer(serializers.Serializer): + event_id = serializers.BigIntegerField() + + +class SaveEventRequestData(TypedDict): + event_id: int + + +class UserSavedEventsView(APIView): + def get(self, request: Request) -> Response: + user = request.user + saved_events = user.saved_events.all() + serializer = EventSerializer(saved_events, many=True, context={"request": request}) + + return Response(serializer.data, status=status.HTTP_200_OK) + + def post(self, request: Request) -> Response: + serializer = SaveEventRequestSerializer(data=request.data) + if not serializer.is_valid(): + return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) + + validated_data = cast(SaveEventRequestData, serializer.validated_data) + + try: + event = Event.objects.get(pk=validated_data["event_id"]) + event.saved_by.add(request.user) + + return Response({"message": "event saved"}, status=status.HTTP_201_CREATED) + except Event.DoesNotExist: + return Response({"error": "event not found"}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({"error": f"an error occured while saving event for user: {e}"}) + + def delete(self, request: Request) -> Response: + serializer = SaveEventRequestSerializer(data=request.data) + if not serializer.is_valid(): + return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) + + validated_data = cast(SaveEventRequestData, serializer.validated_data) + + try: + event = Event.objects.get(pk=validated_data["event_id"]) + event.saved_by.add(request.user) + + return Response({"message": "event unsaved"}, status=status.HTTP_200_OK) + except Event.DoesNotExist: + return Response({"error": "event not found"}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({"error": f"an error occured while unsaving event for user: {e}"}) diff --git a/backend/hoagiecalendar/models/event.py b/backend/hoagiecalendar/models/event.py index 1abcfe4..46f9cff 100644 --- a/backend/hoagiecalendar/models/event.py +++ b/backend/hoagiecalendar/models/event.py @@ -25,6 +25,7 @@ class Event(models.Model): from_mail = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) + saved_by = models.ManyToManyField(User, related_name="saved_events", blank=True) def __str__(self) -> str: return self.name diff --git a/backend/test_models.py b/backend/test_models.py new file mode 100644 index 0000000..e035e18 --- /dev/null +++ b/backend/test_models.py @@ -0,0 +1,36 @@ +from django.test import TestCase + +from hoagiecalendar.api.event_views import EventSerializer +from hoagiecalendar.models.event import Event +from hoagiecalendar.models.user import User + + +# Test creating model and getting attendees count. +class EventModelTestCase(TestCase): + def setUp(self): + self.owner = User.objects.create_user(username="testuser", email="test@hoagie.io", password="testpass") + + self.event = Event.objects.create( + start="2024-01-01T10:00:00Z", + end="2024-01-01T12:00:00Z", + name="Test Event", + location="Test Location", + description="Test Description", + host="Test Host", + owner=self.owner, + ) + + def test_attending_count(self): + event = Event.objects.get(pk=self.event.pk) + + self.assertEqual(event.attending_count(), 0) + + event.attendees.add(self.owner) + + self.assertEqual(event.attending_count(), 1) + + def test_serializing(self): + serializer = EventSerializer(self.event) + deserializer = EventSerializer(data=serializer.data) + + self.assertTrue(deserializer.is_valid(), str(deserializer.errors))