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
bb219ed4
Commit
bb219ed4
authored
Jun 12, 2017
by
rabia23
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into ri/EDUCATOR-394-disable-self-generation-certificates
parents
64845801
484ec256
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
283 additions
and
23 deletions
+283
-23
lms/djangoapps/courseware/views/views.py
+6
-1
openedx/core/djangoapps/catalog/tests/factories.py
+58
-4
openedx/core/djangoapps/programs/tests/test_utils.py
+155
-9
openedx/core/djangoapps/programs/utils.py
+64
-9
No files found.
lms/djangoapps/courseware/views/views.py
View file @
bb219ed4
...
@@ -801,8 +801,13 @@ def program_marketing(request, program_uuid):
...
@@ -801,8 +801,13 @@ def program_marketing(request, program_uuid):
if
not
program_data
:
if
not
program_data
:
raise
Http404
raise
Http404
program
=
ProgramMarketingDataExtender
(
program_data
,
request
.
user
)
.
extend
()
skus
=
program
.
get
(
'skus'
)
ecommerce_service
=
EcommerceService
()
return
render_to_response
(
'courseware/program_marketing.html'
,
{
return
render_to_response
(
'courseware/program_marketing.html'
,
{
'program'
:
ProgramMarketingDataExtender
(
program_data
,
request
.
user
)
.
extend
()
'buy_button_href'
:
ecommerce_service
.
get_checkout_page_url
(
*
skus
)
if
skus
else
'#courses'
,
'program'
:
program
,
})
})
...
...
openedx/core/djangoapps/catalog/tests/factories.py
View file @
bb219ed4
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
from
functools
import
partial
from
functools
import
partial
import
factory
import
factory
import
uuid
from
faker
import
Faker
from
faker
import
Faker
...
@@ -34,6 +35,19 @@ def generate_zulu_datetime():
...
@@ -34,6 +35,19 @@ def generate_zulu_datetime():
return
fake
.
date_time
()
.
isoformat
()
+
'Z'
return
fake
.
date_time
()
.
isoformat
()
+
'Z'
def
generate_price_ranges
():
return
[{
'currency'
:
'USD'
,
'max'
:
1000
,
'min'
:
100
,
'total'
:
500
}]
def
generate_seat_sku
():
return
uuid
.
uuid4
()
.
hex
[:
7
]
.
upper
()
class
DictFactoryBase
(
factory
.
Factory
):
class
DictFactoryBase
(
factory
.
Factory
):
"""
"""
Subclass this to make factories that can be used to produce fake API response
Subclass this to make factories that can be used to produce fake API response
...
@@ -77,12 +91,15 @@ class OrganizationFactory(DictFactoryBase):
...
@@ -77,12 +91,15 @@ class OrganizationFactory(DictFactoryBase):
key
=
factory
.
Faker
(
'word'
)
key
=
factory
.
Faker
(
'word'
)
name
=
factory
.
Faker
(
'company'
)
name
=
factory
.
Faker
(
'company'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
logo_image_url
=
factory
.
Faker
(
'image_url'
)
class
SeatFactory
(
DictFactoryBase
):
class
SeatFactory
(
DictFactoryBase
):
type
=
factory
.
Faker
(
'word'
)
price
=
factory
.
Faker
(
'random_int'
)
currency
=
'USD'
currency
=
'USD'
price
=
factory
.
Faker
(
'random_int'
)
sku
=
factory
.
LazyFunction
(
generate_seat_sku
)
type
=
'verified'
upgrade_deadline
=
factory
.
LazyFunction
(
generate_zulu_datetime
)
class
CourseRunFactory
(
DictFactoryBase
):
class
CourseRunFactory
(
DictFactoryBase
):
...
@@ -91,13 +108,13 @@ class CourseRunFactory(DictFactoryBase):
...
@@ -91,13 +108,13 @@ class CourseRunFactory(DictFactoryBase):
enrollment_end
=
factory
.
LazyFunction
(
generate_zulu_datetime
)
enrollment_end
=
factory
.
LazyFunction
(
generate_zulu_datetime
)
enrollment_start
=
factory
.
LazyFunction
(
generate_zulu_datetime
)
enrollment_start
=
factory
.
LazyFunction
(
generate_zulu_datetime
)
image
=
ImageFactory
()
image
=
ImageFactory
()
is_enrolled
=
False
key
=
factory
.
LazyFunction
(
generate_course_run_key
)
key
=
factory
.
LazyFunction
(
generate_course_run_key
)
marketing_url
=
factory
.
Faker
(
'url'
)
marketing_url
=
factory
.
Faker
(
'url'
)
pacing_type
=
'self_paced'
pacing_type
=
'self_paced'
seats
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
SeatFactory
))
seats
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
SeatFactory
))
short_description
=
factory
.
Faker
(
'sentence'
)
short_description
=
factory
.
Faker
(
'sentence'
)
start
=
factory
.
LazyFunction
(
generate_zulu_datetime
)
start
=
factory
.
LazyFunction
(
generate_zulu_datetime
)
status
=
'published'
title
=
factory
.
Faker
(
'catch_phrase'
)
title
=
factory
.
Faker
(
'catch_phrase'
)
type
=
'verified'
type
=
'verified'
uuid
=
factory
.
Faker
(
'uuid4'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
...
@@ -112,20 +129,57 @@ class CourseFactory(DictFactoryBase):
...
@@ -112,20 +129,57 @@ class CourseFactory(DictFactoryBase):
uuid
=
factory
.
Faker
(
'uuid4'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
class
JobOutlookItemFactory
(
DictFactoryBase
):
value
=
factory
.
Faker
(
'sentence'
)
class
PersonFactory
(
DictFactoryBase
):
bio
=
factory
.
Faker
(
'paragraphs'
)
given_name
=
factory
.
Faker
(
'first_name'
)
family_name
=
factory
.
Faker
(
'last_name'
)
profile_image_url
=
factory
.
Faker
(
'image_url'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
class
EndorserFactory
(
DictFactoryBase
):
person
=
PersonFactory
()
quote
=
factory
.
Faker
(
'sentence'
)
class
ExpectedLearningItemFactory
(
DictFactoryBase
):
value
=
factory
.
Faker
(
'sentence'
)
class
FAQFactory
(
DictFactoryBase
):
answer
=
factory
.
Faker
(
'sentence'
)
question
=
factory
.
Faker
(
'sentence'
)
class
ProgramFactory
(
DictFactoryBase
):
class
ProgramFactory
(
DictFactoryBase
):
authoring_organizations
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
OrganizationFactory
,
count
=
1
))
authoring_organizations
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
OrganizationFactory
,
count
=
1
))
applicable_seat_types
=
[]
banner_image
=
factory
.
LazyFunction
(
generate_sized_stdimage
)
banner_image
=
factory
.
LazyFunction
(
generate_sized_stdimage
)
card_image_url
=
factory
.
Faker
(
'image_url'
)
card_image_url
=
factory
.
Faker
(
'image_url'
)
courses
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
CourseFactory
))
courses
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
CourseFactory
))
expected_learning_items
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
CourseFactory
))
faq
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
FAQFactory
))
hidden
=
False
individual_endorsements
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
EndorserFactory
))
is_program_eligible_for_one_click_purchase
=
True
is_program_eligible_for_one_click_purchase
=
True
job_outlook_items
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
JobOutlookItemFactory
))
marketing_slug
=
factory
.
Faker
(
'slug'
)
marketing_slug
=
factory
.
Faker
(
'slug'
)
marketing_url
=
factory
.
Faker
(
'url'
)
marketing_url
=
factory
.
Faker
(
'url'
)
max_hours_effort_per_week
=
fake
.
random_int
(
21
,
28
)
min_hours_effort_per_week
=
fake
.
random_int
(
7
,
14
)
overview
=
factory
.
Faker
(
'sentence'
)
price_ranges
=
factory
.
LazyFunction
(
generate_price_ranges
)
staff
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
PersonFactory
))
status
=
'active'
status
=
'active'
subtitle
=
factory
.
Faker
(
'sentence'
)
subtitle
=
factory
.
Faker
(
'sentence'
)
title
=
factory
.
Faker
(
'catch_phrase'
)
title
=
factory
.
Faker
(
'catch_phrase'
)
type
=
factory
.
Faker
(
'word'
)
type
=
factory
.
Faker
(
'word'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
hidden
=
False
weeks_to_complete
=
fake
.
random_int
(
1
,
45
)
class
ProgramTypeFactory
(
DictFactoryBase
):
class
ProgramTypeFactory
(
DictFactoryBase
):
...
...
openedx/core/djangoapps/programs/tests/test_utils.py
View file @
bb219ed4
This diff is collapsed.
Click to expand it.
openedx/core/djangoapps/programs/utils.py
View file @
bb219ed4
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
"""Helper functions for working with Programs."""
"""Helper functions for working with Programs."""
import
datetime
import
datetime
import
logging
from
collections
import
defaultdict
from
collections
import
defaultdict
from
copy
import
deepcopy
from
copy
import
deepcopy
from
itertools
import
chain
from
itertools
import
chain
...
@@ -8,17 +9,21 @@ from urlparse import urljoin
...
@@ -8,17 +9,21 @@ from urlparse import urljoin
from
dateutil.parser
import
parse
from
dateutil.parser
import
parse
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth
import
get_user_model
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.utils.functional
import
cached_property
from
django.utils.functional
import
cached_property
from
edx_rest_api_client.exceptions
import
SlumberBaseException
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
pytz
import
utc
from
pytz
import
utc
from
requests.exceptions
import
ConnectionError
,
Timeout
from
course_modes.models
import
CourseMode
from
course_modes.models
import
CourseMode
from
lms.djangoapps.certificates
import
api
as
certificate_api
from
lms.djangoapps.certificates
import
api
as
certificate_api
from
lms.djangoapps.commerce.utils
import
EcommerceService
from
lms.djangoapps.commerce.utils
import
EcommerceService
from
lms.djangoapps.courseware.access
import
has_access
from
lms.djangoapps.courseware.access
import
has_access
from
openedx.core.djangoapps.catalog.utils
import
get_programs
from
openedx.core.djangoapps.catalog.utils
import
get_programs
from
openedx.core.djangoapps.commerce.utils
import
ecommerce_api_client
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.credentials.utils
import
get_credentials
from
openedx.core.djangoapps.credentials.utils
import
get_credentials
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
...
@@ -28,6 +33,8 @@ from xmodule.modulestore.django import modulestore
...
@@ -28,6 +33,8 @@ from xmodule.modulestore.django import modulestore
# The datetime module's strftime() methods require a year >= 1900.
# The datetime module's strftime() methods require a year >= 1900.
DEFAULT_ENROLLMENT_START_DATE
=
datetime
.
datetime
(
1900
,
1
,
1
,
tzinfo
=
utc
)
DEFAULT_ENROLLMENT_START_DATE
=
datetime
.
datetime
(
1900
,
1
,
1
,
tzinfo
=
utc
)
log
=
logging
.
getLogger
(
__name__
)
def
get_program_marketing_url
(
programs_config
):
def
get_program_marketing_url
(
programs_config
):
"""Build a URL used to link to programs on the marketing site."""
"""Build a URL used to link to programs on the marketing site."""
...
@@ -507,27 +514,25 @@ class ProgramMarketingDataExtender(ProgramDataExtender):
...
@@ -507,27 +514,25 @@ class ProgramMarketingDataExtender(ProgramDataExtender):
uuid
=
self
.
data
[
'uuid'
]
uuid
=
self
.
data
[
'uuid'
]
)
)
program_instructors
=
cache
.
get
(
cache_key
)
program_instructors
=
cache
.
get
(
cache_key
)
is_learner_eligible_for_one_click_purchase
=
self
.
data
[
'is_program_eligible_for_one_click_purchase'
]
for
course
in
self
.
data
[
'courses'
]:
for
course
in
self
.
data
[
'courses'
]:
self
.
_execute
(
'_collect_course'
,
course
)
self
.
_execute
(
'_collect_course'
,
course
)
if
not
program_instructors
:
if
not
program_instructors
:
for
course_run
in
course
[
'course_runs'
]:
for
course_run
in
course
[
'course_runs'
]:
self
.
_execute
(
'_collect_instructors'
,
course_run
)
self
.
_execute
(
'_collect_instructors'
,
course_run
)
if
is_learner_eligible_for_one_click_purchase
:
is_learner_eligible_for_one_click_purchase
=
not
any
(
course_run
[
'is_enrolled'
]
for
course_run
in
course
[
'course_runs'
]
)
if
not
program_instructors
:
if
not
program_instructors
:
# We cache the program instructors list to avoid repeated modulestore queries
# We cache the program instructors list to avoid repeated modulestore queries
program_instructors
=
self
.
instructors
.
values
()
program_instructors
=
self
.
instructors
.
values
()
cache
.
set
(
cache_key
,
program_instructors
,
3600
)
cache
.
set
(
cache_key
,
program_instructors
,
3600
)
self
.
data
.
update
({
self
.
data
[
'instructors'
]
=
program_instructors
'instructors'
:
program_instructors
,
'is_learner_eligible_for_one_click_purchase'
:
is_learner_eligible_for_one_click_purchase
,
def
extend
(
self
):
})
"""Execute extension handlers, returning the extended data."""
self
.
data
.
update
(
super
(
ProgramMarketingDataExtender
,
self
)
.
extend
())
self
.
_collect_one_click_purchase_eligibility_data
()
return
self
.
data
@classmethod
@classmethod
def
_handlers
(
cls
,
prefix
):
def
_handlers
(
cls
,
prefix
):
...
@@ -582,3 +587,53 @@ class ProgramMarketingDataExtender(ProgramDataExtender):
...
@@ -582,3 +587,53 @@ class ProgramMarketingDataExtender(ProgramDataExtender):
self
.
instructors
.
update
(
self
.
instructors
.
update
(
{
instructor
.
get
(
'name'
):
instructor
for
instructor
in
course_instructors
.
get
(
'instructors'
,
[])}
{
instructor
.
get
(
'name'
):
instructor
for
instructor
in
course_instructors
.
get
(
'instructors'
,
[])}
)
)
def
_collect_one_click_purchase_eligibility_data
(
self
):
"""
Extend the program data with data about learner's eligibility for one click purchase,
discount data of the program and SKUs of seats that should be added to basket.
"""
applicable_seat_types
=
self
.
data
[
'applicable_seat_types'
]
is_learner_eligible_for_one_click_purchase
=
self
.
data
[
'is_program_eligible_for_one_click_purchase'
]
skus
=
[]
if
is_learner_eligible_for_one_click_purchase
:
for
course
in
self
.
data
[
'courses'
]:
is_learner_eligible_for_one_click_purchase
=
not
any
(
course_run
[
'is_enrolled'
]
for
course_run
in
course
[
'course_runs'
]
)
if
is_learner_eligible_for_one_click_purchase
:
published_course_runs
=
filter
(
lambda
run
:
run
[
'status'
]
==
'published'
,
course
[
'course_runs'
])
if
len
(
published_course_runs
)
==
1
:
for
seat
in
published_course_runs
[
0
][
'seats'
]:
if
seat
[
'type'
]
in
applicable_seat_types
:
skus
.
append
(
seat
[
'sku'
])
else
:
# If a course in the program has more than 1 published course run
# learner won't be eligible for a one click purchase.
is_learner_eligible_for_one_click_purchase
=
False
skus
=
[]
break
else
:
skus
=
[]
break
if
skus
:
try
:
User
=
get_user_model
()
service_user
=
User
.
objects
.
get
(
username
=
settings
.
ECOMMERCE_SERVICE_WORKER_USERNAME
)
api
=
ecommerce_api_client
(
service_user
)
# Make an API call to calculate the discounted price
discount_data
=
api
.
baskets
.
calculate
.
get
(
sku
=
skus
)
self
.
data
.
update
({
'discount_data'
:
discount_data
,
'full_program_price'
:
discount_data
[
'total_incl_tax'
]
})
except
(
ConnectionError
,
SlumberBaseException
,
Timeout
):
log
.
exception
(
'Failed to get discount price for following product SKUs:
%
s '
,
', '
.
join
(
skus
))
self
.
data
.
update
({
'is_learner_eligible_for_one_click_purchase'
:
is_learner_eligible_for_one_click_purchase
,
'skus'
:
skus
,
})
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