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
ad501533
Commit
ad501533
authored
Aug 05, 2016
by
Clinton Blackburn
Committed by
GitHub
Aug 05, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Updated Program model with MicroMasters fields (#196)
ECOM-5094
parent
ae5dcc2c
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
356 additions
and
103 deletions
+356
-103
course_discovery/apps/api/serializers.py
+3
-3
course_discovery/apps/api/tests/test_serializers.py
+8
-5
course_discovery/apps/course_metadata/admin.py
+34
-5
course_discovery/apps/course_metadata/data_loaders.py
+8
-19
course_discovery/apps/course_metadata/migrations/0011_auto_20160805_1949.py
+0
-0
course_discovery/apps/course_metadata/migrations/0012_create_seat_types.py
+28
-0
course_discovery/apps/course_metadata/models.py
+128
-42
course_discovery/apps/course_metadata/search_indexes.py
+13
-2
course_discovery/apps/course_metadata/tests/factories.py
+64
-3
course_discovery/apps/course_metadata/tests/test_data_loaders.py
+4
-12
course_discovery/apps/course_metadata/tests/test_models.py
+64
-11
requirements/base.txt
+2
-1
No files found.
course_discovery/apps/api/serializers.py
View file @
ad501533
...
@@ -50,8 +50,8 @@ PROGRAM_FACET_FIELD_OPTIONS = {
...
@@ -50,8 +50,8 @@ PROGRAM_FACET_FIELD_OPTIONS = {
}
}
PROGRAM_SEARCH_FIELDS
=
(
PROGRAM_SEARCH_FIELDS
=
(
'text'
,
'uuid'
,
'title'
,
'subtitle'
,
'category'
,
'marketing_url'
,
'organizations'
,
'content_type'
,
'
image_url
'
,
'text'
,
'uuid'
,
'title'
,
'subtitle'
,
'category'
,
'marketing_url'
,
'organizations'
,
'content_type'
,
'
status
'
,
'
status
'
,
'
card_image_url
'
,
)
)
...
@@ -264,7 +264,7 @@ class ContainedCoursesSerializer(serializers.Serializer):
...
@@ -264,7 +264,7 @@ class ContainedCoursesSerializer(serializers.Serializer):
class
ProgramSerializer
(
serializers
.
ModelSerializer
):
class
ProgramSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
class
Meta
:
model
=
Program
model
=
Program
fields
=
(
'uuid'
,
'title'
,
'subtitle'
,
'category'
,
'marketing_slug'
,
'marketing_url'
,
'image_url'
,)
fields
=
(
'uuid'
,
'title'
,
'subtitle'
,
'category'
,
'marketing_slug'
,
'marketing_url'
,
'
card_
image_url'
,)
read_only_fields
=
(
'uuid'
,
'marketing_url'
,)
read_only_fields
=
(
'uuid'
,
'marketing_url'
,)
...
...
course_discovery/apps/api/tests/test_serializers.py
View file @
ad501533
...
@@ -181,7 +181,7 @@ class ProgramSerializerTests(TestCase):
...
@@ -181,7 +181,7 @@ class ProgramSerializerTests(TestCase):
'category'
:
program
.
category
,
'category'
:
program
.
category
,
'marketing_slug'
:
program
.
marketing_slug
,
'marketing_slug'
:
program
.
marketing_slug
,
'marketing_url'
:
program
.
marketing_url
,
'marketing_url'
:
program
.
marketing_url
,
'
image_url'
:
program
.
image_url
,
'
card_image_url'
:
program
.
card_
image_url
,
}
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
...
@@ -386,9 +386,12 @@ class CourseRunSearchSerializerTests(TestCase):
...
@@ -386,9 +386,12 @@ class CourseRunSearchSerializerTests(TestCase):
class
ProgramSearchSerializerTests
(
TestCase
):
class
ProgramSearchSerializerTests
(
TestCase
):
def
test_data
(
self
):
def
test_data
(
self
):
program
=
ProgramFactory
()
program
=
ProgramFactory
()
organization
=
OrganizationFactory
()
authoring_organization
,
crediting_organization
=
OrganizationFactory
.
create_batch
(
2
)
program
.
organizations
.
add
(
organization
)
program
.
authoring_organizations
.
add
(
authoring_organization
)
program
.
credit_backing_organizations
.
add
(
crediting_organization
)
program
.
save
()
program
.
save
()
expected_organizations
=
[
OrganizationsMixin
.
format_organization
(
org
)
for
org
in
(
authoring_organization
,
crediting_organization
)]
# NOTE: This serializer expects SearchQuerySet results, so we run a search on the newly-created object
# NOTE: This serializer expects SearchQuerySet results, so we run a search on the newly-created object
# to generate such a result.
# to generate such a result.
...
@@ -401,9 +404,9 @@ class ProgramSearchSerializerTests(TestCase):
...
@@ -401,9 +404,9 @@ class ProgramSearchSerializerTests(TestCase):
'subtitle'
:
program
.
subtitle
,
'subtitle'
:
program
.
subtitle
,
'category'
:
program
.
category
,
'category'
:
program
.
category
,
'marketing_url'
:
program
.
marketing_url
,
'marketing_url'
:
program
.
marketing_url
,
'organizations'
:
[
OrganizationsMixin
.
format_organization
(
organization
)]
,
'organizations'
:
expected_organizations
,
'content_type'
:
'program'
,
'content_type'
:
'program'
,
'
image_url'
:
program
.
image_url
,
'
card_image_url'
:
program
.
card_
image_url
,
'status'
:
program
.
status
,
'status'
:
program
.
status
,
}
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
course_discovery/apps/course_metadata/admin.py
View file @
ad501533
from
django.contrib
import
admin
from
django.contrib
import
admin
from
course_discovery.apps.course_metadata.models
import
(
from
course_discovery.apps.course_metadata.models
import
(
Seat
,
Image
,
Video
,
LevelType
,
Subject
,
Prerequisite
,
ExpectedLearningItem
,
Expertise
,
Seat
,
Image
,
Video
,
LevelType
,
Subject
,
Prerequisite
,
ExpectedLearningItem
,
Expertise
,
Course
,
CourseRun
,
Course
,
CourseRun
,
CourseRunSocialNetwork
,
MajorWork
,
Organization
,
Person
,
PersonSocialNetwork
,
CourseRunSocialNetwork
,
MajorWork
,
Organization
,
Person
,
PersonSocialNetwork
,
CourseOrganization
,
SyllabusItem
,
CourseOrganization
,
SyllabusItem
,
Program
Program
,
JobOutlookItem
,
SeatType
,
Endorsement
,
CorporateEndorsement
,
FAQ
,
ProgramType
)
)
class
CourseOrganizationInline
(
admin
.
TabularInline
):
class
CourseOrganizationInline
(
admin
.
TabularInline
):
...
@@ -21,6 +20,7 @@ class SeatInline(admin.TabularInline):
...
@@ -21,6 +20,7 @@ class SeatInline(admin.TabularInline):
class
CourseAdmin
(
admin
.
ModelAdmin
):
class
CourseAdmin
(
admin
.
ModelAdmin
):
inlines
=
(
CourseOrganizationInline
,)
inlines
=
(
CourseOrganizationInline
,)
list_display
=
(
'key'
,
'title'
,)
list_display
=
(
'key'
,
'title'
,)
list_filter
=
(
'partner'
,)
ordering
=
(
'key'
,
'title'
,)
ordering
=
(
'key'
,
'title'
,)
search_fields
=
(
'key'
,
'title'
,)
search_fields
=
(
'key'
,
'title'
,)
...
@@ -36,10 +36,38 @@ class CourseRunAdmin(admin.ModelAdmin):
...
@@ -36,10 +36,38 @@ class CourseRunAdmin(admin.ModelAdmin):
@admin.register
(
Program
)
@admin.register
(
Program
)
class
ProgramAdmin
(
admin
.
ModelAdmin
):
class
ProgramAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'uuid'
,
'title'
,)
list_display
=
(
'uuid'
,
'title'
,)
list_filter
=
(
'partner'
,)
ordering
=
(
'uuid'
,
'title'
,)
ordering
=
(
'uuid'
,
'title'
,)
readonly_fields
=
(
'uuid'
,)
search_fields
=
(
'uuid'
,
'title'
,
'marketing_slug'
)
search_fields
=
(
'uuid'
,
'title'
,
'marketing_slug'
)
@admin.register
(
ProgramType
)
class
ProgramTypeAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'name'
,)
@admin.register
(
SeatType
)
class
SeatTypeAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'name'
,
'slug'
,)
readonly_fields
=
(
'slug'
,)
@admin.register
(
Endorsement
)
class
EndorsementAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'endorser'
,)
@admin.register
(
CorporateEndorsement
)
class
CorporateEndorsementAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'corporation_name'
,)
@admin.register
(
FAQ
)
class
FAQAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'question'
,)
class
KeyNameAdmin
(
admin
.
ModelAdmin
):
class
KeyNameAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'key'
,
'name'
,)
list_display
=
(
'key'
,
'name'
,)
ordering
=
(
'key'
,
'name'
,)
ordering
=
(
'key'
,
'name'
,)
...
@@ -61,5 +89,6 @@ for model in (LevelType, Subject, Prerequisite, Expertise, MajorWork):
...
@@ -61,5 +89,6 @@ for model in (LevelType, Subject, Prerequisite, Expertise, MajorWork):
admin
.
site
.
register
(
model
,
NamedModelAdmin
)
admin
.
site
.
register
(
model
,
NamedModelAdmin
)
# Register remaining models using basic ModelAdmin classes
# Register remaining models using basic ModelAdmin classes
for
model
in
(
Image
,
Video
,
ExpectedLearningItem
,
SyllabusItem
,
PersonSocialNetwork
,
CourseRunSocialNetwork
):
for
model
in
(
Image
,
Video
,
ExpectedLearningItem
,
SyllabusItem
,
PersonSocialNetwork
,
CourseRunSocialNetwork
,
JobOutlookItem
,):
admin
.
site
.
register
(
model
)
admin
.
site
.
register
(
model
)
course_discovery/apps/course_metadata/data_loaders.py
View file @
ad501533
...
@@ -307,7 +307,8 @@ class DrupalApiDataLoader(AbstractDataLoader):
...
@@ -307,7 +307,8 @@ class DrupalApiDataLoader(AbstractDataLoader):
# Clean Organizations separately from other orphaned instances to avoid removing all orgnaziations
# Clean Organizations separately from other orphaned instances to avoid removing all orgnaziations
# after an initial data load on an empty table.
# after an initial data load on an empty table.
Organization
.
objects
.
filter
(
courseorganization__isnull
=
True
,
program__isnull
=
True
)
.
delete
()
Organization
.
objects
.
filter
(
courseorganization__isnull
=
True
,
authored_programs__isnull
=
True
,
credit_backed_programs__isnull
=
True
)
.
delete
()
self
.
delete_orphans
()
self
.
delete_orphans
()
logger
.
info
(
'Retrieved
%
d course runs from
%
s.'
,
len
(
data
),
api_url
)
logger
.
info
(
'Retrieved
%
d course runs from
%
s.'
,
len
(
data
),
api_url
)
...
@@ -545,7 +546,7 @@ class ProgramsApiDataLoader(AbstractDataLoader):
...
@@ -545,7 +546,7 @@ class ProgramsApiDataLoader(AbstractDataLoader):
'category'
:
body
[
'category'
],
'category'
:
body
[
'category'
],
'status'
:
body
[
'status'
],
'status'
:
body
[
'status'
],
'marketing_slug'
:
body
[
'marketing_slug'
],
'marketing_slug'
:
body
[
'marketing_slug'
],
'
image'
:
self
.
_get_image
(
body
),
'
banner_image_url'
:
self
.
_get_banner_image_url
(
body
),
'partner'
:
self
.
partner
,
'partner'
:
self
.
partner
,
}
}
...
@@ -558,24 +559,15 @@ class ProgramsApiDataLoader(AbstractDataLoader):
...
@@ -558,24 +559,15 @@ class ProgramsApiDataLoader(AbstractDataLoader):
)
)
organizations
.
append
(
organization
)
organizations
.
append
(
organization
)
program
.
organizations
.
clear
()
program
.
authoring_
organizations
.
clear
()
program
.
organizations
.
add
(
*
organizations
)
program
.
authoring_
organizations
.
add
(
*
organizations
)
except
Exception
:
# pylint: disable=broad-except
except
Exception
:
# pylint: disable=broad-except
logger
.
exception
(
'Failed to load program
%
s'
,
uuid
)
logger
.
exception
(
'Failed to load program
%
s'
,
uuid
)
def
_get_image
(
self
,
body
):
def
_get_banner_image_url
(
self
,
body
):
image
=
None
image_key
=
'w{width}h{height}'
.
format
(
width
=
self
.
image_width
,
height
=
self
.
image_height
)
image_key
=
'w{width}h{height}'
.
format
(
width
=
self
.
image_width
,
height
=
self
.
image_height
)
image_url
=
body
.
get
(
'banner_image_urls'
,
{})
.
get
(
image_key
)
image_url
=
body
.
get
(
'banner_image_urls'
,
{})
.
get
(
image_key
)
return
image_url
if
image_url
:
defaults
=
{
'width'
:
self
.
image_width
,
'height'
:
self
.
image_height
,
}
image
,
__
=
Image
.
objects
.
update_or_create
(
src
=
image_url
,
defaults
=
defaults
)
return
image
class
MarketingSiteDataLoader
(
AbstractDataLoader
):
class
MarketingSiteDataLoader
(
AbstractDataLoader
):
...
@@ -667,10 +659,7 @@ class MarketingSiteDataLoader(AbstractDataLoader):
...
@@ -667,10 +659,7 @@ class MarketingSiteDataLoader(AbstractDataLoader):
'subtitle'
:
data
.
get
(
'field_xseries_subtitle_short'
),
'subtitle'
:
data
.
get
(
'field_xseries_subtitle_short'
),
'category'
:
'XSeries'
,
'category'
:
'XSeries'
,
'partner'
:
self
.
partner
,
'partner'
:
self
.
partner
,
'card_image_url'
:
card_image_url
,
}
}
if
card_image_url
:
card_image
,
__
=
Image
.
objects
.
get_or_create
(
src
=
card_image_url
)
defaults
[
'image'
]
=
card_image
Program
.
objects
.
update_or_create
(
marketing_slug
=
marketing_slug
,
defaults
=
defaults
)
Program
.
objects
.
update_or_create
(
marketing_slug
=
marketing_slug
,
defaults
=
defaults
)
course_discovery/apps/course_metadata/migrations/0011_auto_20160805_1949.py
0 → 100644
View file @
ad501533
This diff is collapsed.
Click to expand it.
course_discovery/apps/course_metadata/migrations/0012_create_seat_types.py
0 → 100644
View file @
ad501533
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
SEAT_TYPES
=
(
'Audit'
,
'Credit'
,
'Professional'
,
'Verified'
,)
def
add_seat_types
(
apps
,
schema_editor
):
SeatType
=
apps
.
get_model
(
'course_metadata'
,
'SeatType'
)
for
name
in
SEAT_TYPES
:
SeatType
.
objects
.
update_or_create
(
name
=
name
)
def
drop_seat_types
(
apps
,
schema_editor
):
SeatType
=
apps
.
get_model
(
'course_metadata'
,
'SeatType'
)
SeatType
.
objects
.
filter
(
name__in
=
SEAT_TYPES
)
.
delete
()
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'course_metadata'
,
'0011_auto_20160805_1949'
),
]
operations
=
[
migrations
.
RunPython
(
add_seat_types
,
drop_seat_types
)
]
course_discovery/apps/course_metadata/models.py
View file @
ad501533
This diff is collapsed.
Click to expand it.
course_discovery/apps/course_metadata/search_indexes.py
View file @
ad501533
...
@@ -106,7 +106,7 @@ class CourseRunIndex(BaseCourseIndex, indexes.Indexable):
...
@@ -106,7 +106,7 @@ class CourseRunIndex(BaseCourseIndex, indexes.Indexable):
return
[
self
.
_prepare_language
(
language
)
for
language
in
obj
.
transcript_languages
.
all
()]
return
[
self
.
_prepare_language
(
language
)
for
language
in
obj
.
transcript_languages
.
all
()]
class
ProgramIndex
(
OrganizationsMixin
,
BaseIndex
,
indexes
.
Indexable
):
class
ProgramIndex
(
BaseIndex
,
indexes
.
Indexable
,
OrganizationsMixin
):
model
=
Program
model
=
Program
uuid
=
indexes
.
CharField
(
model_attr
=
'uuid'
)
uuid
=
indexes
.
CharField
(
model_attr
=
'uuid'
)
...
@@ -115,9 +115,20 @@ class ProgramIndex(OrganizationsMixin, BaseIndex, indexes.Indexable):
...
@@ -115,9 +115,20 @@ class ProgramIndex(OrganizationsMixin, BaseIndex, indexes.Indexable):
category
=
indexes
.
CharField
(
model_attr
=
'category'
,
faceted
=
True
)
category
=
indexes
.
CharField
(
model_attr
=
'category'
,
faceted
=
True
)
marketing_url
=
indexes
.
CharField
(
null
=
True
)
marketing_url
=
indexes
.
CharField
(
null
=
True
)
organizations
=
indexes
.
MultiValueField
(
faceted
=
True
)
organizations
=
indexes
.
MultiValueField
(
faceted
=
True
)
image_url
=
indexes
.
CharField
(
model_attr
=
'image_url'
,
null
=
True
)
authoring_organizations
=
indexes
.
MultiValueField
(
faceted
=
True
)
credit_backing_organizations
=
indexes
.
MultiValueField
(
faceted
=
True
)
card_image_url
=
indexes
.
CharField
(
model_attr
=
'card_image_url'
,
null
=
True
)
status
=
indexes
.
CharField
(
model_attr
=
'status'
,
faceted
=
True
)
status
=
indexes
.
CharField
(
model_attr
=
'status'
,
faceted
=
True
)
partner
=
indexes
.
CharField
(
model_attr
=
'partner__name'
,
null
=
True
,
faceted
=
True
)
partner
=
indexes
.
CharField
(
model_attr
=
'partner__name'
,
null
=
True
,
faceted
=
True
)
def
prepare_organizations
(
self
,
obj
):
return
self
.
prepare_authoring_organizations
(
obj
)
+
self
.
prepare_credit_backing_organizations
(
obj
)
def
prepare_authoring_organizations
(
self
,
obj
):
return
[
self
.
format_organization
(
organization
)
for
organization
in
obj
.
authoring_organizations
.
all
()]
def
prepare_credit_backing_organizations
(
self
,
obj
):
return
[
self
.
format_organization
(
organization
)
for
organization
in
obj
.
credit_backing_organizations
.
all
()]
def
prepare_marketing_url
(
self
,
obj
):
def
prepare_marketing_url
(
self
,
obj
):
return
obj
.
marketing_url
return
obj
.
marketing_url
course_discovery/apps/course_metadata/tests/factories.py
View file @
ad501533
...
@@ -7,16 +7,18 @@ from factory.fuzzy import (
...
@@ -7,16 +7,18 @@ from factory.fuzzy import (
)
)
from
pytz
import
UTC
from
pytz
import
UTC
from
course_discovery.apps.core.models
import
Currency
from
course_discovery.apps.core.tests.factories
import
PartnerFactory
from
course_discovery.apps.core.tests.factories
import
PartnerFactory
from
course_discovery.apps.core.tests.utils
import
FuzzyURL
from
course_discovery.apps.core.tests.utils
import
FuzzyURL
from
course_discovery.apps.core.models
import
Currency
from
course_discovery.apps.course_metadata.models
import
(
from
course_discovery.apps.course_metadata.models
import
(
Course
,
CourseRun
,
Organization
,
Person
,
Image
,
Video
,
Subject
,
Seat
,
Prerequisite
,
LevelType
,
Program
,
Course
,
CourseRun
,
Organization
,
Person
,
Image
,
Video
,
Subject
,
Seat
,
Prerequisite
,
LevelType
,
Program
,
AbstractSocialNetworkModel
,
CourseRunSocialNetwork
,
PersonSocialNetwork
AbstractSocialNetworkModel
,
CourseRunSocialNetwork
,
PersonSocialNetwork
,
ProgramType
)
)
from
course_discovery.apps.ietf_language_tags.models
import
LanguageTag
from
course_discovery.apps.ietf_language_tags.models
import
LanguageTag
# pylint: disable=no-member, unused-argument
class
AbstractMediaModelFactory
(
factory
.
DjangoModelFactory
):
class
AbstractMediaModelFactory
(
factory
.
DjangoModelFactory
):
src
=
FuzzyURL
()
src
=
FuzzyURL
()
description
=
FuzzyText
()
description
=
FuzzyText
()
...
@@ -80,6 +82,16 @@ class CourseFactory(factory.DjangoModelFactory):
...
@@ -80,6 +82,16 @@ class CourseFactory(factory.DjangoModelFactory):
class
Meta
:
class
Meta
:
model
=
Course
model
=
Course
@factory.post_generation
def
subjects
(
self
,
create
,
extracted
,
**
kwargs
):
if
not
create
:
# pragma: no cover
# Simple build, do nothing.
return
if
extracted
:
for
subject
in
extracted
:
self
.
subjects
.
add
(
subject
)
class
CourseRunFactory
(
factory
.
DjangoModelFactory
):
class
CourseRunFactory
(
factory
.
DjangoModelFactory
):
key
=
FuzzyText
(
prefix
=
'course-run-id/'
,
suffix
=
'/fake'
)
key
=
FuzzyText
(
prefix
=
'course-run-id/'
,
suffix
=
'/fake'
)
...
@@ -103,6 +115,16 @@ class CourseRunFactory(factory.DjangoModelFactory):
...
@@ -103,6 +115,16 @@ class CourseRunFactory(factory.DjangoModelFactory):
class
Meta
:
class
Meta
:
model
=
CourseRun
model
=
CourseRun
@factory.post_generation
def
transcript_languages
(
self
,
create
,
extracted
,
**
kwargs
):
if
not
create
:
# pragma: no cover
# Simple build, do nothing.
return
if
extracted
:
for
transcript_language
in
extracted
:
self
.
transcript_languages
.
add
(
transcript_language
)
class
OrganizationFactory
(
factory
.
DjangoModelFactory
):
class
OrganizationFactory
(
factory
.
DjangoModelFactory
):
key
=
FuzzyText
(
prefix
=
'Org.fake/'
)
key
=
FuzzyText
(
prefix
=
'Org.fake/'
)
...
@@ -127,6 +149,23 @@ class PersonFactory(factory.DjangoModelFactory):
...
@@ -127,6 +149,23 @@ class PersonFactory(factory.DjangoModelFactory):
model
=
Person
model
=
Person
class
ProgramTypeFactory
(
factory
.
django
.
DjangoModelFactory
):
class
Meta
(
object
):
model
=
ProgramType
name
=
FuzzyText
()
@factory.post_generation
def
applicable_seat_types
(
self
,
create
,
extracted
,
**
kwargs
):
if
not
create
:
# pragma: no cover
# Simple build, do nothing.
return
if
extracted
:
for
seat_type
in
extracted
:
self
.
applicable_seat_types
.
add
(
seat_type
)
class
ProgramFactory
(
factory
.
django
.
DjangoModelFactory
):
class
ProgramFactory
(
factory
.
django
.
DjangoModelFactory
):
class
Meta
(
object
):
class
Meta
(
object
):
model
=
Program
model
=
Program
...
@@ -137,9 +176,31 @@ class ProgramFactory(factory.django.DjangoModelFactory):
...
@@ -137,9 +176,31 @@ class ProgramFactory(factory.django.DjangoModelFactory):
category
=
'xseries'
category
=
'xseries'
status
=
'unpublished'
status
=
'unpublished'
marketing_slug
=
factory
.
Sequence
(
lambda
n
:
'test-slug-{}'
.
format
(
n
))
# pylint: disable=unnecessary-lambda
marketing_slug
=
factory
.
Sequence
(
lambda
n
:
'test-slug-{}'
.
format
(
n
))
# pylint: disable=unnecessary-lambda
image
=
factory
.
SubFactory
(
ImageFactory
)
banner_image_url
=
FuzzyText
(
prefix
=
'https://example.com/program/banner'
)
card_image_url
=
FuzzyText
(
prefix
=
'https://example.com/program/card'
)
partner
=
factory
.
SubFactory
(
PartnerFactory
)
partner
=
factory
.
SubFactory
(
PartnerFactory
)
@factory.post_generation
def
courses
(
self
,
create
,
extracted
,
**
kwargs
):
if
not
create
:
# pragma: no cover
# Simple build, do nothing.
return
if
extracted
:
# Use the passed in list of courses
for
course
in
extracted
:
self
.
courses
.
add
(
course
)
@factory.post_generation
def
excluded_course_runs
(
self
,
create
,
extracted
,
**
kwargs
):
if
not
create
:
# pragma: no cover
# Simple build, do nothing.
return
if
extracted
:
for
course_run
in
extracted
:
self
.
excluded_course_runs
.
add
(
course_run
)
class
AbstractSocialNetworkModelFactory
(
factory
.
DjangoModelFactory
):
class
AbstractSocialNetworkModelFactory
(
factory
.
DjangoModelFactory
):
type
=
FuzzyChoice
([
name
for
name
,
__
in
AbstractSocialNetworkModel
.
SOCIAL_NETWORK_CHOICES
])
type
=
FuzzyChoice
([
name
for
name
,
__
in
AbstractSocialNetworkModel
.
SOCIAL_NETWORK_CHOICES
])
...
...
course_discovery/apps/course_metadata/tests/test_data_loaders.py
View file @
ad501533
...
@@ -636,13 +636,10 @@ class ProgramsApiDataLoaderTests(ApiClientTestMixin, DataLoaderTestMixin, TestCa
...
@@ -636,13 +636,10 @@ class ProgramsApiDataLoaderTests(ApiClientTestMixin, DataLoaderTestMixin, TestCa
keys
=
[
org
[
'key'
]
for
org
in
body
[
'organizations'
]]
keys
=
[
org
[
'key'
]
for
org
in
body
[
'organizations'
]]
expected_organizations
=
list
(
Organization
.
objects
.
filter
(
key__in
=
keys
))
expected_organizations
=
list
(
Organization
.
objects
.
filter
(
key__in
=
keys
))
self
.
assertEqual
(
keys
,
[
org
.
key
for
org
in
expected_organizations
])
self
.
assertEqual
(
keys
,
[
org
.
key
for
org
in
expected_organizations
])
self
.
assertListEqual
(
list
(
program
.
organizations
.
all
()),
expected_organizations
)
self
.
assertListEqual
(
list
(
program
.
authoring_
organizations
.
all
()),
expected_organizations
)
image_url
=
body
.
get
(
'banner_image_urls'
,
{})
.
get
(
'w435h145'
)
banner_image_url
=
body
.
get
(
'banner_image_urls'
,
{})
.
get
(
'w435h145'
)
if
image_url
:
self
.
assertEqual
(
program
.
banner_image_url
,
banner_image_url
)
image
=
Image
.
objects
.
get
(
src
=
image_url
,
width
=
self
.
loader
.
image_width
,
height
=
self
.
loader
.
image_height
)
self
.
assertEqual
(
program
.
image
,
image
)
@responses.activate
@responses.activate
def
test_ingest
(
self
):
def
test_ingest
(
self
):
...
@@ -736,12 +733,7 @@ class MarketingSiteDataLoaderTests(DataLoaderTestMixin, TestCase):
...
@@ -736,12 +733,7 @@ class MarketingSiteDataLoaderTests(DataLoaderTestMixin, TestCase):
self
.
assertEqual
(
program
.
partner
,
self
.
partner
)
self
.
assertEqual
(
program
.
partner
,
self
.
partner
)
card_image_url
=
data
.
get
(
'field_card_image'
,
{})
.
get
(
'url'
)
card_image_url
=
data
.
get
(
'field_card_image'
,
{})
.
get
(
'url'
)
self
.
assertEqual
(
program
.
card_image_url
,
card_image_url
)
if
card_image_url
:
card_image
=
Image
.
objects
.
get
(
src
=
card_image_url
)
self
.
assertEqual
(
program
.
image
,
card_image
)
else
:
self
.
assertIsNone
(
program
.
image
)
def
test_constructor_without_credentials
(
self
):
def
test_constructor_without_credentials
(
self
):
""" Verify the constructor raises an exception if the Partner has no marketing site credentials set. """
""" Verify the constructor raises an exception if the Partner has no marketing site credentials set. """
...
...
course_discovery/apps/course_metadata/tests/test_models.py
View file @
ad501533
import
datetime
import
datetime
import
itertools
from
decimal
import
Decimal
import
ddt
import
ddt
import
mock
import
mock
import
pytz
import
pytz
from
dateutil.parser
import
parse
from
dateutil.parser
import
parse
from
django.db
import
IntegrityError
from
django.db
import
IntegrityError
from
django.test
import
TestCase
from
django.test
import
TestCase
from
freezegun
import
freeze_time
from
freezegun
import
freeze_time
from
course_discovery.apps.core.models
import
Currency
from
course_discovery.apps.core.utils
import
SearchQuerySetWrapper
from
course_discovery.apps.core.utils
import
SearchQuerySetWrapper
from
course_discovery.apps.course_metadata.models
import
(
from
course_discovery.apps.course_metadata.models
import
(
AbstractNamedModel
,
AbstractMediaModel
,
AbstractValueModel
,
CourseOrganization
,
Course
,
CourseRun
AbstractNamedModel
,
AbstractMediaModel
,
AbstractValueModel
,
CourseOrganization
,
Course
,
CourseRun
,
)
SeatType
)
from
course_discovery.apps.course_metadata.tests
import
factories
from
course_discovery.apps.course_metadata.tests
import
factories
from
course_discovery.apps.ietf_language_tags.models
import
LanguageTag
# pylint: disable=no-member
# pylint: disable=no-member
class
CourseTests
(
TestCase
):
class
CourseTests
(
TestCase
):
""" Tests for the `Course` model. """
""" Tests for the `Course` model. """
...
@@ -270,7 +274,13 @@ class ProgramTests(TestCase):
...
@@ -270,7 +274,13 @@ class ProgramTests(TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
ProgramTests
,
self
)
.
setUp
()
super
(
ProgramTests
,
self
)
.
setUp
()
self
.
program
=
factories
.
ProgramFactory
()
transcript_languages
=
LanguageTag
.
objects
.
all
()[:
2
]
subjects
=
factories
.
SubjectFactory
.
create_batch
(
2
)
self
.
course_runs
=
factories
.
CourseRunFactory
.
create_batch
(
3
,
transcript_languages
=
transcript_languages
,
course__subjects
=
subjects
)
self
.
courses
=
[
course_run
.
course
for
course_run
in
self
.
course_runs
]
self
.
excluded_course_run
=
factories
.
CourseRunFactory
(
course
=
self
.
courses
[
0
])
self
.
program
=
factories
.
ProgramFactory
(
courses
=
self
.
courses
,
excluded_course_runs
=
[
self
.
excluded_course_run
])
def
test_str
(
self
):
def
test_str
(
self
):
"""Verify that a program is properly converted to a str."""
"""Verify that a program is properly converted to a str."""
...
@@ -287,13 +297,56 @@ class ProgramTests(TestCase):
...
@@ -287,13 +297,56 @@ class ProgramTests(TestCase):
self
.
program
.
marketing_slug
=
''
self
.
program
.
marketing_slug
=
''
self
.
assertIsNone
(
self
.
program
.
marketing_url
)
self
.
assertIsNone
(
self
.
program
.
marketing_url
)
def
test_image_url
(
self
):
def
test_course_runs
(
self
):
""" Verify the property returns the associated image's URL. """
""" Verify the property returns the set of associated CourseRuns minus those that are explicitly excluded. """
self
.
assertEqual
(
self
.
program
.
image_url
,
self
.
program
.
image
.
src
)
self
.
assertEqual
(
set
(
self
.
program
.
course_runs
),
set
(
self
.
course_runs
))
self
.
program
.
image
=
None
def
test_languages
(
self
):
self
.
assertIsNone
(
self
.
program
.
image
)
expected_languages
=
set
([
course_run
.
language
for
course_run
in
self
.
course_runs
])
self
.
assertIsNone
(
self
.
program
.
image_url
)
actual_languages
=
self
.
program
.
languages
self
.
assertGreater
(
len
(
actual_languages
),
0
)
self
.
assertEqual
(
actual_languages
,
expected_languages
)
def
test_transcript_languages
(
self
):
expected_transcript_languages
=
itertools
.
chain
.
from_iterable
(
[
list
(
course_run
.
transcript_languages
.
all
())
for
course_run
in
self
.
course_runs
])
expected_transcript_languages
=
set
(
expected_transcript_languages
)
actual_transcript_languages
=
self
.
program
.
transcript_languages
self
.
assertGreater
(
len
(
actual_transcript_languages
),
0
)
self
.
assertEqual
(
actual_transcript_languages
,
expected_transcript_languages
)
def
test_subjects
(
self
):
expected_subjects
=
itertools
.
chain
.
from_iterable
([
list
(
course
.
subjects
.
all
())
for
course
in
self
.
courses
])
expected_subjects
=
set
(
expected_subjects
)
actual_subjects
=
self
.
program
.
subjects
self
.
assertGreater
(
len
(
actual_subjects
),
0
)
self
.
assertEqual
(
actual_subjects
,
expected_subjects
)
def
test_start
(
self
):
expected_start
=
min
([
course_run
.
start
for
course_run
in
self
.
course_runs
])
self
.
assertEqual
(
self
.
program
.
start
,
expected_start
)
def
test_price_ranges
(
self
):
currency
=
Currency
.
objects
.
get
(
code
=
'USD'
)
course_run
=
factories
.
CourseRunFactory
()
factories
.
SeatFactory
(
type
=
'audit'
,
currency
=
currency
,
course_run
=
course_run
,
price
=
0
)
factories
.
SeatFactory
(
type
=
'credit'
,
currency
=
currency
,
course_run
=
course_run
,
price
=
600
)
factories
.
SeatFactory
(
type
=
'verified'
,
currency
=
currency
,
course_run
=
course_run
,
price
=
100
)
applicable_seat_types
=
SeatType
.
objects
.
filter
(
slug__in
=
[
'credit'
,
'verified'
])
program_type
=
factories
.
ProgramTypeFactory
(
name
=
'XSeries'
,
applicable_seat_types
=
applicable_seat_types
)
program
=
factories
.
ProgramFactory
(
type
=
program_type
,
courses
=
[
course_run
.
course
])
expected_price_ranges
=
[{
'currency'
:
'USD'
,
'min'
:
Decimal
(
100
),
'max'
:
Decimal
(
600
)}]
self
.
assertEqual
(
program
.
price_ranges
,
expected_price_ranges
)
def
test_instructors
(
self
):
instructors
=
factories
.
PersonFactory
.
create_batch
(
2
)
self
.
course_runs
[
0
]
.
instructors
.
add
(
instructors
[
0
])
self
.
course_runs
[
1
]
.
instructors
.
add
(
instructors
[
1
])
self
.
assertEqual
(
self
.
program
.
instructors
,
set
(
instructors
))
class
PersonSocialNetworkTests
(
TestCase
):
class
PersonSocialNetworkTests
(
TestCase
):
...
...
requirements/base.txt
View file @
ad501533
cryptography==1.4
cryptography==1.4
django==1.8.14
django==1.8.14
django-
extensions==1.6.7
django-
choices==1.4.3
django-compressor==2.0
django-compressor==2.0
django-extensions==1.6.7
django-filter==0.13.0
django-filter==0.13.0
django-guardian==1.4.4
django-guardian==1.4.4
django-haystack==2.4.1
django-haystack==2.4.1
...
...
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