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 start_certificate_regeneration to drf ( 29 ) #35599

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
5 changes: 3 additions & 2 deletions lms/djangoapps/instructor/tests/test_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ def test_certificate_regeneration_error(self):

# Assert Error Message
assert res_json['message'] ==\
'Please select one or more certificate statuses that require certificate regeneration.'
'Please select certificate statuses from the list only.'

# Access the url passing 'certificate_statuses' that are not present in db
url = reverse('start_certificate_regeneration', kwargs={'course_id': str(self.course.id)})
Expand All @@ -378,7 +378,8 @@ def test_certificate_regeneration_error(self):
res_json = json.loads(response.content.decode('utf-8'))

# Assert Error Message
assert res_json['message'] == 'Please select certificate statuses from the list only.'
assert (res_json['message'] ==
'Please select certificate statuses from the list only.')


@override_settings(CERT_QUEUE='certificates')
Expand Down
69 changes: 31 additions & 38 deletions lms/djangoapps/instructor/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@
from common.djangoapps.util.views import require_global_staff # pylint: disable=unused-import
from lms.djangoapps.bulk_email.api import is_bulk_email_feature_enabled, create_course_email
from lms.djangoapps.certificates import api as certs_api
from lms.djangoapps.certificates.models import (
CertificateStatuses
)
from lms.djangoapps.course_home_api.toggles import course_home_mfe_progress_tab_is_active
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.courses import get_course_with_access
Expand Down Expand Up @@ -108,7 +105,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, CertificateStatusesSerializer
)
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 @@ -3266,46 +3264,41 @@ def post(self, request, course_id):
return JsonResponse(response_payload)


@transaction.non_atomic_requests
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_course_permission(permissions.START_CERTIFICATE_REGENERATION)
@require_POST
@common_exceptions_400
def start_certificate_regeneration(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 StartCertificateRegeneration(DeveloperErrorViewMixin, APIView):
"""
Start regenerating certificates for students whose certificate statuses lie with in 'certificate_statuses'
entry in POST data.
"""
course_key = CourseKey.from_string(course_id)
certificates_statuses = request.POST.getlist('certificate_statuses', [])
if not certificates_statuses:
return JsonResponse(
{'message': _('Please select one or more certificate statuses that require certificate regeneration.')},
status=400
)
permission_classes = (IsAuthenticated, permissions.InstructorPermission)
permission_name = permissions.START_CERTIFICATE_REGENERATION
serializer_class = CertificateStatusesSerializer
http_method_names = ['post']

# Check if the selected statuses are allowed
allowed_statuses = [
CertificateStatuses.downloadable,
CertificateStatuses.error,
CertificateStatuses.notpassing,
CertificateStatuses.audit_passing,
CertificateStatuses.audit_notpassing,
]
if not set(certificates_statuses).issubset(allowed_statuses):
return JsonResponse(
{'message': _('Please select certificate statuses from the list only.')},
status=400
)
@method_decorator(transaction.non_atomic_requests, name='dispatch')
@method_decorator(ensure_csrf_cookie)
def post(self, request, course_id):
"""
certificate_statuses 'certificate_statuses' in POST data.
"""
course_key = CourseKey.from_string(course_id)
serializer = self.serializer_class(data=request.data)

task_api.regenerate_certificates(request, course_key, certificates_statuses)
response_payload = {
'message': _('Certificate regeneration task has been started. '
'You can view the status of the generation task in the "Pending Tasks" section.'),
'success': True
}
return JsonResponse(response_payload)
if not serializer.is_valid():
return JsonResponse(
{'message': _('Please select certificate statuses from the list only.')},
status=400
)

certificates_statuses = serializer.validated_data['certificate_statuses']
task_api.regenerate_certificates(request, course_key, certificates_statuses)
response_payload = {
'message': _('Certificate regeneration task has been started. '
'You can view the status of the generation task in the "Pending Tasks" section.'),
'success': True
}
return JsonResponse(response_payload)


@transaction.non_atomic_requests
Expand Down
3 changes: 2 additions & 1 deletion lms/djangoapps/instructor/views/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
# Certificates
path('enable_certificate_generation', api.enable_certificate_generation, name='enable_certificate_generation'),
path('start_certificate_generation', api.StartCertificateGeneration.as_view(), name='start_certificate_generation'),
path('start_certificate_regeneration', api.start_certificate_regeneration, name='start_certificate_regeneration'),
path('start_certificate_regeneration', api.StartCertificateRegeneration.as_view(),
name='start_certificate_regeneration'),
path('certificate_exception_view/', api.certificate_exception_view, name='certificate_exception_view'),
re_path(r'^generate_certificate_exceptions/(?P<generate_for>[^/]*)', api.generate_certificate_exceptions,
name='generate_certificate_exceptions'),
Expand Down
24 changes: 23 additions & 1 deletion lms/djangoapps/instructor/views/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from rest_framework import serializers
from .tools import get_student_from_identifier

from lms.djangoapps.certificates.models import CertificateStatuses
from lms.djangoapps.instructor.access import ROLES

from .tools import get_student_from_identifier


class RoleNameSerializer(serializers.Serializer): # pylint: disable=abstract-method
"""
Expand Down Expand Up @@ -228,3 +230,23 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if disable_due_datetime:
self.fields['due_datetime'].required = False


class CertificateStatusesSerializer(serializers.Serializer):
"""
Serializer for validating and serializing certificate status inputs.

This serializer is used to ensure that the provided certificate statuses
conform to the predefined set of valid statuses defined in the
`CertificateStatuses` enumeration.
"""
certificate_statuses = serializers.ListField(
child=serializers.ChoiceField(choices=[
CertificateStatuses.downloadable,
CertificateStatuses.error,
CertificateStatuses.notpassing,
CertificateStatuses.audit_passing,
CertificateStatuses.audit_notpassing,
]),
allow_empty=False # Set to True if you want to allow empty lists
)
Loading