Merge pull request #13362 from edx/mushtaq/generate-verified-users-with-audit-certs
Generate certificates for verified users with audit certificate statuses
This commit is contained in:
@@ -95,7 +95,9 @@ class CertificateStatuses(object):
|
||||
readable_statuses = {
|
||||
downloadable: "already received",
|
||||
notpassing: "didn't receive",
|
||||
error: "error states"
|
||||
error: "error states",
|
||||
audit_passing: "audit passing states",
|
||||
audit_notpassing: "audit not passing states",
|
||||
}
|
||||
|
||||
PASSED_STATUSES = (downloadable, generating)
|
||||
|
||||
@@ -3,12 +3,21 @@ import contextlib
|
||||
import ddt
|
||||
import mock
|
||||
import json
|
||||
import pytz
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.test.utils import override_settings
|
||||
from django.conf import settings
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from capa.xqueue_interface import XQueueInterface
|
||||
from lms.djangoapps.grades.tests.utils import mock_passing_grade
|
||||
from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
|
||||
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from config_models.models import cache
|
||||
@@ -344,6 +353,85 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase):
|
||||
u'the "Pending Tasks" section.'
|
||||
)
|
||||
|
||||
@override_settings(AUDIT_CERT_CUTOFF_DATE=datetime.now(pytz.UTC) - timedelta(days=1))
|
||||
@ddt.data(
|
||||
(CertificateStatuses.generating, 'ID Verified', 'approved'),
|
||||
(CertificateStatuses.unverified, 'Not ID Verified', 'denied'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_verified_users_with_audit_certs(self, expected_cert_status, verification_output, id_verification_status):
|
||||
"""
|
||||
Test certificate regeneration for verified users with audit certificates.
|
||||
|
||||
Scenario:
|
||||
Enroll user in a course in audit mode,
|
||||
User passed the course and now he has `audit_passing` certificate status,
|
||||
User switched to verified mode and is ID verified,
|
||||
Regenerate certificate for it,
|
||||
Modified certificate status is `generating` if user is ID verified otherwise `unverified`.
|
||||
"""
|
||||
# Check that user is enrolled in audit mode.
|
||||
enrollment = CourseEnrollment.get_enrollment(self.user, self.course.id)
|
||||
self.assertEquals(enrollment.mode, CourseMode.AUDIT)
|
||||
|
||||
with mock_passing_grade():
|
||||
# Generate certificate for user and check that user has a audit passing certificate.
|
||||
cert_status = certs_api.generate_user_certificates(
|
||||
student=self.user,
|
||||
course_key=self.course.id,
|
||||
course=self.course,
|
||||
)
|
||||
|
||||
# Check that certificate status is 'audit_passing'.
|
||||
self.assertEquals(cert_status, CertificateStatuses.audit_passing)
|
||||
|
||||
# Update user enrollment mode to verified mode.
|
||||
enrollment.update_enrollment(mode=CourseMode.VERIFIED)
|
||||
self.assertEquals(enrollment.mode, CourseMode.VERIFIED)
|
||||
|
||||
# Create and assert user's ID verification record.
|
||||
SoftwareSecurePhotoVerificationFactory.create(user=self.user, status=id_verification_status)
|
||||
actual_verification_status = SoftwareSecurePhotoVerification.verification_status_for_user(
|
||||
self.user,
|
||||
self.course.id,
|
||||
enrollment.mode,
|
||||
)
|
||||
self.assertEquals(actual_verification_status, verification_output)
|
||||
|
||||
# Login the client and access the url with 'audit_passing' status.
|
||||
self.client.login(username=self.global_staff.username, password='test')
|
||||
url = reverse(
|
||||
'start_certificate_regeneration',
|
||||
kwargs={'course_id': unicode(self.course.id)}
|
||||
)
|
||||
|
||||
with mock.patch.object(XQueueInterface, 'send_to_queue') as mock_send:
|
||||
mock_send.return_value = (0, None)
|
||||
response = self.client.post(
|
||||
url,
|
||||
{'certificate_statuses': [CertificateStatuses.audit_passing]}
|
||||
)
|
||||
|
||||
# Assert 200 status code in response
|
||||
self.assertEquals(response.status_code, 200)
|
||||
res_json = json.loads(response.content)
|
||||
|
||||
# Assert request is successful
|
||||
self.assertTrue(res_json['success'])
|
||||
|
||||
# Assert success message
|
||||
self.assertEquals(
|
||||
res_json['message'],
|
||||
u'Certificate regeneration task has been started. '
|
||||
u'You can view the status of the generation task in '
|
||||
u'the "Pending Tasks" section.'
|
||||
)
|
||||
|
||||
# Now, check whether user has audit certificate.
|
||||
cert = certs_api.get_certificate_for_user(self.user.username, self.course.id)
|
||||
self.assertNotEquals(cert['status'], CertificateStatuses.audit_passing)
|
||||
self.assertEquals(cert['status'], expected_cert_status)
|
||||
|
||||
def test_certificate_regeneration_error(self):
|
||||
"""
|
||||
Test certificate regeneration errors out when accessed with either empty list of 'certificate_statuses' or
|
||||
|
||||
@@ -2862,7 +2862,13 @@ def start_certificate_regeneration(request, course_id):
|
||||
)
|
||||
|
||||
# Check if the selected statuses are allowed
|
||||
allowed_statuses = [CertificateStatuses.downloadable, CertificateStatuses.error, CertificateStatuses.notpassing]
|
||||
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.')},
|
||||
|
||||
@@ -128,6 +128,18 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
|
||||
${_("Regenerate for learners who have not received certificates. ({count})").format(count=section_data['certificate_statuses_with_count'].get(section_data['status'].notpassing, 0))}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
<input id="certificate_status_${section_data['status'].audit_passing}" type="checkbox" name="certificate_statuses" value="${section_data['status'].audit_passing}">
|
||||
${_("Regenerate for learners with audit passing state. ({count})").format(count=section_data['certificate_statuses_with_count'].get(section_data['status'].audit_passing, 0))}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
<input id="certificate_status_${section_data['status'].audit_notpassing}" type="checkbox" name="certificate_statuses" value="${section_data['status'].audit_notpassing}">
|
||||
${_("Regenerate for learners with audit not passing state. ({count})").format(count=section_data['certificate_statuses_with_count'].get(section_data['status'].audit_notpassing, 0))}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
<input id="certificate_status_${section_data['status'].error}" type="checkbox" name="certificate_statuses" value="${section_data['status'].error}">
|
||||
|
||||
Reference in New Issue
Block a user