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
c5746e6d
Commit
c5746e6d
authored
Jan 09, 2015
by
chrisndodge
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6071 from edx/muhhshoaib/WL-135-add-expiration-dates-to-Coupon-codes
Add expiration dates to Coupon Codes
parents
17c2af23
10f8d8c0
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
478 additions
and
27 deletions
+478
-27
lms/djangoapps/instructor/tests/test_api.py
+22
-1
lms/djangoapps/instructor/tests/test_ecommerce.py
+19
-2
lms/djangoapps/instructor/views/api.py
+10
-2
lms/djangoapps/instructor/views/coupons.py
+19
-3
lms/djangoapps/instructor_analytics/basic.py
+2
-1
lms/djangoapps/instructor_analytics/tests/test_basic.py
+22
-2
lms/djangoapps/shoppingcart/migrations/0023_auto__add_field_coupon_expiration_date.py
+220
-0
lms/djangoapps/shoppingcart/models.py
+9
-0
lms/djangoapps/shoppingcart/tests/test_views.py
+1
-1
lms/djangoapps/shoppingcart/views.py
+9
-4
lms/static/js/instructor_dashboard/ecommerce.js
+36
-0
lms/static/js/spec/instructor_dashboard/ecommerce_spec.js
+36
-0
lms/static/js/spec/main.js
+5
-0
lms/static/sass/course/instructor/_instructor_2.scss
+31
-5
lms/templates/instructor/instructor_dashboard_2/add_coupon_modal.html
+6
-0
lms/templates/instructor/instructor_dashboard_2/e-commerce.html
+24
-6
lms/templates/instructor/instructor_dashboard_2/edit_coupon_modal.html
+6
-0
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
+1
-0
No files found.
lms/djangoapps/instructor/tests/test_api.py
View file @
c5746e6d
...
@@ -4,6 +4,8 @@ Unit tests for instructor.api methods.
...
@@ -4,6 +4,8 @@ Unit tests for instructor.api methods.
"""
"""
import
datetime
import
datetime
import
ddt
import
ddt
import
random
import
pytz
import
io
import
io
import
json
import
json
import
os
import
os
...
@@ -61,7 +63,7 @@ from .test_tools import msk_from_problem_urlname
...
@@ -61,7 +63,7 @@ from .test_tools import msk_from_problem_urlname
from
..views.tools
import
get_extended_due
from
..views.tools
import
get_extended_due
EXPECTED_CSV_HEADER
=
'"code","course_id","company_name","created_by","redeemed_by","invoice_id","purchaser","customer_reference_number","internal_reference"'
EXPECTED_CSV_HEADER
=
'"code","course_id","company_name","created_by","redeemed_by","invoice_id","purchaser","customer_reference_number","internal_reference"'
EXPECTED_COUPON_CSV_HEADER
=
'"course_id","percentage_discount","code_redeemed_count","description"'
EXPECTED_COUPON_CSV_HEADER
=
'"co
de","co
urse_id","percentage_discount","code_redeemed_count","description"'
# ddt data for test cases involving reports
# ddt data for test cases involving reports
REPORTS_DATA
=
(
REPORTS_DATA
=
(
...
@@ -3331,8 +3333,27 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
...
@@ -3331,8 +3333,27 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
)
)
coupon
.
save
()
coupon
.
save
()
#now create coupons with the expiration dates
for
i
in
range
(
5
):
coupon
=
Coupon
(
code
=
'coupon{0}'
.
format
(
i
),
description
=
'test_description'
,
course_id
=
self
.
course
.
id
,
percentage_discount
=
'{0}'
.
format
(
i
),
created_by
=
self
.
instructor
,
is_active
=
True
,
expiration_date
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
2
)
)
coupon
.
save
()
response
=
self
.
client
.
get
(
get_coupon_code_url
)
response
=
self
.
client
.
get
(
get_coupon_code_url
)
self
.
assertEqual
(
response
.
status_code
,
200
,
response
.
content
)
self
.
assertEqual
(
response
.
status_code
,
200
,
response
.
content
)
# filter all the coupons
for
coupon
in
Coupon
.
objects
.
all
():
self
.
assertIn
(
'"{code}","{course_id}","{discount}","0","{description}","{expiration_date}"'
.
format
(
code
=
coupon
.
code
,
course_id
=
coupon
.
course_id
,
discount
=
coupon
.
percentage_discount
,
description
=
coupon
.
description
,
expiration_date
=
coupon
.
display_expiry_date
),
response
.
content
)
self
.
assertEqual
(
response
[
'Content-Type'
],
'text/csv'
)
self
.
assertEqual
(
response
[
'Content-Type'
],
'text/csv'
)
body
=
response
.
content
.
replace
(
'
\r
'
,
''
)
body
=
response
.
content
.
replace
(
'
\r
'
,
''
)
self
.
assertTrue
(
body
.
startswith
(
EXPECTED_COUPON_CSV_HEADER
))
self
.
assertTrue
(
body
.
startswith
(
EXPECTED_COUPON_CSV_HEADER
))
...
...
lms/djangoapps/instructor/tests/test_ecommerce.py
View file @
c5746e6d
...
@@ -3,6 +3,8 @@ Unit tests for Ecommerce feature flag in new instructor dashboard.
...
@@ -3,6 +3,8 @@ Unit tests for Ecommerce feature flag in new instructor dashboard.
"""
"""
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
import
datetime
import
pytz
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
mock
import
patch
from
mock
import
patch
...
@@ -144,13 +146,26 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
...
@@ -144,13 +146,26 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
"""
"""
# URL for add_coupon
# URL for add_coupon
add_coupon_url
=
reverse
(
'add_coupon'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
()})
add_coupon_url
=
reverse
(
'add_coupon'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
()})
expiration_date
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
2
)
data
=
{
data
=
{
'code'
:
'A2314'
,
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
(),
'code'
:
'A2314'
,
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
(),
'description'
:
'ADSADASDSAD'
,
'created_by'
:
self
.
instructor
,
'discount'
:
5
'description'
:
'ADSADASDSAD'
,
'created_by'
:
self
.
instructor
,
'discount'
:
5
,
'expiration_date'
:
'{month}/{day}/{year}'
.
format
(
month
=
expiration_date
.
month
,
day
=
expiration_date
.
day
,
year
=
expiration_date
.
year
)
}
}
response
=
self
.
client
.
post
(
add_coupon_url
,
data
)
response
=
self
.
client
.
post
(
add_coupon_url
,
data
)
self
.
assertTrue
(
"coupon with the coupon code ({code}) added successfully"
.
format
(
code
=
data
[
'code'
])
in
response
.
content
)
self
.
assertTrue
(
"coupon with the coupon code ({code}) added successfully"
.
format
(
code
=
data
[
'code'
])
in
response
.
content
)
#now add the coupon with the wrong value in the expiration_date
# server will through the ValueError Exception in the expiration_date field
data
=
{
'code'
:
'213454'
,
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
(),
'description'
:
'ADSADASDSAD'
,
'created_by'
:
self
.
instructor
,
'discount'
:
5
,
'expiration_date'
:
expiration_date
.
strftime
(
'"
%
d/
%
m/
%
Y'
)
}
response
=
self
.
client
.
post
(
add_coupon_url
,
data
)
self
.
assertTrue
(
"Please enter the date in this format i-e month/day/year"
in
response
.
content
)
data
=
{
data
=
{
'code'
:
'A2314'
,
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
(),
'code'
:
'A2314'
,
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
(),
'description'
:
'asdsasda'
,
'created_by'
:
self
.
instructor
,
'discount'
:
99
'description'
:
'asdsasda'
,
'created_by'
:
self
.
instructor
,
'discount'
:
99
...
@@ -221,13 +236,15 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
...
@@ -221,13 +236,15 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
"""
"""
coupon
=
Coupon
(
coupon
=
Coupon
(
code
=
'AS452'
,
description
=
'asdsadsa'
,
course_id
=
self
.
course
.
id
.
to_deprecated_string
(),
code
=
'AS452'
,
description
=
'asdsadsa'
,
course_id
=
self
.
course
.
id
.
to_deprecated_string
(),
percentage_discount
=
10
,
created_by
=
self
.
instructor
percentage_discount
=
10
,
created_by
=
self
.
instructor
,
expiration_date
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
2
)
)
)
coupon
.
save
()
coupon
.
save
()
# URL for edit_coupon_info
# URL for edit_coupon_info
edit_url
=
reverse
(
'get_coupon_info'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
()})
edit_url
=
reverse
(
'get_coupon_info'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
()})
response
=
self
.
client
.
post
(
edit_url
,
{
'id'
:
coupon
.
id
})
response
=
self
.
client
.
post
(
edit_url
,
{
'id'
:
coupon
.
id
})
self
.
assertTrue
(
'coupon with the coupon id ({coupon_id}) updated successfully'
.
format
(
coupon_id
=
coupon
.
id
)
in
response
.
content
)
self
.
assertTrue
(
'coupon with the coupon id ({coupon_id}) updated successfully'
.
format
(
coupon_id
=
coupon
.
id
)
in
response
.
content
)
self
.
assertIn
(
coupon
.
display_expiry_date
,
response
.
content
)
response
=
self
.
client
.
post
(
edit_url
,
{
'id'
:
444444
})
response
=
self
.
client
.
post
(
edit_url
,
{
'id'
:
444444
})
self
.
assertTrue
(
'coupon with the coupon id ({coupon_id}) DoesNotExist'
.
format
(
coupon_id
=
444444
)
in
response
.
content
)
self
.
assertTrue
(
'coupon with the coupon id ({coupon_id}) DoesNotExist'
.
format
(
coupon_id
=
444444
)
in
response
.
content
)
...
...
lms/djangoapps/instructor/views/api.py
View file @
c5746e6d
...
@@ -18,6 +18,7 @@ from django.views.decorators.cache import cache_control
...
@@ -18,6 +18,7 @@ from django.views.decorators.cache import cache_control
from
django.core.exceptions
import
ValidationError
,
PermissionDenied
from
django.core.exceptions
import
ValidationError
,
PermissionDenied
from
django.core.mail.message
import
EmailMessage
from
django.core.mail.message
import
EmailMessage
from
django.db
import
IntegrityError
from
django.db
import
IntegrityError
from
django.db.models
import
Q
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.core.validators
import
validate_email
from
django.core.validators
import
validate_email
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
...
@@ -28,6 +29,8 @@ import random
...
@@ -28,6 +29,8 @@ import random
import
unicodecsv
import
unicodecsv
import
urllib
import
urllib
from
util.file
import
store_uploaded_file
,
course_and_time_based_filename_generator
,
FileValidationException
,
UniversalNewlineIterator
from
util.file
import
store_uploaded_file
,
course_and_time_based_filename_generator
,
FileValidationException
,
UniversalNewlineIterator
import
datetime
import
pytz
from
util.json_request
import
JsonResponse
from
util.json_request
import
JsonResponse
from
instructor.views.instructor_task_helpers
import
extract_email_features
,
extract_task_features
from
instructor.views.instructor_task_helpers
import
extract_email_features
,
extract_task_features
...
@@ -1007,9 +1010,14 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument
...
@@ -1007,9 +1010,14 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument
Respond with csv which contains a summary of all Active Coupons.
Respond with csv which contains a summary of all Active Coupons.
"""
"""
course_id
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
course_id
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
active_coupons
=
Coupon
.
objects
.
filter
(
course_id
=
course_id
,
is_active
=
True
)
active_coupons
=
Coupon
.
objects
.
filter
(
Q
(
course_id
=
course_id
),
Q
(
is_active
=
True
),
Q
(
expiration_date__gt
=
datetime
.
datetime
.
now
(
pytz
.
UTC
))
|
Q
(
expiration_date__isnull
=
True
)
)
query_features
=
[
query_features
=
[
'co
urse_id'
,
'percentage_discount'
,
'code_redeemed_count'
,
'description
'
'co
de'
,
'course_id'
,
'percentage_discount'
,
'code_redeemed_count'
,
'description'
,
'expiration_date
'
]
]
coupons_list
=
instructor_analytics
.
basic
.
coupon_codes_features
(
query_features
,
active_coupons
)
coupons_list
=
instructor_analytics
.
basic
.
coupon_codes_features
(
query_features
,
active_coupons
)
header
,
data_rows
=
instructor_analytics
.
csvs
.
format_dictlist
(
coupons_list
,
query_features
)
header
,
data_rows
=
instructor_analytics
.
csvs
.
format_dictlist
(
coupons_list
,
query_features
)
...
...
lms/djangoapps/instructor/views/coupons.py
View file @
c5746e6d
...
@@ -10,7 +10,8 @@ from util.json_request import JsonResponse
...
@@ -10,7 +10,8 @@ from util.json_request import JsonResponse
from
django.http
import
HttpResponse
,
HttpResponseNotFound
from
django.http
import
HttpResponse
,
HttpResponseNotFound
from
shoppingcart.models
import
Coupon
,
CourseRegistrationCode
from
shoppingcart.models
import
Coupon
,
CourseRegistrationCode
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
import
datetime
import
pytz
import
logging
import
logging
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -79,9 +80,22 @@ def add_coupon(request, course_id): # pylint: disable=unused-argument
...
@@ -79,9 +80,22 @@ def add_coupon(request, course_id): # pylint: disable=unused-argument
return
JsonResponse
({
return
JsonResponse
({
'message'
:
_
(
"Please Enter the Coupon Discount Value Less than or Equal to 100"
)
'message'
:
_
(
"Please Enter the Coupon Discount Value Less than or Equal to 100"
)
},
status
=
400
)
# status code 400: Bad Request
},
status
=
400
)
# status code 400: Bad Request
expiration_date
=
None
if
request
.
POST
.
get
(
'expiration_date'
):
expiration_date
=
request
.
POST
.
get
(
'expiration_date'
)
try
:
expiration_date
=
datetime
.
datetime
.
strptime
(
expiration_date
,
"
%
m/
%
d/
%
Y"
)
.
replace
(
tzinfo
=
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
1
)
except
ValueError
:
return
JsonResponse
({
'message'
:
_
(
"Please enter the date in this format i-e month/day/year"
)
},
status
=
400
)
# status code 400: Bad Request
coupon
=
Coupon
(
coupon
=
Coupon
(
code
=
code
,
description
=
description
,
course_id
=
course_id
,
code
=
code
,
description
=
description
,
percentage_discount
=
discount
,
created_by_id
=
request
.
user
.
id
course_id
=
course_id
,
percentage_discount
=
discount
,
created_by_id
=
request
.
user
.
id
,
expiration_date
=
expiration_date
)
)
coupon
.
save
()
coupon
.
save
()
return
JsonResponse
(
return
JsonResponse
(
...
@@ -143,10 +157,12 @@ def get_coupon_info(request, course_id): # pylint: disable=unused-argument
...
@@ -143,10 +157,12 @@ def get_coupon_info(request, course_id): # pylint: disable=unused-argument
'message'
:
_
(
"coupon with the coupon id ({coupon_id}) is already inactive"
)
.
format
(
coupon_id
=
coupon_id
)
'message'
:
_
(
"coupon with the coupon id ({coupon_id}) is already inactive"
)
.
format
(
coupon_id
=
coupon_id
)
},
status
=
400
)
# status code 400: Bad Request
},
status
=
400
)
# status code 400: Bad Request
expiry_date
=
coupon
.
display_expiry_date
return
JsonResponse
({
return
JsonResponse
({
'coupon_code'
:
coupon
.
code
,
'coupon_code'
:
coupon
.
code
,
'coupon_description'
:
coupon
.
description
,
'coupon_description'
:
coupon
.
description
,
'coupon_course_id'
:
coupon
.
course_id
.
to_deprecated_string
(),
'coupon_course_id'
:
coupon
.
course_id
.
to_deprecated_string
(),
'coupon_discount'
:
coupon
.
percentage_discount
,
'coupon_discount'
:
coupon
.
percentage_discount
,
'expiry_date'
:
expiry_date
,
'message'
:
_
(
'coupon with the coupon id ({coupon_id}) updated successfully'
)
.
format
(
coupon_id
=
coupon_id
)
'message'
:
_
(
'coupon with the coupon id ({coupon_id}) updated successfully'
)
.
format
(
coupon_id
=
coupon_id
)
})
# status code 200: OK by default
})
# status code 200: OK by default
lms/djangoapps/instructor_analytics/basic.py
View file @
c5746e6d
...
@@ -30,7 +30,7 @@ SALE_ORDER_FEATURES = ('id', 'company_name', 'company_contact_name', 'company_co
...
@@ -30,7 +30,7 @@ SALE_ORDER_FEATURES = ('id', 'company_name', 'company_contact_name', 'company_co
AVAILABLE_FEATURES
=
STUDENT_FEATURES
+
PROFILE_FEATURES
AVAILABLE_FEATURES
=
STUDENT_FEATURES
+
PROFILE_FEATURES
COURSE_REGISTRATION_FEATURES
=
(
'code'
,
'course_id'
,
'created_by'
,
'created_at'
)
COURSE_REGISTRATION_FEATURES
=
(
'code'
,
'course_id'
,
'created_by'
,
'created_at'
)
COUPON_FEATURES
=
(
'co
urse_id'
,
'percentage_discount'
,
'description
'
)
COUPON_FEATURES
=
(
'co
de'
,
'course_id'
,
'percentage_discount'
,
'description'
,
'expiration_date
'
)
def
sale_order_record_features
(
course_id
,
features
):
def
sale_order_record_features
(
course_id
,
features
):
...
@@ -228,6 +228,7 @@ def coupon_codes_features(features, coupons_list):
...
@@ -228,6 +228,7 @@ def coupon_codes_features(features, coupons_list):
# codes csv. In the case of active and generated registration codes the redeemed_by value will be None.
# codes csv. In the case of active and generated registration codes the redeemed_by value will be None.
# They have not been redeemed yet
# They have not been redeemed yet
coupon_dict
[
'expiration_date'
]
=
coupon
.
display_expiry_date
coupon_dict
[
'course_id'
]
=
coupon_dict
[
'course_id'
]
.
to_deprecated_string
()
coupon_dict
[
'course_id'
]
=
coupon_dict
[
'course_id'
]
.
to_deprecated_string
()
return
coupon_dict
return
coupon_dict
return
[
extract_coupon
(
coupon
,
features
)
for
coupon
in
coupons_list
]
return
[
extract_coupon
(
coupon
,
features
)
for
coupon
in
coupons_list
]
...
...
lms/djangoapps/instructor_analytics/tests/test_basic.py
View file @
c5746e6d
...
@@ -22,6 +22,10 @@ from courseware.tests.factories import InstructorFactory
...
@@ -22,6 +22,10 @@ from courseware.tests.factories import InstructorFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
import
datetime
from
django.db.models
import
Q
import
pytz
class
TestAnalyticsBasic
(
ModuleStoreTestCase
):
class
TestAnalyticsBasic
(
ModuleStoreTestCase
):
""" Test basic analytics functions. """
""" Test basic analytics functions. """
...
@@ -303,7 +307,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
...
@@ -303,7 +307,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
def
test_coupon_codes_features
(
self
):
def
test_coupon_codes_features
(
self
):
query_features
=
[
query_features
=
[
'course_id'
,
'percentage_discount'
,
'code_redeemed_count'
,
'description'
'course_id'
,
'percentage_discount'
,
'code_redeemed_count'
,
'description'
,
'expiration_date'
]
]
for
i
in
range
(
10
):
for
i
in
range
(
10
):
coupon
=
Coupon
(
coupon
=
Coupon
(
...
@@ -314,13 +318,29 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
...
@@ -314,13 +318,29 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
is_active
=
True
is_active
=
True
)
)
coupon
.
save
()
coupon
.
save
()
active_coupons
=
Coupon
.
objects
.
filter
(
course_id
=
self
.
course
.
id
,
is_active
=
True
)
#now create coupons with the expiration dates
for
i
in
range
(
5
):
coupon
=
Coupon
(
code
=
'coupon{0}'
.
format
(
i
),
description
=
'test_description'
,
course_id
=
self
.
course
.
id
,
percentage_discount
=
'{0}'
.
format
(
i
),
created_by
=
self
.
instructor
,
is_active
=
True
,
expiration_date
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
2
)
)
coupon
.
save
()
active_coupons
=
Coupon
.
objects
.
filter
(
Q
(
course_id
=
self
.
course
.
id
),
Q
(
is_active
=
True
),
Q
(
expiration_date__gt
=
datetime
.
datetime
.
now
(
pytz
.
UTC
))
|
Q
(
expiration_date__isnull
=
True
)
)
active_coupons_list
=
coupon_codes_features
(
query_features
,
active_coupons
)
active_coupons_list
=
coupon_codes_features
(
query_features
,
active_coupons
)
self
.
assertEqual
(
len
(
active_coupons_list
),
len
(
active_coupons
))
self
.
assertEqual
(
len
(
active_coupons_list
),
len
(
active_coupons
))
for
active_coupon
in
active_coupons_list
:
for
active_coupon
in
active_coupons_list
:
self
.
assertEqual
(
set
(
active_coupon
.
keys
()),
set
(
query_features
))
self
.
assertEqual
(
set
(
active_coupon
.
keys
()),
set
(
query_features
))
self
.
assertIn
(
active_coupon
[
'percentage_discount'
],
[
coupon
.
percentage_discount
for
coupon
in
active_coupons
])
self
.
assertIn
(
active_coupon
[
'percentage_discount'
],
[
coupon
.
percentage_discount
for
coupon
in
active_coupons
])
self
.
assertIn
(
active_coupon
[
'description'
],
[
coupon
.
description
for
coupon
in
active_coupons
])
self
.
assertIn
(
active_coupon
[
'description'
],
[
coupon
.
description
for
coupon
in
active_coupons
])
if
active_coupon
[
'expiration_date'
]:
self
.
assertIn
(
active_coupon
[
'expiration_date'
],
[
coupon
.
display_expiry_date
for
coupon
in
active_coupons
])
self
.
assertIn
(
self
.
assertIn
(
active_coupon
[
'course_id'
],
active_coupon
[
'course_id'
],
[
coupon
.
course_id
.
to_deprecated_string
()
for
coupon
in
active_coupons
]
[
coupon
.
course_id
.
to_deprecated_string
()
for
coupon
in
active_coupons
]
...
...
lms/djangoapps/shoppingcart/migrations/0023_auto__add_field_coupon_expiration_date.py
0 → 100644
View file @
c5746e6d
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding field 'Coupon.expiration_date'
db
.
add_column
(
'shoppingcart_coupon'
,
'expiration_date'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
null
=
True
,
blank
=
True
),
keep_default
=
False
)
def
backwards
(
self
,
orm
):
# Deleting field 'Coupon.expiration_date'
db
.
delete_column
(
'shoppingcart_coupon'
,
'expiration_date'
)
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'shoppingcart.certificateitem'
:
{
'Meta'
:
{
'object_name'
:
'CertificateItem'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_enrollment'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['student.CourseEnrollment']"
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'max_length'
:
'50'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.coupon'
:
{
'Meta'
:
{
'object_name'
:
'Coupon'
},
'code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
}),
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime(2015, 1, 6, 0, 0)'
}),
'created_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'description'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'expiration_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'percentage_discount'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'0'
})
},
'shoppingcart.couponredemption'
:
{
'Meta'
:
{
'object_name'
:
'CouponRedemption'
},
'coupon'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Coupon']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.courseregcodeitem'
:
{
'Meta'
:
{
'object_name'
:
'CourseRegCodeItem'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'default'
:
"'honor'"
,
'max_length'
:
'50'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.courseregcodeitemannotation'
:
{
'Meta'
:
{
'object_name'
:
'CourseRegCodeItemAnnotation'
},
'annotation'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'shoppingcart.courseregistrationcode'
:
{
'Meta'
:
{
'object_name'
:
'CourseRegistrationCode'
},
'code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime(2015, 1, 6, 0, 0)'
}),
'created_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'created_by_user'"
,
'to'
:
"orm['auth.User']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'invoice'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Invoice']"
,
'null'
:
'True'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'purchase_order'"
,
'null'
:
'True'
,
'to'
:
"orm['shoppingcart.Order']"
})
},
'shoppingcart.donation'
:
{
'Meta'
:
{
'object_name'
:
'Donation'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'donation_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'general'"
,
'max_length'
:
'32'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.donationconfiguration'
:
{
'Meta'
:
{
'object_name'
:
'DonationConfiguration'
},
'change_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'changed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
,
'null'
:
'True'
,
'on_delete'
:
'models.PROTECT'
}),
'enabled'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'shoppingcart.invoice'
:
{
'Meta'
:
{
'object_name'
:
'Invoice'
},
'address_line_1'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'address_line_2'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
}),
'address_line_3'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
}),
'city'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
}),
'company_contact_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'company_contact_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'company_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'country'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'customer_reference_number'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'63'
,
'null'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'internal_reference'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
}),
'is_valid'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'recipient_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'recipient_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'state'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
}),
'total_amount'
:
(
'django.db.models.fields.FloatField'
,
[],
{}),
'zip'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'15'
,
'null'
:
'True'
})
},
'shoppingcart.order'
:
{
'Meta'
:
{
'object_name'
:
'Order'
},
'bill_to_cardtype'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'blank'
:
'True'
}),
'bill_to_ccnum'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'8'
,
'blank'
:
'True'
}),
'bill_to_city'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_country'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_first'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_last'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_postalcode'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'16'
,
'blank'
:
'True'
}),
'bill_to_state'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'8'
,
'blank'
:
'True'
}),
'bill_to_street1'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'bill_to_street2'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'company_contact_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'company_contact_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'company_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'customer_reference_number'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'63'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'order_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'personal'"
,
'max_length'
:
'32'
}),
'processor_reply_dump'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'purchase_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'recipient_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'recipient_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'refunded_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'cart'"
,
'max_length'
:
'32'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.orderitem'
:
{
'Meta'
:
{
'object_name'
:
'OrderItem'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'fulfilled_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'line_desc'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'Misc. Item'"
,
'max_length'
:
'1024'
}),
'list_price'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'null'
:
'True'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
}),
'qty'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'1'
}),
'refund_requested_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'report_comments'
:
(
'django.db.models.fields.TextField'
,
[],
{
'default'
:
"''"
}),
'service_fee'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'default'
:
'0.0'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'cart'"
,
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'unit_cost'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'default'
:
'0.0'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.paidcourseregistration'
:
{
'Meta'
:
{
'object_name'
:
'PaidCourseRegistration'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_enrollment'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['student.CourseEnrollment']"
,
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'default'
:
"'honor'"
,
'max_length'
:
'50'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.paidcourseregistrationannotation'
:
{
'Meta'
:
{
'object_name'
:
'PaidCourseRegistrationAnnotation'
},
'annotation'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'shoppingcart.registrationcoderedemption'
:
{
'Meta'
:
{
'object_name'
:
'RegistrationCodeRedemption'
},
'course_enrollment'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['student.CourseEnrollment']"
,
'null'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
,
'null'
:
'True'
}),
'redeemed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime(2015, 1, 6, 0, 0)'
,
'null'
:
'True'
}),
'redeemed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'registration_code'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.CourseRegistrationCode']"
})
},
'student.courseenrollment'
:
{
'Meta'
:
{
'ordering'
:
"('user', 'course_id')"
,
'unique_together'
:
"(('user', 'course_id'),)"
,
'object_name'
:
'CourseEnrollment'
},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'null'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'honor'"
,
'max_length'
:
'100'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
}
}
complete_apps
=
[
'shoppingcart'
]
\ No newline at end of file
lms/djangoapps/shoppingcart/models.py
View file @
c5746e6d
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
from
collections
import
namedtuple
from
collections
import
namedtuple
from
datetime
import
datetime
from
datetime
import
datetime
from
datetime
import
timedelta
from
decimal
import
Decimal
from
decimal
import
Decimal
import
analytics
import
analytics
import
pytz
import
pytz
...
@@ -803,12 +804,20 @@ class Coupon(models.Model):
...
@@ -803,12 +804,20 @@ class Coupon(models.Model):
created_by
=
models
.
ForeignKey
(
User
)
created_by
=
models
.
ForeignKey
(
User
)
created_at
=
models
.
DateTimeField
(
default
=
datetime
.
now
(
pytz
.
utc
))
created_at
=
models
.
DateTimeField
(
default
=
datetime
.
now
(
pytz
.
utc
))
is_active
=
models
.
BooleanField
(
default
=
True
)
is_active
=
models
.
BooleanField
(
default
=
True
)
expiration_date
=
models
.
DateTimeField
(
null
=
True
,
blank
=
True
)
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
"[Coupon] code: {} course: {}"
.
format
(
self
.
code
,
self
.
course_id
)
return
"[Coupon] code: {} course: {}"
.
format
(
self
.
code
,
self
.
course_id
)
objects
=
SoftDeleteCouponManager
()
objects
=
SoftDeleteCouponManager
()
@property
def
display_expiry_date
(
self
):
"""
return the coupon expiration date in the readable format
"""
return
(
self
.
expiration_date
-
timedelta
(
days
=
1
))
.
strftime
(
"
%
B
%
d,
%
Y"
)
if
self
.
expiration_date
else
None
class
CouponRedemption
(
models
.
Model
):
class
CouponRedemption
(
models
.
Model
):
"""
"""
...
...
lms/djangoapps/shoppingcart/tests/test_views.py
View file @
c5746e6d
...
@@ -369,7 +369,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
...
@@ -369,7 +369,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
resp
=
self
.
client
.
post
(
reverse
(
'shoppingcart.views.use_code'
),
{
'code'
:
self
.
coupon_code
})
resp
=
self
.
client
.
post
(
reverse
(
'shoppingcart.views.use_code'
),
{
'code'
:
self
.
coupon_code
})
self
.
assertEqual
(
resp
.
status_code
,
404
)
self
.
assertEqual
(
resp
.
status_code
,
404
)
self
.
assertIn
(
"
Coupon '{0}' is not valid for any course in the shopping cart
."
.
format
(
self
.
coupon_code
),
resp
.
content
)
self
.
assertIn
(
"
Discount does not exist against code '{0}'
."
.
format
(
self
.
coupon_code
),
resp
.
content
)
def
test_course_does_not_exist_in_cart_against_valid_reg_code
(
self
):
def
test_course_does_not_exist_in_cart_against_valid_reg_code
(
self
):
course_key
=
self
.
course_key
.
to_deprecated_string
()
+
'testing'
course_key
=
self
.
course_key
.
to_deprecated_string
()
+
'testing'
...
...
lms/djangoapps/shoppingcart/views.py
View file @
c5746e6d
...
@@ -2,6 +2,7 @@ import logging
...
@@ -2,6 +2,7 @@ import logging
import
datetime
import
datetime
import
decimal
import
decimal
import
pytz
import
pytz
from
django.db.models
import
Q
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.models
import
Group
from
django.contrib.auth.models
import
Group
from
django.http
import
(
from
django.http
import
(
...
@@ -258,7 +259,12 @@ def use_code(request):
...
@@ -258,7 +259,12 @@ def use_code(request):
Registration Code Redemption page.
Registration Code Redemption page.
"""
"""
code
=
request
.
POST
[
"code"
]
code
=
request
.
POST
[
"code"
]
coupons
=
Coupon
.
objects
.
filter
(
code
=
code
,
is_active
=
True
)
coupons
=
Coupon
.
objects
.
filter
(
Q
(
code
=
code
),
Q
(
is_active
=
True
),
Q
(
expiration_date__gt
=
datetime
.
datetime
.
now
(
pytz
.
UTC
))
|
Q
(
expiration_date__isnull
=
True
)
)
if
not
coupons
:
if
not
coupons
:
# If no coupons then we check that code against course registration code
# If no coupons then we check that code against course registration code
try
:
try
:
...
@@ -423,9 +429,8 @@ def use_coupon_code(coupons, user):
...
@@ -423,9 +429,8 @@ def use_coupon_code(coupons, user):
return
HttpResponseBadRequest
(
_
(
"Only one coupon redemption is allowed against an order"
))
return
HttpResponseBadRequest
(
_
(
"Only one coupon redemption is allowed against an order"
))
if
not
is_redemption_applied
:
if
not
is_redemption_applied
:
log
.
warning
(
"Course item does not exist for coupon '{code}'"
.
format
(
code
=
coupons
[
0
]
.
code
))
log
.
warning
(
"Discount does not exist against code '{code}'."
.
format
(
code
=
coupons
[
0
]
.
code
))
return
HttpResponseNotFound
(
return
HttpResponseNotFound
(
_
(
"Discount does not exist against code '{code}'."
.
format
(
code
=
coupons
[
0
]
.
code
)))
_
(
"Coupon '{code}' is not valid for any course in the shopping cart."
.
format
(
code
=
coupons
[
0
]
.
code
)))
return
HttpResponse
(
return
HttpResponse
(
json
.
dumps
({
'response'
:
'success'
,
'coupon_code_applied'
:
True
}),
json
.
dumps
({
'response'
:
'success'
,
'coupon_code_applied'
:
True
}),
...
...
lms/static/js/instructor_dashboard/ecommerce.js
0 → 100644
View file @
c5746e6d
var
edx
=
edx
||
{};
(
function
(
Backbone
,
$
,
_
)
{
'use strict'
;
edx
.
instructor_dashboard
=
edx
.
instructor_dashboard
||
{};
edx
.
instructor_dashboard
.
ecommerce
=
{};
edx
.
instructor_dashboard
.
ecommerce
.
ExpiryCouponView
=
Backbone
.
View
.
extend
({
el
:
'li#add-coupon-modal-field-expiry'
,
events
:
{
'click input[type="checkbox"]'
:
'clicked'
},
initialize
:
function
()
{
$
(
'li#add-coupon-modal-field-expiry input[name="expiration_date"]'
).
hide
();
_
.
bindAll
(
this
,
'clicked'
);
},
clicked
:
function
(
event
)
{
if
(
event
.
currentTarget
.
checked
)
{
this
.
$el
.
find
(
'#coupon_expiration_date'
).
show
();
this
.
$el
.
find
(
'#coupon_expiration_date'
).
focus
();
}
else
{
this
.
$el
.
find
(
'#coupon_expiration_date'
).
hide
();
}
}
});
$
(
function
()
{
$
(
"#coupon_expiration_date"
).
datepicker
({
minDate
:
0
});
var
view
=
new
edx
.
instructor_dashboard
.
ecommerce
.
ExpiryCouponView
();
});
}).
call
(
this
,
Backbone
,
$
,
_
);
\ No newline at end of file
lms/static/js/spec/instructor_dashboard/ecommerce_spec.js
0 → 100644
View file @
c5746e6d
define
([
'backbone'
,
'jquery'
,
'js/instructor_dashboard/ecommerce'
,
'js/common_helpers/template_helpers'
],
function
(
Backbone
,
$
,
ExpiryCouponView
,
TemplateHelpers
)
{
'use strict'
;
var
expiryCouponView
,
createExpiryCoupon
;
describe
(
"edx.instructor_dashboard.ecommerce.ExpiryCouponView"
,
function
()
{
beforeEach
(
function
()
{
setFixtures
(
'<li class="field full-width" id="add-coupon-modal-field-expiry"><input id="expiry-check" type="checkbox"/><label for="expiry-check"></label><input type="text" id="coupon_expiration_date" class="field" name="expiration_date" aria-required="true"/></li>'
)
expiryCouponView
=
new
ExpiryCouponView
();
});
it
(
"is defined"
,
function
()
{
expect
(
expiryCouponView
).
toBeDefined
();
});
it
(
"triggers the callback when the checkbox is clicked"
,
function
()
{
var
target
=
expiryCouponView
.
$el
.
find
(
'input[type="checkbox"]'
);
spyOn
(
expiryCouponView
,
'clicked'
);
expiryCouponView
.
delegateEvents
();
target
.
click
();
expect
(
expiryCouponView
.
clicked
).
toHaveBeenCalled
();
});
it
(
"shows the input field when the checkbox is checked"
,
function
()
{
var
target
=
expiryCouponView
.
$el
.
find
(
'input[type="checkbox"]'
);
target
.
attr
(
"checked"
,
"checked"
);
target
.
click
();
expect
(
expiryCouponView
.
$el
.
find
(
'#coupon_expiration_date'
)).
toHaveAttr
(
'style'
,
'display: inline;'
);
});
it
(
"hides the input field when the checkbox is unchecked"
,
function
()
{
var
target
=
expiryCouponView
.
$el
.
find
(
'input[type="checkbox"]'
);
expect
(
expiryCouponView
.
$el
.
find
(
'#coupon_expiration_date'
)).
toHaveAttr
(
'style'
,
'display: none;'
);
});
});
});
lms/static/js/spec/main.js
View file @
c5746e6d
...
@@ -270,6 +270,10 @@
...
@@ -270,6 +270,10 @@
},
},
// Backbone classes loaded explicitly until they are converted to use RequireJS
// Backbone classes loaded explicitly until they are converted to use RequireJS
'js/instructor_dashboard/ecommerce'
:
{
exports
:
'edx.instructor_dashboard.ecommerce.ExpiryCouponView'
,
deps
:
[
'backbone'
,
'jquery'
,
'underscore'
]
},
'js/models/cohort'
:
{
'js/models/cohort'
:
{
exports
:
'CohortModel'
,
exports
:
'CohortModel'
,
deps
:
[
'backbone'
]
deps
:
[
'backbone'
]
...
@@ -497,6 +501,7 @@
...
@@ -497,6 +501,7 @@
'lms/include/js/spec/views/file_uploader_spec.js'
,
'lms/include/js/spec/views/file_uploader_spec.js'
,
'lms/include/js/spec/dashboard/donation.js'
,
'lms/include/js/spec/dashboard/donation.js'
,
'lms/include/js/spec/shoppingcart/shoppingcart_spec.js'
,
'lms/include/js/spec/shoppingcart/shoppingcart_spec.js'
,
'lms/include/js/spec/instructor_dashboard/ecommerce_spec.js'
,
'lms/include/js/spec/student_account/account_spec.js'
,
'lms/include/js/spec/student_account/account_spec.js'
,
'lms/include/js/spec/student_account/access_spec.js'
,
'lms/include/js/spec/student_account/access_spec.js'
,
'lms/include/js/spec/student_account/login_spec.js'
,
'lms/include/js/spec/student_account/login_spec.js'
,
...
...
lms/static/sass/course/instructor/_instructor_2.scss
View file @
c5746e6d
...
@@ -12,6 +12,9 @@
...
@@ -12,6 +12,9 @@
}
}
}
}
#ui-datepicker-div
{
z-index
:
12000
!
important
;
width
:
16
.5em
!
important
}
.instructor-dashboard-wrapper-2
{
.instructor-dashboard-wrapper-2
{
position
:
relative
;
position
:
relative
;
// display: table;
// display: table;
...
@@ -1325,12 +1328,13 @@ input[name="subject"] {
...
@@ -1325,12 +1328,13 @@ input[name="subject"] {
th
{
th
{
text-align
:
left
;
text-align
:
left
;
border-bottom
:
1px
solid
$border-color-1
;
border-bottom
:
1px
solid
$border-color-1
;
font-size
:
16px
;
&
.c_code
{
&
.c_code
{
width
:
1
7
0px
;
width
:
1
1
0px
;
}
}
&
.c_count
{
&
.c_count
{
width
:
85
px
;
width
:
60
px
;
}
}
&
.c_course_id
{
&
.c_course_id
{
width
:
320px
;
width
:
320px
;
...
@@ -1339,11 +1343,14 @@ input[name="subject"] {
...
@@ -1339,11 +1343,14 @@ input[name="subject"] {
&
.c_discount
{
&
.c_discount
{
width
:
90px
;
width
:
90px
;
}
}
&
.c_expiry
{
width
:
150px
;
}
&
.c_action
{
&
.c_action
{
width
:
89
px
;
width
:
60
px
;
}
}
&
.c_dsc
{
&
.c_dsc
{
width
:
2
6
0px
;
width
:
2
0
0px
;
word-wrap
:
break-word
;
word-wrap
:
break-word
;
}
}
}
}
...
@@ -1361,6 +1368,16 @@ input[name="subject"] {
...
@@ -1361,6 +1368,16 @@ input[name="subject"] {
}
}
}
}
}
}
// in_active coupon rows style
.expired_coupon
{
background
:
#FEEFB3
!
important
;
color
:
rgba
(
51
,
51
,
51
,
0
.2
);
border-bottom
:
1px
solid
#fff
;
td
:nth-child
(
3
)
{
text-decoration
:
line-through
;
}
}
// coupon items style
// coupon items style
.coupons-items
{
.coupons-items
{
...
@@ -1368,6 +1385,7 @@ input[name="subject"] {
...
@@ -1368,6 +1385,7 @@ input[name="subject"] {
padding
:
(
$baseline
/
2
)
0
;
padding
:
(
$baseline
/
2
)
0
;
position
:
relative
;
position
:
relative
;
line-height
:
normal
;
line-height
:
normal
;
font-size
:
14px
;
span
.old-price
{
span
.old-price
{
left
:
-75px
;
left
:
-75px
;
position
:
relative
;
position
:
relative
;
...
@@ -1613,7 +1631,15 @@ input[name="subject"] {
...
@@ -1613,7 +1631,15 @@ input[name="subject"] {
}
}
}
}
}
}
#add-coupon-modal
{
ol
.list-input
{
li
{
input
[
type
=
"checkbox"
]
#expiry-check
,
input
[
type
=
"checkbox"
]
#expiry-check
+
label
{
display
:
inline-block
;
width
:
auto
;
margin-top
:
10px
;}
&
.full-width
{
width
:
100%
;}
input
#coupon_expiration_date
{
width
:
278px
;
display
:
inline-block
;
float
:
right
;}
}
}
}
}
}
.profile-distribution-widget
{
.profile-distribution-widget
{
...
...
lms/templates/instructor/instructor_dashboard_2/add_coupon_modal.html
View file @
c5746e6d
...
@@ -49,6 +49,12 @@
...
@@ -49,6 +49,12 @@
<input
class=
"field readonly"
id=
"coupon_course_id"
type=
"text"
name=
"course_id"
value=
"${section_data['course_id'] | h}"
<input
class=
"field readonly"
id=
"coupon_course_id"
type=
"text"
name=
"course_id"
value=
"${section_data['course_id'] | h}"
readonly
aria-required=
"true"
/>
readonly
aria-required=
"true"
/>
</li>
</li>
<li
class=
"field full-width"
id=
"add-coupon-modal-field-expiry"
>
<input
id=
"expiry-check"
type=
"checkbox"
value=
"true"
/>
<label
for=
"expiry-check"
>
${_('Add expiration date')}
</label>
<input
type=
"text"
id=
"coupon_expiration_date"
value=
""
class=
"field"
name=
"expiration_date"
aria-required=
"true"
/>
</li>
</ol>
</ol>
</fieldset>
</fieldset>
...
...
lms/templates/instructor/instructor_dashboard_2/e-commerce.html
View file @
c5746e6d
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
datetime
import
datetime
,
timedelta
%
>
<
%!
import
pytz
%
>
<
%
page
args=
"section_data"
/>
<
%
page
args=
"section_data"
/>
<
%
include
file=
"add_coupon_modal.html"
args=
"section_data=section_data"
/>
<
%
include
file=
"add_coupon_modal.html"
args=
"section_data=section_data"
/>
<
%
include
file=
"edit_coupon_modal.html"
args=
"section_data=section_data"
/>
<
%
include
file=
"edit_coupon_modal.html"
args=
"section_data=section_data"
/>
...
@@ -95,6 +97,7 @@
...
@@ -95,6 +97,7 @@
<tr
class=
"coupons-headings"
>
<tr
class=
"coupons-headings"
>
<th
class=
"c_code"
>
${_("Code")}
</th>
<th
class=
"c_code"
>
${_("Code")}
</th>
<th
class=
"c_dsc"
>
${_("Description")}
</th>
<th
class=
"c_dsc"
>
${_("Description")}
</th>
<th
class=
"c_expiry"
>
${_("Expiry Date")}
</th>
<th
class=
"c_discount"
>
${_("Discount (%)")}
</th>
<th
class=
"c_discount"
>
${_("Discount (%)")}
</th>
<th
class=
"c_count"
>
${_("Redeem Count")}
</th>
<th
class=
"c_count"
>
${_("Redeem Count")}
</th>
<th
class=
"c_action"
>
${_("Actions")}
</th>
<th
class=
"c_action"
>
${_("Actions")}
</th>
...
@@ -102,14 +105,21 @@
...
@@ -102,14 +105,21 @@
</thead>
</thead>
<tbody>
<tbody>
%for coupon in section_data['coupons']:
%for coupon in section_data['coupons']:
<
%
current_date =
datetime.now(pytz.UTC)
%
>
<
%
coupon_expiry_date =
coupon.expiration_date
%
>
%if coupon.is_active == False:
%if coupon.is_active == False:
<tr
class=
"coupons-items inactive_coupon"
>
<tr
class=
"coupons-items inactive_coupon"
>
%else:
%elif coupon_expiry_date is not None and current_date >= coupon_expiry_date:
<tr
class=
"coupons-items expired_coupon"
>
%else:
<tr
class=
"coupons-items"
>
<tr
class=
"coupons-items"
>
%endif
%endif
<td>
${coupon.code}
</td>
<td>
${_('{code}').format(code=coupon.code)}
</td>
<td>
${coupon.description}
</td>
<td>
${_('{description}').format(description=coupon.description)}
</td>
<td>
${coupon.percentage_discount}
</td>
<td>
${coupon.display_expiry_date}
</td>
<td>
${_('{discount}').format(discount=coupon.percentage_discount)}
</td>
<td>
${ coupon.couponredemption_set.filter(order__status='purchased').count() }
</td>
<td>
${ coupon.couponredemption_set.filter(order__status='purchased').count() }
</td>
<td><a
data-item-id=
"${coupon.id}"
class=
'remove_coupon'
href=
'#'
>
[x]
</a><a
href=
"#edit-modal"
data-item-id=
"${coupon.id}"
class=
"edit-right"
>
${_('Edit')}
</a></td>
<td><a
data-item-id=
"${coupon.id}"
class=
'remove_coupon'
href=
'#'
>
[x]
</a><a
href=
"#edit-modal"
data-item-id=
"${coupon.id}"
class=
"edit-right"
>
${_('Edit')}
</a></td>
</tr>
</tr>
...
@@ -184,7 +194,7 @@
...
@@ -184,7 +194,7 @@
}
}
if
(
$
(
'#invoice_number'
).
val
()
==
""
)
{
if
(
$
(
'#invoice_number'
).
val
()
==
""
)
{
$
(
'#error-msg'
).
attr
(
'class'
,
'error-msgs'
)
$
(
'#error-msg'
).
attr
(
'class'
,
'error-msgs'
)
$
(
'#error-msg'
).
html
(
"${_(
"
Invoice
number
should
not
be
empty
.
"
)}"
).
show
();
$
(
'#error-msg'
).
html
(
"${_(
'Invoice number should not be empty.'
)}"
).
show
();
return
return
}
}
$
.
ajax
({
$
.
ajax
({
...
@@ -224,6 +234,12 @@
...
@@ -224,6 +234,12 @@
$
(
'input#edit_coupon_discount'
).
val
(
data
.
coupon_discount
);
$
(
'input#edit_coupon_discount'
).
val
(
data
.
coupon_discount
);
$
(
'textarea#edit_coupon_description'
).
val
(
data
.
coupon_description
);
$
(
'textarea#edit_coupon_description'
).
val
(
data
.
coupon_description
);
$
(
'input#edit_coupon_course_id'
).
val
(
data
.
coupon_course_id
);
$
(
'input#edit_coupon_course_id'
).
val
(
data
.
coupon_course_id
);
if
(
data
.
expiry_date
)
{
$
(
'input#edit_coupon_expiration_date'
).
val
(
data
.
expiry_date
);
}
else
{
$
(
'input#edit_coupon_expiration_date'
).
val
(
"${_('Never Expires')}"
);
}
$
(
'#edit-modal-trigger'
).
click
();
$
(
'#edit-modal-trigger'
).
click
();
},
},
error
:
function
(
jqXHR
,
textStatus
,
errorThrown
)
{
error
:
function
(
jqXHR
,
textStatus
,
errorThrown
)
{
...
@@ -459,6 +475,7 @@
...
@@ -459,6 +475,7 @@
var
coupon_discount
=
$
.
trim
(
$
(
'#coupon_discount'
).
val
());
var
coupon_discount
=
$
.
trim
(
$
(
'#coupon_discount'
).
val
());
var
course_id
=
$
.
trim
(
$
(
'#coupon_course_id'
).
val
());
var
course_id
=
$
.
trim
(
$
(
'#coupon_course_id'
).
val
());
var
description
=
$
.
trim
(
$
(
'#coupon_description'
).
val
());
var
description
=
$
.
trim
(
$
(
'#coupon_description'
).
val
());
var
expiration_date
=
$
.
trim
(
$
(
'#coupon_expiration_date'
).
val
());
// Check if empty of not
// Check if empty of not
if
(
code
===
''
)
{
if
(
code
===
''
)
{
...
@@ -485,7 +502,8 @@
...
@@ -485,7 +502,8 @@
"code"
:
code
,
"code"
:
code
,
"discount"
:
coupon_discount
,
"discount"
:
coupon_discount
,
"course_id"
:
course_id
,
"course_id"
:
course_id
,
"description"
:
description
"description"
:
description
,
"expiration_date"
:
expiration_date
},
},
url
:
"${section_data['ajax_add_coupon']}"
,
url
:
"${section_data['ajax_add_coupon']}"
,
success
:
function
(
data
)
{
success
:
function
(
data
)
{
...
...
lms/templates/instructor/instructor_dashboard_2/edit_coupon_modal.html
View file @
c5746e6d
...
@@ -49,6 +49,12 @@
...
@@ -49,6 +49,12 @@
<input
class=
"field readonly"
id=
"edit_coupon_course_id"
type=
"text"
name=
"course_id"
value=
""
<input
class=
"field readonly"
id=
"edit_coupon_course_id"
type=
"text"
name=
"course_id"
value=
""
readonly
aria-required=
"true"
/>
readonly
aria-required=
"true"
/>
</li>
</li>
<li
class=
"field"
id=
"edit-coupon-modal-field-expiration-date"
>
<label
for=
"edit_coupon_expiration_date"
>
${_("Expiration Date")}
</label>
<input
class=
"field readonly"
id=
"edit_coupon_expiration_date"
type=
"text"
name=
"expiration_date"
value=
""
readonly
aria-required=
"true"
/>
</li>
</ol>
</ol>
</fieldset>
</fieldset>
...
...
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
View file @
c5746e6d
...
@@ -53,6 +53,7 @@
...
@@ -53,6 +53,7 @@
<
%
static:js
group=
'application'
/>
<
%
static:js
group=
'application'
/>
## Backbone classes declared explicitly until RequireJS is supported
## Backbone classes declared explicitly until RequireJS is supported
<script
type=
"text/javascript"
src=
"${static.url('js/instructor_dashboard/ecommerce.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/models/notification.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/models/notification.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/views/notification.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/views/notification.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/views/file_uploader.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/views/file_uploader.js')}"
></script>
...
...
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