From 698a041ee8764257281574784c76d70f29a0af39 Mon Sep 17 00:00:00 2001 From: noraiz-anwar Date: Thu, 11 Jan 2018 19:09:38 +0500 Subject: [PATCH] auto certs for non-verfied enrollment modes --- lms/djangoapps/certificates/signals.py | 38 +++++++++++++--- .../certificates/tests/test_signals.py | 43 +++++++++++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/lms/djangoapps/certificates/signals.py b/lms/djangoapps/certificates/signals.py index 1a0cf86fe9..ebcc314ed8 100644 --- a/lms/djangoapps/certificates/signals.py +++ b/lms/djangoapps/certificates/signals.py @@ -16,6 +16,7 @@ from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from openedx.core.djangoapps.certificates.api import auto_certificate_generation_enabled from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.signals.signals import COURSE_GRADE_NOW_PASSED, LEARNER_NOW_VERIFIED +from course_modes.models import CourseMode from student.models import CourseEnrollment @@ -81,17 +82,40 @@ def _listen_for_id_verification_status_changed(sender, user, **kwargs): # pylin def fire_ungenerated_certificate_task(user, course_key, expected_verification_status=None): """ - Helper function to fire un-generated certificate tasks + Helper function to fire certificate generation task. + Auto-generation of certificates is available for following course modes: + 1- VERIFIED + 2- CREDIT_MODE + 3- PROFESSIONAL + 4- NO_ID_PROFESSIONAL_MODE - The 'mode_is_verified' query is copied from the GeneratedCertificate model, - but is done here in an attempt to reduce traffic to the workers. - If the learner is verified and their cert has the 'unverified' status, - we regenerate the cert. + Certificate generation task is fired to either generate a certificate + when there is no generated certificate for user in a particular course or + update a certificate if it has 'unverified' status. + + Task is fired to attempt an update to a certificate + with 'unverified' status as this method is called when a user is + successfully verified, any certificate associated + with such user can now be verified. + + NOTE: Purpose of restricting other course modes (HONOR and AUDIT) from auto-generation is to reduce + traffic to workers. """ + + allowed_enrollment_modes_list = [ + CourseMode.VERIFIED, + CourseMode.CREDIT_MODE, + CourseMode.PROFESSIONAL, + CourseMode.NO_ID_PROFESSIONAL_MODE, + ] enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_key) - mode_is_verified = enrollment_mode in GeneratedCertificate.VERIFIED_CERTS_MODES cert = GeneratedCertificate.certificate_for_student(user, course_key) - if mode_is_verified and (cert is None or cert.status == 'unverified'): + + generate_learner_certificate = ( + enrollment_mode in allowed_enrollment_modes_list and (cert is None or cert.status == 'unverified') + ) + + if generate_learner_certificate: kwargs = { 'student': unicode(user.id), 'course_key': unicode(course_key) diff --git a/lms/djangoapps/certificates/tests/test_signals.py b/lms/djangoapps/certificates/tests/test_signals.py index 41716de797..61fd7a3f09 100644 --- a/lms/djangoapps/certificates/tests/test_signals.py +++ b/lms/djangoapps/certificates/tests/test_signals.py @@ -2,6 +2,7 @@ Unit tests for enabling self-generated certificates for self-paced courses and disabling for instructor-paced courses. """ +import ddt import mock from certificates import api as certs_api @@ -11,6 +12,7 @@ from certificates.models import ( GeneratedCertificate, CertificateStatuses, ) +from certificates.signals import fire_ungenerated_certificate_task from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory from lms.djangoapps.grades.tests.utils import mock_passing_grade from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification @@ -286,3 +288,44 @@ class LearnerTrackChangeCertsTest(ModuleStoreTestCase): 'expected_verification_status': SoftwareSecurePhotoVerification.STATUS.approved } ) + + +@ddt.ddt +class CertificateGenerationTaskTest(ModuleStoreTestCase): + """ + Tests for certificate generation task. + """ + + def setUp(self): + super(CertificateGenerationTaskTest, self).setUp() + self.course = CourseFactory.create() + + @ddt.data( + ('professional', True), + ('verified', True), + ('no-id-professional', True), + ('credit', True), + ('audit', False), + ('honor', False), + ) + @ddt.unpack + def test_fire_ungenerated_certificate_task_allowed_modes(self, enrollment_mode, should_create): + """ + Test that certificate generation task is fired for only modes that are + allowed to generate certificates automatically. + """ + self.user = UserFactory.create() + self.enrollment = CourseEnrollmentFactory( + user=self.user, + course_id=self.course.id, + is_active=True, + mode=enrollment_mode + ) + with mock.patch( + 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', + return_value=None + ) as mock_generate_certificate_apply_async: + with waffle.waffle().override(waffle.AUTO_CERTIFICATE_GENERATION, active=True): + fire_ungenerated_certificate_task(self.user, self.course.id) + task_created = mock_generate_certificate_apply_async.called + self.assertEqual(task_created, should_create)