diff --git a/lms/djangoapps/verify_student/api.py b/lms/djangoapps/verify_student/api.py index e69de29bb2d1..c974fa0c8e5f 100644 --- a/lms/djangoapps/verify_student/api.py +++ b/lms/djangoapps/verify_student/api.py @@ -0,0 +1,35 @@ +""" +API module. +""" +from django.conf import settings +from django.utils.translation import gettext as _ + +from lms.djangoapps.verify_student.emails import send_verification_approved_email +from lms.djangoapps.verify_student.tasks import send_verification_status_email + + +def send_approval_email(attempt): + """ + Send an approval email to the learner associated with the IDV attempt. + """ + verification_status_email_vars = { + 'platform_name': settings.PLATFORM_NAME, + } + + expiration_datetime = attempt.expiration_datetime.date() + if settings.VERIFY_STUDENT.get('USE_DJANGO_MAIL'): + verification_status_email_vars['expiration_datetime'] = expiration_datetime.strftime("%m/%d/%Y") + verification_status_email_vars['full_name'] = attempt.user.profile.name + subject = _("Your {platform_name} ID verification was approved!").format( + platform_name=settings.PLATFORM_NAME + ) + context = { + 'subject': subject, + 'template': 'emails/passed_verification_email.txt', + 'email': attempt.user.email, + 'email_vars': verification_status_email_vars + } + send_verification_status_email.delay(context) + else: + email_context = {'user': attempt.user, 'expiration_datetime': expiration_datetime.strftime("%m/%d/%Y")} + send_verification_approved_email(context=email_context) diff --git a/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py b/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py index 48ef6e6cf3a9..b87b2eee4559 100644 --- a/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py +++ b/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py @@ -11,6 +11,7 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.management.base import BaseCommand, CommandError +from lms.djangoapps.verify_student.api import send_approval_email from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from lms.djangoapps.verify_student.utils import earliest_allowed_verification_date @@ -125,8 +126,8 @@ def _approve_verifications_from_file(self, user_ids_file, batch_size, sleep_time def _approve_id_verifications(self, user_ids): """ - This command manually approves ID verification attempts for a provided set of learners whose ID verification - attempt is in the submitted or must_retry state. + This method manually approves ID verification attempts for a provided set of user IDs so long as the attempt + is in the submitted or must_retry state. This method also send an IDV approval email to the user. Arguments: user_ids (list): user IDs of the users whose ID verification attempt should be manually approved @@ -148,5 +149,6 @@ def _approve_id_verifications(self, user_ids): for verification in existing_id_verifications: verification.approve(service='idv_verifications command') + send_approval_email(verification) return list(failed_user_ids) diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py b/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py index 90947a2b0771..e6e580c1d1a6 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py @@ -8,6 +8,7 @@ import tempfile import pytest +from django.core import mail from django.core.management import CommandError, call_command from django.test import TestCase from testfixtures import LogCapture @@ -70,6 +71,35 @@ def test_approve_id_verifications(self, status): assert SoftwareSecurePhotoVerification.objects.filter(status='approved').count() == 3 + @ddt.data('submitted', 'must_retry') + def test_approve_id_verifications_email(self, status): + """ + Tests that the approve_id_verifications management command correctly sends approval emails. + """ + # Create SoftwareSecurePhotoVerification instances for the users. + for user in [self.user1_profile, self.user2_profile]: + SoftwareSecurePhotoVerification.objects.create( + user=user.user, + name=user.name, + status=status, + ) + SoftwareSecurePhotoVerification.objects.create( + user=self.user3_profile.user, + name=self.user3_profile.name, + status='denied', + ) + + call_command('approve_id_verifications', self.tmp_file_path) + + assert len(mail.outbox) == 2 + + # All three emails should have equal expiration dates, so just pick one from an attempt. + expiration_date = SoftwareSecurePhotoVerification.objects.first().expiration_datetime + for email in mail.outbox: + assert email.subject == 'Your édX ID verification was approved!' + assert 'Your édX ID verification photos have been approved' in email.body + assert expiration_date.strftime("%m/%d/%Y") in email.body + def test_user_does_not_exist_log(self): """ Tests that the approve_id_verifications management command logs an error when an invalid user ID is diff --git a/lms/djangoapps/verify_student/tests/test_api.py b/lms/djangoapps/verify_student/tests/test_api.py new file mode 100644 index 000000000000..acdebaa70c1c --- /dev/null +++ b/lms/djangoapps/verify_student/tests/test_api.py @@ -0,0 +1,43 @@ +""" +Tests of API module. +""" +from unittest.mock import patch + +import ddt +from django.conf import settings +from django.core import mail +from django.test import TestCase + +from common.djangoapps.student.tests.factories import UserFactory +from lms.djangoapps.verify_student.api import send_approval_email +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification + + +@ddt.ddt +class TestSendApprovalEmail(TestCase): + """ + Test cases for the send_approval_email API method. + """ + def setUp(self): + super().setUp() + + self.user = UserFactory.create() + self.attempt = SoftwareSecurePhotoVerification( + status="submitted", + user=self.user + ) + self.attempt.save() + + def _assert_verification_approved_email(self, expiration_date): + """Check that a verification approved email was sent.""" + assert len(mail.outbox) == 1 + email = mail.outbox[0] + assert email.subject == 'Your édX ID verification was approved!' + assert 'Your édX ID verification photos have been approved' in email.body + assert expiration_date.strftime("%m/%d/%Y") in email.body + + @ddt.data(True, False) + def test_send_approval(self, use_ace): + with patch.dict(settings.VERIFY_STUDENT, {'USE_DJANGO_MAIL': use_ace}): + send_approval_email(self.attempt) + self._assert_verification_approved_email(self.attempt.expiration_datetime) diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index 06e287d7314b..8c1912d9c2f8 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -36,7 +36,8 @@ from common.djangoapps.util.json_request import JsonResponse from common.djangoapps.util.views import require_global_staff from lms.djangoapps.commerce.utils import EcommerceService, is_account_activation_requirement_disabled -from lms.djangoapps.verify_student.emails import send_verification_approved_email, send_verification_confirmation_email +from lms.djangoapps.verify_student.api import send_approval_email +from lms.djangoapps.verify_student.emails import send_verification_confirmation_email from lms.djangoapps.verify_student.image import InvalidImageData, decode_image_data from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline from lms.djangoapps.verify_student.tasks import send_verification_status_email @@ -1117,24 +1118,7 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme log.info("[COSMO-184] Approved verification for receipt_id={receipt_id}.".format(receipt_id=receipt_id)) attempt.approve() - expiration_datetime = attempt.expiration_datetime.date() - if settings.VERIFY_STUDENT.get('USE_DJANGO_MAIL'): - verification_status_email_vars['expiration_datetime'] = expiration_datetime.strftime("%m/%d/%Y") - verification_status_email_vars['full_name'] = user.profile.name - subject = _("Your {platform_name} ID verification was approved!").format( - platform_name=settings.PLATFORM_NAME - ) - context = { - 'subject': subject, - 'template': 'emails/passed_verification_email.txt', - 'email': user.email, - 'email_vars': verification_status_email_vars - } - send_verification_status_email.delay(context) - else: - email_context = {'user': user, 'expiration_datetime': expiration_datetime.strftime("%m/%d/%Y")} - send_verification_approved_email(context=email_context) - + send_approval_email(attempt) elif result == "FAIL": log.debug("Denying verification for %s", receipt_id)