Commit 65ef041a by Qubad786

Add ability to generate certs for audit_passing/audit_notpassing learners

parent dacfcc98
......@@ -943,17 +943,12 @@ class CourseEnrollmentManager(models.Manager):
return is_course_full
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 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 enrollment_counts(self, course_id):
"""
......
......@@ -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)
......
......@@ -13,9 +13,11 @@ 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 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
......@@ -351,69 +353,84 @@ 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)
(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, user_verified):
def test_verified_users_with_audit_certs(self, expected_cert_status, verification_output, id_verification_status):
"""
Test that `verified_users_with_audit_certs` option regenerates certificate for verified users with audit
certificates get certificate.
Test certificate regeneration for verified users with audit certificates.
Scenario:
User enrolled in course as audit,
User passed the course as audit so they have `audit_passing` certificate status,
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 certificates for `verified_users_with_audit_certs` is run,
Modified certificate status is `verified` if user is ID verified otherwise `unverified`.
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.assertEqual(enrollment.mode, CourseMode.AUDIT)
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,
)
# 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)
# 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='verified')
self.assertEqual(enrollment.mode, CourseMode.VERIFIED)
# Update user enrollment mode to verified mode.
enrollment.update_enrollment(mode=CourseMode.VERIFIED)
self.assertEquals(enrollment.mode, CourseMode.VERIFIED)
with patch(
'lms.djangoapps.verify_student.models.SoftwareSecurePhotoVerification.user_is_verified'
) as user_verify:
user_verify.return_value = user_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 'certificate_statuses'
# 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)})
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.'
url = reverse(
'start_certificate_regeneration',
kwargs={'course_id': unicode(self.course.id)}
)
# Check user has a not audit passing certificate now.
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.assertNotEqual(cert['status'], CertificateStatuses.audit_passing)
self.assertEqual(cert['status'], expected_cert_status)
self.assertNotEquals(cert['status'], CertificateStatuses.audit_passing)
self.assertEquals(cert['status'], expected_cert_status)
def test_certificate_regeneration_error(self):
"""
......
......@@ -2866,8 +2866,8 @@ def start_certificate_regeneration(request, course_id):
CertificateStatuses.downloadable,
CertificateStatuses.error,
CertificateStatuses.notpassing,
# verified users with audit passing and not passing certificate statuses.
'verified_users_with_audit_certs'
CertificateStatuses.audit_passing,
CertificateStatuses.audit_notpassing,
]
if not set(certificates_statuses).issubset(allowed_statuses):
return JsonResponse(
......
......@@ -343,11 +343,6 @@ 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'),
......@@ -359,7 +354,6 @@ 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, CertificateStatuses
from certificates.models import CertificateGenerationHistory
from lms.djangoapps.instructor_task.api_helper import (
check_arguments_for_rescoring,
......@@ -507,11 +507,6 @@ 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,10 +1429,6 @@ 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'}
......
......@@ -130,8 +130,14 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
</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'])}
<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>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment