Generate certificates for verified users with audit certificate statues - ECOM-5012
This commit is contained in:
@@ -943,12 +943,17 @@ class CourseEnrollmentManager(models.Manager):
|
||||
|
||||
return is_course_full
|
||||
|
||||
def users_enrolled_in(self, course_id):
|
||||
"""Return a queryset of User for every user enrolled in the course."""
|
||||
return User.objects.filter(
|
||||
courseenrollment__course_id=course_id,
|
||||
courseenrollment__is_active=True
|
||||
)
|
||||
def users_enrolled_in(self, course_id, mode=None):
|
||||
"""
|
||||
Returns a queryset of User for every user enrolled in the course.
|
||||
|
||||
course_id (CourseKey): The key of the course associated with the enrollment.
|
||||
mode (String): The enrolled mode of the users.
|
||||
"""
|
||||
_query = {'courseenrollment__course_id': course_id, 'courseenrollment__is_active': True}
|
||||
if mode:
|
||||
_query['courseenrollment__mode'] = mode
|
||||
return User.objects.filter(**_query)
|
||||
|
||||
def enrollment_counts(self, course_id):
|
||||
"""
|
||||
|
||||
@@ -3,12 +3,19 @@ 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 mock import patch, PropertyMock
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from config_models.models import cache
|
||||
@@ -344,6 +351,70 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase):
|
||||
u'the "Pending Tasks" section.'
|
||||
)
|
||||
|
||||
@patch(
|
||||
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary',
|
||||
PropertyMock(return_value={'grade': 'Pass', 'percent': 0.75})
|
||||
)
|
||||
@override_settings(AUDIT_CERT_CUTOFF_DATE=datetime.now(pytz.UTC) - timedelta(days=1))
|
||||
@ddt.data(
|
||||
('verified', 'ID Verified', True),
|
||||
('unverified', 'Not ID Verified', False)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_verified_users_with_audit_certs(self, expected_cert_status, verification_output, user_verified):
|
||||
"""
|
||||
Test that `verified_users_with_audit_certs` option regenerates certificate for verified users with audit
|
||||
certificates get certificate.
|
||||
|
||||
Scenario:
|
||||
User enrolled in course as audit,
|
||||
User passed the course as audit so they have `audit_passing` certificate status,
|
||||
User switched to verified mode and is ID verified,
|
||||
Regenerate certificates for `verified_users_with_audit_certs` is run,
|
||||
Modified certificate status is `verified` 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.assertEqual(enrollment.mode, CourseMode.AUDIT)
|
||||
|
||||
# Generate certificate for user and check that user has a audit passing certificate.
|
||||
with patch('student.models.CourseEnrollment.refund_cutoff_date') as cutoff_date:
|
||||
cutoff_date.return_value = datetime.now(pytz.UTC) - timedelta(minutes=5)
|
||||
cert_status = certs_api.generate_user_certificates(student=self.user, course_key=self.course.id, course=self.course)
|
||||
self.assertEqual(cert_status, CertificateStatuses.audit_passing)
|
||||
|
||||
# Update user enrollment mode to verified mode.
|
||||
enrollment.update_enrollment(mode='verified')
|
||||
self.assertEqual(enrollment.mode, CourseMode.VERIFIED)
|
||||
|
||||
with patch(
|
||||
'lms.djangoapps.verify_student.models.SoftwareSecurePhotoVerification.user_is_verified'
|
||||
) as user_verify:
|
||||
user_verify.return_value = user_verified
|
||||
|
||||
# Login the client and access the url with 'certificate_statuses'
|
||||
self.client.login(username=self.global_staff.username, password='test')
|
||||
url = reverse('start_certificate_regeneration', kwargs={'course_id': unicode(self.course.id)})
|
||||
response = self.client.post(url, data={'certificate_statuses': ['verified_users_with_audit_certs']})
|
||||
|
||||
# Assert 200 status code in response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
res_json = json.loads(response.content)
|
||||
|
||||
# Assert request is successful
|
||||
self.assertTrue(res_json['success'])
|
||||
|
||||
# Assert success message
|
||||
self.assertEqual(
|
||||
res_json['message'],
|
||||
u'Certificate regeneration task has been started. You can view the status of the generation task in '
|
||||
u'the "Pending Tasks" section.'
|
||||
)
|
||||
# Check user has a not audit passing certificate now.
|
||||
cert = certs_api.get_certificate_for_user(self.user.username, self.course.id)
|
||||
self.assertNotEqual(cert['status'], CertificateStatuses.audit_passing)
|
||||
self.assertEqual(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,
|
||||
# verified users with audit passing and not passing certificate statuses.
|
||||
'verified_users_with_audit_certs'
|
||||
]
|
||||
if not set(certificates_statuses).issubset(allowed_statuses):
|
||||
return JsonResponse(
|
||||
{'message': _('Please select certificate statuses from the list only.')},
|
||||
|
||||
@@ -343,6 +343,11 @@ def _section_certificates(course):
|
||||
for certificate in GeneratedCertificate.get_unique_statuses(course_key=course.id)
|
||||
}
|
||||
|
||||
# Get the count of all course verified users with audit passing and audit not passing statuses.
|
||||
verified_users_with_audit_certs = CourseEnrollment.objects.users_enrolled_in(course.id, mode='verified').filter(
|
||||
generatedcertificate__status__in=[CertificateStatuses.audit_passing, CertificateStatuses.audit_notpassing]
|
||||
).count()
|
||||
|
||||
return {
|
||||
'section_key': 'certificates',
|
||||
'section_display_name': _('Certificates'),
|
||||
@@ -354,6 +359,7 @@ def _section_certificates(course):
|
||||
'html_cert_enabled': html_cert_enabled,
|
||||
'active_certificate': certs_api.get_active_web_certificate(course),
|
||||
'certificate_statuses_with_count': certificate_statuses_with_count,
|
||||
'verified_users_with_audit_certs': verified_users_with_audit_certs,
|
||||
'status': CertificateStatuses,
|
||||
'certificate_generation_history':
|
||||
CertificateGenerationHistory.objects.filter(course_id=course.id).order_by("-created"),
|
||||
|
||||
@@ -33,7 +33,7 @@ from lms.djangoapps.instructor_task.tasks import (
|
||||
export_ora2_data,
|
||||
)
|
||||
|
||||
from certificates.models import CertificateGenerationHistory
|
||||
from certificates.models import CertificateGenerationHistory, CertificateStatuses
|
||||
|
||||
from lms.djangoapps.instructor_task.api_helper import (
|
||||
check_arguments_for_rescoring,
|
||||
@@ -507,6 +507,11 @@ def regenerate_certificates(request, course_key, statuses_to_regenerate):
|
||||
task_type = 'regenerate_certificates_all_student'
|
||||
task_input = {}
|
||||
|
||||
# Update task_input for verified users with audit passing and not passing certificate statuses.
|
||||
if 'verified_users_with_audit_certs' in statuses_to_regenerate:
|
||||
task_input.update({"student_set": 'verified_users_with_audit_certs'})
|
||||
statuses_to_regenerate = [CertificateStatuses.audit_passing, CertificateStatuses.audit_notpassing]
|
||||
|
||||
task_input.update({"statuses_to_regenerate": statuses_to_regenerate})
|
||||
task_class = generate_certificates
|
||||
task_key = ""
|
||||
|
||||
@@ -1429,6 +1429,10 @@ def generate_students_certificates(
|
||||
specific_student_id = task_input.get('specific_student_id')
|
||||
students_to_generate_certs_for = students_to_generate_certs_for.filter(id=specific_student_id)
|
||||
|
||||
# Verified users with audit passing and not passing certificate statuses.
|
||||
elif student_set == "verified_users_with_audit_certs":
|
||||
students_to_generate_certs_for = CourseEnrollment.objects.users_enrolled_in(course_id, mode='verified')
|
||||
|
||||
task_progress = TaskProgress(action_name, students_to_generate_certs_for.count(), start_time)
|
||||
|
||||
current_step = {'step': 'Calculating students already have certificates'}
|
||||
|
||||
@@ -128,6 +128,12 @@ 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_verified_users_with_audit_certs}" type="checkbox" name="certificate_statuses" value="verified_users_with_audit_certs">
|
||||
${_("Regenerate for verified learners with audit certificates. ({count})").format(count=section_data['verified_users_with_audit_certs'])}
|
||||
</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