From be02fdb4ae1113567cd4c101d38bc6d474a431ab Mon Sep 17 00:00:00 2001 From: Clinton Blackburn Date: Wed, 19 Apr 2017 02:21:33 -0400 Subject: [PATCH] Updated program credential back-population command to account for no-id-professional This command has been updated to treat professional and no-id-professional enrollments in the same manner. ECOM-7442 --- .../backpopulate_program_credentials.py | 6 ++++ .../test_backpopulate_program_credentials.py | 36 +++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/programs/management/commands/backpopulate_program_credentials.py b/openedx/core/djangoapps/programs/management/commands/backpopulate_program_credentials.py index c670b646c4..c9cb763fd2 100644 --- a/openedx/core/djangoapps/programs/management/commands/backpopulate_program_credentials.py +++ b/openedx/core/djangoapps/programs/management/commands/backpopulate_program_credentials.py @@ -8,6 +8,7 @@ from django.db.models import Q from opaque_keys.edx.keys import CourseKey from certificates.models import GeneratedCertificate, CertificateStatuses # pylint: disable=import-error +from course_modes.models import CourseMode from openedx.core.djangoapps.catalog.utils import get_programs from openedx.core.djangoapps.programs.tasks.v1.tasks import award_program_certificates @@ -100,6 +101,11 @@ class Command(BaseCommand): [Q(course_id=course_run.key, mode=course_run.type) for course_run in self.course_runs] ) + # Account for the fact that no-id-professional and professional are equivalent + for course_run in self.course_runs: + if course_run.type == CourseMode.PROFESSIONAL: + course_run_query |= Q(course_id=course_run.key, mode=CourseMode.NO_ID_PROFESSIONAL_MODE) + query = status_query & course_run_query username_dicts = GeneratedCertificate.eligible_certificates.filter(query).values('user__username').distinct() diff --git a/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py b/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py index 40ff3e3983..09d73029fd 100644 --- a/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py +++ b/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py @@ -1,10 +1,11 @@ """Tests for the backpopulate_program_credentials management command.""" import ddt +import mock from django.core.management import call_command from django.test import TestCase -import mock from certificates.models import CertificateStatuses # pylint: disable=import-error +from course_modes.models import CourseMode from lms.djangoapps.certificates.api import MODES from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory from openedx.core.djangoapps.catalog.tests.factories import ( @@ -18,7 +19,6 @@ from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfi from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory - COMMAND_MODULE = 'openedx.core.djangoapps.programs.management.commands.backpopulate_program_credentials' @@ -81,6 +81,37 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp else: mock_task.assert_not_called() + def test_handle_professional(self, mock_task, mock_get_programs): + """ Verify the task can handle both professional and no-id-professional modes. """ + mock_get_programs.return_value = [ + ProgramFactory( + courses=[ + CourseFactory(course_runs=[ + CourseRunFactory(key=self.course_run_key, type='professional'), + ]), + ] + ), + ] + + GeneratedCertificateFactory( + user=self.alice, + course_id=self.course_run_key, + mode=CourseMode.PROFESSIONAL, + status=CertificateStatuses.downloadable, + ) + + GeneratedCertificateFactory( + user=self.bob, + course_id=self.course_run_key, + mode=CourseMode.NO_ID_PROFESSIONAL_MODE, + status=CertificateStatuses.downloadable, + ) + + call_command('backpopulate_program_credentials', commit=True) + + # The task should be called for both users since professional and no-id-professional are equivalent. + mock_task.assert_has_calls([mock.call(self.alice.username), mock.call(self.bob.username)]) + @ddt.data( [ ProgramFactory( @@ -258,6 +289,7 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp @mock.patch(COMMAND_MODULE + '.logger.exception') def test_handle_enqueue_failure(self, mock_log, mock_task, mock_get_programs): """Verify that failure to enqueue a task doesn't halt execution.""" + def side_effect(username): """Simulate failure to enqueue a task.""" if username == self.alice.username: