From 3ebd1fea3bf470a6eb95bd793efd486fa2f6ffe3 Mon Sep 17 00:00:00 2001 From: Veriny <2002.ijustin@gmail.com> Date: Mon, 16 Oct 2023 20:28:24 -0700 Subject: [PATCH 1/2] Prevent students from taking other students' attendances --- csm_web/scheduler/views/student.py | 53 ++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/csm_web/scheduler/views/student.py b/csm_web/scheduler/views/student.py index fc3d29c8..a696376a 100644 --- a/csm_web/scheduler/views/student.py +++ b/csm_web/scheduler/views/student.py @@ -1,16 +1,14 @@ from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q from django.utils import timezone -from scheduler.models import Attendance, SectionOccurrence from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response -import datetime -from .utils import log_str, logger, get_object_or_error from ..models import Student from ..serializers import AttendanceSerializer, StudentSerializer +from .utils import get_object_or_error, logger class StudentViewSet(viewsets.GenericViewSet): @@ -28,13 +26,17 @@ def get_queryset(self): @action(detail=True, methods=["patch"]) def drop(self, request, pk=None): + """ + Drops a student from a section. + Students can drop themselves, and coordinators can drop students from their course. + Mentors are unable to drop students from any course under any circumstances. + Deletes futures attendances for the dropped student. + """ student = get_object_or_error(self.get_queryset(), pk=pk) is_coordinator = student.course.coordinator_set.filter( user=request.user ).exists() if student.user != request.user and not is_coordinator: - # Students can drop themselves, and Coordinators can drop students from their course - # Mentors CANNOT drop their own students, or anyone else for that matter raise PermissionDenied("You do not have permission to drop this student") student.active = False if is_coordinator: @@ -42,10 +44,13 @@ def drop(self, request, pk=None): if student.course.is_restricted and request.data.get("blacklisted", False): student.course.whitelist.remove(student.user) student.save() + logger.info( - f" User {log_str(request.user)} dropped Section {log_str(student.section)} for Student user {log_str(student.user)}" + " User %s dropped SEction %s for Student user %s", + request.user, + student.section, + student.user, ) - # filter attendances and delete future attendances now = timezone.now().astimezone(timezone.get_default_timezone()) num_deleted, _ = student.attendance_set.filter( Q( @@ -54,20 +59,32 @@ def drop(self, request, pk=None): ) ).delete() logger.info( - f" Deleted {num_deleted} attendances for user {log_str(student.user)} in Section {log_str(student.section)} after {now.date()}" + " Deleted %s attendances for user %s in Section %s after %s", + num_deleted, + student.user, + student.section, + now.date(), ) return Response(status=status.HTTP_204_NO_CONTENT) @action(detail=True, methods=["get", "put"]) def attendances(self, request, pk=None): + """ + Take attendance for a student. + """ student = get_object_or_error(self.get_queryset(), pk=pk) + is_mentor = student.course.mentor_set.filter(user=request.user).exists() if request.method == "GET": return Response( AttendanceSerializer(student.attendance_set.all(), many=True).data ) # PUT - if student.user == self.request.user: - raise PermissionDenied("You cannot record your own attendance (nice try)") + if not is_mentor: + raise PermissionDenied( + "You cannot record your own attendance (nice try) or your friend's. But" + " seeing as you're familiar with our API Endpoints, you should apply to" + " work for us!" + ) try: # update attendance = student.attendance_set.get(pk=request.data["id"]) serializer = AttendanceSerializer( @@ -81,18 +98,26 @@ def attendances(self, request, pk=None): ) except ObjectDoesNotExist: logger.error( - f" Could not record attendance for User {log_str(request.user)}, used non-existent attendance id {request.data['id']}" + ( + " Could not record attendance for User %s, used" + " non-existent attendance id %s" + ), + request.user, + request.data["id"], ) return Response(status=status.HTTP_400_BAD_REQUEST) if serializer.is_valid(): attendance = serializer.save() logger.info( - f" Attendance {log_str(attendance)} recorded for User {log_str(request.user)}" + " Attednace %s taken for User %s", + attendance, + request.user, ) return Response(status=status.HTTP_204_NO_CONTENT) logger.error( - f" Could not record attendance for User {log_str(request.user)}, errors: {serializer.errors}" + " Could not record attendance for User %s, errors %s", + request.user, + serializer.errors, ) return Response(serializer.errors, status=status.HTTP_422_UNPROCESSABLE_ENTITY) - From f05ece1f06bbec8c470f087cb88d2ee31772b39e Mon Sep 17 00:00:00 2001 From: Veriny <2002.ijustin@gmail.com> Date: Mon, 13 Nov 2023 19:42:59 -0800 Subject: [PATCH 2/2] fixed spelling mistakes --- csm_web/scheduler/views/student.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/csm_web/scheduler/views/student.py b/csm_web/scheduler/views/student.py index a696376a..7698dcea 100644 --- a/csm_web/scheduler/views/student.py +++ b/csm_web/scheduler/views/student.py @@ -46,7 +46,7 @@ def drop(self, request, pk=None): student.save() logger.info( - " User %s dropped SEction %s for Student user %s", + " User %s dropped Section %s for Student user %s", request.user, student.section, student.user, @@ -110,13 +110,13 @@ def attendances(self, request, pk=None): if serializer.is_valid(): attendance = serializer.save() logger.info( - " Attednace %s taken for User %s", + " Attendance %s taken for User %s", attendance, request.user, ) return Response(status=status.HTTP_204_NO_CONTENT) logger.error( - " Could not record attendance for User %s, errors %s", + " Could not record attendance for User %s, errors %s", request.user, serializer.errors, )