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
8f5ba011
Commit
8f5ba011
authored
Jan 11, 2017
by
asadiqbal
Committed by
Ayesha Baig
Jan 25, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WL-606
parent
c7ff473e
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
271 additions
and
11 deletions
+271
-11
common/djangoapps/course_modes/views.py
+2
-0
common/test/acceptance/pages/lms/create_mode.py
+6
-1
common/test/acceptance/pages/lms/instructor_dashboard.py
+25
-0
common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py
+109
-0
common/test/db_fixtures/course_access_role.json
+74
-0
lms/djangoapps/instructor/tests/test_ecommerce.py
+40
-7
lms/djangoapps/instructor/views/instructor_dashboard.py
+11
-1
lms/templates/instructor/instructor_dashboard_2/e-commerce.html
+4
-2
No files found.
common/djangoapps/course_modes/views.py
View file @
8f5ba011
...
...
@@ -272,6 +272,7 @@ def create_mode(request, course_id):
`min_price` (int): The minimum price a user must pay to enroll in the new course mode
`suggested_prices` (str): Comma-separated prices to suggest to the user.
`currency` (str): The currency in which to list prices.
`sku` (str): The product SKU value.
By default, this endpoint will create an 'honor' mode for the given course with display name
'Honor Code', a minimum price of 0, no suggested prices, and using USD as the currency.
...
...
@@ -289,6 +290,7 @@ def create_mode(request, course_id):
'min_price'
:
0
,
'suggested_prices'
:
u''
,
'currency'
:
u'usd'
,
'sku'
:
None
,
}
# Try pulling querystring parameters out of the request
...
...
common/test/acceptance/pages/lms/create_mode.py
View file @
8f5ba011
...
...
@@ -14,7 +14,8 @@ class ModeCreationPage(PageObject):
created for an existing course.
"""
def
__init__
(
self
,
browser
,
course_id
,
mode_slug
=
None
,
mode_display_name
=
None
,
min_price
=
None
,
suggested_prices
=
None
,
currency
=
None
):
def
__init__
(
self
,
browser
,
course_id
,
mode_slug
=
None
,
mode_display_name
=
None
,
min_price
=
None
,
suggested_prices
=
None
,
currency
=
None
,
sku
=
None
):
"""The mode creation page is an endpoint for HTTP GET requests.
By default, it will create an 'honor' mode for the given course with display name
...
...
@@ -30,6 +31,7 @@ class ModeCreationPage(PageObject):
min_price (int): The minimum price a user must pay to enroll in the new course mode
suggested_prices (str): Comma-separated prices to suggest to the user.
currency (str): The currency in which to list prices.
sku (str): The product SKU value.
"""
super
(
ModeCreationPage
,
self
)
.
__init__
(
browser
)
...
...
@@ -51,6 +53,9 @@ class ModeCreationPage(PageObject):
if
currency
is
not
None
:
self
.
_parameters
[
'currency'
]
=
currency
if
sku
is
not
None
:
self
.
_parameters
[
'sku'
]
=
sku
@property
def
url
(
self
):
"""Construct the mode creation URL."""
...
...
common/test/acceptance/pages/lms/instructor_dashboard.py
View file @
8f5ba011
...
...
@@ -92,6 +92,15 @@ class InstructorDashboardPage(CoursePage):
email_section
.
wait_for_page
()
return
email_section
def
select_ecommerce_tab
(
self
):
"""
Selects the E-commerce tab and returns an EcommercePage.
"""
self
.
q
(
css
=
'[data-section="e-commerce"]'
)
.
first
.
click
()
ecommerce_section
=
EcommercePage
(
self
.
browser
)
ecommerce_section
.
wait_for_page
()
return
ecommerce_section
@staticmethod
def
get_asset_path
(
file_name
):
"""
...
...
@@ -1416,3 +1425,19 @@ class CertificatesPage(PageObject):
Returns the message (error/success) in "Certificate Invalidation" section.
"""
return
self
.
get_selector
(
'.certificate-invalidation-container div.message'
)
class
EcommercePage
(
PageObject
):
"""
E-commerce section of the Instructor dashboard.
"""
url
=
None
def
is_browser_on_page
(
self
):
return
self
.
q
(
css
=
'[data-section="e-commerce"].active-section'
)
.
present
def
get_sections_header_values
(
self
):
"""
Returns a list of the headings text under div.
"""
return
self
.
q
(
css
=
"div.wrap h3"
)
.
text
common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py
View file @
8f5ba011
...
...
@@ -1202,3 +1202,112 @@ class CertificateInvalidationTest(BaseInstructorDashboardTest):
'.certificates-wrapper'
])
self
.
certificates_section
.
a11y_audit
.
check_for_accessibility_errors
()
@attr
(
shard
=
10
)
class
EcommerceTest
(
BaseInstructorDashboardTest
):
"""
Bok Choy tests for the "E-Commerce" tab.
"""
def
setUp
(
self
):
super
(
EcommerceTest
,
self
)
.
setUp
()
def
setup_course
(
self
,
course_number
):
"""
Sets up the course
"""
self
.
course_info
[
'number'
]
=
course_number
course_fixture
=
CourseFixture
(
self
.
course_info
[
"org"
],
self
.
course_info
[
"number"
],
self
.
course_info
[
"run"
],
self
.
course_info
[
"display_name"
]
)
course_fixture
.
install
()
def
log_in_as_unique_user
(
self
):
"""
Log in as a valid lms user.
"""
AutoAuthPage
(
self
.
browser
,
username
=
"test_instructor"
,
email
=
"test_instructor@example.com"
,
password
=
"password"
,
course_id
=
self
.
course_id
)
.
visit
()
def
visit_ecommerce_section
(
self
):
"""
Log in to visit Instructor dashboard and click E-commerce tab
"""
self
.
log_in_as_unique_user
()
instructor_dashboard_page
=
self
.
visit_instructor_dashboard
()
return
instructor_dashboard_page
.
select_ecommerce_tab
()
def
add_course_mode
(
self
,
sku_value
=
None
):
"""
Add an honor mode to the course
"""
ModeCreationPage
(
browser
=
self
.
browser
,
course_id
=
self
.
course_id
,
mode_slug
=
u'honor'
,
min_price
=
10
,
sku
=
sku_value
)
.
visit
()
def
test_enrollment_codes_section_visible_for_non_ecommerce_course
(
self
):
"""
Test Enrollment Codes UI, under E-commerce Tab, should be visible in the Instructor Dashboard with non
e-commerce course
"""
# Setup course
non_ecommerce_course_number
=
"34039497242734583224814321005482849780"
self
.
setup_course
(
non_ecommerce_course_number
)
# Add an honor mode to the course
self
.
add_course_mode
()
# Log in and visit E-commerce section under Instructor dashboard
self
.
assertIn
(
u'Enrollment Codes'
,
self
.
visit_ecommerce_section
()
.
get_sections_header_values
())
def
test_coupon_codes_section_visible_for_non_ecommerce_course
(
self
):
"""
Test Coupon Codes UI, under E-commerce Tab, should be visible in the Instructor Dashboard with non
e-commerce course
"""
# Setup course
non_ecommerce_course_number
=
"34039497242734583224814321005482849781"
self
.
setup_course
(
non_ecommerce_course_number
)
# Add an honor mode to the course
self
.
add_course_mode
()
# Log in and visit E-commerce section under Instructor dashboard
self
.
assertIn
(
u'Coupon Code List'
,
self
.
visit_ecommerce_section
()
.
get_sections_header_values
())
def
test_enrollment_codes_section_not_visible_for_ecommerce_course
(
self
):
"""
Test Enrollment Codes UI, under E-commerce Tab, should not be visible in the Instructor Dashboard with
e-commerce course
"""
# Setup course
ecommerce_course_number
=
"34039497242734583224814321005482849782"
self
.
setup_course
(
ecommerce_course_number
)
# Add an honor mode to the course with sku value
self
.
add_course_mode
(
'test_sku'
)
# Log in and visit E-commerce section under Instructor dashboard
self
.
assertNotIn
(
u'Enrollment Codes'
,
self
.
visit_ecommerce_section
()
.
get_sections_header_values
())
def
test_coupon_codes_section_not_visible_for_ecommerce_course
(
self
):
"""
Test Coupon Codes UI, under E-commerce Tab, should not be visible in the Instructor Dashboard with
e-commerce course
"""
# Setup course
ecommerce_course_number
=
"34039497242734583224814321005482849783"
self
.
setup_course
(
ecommerce_course_number
)
# Add an honor mode to the course with sku value
self
.
add_course_mode
(
'test_sku'
)
# Log in and visit E-commerce section under Instructor dashboard
self
.
assertNotIn
(
u'Coupon Code List'
,
self
.
visit_ecommerce_section
()
.
get_sections_header_values
())
common/test/db_fixtures/course_access_role.json
0 → 100644
View file @
8f5ba011
[
{
"pk"
:
1
,
"model"
:
"auth.user"
,
"fields"
:
{
"username"
:
"test_instructor"
,
"email"
:
"test_instructor@example.com"
,
"password"
:
"password"
,
"is_staff"
:
true
,
"is_active"
:
true
}
},
{
"pk"
:
1
,
"model"
:
"student.userprofile"
,
"fields"
:
{
"user"
:
1
,
"name"
:
"test instructor"
,
"courseware"
:
"course.xml"
}
},
{
"pk"
:
1
,
"model"
:
"student.registration"
,
"fields"
:
{
"user"
:
1
,
"activation_key"
:
"52bfac10384d49219385dcd4cc17177q"
}
},
{
"pk"
:
1
,
"model"
:
"student.courseaccessrole"
,
"fields"
:
{
"id"
:
"1"
,
"org"
:
"test_org"
,
"course_id"
:
"course-v1:test_org+34039497242734583224814321005482849780+test_run"
,
"role"
:
"finance_admin"
,
"user_id"
:
"1"
}
},
{
"pk"
:
2
,
"model"
:
"student.courseaccessrole"
,
"fields"
:
{
"id"
:
"2"
,
"org"
:
"test_org"
,
"course_id"
:
"course-v1:test_org+34039497242734583224814321005482849781+test_run"
,
"role"
:
"finance_admin"
,
"user_id"
:
"1"
}
},
{
"pk"
:
3
,
"model"
:
"student.courseaccessrole"
,
"fields"
:
{
"id"
:
"3"
,
"org"
:
"test_org"
,
"course_id"
:
"course-v1:test_org+34039497242734583224814321005482849782+test_run"
,
"role"
:
"finance_admin"
,
"user_id"
:
"1"
}
},
{
"pk"
:
4
,
"model"
:
"student.courseaccessrole"
,
"fields"
:
{
"id"
:
"4"
,
"org"
:
"test_org"
,
"course_id"
:
"course-v1:test_org+34039497242734583224814321005482849783+test_run"
,
"role"
:
"finance_admin"
,
"user_id"
:
"1"
}
}
]
lms/djangoapps/instructor/tests/test_ecommerce.py
View file @
8f5ba011
...
...
@@ -30,7 +30,7 @@ class TestECommerceDashboardViews(SiteMixin, SharedModuleStoreTestCase):
# URL for instructor dash
cls
.
url
=
reverse
(
'instructor_dashboard'
,
kwargs
=
{
'course_id'
:
cls
.
course
.
id
.
to_deprecated_string
()})
cls
.
e
_
commerce_link
=
'<button type="button" class="btn-link" data-section="e-commerce">E-Commerce</button>'
cls
.
ecommerce_link
=
'<button type="button" class="btn-link" data-section="e-commerce">E-Commerce</button>'
def
setUp
(
self
):
super
(
TestECommerceDashboardViews
,
self
)
.
setUp
()
...
...
@@ -50,7 +50,7 @@ class TestECommerceDashboardViews(SiteMixin, SharedModuleStoreTestCase):
Test Pass E-commerce Tab is in the Instructor Dashboard
"""
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
e
_
commerce_link
,
response
.
content
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
# Coupons should show up for White Label sites with priced honor modes.
self
.
assertIn
(
'Coupon Code List'
,
response
.
content
)
...
...
@@ -61,7 +61,7 @@ class TestECommerceDashboardViews(SiteMixin, SharedModuleStoreTestCase):
self
.
use_site
(
site
=
self
.
site_other
)
self
.
client
.
login
(
username
=
self
.
instructor
.
username
,
password
=
"test"
)
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
e
_
commerce_link
,
response
.
content
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
self
.
assertIn
(
'Create Enrollment Report'
,
response
.
content
)
def
test_reports_section_not_under_e_commerce_tab
(
self
):
...
...
@@ -70,12 +70,12 @@ class TestECommerceDashboardViews(SiteMixin, SharedModuleStoreTestCase):
value
"""
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
e
_
commerce_link
,
response
.
content
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
self
.
assertNotIn
(
'Create Enrollment Report'
,
response
.
content
)
def
test_user_has_finance_admin_rights_in_e_commerce_tab
(
self
):
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
e
_
commerce_link
,
response
.
content
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
# Order/Invoice sales csv button text should render in e-commerce page
self
.
assertIn
(
'Total Credit Card Purchases'
,
response
.
content
)
...
...
@@ -96,7 +96,7 @@ class TestECommerceDashboardViews(SiteMixin, SharedModuleStoreTestCase):
the instructor dashboard
"""
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
e
_
commerce_link
,
response
.
content
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
# Total amount html should render in e-commerce page, total amount will be 0
course_honor_mode
=
CourseMode
.
mode_for_course
(
self
.
course
.
id
,
'honor'
)
...
...
@@ -351,6 +351,39 @@ class TestECommerceDashboardViews(SiteMixin, SharedModuleStoreTestCase):
# Get the response value, ensure the Coupon section is not included.
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
e
_
commerce_link
,
response
.
content
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
# Coupons should show up for White Label sites with priced honor modes.
self
.
assertNotIn
(
'Coupons List'
,
response
.
content
)
def
test_coupon_code_section_not_under_e_commerce_tab
(
self
):
"""
Test Coupon Creation UI, under E-commerce Tab, should not be available in the Instructor Dashboard with
e-commerce course
"""
# Setup e-commerce course
CourseMode
.
objects
.
filter
(
course_id
=
self
.
course
.
id
)
.
update
(
sku
=
'test_sku'
)
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
self
.
assertNotIn
(
'Coupon Code List'
,
response
.
content
)
def
test_enrollment_codes_section_not_under_e_commerce_tab
(
self
):
"""
Test Enrollment Codes UI, under E-commerce Tab, should not be available in the Instructor Dashboard with
e-commerce course
"""
# Setup e-commerce course
CourseMode
.
objects
.
filter
(
course_id
=
self
.
course
.
id
)
.
update
(
sku
=
'test_sku'
)
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
self
.
assertNotIn
(
'<h3 class="hd hd-3">Enrollment Codes</h3>'
,
response
.
content
)
def
test_enrollment_codes_section_visible_for_non_ecommerce_course
(
self
):
"""
Test Enrollment Codes UI, under E-commerce Tab, should be available in the Instructor Dashboard with non
e-commerce course
"""
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertIn
(
self
.
ecommerce_link
,
response
.
content
)
self
.
assertIn
(
'<h3 class="hd hd-3">Enrollment Codes</h3>'
,
response
.
content
)
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
8f5ba011
...
...
@@ -285,7 +285,8 @@ def _section_e_commerce(course, access, paid_mode, coupons_enabled, reports_enab
'coupons_enabled'
:
coupons_enabled
,
'reports_enabled'
:
reports_enabled
,
'course_price'
:
course_price
,
'total_amount'
:
total_amount
'total_amount'
:
total_amount
,
'is_ecommerce_course'
:
is_ecommerce_course
(
course_key
)
}
return
section_data
...
...
@@ -695,3 +696,12 @@ def _section_metrics(course, access):
'post_metrics_data_csv_url'
:
reverse
(
'post_metrics_data_csv'
),
}
return
section_data
def
is_ecommerce_course
(
course_key
):
"""
Checks if the given course is an e-commerce course or not, by checking its SKU value from
CourseMode records for the course
"""
sku_count
=
len
([
mode
.
sku
for
mode
in
CourseMode
.
modes_for_course
(
course_key
)
if
mode
.
sku
])
return
sku_count
>
0
lms/templates/instructor/instructor_dashboard_2/e-commerce.html
View file @
8f5ba011
...
...
@@ -14,7 +14,8 @@ import pytz
<div
class=
"ecommerce-wrapper"
>
<div
class=
"error-msgs"
id=
"error-msg"
></div>
<div
id =
"accordion"
>
<div
class=
"wrap"
>
%if not section_data['is_ecommerce_course']:
<div
class=
"wrap"
>
<h3
class=
"hd hd-3"
>
${_('Enrollment Codes')}
</h3>
<div>
%if section_data['sales_admin']:
...
...
@@ -53,6 +54,7 @@ import pytz
<a
id=
"registration_code_generation_link-trigger"
href=
"#registration_code_generation_modal"
rel=
"leanModal"
></a>
</div>
</div>
%endif
<!-- end wrap -->
%if section_data['coupons_enabled']:
<div
class=
"wrap"
>
...
...
@@ -145,7 +147,7 @@ import pytz
</div>
</div>
<!-- end wrap -->
%endif
%if section_data['coupons_enabled']:
%if section_data['coupons_enabled']
and not section_data['is_ecommerce_course']
:
<div
class=
"wrap"
>
<h3
class=
"hd hd-3"
>
${_("Coupon Code List")}
</h3>
<div>
...
...
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