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
d11aaec3
Commit
d11aaec3
authored
Aug 18, 2014
by
Waqas Khalid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Handle the indexerror exception on create_order
LMS-2646
parent
c7d41833
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
205 additions
and
30 deletions
+205
-30
lms/djangoapps/verify_student/tests/test_views.py
+92
-0
lms/djangoapps/verify_student/views.py
+16
-9
lms/static/js/spec/photocapture_spec.js
+40
-0
lms/static/js/verify_student/photocapture.js
+37
-20
lms/templates/verify_student/photo_verification.html
+20
-1
No files found.
lms/djangoapps/verify_student/tests/test_views.py
View file @
d11aaec3
...
...
@@ -31,6 +31,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from
student.tests.factories
import
UserFactory
from
student.models
import
CourseEnrollment
from
course_modes.tests.factories
import
CourseModeFactory
from
courseware.tests.tests
import
TEST_DATA_MONGO_MODULESTORE
from
course_modes.models
import
CourseMode
from
verify_student.views
import
render_to_response
from
verify_student.models
import
SoftwareSecurePhotoVerification
...
...
@@ -65,6 +66,97 @@ class StartView(TestCase):
self
.
assertHttpForbidden
(
self
.
client
.
get
(
self
.
start_url
()))
@override_settings
(
MODULESTORE
=
TEST_DATA_MONGO_MODULESTORE
)
class
TestCreateOrderView
(
TestCase
):
"""
Tests for the create_order view of verified course registration process
"""
def
setUp
(
self
):
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
=
'Test Course'
)
verified_mode
=
CourseMode
(
course_id
=
SlashSeparatedCourseKey
(
"Robot"
,
"999"
,
'Test_Course'
),
mode_slug
=
"verified"
,
mode_display_name
=
"Verified Certificate"
,
min_price
=
50
)
verified_mode
.
save
()
course_mode_post_data
=
{
'certificate_mode'
:
'Select Certificate'
,
'contribution'
:
50
,
'contribution-other-amt'
:
''
,
'explain'
:
''
}
self
.
client
.
post
(
reverse
(
"course_modes_choose"
,
kwargs
=
{
'course_id'
:
self
.
course_id
}),
course_mode_post_data
)
def
test_invalid_photos_data
(
self
):
"""
Test that the invalid photo data cannot be submitted
"""
create_order_post_data
=
{
'contribution'
:
50
,
'course_id'
:
self
.
course_id
,
'face_image'
:
''
,
'photo_id_image'
:
''
}
response
=
self
.
client
.
post
(
reverse
(
'verify_student_create_order'
),
create_order_post_data
)
json_response
=
json
.
loads
(
response
.
content
)
self
.
assertFalse
(
json_response
.
get
(
'success'
))
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
def
test_invalid_amount
(
self
):
"""
Test that the user cannot give invalid amount
"""
create_order_post_data
=
{
'contribution'
:
'1.a'
,
'course_id'
:
self
.
course_id
,
'face_image'
:
','
,
'photo_id_image'
:
','
}
response
=
self
.
client
.
post
(
reverse
(
'verify_student_create_order'
),
create_order_post_data
)
self
.
assertEquals
(
response
.
status_code
,
400
)
self
.
assertIn
(
'Selected price is not valid number.'
,
response
.
content
)
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
def
test_invalid_mode
(
self
):
"""
Test that the course without verified mode cannot be processed
"""
course_id
=
'Fake/999/Test_Course'
CourseFactory
.
create
(
org
=
'Fake'
,
number
=
'999'
,
display_name
=
'Test Course'
)
create_order_post_data
=
{
'contribution'
:
'50'
,
'course_id'
:
course_id
,
'face_image'
:
','
,
'photo_id_image'
:
','
}
response
=
self
.
client
.
post
(
reverse
(
'verify_student_create_order'
),
create_order_post_data
)
self
.
assertEquals
(
response
.
status_code
,
400
)
self
.
assertIn
(
'This course doesn
\'
t support verified certificates'
,
response
.
content
)
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
def
test_create_order_success
(
self
):
"""
Test that the order is created successfully when given valid data
"""
create_order_post_data
=
{
'contribution'
:
50
,
'course_id'
:
self
.
course_id
,
'face_image'
:
','
,
'photo_id_image'
:
','
}
response
=
self
.
client
.
post
(
reverse
(
'verify_student_create_order'
),
create_order_post_data
)
json_response
=
json
.
loads
(
response
.
content
)
self
.
assertTrue
(
json_response
.
get
(
'success'
))
self
.
assertIsNotNone
(
json_response
.
get
(
'orderNumber'
))
@override_settings
(
MODULESTORE
=
MODULESTORE_CONFIG
)
class
TestVerifyView
(
ModuleStoreTestCase
):
def
setUp
(
self
):
...
...
lms/djangoapps/verify_student/views.py
View file @
d11aaec3
...
...
@@ -38,6 +38,8 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from
.exceptions
import
WindowExpiredException
from
xmodule.modulestore.django
import
modulestore
from
util.json_request
import
JsonResponse
log
=
logging
.
getLogger
(
__name__
)
EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW
=
'edx.course.enrollment.reverify.started'
...
...
@@ -114,6 +116,7 @@ class VerifyView(View):
# TODO (ECOM-16): Remove once the AB test completes
"autoreg"
:
request
.
session
.
get
(
'auto_register'
,
False
),
"retake"
:
request
.
GET
.
get
(
'retake'
,
False
),
}
return
render_to_response
(
'verify_student/photo_verification.html'
,
context
)
...
...
@@ -177,9 +180,14 @@ def create_order(request):
"""
if
not
SoftwareSecurePhotoVerification
.
user_has_valid_or_pending
(
request
.
user
):
attempt
=
SoftwareSecurePhotoVerification
(
user
=
request
.
user
)
b64_face_image
=
request
.
POST
[
'face_image'
]
.
split
(
","
)[
1
]
b64_photo_id_image
=
request
.
POST
[
'photo_id_image'
]
.
split
(
","
)[
1
]
try
:
b64_face_image
=
request
.
POST
[
'face_image'
]
.
split
(
","
)[
1
]
b64_photo_id_image
=
request
.
POST
[
'photo_id_image'
]
.
split
(
","
)[
1
]
except
IndexError
:
context
=
{
'success'
:
False
,
}
return
JsonResponse
(
context
)
attempt
.
upload_face_image
(
b64_face_image
.
decode
(
'base64'
))
attempt
.
upload_photo_id_image
(
b64_photo_id_image
.
decode
(
'base64'
))
attempt
.
mark_ready
()
...
...
@@ -203,13 +211,13 @@ def create_order(request):
# prefer professional mode over verified_mode
current_mode
=
CourseMode
.
verified_mode_for_course
(
course_id
)
if
current_mode
.
slug
==
'professional'
:
amount
=
current_mode
.
min_price
# make sure this course has a verified mode
if
not
current_mode
:
return
HttpResponseBadRequest
(
_
(
"This course doesn't support verified certificates"
))
if
current_mode
.
slug
==
'professional'
:
amount
=
current_mode
.
min_price
if
amount
<
current_mode
.
min_price
:
return
HttpResponseBadRequest
(
_
(
"No selected price or selected price is below minimum."
))
...
...
@@ -225,7 +233,7 @@ def create_order(request):
params
=
get_signed_purchase_params
(
cart
,
callback_url
=
callback_url
)
params
[
'success'
]
=
True
return
HttpResponse
(
json
.
dumps
(
params
),
content_type
=
"text/json"
)
...
...
@@ -484,8 +492,7 @@ def midcourse_reverify_dash(request):
try
:
course_enrollment_pairs
.
append
((
modulestore
()
.
get_course
(
enrollment
.
course_id
),
enrollment
))
except
ItemNotFoundError
:
log
.
error
(
"User {0} enrolled in non-existent course {1}"
.
format
(
user
.
username
,
enrollment
.
course_id
))
log
.
error
(
"User {0} enrolled in non-existent course {1}"
.
format
(
user
.
username
,
enrollment
.
course_id
))
statuses
=
[
"approved"
,
"pending"
,
"must_reverify"
,
"denied"
]
...
...
lms/static/js/spec/photocapture_spec.js
0 → 100644
View file @
d11aaec3
describe
(
"Photo Verification"
,
function
()
{
beforeEach
(
function
()
{
setFixtures
(
'<div id="order-error" style="display: none;"></div><input type="radio" name="contribution" value="35" id="contribution-35" checked="checked"><input type="radio" id="contribution-other" name="contribution" value=""><input type="text" size="9" name="contribution-other-amt" id="contribution-other-amt" value="30"><img id="face_image" src="src="data:image/png;base64,dummy"><img id="photo_id_image" src="src="data:image/png;base64,dummy">'
);
});
it
(
'retake photo'
,
function
()
{
spyOn
(
window
,
"refereshPageMessage"
).
andCallFake
(
function
(){
return
})
spyOn
(
$
,
"ajax"
).
andCallFake
(
function
(
e
)
{
e
.
success
({
"success"
:
false
});
});
submitToPaymentProcessing
();
expect
(
window
.
refereshPageMessage
).
toHaveBeenCalled
();
});
it
(
'successful submission'
,
function
()
{
spyOn
(
window
,
"submitForm"
).
andCallFake
(
function
(){
return
})
spyOn
(
$
,
"ajax"
).
andCallFake
(
function
(
e
)
{
e
.
success
({
"success"
:
true
});
});
submitToPaymentProcessing
();
expect
(
window
.
submitForm
).
toHaveBeenCalled
();
});
it
(
'Error during process'
,
function
()
{
spyOn
(
window
,
"showSubmissionError"
).
andCallFake
(
function
(){
return
})
spyOn
(
$
,
"ajax"
).
andCallFake
(
function
(
e
)
{
e
.
error
({});
});
submitToPaymentProcessing
();
expect
(
window
.
showSubmissionError
).
toHaveBeenCalled
();
});
});
lms/static/js/verify_student/photocapture.js
View file @
d11aaec3
...
...
@@ -44,6 +44,30 @@ var submitMidcourseReverificationPhotos = function() {
$
(
"#reverify_form"
).
submit
();
}
function
showSubmissionError
()
{
if
(
xhr
.
status
==
400
)
{
$
(
'#order-error .copy p'
).
html
(
xhr
.
responseText
);
}
$
(
'#order-error'
).
show
();
$
(
"html, body"
).
animate
({
scrollTop
:
0
});
}
function
submitForm
(
data
)
{
for
(
prop
in
data
)
{
$
(
'<input>'
).
attr
({
type
:
'hidden'
,
name
:
prop
,
value
:
data
[
prop
]
}).
appendTo
(
'#pay_form'
);
}
$
(
"#pay_form"
).
submit
();
}
function
refereshPageMessage
()
{
$
(
'#photo-error'
).
show
();
$
(
"html, body"
).
animate
({
scrollTop
:
0
});
}
var
submitToPaymentProcessing
=
function
()
{
var
contribution_input
=
$
(
"input[name='contribution']:checked"
)
var
contribution
=
0
;
...
...
@@ -54,33 +78,25 @@ var submitToPaymentProcessing = function() {
contribution
=
contribution_input
.
val
();
}
var
course_id
=
$
(
"input[name='course_id']"
).
val
();
var
xhr
=
$
.
post
(
"/verify_student/create_order"
,
{
$
.
ajax
({
url
:
"/verify_student/create_order"
,
type
:
'POST'
,
data
:
{
"course_id"
:
course_id
,
"contribution"
:
contribution
,
"face_image"
:
$
(
"#face_image"
)[
0
].
src
,
"photo_id_image"
:
$
(
"#photo_id_image"
)[
0
].
src
},
function
(
data
)
{
for
(
prop
in
data
)
{
$
(
'<input>'
).
attr
({
type
:
'hidden'
,
name
:
prop
,
value
:
data
[
prop
]
}).
appendTo
(
'#pay_form'
);
success
:
function
(
data
)
{
if
(
data
.
success
)
{
submitForm
(
data
);
}
else
{
refereshPageMessage
();
}
},
error
:
function
(
xhr
,
status
,
error
)
{
showSubmissionError
()
}
)
.
done
(
function
(
data
)
{
$
(
"#pay_form"
).
submit
();
})
.
fail
(
function
(
jqXhr
,
text_status
,
error_thrown
)
{
if
(
jqXhr
.
status
==
400
)
{
$
(
'#order-error .copy p'
).
html
(
jqXhr
.
responseText
);
}
$
(
'#order-error'
).
show
();
$
(
"html, body"
).
animate
({
scrollTop
:
0
});
});
}
...
...
@@ -298,6 +314,7 @@ $(document).ready(function() {
// when moving between steps
$
(
'#face_next_link'
).
click
(
function
(){
analytics
.
pageview
(
"Capture ID Photo"
);
$
(
'#photo-error'
).
hide
();
$
(
'body'
).
addClass
(
'step-photos-id'
).
removeClass
(
'step-photos-cam'
)
})
...
...
lms/templates/verify_student/photo_verification.html
View file @
d11aaec3
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
core
.
urlresolvers
import
reverse
%
>
<
%!
from
lms
.
envs
.
common
import
TECH_SUPPORT_EMAIL
%
>
<
%
inherit
file=
"../main.html"
/>
<
%
namespace
name=
'static'
file=
'/static_content.html'
/>
...
...
@@ -57,6 +57,25 @@
</div>
</div>
<div
id=
"photo-error"
style=
"display: none;"
class=
"wrapper-msg wrapper-msg-activate"
>
<div
class=
" msg msg-activate"
>
<i
class=
"msg-icon icon-warning-sign"
></i>
<div
class=
"msg-content"
>
<h3
class=
"title"
>
${_("Error processing your order")}
</h3>
<div
class=
"copy"
>
<p>
${_("We're sorry, there has been an error on this page. Please refresh the page and retake your photos. "
"If you are still having trouble, please contact us at "
"{email_address}").format(
email_address='
<a
href=
"mailto:{technical_email}"
>
{technical_email}
</a>
'.format(
technical_email=TECH_SUPPORT_EMAIL
)
)}
</p>
</div>
</div>
</div>
</div>
<div
class=
"container"
>
<section
class=
"wrapper"
>
...
...
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