Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade change_due date to drf ( 16th ) #35392

Merged
merged 11 commits into from
Sep 12, 2024
15 changes: 15 additions & 0 deletions lms/djangoapps/instructor/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4145,6 +4145,21 @@ def test_change_due_date(self):
# This operation regenerates the cache, so we can use cached results from edx-when.
assert get_date_for_block(self.course, self.week1, self.user1, use_cached=True) == due_date

def test_change_due_date_with_reason(self):
url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)})
due_date = datetime.datetime(2013, 12, 30, tzinfo=UTC)
response = self.client.post(url, {
'student': self.user1.username,
'url': str(self.week1.location),
'due_datetime': '12/30/2013 00:00',
'reason': 'Testing reason.' # this is optional field.
})
assert response.status_code == 200, response.content

assert get_extended_due(self.course, self.week1, self.user1) == due_date
# This operation regenerates the cache, so we can use cached results from edx-when.
assert get_date_for_block(self.course, self.week1, self.user1, use_cached=True) == due_date

def test_change_to_invalid_due_date(self):
url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)})
response = self.client.post(url, {
Expand Down
58 changes: 40 additions & 18 deletions lms/djangoapps/instructor/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
from lms.djangoapps.instructor_task.data import InstructorTaskTypes
from lms.djangoapps.instructor_task.models import ReportStore
from lms.djangoapps.instructor.views.serializer import (
AccessSerializer, RoleNameSerializer, ShowStudentExtensionSerializer, UserSerializer
AccessSerializer, BlockDueDateSerializer, 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
Expand Down Expand Up @@ -2920,28 +2920,50 @@ def _display_unit(unit):
return str(unit.location)


@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', 'url', 'due_datetime')
def change_due_date(request, course_id):
@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
class ChangeDueDate(APIView):
"""
Grants a due date extension to a student for a particular unit.
"""
course = get_course_by_id(CourseKey.from_string(course_id))
student = require_student_from_identifier(request.POST.get('student'))
unit = find_unit(course, request.POST.get('url'))
due_date = parse_datetime(request.POST.get('due_datetime'))
reason = strip_tags(request.POST.get('reason', ''))
permission_classes = (IsAuthenticated, permissions.InstructorPermission)
permission_name = permissions.GIVE_STUDENT_EXTENSION
serializer_class = BlockDueDateSerializer

@method_decorator(ensure_csrf_cookie)
def post(self, request, course_id):
"""
Grants a due date extension to a student for a particular unit.

set_due_date_extension(course, unit, student, due_date, request.user, reason=reason)
params:
url (str): The URL related to the block that needs the due date update.
due_datetime (str): The new due date and time for the block.
student (str): The email or username of the student whose access is being modified.
"""
serializer_data = self.serializer_class(data=request.data)
if not serializer_data.is_valid():
return HttpResponseBadRequest(reason=serializer_data.errors)

return JsonResponse(_(
'Successfully changed due date for student {0} for {1} '
'to {2}').format(student.profile.name, _display_unit(unit),
due_date.strftime('%Y-%m-%d %H:%M')))
student = serializer_data.validated_data.get('student')
if not student:
response_payload = {
'error': f'Could not find student matching identifier: {request.data.get("student")}'
}
return JsonResponse(response_payload)

course = get_course_by_id(CourseKey.from_string(course_id))

unit = find_unit(course, serializer_data.validated_data.get('url'))
due_date = parse_datetime(serializer_data.validated_data.get('due_datetime'))
reason = strip_tags(serializer_data.validated_data.get('reason', ''))
try:
set_due_date_extension(course, unit, student, due_date, request.user, reason=reason)
except Exception as error: # pylint: disable=broad-except
return JsonResponse({'error': str(error)}, status=400)

return JsonResponse(_(
'Successfully changed due date for student {0} for {1} '
'to {2}').format(student.profile.name, _display_unit(unit),
due_date.strftime('%Y-%m-%d %H:%M')))


@handle_dashboard_error
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 @@ -50,7 +50,7 @@
path('list_forum_members', api.list_forum_members, name='list_forum_members'),
path('update_forum_role_membership', api.update_forum_role_membership, name='update_forum_role_membership'),
path('send_email', api.send_email, name='send_email'),
path('change_due_date', api.change_due_date, name='change_due_date'),
path('change_due_date', api.ChangeDueDate.as_view(), 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.ShowStudentExtensions.as_view(), name='show_student_extensions'),
Expand Down
29 changes: 29 additions & 0 deletions lms/djangoapps/instructor/views/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,32 @@ def validate_student(self, value):
return None

return user


class BlockDueDateSerializer(serializers.Serializer):
"""
Serializer for handling block due date updates for a specific student.
Fields:
url (str): The URL related to the block that needs the due date update.
due_datetime (str): The new due date and time for the block.
student (str): The email or username of the student whose access is being modified.
reason (str): Reason why updating this.
"""
url = serializers.CharField()
due_datetime = serializers.CharField()
student = serializers.CharField(
max_length=255,
help_text="Email or username of user to change access"
)
reason = serializers.CharField(required=False)

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
Loading