Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
a975b838
Commit
a975b838
authored
Apr 29, 2015
by
aamir-khan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ECOM-1476: sending email on software secure response initial work
parent
411df0ae
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
598 additions
and
13 deletions
+598
-13
lms/djangoapps/verify_student/models.py
+16
-0
lms/djangoapps/verify_student/tests/test_models.py
+34
-0
lms/djangoapps/verify_student/tests/test_views.py
+393
-6
lms/djangoapps/verify_student/views.py
+112
-7
lms/templates/emails/reverification_processed.txt
+43
-0
No files found.
lms/djangoapps/verify_student/models.py
View file @
a975b838
...
...
@@ -1107,6 +1107,22 @@ class VerificationStatus(models.Model):
status
=
"submitted"
)
.
count
()
@classmethod
def
get_location_id
(
cls
,
photo_verification
):
""" Return the location id of xblock
Args:
photo_verification(SoftwareSecurePhotoVerification): SoftwareSecurePhotoVerification object
Return:
Location Id of xblock if any else empty string
"""
try
:
ver_status
=
cls
.
objects
.
filter
(
checkpoint__photo_verification
=
photo_verification
)
.
latest
()
return
ver_status
.
location_id
except
cls
.
DoesNotExist
:
return
""
class
InCourseReverificationConfiguration
(
ConfigurationModel
):
"""Configure in-course re-verification.
...
...
lms/djangoapps/verify_student/tests/test_models.py
View file @
a975b838
...
...
@@ -772,6 +772,40 @@ class VerificationStatusTest(ModuleStoreTestCase):
list
(
self
.
check_point2
.
checkpoint_status
.
all
()
.
values_list
(
'location_id'
,
flat
=
True
))
)
def
test_get_location_id
(
self
):
""" Getting location id for a specific checkpoint """
# creating software secure attempt against checkpoint
self
.
check_point1
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
# add initial verification status for checkpoint
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point1
,
user
=
self
.
user
,
status
=
'submitted'
,
location_id
=
self
.
dummy_reverification_item_id_1
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
self
.
assertIsNotNone
(
VerificationStatus
.
get_location_id
(
attempt
))
self
.
assertEqual
(
VerificationStatus
.
get_location_id
(
None
),
''
)
def
test_get_user_attempts
(
self
):
# adding verification status
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point1
,
user
=
self
.
user
,
status
=
'submitted'
,
location_id
=
self
.
dummy_reverification_item_id_1
)
self
.
assertEqual
(
VerificationStatus
.
get_user_attempts
(
course_key
=
self
.
course
.
id
,
user_id
=
self
.
user
.
id
,
related_assessment
=
'midterm'
,
location_id
=
self
.
dummy_reverification_item_id_1
),
1
)
class
SkippedReverificationTest
(
ModuleStoreTestCase
):
"""Tests for the SkippedReverification model. """
...
...
lms/djangoapps/verify_student/tests/test_views.py
View file @
a975b838
...
...
@@ -9,7 +9,8 @@ from uuid import uuid4
from
django.test.utils
import
override_settings
import
mock
from
mock
import
patch
,
Mock
from
mock
import
patch
,
Mock
,
ANY
from
django.utils
import
timezone
import
pytz
import
ddt
from
django.test.client
import
Client
...
...
@@ -24,8 +25,10 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.tests.factories
import
check_mongo_calls
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locator
import
CourseLocator
from
microsite_configuration
import
microsite
from
openedx.core.djangoapps.user_api.accounts.api
import
get_account_settings
from
commerce.tests
import
TEST_PAYMENT_DATA
,
TEST_API_URL
,
TEST_API_SIGNING_KEY
...
...
@@ -38,16 +41,15 @@ from embargo.test_utils import restrict_course
from
util.testing
import
UrlResetMixin
from
verify_student.views
import
(
checkout_with_ecommerce_service
,
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
,
PayAndVerifyView
,
render_to_response
,
render_to_response
,
PayAndVerifyView
,
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
,
_send_email
,
_compose_message_reverification_email
)
from
verify_student.models
import
(
SoftwareSecurePhotoVerification
,
VerificationCheckpoint
,
InCourseReverificationConfiguration
InCourseReverificationConfiguration
,
VerificationStatus
)
from
reverification.tests.factories
import
MidcourseReverificationWindowFactory
from
util.date_utils
import
get_default_time_display
def
mock_render_to_response
(
*
args
,
**
kwargs
):
...
...
@@ -1531,6 +1533,121 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
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
):
"""
Test for verification passed.
"""
data
=
{
"EdX-ID"
:
self
.
receipt_id
,
"Result"
:
"PASS"
,
"Reason"
:
""
,
"MessageType"
:
"You have been verified."
}
json_data
=
json
.
dumps
(
data
)
response
=
self
.
client
.
post
(
reverse
(
'verify_student_results_callback'
),
data
=
json_data
,
content_type
=
'application/json'
,
HTTP_AUTHORIZATION
=
'test BBBBBBBBBBBBBBBBBBBB:testing'
,
HTTP_DATE
=
'testdate'
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
get
(
receipt_id
=
self
.
receipt_id
)
self
.
assertEqual
(
attempt
.
status
,
u'approved'
)
self
.
assertEquals
(
response
.
content
,
'OK!'
)
# Verify that photo submission confirmation email was sent
self
.
assertEqual
(
len
(
mail
.
outbox
),
0
)
user_status
=
VerificationStatus
.
objects
.
filter
(
user
=
self
.
user
)
.
count
()
self
.
assertEqual
(
user_status
,
0
)
@mock.patch
(
'verify_student.ssencrypt.has_valid_signature'
,
mock
.
Mock
(
side_effect
=
mocked_has_valid_signature
))
def
test_pass_in_course_reverify_result
(
self
):
"""
Test for verification passed.
"""
self
.
create_reverification_xblock
()
incourse_reverify_enabled
=
InCourseReverificationConfiguration
.
current
()
incourse_reverify_enabled
.
enabled
=
True
incourse_reverify_enabled
.
save
()
data
=
{
"EdX-ID"
:
self
.
receipt_id
,
"Result"
:
"PASS"
,
"Reason"
:
""
,
"MessageType"
:
"You have been verified."
}
json_data
=
json
.
dumps
(
data
)
response
=
self
.
client
.
post
(
reverse
(
'verify_student_results_callback'
),
data
=
json_data
,
content_type
=
'application/json'
,
HTTP_AUTHORIZATION
=
'test BBBBBBBBBBBBBBBBBBBB:testing'
,
HTTP_DATE
=
'testdate'
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
get
(
receipt_id
=
self
.
receipt_id
)
self
.
assertEqual
(
attempt
.
status
,
u'approved'
)
self
.
assertEquals
(
response
.
content
,
'OK!'
)
# Verify that photo re-verification status email was sent
self
.
assertEqual
(
len
(
mail
.
outbox
),
1
)
self
.
assertEqual
(
"Re-verification Status"
,
mail
.
outbox
[
0
]
.
subject
)
@mock.patch
(
'verify_student.views._send_email'
)
@mock.patch
(
'verify_student.ssencrypt.has_valid_signature'
,
mock
.
Mock
(
side_effect
=
mocked_has_valid_signature
))
def
test_reverification_on_callback
(
self
,
mock_send_email
):
"""
Test software secure callback flow for re-verification.
"""
# Create the 'edx-reverification-block' in course tree
self
.
create_reverification_xblock
()
# create dummy data for software secure photo verification result callback
data
=
{
"EdX-ID"
:
self
.
receipt_id
,
"Result"
:
"PASS"
,
"Reason"
:
""
,
"MessageType"
:
"You have been verified."
}
json_data
=
json
.
dumps
(
data
)
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
.
assertEqual
(
response
.
content
,
'OK!'
)
# now check that '_send_email' method is called on result callback
# with required parameters
subject
=
"Re-verification Status"
mock_send_email
.
assert_called_once_with
(
self
.
user
.
id
,
subject
,
ANY
)
def
create_reverification_xblock
(
self
):
""" Create the reverification xblock
"""
# Create checkpoint
checkpoint
=
VerificationCheckpoint
(
course_id
=
self
.
course_id
,
checkpoint_name
=
"midterm"
)
checkpoint
.
save
()
# Add a re-verification attempt
checkpoint
.
add_verification_attempt
(
self
.
attempt
)
# Create the 'edx-reverification-block' in course tree
section
=
ItemFactory
.
create
(
parent
=
self
.
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
subsection
=
ItemFactory
.
create
(
parent
=
section
,
category
=
'sequential'
,
display_name
=
'Test Subsection'
)
vertical
=
ItemFactory
.
create
(
parent
=
subsection
,
category
=
'vertical'
,
display_name
=
'Test Unit'
)
reverification
=
ItemFactory
.
create
(
parent
=
vertical
,
category
=
'edx-reverification-block'
,
display_name
=
'Test Verification Block'
)
# Add a re-verification attempt status for the user
VerificationStatus
.
add_verification_status
(
checkpoint
,
self
.
user
,
"submitted"
,
reverification
.
location
)
class
TestReverifyView
(
ModuleStoreTestCase
):
"""
...
...
@@ -1922,3 +2039,273 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
"checkpoint_name"
:
checkpoint
,
"usage_id"
:
unicode
(
self
.
reverification_location
)
})
class
TestEmailMessageWithCustomICRVBlock
(
ModuleStoreTestCase
):
"""
Test email sending on re-verification
"""
def
build_course
(
self
):
"""
Build up a course tree with a Reverificaiton xBlock.
"""
# pylint: disable=attribute-defined-outside-init
self
.
course_key
=
SlashSeparatedCourseKey
(
"Robot"
,
"999"
,
"Test_Course"
)
self
.
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
self
.
due_date
=
datetime
(
2015
,
6
,
22
,
tzinfo
=
pytz
.
UTC
)
# Create the course modes
for
mode
in
(
'audit'
,
'honor'
,
'verified'
):
min_price
=
0
if
mode
in
[
"honor"
,
"audit"
]
else
1
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course_key
,
min_price
=
min_price
)
# Create the 'edx-reverification-block' in course tree
section
=
ItemFactory
.
create
(
parent
=
self
.
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
subsection
=
ItemFactory
.
create
(
parent
=
section
,
category
=
'sequential'
,
display_name
=
'Test Subsection'
)
vertical
=
ItemFactory
.
create
(
parent
=
subsection
,
category
=
'vertical'
,
display_name
=
'Test Unit'
)
self
.
reverification
=
ItemFactory
.
create
(
parent
=
vertical
,
category
=
'edx-reverification-block'
,
display_name
=
'Test Verification Block'
,
metadata
=
{
'attempts'
:
3
,
'due'
:
self
.
due_date
}
)
self
.
section_location
=
section
.
location
self
.
subsection_location
=
subsection
.
location
self
.
vertical_location
=
vertical
.
location
self
.
reverification_location
=
self
.
reverification
.
location
self
.
assessment
=
"midterm"
self
.
re_verification_link
=
reverse
(
'verify_student_incourse_reverify'
,
args
=
(
unicode
(
self
.
course_key
),
unicode
(
self
.
assessment
),
unicode
(
self
.
reverification_location
)
)
)
def
setUp
(
self
):
super
(
TestEmailMessageWithCustomICRVBlock
,
self
)
.
setUp
()
self
.
build_course
()
self
.
check_point
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
self
.
assessment
)
self
.
check_point
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
status
=
'submitted'
,
location_id
=
self
.
reverification_location
)
self
.
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
def
test_approved_email_message
(
self
):
subject
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"approved"
,
True
)
self
.
assertIn
(
"Your verification for course {course_name} and assessment {assessment} has been passed."
.
format
(
course_name
=
self
.
course
.
display_name_with_default
,
assessment
=
self
.
assessment
),
body
)
self
.
assertIn
(
"Re-verification Status"
,
subject
)
def
test_denied_email_message_with_valid_due_date_and_attempts_allowed
(
self
):
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
)
self
.
assertIn
(
"Your verification for course {course_name} and assessment {assessment} has failed."
.
format
(
course_name
=
self
.
course
.
display_name_with_default
,
assessment
=
self
.
assessment
),
body
)
self
.
assertIn
(
"Assessment closes on {due_date}"
.
format
(
due_date
=
get_default_time_display
(
self
.
due_date
)),
body
)
self
.
assertIn
(
"Click on link below to re-verify"
,
body
)
self
.
assertIn
(
"https://{}{}"
.
format
(
microsite
.
get_value
(
'SITE_NAME'
,
'localhost'
),
self
.
re_verification_link
),
body
)
def
test_denied_email_message_with_close_verification_dates
(
self
):
return_value
=
datetime
(
2016
,
1
,
1
,
tzinfo
=
timezone
.
utc
)
with
patch
.
object
(
timezone
,
'now'
,
return_value
=
return_value
):
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
)
self
.
assertIn
(
"Your verification for course {course_name} and assessment {assessment} has failed."
.
format
(
course_name
=
self
.
course
.
display_name_with_default
,
assessment
=
self
.
assessment
),
body
)
self
.
assertIn
(
"Assessment date has passed and retake not allowed"
,
body
)
def
test_check_num_queries
(
self
):
# Get the re-verification block to check the call made
with
check_mongo_calls
(
2
):
ver_block
=
modulestore
()
.
get_item
(
self
.
reverification_location
)
# Expect that the verification block is fetched
self
.
assertIsNotNone
(
ver_block
)
class
TestEmailMessageWithDefaultICRVBlock
(
ModuleStoreTestCase
):
"""
Test for In-course Re-verification
"""
def
build_course
(
self
):
"""
Build up a course tree with a Reverificaiton xBlock.
"""
# pylint: disable=attribute-defined-outside-init
self
.
course_key
=
SlashSeparatedCourseKey
(
"Robot"
,
"999"
,
"Test_Course"
)
self
.
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
# Create the course modes
for
mode
in
(
'audit'
,
'honor'
,
'verified'
):
min_price
=
0
if
mode
in
[
"honor"
,
"audit"
]
else
1
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course_key
,
min_price
=
min_price
)
# Create the 'edx-reverification-block' in course tree
section
=
ItemFactory
.
create
(
parent
=
self
.
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
subsection
=
ItemFactory
.
create
(
parent
=
section
,
category
=
'sequential'
,
display_name
=
'Test Subsection'
)
vertical
=
ItemFactory
.
create
(
parent
=
subsection
,
category
=
'vertical'
,
display_name
=
'Test Unit'
)
self
.
reverification
=
ItemFactory
.
create
(
parent
=
vertical
,
category
=
'edx-reverification-block'
,
display_name
=
'Test Verification Block'
)
self
.
section_location
=
section
.
location
self
.
subsection_location
=
subsection
.
location
self
.
vertical_location
=
vertical
.
location
self
.
reverification_location
=
self
.
reverification
.
location
self
.
assessment
=
"midterm"
self
.
re_verification_link
=
reverse
(
'verify_student_incourse_reverify'
,
args
=
(
unicode
(
self
.
course_key
),
unicode
(
self
.
assessment
),
unicode
(
self
.
reverification_location
)
)
)
def
setUp
(
self
):
super
(
TestEmailMessageWithDefaultICRVBlock
,
self
)
.
setUp
()
self
.
build_course
()
self
.
check_point
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
self
.
assessment
)
self
.
check_point
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
self
.
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
def
test_denied_email_message_with_no_attempt_allowed
(
self
):
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
status
=
'submitted'
,
location_id
=
self
.
reverification_location
)
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
)
self
.
assertIn
(
"Your verification for course {course_name} and assessment {assessment} has failed."
.
format
(
course_name
=
self
.
course
.
display_name_with_default
,
assessment
=
self
.
assessment
),
body
)
self
.
assertIn
(
"You have reached your allowed attempts limit. No more retakes allowed."
,
body
)
def
test_due_date
(
self
):
self
.
reverification
.
due
=
datetime
.
now
()
self
.
reverification
.
save
()
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
status
=
'submitted'
,
location_id
=
self
.
reverification_location
)
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
)
self
.
assertIn
(
"Your verification for course {course_name} and assessment {assessment} has failed."
.
format
(
course_name
=
self
.
course
.
display_name_with_default
,
assessment
=
self
.
assessment
),
body
)
self
.
assertIn
(
"You have reached your allowed attempts limit. No more retakes allowed."
,
body
)
def
test_denied_email_message_with_no_due_date
(
self
):
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
status
=
'error'
,
location_id
=
self
.
reverification_location
)
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
)
self
.
assertIn
(
"Your verification for course {course_name} and assessment {assessment} has failed."
.
format
(
course_name
=
self
.
course
.
display_name_with_default
,
assessment
=
self
.
assessment
),
body
)
self
.
assertIn
(
"Assessment is open and you have 1 attempt(s) remaining."
,
body
)
self
.
assertIn
(
"Click on link below to re-verify"
,
body
)
self
.
assertIn
(
"https://{}{}"
.
format
(
microsite
.
get_value
(
'SITE_NAME'
,
'localhost'
),
self
.
re_verification_link
),
body
)
def
test_error_on_compose_email
(
self
):
resp
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
)
self
.
assertIsNone
(
resp
)
lms/djangoapps/verify_student/views.py
View file @
a975b838
...
...
@@ -10,6 +10,7 @@ from collections import namedtuple
from
pytz
import
UTC
from
django.utils
import
timezone
from
ipware.ip
import
get_ip
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
...
...
@@ -58,6 +59,7 @@ from util.date_utils import get_default_time_display
from
eventtracking
import
tracker
import
analytics
from
courseware.url_helpers
import
get_redirect_url
from
django.contrib.auth.models
import
User
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -859,6 +861,96 @@ def submit_photos_for_verification(request):
return
HttpResponse
(
200
)
def
_compose_message_reverification_email
(
course_key
,
user_id
,
relates_assessment
,
photo_verification
,
status
,
is_secure
):
# pylint: disable=invalid-name
""" Composes subject and message for email
Args:
course_key(CourseKey): CourseKey object
user_id(str): User Id
relates_assessment(str): related assessment name
photo_verification(QuerySet/SoftwareSecure): A query set of SoftwareSecure objects or SoftwareSecure objec
status(str): approval status
is_secure(Bool): Is running on secure protocol or not
Returns:
None if any error occurred else Tuple of subject and message strings
"""
try
:
location_id
=
VerificationStatus
.
get_location_id
(
photo_verification
)
usage_key
=
UsageKey
.
from_string
(
location_id
)
course
=
modulestore
()
.
get_course
(
course_key
)
redirect_url
=
get_redirect_url
(
course_key
,
usage_key
.
replace
(
course_key
=
course_key
))
subject
=
"Re-verification Status"
context
=
{
"status"
:
status
,
"course_name"
:
course
.
display_name_with_default
,
"assessment"
:
relates_assessment
,
"courseware_url"
:
redirect_url
}
reverification_block
=
modulestore
()
.
get_item
(
usage_key
)
# Allowed attempts is 1 if not set on verification block
allowed_attempts
=
1
if
reverification_block
.
attempts
==
0
else
reverification_block
.
attempts
user_attempts
=
VerificationStatus
.
get_user_attempts
(
user_id
,
course_key
,
relates_assessment
,
location_id
)
left_attempts
=
allowed_attempts
-
user_attempts
is_attempt_allowed
=
left_attempts
>
0
verification_open
=
True
if
reverification_block
.
due
:
verification_open
=
timezone
.
now
()
<=
reverification_block
.
due
context
[
"left_attempts"
]
=
left_attempts
context
[
"is_attempt_allowed"
]
=
is_attempt_allowed
context
[
"verification_open"
]
=
verification_open
context
[
"due_date"
]
=
get_default_time_display
(
reverification_block
.
due
)
context
[
"is_secure"
]
=
is_secure
context
[
"site"
]
=
microsite
.
get_value
(
'SITE_NAME'
,
'localhost'
)
context
[
'platform_name'
]
=
microsite
.
get_value
(
'platform_name'
,
settings
.
PLATFORM_NAME
),
re_verification_link
=
reverse
(
'verify_student_incourse_reverify'
,
args
=
(
unicode
(
course_key
),
unicode
(
relates_assessment
),
unicode
(
location_id
)
)
)
context
[
"reverify_link"
]
=
re_verification_link
message
=
render_to_string
(
'emails/reverification_processed.txt'
,
context
)
log
.
info
(
"Sending email to User_Id=
%
s. Attempts left for this user are
%
s. "
"Allowed attempts
%
s. "
"Due Date
%
s"
,
str
(
user_id
),
left_attempts
,
allowed_attempts
,
str
(
reverification_block
.
due
)
)
return
subject
,
message
# Catch all exception to avoid raising back to view
except
:
# pylint: disable=bare-except
log
.
exception
(
"The email for re-verification sending failed for user_id
%
s"
,
user_id
)
def
_send_email
(
user_id
,
subject
,
message
):
""" Send email to given user
Args:
user_id(str): User Id
subject(str): Subject lines of emails
message(str): Email message body
Returns:
None
"""
from_address
=
microsite
.
get_value
(
'email_from_address'
,
settings
.
DEFAULT_FROM_EMAIL
)
user
=
User
.
objects
.
get
(
id
=
user_id
)
user
.
email_user
(
subject
,
message
,
from_address
)
@require_POST
@csrf_exempt
# SS does its own message signing, and their API won't have a cookie value
def
results_callback
(
request
):
...
...
@@ -910,26 +1002,24 @@ def results_callback(request):
try
:
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
get
(
receipt_id
=
receipt_id
)
except
SoftwareSecurePhotoVerification
.
DoesNotExist
:
log
.
error
(
"Software Secure posted back for receipt_id
{}, but not found"
.
format
(
receipt_id
)
)
log
.
error
(
"Software Secure posted back for receipt_id
%
s, but not found"
,
receipt_id
)
return
HttpResponseBadRequest
(
"edX ID {} not found"
.
format
(
receipt_id
))
checkpoints
=
VerificationCheckpoint
.
objects
.
filter
(
photo_verification
=
attempt
)
.
all
()
if
result
==
"PASS"
:
log
.
debug
(
"Approving verification for
{}"
.
format
(
receipt_id
)
)
log
.
debug
(
"Approving verification for
%
s"
,
receipt_id
)
attempt
.
approve
()
status
=
"approved"
elif
result
==
"FAIL"
:
log
.
debug
(
"Denying verification for
{}"
.
format
(
receipt_id
)
)
log
.
debug
(
"Denying verification for
%
s"
,
receipt_id
)
attempt
.
deny
(
json
.
dumps
(
reason
),
error_code
=
error_code
)
status
=
"denied"
elif
result
==
"SYSTEM FAIL"
:
log
.
debug
(
"System failure for
{} -- resetting to must_retry"
.
format
(
receipt_id
)
)
log
.
debug
(
"System failure for
%
s -- resetting to must_retry"
,
receipt_id
)
attempt
.
system_error
(
json
.
dumps
(
reason
),
error_code
=
error_code
)
status
=
"error"
log
.
error
(
"Software Secure callback attempt for
%
s failed:
%
s"
,
receipt_id
,
reason
)
else
:
log
.
error
(
"Software Secure returned unknown result
{}"
.
format
(
result
)
)
log
.
error
(
"Software Secure returned unknown result
%
s"
,
result
)
return
HttpResponseBadRequest
(
"Result {} not understood. Known results: PASS, FAIL, SYSTEM FAIL"
.
format
(
result
)
)
...
...
@@ -939,7 +1029,22 @@ def results_callback(request):
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
if
incourse_reverify_enabled
:
checkpoints
=
VerificationCheckpoint
.
objects
.
filter
(
photo_verification
=
attempt
)
.
all
()
VerificationStatus
.
add_status_from_checkpoints
(
checkpoints
=
checkpoints
,
user
=
attempt
.
user
,
status
=
status
)
# If this is re-verification then send the update email
if
checkpoints
:
user_id
=
attempt
.
user
.
id
course_key
=
checkpoints
[
0
]
.
course_id
relates_assessment
=
checkpoints
[
0
]
.
checkpoint_name
subject
,
message
=
_compose_message_reverification_email
(
course_key
,
user_id
,
relates_assessment
,
attempt
,
status
,
request
.
is_secure
()
)
_send_email
(
user_id
,
subject
,
message
)
return
HttpResponse
(
"OK!"
)
...
...
lms/templates/emails/reverification_processed.txt
0 → 100644
View file @
a975b838
<%namespace file="../main.html" import="stanford_theme_enabled" />
<%! from django.utils.translation import ugettext as _ %>
% if status == "approved":
${_("Your verification for course {course_name} and assessment {assessment} "
"has been passed."
).format(course_name=course_name, assessment=assessment)}
%else:
${_("Your verification for course {course_name} and assessment {assessment} "
"has failed."
).format(course_name=course_name, assessment=assessment)}
% if not is_attempt_allowed:
${_("You have reached your allowed attempts limit. No more retakes allowed.")}
% elif not verification_open:
${_("Assessment date has passed and retake not allowed.")}
% else:
% if due_date:
${_("Assessment closes on {due_date}.".format(due_date=due_date))}
% else:
${_("Assessment is open and you have {left_attempts} attempt(s) remaining.".format(left_attempts=left_attempts))}
% endif
${_("Click on link below to re-verify:")}
% if is_secure:
https://${ site }${ reverify_link }
% else:
http://${ site }${ reverify_link }
% endif
% endif
% endif
${_("Click on link below to go to the courseware:")}
% if is_secure:
https://${ site }${ courseware_url }
% else:
http://${ site }${ courseware_url }
% endif
${_("The {platform_name} Team.").format(platform_name=platform_name)}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment