|
116 | 116 | StudentAttemptsSerializer, |
117 | 117 | UserSerializer, |
118 | 118 | UniqueStudentIdentifierSerializer, |
119 | | - ProblemResetSerializer |
| 119 | + ProblemResetSerializer, |
| 120 | + RescoreEntranceExamSerializer |
120 | 121 | ) |
121 | 122 | from openedx.core.djangoapps.content.course_overviews.models import CourseOverview |
122 | 123 | from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted |
@@ -2201,62 +2202,75 @@ def override_problem_score(request, course_id): # lint-amnesty, pylint: disable |
2201 | 2202 | return JsonResponse(response_payload) |
2202 | 2203 |
|
2203 | 2204 |
|
2204 | | -@transaction.non_atomic_requests |
2205 | | -@require_POST |
2206 | | -@ensure_csrf_cookie |
2207 | | -@cache_control(no_cache=True, no_store=True, must_revalidate=True) |
2208 | | -@require_course_permission(permissions.RESCORE_EXAMS) |
2209 | | -@common_exceptions_400 |
2210 | | -def rescore_entrance_exam(request, course_id): |
| 2205 | +@method_decorator(transaction.non_atomic_requests, name='dispatch') |
| 2206 | +class RescoreEntranceExamView(DeveloperErrorViewMixin, APIView): |
2211 | 2207 | """ |
2212 | | - Starts a background process a students attempts counter for entrance exam. |
| 2208 | + Starts a background process for a student's attempts counter for entrance exam. |
2213 | 2209 | Optionally deletes student state for a problem. Limited to instructor access. |
2214 | 2210 |
|
2215 | | - Takes either of the following query parameters |
2216 | | - - unique_student_identifier is an email or username |
2217 | | - - all_students is a boolean |
| 2211 | + Takes either of the following parameters: |
| 2212 | + - unique_student_identifier: an email or username |
| 2213 | + - all_students: a boolean |
2218 | 2214 |
|
2219 | 2215 | all_students and unique_student_identifier cannot both be present. |
2220 | 2216 | """ |
2221 | | - course_id = CourseKey.from_string(course_id) |
2222 | | - course = get_course_with_access( |
2223 | | - request.user, 'staff', course_id, depth=None |
2224 | | - ) |
2225 | | - |
2226 | | - student_identifier = request.POST.get('unique_student_identifier', None) |
2227 | | - only_if_higher = request.POST.get('only_if_higher', None) |
2228 | | - student = None |
2229 | | - if student_identifier is not None: |
2230 | | - student = get_student_from_identifier(student_identifier) |
| 2217 | + permission_classes = (IsAuthenticated, permissions.InstructorPermission) |
| 2218 | + permission_name = permissions.RESCORE_EXAMS |
| 2219 | + serializer_class = RescoreEntranceExamSerializer |
2231 | 2220 |
|
2232 | | - all_students = _get_boolean_param(request, 'all_students') |
| 2221 | + @method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True)) |
| 2222 | + @method_decorator(ensure_csrf_cookie) |
| 2223 | + def post(self, request, course_id): |
| 2224 | + """ |
| 2225 | + Initiates a Celery task to rescore the entrance exam for a student or all students. |
| 2226 | + """ |
| 2227 | + serializer = self.serializer_class(data=request.data) |
| 2228 | + serializer.is_valid(raise_exception=True) |
| 2229 | + data = serializer.validated_data |
2233 | 2230 |
|
2234 | | - if not course.entrance_exam_id: |
2235 | | - return HttpResponseBadRequest( |
2236 | | - _("Course has no entrance exam section.") |
| 2231 | + course_id = CourseKey.from_string(course_id) |
| 2232 | + course = get_course_with_access( |
| 2233 | + request.user, 'staff', course_id, depth=None |
2237 | 2234 | ) |
2238 | 2235 |
|
2239 | | - if all_students and student: |
2240 | | - return HttpResponseBadRequest( |
2241 | | - _("Cannot rescore with all_students and unique_student_identifier.") |
2242 | | - ) |
| 2236 | + if not course.entrance_exam_id: |
| 2237 | + return Response( |
| 2238 | + {"error": _("Course has no entrance exam section.")}, |
| 2239 | + status=status.HTTP_400_BAD_REQUEST |
| 2240 | + ) |
2243 | 2241 |
|
2244 | | - try: |
2245 | | - entrance_exam_key = UsageKey.from_string(course.entrance_exam_id).map_into_course(course_id) |
2246 | | - except InvalidKeyError: |
2247 | | - return HttpResponseBadRequest(_("Course has no valid entrance exam section.")) |
| 2242 | + student_identifier = data.get('unique_student_identifier') |
| 2243 | + only_if_higher = data.get('only_if_higher') |
| 2244 | + all_students = data.get('all_students', False) |
| 2245 | + student = None |
2248 | 2246 |
|
2249 | | - response_payload = {} |
2250 | | - if student: |
2251 | | - response_payload['student'] = student_identifier |
2252 | | - else: |
2253 | | - response_payload['student'] = _("All Students") |
| 2247 | + if student_identifier: |
| 2248 | + student = get_student_from_identifier(student_identifier) |
2254 | 2249 |
|
2255 | | - task_api.submit_rescore_entrance_exam_for_student( |
2256 | | - request, entrance_exam_key, student, only_if_higher, |
2257 | | - ) |
2258 | | - response_payload['task'] = TASK_SUBMISSION_OK |
2259 | | - return JsonResponse(response_payload) |
| 2250 | + if all_students and student: |
| 2251 | + return Response( |
| 2252 | + {"error": _("Cannot rescore with all_students and unique_student_identifier.")}, |
| 2253 | + status=status.HTTP_400_BAD_REQUEST |
| 2254 | + ) |
| 2255 | + |
| 2256 | + try: |
| 2257 | + entrance_exam_key = UsageKey.from_string(course.entrance_exam_id).map_into_course(course_id) |
| 2258 | + except InvalidKeyError: |
| 2259 | + return Response( |
| 2260 | + {"error": _("Course has no valid entrance exam section.")}, |
| 2261 | + status=status.HTTP_400_BAD_REQUEST |
| 2262 | + ) |
| 2263 | + |
| 2264 | + response_payload = { |
| 2265 | + 'student': student_identifier if student else _("All Students"), |
| 2266 | + 'task': TASK_SUBMISSION_OK |
| 2267 | + } |
| 2268 | + |
| 2269 | + task_api.submit_rescore_entrance_exam_for_student( |
| 2270 | + request, entrance_exam_key, student, only_if_higher, |
| 2271 | + ) |
| 2272 | + |
| 2273 | + return Response(response_payload) |
2260 | 2274 |
|
2261 | 2275 |
|
2262 | 2276 | @method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch') |
|
0 commit comments