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
14fed7a7
Commit
14fed7a7
authored
Jun 08, 2017
by
Calen Pennington
Committed by
GitHub
Jun 08, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15265 from edx/ret/upsell-cookie
Upgrade course experiment
parents
91f1923d
ede33605
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
143 additions
and
211 deletions
+143
-211
lms/djangoapps/ccx/tests/test_field_override_performance.py
+27
-27
lms/djangoapps/courseware/date_summary.py
+10
-9
lms/djangoapps/courseware/tests/test_course_info.py
+2
-2
lms/djangoapps/courseware/tests/test_date_summary.py
+0
-83
lms/djangoapps/courseware/tests/test_views.py
+12
-10
lms/djangoapps/courseware/views/index.py
+8
-4
lms/djangoapps/courseware/views/views.py
+61
-48
lms/djangoapps/discussion/views.py
+5
-1
lms/templates/courseware/info.html
+0
-20
lms/templates/courseware/upgrade.html
+9
-0
lms/templates/main.html
+1
-0
openedx/features/course_experience/tests/views/test_course_home.py
+1
-1
openedx/features/course_experience/tests/views/test_course_updates.py
+1
-1
openedx/features/course_experience/views/course_outline.py
+4
-1
pavelib/utils/test/suites/suite.py
+1
-3
scripts/generic-ci-tests.sh
+1
-1
No files found.
lms/djangoapps/ccx/tests/test_field_override_performance.py
View file @
14fed7a7
...
...
@@ -231,18 +231,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
# # of sql queries to default,
# # of mongo queries,
# )
(
'no_overrides'
,
1
,
True
,
False
):
(
2
5
,
1
),
(
'no_overrides'
,
2
,
True
,
False
):
(
2
5
,
1
),
(
'no_overrides'
,
3
,
True
,
False
):
(
2
5
,
1
),
(
'ccx'
,
1
,
True
,
False
):
(
2
5
,
1
),
(
'ccx'
,
2
,
True
,
False
):
(
2
5
,
1
),
(
'ccx'
,
3
,
True
,
False
):
(
2
5
,
1
),
(
'no_overrides'
,
1
,
False
,
False
):
(
2
5
,
1
),
(
'no_overrides'
,
2
,
False
,
False
):
(
2
5
,
1
),
(
'no_overrides'
,
3
,
False
,
False
):
(
2
5
,
1
),
(
'ccx'
,
1
,
False
,
False
):
(
2
5
,
1
),
(
'ccx'
,
2
,
False
,
False
):
(
2
5
,
1
),
(
'ccx'
,
3
,
False
,
False
):
(
2
5
,
1
),
(
'no_overrides'
,
1
,
True
,
False
):
(
2
7
,
1
),
(
'no_overrides'
,
2
,
True
,
False
):
(
2
7
,
1
),
(
'no_overrides'
,
3
,
True
,
False
):
(
2
7
,
1
),
(
'ccx'
,
1
,
True
,
False
):
(
2
7
,
1
),
(
'ccx'
,
2
,
True
,
False
):
(
2
7
,
1
),
(
'ccx'
,
3
,
True
,
False
):
(
2
7
,
1
),
(
'no_overrides'
,
1
,
False
,
False
):
(
2
7
,
1
),
(
'no_overrides'
,
2
,
False
,
False
):
(
2
7
,
1
),
(
'no_overrides'
,
3
,
False
,
False
):
(
2
7
,
1
),
(
'ccx'
,
1
,
False
,
False
):
(
2
7
,
1
),
(
'ccx'
,
2
,
False
,
False
):
(
2
7
,
1
),
(
'ccx'
,
3
,
False
,
False
):
(
2
7
,
1
),
}
...
...
@@ -254,19 +254,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
__test__
=
True
TEST_DATA
=
{
(
'no_overrides'
,
1
,
True
,
False
):
(
2
5
,
3
),
(
'no_overrides'
,
2
,
True
,
False
):
(
2
5
,
3
),
(
'no_overrides'
,
3
,
True
,
False
):
(
2
5
,
3
),
(
'ccx'
,
1
,
True
,
False
):
(
2
5
,
3
),
(
'ccx'
,
2
,
True
,
False
):
(
2
5
,
3
),
(
'ccx'
,
3
,
True
,
False
):
(
2
5
,
3
),
(
'ccx'
,
1
,
True
,
True
):
(
2
6
,
3
),
(
'ccx'
,
2
,
True
,
True
):
(
2
6
,
3
),
(
'ccx'
,
3
,
True
,
True
):
(
2
6
,
3
),
(
'no_overrides'
,
1
,
False
,
False
):
(
2
5
,
3
),
(
'no_overrides'
,
2
,
False
,
False
):
(
2
5
,
3
),
(
'no_overrides'
,
3
,
False
,
False
):
(
2
5
,
3
),
(
'ccx'
,
1
,
False
,
False
):
(
2
5
,
3
),
(
'ccx'
,
2
,
False
,
False
):
(
2
5
,
3
),
(
'ccx'
,
3
,
False
,
False
):
(
2
5
,
3
),
(
'no_overrides'
,
1
,
True
,
False
):
(
2
7
,
3
),
(
'no_overrides'
,
2
,
True
,
False
):
(
2
7
,
3
),
(
'no_overrides'
,
3
,
True
,
False
):
(
2
7
,
3
),
(
'ccx'
,
1
,
True
,
False
):
(
2
7
,
3
),
(
'ccx'
,
2
,
True
,
False
):
(
2
7
,
3
),
(
'ccx'
,
3
,
True
,
False
):
(
2
7
,
3
),
(
'ccx'
,
1
,
True
,
True
):
(
2
8
,
3
),
(
'ccx'
,
2
,
True
,
True
):
(
2
8
,
3
),
(
'ccx'
,
3
,
True
,
True
):
(
2
8
,
3
),
(
'no_overrides'
,
1
,
False
,
False
):
(
2
7
,
3
),
(
'no_overrides'
,
2
,
False
,
False
):
(
2
7
,
3
),
(
'no_overrides'
,
3
,
False
,
False
):
(
2
7
,
3
),
(
'ccx'
,
1
,
False
,
False
):
(
2
7
,
3
),
(
'ccx'
,
2
,
False
,
False
):
(
2
7
,
3
),
(
'ccx'
,
3
,
False
,
False
):
(
2
7
,
3
),
}
lms/djangoapps/courseware/date_summary.py
View file @
14fed7a7
...
...
@@ -72,9 +72,10 @@ class DateSummary(object):
self
.
user
.
preferences
.
model
.
get_value
(
self
.
user
,
"time_zone"
,
"UTC"
)
)
def
__init__
(
self
,
course
,
user
):
def
__init__
(
self
,
course
,
user
,
course_id
=
None
):
self
.
course
=
course
self
.
user
=
user
self
.
course_id
=
course_id
or
self
.
course
.
id
@property
def
relative_datestring
(
self
):
...
...
@@ -174,7 +175,7 @@ class CourseEndDate(DateSummary):
@property
def
description
(
self
):
if
datetime
.
now
(
utc
)
<=
self
.
date
:
mode
,
is_active
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
.
id
)
mode
,
is_active
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
_
id
)
if
is_active
and
CourseMode
.
is_eligible_for_certificate
(
mode
):
return
_
(
'To earn a certificate, you must complete all requirements before this date.'
)
else
:
...
...
@@ -204,10 +205,10 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
ecommerce_service
=
EcommerceService
()
if
ecommerce_service
.
is_enabled
(
self
.
user
):
course_mode
=
CourseMode
.
objects
.
get
(
course_id
=
self
.
course
.
id
,
mode_slug
=
CourseMode
.
VERIFIED
course_id
=
self
.
course
_
id
,
mode_slug
=
CourseMode
.
VERIFIED
)
return
ecommerce_service
.
checkout_page_url
(
course_mode
.
sku
)
return
reverse
(
'verify_student_upgrade_and_verify'
,
args
=
(
self
.
course
.
id
,))
return
reverse
(
'verify_student_upgrade_and_verify'
,
args
=
(
self
.
course
_
id
,))
@property
def
is_enabled
(
self
):
...
...
@@ -221,7 +222,7 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
if
not
is_enabled
:
return
False
enrollment_mode
,
is_active
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
.
id
)
enrollment_mode
,
is_active
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
_
id
)
# Return `true` if user is not enrolled in course
if
enrollment_mode
is
None
and
is_active
is
None
:
...
...
@@ -234,7 +235,7 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
def
date
(
self
):
try
:
verified_mode
=
CourseMode
.
objects
.
get
(
course_id
=
self
.
course
.
id
,
mode_slug
=
CourseMode
.
VERIFIED
course_id
=
self
.
course
_
id
,
mode_slug
=
CourseMode
.
VERIFIED
)
return
verified_mode
.
expiration_datetime
except
CourseMode
.
DoesNotExist
:
...
...
@@ -273,7 +274,7 @@ class VerificationDeadlineDate(DateSummary):
'verification-deadline-retry'
:
(
_
(
'Retry Verification'
),
reverse
(
'verify_student_reverify'
)),
'verification-deadline-upcoming'
:
(
_
(
'Verify My Identity'
),
reverse
(
'verify_student_verify_now'
,
args
=
(
self
.
course
.
id
,))
reverse
(
'verify_student_verify_now'
,
args
=
(
self
.
course
_
id
,))
)
}
...
...
@@ -297,13 +298,13 @@ class VerificationDeadlineDate(DateSummary):
@lazy
def
date
(
self
):
return
VerificationDeadline
.
deadline_for_course
(
self
.
course
.
id
)
return
VerificationDeadline
.
deadline_for_course
(
self
.
course
_
id
)
@lazy
def
is_enabled
(
self
):
if
self
.
date
is
None
:
return
False
(
mode
,
is_active
)
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
.
id
)
(
mode
,
is_active
)
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
_
id
)
if
is_active
and
mode
==
'verified'
:
return
self
.
verification_status
in
(
'expired'
,
'none'
,
'must_reverify'
)
return
False
...
...
lms/djangoapps/courseware/tests/test_course_info.py
View file @
14fed7a7
...
...
@@ -367,7 +367,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
self
.
assertEqual
(
resp
.
status_code
,
200
)
def
test_num_queries_instructor_paced
(
self
):
self
.
fetch_course_info_with_queries
(
self
.
instructor_paced_course
,
2
1
,
4
)
self
.
fetch_course_info_with_queries
(
self
.
instructor_paced_course
,
2
3
,
4
)
def
test_num_queries_self_paced
(
self
):
self
.
fetch_course_info_with_queries
(
self
.
self_paced_course
,
2
1
,
4
)
self
.
fetch_course_info_with_queries
(
self
.
self_paced_course
,
2
3
,
4
)
lms/djangoapps/courseware/tests/test_date_summary.py
View file @
14fed7a7
...
...
@@ -310,89 +310,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
)
self
.
assertEqual
(
block
.
title
,
'Course End'
)
# Tests Verified Upgrade Deadline Date Block
def
check_upgrade_banner
(
self
,
banner_expected
=
True
,
include_url_parameter
=
True
,
expected_cookie_value
=
None
):
"""
Helper method to check for the presence of the Upgrade Banner
"""
url
=
reverse
(
'info'
,
args
=
[
self
.
course
.
id
.
to_deprecated_string
()])
if
include_url_parameter
:
url
+=
'?upgrade=true'
resp
=
self
.
client
.
get
(
url
)
upgrade_cookie_name
=
'show_upgrade_notification'
expected_banner_text
=
"Give yourself an additional incentive to complete"
if
banner_expected
:
self
.
assertIn
(
expected_banner_text
,
resp
.
content
)
self
.
assertIn
(
str
(
self
.
course
.
id
),
self
.
client
.
cookies
[
upgrade_cookie_name
]
.
value
)
else
:
self
.
assertNotIn
(
expected_banner_text
,
resp
.
content
)
if
upgrade_cookie_name
in
self
.
client
.
cookies
:
self
.
assertNotIn
(
str
(
self
.
course
.
id
),
self
.
client
.
cookies
[
upgrade_cookie_name
]
.
value
)
if
expected_cookie_value
is
not
None
:
self
.
assertIn
(
str
(
expected_cookie_value
),
self
.
client
.
cookies
[
upgrade_cookie_name
]
.
value
)
def
test_verified_upgrade_deadline_date
(
self
):
with
freeze_time
(
'2015-01-02'
):
self
.
setup_course_and_user
(
days_till_upgrade_deadline
=
1
,
user_enrollment_mode
=
CourseMode
.
AUDIT
)
self
.
client
.
login
(
username
=
'mrrobot'
,
password
=
'test'
)
block
=
VerifiedUpgradeDeadlineDate
(
self
.
course
,
self
.
user
)
self
.
assertEqual
(
block
.
date
,
datetime
.
now
(
utc
)
+
timedelta
(
days
=
1
))
self
.
assertTrue
(
block
.
is_enabled
)
self
.
assertEqual
(
block
.
link
,
reverse
(
'verify_student_upgrade_and_verify'
,
args
=
(
self
.
course
.
id
,)))
self
.
check_upgrade_banner
()
def
test_without_upgrade_deadline
(
self
):
self
.
setup_course_and_user
(
enrollment_mode
=
None
)
self
.
client
.
login
(
username
=
'mrrobot'
,
password
=
'test'
)
block
=
VerifiedUpgradeDeadlineDate
(
self
.
course
,
self
.
user
)
self
.
assertFalse
(
block
.
is_enabled
)
self
.
assertIsNone
(
block
.
date
)
self
.
check_upgrade_banner
(
banner_expected
=
False
)
def
test_verified_upgrade_banner_not_present_past_deadline
(
self
):
with
freeze_time
(
'2015-01-02'
):
self
.
setup_course_and_user
(
days_till_upgrade_deadline
=-
1
,
user_enrollment_mode
=
CourseMode
.
AUDIT
)
self
.
client
.
login
(
username
=
'mrrobot'
,
password
=
'test'
)
block
=
VerifiedUpgradeDeadlineDate
(
self
.
course
,
self
.
user
)
self
.
assertFalse
(
block
.
is_enabled
)
self
.
check_upgrade_banner
(
banner_expected
=
False
)
def
test_verified_upgrade_banner_cookie
(
self
):
with
freeze_time
(
'2015-01-02'
):
self
.
setup_course_and_user
(
days_till_upgrade_deadline
=
1
,
user_enrollment_mode
=
CourseMode
.
AUDIT
)
self
.
client
.
login
(
username
=
'mrrobot'
,
password
=
'test'
)
# No URL parameter or cookie present, notification should not be shown.
self
.
check_upgrade_banner
(
include_url_parameter
=
False
,
banner_expected
=
False
)
# Now pass URL parameter-- notification should be shown.
self
.
check_upgrade_banner
(
include_url_parameter
=
True
)
# A cookie should be set in the previous call, so it is no longer necessary to pass
# the URL parameter in order to see the notification.
self
.
check_upgrade_banner
(
include_url_parameter
=
False
)
# Store the current course_id for testing
old_course_id
=
self
.
course
.
id
# Change to another course
self
.
setup_course_and_user
(
days_till_upgrade_deadline
=
1
,
user_enrollment_mode
=
CourseMode
.
AUDIT
,
create_user
=
False
)
# Banner should not be present in the newly created course
self
.
check_upgrade_banner
(
include_url_parameter
=
False
,
banner_expected
=
False
,
expected_cookie_value
=
old_course_id
)
# Unfortunately (according to django doc), it is not possible to test expiration of the cookie.
def
test_ecommerce_checkout_redirect
(
self
):
"""Verify the block link redirects to ecommerce checkout if it's enabled."""
sku
=
'TESTSKU'
...
...
lms/djangoapps/courseware/tests/test_views.py
View file @
14fed7a7
...
...
@@ -208,8 +208,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
NUM_PROBLEMS
=
20
@ddt.data
(
(
ModuleStoreEnum
.
Type
.
mongo
,
10
,
14
5
),
(
ModuleStoreEnum
.
Type
.
split
,
4
,
14
5
),
(
ModuleStoreEnum
.
Type
.
mongo
,
10
,
14
7
),
(
ModuleStoreEnum
.
Type
.
split
,
4
,
14
7
),
)
@ddt.unpack
def
test_index_query_counts
(
self
,
store_type
,
expected_mongo_query_count
,
expected_mysql_query_count
):
...
...
@@ -584,16 +584,18 @@ class ViewsTestCase(ModuleStoreTestCase):
"""
registration_price
=
99
self
.
course
.
cosmetic_display_price
=
10
# Since registration_price is set, it overrides the cosmetic_display_price and should be returned
self
.
assertEqual
(
views
.
get_cosmetic_display_price
(
self
.
course
,
registration_price
),
"$99"
)
with
patch
(
'course_modes.models.CourseMode.min_course_price_for_currency'
,
return_value
=
registration_price
):
# Since registration_price is set, it overrides the cosmetic_display_price and should be returned
self
.
assertEqual
(
views
.
get_cosmetic_display_price
(
self
.
course
),
"$99"
)
registration_price
=
0
# Since registration_price is not set, cosmetic_display_price should be returned
self
.
assertEqual
(
views
.
get_cosmetic_display_price
(
self
.
course
,
registration_price
),
"$10"
)
with
patch
(
'course_modes.models.CourseMode.min_course_price_for_currency'
,
return_value
=
registration_price
):
# Since registration_price is not set, cosmetic_display_price should be returned
self
.
assertEqual
(
views
.
get_cosmetic_display_price
(
self
.
course
),
"$10"
)
self
.
course
.
cosmetic_display_price
=
0
# Since both prices are not set, there is no price, thus "Free"
self
.
assertEqual
(
views
.
get_cosmetic_display_price
(
self
.
course
,
registration_price
),
"Free"
)
self
.
assertEqual
(
views
.
get_cosmetic_display_price
(
self
.
course
),
"Free"
)
def
test_jump_to_invalid
(
self
):
# TODO add a test for invalid location
...
...
@@ -1432,12 +1434,12 @@ class ProgressPageTests(ProgressPageBaseTests):
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
SelfPacedConfiguration
(
enabled
=
self_paced_enabled
)
.
save
()
self
.
setup_course
(
self_paced
=
self_paced
)
with
self
.
assertNumQueries
(
4
1
),
check_mongo_calls
(
1
):
with
self
.
assertNumQueries
(
4
3
),
check_mongo_calls
(
1
):
self
.
_get_progress_page
()
@ddt.data
(
(
False
,
4
1
,
27
),
(
True
,
3
4
,
23
)
(
False
,
4
3
,
29
),
(
True
,
3
6
,
25
)
)
@ddt.unpack
def
test_progress_queries
(
self
,
enable_waffle
,
initial
,
subsequent
):
...
...
lms/djangoapps/courseware/views/index.py
View file @
14fed7a7
...
...
@@ -55,7 +55,10 @@ from ..entrance_exams import (
from
..masquerade
import
setup_masquerade
from
..model_data
import
FieldDataCache
from
..module_render
import
toc_for_course
,
get_module_for_descriptor
from
.views
import
CourseTabView
,
check_access_to_course
from
.views
import
(
CourseTabView
,
check_access_to_course
,
check_and_get_upgrade_link
,
get_cosmetic_verified_display_price
)
TEMPLATE_IMPORTS
=
{
'urllib'
:
urllib
}
...
...
@@ -149,7 +152,7 @@ class CoursewareIndex(View):
self
.
_save_positions
()
self
.
_prefetch_and_bind_section
()
return
render_to_response
(
'courseware/courseware.html'
,
self
.
_create_courseware_context
())
return
render_to_response
(
'courseware/courseware.html'
,
self
.
_create_courseware_context
(
request
))
def
_redirect_if_not_requested_section
(
self
):
"""
...
...
@@ -319,12 +322,11 @@ class CoursewareIndex(View):
save_child_position
(
self
.
course
,
self
.
chapter_url_name
)
save_child_position
(
self
.
chapter
,
self
.
section_url_name
)
def
_create_courseware_context
(
self
):
def
_create_courseware_context
(
self
,
request
):
"""
Returns and creates the rendering context for the courseware.
Also returns the table of contents for the courseware.
"""
request
=
RequestCache
.
get_current_request
()
course_url_name
=
default_course_url_name
(
request
)
course_url
=
reverse
(
course_url_name
,
kwargs
=
{
'course_id'
:
unicode
(
self
.
course
.
id
)})
courseware_context
=
{
...
...
@@ -346,6 +348,8 @@ class CoursewareIndex(View):
'section_title'
:
None
,
'sequence_title'
:
None
,
'disable_accordion'
:
waffle
.
flag_is_active
(
request
,
UNIFIED_COURSE_VIEW_FLAG
),
'upgrade_link'
:
check_and_get_upgrade_link
(
request
,
self
.
effective_user
,
self
.
course
.
id
),
'upgrade_price'
:
get_cosmetic_verified_display_price
(
self
.
course
),
}
table_of_contents
=
toc_for_course
(
self
.
effective_user
,
...
...
lms/djangoapps/courseware/views/views.py
View file @
14fed7a7
...
...
@@ -320,30 +320,12 @@ def course_info(request, course_id):
if
settings
.
FEATURES
.
get
(
'ENABLE_MKTG_SITE'
):
url_to_enroll
=
marketing_link
(
'COURSES'
)
store_upgrade_cookie
=
False
upgrade_cookie_name
=
'show_upgrade_notification'
upgrade_link
=
None
# Construct the dates fragment
dates_fragment
=
None
if
request
.
user
.
is_authenticated
():
if
SelfPacedConfiguration
.
current
()
.
enable_course_home_improvements
:
dates_fragment
=
CourseDatesFragmentView
()
.
render_to_fragment
(
request
,
course_id
=
course_id
)
show_upgrade_notification
=
False
if
request
.
GET
.
get
(
'upgrade'
,
'false'
)
==
'true'
:
store_upgrade_cookie
=
True
show_upgrade_notification
=
True
elif
upgrade_cookie_name
in
request
.
COOKIES
and
course_id
in
request
.
COOKIES
[
upgrade_cookie_name
]:
show_upgrade_notification
=
True
if
show_upgrade_notification
:
upgrade_data
=
VerifiedUpgradeDeadlineDate
(
course
,
user
)
if
upgrade_data
.
is_enabled
:
upgrade_link
=
upgrade_data
.
link
else
:
# The upgrade is not enabled so the cookie does not need to be stored
store_upgrade_cookie
=
False
context
=
{
'request'
:
request
,
...
...
@@ -358,7 +340,8 @@ def course_info(request, course_id):
'show_enroll_banner'
:
show_enroll_banner
,
'dates_fragment'
:
dates_fragment
,
'url_to_enroll'
:
url_to_enroll
,
'upgrade_link'
:
upgrade_link
,
'upgrade_link'
:
check_and_get_upgrade_link
(
request
,
user
,
course
.
id
),
'upgrade_price'
:
get_cosmetic_verified_display_price
(
course
),
}
# Get the URL of the user's last position in order to display the 'where you were last' message
...
...
@@ -375,25 +358,22 @@ def course_info(request, course_id):
if
CourseEnrollment
.
is_enrolled
(
request
.
user
,
course
.
id
):
inject_coursetalk_keys_into_context
(
context
,
course_key
)
response
=
render_to_response
(
'courseware/info.html'
,
context
)
if
store_upgrade_cookie
:
if
upgrade_cookie_name
in
request
.
COOKIES
and
str
(
course_id
)
not
in
request
.
COOKIES
[
upgrade_cookie_name
]:
cookie_value
=
'
%
s,
%
s'
%
(
course_id
,
request
.
COOKIES
[
upgrade_cookie_name
])
elif
upgrade_cookie_name
in
request
.
COOKIES
and
str
(
course_id
)
in
request
.
COOKIES
[
upgrade_cookie_name
]:
cookie_value
=
request
.
COOKIES
[
upgrade_cookie_name
]
else
:
cookie_value
=
course_id
if
cookie_value
is
not
None
:
response
.
set_cookie
(
upgrade_cookie_name
,
cookie_value
,
max_age
=
10
*
24
*
60
*
60
,
# set for 10 days
domain
=
settings
.
SESSION_COOKIE_DOMAIN
,
httponly
=
True
# no use case for accessing from JavaScript
)
return
render_to_response
(
'courseware/info.html'
,
context
)
UPGRADE_COOKIE_NAME
=
'show_upgrade_notification'
def
check_and_get_upgrade_link
(
request
,
user
,
course_id
):
upgrade_link
=
None
if
request
.
user
.
is_authenticated
():
upgrade_data
=
VerifiedUpgradeDeadlineDate
(
None
,
user
,
course_id
=
course_id
)
if
upgrade_data
.
is_enabled
:
upgrade_link
=
upgrade_data
.
link
request
.
need_to_set_upgrade_cookie
=
True
return
response
return
upgrade_link
class
StaticCourseTabView
(
EdxFragmentView
):
...
...
@@ -518,6 +498,8 @@ class CourseTabView(EdxFragmentView):
'supports_preview_menu'
:
supports_preview_menu
,
'uses_pattern_library'
:
True
,
'disable_courseware_js'
:
True
,
'upgrade_link'
:
check_and_get_upgrade_link
(
request
,
request
.
user
,
course
.
id
),
'upgrade_price'
:
get_cosmetic_verified_display_price
(
course
),
}
def
render_to_fragment
(
self
,
request
,
course
=
None
,
page_context
=
None
,
**
kwargs
):
...
...
@@ -569,23 +551,57 @@ def registered_for_course(course, user):
return
False
def
get_cosmetic_
display_price
(
course
,
registration_pric
e
):
def
get_cosmetic_
verified_display_price
(
cours
e
):
"""
Return Course Price as a string preceded by correct currency, or 'Free'
Returns the minimum verified cert course price as a string preceded by correct currency, or 'Free'.
"""
return
get_course_prices
(
course
,
verified_only
=
True
)[
1
]
def
get_cosmetic_display_price
(
course
):
"""
Returns the course price as a string preceded by correct currency, or 'Free'.
"""
return
get_course_prices
(
course
)[
1
]
def
get_course_prices
(
course
,
verified_only
=
False
):
"""
Return registration_price and cosmetic_display_prices.
registration_price is the minimum price for the course across all course modes.
cosmetic_display_prices is the course price as a string preceded by correct currency, or 'Free'.
"""
# Find the
if
verified_only
:
registration_price
=
CourseMode
.
min_course_price_for_verified_for_currency
(
course
.
id
,
settings
.
PAID_COURSE_REGISTRATION_CURRENCY
[
0
]
)
else
:
registration_price
=
CourseMode
.
min_course_price_for_currency
(
course
.
id
,
settings
.
PAID_COURSE_REGISTRATION_CURRENCY
[
0
]
)
currency_symbol
=
settings
.
PAID_COURSE_REGISTRATION_CURRENCY
[
1
]
price
=
course
.
cosmetic_display_price
if
registration_price
>
0
:
price
=
registration_price
# Handle course overview objects which have no cosmetic_display_price
elif
hasattr
(
course
,
'cosmetic_display_price'
):
price
=
course
.
cosmetic_display_price
else
:
price
=
None
if
price
:
# Translators: This will look like '$50', where {currency_symbol} is a symbol such as '$' and {price} is a
# numerical amount in that currency. Adjust this display as needed for your language.
return
_
(
"{currency_symbol}{price}"
)
.
format
(
currency_symbol
=
currency_symbol
,
price
=
price
)
cosmetic_display_price
=
_
(
"{currency_symbol}{price}"
)
.
format
(
currency_symbol
=
currency_symbol
,
price
=
price
)
else
:
# Translators: This refers to the cost of the course. In this case, the course costs nothing so it is free.
return
_
(
'Free'
)
cosmetic_display_price
=
_
(
'Free'
)
return
registration_price
,
cosmetic_display_price
class
EnrollStaffView
(
View
):
...
...
@@ -720,12 +736,7 @@ def course_about(request, course_id):
if
professional_mode
.
bulk_sku
:
ecommerce_bulk_checkout_link
=
ecomm_service
.
checkout_page_url
(
professional_mode
.
bulk_sku
)
# Find the minimum price for the course across all course modes
registration_price
=
CourseMode
.
min_course_price_for_currency
(
course_key
,
settings
.
PAID_COURSE_REGISTRATION_CURRENCY
[
0
]
)
course_price
=
get_cosmetic_display_price
(
course
,
registration_price
)
registration_price
,
course_price
=
get_course_prices
(
course
)
# Determine which checkout workflow to use -- LMS shoppingcart or Otto basket
can_add_course_to_cart
=
_is_shopping_cart_enabled
and
registration_price
and
not
ecommerce_checkout_link
...
...
@@ -889,6 +900,8 @@ def _progress(request, course_key, student_id):
'passed'
:
is_course_passed
(
course
,
grade_summary
),
'credit_course_requirements'
:
_credit_course_requirements
(
course_key
,
student
),
'certificate_data'
:
_get_cert_data
(
student
,
course
,
course_key
,
is_active
,
enrollment_mode
),
'upgrade_link'
:
check_and_get_upgrade_link
(
request
,
student
,
course
.
id
),
'upgrade_price'
:
get_cosmetic_verified_display_price
(
course
),
}
with
outer_atomic
():
...
...
lms/djangoapps/discussion/views.py
View file @
14fed7a7
...
...
@@ -39,6 +39,8 @@ from django_comment_client.utils import (
strip_none
)
from
django_comment_common.utils
import
ThreadContext
,
get_course_discussion_settings
,
set_course_discussion_settings
from
lms.djangoapps.courseware.views.views
import
check_and_get_upgrade_link
,
get_cosmetic_verified_display_price
from
opaque_keys.edx.keys
import
CourseKey
from
openedx.core.djangoapps.plugin_api.views
import
EdxFragmentView
from
rest_framework
import
status
...
...
@@ -440,7 +442,9 @@ def _create_discussion_board_context(request, course_key, discussion_id=None, th
'sort_preference'
:
cc_user
.
default_sort_key
,
'category_map'
:
course_settings
[
"category_map"
],
'course_settings'
:
course_settings
,
'is_commentable_divided'
:
is_commentable_divided
(
course_key
,
discussion_id
,
course_discussion_settings
)
'is_commentable_divided'
:
is_commentable_divided
(
course_key
,
discussion_id
,
course_discussion_settings
),
'upgrade_link'
:
check_and_get_upgrade_link
(
request
,
user
,
course
.
id
),
'upgrade_price'
:
get_cosmetic_verified_display_price
(
course
),
})
return
context
...
...
lms/templates/courseware/info.html
View file @
14fed7a7
...
...
@@ -82,26 +82,6 @@ from openedx.core.djangolib.markup import HTML, Text
</div>
% endif
% if upgrade_link:
<div
class=
"upgrade-banner"
>
<div
class=
"notification-color-border"
></div>
<div
class=
"notification-content"
>
<div
class=
"upgrade-icon"
>
<img
src=
"${STATIC_URL}images/edx-verified-mini-cert.png"
>
</div>
<div
class=
"upgrade-msg"
>
<h3
class=
"msg-title"
>
${_("Give yourself an additional incentive to complete")}
</h3>
<p
class=
"view-verified-info"
>
${_("Earn a verified certificate.")}
<a
href=
"https://www.edx.org/verified-certificate"
target=
"_blank"
>
${_("Learn More")}
</a>
</p>
</div>
<div
class=
"upgrade-banner-button"
>
<a
href=
"${upgrade_link}"
class=
"btn-upgrade"
>
${_("Upgrade Now")}
</a>
</div>
</div>
</div>
% endif
<h3
class=
"hd hd-3"
>
${_("Course Updates and News")}
</h3>
${HTML(get_course_info_section(request, masquerade_user, course, 'updates'))}
...
...
lms/templates/courseware/upgrade.html
0 → 100644
View file @
14fed7a7
<
%
page
expression_filter=
"h"
/>
% if upgrade_link:
<script
type=
"text/plain"
id=
"upgrade_user"
data-link=
"${upgrade_link}"
data-price=
"${upgrade_price}"
>
</script>
% endif
lms/templates/main.html
View file @
14fed7a7
...
...
@@ -98,6 +98,7 @@ from pipeline_mako import render_require_js_path_overrides
<
%
block
name=
"headextra"
/>
<
%
block
name=
"head_extra"
/>
<
%
include
file=
"/courseware/upgrade.html"
/>
<
%
static:optional_include_mako
file=
"head-extra.html"
is_theming_enabled=
"True"
/>
<
%
include
file=
"widgets/optimizely.html"
/>
...
...
openedx/features/course_experience/tests/views/test_course_home.py
View file @
14fed7a7
...
...
@@ -89,7 +89,7 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
course_home_url
(
self
.
course
)
# Fetch the view and verify the query counts
with
self
.
assertNumQueries
(
4
2
):
with
self
.
assertNumQueries
(
4
5
):
with
check_mongo_calls
(
5
):
url
=
course_home_url
(
self
.
course
)
self
.
client
.
get
(
url
)
openedx/features/course_experience/tests/views/test_course_updates.py
View file @
14fed7a7
...
...
@@ -124,7 +124,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
course_updates_url
(
self
.
course
)
# Fetch the view and verify that the query counts haven't changed
with
self
.
assertNumQueries
(
3
2
):
with
self
.
assertNumQueries
(
3
4
):
with
check_mongo_calls
(
4
):
url
=
course_updates_url
(
self
.
course
)
self
.
client
.
get
(
url
)
openedx/features/course_experience/views/course_outline.py
View file @
14fed7a7
...
...
@@ -8,6 +8,7 @@ from opaque_keys.edx.keys import CourseKey
from
web_fragments.fragment
import
Fragment
from
courseware.courses
import
get_course_overview_with_access
from
lms.djangoapps.courseware.views.views
import
check_and_get_upgrade_link
,
get_cosmetic_verified_display_price
from
openedx.core.djangoapps.plugin_api.views
import
EdxFragmentView
from
..utils
import
get_course_outline_block_tree
...
...
@@ -30,7 +31,9 @@ class CourseOutlineFragmentView(EdxFragmentView):
context
=
{
'csrf'
:
csrf
(
request
)[
'csrf_token'
],
'course'
:
course_overview
,
'blocks'
:
course_block_tree
'blocks'
:
course_block_tree
,
'upgrade_link'
:
check_and_get_upgrade_link
(
request
,
request
.
user
,
course_key
),
'upgrade_price'
:
get_cosmetic_verified_display_price
(
course_overview
),
}
html
=
render_to_string
(
'course_experience/course-outline-fragment.html'
,
context
)
return
Fragment
(
html
)
pavelib/utils/test/suites/suite.py
View file @
14fed7a7
...
...
@@ -88,12 +88,10 @@ class TestSuite(object):
try
:
process
=
subprocess
.
Popen
(
cmd
,
**
kwargs
)
process
.
communicate
(
)
return
(
process
.
wait
()
==
0
)
except
KeyboardInterrupt
:
kill_process
(
process
)
sys
.
exit
(
1
)
else
:
return
process
.
returncode
==
0
def
run_suite_tests
(
self
):
"""
...
...
scripts/generic-ci-tests.sh
View file @
14fed7a7
...
...
@@ -99,7 +99,7 @@ case "$TEST_SUITE" in
;;
"lms-unit"
)
PAVER_ARGS
=
"--with-flaky --processes=-1 --cov-args='-p' -
v -
-with-xunitmp"
PAVER_ARGS
=
"--with-flaky --processes=-1 --cov-args='-p' --with-xunitmp"
case
"
$SHARD
"
in
"all"
)
paver test_system
-s
lms
$PAVER_ARGS
...
...
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