From 460b68977a632e7a40dbfee9441502122cf4a501 Mon Sep 17 00:00:00 2001 From: monicataccoli Date: Sat, 16 May 2026 00:01:34 +1000 Subject: [PATCH 1/4] modified events/views.py --- workshopnav/events/views.py | 194 +++++++++++++++++++++++------------- 1 file changed, 125 insertions(+), 69 deletions(-) diff --git a/workshopnav/events/views.py b/workshopnav/events/views.py index acbdbd6..1a772ce 100644 --- a/workshopnav/events/views.py +++ b/workshopnav/events/views.py @@ -1,4 +1,4 @@ -#from django.shortcuts import render +# from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status, permissions @@ -6,7 +6,16 @@ from django.db.models import Count from .models import Event, Poll, PollOption, Question, Feedback, EmailCapture -from .serializers import EventSerializer, PollSerializer, PollResponseSerializer, PollOptionSerializer, QuestionSerializer, FeedbackSerializer, EmailCaptureSerializer +from .serializers import ( + EventSerializer, + PollSerializer, + PollResponseSerializer, + PollOptionSerializer, + QuestionSerializer, + FeedbackSerializer, + EmailCaptureSerializer, +) + class EventOwnerMixin: def is_event_owner(self, event, user): @@ -14,11 +23,17 @@ def is_event_owner(self, event, user): class EventListCreateView(APIView): serializer_class = EventSerializer - permission_classes = [permissions.IsAuthenticated] + permission_classes = [permissions.AllowAny] def get_queryset(self): + return Event.objects.filter(owner=self.request.user) + + def is_event_owner(self, event, user): + return event.owner == user + + # Get all events owned by the authenticated user def get(self, request): events = self.get_queryset() @@ -75,27 +90,43 @@ def get(self, request, code): if not self.is_event_owner(event, request.user): return Response({"error": "You do not have permission to view this event."}, status=status.HTTP_403_FORBIDDEN) + +# Get event by code (public view, but edit/delete restricted to owner) +class EventByCodeView(APIView): + permission_classes = [permissions.AllowAny] + + def get(self, request, code): + event = get_object_or_404(Event, event_code__iexact=code) + + serializer = EventSerializer(event) return Response(serializer.data, status=status.HTTP_200_OK) - + def put(self, request, code): - + if not request.user.is_authenticated: - return Response({"error": "Authentication required."}, status=status.HTTP_401_UNAUTHORIZED) - + return Response( + {"error": "Authentication required."}, + status=status.HTTP_401_UNAUTHORIZED, + ) + event = get_object_or_404(Event, event_code__iexact=code) - if not self.is_event_owner(event, request.user): - return Response({"error": "You do not have permission to edit this event."}, status=status.HTTP_403_FORBIDDEN) - + return Response( + {"error": "You do not have permission to edit this event."}, + status=status.HTTP_403_FORBIDDEN, + ) + + serializer = EventSerializer(event, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - + def delete(self, request, code): if not request.user.is_authenticated: + return Response({"error": "Authentication required."}, status=status.HTTP_401_UNAUTHORIZED) event = get_object_or_404(Event, event_code__iexact=code) @@ -106,16 +137,16 @@ def delete(self, request, code): event.delete() return Response({"message": "Event deleted successfully."}, status=status.HTTP_204_NO_CONTENT) -#polls for an event -#GET /events/{event_id}/polls/ - get all polls for the event (public) -#POST /events/{event_id}/polls/ - create a new poll (event owner only) +# polls for an event +# GET /events/{event_id}/polls/ - get all polls for the event (public) +# POST /events/{event_id}/polls/ - create a new poll (event owner only) class PollListCreateView(APIView): permission_classes = [permissions.IsAuthenticated] def get(self, request, event_id): event = get_object_or_404(Event, id=event_id) - + polls = event.polls.all() serializer = PollSerializer(polls, many=True) return Response(serializer.data) @@ -127,118 +158,127 @@ def post(self, request, event_id): if event.owner != request.user: return Response( {"error": "Only the event owner can create polls."}, - status=status.HTTP_403_FORBIDDEN) - + status=status.HTTP_403_FORBIDDEN, + ) + serializer = PollSerializer(data=request.data) if serializer.is_valid(): serializer.save(event=event) return Response(serializer.data, status=status.HTTP_201_CREATED) - + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + # update & delete polls class PollDetailView(APIView): permission_classes = [permissions.IsAuthenticated] def get(self, request, poll_id): poll = get_object_or_404(Poll, id=poll_id) - + serializer = PollSerializer(poll) return Response(serializer.data) def put(self, request, poll_id): poll = get_object_or_404(Poll, id=poll_id) - + # Only event owner can update polls if poll.event.owner != request.user: return Response( {"error": "Only the event owner can update this poll."}, - status=status.HTTP_403_FORBIDDEN) - + status=status.HTTP_403_FORBIDDEN, + ) + serializer = PollSerializer(poll, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data) - + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, poll_id): poll = get_object_or_404(Poll, id=poll_id) - + # Only event owner can delete polls if poll.event.owner != request.user: return Response( {"error": "Only the event owner can delete this poll."}, - status=status.HTTP_403_FORBIDDEN) - + status=status.HTTP_403_FORBIDDEN, + ) + poll.delete() - return Response({"message": "Poll deleted successfully."}, status=status.HTTP_204_NO_CONTENT) + return Response( + {"message": "Poll deleted successfully."}, status=status.HTTP_204_NO_CONTENT + ) - -#POST /polls/{poll_id}/responses/ - submit a response to a poll + +# POST /polls/{poll_id}/responses/ - submit a response to a poll class PollResponseCreateView(APIView): def get(self, request, poll_id): poll = get_object_or_404(Poll, id=poll_id) - + self.responses = poll.responses.all() serializer = PollResponseSerializer(self.responses, many=True) return Response(serializer.data) - def post(self, request, poll_id): poll = get_object_or_404(Poll, id=poll_id) - + if not poll.is_active: return Response( - {"error": "Poll is not active."}, - status=status.HTTP_400_BAD_REQUEST) - - serializer = PollResponseSerializer(data=request.data, context={'poll': poll}) + {"error": "Poll is not active."}, status=status.HTTP_400_BAD_REQUEST + ) + + serializer = PollResponseSerializer(data=request.data, context={"poll": poll}) if serializer.is_valid(): serializer.save(poll=poll) return Response(serializer.data, status=status.HTTP_201_CREATED) - + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - -#GET /polls/{poll_id}/results/ - get the results of a poll (number of responses for each option) + + +# GET /polls/{poll_id}/results/ - get the results of a poll (number of responses for each option) class PollResultsView(APIView): def get(self, request, poll_id): poll = get_object_or_404(Poll, id=poll_id) - + # options = poll.options.all() # results = {} # for option in options: # results[option.option_text] = option.responses.count() # return Response(results) - options = PollOption.objects.filter(poll_id=poll_id).annotate(count=Count('responses')) + options = PollOption.objects.filter(poll_id=poll_id).annotate( + count=Count("responses") + ) total = sum(option.count for option in options) options_data = [ { - 'id': option.id, - 'option_text': option.option_text, - 'count': option.count, - 'percentage': (option.count / total * 100) if total > 0 else 0 + "id": option.id, + "option_text": option.option_text, + "count": option.count, + "percentage": (option.count / total * 100) if total > 0 else 0, } for option in options ] return Response(options_data) -#Ask questions in an event -#POST /questions/{question_id}/ + +# Ask questions in an event +# POST /questions/{question_id}/ class QuestionListCreateView(APIView): def get(self, request, event_id): event = get_object_or_404(Event, id=event_id) - + questions = event.questions.all() serializer = QuestionSerializer(questions, many=True) return Response(serializer.data) @@ -252,58 +292,73 @@ def post(self, request, event_id): serializer.save(event=event) return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - -#POST /questions/{question_id}/upvote/ - upvote a question + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +# POST /questions/{question_id}/upvote/ - upvote a question class QuestionUpvoteView(APIView): def get(self, request, question_id): question = get_object_or_404(Question, id=question_id) - + serializer = QuestionSerializer(question) return Response(serializer.data, status=status.HTTP_200_OK) def patch(self, request, question_id): question = get_object_or_404(Question, id=question_id) - + question.upvotes += 1 question.save() serializer = QuestionSerializer(question) - return Response(serializer.data | {"message": "Upvoted successfully"}, status=status.HTTP_200_OK) - + return Response( + serializer.data | {"message": "Upvoted successfully"}, + status=status.HTTP_200_OK, + ) + + class QuestionVisibilityView(APIView): def post(self, request, question_id): question = get_object_or_404(Question, id=question_id) - + question.visible = not question.visible question.save() serializer = QuestionSerializer(question) - return Response(serializer.data | {"message": f"Question is now {'visible' if question.visible else 'hidden'}"}, status=status.HTTP_200_OK) -#POST /events/:id/feedback/ -> User submits feedback + return Response( + serializer.data + | { + "message": f"Question is now {'visible' if question.visible else 'hidden'}" + }, + status=status.HTTP_200_OK, + ) + + +# POST /events/:id/feedback/ -> User submits feedback + class FeedbackCreateView(APIView): -#GET feedback summary + # GET feedback summary def get(self, request, event_id): event = get_object_or_404(Event, id=event_id) feedback = event.feedback.all() serializer = FeedbackSerializer(feedback, many=True) return Response(serializer.data, status=status.HTTP_200_OK) -#POST /events/:id/feedback/ -> User submits feedback + # POST /events/:id/feedback/ -> User submits feedback def post(self, request, event_id): event = get_object_or_404(Event, id=event_id) - + serializer = FeedbackSerializer(data=request.data) if serializer.is_valid(): serializer.save(event=event) return Response(serializer.data, status=status.HTTP_201_CREATED) - + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -#POST /events/{event_id}/open-feedback/ -> Facilitator opens feedback at end of session + +# POST /events/{event_id}/open-feedback/ -> Facilitator opens feedback at end of session class OpenFeedbackView(APIView): def post(self, request, event_id): @@ -314,20 +369,21 @@ def post(self, request, event_id): return Response( { "message": "Feedback opened successfully.", - "feedback_open": event.feedback_open + "feedback_open": event.feedback_open, }, - status=status.HTTP_200_OK + status=status.HTTP_200_OK, ) + class EmailCaptureCreateView(APIView): -#Get collected emails endpoint + # Get collected emails endpoint def get(self, request, event_id): event = get_object_or_404(Event, id=event_id) emails = event.emails.all() serializer = EmailCaptureSerializer(emails, many=True) return Response(serializer.data, status=status.HTTP_200_OK) -#POST /events/:id/emails/ -> User submits email + # POST /events/:id/emails/ -> User submits email def post(self, request, event_id): event = get_object_or_404(Event, id=event_id) email = request.data.get("email") @@ -335,7 +391,7 @@ def post(self, request, event_id): if EmailCapture.objects.filter(event=event, email=email).exists(): return Response( {"error": "This email has already been submitted for this event."}, - status=status.HTTP_400_BAD_REQUEST + status=status.HTTP_400_BAD_REQUEST, ) serializer = EmailCaptureSerializer(data=request.data) @@ -344,4 +400,4 @@ def post(self, request, event_id): serializer.save(event=event) return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) From 3166dbc3aa5b9d6fac17470e167bec8222e72ad1 Mon Sep 17 00:00:00 2001 From: monicataccoli Date: Sun, 17 May 2026 23:39:51 +1000 Subject: [PATCH 2/4] modified events/urls.py, events/views.py --- workshopnav/events/urls.py | 40 ++++--- workshopnav/events/views.py | 206 ++++++++++++++++++------------------ 2 files changed, 126 insertions(+), 120 deletions(-) diff --git a/workshopnav/events/urls.py b/workshopnav/events/urls.py index 905ca1e..61a1a43 100644 --- a/workshopnav/events/urls.py +++ b/workshopnav/events/urls.py @@ -1,18 +1,28 @@ -from django.urls import path -from . import views - urlpatterns = [ - path('events/', views.EventListCreateView.as_view()), - path('events//', views.EventDetailView.as_view()), - path('events/join//', views.AttendeeEventByCodeView.as_view()), - path('events//', views.FacilitatorEventDetailView.as_view()), - path('events//polls/', views.PollListCreateView.as_view()), - path('polls//', views.PollDetailView.as_view()), - path('polls//responses/', views.PollResponseCreateView.as_view()), - path('polls//results/', views.PollResultsView.as_view()), - path('events//questions/', views.QuestionListCreateView.as_view()), - path('questions//upvote/', views.QuestionUpvoteView.as_view()), + path("events/", views.EventListCreateView.as_view()), + path("events//", views.EventDetailView.as_view()), + + # attendee join flow + path("events/join//", views.AttendeeEventByCodeView.as_view()), + + # facilitator / event code routes + path("events//", views.EventByCodeView.as_view()), + + # polls + path("events//polls/", views.PollListCreateView.as_view()), + path("polls//", views.PollDetailView.as_view()), + path("polls//responses/", views.PollResponseCreateView.as_view()), + path("polls//results/", views.PollResultsView.as_view()), + + # questions + path("events//questions/", views.QuestionListCreateView.as_view()), + path("questions//upvote/", views.QuestionUpvoteView.as_view()), + + # feedback path("events//feedback/", views.FeedbackCreateView.as_view()), - path('events//open-feedback/', views.OpenFeedbackView.as_view()), + path("events//open-feedback/", views.OpenFeedbackView.as_view()), + + # emails path("events//emails/", views.EmailCaptureCreateView.as_view()), -] \ No newline at end of file +] + \ No newline at end of file diff --git a/workshopnav/events/views.py b/workshopnav/events/views.py index 1a772ce..491d0af 100644 --- a/workshopnav/events/views.py +++ b/workshopnav/events/views.py @@ -1,4 +1,3 @@ -# from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status, permissions @@ -10,7 +9,6 @@ EventSerializer, PollSerializer, PollResponseSerializer, - PollOptionSerializer, QuestionSerializer, FeedbackSerializer, EmailCaptureSerializer, @@ -21,84 +19,62 @@ class EventOwnerMixin: def is_event_owner(self, event, user): return event.owner == user + +# EVENTS class EventListCreateView(APIView): - serializer_class = EventSerializer permission_classes = [permissions.AllowAny] def get_queryset(self): + return Event.objects.all() - return Event.objects.filter(owner=self.request.user) - - - def is_event_owner(self, event, user): - return event.owner == user - - - # Get all events owned by the authenticated user def get(self, request): events = self.get_queryset() serializer = EventSerializer(events, many=True) return Response(serializer.data) - # Create a new event def post(self, request): serializer = EventSerializer(data=request.data) + if serializer.is_valid(): - serializer.save(owner=request.user) + + if request.user.is_authenticated: + serializer.save(owner=request.user) + else: + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - #updtae & delete events -class EventDetailView(EventOwnerMixin, APIView): - def put(self, request, event_id): - event = get_object_or_404(Event, id=event_id) - if not(self.is_event_owner(event, request.user)): - return Response({"error": "You do not have permission to edit this event."}, status=status.HTTP_403_FORBIDDEN) - - serializer = EventSerializer(event, data=request.data, partial=True) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - def delete(self, request, event_id): - event = get_object_or_404(Event, id=event_id) - if not self.is_event_owner(event, request.user): - return Response({"error": "You do not have permission to delete this event."}, status=status.HTTP_403_FORBIDDEN) +class EventDetailView(APIView): + permission_classes = [permissions.AllowAny] - event.delete() - return Response({"message": "Event deleted successfully."}, status=status.HTTP_204_NO_CONTENT) + def get(self, request, pk): + event = get_object_or_404(Event, pk=pk) -# Get event by code (public view, but edit/delete restricted to owner)--Attendee + serializer = EventSerializer(event) + return Response(serializer.data, status=status.HTTP_200_OK) + + +# ATTENDEE EVENT VIEW class AttendeeEventByCodeView(APIView): permission_classes = [permissions.AllowAny] + def get(self, request, code): event = get_object_or_404(Event, event_code__iexact=code) - + serializer = EventSerializer(event) return Response(serializer.data, status=status.HTTP_200_OK) - -#Get event by code (owner view with edit/delete permissions)--Facilitator -class FacilitatorEventDetailView(EventOwnerMixin, APIView): - permission_classes = [permissions.IsAuthenticated] - def get(self, request, code): - event = get_object_or_404(Event, event_code__iexact=code) - - if not self.is_event_owner(event, request.user): - return Response({"error": "You do not have permission to view this event."}, status=status.HTTP_403_FORBIDDEN) - -# Get event by code (public view, but edit/delete restricted to owner) -class EventByCodeView(APIView): +# FACILITATOR EVENT VIEW +class EventByCodeView(EventOwnerMixin, APIView): permission_classes = [permissions.AllowAny] def get(self, request, code): event = get_object_or_404(Event, event_code__iexact=code) - serializer = EventSerializer(event) return Response(serializer.data, status=status.HTTP_200_OK) @@ -111,35 +87,46 @@ def put(self, request, code): ) event = get_object_or_404(Event, event_code__iexact=code) + if not self.is_event_owner(event, request.user): return Response( {"error": "You do not have permission to edit this event."}, status=status.HTTP_403_FORBIDDEN, - ) - + ) serializer = EventSerializer(event, data=request.data, partial=True) + if serializer.is_valid(): serializer.save() return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, code): + if not request.user.is_authenticated: + return Response( + {"error": "Authentication required."}, + status=status.HTTP_401_UNAUTHORIZED, + ) - return Response({"error": "Authentication required."}, status=status.HTTP_401_UNAUTHORIZED) - event = get_object_or_404(Event, event_code__iexact=code) - + if not self.is_event_owner(event, request.user): - return Response({"error": "You do not have permission to delete this event."}, status=status.HTTP_403_FORBIDDEN) - + return Response( + {"error": "You do not have permission to delete this event."}, + status=status.HTTP_403_FORBIDDEN, + ) + event.delete() - return Response({"message": "Event deleted successfully."}, status=status.HTTP_204_NO_CONTENT) -# polls for an event -# GET /events/{event_id}/polls/ - get all polls for the event (public) -# POST /events/{event_id}/polls/ - create a new poll (event owner only) + return Response( + {"message": "Event deleted successfully."}, + status=status.HTTP_204_NO_CONTENT, + ) + + +# POLLS class PollListCreateView(APIView): permission_classes = [permissions.IsAuthenticated] @@ -149,12 +136,13 @@ def get(self, request, event_id): polls = event.polls.all() serializer = PollSerializer(polls, many=True) + return Response(serializer.data) def post(self, request, event_id): + event = get_object_or_404(Event, id=event_id) - # Only event owner can create polls if event.owner != request.user: return Response( {"error": "Only the event owner can create polls."}, @@ -165,25 +153,27 @@ def post(self, request, event_id): if serializer.is_valid(): serializer.save(event=event) + return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# update & delete polls class PollDetailView(APIView): permission_classes = [permissions.IsAuthenticated] def get(self, request, poll_id): + poll = get_object_or_404(Poll, id=poll_id) serializer = PollSerializer(poll) + return Response(serializer.data) def put(self, request, poll_id): + poll = get_object_or_404(Poll, id=poll_id) - # Only event owner can update polls if poll.event.owner != request.user: return Response( {"error": "Only the event owner can update this poll."}, @@ -194,14 +184,15 @@ def put(self, request, poll_id): if serializer.is_valid(): serializer.save() + return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, poll_id): + poll = get_object_or_404(Poll, id=poll_id) - # Only event owner can delete polls if poll.event.owner != request.user: return Response( {"error": "Only the event owner can delete this poll."}, @@ -209,49 +200,53 @@ def delete(self, request, poll_id): ) poll.delete() + return Response( - {"message": "Poll deleted successfully."}, status=status.HTTP_204_NO_CONTENT + {"message": "Poll deleted successfully."}, + status=status.HTTP_204_NO_CONTENT, ) -# POST /polls/{poll_id}/responses/ - submit a response to a poll +# POLL RESPONSES class PollResponseCreateView(APIView): def get(self, request, poll_id): + poll = get_object_or_404(Poll, id=poll_id) - self.responses = poll.responses.all() - serializer = PollResponseSerializer(self.responses, many=True) + responses = poll.responses.all() + + serializer = PollResponseSerializer(responses, many=True) + return Response(serializer.data) def post(self, request, poll_id): + poll = get_object_or_404(Poll, id=poll_id) if not poll.is_active: return Response( - {"error": "Poll is not active."}, status=status.HTTP_400_BAD_REQUEST + {"error": "Poll is not active."}, + status=status.HTTP_400_BAD_REQUEST, ) - serializer = PollResponseSerializer(data=request.data, context={"poll": poll}) + serializer = PollResponseSerializer( + data=request.data, + context={"poll": poll}, + ) if serializer.is_valid(): serializer.save(poll=poll) + return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# GET /polls/{poll_id}/results/ - get the results of a poll (number of responses for each option) +# POLL RESULTS class PollResultsView(APIView): def get(self, request, poll_id): - poll = get_object_or_404(Poll, id=poll_id) - - # options = poll.options.all() - # results = {} - # for option in options: - # results[option.option_text] = option.responses.count() - # return Response(results) options = PollOption.objects.filter(poll_id=poll_id).annotate( count=Count("responses") @@ -272,97 +267,91 @@ def get(self, request, poll_id): return Response(options_data) -# Ask questions in an event -# POST /questions/{question_id}/ +# QUESTIONS class QuestionListCreateView(APIView): def get(self, request, event_id): + event = get_object_or_404(Event, id=event_id) questions = event.questions.all() + serializer = QuestionSerializer(questions, many=True) + return Response(serializer.data) def post(self, request, event_id): + event = get_object_or_404(Event, id=event_id) serializer = QuestionSerializer(data=request.data) if serializer.is_valid(): serializer.save(event=event) + return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# POST /questions/{question_id}/upvote/ - upvote a question class QuestionUpvoteView(APIView): def get(self, request, question_id): + question = get_object_or_404(Question, id=question_id) serializer = QuestionSerializer(question) + return Response(serializer.data, status=status.HTTP_200_OK) def patch(self, request, question_id): + question = get_object_or_404(Question, id=question_id) question.upvotes += 1 question.save() - serializer = QuestionSerializer(question) - return Response( - serializer.data | {"message": "Upvoted successfully"}, - status=status.HTTP_200_OK, - ) - -class QuestionVisibilityView(APIView): - - def post(self, request, question_id): - question = get_object_or_404(Question, id=question_id) - - question.visible = not question.visible - question.save() serializer = QuestionSerializer(question) + return Response( - serializer.data - | { - "message": f"Question is now {'visible' if question.visible else 'hidden'}" - }, + serializer.data | {"message": "Upvoted successfully"}, status=status.HTTP_200_OK, ) -# POST /events/:id/feedback/ -> User submits feedback - - +# FEEDBACK class FeedbackCreateView(APIView): - # GET feedback summary def get(self, request, event_id): + event = get_object_or_404(Event, id=event_id) + feedback = event.feedback.all() + serializer = FeedbackSerializer(feedback, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) - # POST /events/:id/feedback/ -> User submits feedback def post(self, request, event_id): + event = get_object_or_404(Event, id=event_id) serializer = FeedbackSerializer(data=request.data) if serializer.is_valid(): serializer.save(event=event) + return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# POST /events/{event_id}/open-feedback/ -> Facilitator opens feedback at end of session class OpenFeedbackView(APIView): def post(self, request, event_id): + event = get_object_or_404(Event, id=event_id) + event.feedback_open = True event.save() @@ -375,17 +364,23 @@ def post(self, request, event_id): ) +# EMAIL CAPTURE class EmailCaptureCreateView(APIView): - # Get collected emails endpoint + def get(self, request, event_id): + event = get_object_or_404(Event, id=event_id) + emails = event.emails.all() + serializer = EmailCaptureSerializer(emails, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) - # POST /events/:id/emails/ -> User submits email def post(self, request, event_id): + event = get_object_or_404(Event, id=event_id) + email = request.data.get("email") if EmailCapture.objects.filter(event=event, email=email).exists(): @@ -398,6 +393,7 @@ def post(self, request, event_id): if serializer.is_valid(): serializer.save(event=event) + return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file From 2c15e405f08e96f378520a83fdb373652b84a8ab Mon Sep 17 00:00:00 2001 From: monicataccoli Date: Mon, 18 May 2026 01:45:42 +1000 Subject: [PATCH 3/4] Fix event API integration and event details flow --- workshopnav/events/urls.py | 3 +++ workshopnav/events/views.py | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/workshopnav/events/urls.py b/workshopnav/events/urls.py index 61a1a43..062cd34 100644 --- a/workshopnav/events/urls.py +++ b/workshopnav/events/urls.py @@ -1,3 +1,6 @@ +from django.urls import path +from . import views + urlpatterns = [ path("events/", views.EventListCreateView.as_view()), path("events//", views.EventDetailView.as_view()), diff --git a/workshopnav/events/views.py b/workshopnav/events/views.py index 491d0af..7fb572a 100644 --- a/workshopnav/events/views.py +++ b/workshopnav/events/views.py @@ -26,6 +26,16 @@ class EventListCreateView(APIView): def get_queryset(self): return Event.objects.all() +<<<<<<< HEAD +======= + + def is_event_owner(self, event, user): + return event.owner == user + + # Get all events owned by the authenticated user + + # Get all events +>>>>>>> b4a5b6d (Fix event API integration and event details flow) def get(self, request): events = self.get_queryset() From 44d144af8ab9bb34510e133621a4ae0d51684f5e Mon Sep 17 00:00:00 2001 From: monicataccoli Date: Mon, 18 May 2026 13:53:14 +1000 Subject: [PATCH 4/4] Fix event creation owner constraint issue --- .../migrations/0012_alter_event_owner.py | 21 +++++++++ workshopnav/events/models.py | 46 ++++++++++++------- 2 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 workshopnav/events/migrations/0012_alter_event_owner.py diff --git a/workshopnav/events/migrations/0012_alter_event_owner.py b/workshopnav/events/migrations/0012_alter_event_owner.py new file mode 100644 index 0000000..625aca1 --- /dev/null +++ b/workshopnav/events/migrations/0012_alter_event_owner.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1 on 2026-05-18 03:48 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0011_merge_20260513_2203'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='owner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='owned_events', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/workshopnav/events/models.py b/workshopnav/events/models.py index 04033e7..9049e01 100644 --- a/workshopnav/events/models.py +++ b/workshopnav/events/models.py @@ -4,53 +4,67 @@ from django.contrib.auth import get_user_model from django.conf import settings + # auto-generate a unique 6-character code for each event def generate_code(): return uuid.uuid4().hex[:6].upper() + # Event model with title, unique event code, and creation timestamp class Event(models.Model): title = models.CharField(max_length=255) event_code = models.CharField(max_length=10, unique=True, default=generate_code) created_at = models.DateTimeField(auto_now_add=True) - owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owned_events', on_delete=models.CASCADE) + owner = models.ForeignKey( + settings.AUTH_USER_MODEL, + related_name="owned_events", + on_delete=models.CASCADE, + null=True, + blank=True, + ) feedback_open = models.BooleanField(default=False) is_active = models.BooleanField(default=True) def __str__(self): return f"{self.title} ({self.event_code})" + # Poll model linked to an event, with a question, creation timestamp, and active status class Poll(models.Model): - event = models.ForeignKey(Event, related_name='polls', on_delete=models.CASCADE) + event = models.ForeignKey(Event, related_name="polls", on_delete=models.CASCADE) question = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) is_active = models.BooleanField(default=False) def __str__(self): return f"Poll: {self.question} for Event: {self.event.title}" - + + # response model linked to a poll, with a response text and creation timestamp class PollResponse(models.Model): - poll = models.ForeignKey(Poll, related_name='responses', on_delete=models.CASCADE) - option = models.ForeignKey('PollOption', related_name='responses', on_delete=models.CASCADE) + poll = models.ForeignKey(Poll, related_name="responses", on_delete=models.CASCADE) + option = models.ForeignKey( + "PollOption", related_name="responses", on_delete=models.CASCADE + ) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Response: {self.option.option_text} for Poll: {self.poll.question}" + # options model linked to a poll, with an option text and creation timestamp class PollOption(models.Model): - poll = models.ForeignKey(Poll, related_name='options', on_delete=models.CASCADE) + poll = models.ForeignKey(Poll, related_name="options", on_delete=models.CASCADE) option_text = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Option: {self.option_text} for Poll: {self.poll.question}" + # Question model linked to an event, with question text, visibility, upvotes, and creation timestamp class Question(models.Model): - event = models.ForeignKey(Event, related_name='questions', on_delete=models.CASCADE) + event = models.ForeignKey(Event, related_name="questions", on_delete=models.CASCADE) question_text = models.TextField() created_at = models.DateTimeField(auto_now_add=True) anonymous = models.BooleanField(default=False) @@ -63,26 +77,26 @@ def __str__(self): # Feedback model class Feedback(models.Model): - #Links feedback to specific event + # Links feedback to specific event event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="feedback") - #rating score + # rating score rating = models.IntegerField() - #optional comment field + # optional comment field comment = models.TextField(blank=True) - #auto stores when the feedback is created + # auto stores when the feedback is created created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Feedback for {self.event.title}" -#Email capture model +# Email capture model class EmailCapture(models.Model): - #Links email to an event + # Links email to an event event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="emails") - #Stores user's email + # Stores user's email email = models.EmailField() - #When email was submitted + # When email was submitted created_at = models.DateTimeField(auto_now_add=True) # Prevent the same email being submitted twice for the same event @@ -90,4 +104,4 @@ class Meta: unique_together = ("event", "email") def __str__(self): - return self.email \ No newline at end of file + return self.email