diff --git a/lms/djangoapps/verify_student/tasks.py b/lms/djangoapps/verify_student/tasks.py new file mode 100644 index 0000000000..ceb5ac858d --- /dev/null +++ b/lms/djangoapps/verify_student/tasks.py @@ -0,0 +1,40 @@ +""" +Django Celery tasks for service status app +""" +import logging +from smtplib import SMTPException + +from celery import task +from django.conf import settings +from django.core.mail import send_mail + +from edxmako.shortcuts import render_to_string +from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers + +ACE_ROUTING_KEY = getattr(settings, 'ACE_ROUTING_KEY', None) +log = logging.getLogger(__name__) + + +@task(routing_key=ACE_ROUTING_KEY) +def send_verification_status_email(context): + """ + Spins a task to send verification status email to the learner + """ + subject = context.get('subject') + message = render_to_string(context.get('template'), context.get('email_vars')) + from_addr = configuration_helpers.get_value( + 'email_from_address', + settings.DEFAULT_FROM_EMAIL + ) + dest_addr = context.get('email') + + try: + send_mail( + subject, + message, + from_addr, + [dest_addr], + fail_silently=False + ) + except SMTPException: + log.warning("Failure in sending verification status e-mail to %s", dest_addr) diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index a7013c59ad..ee0eea679b 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -38,8 +38,7 @@ from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from lms.djangoapps.verify_student.views import ( PayAndVerifyView, checkout_with_ecommerce_service, - render_to_response, - EmailMarketingConfiguration + render_to_response ) from openedx.core.djangoapps.embargo.test_utils import restrict_course from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme @@ -1775,7 +1774,6 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): """ Test for verification passed. """ - EmailMarketingConfiguration.objects.create(sailthru_verification_passed_template='test_template') data = { "EdX-ID": self.receipt_id, "Result": "PASS", @@ -1792,8 +1790,7 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'approved') self.assertEquals(response.content, 'OK!') - self.assertFalse(mock_log_error.called) - self.assertTrue(mock_sailthru_send.call_args[1], 'test_template') + self.assertEqual(len(mail.outbox), 1) @patch( 'lms.djangoapps.verify_student.ssencrypt.has_valid_signature', @@ -1805,11 +1802,10 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): """ Test for failed verification. """ - EmailMarketingConfiguration.objects.create(sailthru_verification_failed_template='test_template') data = { "EdX-ID": self.receipt_id, "Result": 'FAIL', - "Reason": 'Invalid photo', + "Reason": [{"photoIdReasons": ["Not provided"]}], "MessageType": 'Your photo doesn\'t meet standards.' } json_data = json.dumps(data) @@ -1823,10 +1819,9 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'denied') self.assertEqual(attempt.error_code, u'Your photo doesn\'t meet standards.') - self.assertEqual(attempt.error_msg, u'"Invalid photo"') + self.assertEqual(attempt.error_msg, u'[{"photoIdReasons": ["Not provided"]}]') self.assertEquals(response.content, 'OK!') - self.assertFalse(mock_log_error.called) - self.assertTrue(mock_sailthru_send.call_args[1], 'test_template') + self.assertEqual(len(mail.outbox), 1) @patch( 'lms.djangoapps.verify_student.ssencrypt.has_valid_signature', diff --git a/lms/djangoapps/verify_student/utils.py b/lms/djangoapps/verify_student/utils.py index 5fb4e11c9c..196828cb91 100644 --- a/lms/djangoapps/verify_student/utils.py +++ b/lms/djangoapps/verify_student/utils.py @@ -76,23 +76,6 @@ def verification_for_datetime(deadline, candidates): return verification -def send_verification_status_email(context): - """ - Send an email to inform learners about their verification status - using sailthru - """ - sailthru_client = SailthruClient(context['email_config'].sailthru_key, context['email_config'].sailthru_secret) - sailthru_response = sailthru_client.send( - email=context['email'], template=context['template'], - _vars=context['template_vars'] - ) - if not sailthru_response.is_ok(): - error = sailthru_response.get_error() - log.error("Error attempting to send verification status email to user: {} via Sailthru: {}".format( - context['email'], error.get_message() - )) - - def most_recent_verification(photo_id_verifications, sso_id_verifications, manual_id_verifications, most_recent_key): """ Return the most recent verification given querysets for photo, sso and manual verifications. diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index c55dd919d1..8e5851646e 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -13,10 +13,10 @@ from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.staticfiles.storage import staticfiles_storage from django.core.mail import send_mail -from django.urls import reverse from django.db import transaction from django.http import Http404, HttpResponse, HttpResponseBadRequest from django.shortcuts import redirect +from django.urls import reverse from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy @@ -32,13 +32,13 @@ from pytz import UTC from course_modes.models import CourseMode from edxmako.shortcuts import render_to_response, render_to_string -from email_marketing.models import EmailMarketingConfiguration from lms.djangoapps.commerce.utils import EcommerceService, is_account_activation_requirement_disabled 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.services import IDVerificationService from lms.djangoapps.verify_student.ssencrypt import has_valid_signature -from lms.djangoapps.verify_student.utils import is_verification_expiring_soon, send_verification_status_email +from lms.djangoapps.verify_student.tasks import send_verification_status_email +from lms.djangoapps.verify_student.utils import is_verification_expiring_soon from openedx.core.djangoapps.commerce.utils import ecommerce_api_client from openedx.core.djangoapps.embargo import api as embargo_api from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers @@ -1162,48 +1162,47 @@ def results_callback(request): return HttpResponseBadRequest("edX ID {} not found".format(receipt_id)) user = attempt.user - email_config = EmailMarketingConfiguration.current() verification_status_email_vars = { 'platform_name': settings.PLATFORM_NAME, } - email_context = { - 'email_config': email_config, - 'email': user.email - } if result == "PASS": log.debug("Approving verification for %s", receipt_id) attempt.approve() status = "approved" - if email_config.sailthru_verification_passed_template: - expiry_date = datetime.date.today() + datetime.timedelta( - days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] - ) - verification_status_email_vars['expiry_date'] = expiry_date.strftime("%m/%d/%Y") - verification_status_email_vars['subject'] = _("Your {platform_name} ID Verification Approved").format( - platform_name=settings.PLATFORM_NAME - ) - verification_status_email_vars['full_name'] = user.profile.name - email_context['template'] = email_config.sailthru_verification_passed_template - email_context['template_vars'] = verification_status_email_vars - send_verification_status_email(email_context) + expiry_date = datetime.date.today() + datetime.timedelta( + days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] + ) + verification_status_email_vars['expiry_date'] = expiry_date.strftime("%m/%d/%Y") + verification_status_email_vars['full_name'] = user.profile.name + subject = _("Your {platform_name} ID Verification 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) elif result == "FAIL": log.debug("Denying verification for %s", receipt_id) attempt.deny(json.dumps(reason), error_code=error_code) status = "denied" - if email_config.sailthru_verification_failed_template: - verification_status_email_vars['reason'] = reason - verification_status_email_vars['reverify_url'] = reverse("verify_student_reverify") - verification_status_email_vars['faq_url'] = configuration_helpers.get_value( - 'ID_VERIFICATION_SUPPORT_LINK', - settings.SUPPORT_SITE_LINK - ) - verification_status_email_vars['subject'] = _("Your {platform_name} Verification Has Been Denied").format( - platform_name=settings.PLATFORM_NAME - ) - email_context['template'] = email_config.sailthru_verification_failed_template - email_context['template_vars'] = verification_status_email_vars - send_verification_status_email(email_context) + reverify_url = '{}{}'.format(settings.LMS_ROOT_URL, reverse("verify_student_reverify")) + verification_status_email_vars['reasons'] = reason + verification_status_email_vars['reverify_url'] = reverify_url + verification_status_email_vars['faq_url'] = settings.ID_VERIFICATION_SUPPORT_LINK + subject = _("Your {platform_name} Verification Has Been Denied").format( + platform_name=settings.PLATFORM_NAME + ) + context = { + 'subject': subject, + 'template': 'emails/failed_verification_email.txt', + 'email': user.email, + 'email_vars': verification_status_email_vars + } + send_verification_status_email.delay(context) elif result == "SYSTEM FAIL": log.debug("System failure for %s -- resetting to must_retry", receipt_id) diff --git a/lms/templates/emails/failed_verification_email.txt b/lms/templates/emails/failed_verification_email.txt new file mode 100644 index 0000000000..bdedc646f4 --- /dev/null +++ b/lms/templates/emails/failed_verification_email.txt @@ -0,0 +1,38 @@ +<%! from django.utils.translation import ugettext as _ %> + + +${_("Sorry! The photos you submitted for ID verification were not accepted, for the following reason(s):")} +${_("The photo(s) of you:")} + +%for reason in reasons: + %if reason.get('userPhotoReasons'): + ${_("The photo of you:")} + + %endif + %if reason.get('photoIdReasons'): + ${_("The photo of your ID:")} + + %endif + %if reason.get('generalReasons'): + ${_("Other Reasons:")} + + %endif +%endfor + +${_("Resubmit Verification: {reverify_url}").format(reverify_url=reverify_url)} +${_("ID Verification FAQ: {faq_url}").format(faq_url=faq_url)} + +${_("Thank you,")} +${_("The {platform_name} team").format(platform_name=platform_name)} \ No newline at end of file diff --git a/lms/templates/emails/passed_verification_email.txt b/lms/templates/emails/passed_verification_email.txt new file mode 100644 index 0000000000..7be48113a7 --- /dev/null +++ b/lms/templates/emails/passed_verification_email.txt @@ -0,0 +1,10 @@ +<%! from django.utils.translation import ugettext as _ %> + + +${_("Hi {full_name}").format(full_name=full_name)} + +${_("Congratulations! Your ID verification process was successful.")} +${_("Your verification is effective for one year. It will expire on {expiry_date}").format(expiry_date=expiry_date)} + +${_("Thank you,")} +${_("The {platform_name} team").format(platform_name=platform_name)} \ No newline at end of file