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 = {
}
PROGRAM_SEARCH_FIELDS
=
(
'text'
,
'uuid'
,
'title'
,
'subtitle'
,
'category'
,
'marketing_url'
,
'organizations'
,
'content_type'
,
'
image_url
'
,
'
status
'
,
'text'
,
'uuid'
,
'title'
,
'subtitle'
,
'category'
,
'marketing_url'
,
'organizations'
,
'content_type'
,
'
status
'
,
'
card_image_url
'
,
)
...
...
@@ -264,7 +264,7 @@ class ContainedCoursesSerializer(serializers.Serializer):
class
ProgramSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
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'
,)
...
...
course_discovery/apps/api/tests/test_serializers.py
View file @
ad501533
...
...
@@ -181,7 +181,7 @@ class ProgramSerializerTests(TestCase):
'category'
:
program
.
category
,
'marketing_slug'
:
program
.
marketing_slug
,
'marketing_url'
:
program
.
marketing_url
,
'
image_url'
:
program
.
image_url
,
'
card_image_url'
:
program
.
card_
image_url
,
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
...
...
@@ -386,9 +386,12 @@ class CourseRunSearchSerializerTests(TestCase):
class
ProgramSearchSerializerTests
(
TestCase
):
def
test_data
(
self
):
program
=
ProgramFactory
()
organization
=
OrganizationFactory
()
program
.
organizations
.
add
(
organization
)
authoring_organization
,
crediting_organization
=
OrganizationFactory
.
create_batch
(
2
)
program
.
authoring_organizations
.
add
(
authoring_organization
)
program
.
credit_backing_organizations
.
add
(
crediting_organization
)
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
# to generate such a result.
...
...
@@ -401,9 +404,9 @@ class ProgramSearchSerializerTests(TestCase):
'subtitle'
:
program
.
subtitle
,
'category'
:
program
.
category
,
'marketing_url'
:
program
.
marketing_url
,
'organizations'
:
[
OrganizationsMixin
.
format_organization
(
organization
)]
,
'organizations'
:
expected_organizations
,
'content_type'
:
'program'
,
'
image_url'
:
program
.
image_url
,
'
card_image_url'
:
program
.
card_
image_url
,
'status'
:
program
.
status
,
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
course_discovery/apps/course_metadata/admin.py
View file @
ad501533
from
django.contrib
import
admin
from
course_discovery.apps.course_metadata.models
import
(
Seat
,
Image
,
Video
,
LevelType
,
Subject
,
Prerequisite
,
ExpectedLearningItem
,
Expertise
,
Course
,
CourseRun
,
CourseRunSocialNetwork
,
MajorWork
,
Organization
,
Person
,
PersonSocialNetwork
,
CourseOrganization
,
SyllabusItem
,
Program
)
Seat
,
Image
,
Video
,
LevelType
,
Subject
,
Prerequisite
,
ExpectedLearningItem
,
Expertise
,
Course
,
CourseRun
,
CourseRunSocialNetwork
,
MajorWork
,
Organization
,
Person
,
PersonSocialNetwork
,
CourseOrganization
,
SyllabusItem
,
Program
,
JobOutlookItem
,
SeatType
,
Endorsement
,
CorporateEndorsement
,
FAQ
,
ProgramType
)
class
CourseOrganizationInline
(
admin
.
TabularInline
):
...
...
@@ -21,6 +20,7 @@ class SeatInline(admin.TabularInline):
class
CourseAdmin
(
admin
.
ModelAdmin
):
inlines
=
(
CourseOrganizationInline
,)
list_display
=
(
'key'
,
'title'
,)
list_filter
=
(
'partner'
,)
ordering
=
(
'key'
,
'title'
,)
search_fields
=
(
'key'
,
'title'
,)
...
...
@@ -36,10 +36,38 @@ class CourseRunAdmin(admin.ModelAdmin):
@admin.register
(
Program
)
class
ProgramAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'uuid'
,
'title'
,)
list_filter
=
(
'partner'
,)
ordering
=
(
'uuid'
,
'title'
,)
readonly_fields
=
(
'uuid'
,)
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
):
list_display
=
(
'key'
,
'name'
,)
ordering
=
(
'key'
,
'name'
,)
...
...
@@ -61,5 +89,6 @@ for model in (LevelType, Subject, Prerequisite, Expertise, MajorWork):
admin
.
site
.
register
(
model
,
NamedModelAdmin
)
# 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
)
course_discovery/apps/course_metadata/data_loaders.py
View file @
ad501533
...
...
@@ -307,7 +307,8 @@ class DrupalApiDataLoader(AbstractDataLoader):
# Clean Organizations separately from other orphaned instances to avoid removing all orgnaziations
# 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
()
logger
.
info
(
'Retrieved
%
d course runs from
%
s.'
,
len
(
data
),
api_url
)
...
...
@@ -545,7 +546,7 @@ class ProgramsApiDataLoader(AbstractDataLoader):
'category'
:
body
[
'category'
],
'status'
:
body
[
'status'
],
'marketing_slug'
:
body
[
'marketing_slug'
],
'
image'
:
self
.
_get_image
(
body
),
'
banner_image_url'
:
self
.
_get_banner_image_url
(
body
),
'partner'
:
self
.
partner
,
}
...
...
@@ -558,24 +559,15 @@ class ProgramsApiDataLoader(AbstractDataLoader):
)
organizations
.
append
(
organization
)
program
.
organizations
.
clear
()
program
.
organizations
.
add
(
*
organizations
)
program
.
authoring_
organizations
.
clear
()
program
.
authoring_
organizations
.
add
(
*
organizations
)
except
Exception
:
# pylint: disable=broad-except
logger
.
exception
(
'Failed to load program
%
s'
,
uuid
)
def
_get_image
(
self
,
body
):
image
=
None
def
_get_banner_image_url
(
self
,
body
):
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
)
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
return
image_url
class
MarketingSiteDataLoader
(
AbstractDataLoader
):
...
...
@@ -667,10 +659,7 @@ class MarketingSiteDataLoader(AbstractDataLoader):
'subtitle'
:
data
.
get
(
'field_xseries_subtitle_short'
),
'category'
:
'XSeries'
,
'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
)
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):
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
uuid
=
indexes
.
CharField
(
model_attr
=
'uuid'
)
...
...
@@ -115,9 +115,20 @@ class ProgramIndex(OrganizationsMixin, BaseIndex, indexes.Indexable):
category
=
indexes
.
CharField
(
model_attr
=
'category'
,
faceted
=
True
)
marketing_url
=
indexes
.
CharField
(
null
=
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
)
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
):
return
obj
.
marketing_url
course_discovery/apps/course_metadata/tests/factories.py
View file @
ad501533
...
...
@@ -7,16 +7,18 @@ from factory.fuzzy import (
)
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.utils
import
FuzzyURL
from
course_discovery.apps.core.models
import
Currency
from
course_discovery.apps.course_metadata.models
import
(
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
# pylint: disable=no-member, unused-argument
class
AbstractMediaModelFactory
(
factory
.
DjangoModelFactory
):
src
=
FuzzyURL
()
description
=
FuzzyText
()
...
...
@@ -80,6 +82,16 @@ class CourseFactory(factory.DjangoModelFactory):
class
Meta
:
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
):
key
=
FuzzyText
(
prefix
=
'course-run-id/'
,
suffix
=
'/fake'
)
...
...
@@ -103,6 +115,16 @@ class CourseRunFactory(factory.DjangoModelFactory):
class
Meta
:
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
):
key
=
FuzzyText
(
prefix
=
'Org.fake/'
)
...
...
@@ -127,6 +149,23 @@ class PersonFactory(factory.DjangoModelFactory):
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
Meta
(
object
):
model
=
Program
...
...
@@ -137,9 +176,31 @@ class ProgramFactory(factory.django.DjangoModelFactory):
category
=
'xseries'
status
=
'unpublished'
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
)
@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
):
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
keys
=
[
org
[
'key'
]
for
org
in
body
[
'organizations'
]]
expected_organizations
=
list
(
Organization
.
objects
.
filter
(
key__in
=
keys
))
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'
)
if
image_url
:
image
=
Image
.
objects
.
get
(
src
=
image_url
,
width
=
self
.
loader
.
image_width
,
height
=
self
.
loader
.
image_height
)
self
.
assertEqual
(
program
.
image
,
image
)
banner_image_url
=
body
.
get
(
'banner_image_urls'
,
{})
.
get
(
'w435h145'
)
self
.
assertEqual
(
program
.
banner_image_url
,
banner_image_url
)
@responses.activate
def
test_ingest
(
self
):
...
...
@@ -736,12 +733,7 @@ class MarketingSiteDataLoaderTests(DataLoaderTestMixin, TestCase):
self
.
assertEqual
(
program
.
partner
,
self
.
partner
)
card_image_url
=
data
.
get
(
'field_card_image'
,
{})
.
get
(
'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
)
self
.
assertEqual
(
program
.
card_image_url
,
card_image_url
)
def
test_constructor_without_credentials
(
self
):
""" 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
itertools
from
decimal
import
Decimal
import
ddt
import
mock
import
pytz
from
dateutil.parser
import
parse
from
django.db
import
IntegrityError
from
django.test
import
TestCase
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.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.ietf_language_tags.models
import
LanguageTag
# pylint: disable=no-member
class
CourseTests
(
TestCase
):
""" Tests for the `Course` model. """
...
...
@@ -270,7 +274,13 @@ class ProgramTests(TestCase):
def
setUp
(
self
):
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
):
"""Verify that a program is properly converted to a str."""
...
...
@@ -287,13 +297,56 @@ class ProgramTests(TestCase):
self
.
program
.
marketing_slug
=
''
self
.
assertIsNone
(
self
.
program
.
marketing_url
)
def
test_image_url
(
self
):
""" Verify the property returns the associated image's URL. """
self
.
assertEqual
(
self
.
program
.
image_url
,
self
.
program
.
image
.
src
)
self
.
program
.
image
=
None
self
.
assertIsNone
(
self
.
program
.
image
)
self
.
assertIsNone
(
self
.
program
.
image_url
)
def
test_course_runs
(
self
):
""" Verify the property returns the set of associated CourseRuns minus those that are explicitly excluded. """
self
.
assertEqual
(
set
(
self
.
program
.
course_runs
),
set
(
self
.
course_runs
))
def
test_languages
(
self
):
expected_languages
=
set
([
course_run
.
language
for
course_run
in
self
.
course_runs
])
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
):
...
...
requirements/base.txt
View file @
ad501533
cryptography==1.4
django==1.8.14
django-
extensions==1.6.7
django-
choices==1.4.3
django-compressor==2.0
django-extensions==1.6.7
django-filter==0.13.0
django-guardian==1.4.4
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