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 10, 2024
1 parent 8c978c2 commit 40dd90e
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 49 deletions.
103 changes: 55 additions & 48 deletions lms/djangoapps/instructor/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@
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, BaseProblemSerializer
)
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 @@ -2127,62 +2128,68 @@ def override_problem_score(request, course_id): # lint-amnesty, pylint: disable
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.RESCORE_EXAMS)
@common_exceptions_400
def rescore_entrance_exam(request, course_id):
"""
Starts a background process a students attempts counter for entrance exam.
Optionally deletes student state for a problem. Limited to instructor access.
@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
@method_decorator(transaction.non_atomic_requests, name='dispatch')
class RescoreEntranceExam(DeveloperErrorViewMixin, APIView):
permission_classes = (IsAuthenticated, permissions.InstructorPermission)
permission_name = permissions.OVERRIDE_GRADES
serializer_class = BaseProblemSerializer

Takes either of the following query parameters
- 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):
"""
Starts a background process a students attempts counter for entrance exam.
Optionally deletes student state for a problem. Limited to instructor access.
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, depth=None
)
Takes either of the following query parameters
- unique_student_identifier is an email or username
- all_students is a boolean
student_identifier = request.POST.get('unique_student_identifier', None)
only_if_higher = request.POST.get('only_if_higher', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
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, depth=None
)

all_students = _get_boolean_param(request, 'all_students')
serializer_data = self.serializer_class(data=request.data)

if not course.entrance_exam_id:
return HttpResponseBadRequest(
_("Course has no entrance exam section.")
)
if not serializer_data.is_valid():
return HttpResponseBadRequest(reason=serializer_data.errors)

if all_students and student:
return HttpResponseBadRequest(
_("Cannot rescore with all_students and unique_student_identifier.")
)
all_students = serializer_data.validated_data.get("all_students")
only_if_higher = serializer_data.validated_data.get("only_if_higher")

try:
entrance_exam_key = UsageKey.from_string(course.entrance_exam_id).map_into_course(course_id)
except InvalidKeyError:
return HttpResponseBadRequest(_("Course has no valid entrance exam section."))
student = serializer_data.validated_data.get("unique_student_identifier")
student_identifier = request.data.get("unique_student_identifier")

response_payload = {}
if student:
response_payload['student'] = student_identifier
else:
response_payload['student'] = _("All Students")
if not course.entrance_exam_id:
return HttpResponseBadRequest(
_("Course has no entrance exam section.")
)

task_api.submit_rescore_entrance_exam_for_student(
request, entrance_exam_key, student, only_if_higher,
)
response_payload['task'] = TASK_SUBMISSION_OK
return JsonResponse(response_payload)
if all_students and student:
return HttpResponseBadRequest(
_("Cannot rescore with all_students and unique_student_identifier.")
)

try:
entrance_exam_key = UsageKey.from_string(course.entrance_exam_id).map_into_course(course_id)
except InvalidKeyError:
return HttpResponseBadRequest(_("Course has no valid entrance exam section."))

response_payload = {}
if student:
response_payload['student'] = student_identifier
else:
response_payload['student'] = _("All Students")

task_api.submit_rescore_entrance_exam_for_student(
request, entrance_exam_key, student, only_if_higher,
)
response_payload['task'] = TASK_SUBMISSION_OK
return JsonResponse(response_payload)


@require_POST
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 @@ -40,7 +40,7 @@
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'),
path('rescore_entrance_exam', api.rescore_entrance_exam, name='rescore_entrance_exam'),
path('rescore_entrance_exam', api.RescoreEntranceExam.as_view(), name='rescore_entrance_exam'),
path('list_entrance_exam_instructor_tasks', api.ListEntranceExamInstructorTasks.as_view(),
name='list_entrance_exam_instructor_tasks'),
path('mark_student_can_skip_entrance_exam', api.MarkStudentCanSkipEntranceExam.as_view(),
Expand Down
26 changes: 26 additions & 0 deletions lms/djangoapps/instructor/views/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,29 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if disable_due_datetime:
self.fields['due_datetime'].required = False


class BaseProblemSerializer(UniqueStudentIdentifierSerializer):
all_students = serializers.BooleanField(
default=False,
help_text=_("Whether to reset the problem for all students."),
)
only_if_higher = serializers.BooleanField(
default=False,
)

# Override unique_student_identifier to be optional
unique_student_identifier = serializers.CharField(
required=False, # Make this field optional
allow_null=True,
help_text=_("Unique student identifier.")
)


class ProblemResetSerializer(BaseProblemSerializer):
problem_to_reset = serializers.CharField(
help_text=_("The URL name of the problem to reset."),
error_messages={
'blank': _("Problem URL name cannot be blank."),
}
)

0 comments on commit 40dd90e

Please sign in to comment.