Files
edx-platform/lms/djangoapps/verify_student/api.py
Isaac Lee a3871cda22 API functions for the new generic VerificationAttempt model in the verify_student app (#35338)
* feat: add VerificationAttempt model to verify_student application

This commits adds a VerificationAttempt model to store implementation and provider agnostic information about identity verification attempts in the platform.

* feat: add api for VerificationAttempt model

* fix: error handling for update

- added tests accordingly
- also took care of some nits

* chore: lint

* chore: lint for equals spaces

* feat: using generic update function instead

- can now update name, status, and exp. date on generic attempts
- changed tests accordingly
- a few nits

* chore: fix docstring args

* fix: corrected status validation

- reverted to old status validation method
- fixed tests accordingly

* fix: datetime, status, and annotation fixes

- expiration_datetime can be updated to None
- VerificationAttemptStatus is now StrEnum
- Added type annotations for api functions

---------

Co-authored-by: michaelroytman <mroytman@edx.org>
2024-09-09 13:16:29 -04:00

128 lines
4.5 KiB
Python

"""
API module.
"""
import logging
from django.conf import settings
from django.contrib.auth import get_user_model
from django.utils.translation import gettext as _
from datetime import datetime
from typing import Optional
from lms.djangoapps.verify_student.emails import send_verification_approved_email
from lms.djangoapps.verify_student.exceptions import VerificationAttemptInvalidStatus
from lms.djangoapps.verify_student.models import VerificationAttempt
from lms.djangoapps.verify_student.statuses import VerificationAttemptStatus
from lms.djangoapps.verify_student.tasks import send_verification_status_email
log = logging.getLogger(__name__)
User = get_user_model()
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)
def create_verification_attempt(user: User, name: str, status: str, expiration_datetime: Optional[datetime] = None):
"""
Create a verification attempt.
This method is intended to be used by IDV implementation plugins to create VerificationAttempt instances.
Args:
user (User): the user (usually a learner) performing the verification attempt
name (string): the name being ID verified
status (string): the initial status of the verification attempt
expiration_datetime (datetime, optional): When the verification attempt expires. Defaults to None.
Returns:
id (int): The id of the created VerificationAttempt instance
"""
verification_attempt = VerificationAttempt.objects.create(
user=user,
name=name,
status=status,
expiration_datetime=expiration_datetime,
)
return verification_attempt.id
def update_verification_attempt(
attempt_id: int,
name: Optional[str] = None,
status: Optional[str] = None,
expiration_datetime: Optional[datetime] = None
):
"""
Update a verification attempt.
This method is intended to be used by IDV implementation plugins to update VerificationAttempt instances.
Arguments:
* attempt_id (int): the verification attempt id of the attempt to update
* name (string, optional): the new name being ID verified
* status (string, optional): the new status of the verification attempt
* expiration_datetime (datetime, optional): The new expiration date and time
Returns:
* None
"""
try:
attempt = VerificationAttempt.objects.get(id=attempt_id)
except VerificationAttempt.DoesNotExist:
log.error(
f'VerificationAttempt with id {attempt_id} was not found '
f'when updating the attempt to status={status}',
)
raise
if name is not None:
attempt.name = name
if status is not None:
attempt.status = status
status_list = list(VerificationAttemptStatus)
if status not in status_list:
log.error(
'Attempted to call update_verification_attempt called with invalid status: %(status)s. '
'Status must be one of: %(status_list)s',
{
'status': status,
'status_list': VerificationAttempt.STATUS_CHOICES,
},
)
raise VerificationAttemptInvalidStatus
# NOTE: Generally, we only set the expiration date from the time that an IDV attempt is marked approved,
# so we allow expiration_datetime to = None for other status updates (e.g. pending).
attempt.expiration_datetime = expiration_datetime
attempt.save()