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
5107ad29
Commit
5107ad29
authored
Aug 17, 2016
by
Mike Dikan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update to course API to include (condensed) program info
parent
125bf70c
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
78 additions
and
15 deletions
+78
-15
course_discovery/apps/api/serializers.py
+32
-1
course_discovery/apps/api/tests/test_serializers.py
+26
-5
course_discovery/apps/api/v1/tests/test_views/mixins.py
+3
-2
course_discovery/apps/api/v1/tests/test_views/test_course_runs.py
+2
-2
course_discovery/apps/api/v1/views.py
+2
-2
course_discovery/apps/course_metadata/models.py
+6
-2
course_discovery/apps/course_metadata/tests/test_models.py
+7
-1
No files found.
course_discovery/apps/api/serializers.py
View file @
5107ad29
...
...
@@ -189,6 +189,19 @@ class CatalogSerializer(serializers.ModelSerializer):
fields
=
(
'id'
,
'name'
,
'query'
,
'courses_count'
,
'viewers'
)
class
NestedProgramSerializer
(
serializers
.
ModelSerializer
):
"""
Serializer used when nesting a Program inside another entity (e.g. a Course). The resulting data includes only
the basic details of the Program and none of the details about its related entities (e.g. courses).
"""
type
=
serializers
.
SlugRelatedField
(
slug_field
=
'name'
,
queryset
=
ProgramType
.
objects
.
all
())
class
Meta
:
model
=
Program
fields
=
(
'uuid'
,
'title'
,
'type'
,
'marketing_slug'
,
'marketing_url'
,)
read_only_fields
=
(
'uuid'
,
'marketing_url'
,)
class
CourseRunSerializer
(
TimestampModelSerializer
):
"""Serializer for the ``CourseRun`` model."""
course
=
serializers
.
SlugRelatedField
(
read_only
=
True
,
slug_field
=
'key'
)
...
...
@@ -218,6 +231,15 @@ class CourseRunSerializer(TimestampModelSerializer):
return
get_marketing_url_for_user
(
self
.
context
[
'request'
]
.
user
,
obj
.
marketing_url
)
class
CourseRunWithProgramsSerializer
(
CourseRunSerializer
):
"""A ``CourseRunSerializer`` which includes programs derived from parent course."""
programs
=
NestedProgramSerializer
(
many
=
True
)
class
Meta
(
CourseRunSerializer
.
Meta
):
model
=
CourseRun
fields
=
CourseRunSerializer
.
Meta
.
fields
+
(
'programs'
,
)
class
ContainedCourseRunsSerializer
(
serializers
.
Serializer
):
"""Serializer used to represent course runs contained by a catalog."""
course_runs
=
serializers
.
DictField
(
...
...
@@ -244,13 +266,22 @@ class CourseSerializer(TimestampModelSerializer):
fields
=
(
'key'
,
'title'
,
'short_description'
,
'full_description'
,
'level_type'
,
'subjects'
,
'prerequisites'
,
'expected_learning_items'
,
'image'
,
'video'
,
'owners'
,
'sponsors'
,
'modified'
,
'course_runs'
,
'marketing_url'
'marketing_url'
,
)
def
get_marketing_url
(
self
,
obj
):
return
get_marketing_url_for_user
(
self
.
context
[
'request'
]
.
user
,
obj
.
marketing_url
)
class
CourseWithProgramsSerializer
(
CourseSerializer
):
"""A ``CourseSerializer`` which includes programs."""
programs
=
NestedProgramSerializer
(
many
=
True
)
class
Meta
(
CourseSerializer
.
Meta
):
model
=
Course
fields
=
CourseSerializer
.
Meta
.
fields
+
(
'programs'
,
)
class
CourseSerializerExcludingClosedRuns
(
CourseSerializer
):
"""A ``CourseSerializer`` which only includes active course runs, as determined by ``CourseQuerySet``."""
course_runs
=
CourseRunSerializer
(
many
=
True
,
source
=
'active_course_runs'
)
...
...
course_discovery/apps/api/tests/test_serializers.py
View file @
5107ad29
...
...
@@ -11,7 +11,8 @@ from course_discovery.apps.api.serializers import (
CatalogSerializer
,
CourseSerializer
,
CourseRunSerializer
,
ContainedCoursesSerializer
,
ImageSerializer
,
SubjectSerializer
,
PrerequisiteSerializer
,
VideoSerializer
,
OrganizationSerializer
,
SeatSerializer
,
PersonSerializer
,
AffiliateWindowSerializer
,
ContainedCourseRunsSerializer
,
CourseRunSearchSerializer
,
ProgramSerializer
,
ProgramSearchSerializer
,
ProgramCourseSerializer
ProgramSerializer
,
ProgramSearchSerializer
,
ProgramCourseSerializer
,
NestedProgramSerializer
,
CourseRunWithProgramsSerializer
,
CourseWithProgramsSerializer
)
from
course_discovery.apps.catalogs.tests.factories
import
CatalogFactory
from
course_discovery.apps.core.models
import
User
...
...
@@ -76,7 +77,7 @@ class CourseSerializerTests(TestCase):
request
=
make_request
()
CourseRunFactory
.
create_batch
(
3
,
course
=
course
)
serializer
=
CourseSerializer
(
course
,
context
=
{
'request'
:
request
})
serializer
=
Course
WithPrograms
Serializer
(
course
,
context
=
{
'request'
:
request
})
expected
=
{
'key'
:
course
.
key
,
...
...
@@ -99,7 +100,8 @@ class CourseSerializerTests(TestCase):
'utm_source'
:
request
.
user
.
username
,
'utm_medium'
:
request
.
user
.
referral_tracking_id
,
})
)
),
'programs'
:
NestedProgramSerializer
(
course
.
programs
,
many
=
True
,
context
=
{
'request'
:
request
})
.
data
,
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
...
...
@@ -119,9 +121,11 @@ class CourseRunSerializerTests(TestCase):
def
test_data
(
self
):
request
=
make_request
()
course_run
=
CourseRunFactory
()
course
=
course_run
.
course
image
=
course_run
.
image
video
=
course_run
.
video
serializer
=
CourseRunSerializer
(
course_run
,
context
=
{
'request'
:
request
})
serializer
=
CourseRunWithProgramsSerializer
(
course_run
,
context
=
{
'request'
:
request
})
ProgramFactory
(
courses
=
[
course
])
expected
=
{
'course'
:
course_run
.
course
.
key
,
...
...
@@ -154,6 +158,7 @@ class CourseRunSerializerTests(TestCase):
),
'level_type'
:
course_run
.
level_type
.
name
,
'availability'
:
course_run
.
availability
,
'programs'
:
NestedProgramSerializer
(
course
.
programs
,
many
=
True
,
context
=
{
'request'
:
request
})
.
data
,
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
...
...
@@ -239,7 +244,7 @@ class ProgramCourseSerializerTests(TestCase):
'utm_source'
:
self
.
request
.
user
.
username
,
'utm_medium'
:
self
.
request
.
user
.
referral_tracking_id
,
})
)
)
,
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
...
...
@@ -384,6 +389,22 @@ class ImageSerializerTests(TestCase):
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
class
NestedProgramSerializerTests
(
TestCase
):
def
test_data
(
self
):
program
=
ProgramFactory
()
serializer
=
NestedProgramSerializer
(
program
)
expected
=
{
'uuid'
:
str
(
program
.
uuid
),
'marketing_slug'
:
program
.
marketing_slug
,
'marketing_url'
:
program
.
marketing_url
,
# pylint: disable=no-member
'type'
:
program
.
type
.
name
,
'title'
:
program
.
title
,
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
class
VideoSerializerTests
(
TestCase
):
def
test_data
(
self
):
video
=
VideoFactory
()
...
...
course_discovery/apps/api/v1/tests/test_views/mixins.py
View file @
5107ad29
...
...
@@ -7,7 +7,8 @@ from django.conf import settings
from
rest_framework.test
import
APIRequestFactory
from
course_discovery.apps.api.serializers
import
(
CatalogSerializer
,
CourseSerializer
,
CourseSerializerExcludingClosedRuns
,
FlattenedCourseRunWithCourseSerializer
CatalogSerializer
,
CourseWithProgramsSerializer
,
CourseSerializerExcludingClosedRuns
,
FlattenedCourseRunWithCourseSerializer
)
...
...
@@ -27,7 +28,7 @@ class SerializationMixin(object):
return
self
.
_serialize_object
(
CatalogSerializer
,
catalog
,
many
,
format
)
def
serialize_course
(
self
,
course
,
many
=
False
,
format
=
None
):
return
self
.
_serialize_object
(
CourseSerializer
,
course
,
many
,
format
)
return
self
.
_serialize_object
(
Course
WithPrograms
Serializer
,
course
,
many
,
format
)
def
serialize_catalog_course
(
self
,
course
,
many
=
False
,
format
=
None
):
return
self
.
_serialize_object
(
CourseSerializerExcludingClosedRuns
,
course
,
many
,
format
)
...
...
course_discovery/apps/api/v1/tests/test_views/test_course_runs.py
View file @
5107ad29
...
...
@@ -6,7 +6,7 @@ from django.db.models.functions import Lower
from
rest_framework.reverse
import
reverse
from
rest_framework.test
import
APITestCase
,
APIRequestFactory
from
course_discovery.apps.api.serializers
import
CourseRunSerializer
from
course_discovery.apps.api.serializers
import
CourseRun
WithPrograms
Serializer
from
course_discovery.apps.core.tests.factories
import
UserFactory
from
course_discovery.apps.core.tests.mixins
import
ElasticsearchTestMixin
from
course_discovery.apps.course_metadata.models
import
CourseRun
...
...
@@ -27,7 +27,7 @@ class CourseRunViewSetTests(ElasticsearchTestMixin, APITestCase):
self
.
request
.
user
=
self
.
user
def
serialize_course_run
(
self
,
course_run
,
**
kwargs
):
return
CourseRunSerializer
(
course_run
,
context
=
{
'request'
:
self
.
request
},
**
kwargs
)
.
data
return
CourseRun
WithPrograms
Serializer
(
course_run
,
context
=
{
'request'
:
self
.
request
},
**
kwargs
)
.
data
def
test_get
(
self
):
""" Verify the endpoint returns the details for a single course. """
...
...
course_discovery/apps/api/v1/views.py
View file @
5107ad29
...
...
@@ -181,7 +181,7 @@ class CourseViewSet(viewsets.ReadOnlyModelViewSet):
lookup_value_regex
=
COURSE_ID_REGEX
queryset
=
Course
.
objects
.
all
()
permission_classes
=
(
IsAuthenticated
,)
serializer_class
=
serializers
.
CourseSerializer
serializer_class
=
serializers
.
Course
WithPrograms
Serializer
def
get_queryset
(
self
):
q
=
self
.
request
.
query_params
.
get
(
'q'
,
None
)
...
...
@@ -225,7 +225,7 @@ class CourseRunViewSet(viewsets.ReadOnlyModelViewSet):
lookup_value_regex
=
COURSE_RUN_ID_REGEX
queryset
=
CourseRun
.
objects
.
all
()
.
order_by
(
Lower
(
'key'
))
permission_classes
=
(
IsAuthenticated
,)
serializer_class
=
serializers
.
CourseRunSerializer
serializer_class
=
serializers
.
CourseRun
WithPrograms
Serializer
def
_get_partner
(
self
):
""" Return the partner for the code passed in or the default partner """
...
...
course_discovery/apps/course_metadata/models.py
View file @
5107ad29
...
...
@@ -389,6 +389,10 @@ class CourseRun(TimeStampedModel):
return
self
.
course
.
prerequisites
@property
def
programs
(
self
):
return
self
.
course
.
programs
@property
def
seat_types
(
self
):
return
list
(
self
.
seats
.
values_list
(
'type'
,
flat
=
True
))
...
...
@@ -576,7 +580,7 @@ class Program(TimeStampedModel):
)
marketing_slug
=
models
.
CharField
(
help_text
=
_
(
'Slug used to generate links to the marketing site'
),
blank
=
True
,
max_length
=
255
,
db_index
=
True
)
courses
=
models
.
ManyToManyField
(
Course
)
courses
=
models
.
ManyToManyField
(
Course
,
related_name
=
'programs'
)
# NOTE (CCB): Editors of this field should validate the values to ensure only CourseRuns associated
# with related Courses are stored.
excluded_course_runs
=
models
.
ManyToManyField
(
CourseRun
,
blank
=
True
)
...
...
@@ -623,7 +627,7 @@ class Program(TimeStampedModel):
@property
def
course_runs
(
self
):
excluded_course_run_ids
=
[
course_run
.
id
for
course_run
in
self
.
excluded_course_runs
.
all
()]
return
CourseRun
.
objects
.
filter
(
course__program
=
self
)
.
exclude
(
id__in
=
excluded_course_run_ids
)
return
CourseRun
.
objects
.
filter
(
course__program
s
=
self
)
.
exclude
(
id__in
=
excluded_course_run_ids
)
@property
def
languages
(
self
):
...
...
course_discovery/apps/course_metadata/tests/test_models.py
View file @
5107ad29
...
...
@@ -309,7 +309,13 @@ class ProgramTests(TestCase):
self
.
assertIsNone
(
self
.
program
.
marketing_url
)
def
test_course_runs
(
self
):
""" Verify the property returns the set of associated CourseRuns minus those that are explicitly excluded. """
"""
Verify that we only fetch course runs for the program, and not other course runs for other programs and that the
property returns the set of associated CourseRuns minus those that are explicitly excluded.
"""
course_run
=
factories
.
CourseRunFactory
()
factories
.
ProgramFactory
(
courses
=
[
course_run
.
course
])
# Verify that course run is not returned in set
self
.
assertEqual
(
set
(
self
.
program
.
course_runs
),
set
(
self
.
course_runs
))
def
test_languages
(
self
):
...
...
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