diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 7ca1e70467b0..1aa40b5e3376 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -105,7 +105,9 @@ from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError from lms.djangoapps.instructor_task.data import InstructorTaskTypes from lms.djangoapps.instructor_task.models import ReportStore -from lms.djangoapps.instructor.views.serializer import RoleNameSerializer, UserSerializer, AccessSerializer +from lms.djangoapps.instructor.views.serializer import ( + AccessSerializer, RoleNameSerializer, ShowStudentExtensionSerializer, UserSerializer +) from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted from openedx.core.djangoapps.course_groups.models import CourseUserGroup @@ -2979,20 +2981,50 @@ def show_unit_extensions(request, course_id): return JsonResponse(dump_block_extensions(course, unit)) -@handle_dashboard_error -@require_POST -@ensure_csrf_cookie -@cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_course_permission(permissions.GIVE_STUDENT_EXTENSION) -@require_post_params('student') -def show_student_extensions(request, course_id): +@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch') +class ShowStudentExtensions(APIView): """ Shows all of the due date extensions granted to a particular student in a particular course. """ - student = require_student_from_identifier(request.POST.get('student')) - course = get_course_by_id(CourseKey.from_string(course_id)) - return JsonResponse(dump_student_extensions(course, student)) + permission_classes = (IsAuthenticated, permissions.InstructorPermission) + serializer_class = ShowStudentExtensionSerializer + permission_name = permissions.GIVE_STUDENT_EXTENSION + + @method_decorator(ensure_csrf_cookie) + def post(self, request, course_id): + """ + Handles POST requests to retrieve due date extensions for a specific student + within a specified course. + + Parameters: + - `request`: The HTTP request object containing user-submitted data. + - `course_id`: The ID of the course for which the extensions are being queried. + + Data expected in the request: + - `student`: A required field containing the identifier of the student for whom + the due date extensions are being retrieved. This data is extracted from the + request body. + + Returns: + - A JSON response containing the details of the due date extensions granted to + the specified student in the specified course. + """ + data = { + 'student': request.data.get('student') + } + serializer_data = self.serializer_class(data=data) + + if not serializer_data.is_valid(): + return HttpResponseBadRequest(reason=serializer_data.errors) + + student = serializer_data.validated_data.get('student') + if not student: + response_payload = f'Could not find student matching identifier: {request.data.get("student")}' + return JsonResponse({'error': response_payload}, status=400) + + course = get_course_by_id(CourseKey.from_string(course_id)) + return Response(dump_student_extensions(course, student)) def _split_input_list(str_list): diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py index c0e12e46756d..18da0b63b218 100644 --- a/lms/djangoapps/instructor/views/api_urls.py +++ b/lms/djangoapps/instructor/views/api_urls.py @@ -53,7 +53,7 @@ path('change_due_date', api.change_due_date, name='change_due_date'), path('reset_due_date', api.reset_due_date, name='reset_due_date'), path('show_unit_extensions', api.show_unit_extensions, name='show_unit_extensions'), - path('show_student_extensions', api.show_student_extensions, name='show_student_extensions'), + path('show_student_extensions', api.ShowStudentExtensions.as_view(), name='show_student_extensions'), # proctored exam downloads... path('get_proctored_exam_results', api.get_proctored_exam_results, name='get_proctored_exam_results'), diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py index 463f05ad45b8..0697bed6832d 100644 --- a/lms/djangoapps/instructor/views/serializer.py +++ b/lms/djangoapps/instructor/views/serializer.py @@ -59,3 +59,21 @@ def validate_unique_student_identifier(self, value): return None return user + + +class ShowStudentExtensionSerializer(serializers.Serializer): + """ + Serializer for validating and processing the student identifier. + """ + student = serializers.CharField(write_only=True, required=True) + + def validate_student(self, value): + """ + Validate that the student corresponds to an existing user. + """ + try: + user = get_student_from_identifier(value) + except User.DoesNotExist: + return None + + return user