From 3e805fa61b2fea694c788c28725b45966bf92729 Mon Sep 17 00:00:00 2001 From: Gregory Martin Date: Thu, 22 Jun 2017 15:01:03 -0400 Subject: [PATCH] add cert task firing on whitelist append --- lms/djangoapps/certificates/__init__.py | 3 - lms/djangoapps/certificates/apps.py | 22 +++++++ lms/djangoapps/certificates/signals.py | 41 +++++++++++- .../certificates/tests/test_signals.py | 62 ++++++++++++++++++- 4 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 lms/djangoapps/certificates/apps.py diff --git a/lms/djangoapps/certificates/__init__.py b/lms/djangoapps/certificates/__init__.py index 755f73d708..e69de29bb2 100644 --- a/lms/djangoapps/certificates/__init__.py +++ b/lms/djangoapps/certificates/__init__.py @@ -1,3 +0,0 @@ -""" Certificates app """ -# this is here to support registering the signals in signals.py -from . import signals diff --git a/lms/djangoapps/certificates/apps.py b/lms/djangoapps/certificates/apps.py new file mode 100644 index 0000000000..15db77dfb8 --- /dev/null +++ b/lms/djangoapps/certificates/apps.py @@ -0,0 +1,22 @@ +""" +Certificates Application Configuration + +Signal handlers are connected here. +""" + +from django.apps import AppConfig + + +class CertificatesConfig(AppConfig): + """ + Application Configuration for Certificates. + """ + name = u'certificates' + + def ready(self): + """ + Connect handlers to signals. + """ + # Can't import models at module level in AppConfigs, and models get + # included from the signal handlers + from . import signals # pylint: disable=unused-variable diff --git a/lms/djangoapps/certificates/signals.py b/lms/djangoapps/certificates/signals.py index 387c63c73c..fa4854e352 100644 --- a/lms/djangoapps/certificates/signals.py +++ b/lms/djangoapps/certificates/signals.py @@ -1,14 +1,53 @@ """ Signal handler for enabling/disabling self-generated certificates based on the course-pacing. """ +import logging + from celery.task import task +from django.db.models.signals import post_save from django.dispatch import receiver from opaque_keys.edx.keys import CourseKey -from certificates.models import CertificateGenerationCourseSetting +from .config import waffle +from certificates.models import CertificateGenerationCourseSetting, CertificateWhitelist +from certificates.tasks import generate_certificate +from courseware import courses from openedx.core.djangoapps.models.course_details import COURSE_PACING_CHANGE +log = logging.getLogger(__name__) + + +@receiver(post_save, sender=CertificateWhitelist, dispatch_uid="append_certificate_whitelist") +def _listen_for_certificate_whitelist_append(sender, instance, **kwargs): # pylint: disable=unused-argument + switches = waffle.waffle() + # All flags enabled + if ( + not switches.is_enabled(waffle.SELF_PACED_ONLY) and + not switches.is_enabled(waffle.INSTRUCTOR_PACED_ONLY) + ): + return + + # Only SELF_PACED_ONLY flag enabled + if not switches.is_enabled(waffle.INSTRUCTOR_PACED_ONLY): + if not courses.get_course_by_id(instance.course_id, depth=0).self_paced: + return + + # Only INSTRUCTOR_PACED_ONLY flag enabled + if not switches.is_enabled(waffle.SELF_PACED_ONLY): + if courses.get_course_by_id(instance.course_id, depth=0).self_paced: + return + + generate_certificate.apply_async( + student=instance.user, + course_key=instance.course_id, + ) + log.info(u'Certificate generation task initiated for {user} : {course} via whitelist'.format( + user=instance.user.id, + course=instance.course_id + )) + + @receiver(COURSE_PACING_CHANGE, dispatch_uid="course_pacing_changed") def _listen_for_course_pacing_changed(sender, course_key, course_self_paced, **kwargs): # pylint: disable=unused-argument """ diff --git a/lms/djangoapps/certificates/tests/test_signals.py b/lms/djangoapps/certificates/tests/test_signals.py index 1f90f8b492..ae6e241130 100644 --- a/lms/djangoapps/certificates/tests/test_signals.py +++ b/lms/djangoapps/certificates/tests/test_signals.py @@ -2,10 +2,14 @@ Unit tests for enabling self-generated certificates for self-paced courses and disabling for instructor-paced courses. """ +import mock + from certificates import api as certs_api -from certificates.models import CertificateGenerationConfiguration +from certificates.config import waffle +from certificates.models import CertificateGenerationConfiguration, CertificateWhitelist from certificates.signals import _listen_for_course_pacing_changed from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration +from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -40,3 +44,59 @@ class SelfGeneratedCertsSignalTest(ModuleStoreTestCase): _listen_for_course_pacing_changed('store', self.course.id, self.course.self_paced) # verify that self-generation of cert is disabled for instructor-paced course self.assertFalse(certs_api.cert_generation_enabled(self.course.id)) + + +class WhitelistGeneratedCertificatesTest(ModuleStoreTestCase): + """ + Tests for whitelisted student auto-certificate generation + """ + def setUp(self): + super(WhitelistGeneratedCertificatesTest, self).setUp() + self.course = CourseFactory.create(self_paced=True) + self.user = UserFactory.create() + self.ip_course = CourseFactory.create(self_paced=False) + + def test_cert_generation_on_whitelist_append(self): + """ + Verify that signal is sent, received, and fires task based on various flag configs + """ + 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.SELF_PACED_ONLY, active=False): + CertificateWhitelist.objects.create( + user=self.user, + course_id=self.course.id + ) + mock_generate_certificate_apply_async.assert_not_called( + student=self.user, + course_key=self.course.id + ) + with waffle.waffle().override(waffle.SELF_PACED_ONLY, active=True): + CertificateWhitelist.objects.create( + user=self.user, + course_id=self.course.id + ) + mock_generate_certificate_apply_async.assert_called_with( + student=self.user, + course_key=self.course.id, + ) + with waffle.waffle().override(waffle.INSTRUCTOR_PACED_ONLY, active=False): + CertificateWhitelist.objects.create( + user=self.user, + course_id=self.ip_course.id + ) + mock_generate_certificate_apply_async.assert_not_called( + student=self.user, + course_key=self.ip_course.id + ) + with waffle.waffle().override(waffle.INSTRUCTOR_PACED_ONLY, active=True): + CertificateWhitelist.objects.create( + user=self.user, + course_id=self.ip_course.id + ) + mock_generate_certificate_apply_async.assert_called_with( + student=self.user, + course_key=self.ip_course.id + )