Show verification expiration message on learner dashboard and allow them to reverify if expiration is X days away.
ECOM-2979
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
from datetime import datetime
|
||||
import urllib
|
||||
|
||||
from pytz import UTC
|
||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||
from oauth2_provider.models import (
|
||||
AccessToken as dot_access_token,
|
||||
@@ -11,7 +12,6 @@ from provider.oauth2.models import (
|
||||
AccessToken as dop_access_token,
|
||||
RefreshToken as dop_refresh_token
|
||||
)
|
||||
from pytz import UTC
|
||||
|
||||
import third_party_auth
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline, SoftwareSecurePhotoVerification
|
||||
@@ -22,6 +22,7 @@ from course_modes.models import CourseMode
|
||||
# we display on the student dashboard.
|
||||
VERIFY_STATUS_NEED_TO_VERIFY = "verify_need_to_verify"
|
||||
VERIFY_STATUS_SUBMITTED = "verify_submitted"
|
||||
VERIFY_STATUS_RESUBMITTED = "re_verify_submitted"
|
||||
VERIFY_STATUS_APPROVED = "verify_approved"
|
||||
VERIFY_STATUS_MISSED_DEADLINE = "verify_missed_deadline"
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY = "verify_need_to_reverify"
|
||||
@@ -40,6 +41,8 @@ def check_verify_status_by_course(user, course_enrollments):
|
||||
* VERIFY_STATUS_NEED_TO_VERIFY: The student has not yet submitted photos for verification.
|
||||
* VERIFY_STATUS_SUBMITTED: The student has submitted photos for verification,
|
||||
but has have not yet been approved.
|
||||
* VERIFY_STATUS_RESUBMITTED: The student has re-submitted photos for re-verification while
|
||||
they still have an active but expiring ID verification
|
||||
* VERIFY_STATUS_APPROVED: The student has been successfully verified.
|
||||
* VERIFY_STATUS_MISSED_DEADLINE: The student did not submit photos within the course's deadline.
|
||||
* VERIFY_STATUS_NEED_TO_REVERIFY: The student has an active verification, but it is
|
||||
@@ -80,6 +83,11 @@ def check_verify_status_by_course(user, course_enrollments):
|
||||
user, queryset=verifications
|
||||
)
|
||||
|
||||
# Retrieve expiration_datetime of most recent approved verification
|
||||
# To avoid another database hit, we re-use the queryset we have already retrieved.
|
||||
expiration_datetime = SoftwareSecurePhotoVerification.get_expiration_datetime(user, verifications)
|
||||
verification_expiring_soon = SoftwareSecurePhotoVerification.is_verification_expiring_soon(expiration_datetime)
|
||||
|
||||
# Retrieve verification deadlines for the enrolled courses
|
||||
enrolled_course_keys = [enrollment.course_id for enrollment in course_enrollments]
|
||||
course_deadlines = VerificationDeadline.deadlines_for_courses(enrolled_course_keys)
|
||||
@@ -112,9 +120,15 @@ def check_verify_status_by_course(user, course_enrollments):
|
||||
# Check whether the user was approved or is awaiting approval
|
||||
if relevant_verification is not None:
|
||||
if relevant_verification.status == "approved":
|
||||
status = VERIFY_STATUS_APPROVED
|
||||
if verification_expiring_soon:
|
||||
status = VERIFY_STATUS_NEED_TO_REVERIFY
|
||||
else:
|
||||
status = VERIFY_STATUS_APPROVED
|
||||
elif relevant_verification.status == "submitted":
|
||||
status = VERIFY_STATUS_SUBMITTED
|
||||
if verification_expiring_soon:
|
||||
status = VERIFY_STATUS_RESUBMITTED
|
||||
else:
|
||||
status = VERIFY_STATUS_SUBMITTED
|
||||
|
||||
# If the user didn't submit at all, then tell them they need to verify
|
||||
# If the deadline has already passed, then tell them they missed it.
|
||||
@@ -127,11 +141,12 @@ def check_verify_status_by_course(user, course_enrollments):
|
||||
)
|
||||
if status is None and not submitted:
|
||||
if deadline is None or deadline > datetime.now(UTC):
|
||||
if has_active_or_pending:
|
||||
# The user has an active verification, but the verification
|
||||
# is set to expire before the deadline. Tell the student
|
||||
# to reverify.
|
||||
status = VERIFY_STATUS_NEED_TO_REVERIFY
|
||||
if SoftwareSecurePhotoVerification.user_is_verified(user):
|
||||
if verification_expiring_soon:
|
||||
# The user has an active verification, but the verification
|
||||
# is set to expire within "EXPIRING_SOON_WINDOW" days (default is 4 weeks).
|
||||
# Tell the student to reverify.
|
||||
status = VERIFY_STATUS_NEED_TO_REVERIFY
|
||||
else:
|
||||
status = VERIFY_STATUS_NEED_TO_VERIFY
|
||||
else:
|
||||
|
||||
@@ -8,10 +8,12 @@ from nose.plugins.attrib import attr
|
||||
from pytz import UTC
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.test import override_settings
|
||||
|
||||
from student.helpers import (
|
||||
VERIFY_STATUS_NEED_TO_VERIFY,
|
||||
VERIFY_STATUS_SUBMITTED,
|
||||
VERIFY_STATUS_RESUBMITTED,
|
||||
VERIFY_STATUS_APPROVED,
|
||||
VERIFY_STATUS_MISSED_DEADLINE,
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY
|
||||
@@ -192,6 +194,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
# messaging relating to verification
|
||||
self._assert_course_verification_status(None)
|
||||
|
||||
@override_settings(VERIFY_STUDENT={"DAYS_GOOD_FOR": 5, "EXPIRING_SOON_WINDOW": 10})
|
||||
def test_verification_will_expire_by_deadline(self):
|
||||
# Expiration date in the future
|
||||
self._setup_mode_and_enrollment(self.FUTURE, "verified")
|
||||
@@ -202,16 +205,36 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
|
||||
attempt.mark_ready()
|
||||
attempt.submit()
|
||||
|
||||
# This attempt will expire tomorrow, before the course deadline
|
||||
attempt.created_at = attempt.created_at - timedelta(days=364)
|
||||
attempt.approve()
|
||||
attempt.save()
|
||||
|
||||
# Expect that the "verify now" message is hidden
|
||||
# (since the user isn't allowed to submit another attempt while
|
||||
# a verification is active).
|
||||
# Verify that learner can submit photos if verification is set to expire soon.
|
||||
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_REVERIFY)
|
||||
|
||||
@override_settings(VERIFY_STUDENT={"DAYS_GOOD_FOR": 5, "EXPIRING_SOON_WINDOW": 10})
|
||||
def test_reverification_submitted_with_current_approved_verificaiton(self):
|
||||
# Expiration date in the future
|
||||
self._setup_mode_and_enrollment(self.FUTURE, "verified")
|
||||
|
||||
# Create a verification attempt that is approved but expiring soon
|
||||
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
|
||||
attempt.mark_ready()
|
||||
attempt.submit()
|
||||
attempt.approve()
|
||||
attempt.save()
|
||||
|
||||
# Verify that learner can submit photos if verification is set to expire soon.
|
||||
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_REVERIFY)
|
||||
|
||||
# Submit photos for reverification
|
||||
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
|
||||
attempt.mark_ready()
|
||||
attempt.submit()
|
||||
|
||||
# Expect that learner has submitted photos for reverfication and his/her
|
||||
# previous verification is set to expired soon.
|
||||
self._assert_course_verification_status(VERIFY_STATUS_RESUBMITTED)
|
||||
|
||||
def test_verification_occurred_after_deadline(self):
|
||||
# Expiration date in the past
|
||||
self._setup_mode_and_enrollment(self.PAST, "verified")
|
||||
@@ -304,9 +327,10 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
"You still need to verify for this course.",
|
||||
"Verification not yet complete"
|
||||
],
|
||||
VERIFY_STATUS_SUBMITTED: ["Thanks for your patience as we process your request."],
|
||||
VERIFY_STATUS_APPROVED: ["You have already verified your ID!"],
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY: ["Your verification will expire soon!"]
|
||||
VERIFY_STATUS_SUBMITTED: ["You have submitted your verification information."],
|
||||
VERIFY_STATUS_RESUBMITTED: ["You have submitted your reverification information."],
|
||||
VERIFY_STATUS_APPROVED: ["You have successfully verified your ID with edX"],
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY: ["Your current verification will expire soon."]
|
||||
}
|
||||
|
||||
MODE_CLASSES = {
|
||||
@@ -315,7 +339,8 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
VERIFY_STATUS_SUBMITTED: "verified",
|
||||
VERIFY_STATUS_APPROVED: "verified",
|
||||
VERIFY_STATUS_MISSED_DEADLINE: "audit",
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY: "audit"
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY: "audit",
|
||||
VERIFY_STATUS_RESUBMITTED: "audit"
|
||||
}
|
||||
|
||||
def _assert_course_verification_status(self, status):
|
||||
|
||||
Reference in New Issue
Block a user