Skip to content

Commit

Permalink
feat!: upgrading api to DRF.
Browse files Browse the repository at this point in the history
  • Loading branch information
awais786 committed Oct 9, 2024
1 parent 8c978c2 commit 487ae50
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 64 deletions.
136 changes: 73 additions & 63 deletions lms/djangoapps/instructor/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@
from lms.djangoapps.instructor_task.models import ReportStore
from lms.djangoapps.instructor.views.serializer import (
AccessSerializer, BlockDueDateSerializer, RoleNameSerializer, ShowStudentExtensionSerializer, UserSerializer,
SendEmailSerializer, StudentAttemptsSerializer, ListInstructorTaskInputSerializer, UniqueStudentIdentifierSerializer
SendEmailSerializer, StudentAttemptsSerializer,
ListInstructorTaskInputSerializer,
UniqueStudentIdentifierSerializer,
ProblemResetSerializer
)
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
Expand Down Expand Up @@ -1987,84 +1990,91 @@ def reset_student_attempts_for_entrance_exam(request, course_id):
return JsonResponse(response_payload)


@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_course_permission(permissions.OVERRIDE_GRADES)
@require_post_params(problem_to_reset="problem urlname to reset")
@common_exceptions_400
def rescore_problem(request, course_id):
@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
@method_decorator(transaction.non_atomic_requests, name='dispatch')
class RescoreProblem(DeveloperErrorViewMixin, APIView):
"""
Starts a background process a students attempts counter. Optionally deletes student state for a problem.
Rescore for all students is limited to instructor access.
"""
permission_classes = (IsAuthenticated, permissions.InstructorPermission)
permission_name = permissions.OVERRIDE_GRADES
serializer_class = ProblemResetSerializer

Takes either of the following query parameters
- problem_to_reset is a urlname of a problem
- unique_student_identifier is an email or username
- all_students is a boolean
@method_decorator(ensure_csrf_cookie)
@method_decorator(transaction.non_atomic_requests)
def post(self, request, course_id):
"""
Takes either of the following query parameters
- problem_to_reset is a urlname of a problem
- unique_student_identifier is an email or username
- all_students is a boolean
all_students and unique_student_identifier cannot both be present.
"""
course_id = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'staff', course_id)
all_students = _get_boolean_param(request, 'all_students')
all_students and unique_student_identifier cannot both be present.
"""
course_id = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'staff', course_id)

if all_students and not has_access(request.user, 'instructor', course):
return HttpResponseForbidden("Requires instructor access.")
serializer_data = self.serializer_class(data=request.data)
student = None

only_if_higher = _get_boolean_param(request, 'only_if_higher')
problem_to_reset = strip_if_string(request.POST.get('problem_to_reset'))
student_identifier = request.POST.get('unique_student_identifier', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
if not serializer_data.is_valid():
return HttpResponseBadRequest(reason=serializer_data.errors)

if not (problem_to_reset and (all_students or student)):
return HttpResponseBadRequest("Missing query parameters.")
problem_to_reset = serializer_data.validated_data.get("problem_to_reset")
all_students = serializer_data.validated_data.get("all_students")
only_if_higher = serializer_data.validated_data.get("only_if_higher")

if all_students and student:
return HttpResponseBadRequest(
"Cannot rescore with all_students and unique_student_identifier."
)
if all_students and not has_access(request.user, 'instructor', course):
return HttpResponseForbidden("Requires instructor access.")

try:
module_state_key = UsageKey.from_string(problem_to_reset).map_into_course(course_id)
except InvalidKeyError:
return HttpResponseBadRequest("Unable to parse problem id")
student_identifier = serializer_data.validated_data.get("unique_student_identifier")

response_payload = {'problem_to_reset': problem_to_reset}
if not (problem_to_reset and (all_students or student)):
return HttpResponseBadRequest("Missing query parameters.")

if student:
response_payload['student'] = student_identifier
try:
task_api.submit_rescore_problem_for_student(
request,
module_state_key,
student,
only_if_higher,
if all_students and student:
return HttpResponseBadRequest(
"Cannot rescore with all_students and unique_student_identifier."
)
except NotImplementedError as exc:
return HttpResponseBadRequest(str(exc))
except ItemNotFoundError as exc:
return HttpResponseBadRequest(f"{module_state_key} not found")

elif all_students:
try:
task_api.submit_rescore_problem_for_all_students(
request,
module_state_key,
only_if_higher,
)
except NotImplementedError as exc:
return HttpResponseBadRequest(str(exc))
except ItemNotFoundError as exc:
return HttpResponseBadRequest(f"{module_state_key} not found")
else:
return HttpResponseBadRequest()
module_state_key = UsageKey.from_string(problem_to_reset).map_into_course(course_id)
except InvalidKeyError:
return HttpResponseBadRequest("Unable to parse problem id")

response_payload['task'] = TASK_SUBMISSION_OK
return JsonResponse(response_payload)
response_payload = {'problem_to_reset': problem_to_reset}

if student:
response_payload['student'] = student_identifier
try:
task_api.submit_rescore_problem_for_student(
request,
module_state_key,
student,
only_if_higher,
)
except NotImplementedError as exc:
return HttpResponseBadRequest(str(exc))
except ItemNotFoundError as exc:
return HttpResponseBadRequest(f"{module_state_key} not found")

elif all_students:
try:
task_api.submit_rescore_problem_for_all_students(
request,
module_state_key,
only_if_higher,
)
except NotImplementedError as exc:
return HttpResponseBadRequest(str(exc))
except ItemNotFoundError as exc:
return HttpResponseBadRequest(f"{module_state_key} not found")
else:
return HttpResponseBadRequest()

response_payload['task'] = TASK_SUBMISSION_OK
return JsonResponse(response_payload)


@transaction.non_atomic_requests
Expand Down
2 changes: 1 addition & 1 deletion lms/djangoapps/instructor/views/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
name="get_student_enrollment_status"),
path('get_student_progress_url', api.StudentProgressUrl.as_view(), name='get_student_progress_url'),
path('reset_student_attempts', api.ResetStudentAttempts.as_view(), name='reset_student_attempts'),
path('rescore_problem', api.rescore_problem, name='rescore_problem'),
path('rescore_problem', api.RescoreProblem.as_view(), name='rescore_problem'),
path('override_problem_score', api.override_problem_score, name='override_problem_score'),
path('reset_student_attempts_for_entrance_exam', api.reset_student_attempts_for_entrance_exam,
name='reset_student_attempts_for_entrance_exam'),
Expand Down
17 changes: 17 additions & 0 deletions lms/djangoapps/instructor/views/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,20 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if disable_due_datetime:
self.fields['due_datetime'].required = False


class ProblemResetSerializer(UniqueStudentIdentifierSerializer):
problem_to_reset = serializers.CharField(
help_text=_("The URL name of the problem to reset."),
error_messages={
'blank': _("Problem URL name cannot be blank."),
}
)
all_students = serializers.BooleanField(
default=False,
help_text=_("Whether to reset the problem for all students."),
)
only_if_higher = serializers.BooleanField(
default=False,
help_text=_("Whether to reset the problem for all students."),
)

0 comments on commit 487ae50

Please sign in to comment.