-
M. Zulqarnain authoredUnverifiedba861985
tasks.py 5.51 KiB
"""
Django Celery tasks for service status app
"""
import logging
from smtplib import SMTPException
import requests
import simplejson
from celery import Task, shared_task
from celery.states import FAILURE
from django.conf import settings
from django.core.mail import EmailMessage
from edx_django_utils.monitoring import set_code_owner_attribute
from common.djangoapps.edxmako.shortcuts import render_to_string
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
log = logging.getLogger(__name__)
class BaseSoftwareSecureTask(Task): # lint-amnesty, pylint: disable=abstract-method
"""
Base task class for use with Software Secure request.
Permits updating information about user attempt in correspondence to submitting
request to software secure.
"""
abstract = True
def on_success(self, retval, task_id, args, kwargs):
"""
Update SoftwareSecurePhotoVerification object corresponding to this
task with info about success.
Updates user verification attempt to "submitted" if the response was ok otherwise
set it to "must_retry".
Assumes `retval` is a dict containing the task's result, with the following keys:
'response_ok': boolean, indicating if the response was ok
'response_text': string, indicating the response text in case of failure.
"""
from .models import SoftwareSecurePhotoVerification
user_verification = SoftwareSecurePhotoVerification.objects.get(id=kwargs['user_verification_id'])
if retval['response_ok']:
user_verification.mark_submit()
log.info(
'Sent request to Software Secure for user: %r and receipt ID %r.',
user_verification.user.username,
user_verification.receipt_id,
)
return user_verification
user_verification.mark_must_retry(retval['response_text'])
def after_return(self, status, retval, task_id, args, kwargs, einfo):
"""
If max retries have reached and task status is still failing, mark user submission
with "must_retry" so that it can be retried latter.
"""
if self.max_retries == self.request.retries and status == FAILURE:
from .models import SoftwareSecurePhotoVerification
user_verification_id = kwargs['user_verification_id']
user_verification = SoftwareSecurePhotoVerification.objects.get(id=user_verification_id)
user_verification.mark_must_retry()
log.error(
'Software Secure submission failed for user %r, setting status to must_retry',
user_verification.user.username,
exc_info=True
)
@shared_task
@set_code_owner_attribute
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:
msg = EmailMessage(subject, message, from_addr, [dest_addr])
msg.content_subtype = 'html'
msg.send(fail_silently=False)
except SMTPException:
log.warning("Failure in sending verification status e-mail to %s", dest_addr)
@shared_task(
base=BaseSoftwareSecureTask,
bind=True,
default_retry_delay=settings.SOFTWARE_SECURE_REQUEST_RETRY_DELAY,
max_retries=settings.SOFTWARE_SECURE_RETRY_MAX_ATTEMPTS,
)
@set_code_owner_attribute
def send_request_to_ss_for_user(self, user_verification_id, copy_id_photo_from):
"""
Assembles a submission to Software Secure.
Keyword Arguments:
user_verification_id (int) SoftwareSecurePhotoVerification model object identifier.
copy_id_photo_from (SoftwareSecurePhotoVerification): If provided, re-send the ID photo
data from this attempt. This is used for re-verification, in which new face photos
are sent with previously-submitted ID photos.
Returns:
request.Response
"""
from .models import SoftwareSecurePhotoVerification
user_verification = SoftwareSecurePhotoVerification.objects.get(id=user_verification_id)
log.info('=>New Verification Task Received %r', user_verification.user.username)
try:
headers, body = user_verification.create_request(copy_id_photo_from)
# checkout PROD-1395 for detail why we are adding system certificate paths for verification.
response = requests.post(
settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_URL"],
headers=headers,
data=simplejson.dumps(body, indent=2, sort_keys=True, ensure_ascii=False).encode('utf-8'),
verify=settings.VERIFY_STUDENT["SOFTWARE_SECURE"]['CERT_VERIFICATION_PATH']
)
return {
'response_ok': getattr(response, 'ok', False),
'response_text': getattr(response, 'text', '')
}
except Exception as exc: # pylint: disable=broad-except
log.error(
(
'Retrying sending request to Software Secure for user: %r, Receipt ID: %r '
'attempt#: %s of %s'
),
user_verification.user.username,
user_verification.receipt_id,
self.request.retries,
settings.SOFTWARE_SECURE_RETRY_MAX_ATTEMPTS,
)
log.error(str(exc))
self.retry()