Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
course-discovery
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
course-discovery
Commits
d3081bb2
Commit
d3081bb2
authored
Mar 13, 2018
by
Adam Stankiewicz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add enrollment code SKU in Seat model, modify ecommerce data loader to get enrollment codes
parent
1efd2cbd
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
139 additions
and
10 deletions
+139
-10
course_discovery/apps/api/serializers.py
+2
-1
course_discovery/apps/api/tests/test_serializers.py
+2
-1
course_discovery/apps/course_metadata/data_loaders/api.py
+90
-2
course_discovery/apps/course_metadata/data_loaders/tests/mock_data.py
+23
-6
course_discovery/apps/course_metadata/data_loaders/tests/test_api.py
+0
-0
course_discovery/apps/course_metadata/migrations/0080_seat_bulk_sku.py
+20
-0
course_discovery/apps/course_metadata/models.py
+1
-0
course_discovery/apps/course_metadata/tests/factories.py
+1
-0
No files found.
course_discovery/apps/api/serializers.py
View file @
d3081bb2
...
...
@@ -387,6 +387,7 @@ class SeatSerializer(serializers.ModelSerializer):
credit_provider
=
serializers
.
CharField
()
credit_hours
=
serializers
.
IntegerField
()
sku
=
serializers
.
CharField
()
bulk_sku
=
serializers
.
CharField
()
@classmethod
def
prefetch_queryset
(
cls
):
...
...
@@ -394,7 +395,7 @@ class SeatSerializer(serializers.ModelSerializer):
class
Meta
(
object
):
model
=
Seat
fields
=
(
'type'
,
'price'
,
'currency'
,
'upgrade_deadline'
,
'credit_provider'
,
'credit_hours'
,
'sku'
,)
fields
=
(
'type'
,
'price'
,
'currency'
,
'upgrade_deadline'
,
'credit_provider'
,
'credit_hours'
,
'sku'
,
'bulk_sku'
)
class
CourseEntitlementSerializer
(
serializers
.
ModelSerializer
):
...
...
course_discovery/apps/api/tests/test_serializers.py
View file @
d3081bb2
...
...
@@ -1117,7 +1117,8 @@ class SeatSerializerTests(TestCase):
'upgrade_deadline'
:
json_date_format
(
seat
.
upgrade_deadline
),
'credit_provider'
:
seat
.
credit_provider
,
# pylint: disable=no-member
'credit_hours'
:
seat
.
credit_hours
,
# pylint: disable=no-member
'sku'
:
seat
.
sku
'sku'
:
seat
.
sku
,
'bulk_sku'
:
seat
.
bulk_sku
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
...
...
course_discovery/apps/course_metadata/data_loaders/api.py
View file @
d3081bb2
...
...
@@ -257,6 +257,7 @@ class EcommerceApiDataLoader(AbstractDataLoader):
super
(
EcommerceApiDataLoader
,
self
)
.
__init__
(
partner
,
api_url
,
access_token
,
token_type
,
max_workers
,
is_threadsafe
,
**
kwargs
)
self
.
enrollment_skus
=
[]
self
.
entitlement_skus
=
[]
def
ingest
(
self
):
...
...
@@ -265,11 +266,14 @@ class EcommerceApiDataLoader(AbstractDataLoader):
initial_page
=
1
course_runs
=
self
.
_request_course_runs
(
initial_page
)
entitlements
=
self
.
_request_entitlments
(
initial_page
)
enrollment_codes
=
self
.
_request_enrollment_codes
(
initial_page
)
count
=
course_runs
[
'count'
]
+
entitlements
[
'count'
]
pages
=
math
.
ceil
(
count
/
self
.
PAGE_SIZE
)
self
.
entitlement_skus
=
[]
self
.
enrollment_skus
=
[]
self
.
_process_course_runs
(
course_runs
)
self
.
_process_entitlements
(
entitlements
)
self
.
_process_enrollment_codes
(
enrollment_codes
)
pagerange
=
range
(
initial_page
+
1
,
pages
+
1
)
...
...
@@ -284,9 +288,13 @@ class EcommerceApiDataLoader(AbstractDataLoader):
for
future
in
[
executor
.
submit
(
self
.
_request_entitlments
,
page
)
for
page
in
pagerange
]:
response
=
future
.
result
()
self
.
_process_entitlements
(
response
)
for
future
in
[
executor
.
submit
(
self
.
_request_enrollment_codes
,
page
)
for
page
in
pagerange
]:
response
=
future
.
result
()
self
.
_process_enrollment_codes
(
response
)
logger
.
info
(
'Retrieved
%
d course seats and
%
d course entitlements from
%
s.'
,
course_runs
[
'count'
],
entitlements
[
'count'
],
self
.
partner
.
ecommerce_api_url
)
logger
.
info
(
'Retrieved
%
d course seats,
%
d course entitlements, and
%
d course enrollment codes from
%
s.'
,
course_runs
[
'count'
],
entitlements
[
'count'
],
enrollment_codes
[
'count'
],
self
.
partner
.
ecommerce_api_url
)
self
.
delete_orphans
()
self
.
_delete_entitlements
()
...
...
@@ -297,6 +305,8 @@ class EcommerceApiDataLoader(AbstractDataLoader):
self
.
_process_course_runs
(
course_runs
)
entitlements
=
self
.
_request_entitlments
(
page
)
self
.
_process_entitlements
(
entitlements
)
enrollment_codes
=
self
.
_request_enrollment_codes
(
page
)
self
.
_process_enrollment_codes
(
enrollment_codes
)
def
_request_course_runs
(
self
,
page
):
return
self
.
api_client
.
courses
()
.
get
(
page
=
page
,
page_size
=
self
.
PAGE_SIZE
,
include_products
=
True
)
...
...
@@ -304,6 +314,9 @@ class EcommerceApiDataLoader(AbstractDataLoader):
def
_request_entitlments
(
self
,
page
):
return
self
.
api_client
.
products
()
.
get
(
page
=
page
,
page_size
=
self
.
PAGE_SIZE
,
product_class
=
'Course Entitlement'
)
def
_request_enrollment_codes
(
self
,
page
):
return
self
.
api_client
.
products
()
.
get
(
page
=
page
,
page_size
=
self
.
PAGE_SIZE
,
product_class
=
'Enrollment Code'
)
def
_process_course_runs
(
self
,
response
):
results
=
response
[
'results'
]
logger
.
info
(
'Retrieved
%
d course seats...'
,
len
(
results
))
...
...
@@ -320,6 +333,14 @@ class EcommerceApiDataLoader(AbstractDataLoader):
body
=
self
.
clean_strings
(
body
)
self
.
entitlement_skus
.
append
(
self
.
update_entitlement
(
body
))
def
_process_enrollment_codes
(
self
,
response
):
results
=
response
[
'results'
]
logger
.
info
(
'Retrieved
%
d course enrollment codes...'
,
len
(
results
))
for
body
in
results
:
body
=
self
.
clean_strings
(
body
)
self
.
enrollment_skus
.
append
(
self
.
update_enrollment_code
(
body
))
def
_delete_entitlements
(
self
):
entitlements_to_delete
=
CourseEntitlement
.
objects
.
filter
(
partner
=
self
.
partner
...
...
@@ -453,6 +474,73 @@ class EcommerceApiDataLoader(AbstractDataLoader):
course
.
entitlements
.
update_or_create
(
mode
=
mode
,
defaults
=
defaults
)
return
sku
def
update_enrollment_code
(
self
,
body
):
"""
Argument:
body (dict): enrollment code product data from ecommerce
Returns:
enrollment code product sku if no exceptions, else None
"""
attributes
=
{
attribute
[
'code'
]:
attribute
[
'value'
]
for
attribute
in
body
[
'attribute_values'
]}
course_key
=
attributes
.
get
(
'course_key'
)
title
=
body
[
'title'
]
if
body
[
'stockrecords'
]:
stock_record
=
body
[
'stockrecords'
][
0
]
else
:
msg
=
'Enrollment code product {title} has no stockrecords'
.
format
(
title
=
title
)
logger
.
warning
(
msg
)
return
None
try
:
currency_code
=
stock_record
[
'price_currency'
]
bulk_sku
=
stock_record
[
'partner_sku'
]
except
(
KeyError
,
ValueError
):
msg
=
'A necessary stockrecord field is missing or incorrectly set for enrollment code {title}'
.
format
(
title
=
title
)
logger
.
warning
(
msg
)
return
None
try
:
course_run
=
CourseRun
.
objects
.
get
(
key
=
course_key
)
except
CourseRun
.
DoesNotExist
:
msg
=
'Could not find course run {key} while loading enrollment code {title} with sku {sku}'
.
format
(
key
=
course_key
,
title
=
title
,
sku
=
bulk_sku
)
logger
.
warning
(
msg
)
return
None
try
:
Currency
.
objects
.
get
(
code
=
currency_code
)
except
Currency
.
DoesNotExist
:
msg
=
'Could not find currency {code} while loading enrollment code {title} with sku {sku}'
.
format
(
code
=
currency_code
,
title
=
title
,
sku
=
bulk_sku
)
logger
.
warning
(
msg
)
return
None
seat_type
=
attributes
.
get
(
'seat_type'
)
try
:
Seat
.
objects
.
get
(
course_run
=
course_run
,
type
=
seat_type
)
except
SeatType
.
DoesNotExist
:
msg
=
'Could not find seat type {type} while loading enrollment code {title} with sku {sku}'
.
format
(
type
=
seat_type
,
title
=
title
,
sku
=
bulk_sku
)
logger
.
warning
(
msg
)
return
None
defaults
=
{
'bulk_sku'
:
bulk_sku
}
msg
=
'Creating enrollment code {title} with sku {sku} for partner {partner}'
.
format
(
title
=
title
,
sku
=
bulk_sku
,
partner
=
self
.
partner
)
logger
.
info
(
msg
)
course_run
.
seats
.
update_or_create
(
type
=
seat_type
,
defaults
=
defaults
)
return
bulk_sku
def
get_certificate_type
(
self
,
product
):
return
next
(
(
att
[
'value'
]
for
att
in
product
[
'attribute_values'
]
if
att
[
'name'
]
==
'certificate_type'
),
...
...
course_discovery/apps/course_metadata/data_loaders/tests/mock_data.py
View file @
d3081bb2
...
...
@@ -231,6 +231,23 @@ ECOMMERCE_API_BODIES = [
"partner_sku"
:
"sku003"
,
}
]
},
{
"structure"
:
"standalone"
,
"expires"
:
"2017-01-01T12:00:00Z"
,
"attribute_values"
:
[
{
"code"
:
"seat_type"
,
"value"
:
"verified"
}
],
"stockrecords"
:
[
{
"price_currency"
:
"EUR"
,
"price_excl_tax"
:
"25.00"
,
"partner_sku"
:
"sku004"
}
]
}
]
},
...
...
@@ -255,7 +272,7 @@ ECOMMERCE_API_BODIES = [
{
"price_currency"
:
"USD"
,
"price_excl_tax"
:
"0.00"
,
"partner_sku"
:
"sku00
4
"
,
"partner_sku"
:
"sku00
5
"
,
}
]
},
...
...
@@ -272,7 +289,7 @@ ECOMMERCE_API_BODIES = [
{
"price_currency"
:
"USD"
,
"price_excl_tax"
:
"25.00"
,
"partner_sku"
:
"sku00
5
"
,
"partner_sku"
:
"sku00
6
"
,
}
]
},
...
...
@@ -301,7 +318,7 @@ ECOMMERCE_API_BODIES = [
{
"price_currency"
:
"USD"
,
"price_excl_tax"
:
"250.00"
,
"partner_sku"
:
"sku00
6
"
,
"partner_sku"
:
"sku00
7
"
,
}
]
},
...
...
@@ -330,7 +347,7 @@ ECOMMERCE_API_BODIES = [
{
"price_currency"
:
"USD"
,
"price_excl_tax"
:
"250.00"
,
"partner_sku"
:
"sku00
7
"
,
"partner_sku"
:
"sku00
8
"
,
}
]
}
...
...
@@ -355,7 +372,7 @@ ECOMMERCE_API_BODIES = [
{
"price_currency"
:
"123"
,
"price_excl_tax"
:
"0.00"
,
"partner_sku"
:
"sku00
8
"
,
"partner_sku"
:
"sku00
9
"
,
}
]
}
...
...
@@ -380,7 +397,7 @@ ECOMMERCE_API_BODIES = [
{
"price_currency"
:
"USD"
,
"price_excl_tax"
:
"0.00"
,
"partner_sku"
:
"sku0
09
"
,
"partner_sku"
:
"sku0
10
"
,
}
]
}
...
...
course_discovery/apps/course_metadata/data_loaders/tests/test_api.py
View file @
d3081bb2
This diff is collapsed.
Click to expand it.
course_discovery/apps/course_metadata/migrations/0080_seat_bulk_sku.py
0 → 100644
View file @
d3081bb2
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2018-03-09 19:38
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'course_metadata'
,
'0079_enable_program_default_true'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'seat'
,
name
=
'bulk_sku'
,
field
=
models
.
CharField
(
blank
=
True
,
max_length
=
128
,
null
=
True
),
),
]
course_discovery/apps/course_metadata/models.py
View file @
d3081bb2
...
...
@@ -832,6 +832,7 @@ class Seat(TimeStampedModel):
credit_provider
=
models
.
CharField
(
max_length
=
255
,
null
=
True
,
blank
=
True
)
credit_hours
=
models
.
IntegerField
(
null
=
True
,
blank
=
True
)
sku
=
models
.
CharField
(
max_length
=
128
,
null
=
True
,
blank
=
True
)
bulk_sku
=
models
.
CharField
(
max_length
=
128
,
null
=
True
,
blank
=
True
)
class
Meta
(
object
):
unique_together
=
(
...
...
course_discovery/apps/course_metadata/tests/factories.py
View file @
d3081bb2
...
...
@@ -155,6 +155,7 @@ class SeatFactory(factory.DjangoModelFactory):
currency
=
factory
.
Iterator
(
Currency
.
objects
.
all
())
upgrade_deadline
=
FuzzyDateTime
(
datetime
.
datetime
(
2014
,
1
,
1
,
tzinfo
=
UTC
))
sku
=
FuzzyText
(
length
=
8
)
bulk_sku
=
FuzzyText
(
length
=
8
)
course_run
=
factory
.
SubFactory
(
CourseRunFactory
)
class
Meta
:
...
...
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