Commit 5c5ec5c4 by Awais Qureshi

Merge pull request #8157 from edx/awais786/ECOM-1494-deprecate-midcourse-code

Awais786/ecom 1494 deprecate midcourse code
parents ba4be3cd d0df6266
"""
Reverification admin
"""
from ratelimitbackend import admin
from reverification.models import MidcourseReverificationWindow
admin.site.register(MidcourseReverificationWindow)
...@@ -10,8 +10,10 @@ from util.validate_on_save import ValidateOnSaveMixin ...@@ -10,8 +10,10 @@ from util.validate_on_save import ValidateOnSaveMixin
from xmodule_django.models import CourseKeyField from xmodule_django.models import CourseKeyField
class MidcourseReverificationWindow(ValidateOnSaveMixin, models.Model): class MidcourseReverificationWindow(ValidateOnSaveMixin, models.Model): # TODO (ECOM-1494): Delete this model.
""" """
This model has been deprecated and will be removed in a future release.
Defines the start and end times for midcourse reverification for a particular course. Defines the start and end times for midcourse reverification for a particular course.
There can be many MidcourseReverificationWindows per course, but they cannot have There can be many MidcourseReverificationWindows per course, but they cannot have
......
"""
verify_student factories
"""
from reverification.models import MidcourseReverificationWindow
from factory.django import DjangoModelFactory
import pytz
from datetime import timedelta, datetime
from opaque_keys.edx.locations import SlashSeparatedCourseKey
# Factories are self documenting
# pylint: disable=missing-docstring
class MidcourseReverificationWindowFactory(DjangoModelFactory):
""" Creates a generic MidcourseReverificationWindow. """
FACTORY_FOR = MidcourseReverificationWindow
course_id = SlashSeparatedCourseKey.from_deprecated_string(u'MITx/999/Robot_Super_Course')
# By default this factory creates a window that is currently open
start_date = datetime.now(pytz.UTC) - timedelta(days=100)
end_date = datetime.now(pytz.UTC) + timedelta(days=100)
"""
Tests for Reverification models
"""
from datetime import timedelta, datetime
import pytz
from django.core.exceptions import ValidationError
from reverification.models import MidcourseReverificationWindow
from reverification.tests.factories import MidcourseReverificationWindowFactory
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
class TestMidcourseReverificationWindow(ModuleStoreTestCase):
""" Tests for MidcourseReverificationWindow objects """
def setUp(self, **kwargs):
super(TestMidcourseReverificationWindow, self).setUp()
self.course_id = CourseFactory.create().id
def test_window_open_for_course(self):
# Should return False if no windows exist for a course
self.assertFalse(MidcourseReverificationWindow.window_open_for_course(self.course_id))
# Should return False if a window exists, but it's not in the current timeframe
MidcourseReverificationWindowFactory(
course_id=self.course_id,
start_date=datetime.now(pytz.utc) - timedelta(days=10),
end_date=datetime.now(pytz.utc) - timedelta(days=5)
)
self.assertFalse(MidcourseReverificationWindow.window_open_for_course(self.course_id))
# Should return True if a non-expired window exists
MidcourseReverificationWindowFactory(
course_id=self.course_id,
start_date=datetime.now(pytz.utc) - timedelta(days=3),
end_date=datetime.now(pytz.utc) + timedelta(days=3)
)
self.assertTrue(MidcourseReverificationWindow.window_open_for_course(self.course_id))
def test_get_window(self):
# if no window exists, returns None
self.assertIsNone(MidcourseReverificationWindow.get_window(self.course_id, datetime.now(pytz.utc)))
# we should get the expected window otherwise
window_valid = MidcourseReverificationWindowFactory(
course_id=self.course_id,
start_date=datetime.now(pytz.utc) - timedelta(days=3),
end_date=datetime.now(pytz.utc) + timedelta(days=3)
)
self.assertEquals(
window_valid,
MidcourseReverificationWindow.get_window(self.course_id, datetime.now(pytz.utc))
)
def test_no_overlapping_windows(self):
window_valid = MidcourseReverificationWindow(
course_id=self.course_id,
start_date=datetime.now(pytz.utc) - timedelta(days=3),
end_date=datetime.now(pytz.utc) + timedelta(days=3)
)
window_valid.save()
with self.assertRaises(ValidationError):
window_invalid = MidcourseReverificationWindow(
course_id=self.course_id,
start_date=datetime.now(pytz.utc) - timedelta(days=2),
end_date=datetime.now(pytz.utc) + timedelta(days=4)
)
window_invalid.save()
...@@ -7,7 +7,6 @@ from mock import patch ...@@ -7,7 +7,6 @@ from mock import patch
from pytz import UTC from pytz import UTC
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.conf import settings from django.conf import settings
from reverification.tests.factories import MidcourseReverificationWindowFactory
from student.helpers import ( from student.helpers import (
VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_NEED_TO_VERIFY,
...@@ -260,13 +259,11 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase): ...@@ -260,13 +259,11 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
mode="verified" mode="verified"
) )
window = MidcourseReverificationWindowFactory(course_id=course2.id)
# The student has an approved verification # The student has an approved verification
attempt2 = SoftwareSecurePhotoVerification.objects.create(user=self.user) attempt2 = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt2.mark_ready() attempt2.mark_ready()
attempt2.submit() attempt2.submit()
attempt2.approve() attempt2.approve()
attempt2.window = window
attempt2.save() attempt2.save()
# Mark the attemp2 as approved so its date will appear on dasboard. # Mark the attemp2 as approved so its date will appear on dasboard.
......
...@@ -217,10 +217,6 @@ def reverification_info(course_enrollment_pairs, user, statuses): ...@@ -217,10 +217,6 @@ def reverification_info(course_enrollment_pairs, user, statuses):
dict["must_reverify"] = [some information] dict["must_reverify"] = [some information]
""" """
reverifications = defaultdict(list) reverifications = defaultdict(list)
for (course, enrollment) in course_enrollment_pairs:
info = single_course_reverification_info(user, course, enrollment)
if info:
reverifications[info.status].append(info)
# Sort the data by the reverification_end_date # Sort the data by the reverification_end_date
for status in statuses: for status in statuses:
...@@ -229,34 +225,6 @@ def reverification_info(course_enrollment_pairs, user, statuses): ...@@ -229,34 +225,6 @@ def reverification_info(course_enrollment_pairs, user, statuses):
return reverifications return reverifications
def single_course_reverification_info(user, course, enrollment): # pylint: disable=invalid-name
"""Returns midcourse reverification-related information for user with enrollment in course.
If a course has an open re-verification window, and that user has a verified enrollment in
the course, we return a tuple with relevant information. Returns None if there is no info..
Args:
user (User): the user we want to get information for
course (Course): the course in which the student is enrolled
enrollment (CourseEnrollment): the object representing the type of enrollment user has in course
Returns:
ReverifyInfo: (course_id, course_name, course_number, date, status)
OR, None: None if there is no re-verification info for this enrollment
"""
window = MidcourseReverificationWindow.get_window(course.id, datetime.datetime.now(UTC))
# If there's no window OR the user is not verified, we don't get reverification info
if (not window) or (enrollment.mode != "verified"):
return None
return ReverifyInfo(
course.id, course.display_name, course.number,
window.end_date.strftime('%B %d, %Y %X %p'),
SoftwareSecurePhotoVerification.user_status(user, window)[0],
SoftwareSecurePhotoVerification.display_status(user, window),
)
def get_course_enrollment_pairs(user, course_org_filter, org_filter_out_set): def get_course_enrollment_pairs(user, course_org_filter, org_filter_out_set):
""" """
Get the relevant set of (Course, CourseEnrollment) pairs to be displayed on Get the relevant set of (Course, CourseEnrollment) pairs to be displayed on
......
...@@ -245,11 +245,10 @@ class XQueueCertInterface(object): ...@@ -245,11 +245,10 @@ class XQueueCertInterface(object):
enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id) enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id)
mode_is_verified = (enrollment_mode == GeneratedCertificate.MODES.verified) mode_is_verified = (enrollment_mode == GeneratedCertificate.MODES.verified)
user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student) user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student)
user_is_reverified = SoftwareSecurePhotoVerification.user_is_reverified_for_all(course_id, student)
cert_mode = enrollment_mode cert_mode = enrollment_mode
if (mode_is_verified and user_is_verified and user_is_reverified): if mode_is_verified and user_is_verified:
template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(id=course_id) template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(id=course_id)
elif (mode_is_verified and not (user_is_verified and user_is_reverified)): elif mode_is_verified and not user_is_verified:
template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id) template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id)
cert_mode = GeneratedCertificate.MODES.honor cert_mode = GeneratedCertificate.MODES.honor
else: else:
......
...@@ -8,7 +8,6 @@ import json ...@@ -8,7 +8,6 @@ import json
import cgi import cgi
from datetime import datetime from datetime import datetime
from collections import defaultdict
from django.utils import translation from django.utils import translation
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ungettext from django.utils.translation import ungettext
...@@ -51,11 +50,9 @@ from .entrance_exams import ( ...@@ -51,11 +50,9 @@ from .entrance_exams import (
from courseware.models import StudentModule, StudentModuleHistory from courseware.models import StudentModule, StudentModuleHistory
from course_modes.models import CourseMode from course_modes.models import CourseMode
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
from open_ended_grading import open_ended_notifications from open_ended_grading import open_ended_notifications
from student.models import UserTestGroup, CourseEnrollment from student.models import UserTestGroup, CourseEnrollment
from student.views import single_course_reverification_info, is_course_blocked from student.views import is_course_blocked
from util.cache import cache, cache_if_anonymous from util.cache import cache, cache_if_anonymous
from xblock.fragment import Fragment from xblock.fragment import Fragment
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
...@@ -422,7 +419,6 @@ def _index_bulk_op(request, course_key, chapter, section, position): ...@@ -422,7 +419,6 @@ def _index_bulk_op(request, course_key, chapter, section, position):
'studio_url': studio_url, 'studio_url': studio_url,
'masquerade': masquerade, 'masquerade': masquerade,
'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"), 'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"),
'reverifications': fetch_reverify_banner_info(request, course_key),
} }
now = datetime.now(UTC()) now = datetime.now(UTC())
...@@ -676,7 +672,6 @@ def course_info(request, course_id): ...@@ -676,7 +672,6 @@ def course_info(request, course_id):
staff_access = has_access(request.user, 'staff', course) staff_access = has_access(request.user, 'staff', course)
masquerade = setup_masquerade(request, course_key, staff_access) # allow staff to masquerade on the info page masquerade = setup_masquerade(request, course_key, staff_access) # allow staff to masquerade on the info page
reverifications = fetch_reverify_banner_info(request, course_key)
studio_url = get_studio_url(course, 'course_info') studio_url = get_studio_url(course, 'course_info')
# link to where the student should go to enroll in the course: # link to where the student should go to enroll in the course:
...@@ -695,7 +690,6 @@ def course_info(request, course_id): ...@@ -695,7 +690,6 @@ def course_info(request, course_id):
'staff_access': staff_access, 'staff_access': staff_access,
'masquerade': masquerade, 'masquerade': masquerade,
'studio_url': studio_url, 'studio_url': studio_url,
'reverifications': reverifications,
'show_enroll_banner': show_enroll_banner, 'show_enroll_banner': show_enroll_banner,
'url_to_enroll': url_to_enroll, 'url_to_enroll': url_to_enroll,
} }
...@@ -1058,7 +1052,6 @@ def _progress(request, course_key, student_id): ...@@ -1058,7 +1052,6 @@ def _progress(request, course_key, student_id):
'grade_summary': grade_summary, 'grade_summary': grade_summary,
'staff_access': staff_access, 'staff_access': staff_access,
'student': student, 'student': student,
'reverifications': fetch_reverify_banner_info(request, course_key),
'passed': is_course_passed(course, grade_summary), 'passed': is_course_passed(course, grade_summary),
'show_generate_cert_btn': show_generate_cert_btn 'show_generate_cert_btn': show_generate_cert_btn
} }
...@@ -1072,23 +1065,6 @@ def _progress(request, course_key, student_id): ...@@ -1072,23 +1065,6 @@ def _progress(request, course_key, student_id):
return response return response
def fetch_reverify_banner_info(request, course_key):
"""
Fetches needed context variable to display reverification banner in courseware
"""
reverifications = defaultdict(list)
user = request.user
if not user.id:
return reverifications
enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
if enrollment is not None:
course = modulestore().get_course(course_key)
info = single_course_reverification_info(user, course, enrollment)
if info:
reverifications[info.status].append(info)
return reverifications
@login_required @login_required
@ensure_valid_course_key @ensure_valid_course_key
def submission_history(request, course_id, student_username, location): def submission_history(request, course_id, student_username, location):
......
...@@ -13,6 +13,7 @@ class SoftwareSecurePhotoVerificationAdmin(admin.ModelAdmin): ...@@ -13,6 +13,7 @@ class SoftwareSecurePhotoVerificationAdmin(admin.ModelAdmin):
Admin for the SoftwareSecurePhotoVerification table. Admin for the SoftwareSecurePhotoVerification table.
""" """
list_display = ('id', 'user', 'status', 'receipt_id', 'submitted_at', 'updated_at') list_display = ('id', 'user', 'status', 'receipt_id', 'submitted_at', 'updated_at')
exclude = ('window',) # TODO: Remove after deleting this field from the model.
search_fields = ( search_fields = (
'receipt_id', 'receipt_id',
) )
......
...@@ -12,7 +12,6 @@ from mock import patch ...@@ -12,7 +12,6 @@ from mock import patch
from nose.tools import assert_is_none, assert_equals, assert_raises, assert_true, assert_false # pylint: disable=E0611 from nose.tools import assert_is_none, assert_equals, assert_raises, assert_true, assert_false # pylint: disable=E0611
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from reverification.tests.factories import MidcourseReverificationWindowFactory
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
...@@ -220,17 +219,12 @@ class TestPhotoVerification(ModuleStoreTestCase): ...@@ -220,17 +219,12 @@ class TestPhotoVerification(ModuleStoreTestCase):
def test_fetch_photo_id_image(self): def test_fetch_photo_id_image(self):
user = UserFactory.create() user = UserFactory.create()
orig_attempt = SoftwareSecurePhotoVerification(user=user, window=None) orig_attempt = SoftwareSecurePhotoVerification(user=user)
orig_attempt.save() orig_attempt.save()
old_key = orig_attempt.photo_id_key old_key = orig_attempt.photo_id_key
window = MidcourseReverificationWindowFactory( new_attempt = SoftwareSecurePhotoVerification(user=user)
course_id=SlashSeparatedCourseKey("pony", "rainbow", "dash"),
start_date=datetime.now(pytz.utc) - timedelta(days=5),
end_date=datetime.now(pytz.utc) + timedelta(days=5)
)
new_attempt = SoftwareSecurePhotoVerification(user=user, window=window)
new_attempt.save() new_attempt.save()
new_attempt.fetch_photo_id_image() new_attempt.fetch_photo_id_image()
assert_equals(new_attempt.photo_id_key, old_key) assert_equals(new_attempt.photo_id_key, old_key)
...@@ -366,45 +360,6 @@ class TestPhotoVerification(ModuleStoreTestCase): ...@@ -366,45 +360,6 @@ class TestPhotoVerification(ModuleStoreTestCase):
status = SoftwareSecurePhotoVerification.user_status(user) status = SoftwareSecurePhotoVerification.user_status(user)
self.assertEquals(status, ('must_reverify', "No photo ID was provided.")) self.assertEquals(status, ('must_reverify', "No photo ID was provided."))
# test for correct status for reverifications
window = MidcourseReverificationWindowFactory()
reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window)
self.assertEquals(reverify_status, ('must_reverify', ''))
reverify_attempt = SoftwareSecurePhotoVerification(user=user, window=window)
reverify_attempt.status = 'approved'
reverify_attempt.save()
reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window)
self.assertEquals(reverify_status, ('approved', ''))
reverify_attempt.status = 'denied'
reverify_attempt.save()
reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window)
self.assertEquals(reverify_status, ('denied', ''))
reverify_attempt.status = 'approved'
# pylint: disable=protected-access
reverify_attempt.created_at = SoftwareSecurePhotoVerification._earliest_allowed_date() + timedelta(days=-1)
reverify_attempt.save()
reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window)
message = 'Your {platform_name} verification has expired.'.format(platform_name=settings.PLATFORM_NAME)
self.assertEquals(reverify_status, ('expired', message))
def test_display(self):
user = UserFactory.create()
window = MidcourseReverificationWindowFactory()
attempt = SoftwareSecurePhotoVerification(user=user, window=window, status="denied")
attempt.save()
# We expect the verification to be displayed by default
self.assertEquals(SoftwareSecurePhotoVerification.display_status(user, window), True)
# Turn it off
SoftwareSecurePhotoVerification.display_off(user.id)
self.assertEquals(SoftwareSecurePhotoVerification.display_status(user, window), False)
def test_parse_error_msg_success(self): def test_parse_error_msg_success(self):
user = UserFactory.create() user = UserFactory.create()
attempt = SoftwareSecurePhotoVerification(user=user) attempt = SoftwareSecurePhotoVerification(user=user)
...@@ -508,10 +463,9 @@ class TestPhotoVerification(ModuleStoreTestCase): ...@@ -508,10 +463,9 @@ class TestPhotoVerification(ModuleStoreTestCase):
@ddt.unpack @ddt.unpack
@ddt.data( @ddt.data(
{'enrollment_mode': 'honor', 'status': (None, None), 'output': 'N/A'}, {'enrollment_mode': 'honor', 'status': None, 'output': 'N/A'},
{'enrollment_mode': 'verified', 'status': (False, False), 'output': 'Not ID Verified'}, {'enrollment_mode': 'verified', 'status': False, 'output': 'Not ID Verified'},
{'enrollment_mode': 'verified', 'status': (True, True), 'output': 'ID Verified'}, {'enrollment_mode': 'verified', 'status': True, 'output': 'ID Verified'},
{'enrollment_mode': 'verified', 'status': (True, False), 'output': 'ID Verification Expired'}
) )
def test_verification_status_for_user(self, enrollment_mode, status, output): def test_verification_status_for_user(self, enrollment_mode, status, output):
""" """
...@@ -520,112 +474,12 @@ class TestPhotoVerification(ModuleStoreTestCase): ...@@ -520,112 +474,12 @@ class TestPhotoVerification(ModuleStoreTestCase):
user = UserFactory.create() user = UserFactory.create()
course = CourseFactory.create() course = CourseFactory.create()
user_reverified_path = 'verify_student.models.SoftwareSecurePhotoVerification.user_is_reverified_for_all'
with patch('verify_student.models.SoftwareSecurePhotoVerification.user_is_verified') as mock_verification: with patch('verify_student.models.SoftwareSecurePhotoVerification.user_is_verified') as mock_verification:
with patch(user_reverified_path) as mock_re_verification:
mock_verification.return_value = status[0]
mock_re_verification.return_value = status[1]
status = SoftwareSecurePhotoVerification.verification_status_for_user(user, course.id, enrollment_mode)
self.assertEqual(status, output)
@patch.dict(settings.VERIFY_STUDENT, FAKE_SETTINGS) mock_verification.return_value = status
@patch('verify_student.models.S3Connection', new=MockS3Connection)
@patch('verify_student.models.Key', new=MockKey)
@patch('verify_student.models.requests.post', new=mock_software_secure_post)
class TestMidcourseReverification(ModuleStoreTestCase):
""" Tests for methods that are specific to midcourse SoftwareSecurePhotoVerification objects """
def setUp(self):
super(TestMidcourseReverification, self).setUp()
self.course = CourseFactory.create()
self.user = UserFactory.create()
def test_user_is_reverified_for_all(self):
# if there are no windows for a course, this should return True
self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user))
# first, make three windows
window1 = MidcourseReverificationWindowFactory(
course_id=self.course.id,
start_date=datetime.now(pytz.UTC) - timedelta(days=15),
end_date=datetime.now(pytz.UTC) - timedelta(days=13),
)
window2 = MidcourseReverificationWindowFactory(
course_id=self.course.id,
start_date=datetime.now(pytz.UTC) - timedelta(days=10),
end_date=datetime.now(pytz.UTC) - timedelta(days=8),
)
window3 = MidcourseReverificationWindowFactory( status = SoftwareSecurePhotoVerification.verification_status_for_user(user, course.id, enrollment_mode)
course_id=self.course.id, self.assertEqual(status, output)
start_date=datetime.now(pytz.UTC) - timedelta(days=5),
end_date=datetime.now(pytz.UTC) - timedelta(days=3),
)
# make two SSPMidcourseReverifications for those windows
attempt1 = SoftwareSecurePhotoVerification(
status="approved",
user=self.user,
window=window1
)
attempt1.save()
attempt2 = SoftwareSecurePhotoVerification(
status="approved",
user=self.user,
window=window2
)
attempt2.save()
# should return False because only 2 of 3 windows have verifications
self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user))
attempt3 = SoftwareSecurePhotoVerification(
status="must_retry",
user=self.user,
window=window3
)
attempt3.save()
# should return False because the last verification exists BUT is not approved
self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user))
attempt3.status = "approved"
attempt3.save()
# should now return True because all windows have approved verifications
self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user))
def test_original_verification(self):
orig_attempt = SoftwareSecurePhotoVerification(user=self.user)
orig_attempt.save()
window = MidcourseReverificationWindowFactory(
course_id=self.course.id,
start_date=datetime.now(pytz.UTC) - timedelta(days=15),
end_date=datetime.now(pytz.UTC) - timedelta(days=13),
)
midcourse_attempt = SoftwareSecurePhotoVerification(user=self.user, window=window)
self.assertEquals(midcourse_attempt.original_verification(user=self.user), orig_attempt)
def test_user_has_valid_or_pending(self):
window = MidcourseReverificationWindowFactory(
course_id=self.course.id,
start_date=datetime.now(pytz.UTC) - timedelta(days=15),
end_date=datetime.now(pytz.UTC) - timedelta(days=13),
)
attempt = SoftwareSecurePhotoVerification(status="must_retry", user=self.user, window=window)
attempt.save()
assert_false(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window))
attempt.status = "approved"
attempt.save()
assert_true(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window))
@ddt.ddt @ddt.ddt
......
...@@ -48,7 +48,6 @@ from verify_student.models import ( ...@@ -48,7 +48,6 @@ from verify_student.models import (
SoftwareSecurePhotoVerification, VerificationCheckpoint, SoftwareSecurePhotoVerification, VerificationCheckpoint,
InCourseReverificationConfiguration, VerificationStatus InCourseReverificationConfiguration, VerificationStatus
) )
from reverification.tests.factories import MidcourseReverificationWindowFactory
from util.date_utils import get_default_time_display from util.date_utils import get_default_time_display
...@@ -1508,32 +1507,6 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): ...@@ -1508,32 +1507,6 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
self.assertIn('Result Unknown not understood', response.content) self.assertIn('Result Unknown not understood', response.content)
@mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature))
def test_reverification(self):
"""
Test software secure result for reverification window.
"""
data = {
"EdX-ID": self.receipt_id,
"Result": "PASS",
"Reason": "",
"MessageType": "You have been verified."
}
window = MidcourseReverificationWindowFactory(course_id=self.course_id)
self.attempt.window = window
self.attempt.save()
json_data = json.dumps(data)
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id).count(), 0)
response = self.client.post(
reverse('verify_student_results_callback'),
data=json_data,
content_type='application/json',
HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing',
HTTP_DATE='testdate'
)
self.assertEquals(response.content, 'OK!')
self.assertIsNotNone(CourseEnrollment.objects.get(course_id=self.course_id))
@mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature))
def test_in_course_reverify_disabled(self): def test_in_course_reverify_disabled(self):
""" """
Test for verification passed. Test for verification passed.
...@@ -1697,154 +1670,6 @@ class TestReverifyView(ModuleStoreTestCase): ...@@ -1697,154 +1670,6 @@ class TestReverifyView(ModuleStoreTestCase):
self.assertTrue(context['error']) self.assertTrue(context['error'])
class TestMidCourseReverifyView(ModuleStoreTestCase):
"""
Tests for the midcourse reverification views.
"""
def setUp(self):
super(TestMidCourseReverifyView, self).setUp()
self.user = UserFactory.create(username="rusty", password="test")
self.client.login(username="rusty", password="test")
self.course_key = SlashSeparatedCourseKey("Robot", "999", "Test_Course")
CourseFactory.create(org='Robot', number='999', display_name='Test Course')
patcher = patch('student.models.tracker')
self.mock_tracker = patcher.start()
self.addCleanup(patcher.stop)
@patch('verify_student.views.render_to_response', render_mock)
def test_midcourse_reverify_get(self):
url = reverse('verify_student_midcourse_reverify',
kwargs={"course_id": self.course_key.to_deprecated_string()})
response = self.client.get(url)
self.mock_tracker.emit.assert_any_call( # pylint: disable=maybe-no-member
'edx.course.enrollment.mode_changed',
{
'user_id': self.user.id,
'course_id': self.course_key.to_deprecated_string(),
'mode': "verified",
}
)
# Check that user entering the reverify flow was logged, and that it was the last call
self.mock_tracker.emit.assert_called_with( # pylint: disable=maybe-no-member
'edx.course.enrollment.reverify.started',
{
'user_id': self.user.id,
'course_id': self.course_key.to_deprecated_string(),
'mode': "verified",
}
)
self.assertTrue(self.mock_tracker.emit.call_count, 2) # pylint: disable=no-member
self.mock_tracker.emit.reset_mock() # pylint: disable=no-member
self.assertEquals(response.status_code, 200)
((_template, context), _kwargs) = render_mock.call_args # pylint: disable=unpacking-non-sequence
self.assertFalse(context['error'])
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
def test_midcourse_reverify_post_success(self):
window = MidcourseReverificationWindowFactory(course_id=self.course_key)
url = reverse('verify_student_midcourse_reverify', kwargs={'course_id': self.course_key.to_deprecated_string()})
response = self.client.post(url, {'face_image': ','})
self.mock_tracker.emit.assert_any_call( # pylint: disable=maybe-no-member
'edx.course.enrollment.mode_changed',
{
'user_id': self.user.id,
'course_id': self.course_key.to_deprecated_string(),
'mode': "verified",
}
)
# Check that submission event was logged, and that it was the last call
self.mock_tracker.emit.assert_called_with( # pylint: disable=maybe-no-member
'edx.course.enrollment.reverify.submitted',
{
'user_id': self.user.id,
'course_id': self.course_key.to_deprecated_string(),
'mode': "verified",
}
)
self.assertTrue(self.mock_tracker.emit.call_count, 2) # pylint: disable=no-member
self.mock_tracker.emit.reset_mock() # pylint: disable=no-member
self.assertEquals(response.status_code, 302)
try:
verification_attempt = SoftwareSecurePhotoVerification.objects.get(user=self.user, window=window)
self.assertIsNotNone(verification_attempt)
except ObjectDoesNotExist:
self.fail('No verification object generated')
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
def test_midcourse_reverify_post_failure_expired_window(self):
window = MidcourseReverificationWindowFactory(
course_id=self.course_key,
start_date=datetime.now(pytz.UTC) - timedelta(days=100),
end_date=datetime.now(pytz.UTC) - timedelta(days=50),
)
url = reverse('verify_student_midcourse_reverify', kwargs={'course_id': self.course_key.to_deprecated_string()})
response = self.client.post(url, {'face_image': ','})
self.assertEquals(response.status_code, 302)
with self.assertRaises(ObjectDoesNotExist):
SoftwareSecurePhotoVerification.objects.get(user=self.user, window=window)
@patch('verify_student.views.render_to_response', render_mock)
def test_midcourse_reverify_dash(self):
url = reverse('verify_student_midcourse_reverify_dash')
response = self.client.get(url)
# not enrolled in any courses
self.assertEquals(response.status_code, 200)
enrollment = CourseEnrollment.get_or_create_enrollment(self.user, self.course_key)
enrollment.update_enrollment(mode="verified", is_active=True)
MidcourseReverificationWindowFactory(course_id=self.course_key)
response = self.client.get(url)
# enrolled in a verified course, and the window is open
self.assertEquals(response.status_code, 200)
@patch('verify_student.views.render_to_response', render_mock)
def test_midcourse_reverify_invalid_course_id(self):
# if course id is invalid return 400
invalid_course_key = CourseLocator('edx', 'not', 'valid')
url = reverse('verify_student_midcourse_reverify', kwargs={'course_id': unicode(invalid_course_key)})
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
class TestReverificationBanner(ModuleStoreTestCase):
"""
Tests for toggling the "midcourse reverification failed" banner off.
"""
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
def setUp(self):
super(TestReverificationBanner, self).setUp()
self.user = UserFactory.create(username="rusty", password="test")
self.client.login(username="rusty", password="test")
self.course_id = 'Robot/999/Test_Course'
CourseFactory.create(org='Robot', number='999', display_name=u'Test Course é')
self.window = MidcourseReverificationWindowFactory(course_id=self.course_id)
url = reverse('verify_student_midcourse_reverify', kwargs={'course_id': self.course_id})
self.client.post(url, {'face_image': ','})
photo_verification = SoftwareSecurePhotoVerification.objects.get(user=self.user, window=self.window)
photo_verification.status = 'denied'
photo_verification.save()
def test_banner_display_off(self):
self.client.post(reverse('verify_student_toggle_failed_banner_off'))
photo_verification = SoftwareSecurePhotoVerification.objects.get(user=self.user, window=self.window)
self.assertFalse(photo_verification.display)
class TestInCourseReverifyView(ModuleStoreTestCase): class TestInCourseReverifyView(ModuleStoreTestCase):
""" """
Tests for the incourse reverification views. Tests for the incourse reverification views.
......
...@@ -96,42 +96,18 @@ urlpatterns = patterns( ...@@ -96,42 +96,18 @@ urlpatterns = patterns(
), ),
url( url(
r'^midcourse_reverify/{}/$'.format(settings.COURSE_ID_PATTERN),
views.MidCourseReverifyView.as_view(), # pylint: disable=no-value-for-parameter
name="verify_student_midcourse_reverify"
),
url(
r'^reverification_confirmation$', r'^reverification_confirmation$',
views.reverification_submission_confirmation, views.reverification_submission_confirmation,
name="verify_student_reverification_confirmation" name="verify_student_reverification_confirmation"
), ),
url( url(
r'^midcourse_reverification_confirmation$',
views.midcourse_reverification_confirmation,
name="verify_student_midcourse_reverification_confirmation"
),
url(
r'^midcourse_reverify_dash$',
views.midcourse_reverify_dash,
name="verify_student_midcourse_reverify_dash"
),
url(
r'^reverification_window_expired$', r'^reverification_window_expired$',
views.reverification_window_expired, views.reverification_window_expired,
name="verify_student_reverification_window_expired" name="verify_student_reverification_window_expired"
), ),
url( url(
r'^toggle_failed_banner_off$',
views.toggle_failed_banner_off,
name="verify_student_toggle_failed_banner_off"
),
url(
r'^submit-photos/$', r'^submit-photos/$',
views.submit_photos_for_verification, views.submit_photos_for_verification,
name="verify_student_submit_photos" name="verify_student_submit_photos"
......
...@@ -63,9 +63,6 @@ from django.contrib.auth.models import User ...@@ -63,9 +63,6 @@ from django.contrib.auth.models import User
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW = 'edx.course.enrollment.reverify.started'
EVENT_NAME_USER_SUBMITTED_MIDCOURSE_REVERIFY = 'edx.course.enrollment.reverify.submitted'
EVENT_NAME_USER_REVERIFICATION_REVIEWED_BY_SOFTWARESECURE = 'edx.course.enrollment.reverify.reviewed'
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW = 'edx.bi.reverify.started' EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW = 'edx.bi.reverify.started'
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY = 'edx.bi.reverify.submitted' EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY = 'edx.bi.reverify.submitted'
...@@ -1024,12 +1021,6 @@ def results_callback(request): ...@@ -1024,12 +1021,6 @@ def results_callback(request):
"Result {} not understood. Known results: PASS, FAIL, SYSTEM FAIL".format(result) "Result {} not understood. Known results: PASS, FAIL, SYSTEM FAIL".format(result)
) )
# If this is a reverification, log an event
if attempt.window:
course_id = attempt.window.course_id
course_enrollment = CourseEnrollment.get_or_create_enrollment(attempt.user, course_id)
course_enrollment.emit_event(EVENT_NAME_USER_REVERIFICATION_REVIEWED_BY_SOFTWARESECURE)
incourse_reverify_enabled = InCourseReverificationConfiguration.current().enabled incourse_reverify_enabled = InCourseReverificationConfiguration.current().enabled
if incourse_reverify_enabled: if incourse_reverify_enabled:
checkpoints = VerificationCheckpoint.objects.filter(photo_verification=attempt).all() checkpoints = VerificationCheckpoint.objects.filter(photo_verification=attempt).all()
...@@ -1102,122 +1093,6 @@ class ReverifyView(View): ...@@ -1102,122 +1093,6 @@ class ReverifyView(View):
return render_to_response("verify_student/photo_reverification.html", context) return render_to_response("verify_student/photo_reverification.html", context)
class MidCourseReverifyView(View):
"""
The mid-course reverification view.
Needs to perform these functions:
- take new face photo
- retrieve the old id photo
- submit these photos to photo verification service
Does not need to worry about pricing
"""
@method_decorator(login_required)
def get(self, request, course_id):
"""
display this view
"""
course_id = CourseKey.from_string(course_id)
course = modulestore().get_course(course_id)
if course is None:
raise Http404
course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, course_id)
course_enrollment.update_enrollment(mode="verified")
course_enrollment.emit_event(EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW)
context = {
"user_full_name": request.user.profile.name,
"error": False,
"course_id": course_id.to_deprecated_string(),
"course_name": course.display_name_with_default,
"course_org": course.display_org_with_default,
"course_num": course.display_number_with_default,
"reverify": True,
}
return render_to_response("verify_student/midcourse_photo_reverification.html", context)
@method_decorator(login_required)
def post(self, request, course_id):
"""
submits the reverification to SoftwareSecure
"""
try:
now = datetime.datetime.now(UTC)
course_id = CourseKey.from_string(course_id)
window = MidcourseReverificationWindow.get_window(course_id, now)
if window is None:
raise WindowExpiredException
attempt = SoftwareSecurePhotoVerification(user=request.user, window=window)
b64_face_image = request.POST['face_image'].split(",")[1]
attempt.upload_face_image(b64_face_image.decode('base64'))
attempt.fetch_photo_id_image()
attempt.mark_ready()
attempt.save()
attempt.submit()
course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, course_id)
course_enrollment.update_enrollment(mode="verified")
course_enrollment.emit_event(EVENT_NAME_USER_SUBMITTED_MIDCOURSE_REVERIFY)
return HttpResponseRedirect(reverse('verify_student_midcourse_reverification_confirmation'))
except WindowExpiredException:
log.exception(
"User {} attempted to re-verify, but the window expired before the attempt".format(request.user.id)
)
return HttpResponseRedirect(reverse('verify_student_reverification_window_expired'))
except Exception:
log.exception(
"Could not submit verification attempt for user {}".format(request.user.id)
)
context = {
"user_full_name": request.user.profile.name,
"error": True,
}
return render_to_response("verify_student/midcourse_photo_reverification.html", context)
@login_required
def midcourse_reverify_dash(request):
"""
Shows the "course reverification dashboard", which displays the reverification status (must reverify,
pending, approved, failed, etc) of all courses in which a student has a verified enrollment.
"""
user = request.user
course_enrollment_pairs = []
for enrollment in CourseEnrollment.enrollments_for_user(user):
try:
course_enrollment_pairs.append((modulestore().get_course(enrollment.course_id), enrollment))
except ItemNotFoundError:
log.error(u"User %s enrolled in non-existent course %s", user.username, enrollment.course_id)
statuses = ["approved", "pending", "must_reverify", "denied"]
reverifications = reverification_info(course_enrollment_pairs, user, statuses)
context = {
"user_full_name": user.profile.name,
'reverifications': reverifications,
'referer': request.META.get('HTTP_REFERER'),
'billing_email': settings.PAYMENT_SUPPORT_EMAIL,
}
return render_to_response("verify_student/midcourse_reverify_dash.html", context)
@login_required
@require_POST
def toggle_failed_banner_off(request):
"""
Finds all denied midcourse reverifications for a user and permanently toggles
the "Reverification Failed" banner off for those verifications.
"""
user_id = request.user.id
SoftwareSecurePhotoVerification.display_off(user_id)
return HttpResponse('Success')
@login_required @login_required
def reverification_submission_confirmation(_request): def reverification_submission_confirmation(_request):
""" """
...@@ -1227,14 +1102,6 @@ def reverification_submission_confirmation(_request): ...@@ -1227,14 +1102,6 @@ def reverification_submission_confirmation(_request):
@login_required @login_required
def midcourse_reverification_confirmation(_request): # pylint: disable=invalid-name
"""
Shows the user a confirmation page if the submission to SoftwareSecure was successful
"""
return render_to_response("verify_student/midcourse_reverification_confirmation.html")
@login_required
def reverification_window_expired(_request): def reverification_window_expired(_request):
""" """
Displays an error page if a student tries to submit a reverification, but the window Displays an error page if a student tries to submit a reverification, but the window
......
...@@ -132,7 +132,6 @@ ${fragment.foot_html()} ...@@ -132,7 +132,6 @@ ${fragment.foot_html()}
</%block> </%block>
<%include file="/dashboard/_dashboard_prompt_midcourse_reverify.html" />
% if default_tab: % if default_tab:
<%include file="/courseware/course_navigation.html" /> <%include file="/courseware/course_navigation.html" />
% else: % else:
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
<%static:css group='style-course'/> <%static:css group='style-course'/>
</%block> </%block>
<%include file="/dashboard/_dashboard_prompt_midcourse_reverify.html" />
% if show_enroll_banner: % if show_enroll_banner:
<div class="wrapper-msg urgency-low" id="failed-verification-banner"> <div class="wrapper-msg urgency-low" id="failed-verification-banner">
......
...@@ -37,7 +37,6 @@ from django.utils.http import urlquote_plus ...@@ -37,7 +37,6 @@ from django.utils.http import urlquote_plus
</script> </script>
</%block> </%block>
<%include file="/dashboard/_dashboard_prompt_midcourse_reverify.html" />
<%include file="/courseware/course_navigation.html" args="active_page='progress'" /> <%include file="/courseware/course_navigation.html" args="active_page='progress'" />
......
...@@ -42,21 +42,13 @@ ...@@ -42,21 +42,13 @@
edx.dashboard.legacy.init({ edx.dashboard.legacy.init({
dashboard: "${reverse('dashboard')}", dashboard: "${reverse('dashboard')}",
signInUser: "${reverse('signin_user')}", signInUser: "${reverse('signin_user')}",
changeEmailSettings: "${reverse('change_email_settings')}", changeEmailSettings: "${reverse('change_email_settings')}"
verifyToggleBannerFailedOff: "${reverse('verify_student_toggle_failed_banner_off')}",
}); });
}); });
</script> </script>
</%block> </%block>
<div class="dashboard-notifications" tabindex="-1"> <div class="dashboard-notifications" tabindex="-1">
% if reverifications["must_reverify"] or reverifications["denied"]:
## Section Element must be outside of the re-verify template. The template is re-used for courseware, and has separate styling.
<section class="dashboard-banner">
<%include file='dashboard/_dashboard_prompt_midcourse_reverify.html' />
</section>
% endif
%if message: %if message:
<section class="dashboard-banner"> <section class="dashboard-banner">
${message} ${message}
...@@ -175,8 +167,6 @@ ...@@ -175,8 +167,6 @@
<%include file='dashboard/_dashboard_status_verification.html' /> <%include file='dashboard/_dashboard_status_verification.html' />
<%include file='dashboard/_dashboard_reverification_sidebar.html' />
</ul> </ul>
</section> </section>
</section> </section>
......
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
% if reverifications:
% if reverifications["must_reverify"]:
% if len(reverifications["must_reverify"]) > 1:
<div class="wrapper-msg urgency-high">
<div class="msg msg-reverify has-actions">
<div class="msg-content">
<h2 class="title">${_("You need to re-verify to continue")}</h2>
<div class="copy">
<p class="activation-message">
${_("People change, and each year we ask you to re-verify your identity for our verified certificates. Take a minute now to help us renew your identity.")}
</p>
<ul class="reverify-list">
% for item in reverifications["must_reverify"]:
<li class="item">
${_('{course_name}: Re-verify by {date}').format(
course_name=u"<strong>{0}</strong>".format(item.course_name),
date=item.date
)}
</li>
% endfor
</ul>
</div>
</div>
<nav class="nav-actions" aria-label="${_('Notification Actions')}">
<h3 class="sr">${_('Notification Actions')}</h3>
<ul>
<li class="nav-item"><a class="btn action-primary action-reverify" href="${reverse('verify_student_midcourse_reverify_dash')}">Re-verify</a></li>
</ul>
</nav>
</div>
</div>
% elif reverifications["must_reverify"]:
<div class="wrapper-msg urgency-high">
<div class="msg msg-reverify has-actions">
<div class="msg-content">
<h2 class="title">${_("You need to re-verify to continue")}</h2>
% for item in reverifications["must_reverify"]:
<div class="copy">
<p class='activation-message'>
${_('To continue in the ID Verified track in {course_name}, you need to re-verify your identity by {date}.').format(
course_name=u"<strong>{}</strong>".format(item.course_name),
date=item.date
)}
</p>
</div>
</div>
<nav class="nav-actions" aria-label="${_('Notification Actions')}">
<h3 class="sr">${_('Notification Actions')}</h3>
<ul>
<li class="nav-item"><a class="btn action-primary action-reverify" href="${reverse('verify_student_midcourse_reverify_dash')}">Re-verify</a></li>
</ul>
</nav>
</div>
</div>
% endfor
%endif
%endif
%if reverifications["denied"] and denied_banner:
<div class="wrapper-msg urgency-high" id="failed-verification-banner">
<div class="msg msg-reverify is-dismissable">
<div class="msg-content">
<h2 class="title">${_("Your re-verification failed")}</h2>
% for item in reverifications["denied"]:
% if item.display:
<div class="copy">
<p class='activation-message'>
${_('Your re-verification for {course_name} failed and you are no longer eligible for a Verified Certificate. If you think this is in error, please contact us at {email}.').format(
course_name=u"<strong>{}</strong>".format(item.course_name),
email=u'<a class="contact-link" href="mailto:{email}">{email}</a>'.format(
email=billing_email
)
)}
</p>
</div>
% endif
% endfor
</div>
<div class="action-dismiss">
<button class="button-dismiss" id="failed-verification-button-dismiss"><i class="icon fa fa-times-circle"></i> <span class="sr">${_('Dismiss')}</span></button>
</div>
</div>
</div>
%endif
%endif
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
## TODO replace this with something a clever deisgn person approves of
## TODO replace this with a shiny loopy thing to actually print out all courses
% if reverifications["must_reverify"] or reverifications["pending"] or reverifications["denied"] or reverifications["approved"]:
<li class="status status-verification is-accepted">
<span class="title status-title">${_("Re-verification now open for:")}</span>
<ul class="reverify-status-list">
% if reverifications["must_reverify"]:
% for item in reverifications["must_reverify"]:
<li class="status-item is-open"><i class="icon fa fa-circle-blank"></i><span class="label">${_('Re-verify now:')}</span> <span class="course-name"><a href="${reverse('verify_student_midcourse_reverify_dash')}">${item.course_name}</span></a></li>
% endfor
%endif
% if reverifications["pending"]:
% for item in reverifications["pending"]:
<li class="status-item is-pending"><i class="icon fa fa-circle-blank"></i><span class="label">${_('Pending:')}</span> <span class="course-name"><a href="${reverse('verify_student_midcourse_reverify_dash')}">${item.course_name}</span></a></li>
% endfor
%endif
% if reverifications["denied"]:
% for item in reverifications["denied"]:
<li class="status-item is-denied"><i class="icon fa fa-times-circle"></i><span class="label">${_('Denied:')}</span> <span class="course-name"><a href="${reverse('verify_student_midcourse_reverify_dash')}">${item.course_name}</span></a></li>
% endfor
%endif
% if reverifications["approved"]:
% for item in reverifications["approved"]:
<li class="status-item is-approved"><i class="icon fa fa-check-square-o"></i><span class="label">${_('Approved:')}</span> <span class="course-name"><a href="${reverse('verify_student_midcourse_reverify_dash')}">${item.course_name}</span></a></li>
% endfor
%endif
</ul>
</li>
%endif
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%inherit file="../main.html" />
<%namespace name='static' file='/static_content.html'/>
<%block name="bodyclass">midcourse-reverification-process is-not-verified step-photos register</%block>
<%block name="pagetitle">${_("Re-Verify")}</%block>
<%block name="js_extra">
<script src="${static.url('js/vendor/responsive-carousel/responsive-carousel.js')}"></script>
<script src="${static.url('js/vendor/responsive-carousel/responsive-carousel.keybd.js')}"></script>
<script src="${static.url('js/verify_student/photocapture.js')}"></script>
</%block>
<%block name="content">
<div id="no-webcam" style="display: none;" class="wrapper-msg wrapper-msg-activate">
<div class=" msg msg-activate">
<i class="msg-icon fa fa-warning"></i>
<div class="msg-content">
<h3 class="title">${_("No Webcam Detected")}</h3>
<div class="copy">
<p>${_("You don't seem to have a webcam connected. Double-check that your webcam is connected and working to continue.")}</p>
</div>
</div>
</div>
</div>
<div id="no-flash" style="display: none;" class="wrapper-msg wrapper-msg-activate">
<div class=" msg msg-activate">
<i class="msg-icon fa fa-warning"></i>
<div class="msg-content">
<h3 class="title">${_("No Flash Detected")}</h3>
<div class="copy">
<p>${_("You don't seem to have Flash installed. {a_start} Get Flash {a_end} to continue your registration.").format(a_start='<a rel="external" href="http://get.adobe.com/flashplayer/">', a_end="</a>")}</p>
</div>
</div>
</div>
</div>
%if error:
<div id="submission-error" class="wrapper-msg wrapper-msg-activate">
<div class=" msg msg-activate">
<i class="msg-icon fa fa-warning"></i>
<div class="msg-content">
<h3 class="title">${_("Error submitting your images")}</h3>
<div class="copy">
<p>${_("Oops! Something went wrong. Please confirm your details and try again.")}</p>
</div>
</div>
</div>
</div>
%endif
<div class="container">
<section class="wrapper">
<div class="wrapper-content-main">
<article class="content-main">
<section class="wrapper">
<%include file="_verification_header.html" args="course_name=course_name" />
<div id="wrapper-facephoto" class="wrapper-view block-photo">
<div class="facephoto view">
<h3 class="title">${_("Re-Take Your Photo")}</h3>
<div class="instruction">
<p>${_("Use your webcam to take a picture of your face so we can match it with your original verification.")}</p>
</div>
<div class="wrapper-task">
<div id="facecam" class="task cam">
<div class="placeholder-cam" id="face_capture_div">
<div class="placeholder-art">
<p class="copy">${_("Don't see your picture? Make sure to allow your browser to use your camera when it asks for permission.")}</p>
</div>
<video id="face_video" autoplay></video><br/>
<canvas id="face_canvas" style="display:none;" width="640" height="480"></canvas>
</div>
<div class="controls photo-controls">
<ul class="list-controls">
<li class="control control-redo" id="face_reset_button">
<a class="action action-redo" href="">
<i class="icon fa fa-undo"></i> <span class="sr">${_("Retake")}</span>
</a>
</li>
<li class="control control-do" id="face_capture_button">
<a class="action action-do" href="">
<i class="icon fa fa-camera"></i><span class="sr">${_("Take photo")}</span>
</a>
</li>
<li class="control control-approve" id="face_approve_button">
<a class="action action-approve" href="">
<i class="icon fa fa-check-square-o"></i> <span class="sr">${_("Looks good")}</span>
</a>
</li>
</ul>
</div>
</div>
<div class="wrapper-help">
<div class="help help-task photo-tips facetips">
<h4 class="title">${_("Tips on taking a successful photo")}</h4>
<div class="copy">
<ul class="list-help">
<li class="help-item">${_("Make sure your face is well-lit")}</li>
<li class="help-item">${_("Be sure your entire face is inside the frame")}</li>
<li class="help-item">${_("Can we match the photo you took with the one on your ID?")}</li>
<li class="help-item">${_("Once in position, use the camera button {btn_icon} to capture your picture").format(btn_icon='<span class="example">(<i class="icon fa fa-camera"></i>)</span>')}</li>
<li class="help-item">${_("Use the checkmark button {btn_icon} once you are happy with the photo").format(btn_icon='<span class="example">(<i class="icon fa fa-check-square-o"></i>)</span>')}</li>
</ul>
</div>
</div>
<div class="help help-faq facefaq">
<h4 class="sr title">${_("Common Questions")}</h4>
<div class="copy">
<dl class="list-faq">
<dt class="faq-question">${_("Why do you need my photo?")}</dt>
<dd class="faq-answer">${_("As part of the verification process, we need your photo to confirm that you are you.")}</dd>
<dt class="faq-question">${_("What do you do with this picture?")}</dt>
<dd class="faq-answer">${_("We only use it to verify your identity. It is not displayed anywhere.")}</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="review-task review-task-name">
<h4 class="title">${_("Check Your Name")}</h4>
<div class="copy">
<p>${_("Make sure your full name on your {platform_name} account ({full_name}) matches the ID you originally submitted. We will also use this as the name on your certificate.").format(
full_name="<span id='full-name'>{name}</span>".format(name=user_full_name),
platform_name=settings.PLATFORM_NAME,
)}</p>
</div>
<ul class="list-actions">
<li class="action action-editname">
<a class="edit-name" rel="leanModal" href="#edit-name">${_("Edit your name")}</a>
</li>
</ul>
</div>
<img id="face_image" src="" style="display: none;"/>
<nav class="nav-wizard" id="face_id_next_button_nav" aria-label="${_('Verification')}">
<div class="prompt-verify">
<h3 class="title">Before proceeding, please review carefully</h3>
<p class="copy"> ${_("Once you verify your photo looks good and your name is correct, you can finish your re-verification and return to your course. <strong>Note: You will not have another chance to re-verify.</strong>")}</p>
<ul class="list-actions">
<li class="action action-verify">
<input type="checkbox" name="match" id="confirm_pics_good" />
<label for="confirm_pics_good">${_("Yes! You can confirm my identity with this information.")}</label>
</li>
</ul>
</div>
<ol class="wizard-steps">
<li class="wizard-step step-proceed">
<form id="reverify_form" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
<input type="hidden" name="course_id" value="${course_id | h}">
<input class="action-primary disabled" type="button" id="midcourse_reverify_button" value="${_('Submit photos &amp; re-verify')}" name="payment">
</form>
</li>
</ol>
</nav>
</div> <!-- /view -->
</div> <!-- /wrapper-view -->
</section>
</article>
</div> <!-- /wrapper-content-main -->
<%include file="_reverification_support.html" />
</section>
</div>
<%include file="_modal_editname.html" />
</%block>
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%inherit file="../main.html" />
<%namespace name='static' file='/static_content.html'/>
<%block name="bodyclass">register verification-process is-not-verified step-confirmation</%block>
<%block name="pagetitle">${_("Re-Verification Submission Confirmation")}</%block>
<%block name="content">
<div class="container">
<section class="wrapper">
<div class="wrapper-content-main">
<article class="content-main">
<section class="content-confirmation">
<div class="wrapper-view">
<div class="view">
## Translators: 'Credentials' refer to photographs the user has taken to verify their identity.
<h3 class="title">${_("Your Credentials Have Been Updated")}</h3>
<div class="instruction">
<p>${_("We have received your re-verification details and submitted them for review. Your dashboard will show the notification status once the review is complete.")}</p>
<p>${_("Please note: The professor may ask you to re-verify again at other key points in the course.")}</p>
</div>
<div class="actions-next">
<ol class="list-nav">
<li class="nav-item conditional">
<a class="action action-primary" href="${reverse('verify_student_midcourse_reverify_dash')}">${_("Complete your other re-verifications")}</a>
</li>
<li class="nav-item">
<a class="action" href="${reverse('dashboard')}">${_("Return to where you left off")}</a>
</li>
</ol>
</div>
</div> <!-- /view -->
</div> <!-- /wrapper-view -->
</section>
</article>
</div> <!-- /wrapper-content-main -->
<%include file="_reverification_support.html" />
</section>
</div>
</%block>
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%inherit file="../main.html" />
<%block name="bodyclass">midcourse-reverification-process step-dash register</%block>
<%block name="pagetitle">${_("Reverification Status")}</%block>
<%block name="content">
<div class="container">
<section class="wrapper">
<div class="wrapper-content-main">
<article class="content-main">
<h2 class="title">${_("You are in the ID Verified track")}</h2>
<div class="copy">
% if reverifications["must_reverify"]: # If you have reverifications to do
% if len(reverifications["must_reverify"]) > 1: # If you have >1 reverifications
<div class="wrapper-reverify-open">
<h3 class="title">${_("You currently need to re-verify for the following courses:")}</h3>
<ul class="reverification-list">
% for item in reverifications["must_reverify"]: # for 1st
<li class="item">
<div class="course-info">
<h3 class="course-name">${item.course_name} (${item.course_number})</h3>
<p class="deadline">${_('Re-verify by {date}').format(date="<strong>" + item.date + "</strong>")}</p>
</div>
<p class="reverify-status"><a class="btn action-primary action-reverify" href="${reverse('verify_student_midcourse_reverify', kwargs={'course_id': item.course_id.to_deprecated_string()})}">
${_("Re-verify for {course_number}").format(course_number=item.course_number)}
</a></p>
</li>
% endfor
</ul>
</div>
% else: # You only have one reverification
<div class="wrapper-reverify-open">
<h3 class="title">${_("You currently need to re-verify for the following course:")}</h3>
<ul class="reverification-list">
% for item in reverifications["must_reverify"]:
<li class="item">
<div class="course-info">
<h3 class="course-name">${item.course_name} (${item.course_number})</h3>
<p class="deadline">${_('Re-verify by {date}').format(date="<strong>" + item.date + "</strong>")}</p>
</div>
<p class="reverify-status"><a class="btn action-primary action-reverify" href="${reverse('verify_student_midcourse_reverify', kwargs={'course_id': item.course_id.to_deprecated_string()})}">
${_("Re-verify for {course_number}").format(course_number=item.course_number)}
</a></p>
</li>
% endfor
</ul>
</div>
%endif
% else:
<div class="wrapper-reverify-open">
<p class="title">${_("You have no re-verifications at present.")}</p>
</div>
%endif
% if reverifications["pending"] or reverifications["approved"] or reverifications["denied"]:
<div class="wrapper-reverify-status">
<h3 class="title">${_("The status of your submitted re-verifications:")}</h3>
<ul class="reverification-list reverification-status">
% for item in reverifications["pending"]:
<li class="item pending">
<div class="course-info">
<h3 class="course-name">${item.course_name} (${item.course_number})</h3>
<p class="deadline">${_('Re-verify by {date}').format(date="<strong>" + item.date + "</strong>")}</p>
</div>
<p class="reverify-status pending">${_("Pending")}</p>
</li>
% endfor
% for item in reverifications["approved"]:
<li class="item complete">
<div class="course-info">
<h3 class="course-name">${item.course_name} (${item.course_number})</h3>
<p class="deadline">${_('Re-verify by {date}').format(date="<strong>" + item.date + "</strong>")}</p>
</div>
<p class="reverify-status complete">${_("Complete")}</p>
</li>
% endfor
% for item in reverifications["denied"]:
<li class="item failed">
<div class="course-info">
<h3 class="course-name">${item.course_name} (${item.course_number})</h3>
<p class="deadline">${_('Re-verify by {date}').format(date="<strong>" + item.date + "</strong>")}</p>
</div>
<p class="reverify-status">${_("Failed")}</p>
</li>
% endfor
</ul>
</div>
% endif
% if reverifications["must_reverify"]:
<p class="support">${_("Don't want to re-verify right now?") + u" {a_start}{text}{a_end}".format(
text=_("Return to where you left off"),
a_start='<a href="{url}">'.format(url=referer),
a_end="</a>",
)}</p>
% else:
<p class="support">${u"{a_start}{text}{a_end}".format(
text=_("Return to where you left off"),
a_start='<a href="{url}">'.format(url=referer),
a_end="</a>",
)}</p>
% endif
</div>
<div class="wrapper-reverification-help list-faq">
<div class="faq-item">
<h3 class="title faq-question">${_("Why do I need to re-verify?")}</h3>
<div class="copy faq-answer">
<p>${_("At key points in a course, the professor will ask you to re-verify your identity by submitting a new photo of your face. We will send the new photo to be matched up with the photo of the original ID you submitted when you signed up for the course. If you are taking multiple courses, you may need to re-verify multiple times, once for every important point in each course you are taking as a verified student.")}</p>
</div>
</div>
<div class="faq-item">
<h3 class="title faq-question">${_("What will I need to re-verify?")}</h3>
<div class="copy faq-answer">
<p>${_("Because you are just confirming that you are still you, the only thing you will need to do to re-verify is to <b>submit a new photo of your face with your webcam</b>. The process is quick and you will be brought back to where you left off so you can keep on learning.")}</p>
<p>${_("If you changed your name during the semester and it no longer matches the original ID you submitted, you will need to re-edit your name to match as well.")}</p>
</div>
</div>
<div class="faq-item">
<h3 class="title faq-question">${_("What if I have trouble with my re-verification?")}</h3>
<div class="copy faq-answer">
<p>${_('Because of the short time that re-verification is open, you <strong>will not be able to correct a failed verification</strong>. If you think there was an error in the review, please contact us at {email}').format(email='<a class="contact-link" href="mailto:{email}"">{email}</a>.'.format(email=billing_email))}</p>
</div>
</div>
</div>
</article>
</div> <!-- /wrapper-content-main -->
</section>
</div>
</%block>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment