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
4e454cca
Commit
4e454cca
authored
Mar 13, 2015
by
Awais Qureshi
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #7267 from edx/awais786/ECOM-911-enable-noid-paid-course
Awais786/ecom 911 enable noid paid course
parents
cff9a5aa
4bab316b
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
527 additions
and
133 deletions
+527
-133
common/djangoapps/course_modes/admin.py
+2
-1
common/djangoapps/course_modes/models.py
+176
-19
common/djangoapps/course_modes/tests/test_models.py
+114
-0
common/djangoapps/course_modes/tests/test_views.py
+25
-4
common/djangoapps/course_modes/views.py
+4
-4
common/djangoapps/enrollment/tests/test_api.py
+4
-2
common/djangoapps/student/models.py
+11
-3
common/djangoapps/student/tests/test_enrollment.py
+4
-0
common/djangoapps/student/tests/tests.py
+6
-1
common/djangoapps/student/views.py
+2
-2
lms/djangoapps/shoppingcart/models.py
+17
-11
lms/djangoapps/shoppingcart/tests/test_models.py
+23
-0
lms/djangoapps/student_account/test/test_views.py
+1
-1
lms/djangoapps/verify_student/tests/test_views.py
+70
-6
lms/djangoapps/verify_student/views.py
+53
-33
lms/templates/dashboard/_dashboard_course_listing.html
+15
-46
No files found.
common/djangoapps/course_modes/admin.py
View file @
4e454cca
...
...
@@ -21,7 +21,8 @@ class CourseModeForm(forms.ModelForm):
COURSE_MODE_SLUG_CHOICES
=
(
[(
CourseMode
.
DEFAULT_MODE_SLUG
,
CourseMode
.
DEFAULT_MODE_SLUG
)]
+
[(
mode_slug
,
mode_slug
)
for
mode_slug
in
CourseMode
.
VERIFIED_MODES
]
[(
mode_slug
,
mode_slug
)
for
mode_slug
in
CourseMode
.
VERIFIED_MODES
]
+
[(
CourseMode
.
NO_ID_PROFESSIONAL_MODE
,
CourseMode
.
NO_ID_PROFESSIONAL_MODE
)]
)
mode_slug
=
forms
.
ChoiceField
(
choices
=
COURSE_MODE_SLUG_CHOICES
)
...
...
common/djangoapps/course_modes/models.py
View file @
4e454cca
...
...
@@ -65,11 +65,17 @@ class CourseMode(models.Model):
help_text
=
"This is the SKU(stock keeping unit) of this mode in external services."
)
DEFAULT_MODE
=
Mode
(
'honor'
,
_
(
'Honor Code Certificate'
),
0
,
''
,
'usd'
,
None
,
None
,
None
)
DEFAULT_MODE_SLUG
=
'honor'
HONOR
=
'honor'
PROFESSIONAL
=
'professional'
VERIFIED
=
"verified"
AUDIT
=
"audit"
NO_ID_PROFESSIONAL_MODE
=
"no-id-professional"
DEFAULT_MODE
=
Mode
(
HONOR
,
_
(
'Honor Code Certificate'
),
0
,
''
,
'usd'
,
None
,
None
,
None
)
DEFAULT_MODE_SLUG
=
HONOR
# Modes that allow a student to pursue a verified certificate
VERIFIED_MODES
=
[
"verified"
,
"professional"
]
VERIFIED_MODES
=
[
VERIFIED
,
PROFESSIONAL
]
class
Meta
:
""" meta attributes of this model """
...
...
@@ -249,6 +255,23 @@ class CourseMode(models.Model):
return
professional_mode
if
professional_mode
else
verified_mode
@classmethod
def
min_course_price_for_verified_for_currency
(
cls
,
course_id
,
currency
):
# pylint: disable=invalid-name
"""
Returns the minimum price of the course int he appropriate currency over all the
course's *verified*, non-expired modes.
Assuming all verified courses have a minimum price of >0, this value should always
be >0.
If no verified mode is found, 0 is returned.
"""
modes
=
cls
.
modes_for_course
(
course_id
)
for
mode
in
modes
:
if
(
mode
.
currency
==
currency
)
and
(
mode
.
slug
==
'verified'
):
return
mode
.
min_price
return
0
@classmethod
def
has_verified_mode
(
cls
,
course_mode_dict
):
"""Check whether the modes for a course allow a student to pursue a verfied certificate.
...
...
@@ -265,21 +288,65 @@ class CourseMode(models.Model):
return
False
@classmethod
def
min_course_price_for_verified_for_currency
(
cls
,
course_id
,
currency
):
def
has_professional_mode
(
cls
,
modes_dict
):
"""
Returns the minimum price of the course int he appropriate currency over all the
course's *verified*, non-expired modes.
check the course mode is profession or no-id-professional
A
ssuming all verified courses have a minimum price of >0, this value should always
be >0
.
A
rgs:
modes_dict (dict): course modes
.
If no verified mode is found, 0 is returned.
Returns:
bool
"""
modes
=
cls
.
modes_for_course
(
course_id
)
for
mode
in
modes
:
if
(
mode
.
currency
==
currency
)
and
(
mode
.
slug
==
'verified'
):
return
mode
.
min_price
return
0
return
cls
.
PROFESSIONAL
in
modes_dict
or
cls
.
NO_ID_PROFESSIONAL_MODE
in
modes_dict
@classmethod
def
is_professional_mode
(
cls
,
course_mode_tuple
):
"""
checking that tuple is professional mode.
Args:
course_mode_tuple (tuple) : course mode tuple
Returns:
bool
"""
return
course_mode_tuple
.
slug
in
[
cls
.
PROFESSIONAL
,
cls
.
NO_ID_PROFESSIONAL_MODE
]
if
course_mode_tuple
else
False
@classmethod
def
is_professional_slug
(
cls
,
slug
):
"""checking slug is professional
Args:
slug (str) : course mode string
Return:
bool
"""
return
slug
in
[
cls
.
PROFESSIONAL
,
cls
.
NO_ID_PROFESSIONAL_MODE
]
@classmethod
def
is_verified_mode
(
cls
,
course_mode_tuple
):
"""Check whether the given modes is_verified or not.
Args:
course_mode_tuple(Mode): Mode tuple
Returns:
bool: True iff the course modes is verified else False.
"""
return
course_mode_tuple
.
slug
in
cls
.
VERIFIED_MODES
@classmethod
def
is_verified_slug
(
cls
,
mode_slug
):
"""Check whether the given mode_slug is_verified or not.
Args:
mode_slug(str): Mode Slug
Returns:
bool: True iff the course mode slug is verified else False.
"""
return
mode_slug
in
cls
.
VERIFIED_MODES
@classmethod
def
has_payment_options
(
cls
,
course_id
):
...
...
@@ -325,8 +392,8 @@ class CourseMode(models.Model):
if
modes_dict
is
None
:
modes_dict
=
cls
.
modes_for_course_dict
(
course_id
)
# Professional mode courses are always behind a paywall
if
"professional"
in
modes_dict
:
# Professional
and no-id-professional
mode courses are always behind a paywall
if
cls
.
has_professional_mode
(
modes_dict
)
:
return
False
# White-label uses course mode honor with a price
...
...
@@ -335,7 +402,7 @@ class CourseMode(models.Model):
return
False
# Check that the default mode is available.
return
(
"honor"
in
modes_dict
)
return
(
cls
.
HONOR
in
modes_dict
)
@classmethod
def
is_white_label
(
cls
,
course_id
,
modes_dict
=
None
):
...
...
@@ -360,13 +427,13 @@ class CourseMode(models.Model):
# White-label uses course mode honor with a price
# to indicate that the course is behind a paywall.
if
"honor"
in
modes_dict
and
len
(
modes_dict
)
==
1
:
if
cls
.
HONOR
in
modes_dict
and
len
(
modes_dict
)
==
1
:
if
modes_dict
[
"honor"
]
.
min_price
>
0
or
modes_dict
[
"honor"
]
.
suggested_prices
!=
''
:
return
True
return
False
@classmethod
def
min_course_price_for_currency
(
cls
,
course_id
,
currency
):
def
min_course_price_for_currency
(
cls
,
course_id
,
currency
):
# pylint: disable=invalid-name
"""
Returns the minimum price of the course in the appropriate currency over all the course's
non-expired modes.
...
...
@@ -375,6 +442,96 @@ class CourseMode(models.Model):
modes
=
cls
.
modes_for_course
(
course_id
)
return
min
(
mode
.
min_price
for
mode
in
modes
if
mode
.
currency
==
currency
)
@classmethod
def
enrollment_mode_display
(
cls
,
mode
,
verification_status
):
""" Select appropriate display strings and CSS classes.
Uses mode and verification status to select appropriate display strings and CSS classes
for certificate display.
Args:
mode (str): enrollment mode.
verification_status (str) : verification status of student
Returns:
dictionary:
"""
# import inside the function to avoid the circular import
from
student.helpers
import
(
VERIFY_STATUS_NEED_TO_VERIFY
,
VERIFY_STATUS_SUBMITTED
,
VERIFY_STATUS_APPROVED
)
show_image
=
False
image_alt
=
''
if
mode
==
cls
.
VERIFIED
:
if
verification_status
in
[
VERIFY_STATUS_NEED_TO_VERIFY
,
VERIFY_STATUS_SUBMITTED
]:
enrollment_title
=
_
(
"Your verification is pending"
)
enrollment_value
=
_
(
"Verified: Pending Verification"
)
show_image
=
True
image_alt
=
_
(
"ID verification pending"
)
elif
verification_status
==
VERIFY_STATUS_APPROVED
:
enrollment_title
=
_
(
"You're enrolled as a verified student"
)
enrollment_value
=
_
(
"Verified"
)
show_image
=
True
image_alt
=
_
(
"ID Verified Ribbon/Badge"
)
else
:
enrollment_title
=
_
(
"You're enrolled as an honor code student"
)
enrollment_value
=
_
(
"Honor Code"
)
elif
mode
==
cls
.
HONOR
:
enrollment_title
=
_
(
"You're enrolled as an honor code student"
)
enrollment_value
=
_
(
"Honor Code"
)
elif
mode
==
cls
.
AUDIT
:
enrollment_title
=
_
(
"You're auditing this course"
)
enrollment_value
=
_
(
"Auditing"
)
elif
mode
in
[
cls
.
PROFESSIONAL
,
cls
.
NO_ID_PROFESSIONAL_MODE
]:
enrollment_title
=
_
(
"You're enrolled as a professional education student"
)
enrollment_value
=
_
(
"Professional Ed"
)
else
:
enrollment_title
=
''
enrollment_value
=
''
return
{
'enrollment_title'
:
unicode
(
enrollment_title
),
'enrollment_value'
:
unicode
(
enrollment_value
),
'show_image'
:
show_image
,
'image_alt'
:
unicode
(
image_alt
),
'display_mode'
:
cls
.
_enrollment_mode_display
(
mode
,
verification_status
)
}
@staticmethod
def
_enrollment_mode_display
(
enrollment_mode
,
verification_status
):
"""Checking enrollment mode and status and returns the display mode
Args:
enrollment_mode (str): enrollment mode.
verification_status (str) : verification status of student
Returns:
display_mode (str) : display mode for certs
"""
# import inside the function to avoid the circular import
from
student.helpers
import
(
VERIFY_STATUS_NEED_TO_VERIFY
,
VERIFY_STATUS_SUBMITTED
,
VERIFY_STATUS_APPROVED
)
if
enrollment_mode
==
CourseMode
.
VERIFIED
:
if
verification_status
in
[
VERIFY_STATUS_NEED_TO_VERIFY
,
VERIFY_STATUS_SUBMITTED
,
VERIFY_STATUS_APPROVED
]:
display_mode
=
"verified"
else
:
display_mode
=
"honor"
elif
enrollment_mode
in
[
CourseMode
.
PROFESSIONAL
,
CourseMode
.
NO_ID_PROFESSIONAL_MODE
]:
display_mode
=
"professional"
else
:
display_mode
=
enrollment_mode
return
display_mode
def
to_tuple
(
self
):
"""
Takes a mode model and turns it into a model named tuple.
...
...
common/djangoapps/course_modes/tests/test_models.py
View file @
4e454cca
...
...
@@ -150,11 +150,17 @@ class CourseModeModelTest(TestCase):
honor
.
save
()
self
.
assertTrue
(
CourseMode
.
has_payment_options
(
self
.
course_key
))
def
test_course_has_payment_options_with_no_id_professional
(
self
):
# Has payment options.
self
.
create_mode
(
'no-id-professional'
,
'no-id-professional'
,
min_price
=
5
)
self
.
assertTrue
(
CourseMode
.
has_payment_options
(
self
.
course_key
))
@ddt.data
(
([],
True
),
([(
"honor"
,
0
),
(
"audit"
,
0
),
(
"verified"
,
100
)],
True
),
([(
"honor"
,
100
)],
False
),
([(
"professional"
,
100
)],
False
),
([(
"no-id-professional"
,
100
)],
False
),
)
@ddt.unpack
def
test_can_auto_enroll
(
self
,
modes_and_prices
,
can_auto_enroll
):
...
...
@@ -206,3 +212,111 @@ class CourseModeModelTest(TestCase):
# Check that we get a default mode for when no course mode is available
self
.
assertEqual
(
len
(
all_modes
[
other_course_key
]),
1
)
self
.
assertEqual
(
all_modes
[
other_course_key
][
0
],
CourseMode
.
DEFAULT_MODE
)
@ddt.data
(
''
,
'no-id-professional'
,
'professional'
,
'verified'
)
def
test_course_has_professional_mode
(
self
,
mode
):
# check the professional mode.
self
.
create_mode
(
mode
,
'course mode'
,
10
)
modes_dict
=
CourseMode
.
modes_for_course_dict
(
self
.
course_key
)
if
mode
in
[
'professional'
,
'no-id-professional'
]:
self
.
assertTrue
(
CourseMode
.
has_professional_mode
(
modes_dict
))
else
:
self
.
assertFalse
(
CourseMode
.
has_professional_mode
(
modes_dict
))
@ddt.data
(
'no-id-professional'
,
'professional'
,
'verified'
)
def
test_course_is_professional_mode
(
self
,
mode
):
# check that tuple has professional mode
course_mode
,
__
=
self
.
create_mode
(
mode
,
'course mode'
,
10
)
if
mode
in
[
'professional'
,
'no-id-professional'
]:
self
.
assertTrue
(
CourseMode
.
is_professional_mode
(
course_mode
.
to_tuple
()))
else
:
self
.
assertFalse
(
CourseMode
.
is_professional_mode
(
course_mode
.
to_tuple
()))
def
test_course_is_professional_mode_with_invalid_tuple
(
self
):
# check that tuple has professional mode with None
self
.
assertFalse
(
CourseMode
.
is_professional_mode
(
None
))
@ddt.data
(
(
'no-id-professional'
,
False
),
(
'professional'
,
True
),
(
'verified'
,
True
),
(
'honor'
,
False
),
(
'audit'
,
False
)
)
@ddt.unpack
def
test_is_verified_slug
(
self
,
mode_slug
,
is_verified
):
# check that mode slug is verified or not
if
is_verified
:
self
.
assertTrue
(
CourseMode
.
is_verified_slug
(
mode_slug
))
else
:
self
.
assertFalse
(
CourseMode
.
is_verified_slug
(
mode_slug
))
@ddt.data
(
(
"verified"
,
"verify_need_to_verify"
),
(
"verified"
,
"verify_submitted"
),
(
"verified"
,
"verify_approved"
),
(
"verified"
,
'dummy'
),
(
"verified"
,
None
),
(
'honor'
,
None
),
(
'honor'
,
'dummy'
),
(
'audit'
,
None
),
(
'professional'
,
None
),
(
'no-id-professional'
,
None
),
(
'no-id-professional'
,
'dummy'
)
)
@ddt.unpack
def
test_enrollment_mode_display
(
self
,
mode
,
verification_status
):
if
mode
==
"verified"
:
self
.
assertEqual
(
CourseMode
.
enrollment_mode_display
(
mode
,
verification_status
),
self
.
_enrollment_display_modes_dicts
(
verification_status
)
)
self
.
assertEqual
(
CourseMode
.
enrollment_mode_display
(
mode
,
verification_status
),
self
.
_enrollment_display_modes_dicts
(
verification_status
)
)
self
.
assertEqual
(
CourseMode
.
enrollment_mode_display
(
mode
,
verification_status
),
self
.
_enrollment_display_modes_dicts
(
verification_status
)
)
elif
mode
==
"honor"
:
self
.
assertEqual
(
CourseMode
.
enrollment_mode_display
(
mode
,
verification_status
),
self
.
_enrollment_display_modes_dicts
(
mode
)
)
elif
mode
==
"audit"
:
self
.
assertEqual
(
CourseMode
.
enrollment_mode_display
(
mode
,
verification_status
),
self
.
_enrollment_display_modes_dicts
(
mode
)
)
elif
mode
==
"professional"
:
self
.
assertEqual
(
CourseMode
.
enrollment_mode_display
(
mode
,
verification_status
),
self
.
_enrollment_display_modes_dicts
(
mode
)
)
def
_enrollment_display_modes_dicts
(
self
,
dict_type
):
"""
Helper function to generate the enrollment display mode dict.
"""
dict_keys
=
[
'enrollment_title'
,
'enrollment_value'
,
'show_image'
,
'image_alt'
,
'display_mode'
]
display_values
=
{
"verify_need_to_verify"
:
[
"Your verification is pending"
,
"Verified: Pending Verification"
,
True
,
'ID verification pending'
,
'verified'
],
"verify_approved"
:
[
"You're enrolled as a verified student"
,
"Verified"
,
True
,
'ID Verified Ribbon/Badge'
,
'verified'
],
"verify_none"
:
[
"You're enrolled as an honor code student"
,
"Honor Code"
,
False
,
''
,
'honor'
],
"honor"
:
[
"You're enrolled as an honor code student"
,
"Honor Code"
,
False
,
''
,
'honor'
],
"audit"
:
[
"You're auditing this course"
,
"Auditing"
,
False
,
''
,
'audit'
],
"professional"
:
[
"You're enrolled as a professional education student"
,
"Professional Ed"
,
False
,
''
,
'professional'
]
}
if
dict_type
in
[
'verify_need_to_verify'
,
'verify_submitted'
]:
return
dict
(
zip
(
dict_keys
,
display_values
.
get
(
'verify_need_to_verify'
)))
elif
dict_type
is
None
or
dict_type
==
'dummy'
:
return
dict
(
zip
(
dict_keys
,
display_values
.
get
(
'verify_none'
)))
else
:
return
dict
(
zip
(
dict_keys
,
display_values
.
get
(
dict_type
)))
common/djangoapps/course_modes/tests/test_views.py
View file @
4e454cca
...
...
@@ -68,6 +68,25 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
else
:
self
.
assertEquals
(
response
.
status_code
,
200
)
def
test_no_id_redirect
(
self
):
# Create the course modes
CourseModeFactory
(
mode_slug
=
CourseMode
.
NO_ID_PROFESSIONAL_MODE
,
course_id
=
self
.
course
.
id
,
min_price
=
100
)
# Enroll the user in the test course
CourseEnrollmentFactory
(
is_active
=
False
,
mode
=
CourseMode
.
NO_ID_PROFESSIONAL_MODE
,
course_id
=
self
.
course
.
id
,
user
=
self
.
user
)
# Configure whether we're upgrading or not
url
=
reverse
(
'course_modes_choose'
,
args
=
[
unicode
(
self
.
course
.
id
)])
response
=
self
.
client
.
get
(
url
)
# Check whether we were correctly redirected
start_flow_url
=
reverse
(
'verify_student_start_flow'
,
args
=
[
unicode
(
self
.
course
.
id
)])
self
.
assertRedirects
(
response
,
start_flow_url
)
def
test_no_enrollment
(
self
):
# Create the course modes
for
mode
in
(
'audit'
,
'honor'
,
'verified'
):
...
...
@@ -115,9 +134,10 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
# TODO: Fix it so that response.templates works w/ mako templates, and then assert
# that the right template rendered
def
test_professional_enrollment
(
self
):
@ddt.data
(
'professional'
,
'no-id-professional'
)
def
test_professional_enrollment
(
self
,
mode
):
# The only course mode is professional ed
CourseModeFactory
(
mode_slug
=
'professional'
,
course_id
=
self
.
course
.
id
)
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course
.
id
,
min_price
=
1
)
# Go to the "choose your track" page
choose_track_url
=
reverse
(
'course_modes_choose'
,
args
=
[
unicode
(
self
.
course
.
id
)])
...
...
@@ -132,7 +152,7 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
CourseEnrollmentFactory
(
user
=
self
.
user
,
is_active
=
True
,
mode
=
"professional"
,
mode
=
mode
,
course_id
=
unicode
(
self
.
course
.
id
),
)
...
...
@@ -156,7 +176,8 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
def
test_choose_mode_redirect
(
self
,
course_mode
,
expected_redirect
):
# Create the course modes
for
mode
in
(
'audit'
,
'honor'
,
'verified'
):
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course
.
id
)
min_price
=
0
if
course_mode
in
[
"honor"
,
"audit"
]
else
1
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course
.
id
,
min_price
=
min_price
)
# Choose the mode (POST request)
choose_track_url
=
reverse
(
'course_modes_choose'
,
args
=
[
unicode
(
self
.
course
.
id
)])
...
...
common/djangoapps/course_modes/views.py
View file @
4e454cca
...
...
@@ -72,9 +72,9 @@ class ChooseModeView(View):
# We assume that, if 'professional' is one of the modes, it is the *only* mode.
# If we offer more modes alongside 'professional' in the future, this will need to route
# to the usual "choose your track" page.
has_enrolled_professional
=
(
enrollment_mode
==
"professional"
and
is_active
)
if
"professional"
in
modes
and
not
has_enrolled_professional
:
# to the usual "choose your track" page
same is true for no-id-professional mode
.
has_enrolled_professional
=
(
CourseMode
.
is_professional_slug
(
enrollment_mode
)
and
is_active
)
if
CourseMode
.
has_professional_mode
(
modes
)
and
not
has_enrolled_professional
:
return
redirect
(
reverse
(
'verify_student_start_flow'
,
...
...
@@ -90,7 +90,7 @@ class ChooseModeView(View):
return
redirect
(
reverse
(
'dashboard'
))
# If a user has already paid, redirect them to the dashboard.
if
is_active
and
enrollment_mode
in
CourseMode
.
VERIFIED_MODES
:
if
is_active
and
(
enrollment_mode
in
CourseMode
.
VERIFIED_MODES
+
[
CourseMode
.
NO_ID_PROFESSIONAL_MODE
])
:
return
redirect
(
reverse
(
'dashboard'
))
donation_for_course
=
request
.
session
.
get
(
"donation_for_course"
,
{})
...
...
common/djangoapps/enrollment/tests/test_api.py
View file @
4e454cca
...
...
@@ -38,7 +38,8 @@ class EnrollmentTest(TestCase):
([
'honor'
,
'verified'
,
'audit'
],
'honor'
),
# Check for professional ed happy path.
([
'professional'
],
'professional'
)
([
'professional'
],
'professional'
),
([
'no-id-professional'
],
'no-id-professional'
)
)
@ddt.unpack
def
test_enroll
(
self
,
course_modes
,
mode
):
...
...
@@ -72,7 +73,8 @@ class EnrollmentTest(TestCase):
([
'honor'
,
'verified'
,
'audit'
],
'honor'
),
# Check for professional ed happy path.
([
'professional'
],
'professional'
)
([
'professional'
],
'professional'
),
([
'no-id-professional'
],
'no-id-professional'
)
)
@ddt.unpack
def
test_unenroll
(
self
,
course_modes
,
mode
):
...
...
common/djangoapps/student/models.py
View file @
4e454cca
...
...
@@ -1100,9 +1100,8 @@ class CourseEnrollment(models.Model):
"""
Returns True, if course is paid
"""
paid_course
=
CourseMode
.
objects
.
filter
(
Q
(
course_id
=
self
.
course_id
)
&
Q
(
mode_slug
=
'honor'
)
&
(
Q
(
expiration_datetime__isnull
=
True
)
|
Q
(
expiration_datetime__gte
=
datetime
.
now
(
pytz
.
UTC
))))
.
exclude
(
min_price
=
0
)
if
paid_course
or
self
.
mode
==
'professional'
:
paid_course
=
CourseMode
.
is_white_label
(
self
.
course_id
)
if
paid_course
or
CourseMode
.
is_professional_slug
(
self
.
mode
):
return
True
return
False
...
...
@@ -1154,6 +1153,12 @@ class CourseEnrollment(models.Model):
def
course
(
self
):
return
modulestore
()
.
get_course
(
self
.
course_id
)
def
is_verified_enrollment
(
self
):
"""
Check the course enrollment mode is verified or not
"""
return
CourseMode
.
is_verified_slug
(
self
.
mode
)
class
CourseEnrollmentAllowed
(
models
.
Model
):
"""
...
...
@@ -1403,6 +1408,9 @@ class LinkedInAddToProfileConfiguration(ConfigurationModel):
"honor"
:
ugettext_lazy
(
u"{platform_name} Honor Code Certificate for {course_name}"
),
"verified"
:
ugettext_lazy
(
u"{platform_name} Verified Certificate for {course_name}"
),
"professional"
:
ugettext_lazy
(
u"{platform_name} Professional Certificate for {course_name}"
),
"no-id-professional"
:
ugettext_lazy
(
u"{platform_name} Professional Certificate for {course_name}"
),
}
company_identifier
=
models
.
TextField
(
...
...
common/djangoapps/student/tests/test_enrollment.py
View file @
4e454cca
...
...
@@ -55,6 +55,7 @@ class EnrollmentTest(UrlResetMixin, ModuleStoreTestCase):
# We should NOT be auto-enrolled, because that would be giving
# away an expensive course for free :)
([
'professional'
],
'course_modes_choose'
,
None
),
([
'no-id-professional'
],
'course_modes_choose'
,
None
),
)
@ddt.unpack
def
test_enroll
(
self
,
course_modes
,
next_url
,
enrollment_mode
):
...
...
@@ -113,6 +114,9 @@ class EnrollmentTest(UrlResetMixin, ModuleStoreTestCase):
([
'professional'
],
'true'
),
([
'professional'
],
'false'
),
([
'professional'
],
None
),
([
'no-id-professional'
],
'true'
),
([
'no-id-professional'
],
'false'
),
([
'no-id-professional'
],
None
),
)
@ddt.unpack
def
test_enroll_with_email_opt_in
(
self
,
course_modes
,
email_opt_in
,
mock_update_email_opt_in
):
...
...
common/djangoapps/student/tests/tests.py
View file @
4e454cca
...
...
@@ -219,7 +219,10 @@ class DashboardTest(ModuleStoreTestCase):
attempt
.
approve
()
response
=
self
.
client
.
get
(
reverse
(
'dashboard'
))
self
.
assertContains
(
response
,
"class=
\"
course {0}
\"
"
.
format
(
mode
))
if
mode
in
[
'professional'
,
'no-id-professional'
]:
self
.
assertContains
(
response
,
'class="course professional"'
)
else
:
self
.
assertContains
(
response
,
'class="course {0}"'
.
format
(
mode
))
self
.
assertContains
(
response
,
value
)
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_VERIFIED_CERTIFICATES'
:
True
})
...
...
@@ -231,6 +234,8 @@ class DashboardTest(ModuleStoreTestCase):
self
.
_check_verification_status_on
(
'verified'
,
'You
\'
re enrolled as a verified student'
)
self
.
_check_verification_status_on
(
'honor'
,
'You
\'
re enrolled as an honor code student'
)
self
.
_check_verification_status_on
(
'audit'
,
'You
\'
re auditing this course'
)
self
.
_check_verification_status_on
(
'professional'
,
'You
\'
re enrolled as a professional education student'
)
self
.
_check_verification_status_on
(
'no-id-professional'
,
'You
\'
re enrolled as a professional education student'
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
def
_check_verification_status_off
(
self
,
mode
,
value
):
...
...
common/djangoapps/student/views.py
View file @
4e454cca
...
...
@@ -909,9 +909,9 @@ def change_enrollment(request, check_access=True):
# If we have more than one course mode or professional ed is enabled,
# then send the user to the choose your track page.
# (In the case of professional ed, this will redirect to a page that
# (In the case of
no-id-professional/
professional ed, this will redirect to a page that
# funnels users directly into the verification / payment flow)
if
CourseMode
.
has_verified_mode
(
available_modes
):
if
CourseMode
.
has_verified_mode
(
available_modes
)
or
CourseMode
.
has_professional_mode
(
available_modes
)
:
return
HttpResponse
(
reverse
(
"course_modes_choose"
,
kwargs
=
{
'course_id'
:
unicode
(
course_id
)})
)
...
...
lms/djangoapps/shoppingcart/models.py
View file @
4e454cca
...
...
@@ -1746,20 +1746,26 @@ class CertificateItem(OrderItem):
self
.
course_enrollment
.
activate
()
def
additional_instruction_text
(
self
):
verification_reminder
=
""
is_enrollment_mode_verified
=
self
.
course_enrollment
.
is_verified_enrollment
()
# pylint: disable=E1101
if
is_enrollment_mode_verified
:
domain
=
microsite
.
get_value
(
'SITE_NAME'
,
settings
.
SITE_NAME
)
path
=
reverse
(
'verify_student_verify_later'
,
kwargs
=
{
'course_id'
:
unicode
(
self
.
course_id
)})
verification_url
=
"http://{domain}{path}"
.
format
(
domain
=
domain
,
path
=
path
)
verification_reminder
=
_
(
"If you haven't verified your identity yet, please start the verification process ({verification_url})."
)
.
format
(
verification_url
=
verification_url
)
refund_reminder
=
_
(
"You have up to two weeks into the course to unenroll
from the Verified Certificate option
"
"
and receive a full refund.
To receive your refund, contact {billing_email}. "
"You have up to two weeks into the course to unenroll
and receive a full refund.
"
"To receive your refund, contact {billing_email}. "
"Please include your order number in your email. "
"Please do NOT include your credit card information."
)
.
format
(
billing_email
=
settings
.
PAYMENT_SUPPORT_EMAIL
)
domain
=
microsite
.
get_value
(
'SITE_NAME'
,
settings
.
SITE_NAME
)
path
=
reverse
(
'verify_student_verify_later'
,
kwargs
=
{
'course_id'
:
unicode
(
self
.
course_id
)})
verification_url
=
"http://{domain}{path}"
.
format
(
domain
=
domain
,
path
=
path
)
verification_reminder
=
_
(
"If you haven't verified your identity yet, please start the verification process ({verification_url})."
)
.
format
(
verification_url
=
verification_url
)
)
.
format
(
billing_email
=
settings
.
PAYMENT_SUPPORT_EMAIL
)
# Need this to be unicode in case the reminder strings
# have been translated and contain non-ASCII unicode
...
...
lms/djangoapps/shoppingcart/tests/test_models.py
View file @
4e454cca
...
...
@@ -801,6 +801,29 @@ class CertificateItemTest(ModuleStoreTestCase):
ret_val
=
CourseEnrollment
.
unenroll
(
self
.
user
,
self
.
course_key
)
self
.
assertFalse
(
ret_val
)
def
test_no_id_prof_confirm_email
(
self
):
# Pay for a no-id-professional course
course_mode
=
CourseMode
(
course_id
=
self
.
course_key
,
mode_slug
=
"no-id-professional"
,
mode_display_name
=
"No Id Professional Cert"
,
min_price
=
self
.
cost
)
course_mode
.
save
()
CourseEnrollment
.
enroll
(
self
.
user
,
self
.
course_key
)
cart
=
Order
.
get_cart_for_user
(
user
=
self
.
user
)
CertificateItem
.
add_to_order
(
cart
,
self
.
course_key
,
self
.
cost
,
'no-id-professional'
)
# verify that we are still enrolled
self
.
assertTrue
(
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course_key
))
self
.
mock_tracker
.
reset_mock
()
cart
.
purchase
()
enrollment
=
CourseEnrollment
.
objects
.
get
(
user
=
self
.
user
,
course_id
=
self
.
course_key
)
self
.
assertEquals
(
enrollment
.
mode
,
u'no-id-professional'
)
# Check that the tax-deduction information appears in the confirmation email
self
.
assertEqual
(
len
(
mail
.
outbox
),
1
)
email
=
mail
.
outbox
[
0
]
self
.
assertEquals
(
'Order Payment Confirmation'
,
email
.
subject
)
self
.
assertNotIn
(
"If you haven't verified your identity yet, please start the verification process"
,
email
.
body
)
class
DonationTest
(
ModuleStoreTestCase
):
"""Tests for the donation order item type. """
...
...
lms/djangoapps/student_account/test/test_views.py
View file @
4e454cca
...
...
@@ -298,7 +298,7 @@ class StudentAccountLoginAndRegistrationTest(UrlResetMixin, ModuleStoreTestCase)
]
self
.
_assert_third_party_auth_data
(
response
,
current_provider
,
expected_providers
)
@ddt.data
([],
[
"honor"
],
[
"honor"
,
"verified"
,
"audit"
],
[
"professional"
])
@ddt.data
([],
[
"honor"
],
[
"honor"
,
"verified"
,
"audit"
],
[
"professional"
]
,
[
"no-id-professional"
]
)
def
test_third_party_auth_course_id_verified
(
self
,
modes
):
# Create a course with the specified course modes
course
=
CourseFactory
.
create
()
...
...
lms/djangoapps/verify_student/tests/test_views.py
View file @
4e454cca
...
...
@@ -98,6 +98,21 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
])
self
.
_assert_upgrade_session_flag
(
False
)
@ddt.data
(
"no-id-professional"
)
def
test_start_flow_with_no_id_professional
(
self
,
course_mode
):
course
=
self
.
_create_course
(
course_mode
)
# by default enrollment is honor
self
.
_enroll
(
course
.
id
,
"honor"
)
response
=
self
.
_get_page
(
'verify_student_start_flow'
,
course
.
id
)
self
.
_assert_displayed_mode
(
response
,
course_mode
)
self
.
_assert_steps_displayed
(
response
,
PayAndVerifyView
.
PAYMENT_STEPS
,
PayAndVerifyView
.
MAKE_PAYMENT_STEP
)
self
.
_assert_messaging
(
response
,
PayAndVerifyView
.
FIRST_TIME_VERIFY_MSG
)
self
.
_assert_requirements_displayed
(
response
,
[])
@ddt.data
(
"expired"
,
"denied"
)
def
test_start_flow_expired_or_denied_verification
(
self
,
verification_status
):
course
=
self
.
_create_course
(
"verified"
)
...
...
@@ -121,7 +136,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
(
"verified"
,
"submitted"
),
(
"verified"
,
"approved"
),
(
"verified"
,
"error"
),
(
"professional"
,
"submitted"
)
(
"professional"
,
"submitted"
),
(
"no-id-professional"
,
None
),
)
@ddt.unpack
def
test_start_flow_already_verified
(
self
,
course_mode
,
verification_status
):
...
...
@@ -516,6 +532,14 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
expected_status_code
=
404
)
@ddt.data
([],
[
"no-id-professional"
,
"professional"
],
[
"honor"
,
"audit"
])
def
test_no_id_professional_entry_point
(
self
,
modes_available
):
course
=
self
.
_create_course
(
*
modes_available
)
if
"no-id-professional"
in
modes_available
or
"professional"
in
modes_available
:
self
.
_get_page
(
"verify_student_start_flow"
,
course
.
id
,
expected_status_code
=
200
)
else
:
self
.
_get_page
(
"verify_student_start_flow"
,
course
.
id
,
expected_status_code
=
404
)
@ddt.data
(
"verify_student_start_flow"
,
"verify_student_verify_now"
,
...
...
@@ -647,7 +671,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
modulestore
()
.
update_item
(
course
,
ModuleStoreEnum
.
UserID
.
test
)
for
course_mode
in
course_modes
:
min_price
=
(
self
.
MIN_PRICE
if
course_mode
!=
"honor"
else
0
)
min_price
=
(
0
if
course_mode
in
[
"honor"
,
"audit"
]
else
self
.
MIN_PRICE
)
CourseModeFactory
(
course_id
=
course
.
id
,
mode_slug
=
course_mode
,
...
...
@@ -826,8 +850,8 @@ class TestCreateOrder(ModuleStoreTestCase):
self
.
user
=
UserFactory
.
create
(
username
=
"test"
,
password
=
"test"
)
self
.
course
=
CourseFactory
.
create
()
for
mode
in
(
'audit'
,
'honor'
,
'verified'
):
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course
.
id
)
for
mode
,
min_price
in
((
'audit'
,
0
),
(
'honor'
,
0
),
(
'verified'
,
100
)
):
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course
.
id
,
min_price
=
min_price
)
self
.
client
.
login
(
username
=
"test"
,
password
=
"test"
)
def
test_create_order_already_verified
(
self
):
...
...
@@ -838,6 +862,7 @@ class TestCreateOrder(ModuleStoreTestCase):
url
=
reverse
(
'verify_student_create_order'
)
params
=
{
'course_id'
:
unicode
(
self
.
course
.
id
),
'contribution'
:
100
}
response
=
self
.
client
.
post
(
url
,
params
)
self
.
assertEqual
(
response
.
status_code
,
200
)
...
...
@@ -857,7 +882,7 @@ class TestCreateOrder(ModuleStoreTestCase):
# Create a prof ed course
course
=
CourseFactory
.
create
()
CourseModeFactory
(
mode_slug
=
"professional"
,
course_id
=
course
.
id
)
CourseModeFactory
(
mode_slug
=
"professional"
,
course_id
=
course
.
id
,
min_price
=
10
)
# Create an order for a prof ed course
url
=
reverse
(
'verify_student_create_order'
)
...
...
@@ -872,6 +897,45 @@ class TestCreateOrder(ModuleStoreTestCase):
self
.
assertEqual
(
data
[
'merchant_defined_data1'
],
unicode
(
course
.
id
))
self
.
assertEqual
(
data
[
'merchant_defined_data2'
],
"professional"
)
def
test_create_order_for_no_id_professional
(
self
):
# Create a no-id-professional ed course
course
=
CourseFactory
.
create
()
CourseModeFactory
(
mode_slug
=
"no-id-professional"
,
course_id
=
course
.
id
,
min_price
=
10
)
# Create an order for a prof ed course
url
=
reverse
(
'verify_student_create_order'
)
params
=
{
'course_id'
:
unicode
(
course
.
id
)
}
response
=
self
.
client
.
post
(
url
,
params
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# Verify that the course ID and transaction type are included in "merchant-defined data"
data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
data
[
'merchant_defined_data1'
],
unicode
(
course
.
id
))
self
.
assertEqual
(
data
[
'merchant_defined_data2'
],
"no-id-professional"
)
def
test_create_order_for_multiple_paid_modes
(
self
):
# Create a no-id-professional ed course
course
=
CourseFactory
.
create
()
CourseModeFactory
(
mode_slug
=
"no-id-professional"
,
course_id
=
course
.
id
,
min_price
=
10
)
CourseModeFactory
(
mode_slug
=
"professional"
,
course_id
=
course
.
id
,
min_price
=
10
)
# Create an order for a prof ed course
url
=
reverse
(
'verify_student_create_order'
)
params
=
{
'course_id'
:
unicode
(
course
.
id
)
}
response
=
self
.
client
.
post
(
url
,
params
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# Verify that the course ID and transaction type are included in "merchant-defined data"
data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
data
[
'merchant_defined_data1'
],
unicode
(
course
.
id
))
self
.
assertEqual
(
data
[
'merchant_defined_data2'
],
"no-id-professional"
)
def
test_create_order_set_donation_amount
(
self
):
# Verify the student so we don't need to submit photos
self
.
_verify_student
()
...
...
@@ -959,7 +1023,7 @@ class TestCreateOrderView(ModuleStoreTestCase):
photo_id_image
=
self
.
IMAGE_DATA
,
expect_status_code
=
400
)
self
.
assertIn
(
'This course doesn
\'
t support
verifie
d certificates'
,
response
.
content
)
self
.
assertIn
(
'This course doesn
\'
t support
pai
d certificates'
,
response
.
content
)
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
def
test_create_order_fail_with_get
(
self
):
...
...
lms/djangoapps/verify_student/views.py
View file @
4e454cca
...
...
@@ -272,15 +272,16 @@ class PayAndVerifyView(View):
if
redirect_url
:
return
redirect
(
redirect_url
)
# Check that the course has an unexpired verified mode
course_mode
,
expired_course_mode
=
self
.
_get_verified_modes_for_course
(
course_key
)
if
course_mode
is
not
None
:
log
.
info
(
u"Entering verified workflow for user '
%
s', course '
%
s', with current step '
%
s'."
,
request
.
user
.
id
,
course_id
,
current_step
)
elif
expired_course_mode
is
not
None
:
expired_verified_course_mode
,
unexpired_paid_course_mode
=
self
.
_get_expired_verified_and_paid_mode
(
course_key
)
# Check that the course has an unexpired paid mode
if
unexpired_paid_course_mode
is
not
None
:
if
CourseMode
.
is_verified_mode
(
unexpired_paid_course_mode
):
log
.
info
(
u"Entering verified workflow for user '
%
s', course '
%
s', with current step '
%
s'."
,
request
.
user
.
id
,
course_id
,
current_step
)
elif
expired_verified_course_mode
is
not
None
:
# Check if there is an *expired* verified course mode;
# if so, we should show a message explaining that the verification
# deadline has passed.
...
...
@@ -288,16 +289,16 @@ class PayAndVerifyView(View):
context
=
{
'course'
:
course
,
'deadline'
:
(
get_default_time_display
(
expired_course_mode
.
expiration_datetime
)
if
expired_course_mode
.
expiration_datetime
else
""
get_default_time_display
(
expired_
verified_
course_mode
.
expiration_datetime
)
if
expired_
verified_
course_mode
.
expiration_datetime
else
""
)
}
return
render_to_response
(
"verify_student/missed_verification_deadline.html"
,
context
)
else
:
# Otherwise, there has never been a verified mode,
# Otherwise, there has never been a verified
/paid
mode,
# so return a page not found response.
log
.
warn
(
u"No
verified course mode found for course '
%
s' for verification
flow request"
,
u"No
paid/verified course mode found for course '
%
s' for verification/payment
flow request"
,
course_id
)
raise
Http404
...
...
@@ -307,7 +308,9 @@ class PayAndVerifyView(View):
# with a paid course mode (such as "verified").
# For this reason, every paid user is enrolled, but not
# every enrolled user is paid.
already_verified
=
self
.
_check_already_verified
(
request
.
user
)
# If the course mode is not verified(i.e only paid) then already_verified is always True
already_verified
=
self
.
_check_already_verified
(
request
.
user
)
\
if
CourseMode
.
is_verified_mode
(
unexpired_paid_course_mode
)
else
True
already_paid
,
is_enrolled
=
self
.
_check_enrollment
(
request
.
user
,
course_key
)
# Redirect the user to a more appropriate page if the
...
...
@@ -326,7 +329,8 @@ class PayAndVerifyView(View):
display_steps
=
self
.
_display_steps
(
always_show_payment
,
already_verified
,
already_paid
already_paid
,
unexpired_paid_course_mode
)
requirements
=
self
.
_requirements
(
display_steps
,
request
.
user
.
is_active
)
...
...
@@ -371,7 +375,7 @@ class PayAndVerifyView(View):
'contribution_amount'
:
contribution_amount
,
'course'
:
course
,
'course_key'
:
unicode
(
course_key
),
'course_mode'
:
course_mode
,
'course_mode'
:
unexpired_paid_
course_mode
,
'courseware_url'
:
courseware_url
,
'current_step'
:
current_step
,
'disable_courseware_js'
:
True
,
...
...
@@ -383,8 +387,8 @@ class PayAndVerifyView(View):
'requirements'
:
requirements
,
'user_full_name'
:
full_name
,
'verification_deadline'
:
(
get_default_time_display
(
course_mode
.
expiration_datetime
)
if
course_mode
.
expiration_datetime
else
""
get_default_time_display
(
unexpired_paid_
course_mode
.
expiration_datetime
)
if
unexpired_paid_
course_mode
.
expiration_datetime
else
""
),
}
return
render_to_response
(
"verify_student/pay_and_verify.html"
,
context
)
...
...
@@ -459,22 +463,33 @@ class PayAndVerifyView(View):
if
url
is
not
None
:
return
redirect
(
url
)
def
_get_
verified_modes_for_course
(
self
,
course_key
):
"""Retrieve
unexpired and expired verified modes
for a course.
def
_get_
expired_verified_and_paid_mode
(
self
,
course_key
):
# pylint: disable=invalid-name
"""Retrieve
expired verified mode and unexpired paid mode(with min_price>0)
for a course.
Arguments:
course_key (CourseKey): The location of the course.
Returns:
Tuple of `(
verified_mode, expired_verifie
d_mode)`. If provided,
`
verified_mode` is an *un
expired* verified mode for the course.
If provided, `
expired_verified_mode` is an *expired* verified
Tuple of `(
expired_verified_mode, unexpired_pai
d_mode)`. If provided,
`
expired_verified_mode` is an *
expired* verified mode for the course.
If provided, `
unexpired_paid_mode` is an *unexpired* paid(with min_price>0)
mode for the course. Either of these may be None.
"""
# Retrieve all the modes at once to reduce the number of database queries
all_modes
,
unexpired_modes
=
CourseMode
.
all_and_unexpired_modes_for_courses
([
course_key
])
# Unexpired paid modes
unexpired_paid_modes
=
[
mode
for
mode
in
unexpired_modes
[
course_key
]
if
mode
.
min_price
]
if
len
(
unexpired_paid_modes
)
>
1
:
# There is more than one paid mode defined,
# so choose the first one.
log
.
warn
(
u"More than one paid modes are defined for course '
%
s' choosing the first one
%
s"
,
course_key
,
unexpired_paid_modes
[
0
]
)
unexpired_paid_mode
=
unexpired_paid_modes
[
0
]
if
unexpired_paid_modes
else
None
# Find an unexpired verified mode
verified_mode
=
CourseMode
.
verified_mode_for_course
(
course_key
,
modes
=
unexpired_modes
[
course_key
])
expired_verified_mode
=
None
...
...
@@ -482,9 +497,9 @@ class PayAndVerifyView(View):
if
verified_mode
is
None
:
expired_verified_mode
=
CourseMode
.
verified_mode_for_course
(
course_key
,
modes
=
all_modes
[
course_key
])
return
(
verified_mode
,
expired_verifie
d_mode
)
return
(
expired_verified_mode
,
unexpired_pai
d_mode
)
def
_display_steps
(
self
,
always_show_payment
,
already_verified
,
already_paid
):
def
_display_steps
(
self
,
always_show_payment
,
already_verified
,
already_paid
,
course_mode
):
"""Determine which steps to display to the user.
Includes all steps by default, but removes steps
...
...
@@ -508,7 +523,7 @@ class PayAndVerifyView(View):
display_steps
=
self
.
ALL_STEPS
remove_steps
=
set
()
if
already_verified
:
if
already_verified
or
not
CourseMode
.
is_verified_mode
(
course_mode
)
:
remove_steps
|=
set
(
self
.
VERIFICATION_STEPS
)
if
already_paid
and
not
always_show_payment
:
...
...
@@ -517,7 +532,6 @@ class PayAndVerifyView(View):
# The "make payment" step doubles as an intro step,
# so if we're showing the payment step, hide the intro step.
remove_steps
|=
set
([
self
.
INTRO_STEP
])
return
[
{
'name'
:
step
,
...
...
@@ -642,15 +656,21 @@ def create_order(request):
except
decimal
.
InvalidOperation
:
return
HttpResponseBadRequest
(
_
(
"Selected price is not valid number."
))
# prefer professional mode over verified_mode
current_mode
=
CourseMode
.
verified_mode_for_course
(
course_id
)
current_mode
=
None
paid_modes
=
CourseMode
.
paid_modes_for_course
(
course_id
)
# Check if there are more than 1 paid(mode with min_price>0 e.g verified/professional/no-id-professional) modes
# for course exist then choose the first one
if
paid_modes
:
if
len
(
paid_modes
)
>
1
:
log
.
warn
(
u"Multiple paid course modes found for course '
%
s' for create order request"
,
course_id
)
current_mode
=
paid_modes
[
0
]
#
make sure this course has a verifie
d mode
#
Make sure this course has a pai
d mode
if
not
current_mode
:
log
.
warn
(
u"
Verification requested for course {course_id} without a verified mode."
.
format
(
course_id
=
course_id
)
)
return
HttpResponseBadRequest
(
_
(
"This course doesn't support
verifie
d certificates"
))
log
.
warn
(
u"
Create order requested for course '
%
s' without a paid mode."
,
course_id
)
return
HttpResponseBadRequest
(
_
(
"This course doesn't support
pai
d certificates"
))
if
current_mode
.
slug
==
'professional'
:
if
CourseMode
.
is_professional_mode
(
current_mode
)
:
amount
=
current_mode
.
min_price
if
amount
<
current_mode
.
min_price
:
...
...
lms/templates/dashboard/_dashboard_course_listing.html
View file @
4e454cca
...
...
@@ -6,6 +6,7 @@ from django.utils.translation import ungettext
from
django
.
core
.
urlresolvers
import
reverse
from
markupsafe
import
escape
from
courseware
.
courses
import
course_image_url
,
get_course_about_section
from
course_modes
.
models
import
CourseMode
from
student
.
helpers
import
(
VERIFY_STATUS_NEED_TO_VERIFY
,
VERIFY_STATUS_SUBMITTED
,
...
...
@@ -30,18 +31,15 @@ from student.helpers import (
<li
class=
"course-item"
>
% if settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES'):
% if enrollment.mode == "verified":
% if verification_status.get('status') in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED, VERIFY_STATUS_APPROVED]:
<
%
mode_class =
" verified"
%
>
% else:
<
%
mode_class =
" honor"
%
>
% endif
% else:
<
%
mode_class =
" "
+
enrollment
.
mode
%
>
% endif
<
%
course_verified_certs =
CourseMode.enrollment_mode_display(enrollment.mode,
verification_status
.
get
('
status
'))
%
>
<
%
mode_class =
course_verified_certs.get('display_mode',
'')
if
mode_class
!=
''
:
mode_class =
' '
+
mode_class
;
%
>
% else:
<
%
mode_class =
""
%
>
% endif
<
%
mode_class =
''
%
>
% endif
<article
class=
"course${mode_class}"
>
...
...
@@ -64,44 +62,15 @@ from student.helpers import (
<img
src=
"${course_image_url(course)}"
alt=
"${_('{course_number} {course_name} Cover Image').format(course_number=course.number, course_name=course.display_name_with_default) | h}"
/>
</div>
% endif
% if settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES'):
% if enrollment.mode == "verified":
% if verification_status.get('status') in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED]:
<span
class=
"sts-enrollment"
title=
"${_("
Your
verification
is
pending
")}"
>
<span
class=
"label"
>
${_("Enrolled as: ")}
</span>
## Translators: This text describes that the student has enrolled for a Verified Certificate, but verification of identity is pending.
<img
class=
"deco-graphic"
src=
"${static.url('images/verified-ribbon.png')}"
alt=
"${_("
ID
verification
pending
")}"
/>
## Translators: The student is enrolled for a Verified Certificate, but verification of identity is pending.
<div
class=
"sts-enrollment-value"
>
${_("Verified: Pending Verification")}
</div>
</span>
% elif verification_status.get('status') == VERIFY_STATUS_APPROVED:
<span
class=
"sts-enrollment"
title=
"${_("
You
'
re
enrolled
as
a
verified
student
")}"
>
<span
class=
"label"
>
${_("Enrolled as: ")}
</span>
<img
class=
"deco-graphic"
src=
"${static.url('images/verified-ribbon.png')}"
alt=
"${_("
ID
Verified
Ribbon
/
Badge
")}"
/>
<div
class=
"sts-enrollment-value"
>
${_("Verified")}
</div>
</span>
% else:
<span
class=
"sts-enrollment"
title=
"${_("
You
'
re
enrolled
as
an
honor
code
student
")}"
>
<span
class=
"label"
>
${_("Enrolled as: ")}
</span>
<div
class=
"sts-enrollment-value"
>
${_("Honor Code")}
</div>
</span>
% endif
% elif enrollment.mode == "honor":
<span
class=
"sts-enrollment"
title=
"${_("
You
'
re
enrolled
as
an
honor
code
student
")}"
>
<span
class=
"label"
>
${_("Enrolled as: ")}
</span>
<div
class=
"sts-enrollment-value"
>
${_("Honor Code")}
</div>
</span>
% elif enrollment.mode == "audit":
<span
class=
"sts-enrollment"
title=
"${_("
You
'
re
auditing
this
course
")}"
>
<span
class=
"label"
>
${_("Enrolled as: ")}
</span>
<div
class=
"sts-enrollment-value"
>
${_("Auditing")}
</div>
</span>
% elif enrollment.mode == "professional":
<span
class=
"sts-enrollment"
title=
"${_("
You
'
re
enrolled
as
a
professional
education
student
")}"
>
<span
class=
"sts-enrollment"
title=
"${course_verified_certs.get('enrollment_title')}"
>
<span
class=
"label"
>
${_("Enrolled as: ")}
</span>
<div
class=
"sts-enrollment-value"
>
${_("Professional Ed")}
</div>
% if course_verified_certs.get('show_image'):
<img
class=
"deco-graphic"
src=
"${static.url('images/verified-ribbon.png')}"
alt=
"${course_verified_certs.get('image_alt')}"
/>
% endif
<div
class=
"sts-enrollment-value"
>
${course_verified_certs.get('enrollment_value')}
</div>
</span>
% endif
% endif
<section
class=
"info"
>
...
...
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