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
910299d7
Commit
910299d7
authored
Oct 17, 2016
by
Simon Chen
Committed by
GitHub
Oct 17, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #381 from edx/schen/ECOM-5729
Exclude deleted programs from course and course_run api
parents
823f2de8
eb4070f5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
161 additions
and
22 deletions
+161
-22
course_discovery/apps/api/serializers.py
+14
-3
course_discovery/apps/api/tests/test_serializers.py
+72
-14
course_discovery/apps/api/v1/tests/test_views/test_course_runs.py
+31
-1
course_discovery/apps/api/v1/tests/test_views/test_courses.py
+30
-4
course_discovery/apps/api/v1/views.py
+14
-0
No files found.
course_discovery/apps/api/serializers.py
View file @
910299d7
...
...
@@ -15,7 +15,7 @@ from taggit_serializer.serializers import TagListSerializerField, TaggitSerializ
from
course_discovery.apps.api.fields
import
StdImageSerializerField
,
ImageField
from
course_discovery.apps.catalogs.models
import
Catalog
from
course_discovery.apps.course_metadata.choices
import
CourseRunStatus
from
course_discovery.apps.course_metadata.choices
import
CourseRunStatus
,
ProgramStatus
from
course_discovery.apps.course_metadata.models
import
(
Course
,
CourseRun
,
Image
,
Organization
,
Person
,
Prerequisite
,
Seat
,
Subject
,
Video
,
Program
,
ProgramType
,
FAQ
,
CorporateEndorsement
,
Endorsement
,
Position
...
...
@@ -384,8 +384,11 @@ class CourseRunWithProgramsSerializer(CourseRunSerializer):
programs
=
serializers
.
SerializerMethodField
()
def
get_programs
(
self
,
obj
):
# Filter out non-deleted programs which this course_run is part of the program course_run exclusion
programs
=
[
program
for
program
in
obj
.
programs
.
all
()
if
obj
.
id
not
in
(
run
.
id
for
run
in
program
.
excluded_course_runs
.
all
())]
if
(
self
.
context
.
get
(
'include_deleted_programs'
)
or
program
.
status
!=
ProgramStatus
.
Deleted
)
and
obj
.
id
not
in
(
run
.
id
for
run
in
program
.
excluded_course_runs
.
all
())]
return
NestedProgramSerializer
(
programs
,
many
=
True
)
.
data
...
...
@@ -459,7 +462,15 @@ class CourseSerializer(MinimalCourseSerializer):
class
CourseWithProgramsSerializer
(
CourseSerializer
):
"""A ``CourseSerializer`` which includes programs."""
programs
=
NestedProgramSerializer
(
many
=
True
)
programs
=
serializers
.
SerializerMethodField
()
def
get_programs
(
self
,
obj
):
if
self
.
context
.
get
(
'include_deleted_programs'
):
eligible_programs
=
obj
.
programs
.
all
()
else
:
eligible_programs
=
obj
.
programs
.
exclude
(
status
=
ProgramStatus
.
Deleted
)
return
NestedProgramSerializer
(
eligible_programs
,
many
=
True
)
.
data
class
Meta
(
CourseSerializer
.
Meta
):
model
=
Course
...
...
course_discovery/apps/api/tests/test_serializers.py
View file @
910299d7
...
...
@@ -161,11 +161,43 @@ class CourseWithProgramsSerializerTests(CourseSerializerTests):
def
get_expected_data
(
self
,
course
,
request
):
expected
=
super
()
.
get_expected_data
(
course
,
request
)
expected
.
update
({
'programs'
:
NestedProgramSerializer
(
course
.
programs
,
many
=
True
,
context
=
{
'request'
:
request
})
.
data
,
'programs'
:
NestedProgramSerializer
(
course
.
programs
,
many
=
True
,
context
=
{
'request'
:
request
}
)
.
data
,
})
return
expected
def
setUp
(
self
):
super
()
.
setUp
()
self
.
request
=
make_request
()
self
.
course
=
CourseFactory
()
self
.
deleted_program
=
ProgramFactory
(
courses
=
[
self
.
course
],
status
=
ProgramStatus
.
Deleted
)
def
test_exclude_deleted_programs
(
self
):
"""
If the associated program is deleted,
CourseWithProgramsSerializer should not return any serialized programs
"""
serializer
=
self
.
serializer_class
(
self
.
course
,
context
=
{
'request'
:
self
.
request
})
self
.
assertEqual
(
serializer
.
data
[
'programs'
],
[])
def
test_include_deleted_programs
(
self
):
"""
If the associated program is deleted, but we are sending in the 'include_deleted_programs' flag
CourseWithProgramsSerializer should return deleted programs
"""
serializer
=
self
.
serializer_class
(
self
.
course
,
context
=
{
'request'
:
self
.
request
,
'include_deleted_programs'
:
1
}
)
self
.
assertEqual
(
serializer
.
data
,
self
.
get_expected_data
(
self
.
course
,
self
.
request
))
class
MinimalCourseRunSerializerTests
(
TestCase
):
serializer_class
=
MinimalCourseRunSerializer
...
...
@@ -234,16 +266,23 @@ class CourseRunSerializerTests(MinimalCourseRunSerializerTests):
class
CourseRunWithProgramsSerializerTests
(
TestCase
):
def
setUp
(
self
):
super
()
.
setUp
()
self
.
request
=
make_request
()
self
.
course_run
=
CourseRunFactory
()
self
.
serializer_context
=
{
'request'
:
self
.
request
}
def
test_data
(
self
):
request
=
make_request
()
course_run
=
CourseRunFactory
()
serializer_context
=
{
'request'
:
request
}
serializer
=
CourseRunWithProgramsSerializer
(
course_run
,
context
=
serializer_context
)
ProgramFactory
(
courses
=
[
course_run
.
course
])
serializer
=
CourseRunWithProgramsSerializer
(
self
.
course_run
,
context
=
self
.
serializer_context
)
ProgramFactory
(
courses
=
[
self
.
course_run
.
course
])
expected
=
CourseRunSerializer
(
course_run
,
context
=
serializer_context
)
.
data
expected
=
CourseRunSerializer
(
self
.
course_run
,
context
=
self
.
serializer_context
)
.
data
expected
.
update
({
'programs'
:
NestedProgramSerializer
(
course_run
.
course
.
programs
,
many
=
True
,
context
=
serializer_context
)
.
data
,
'programs'
:
NestedProgramSerializer
(
self
.
course_run
.
course
.
programs
,
many
=
True
,
context
=
self
.
serializer_context
)
.
data
,
})
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
...
...
@@ -253,18 +292,37 @@ class CourseRunWithProgramsSerializerTests(TestCase):
If a course run is excluded on a program, that program should not be
returned for that course run on the course run endpoint.
"""
request
=
make_request
()
course_run
=
CourseRunFactory
()
serializer_context
=
{
'request'
:
request
}
serializer
=
CourseRunWithProgramsSerializer
(
course_run
,
context
=
serializer_context
)
ProgramFactory
(
courses
=
[
course_run
.
course
],
excluded_course_runs
=
[
course_run
])
expected
=
CourseRunSerializer
(
course_run
,
context
=
serializer_context
)
.
data
serializer
=
CourseRunWithProgramsSerializer
(
self
.
course_run
,
context
=
self
.
serializer_context
)
ProgramFactory
(
courses
=
[
self
.
course_run
.
course
],
excluded_course_runs
=
[
self
.
course_run
])
expected
=
CourseRunSerializer
(
self
.
course_run
,
context
=
self
.
serializer_context
)
.
data
expected
.
update
({
'programs'
:
[],
})
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
def
test_exclude_deleted_programs
(
self
):
"""
If the associated program is deleted,
CourseRunWithProgramsSerializer should not return any serialized programs
"""
ProgramFactory
(
courses
=
[
self
.
course_run
.
course
],
status
=
ProgramStatus
.
Deleted
)
serializer
=
CourseRunWithProgramsSerializer
(
self
.
course_run
,
context
=
self
.
serializer_context
)
self
.
assertEqual
(
serializer
.
data
[
'programs'
],
[])
def
test_include_deleted_programs
(
self
):
"""
If the associated program is deleted, but we are sending in the 'include_deleted_programs' flag
CourseRunWithProgramsSerializer should return deleted programs
"""
deleted_program
=
ProgramFactory
(
courses
=
[
self
.
course_run
.
course
],
status
=
ProgramStatus
.
Deleted
)
self
.
serializer_context
[
'include_deleted_programs'
]
=
1
serializer
=
CourseRunWithProgramsSerializer
(
self
.
course_run
,
context
=
self
.
serializer_context
)
self
.
assertEqual
(
serializer
.
data
[
'programs'
],
NestedProgramSerializer
([
deleted_program
],
many
=
True
,
context
=
self
.
serializer_context
)
.
data
)
class
FlattenedCourseRunWithCourseSerializerTests
(
TestCase
):
# pragma: no cover
def
serialize_seats
(
self
,
course_run
):
...
...
course_discovery/apps/api/v1/tests/test_views/test_course_runs.py
View file @
910299d7
...
...
@@ -11,8 +11,9 @@ from rest_framework.test import APITestCase, APIRequestFactory
from
course_discovery.apps.api.v1.tests.test_views.mixins
import
SerializationMixin
from
course_discovery.apps.core.tests.factories
import
UserFactory
from
course_discovery.apps.core.tests.mixins
import
ElasticsearchTestMixin
from
course_discovery.apps.course_metadata.choices
import
ProgramStatus
from
course_discovery.apps.course_metadata.models
import
CourseRun
from
course_discovery.apps.course_metadata.tests.factories
import
CourseRunFactory
,
PartnerFactory
from
course_discovery.apps.course_metadata.tests.factories
import
CourseRunFactory
,
PartnerFactory
,
ProgramFactory
@ddt.ddt
...
...
@@ -38,6 +39,35 @@ class CourseRunViewSetTests(SerializationMixin, ElasticsearchTestMixin, APITestC
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
,
self
.
serialize_course_run
(
self
.
course_run
))
def
test_get_exclude_deleted_programs
(
self
):
""" Verify the endpoint returns no associated deleted programs """
ProgramFactory
(
courses
=
[
self
.
course_run
.
course
],
status
=
ProgramStatus
.
Deleted
)
url
=
reverse
(
'api:v1:course_run-detail'
,
kwargs
=
{
'key'
:
self
.
course_run
.
key
})
with
self
.
assertNumQueries
(
12
):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
.
get
(
'programs'
),
[])
def
test_get_include_deleted_programs
(
self
):
"""
Verify the endpoint returns associated deleted programs
with the 'include_deleted_programs' flag set to True
"""
ProgramFactory
(
courses
=
[
self
.
course_run
.
course
],
status
=
ProgramStatus
.
Deleted
)
url
=
reverse
(
'api:v1:course_run-detail'
,
kwargs
=
{
'key'
:
self
.
course_run
.
key
})
url
+=
'?include_deleted_programs=1'
with
self
.
assertNumQueries
(
19
):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
,
self
.
serialize_course_run
(
self
.
course_run
,
extra_context
=
{
'include_deleted_programs'
:
True
})
)
def
test_list
(
self
):
""" Verify the endpoint returns a list of all catalogs. """
url
=
reverse
(
'api:v1:course_run-list'
)
...
...
course_discovery/apps/api/v1/tests/test_views/test_courses.py
View file @
910299d7
...
...
@@ -4,8 +4,9 @@ from rest_framework.test import APITestCase
from
course_discovery.apps.api.v1.tests.test_views.mixins
import
SerializationMixin
from
course_discovery.apps.core.tests.factories
import
UserFactory
,
USER_PASSWORD
from
course_discovery.apps.course_metadata.choices
import
ProgramStatus
from
course_discovery.apps.course_metadata.models
import
Course
from
course_discovery.apps.course_metadata.tests.factories
import
CourseFactory
from
course_discovery.apps.course_metadata.tests.factories
import
CourseFactory
,
ProgramFactory
class
CourseViewSetTests
(
SerializationMixin
,
APITestCase
):
...
...
@@ -21,16 +22,41 @@ class CourseViewSetTests(SerializationMixin, APITestCase):
""" Verify the endpoint returns the details for a single course. """
url
=
reverse
(
'api:v1:course-detail'
,
kwargs
=
{
'key'
:
self
.
course
.
key
})
with
self
.
assertNumQueries
(
1
8
):
with
self
.
assertNumQueries
(
1
9
):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
,
self
.
serialize_course
(
self
.
course
))
def
test_get_exclude_deleted_programs
(
self
):
""" Verify the endpoint returns no deleted associated programs """
ProgramFactory
(
courses
=
[
self
.
course
],
status
=
ProgramStatus
.
Deleted
)
url
=
reverse
(
'api:v1:course-detail'
,
kwargs
=
{
'key'
:
self
.
course
.
key
})
with
self
.
assertNumQueries
(
12
):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
.
get
(
'programs'
),
[])
def
test_get_include_deleted_programs
(
self
):
"""
Verify the endpoint returns associated deleted programs
with the 'include_deleted_programs' flag set to True
"""
ProgramFactory
(
courses
=
[
self
.
course
],
status
=
ProgramStatus
.
Deleted
)
url
=
reverse
(
'api:v1:course-detail'
,
kwargs
=
{
'key'
:
self
.
course
.
key
})
url
+=
'?include_deleted_programs=1'
with
self
.
assertNumQueries
(
22
):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
,
self
.
serialize_course
(
self
.
course
,
extra_context
=
{
'include_deleted_programs'
:
True
})
)
def
test_list
(
self
):
""" Verify the endpoint returns a list of all courses. """
url
=
reverse
(
'api:v1:course-list'
)
with
self
.
assertNumQueries
(
2
4
):
with
self
.
assertNumQueries
(
2
5
):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertListEqual
(
...
...
@@ -57,7 +83,7 @@ class CourseViewSetTests(SerializationMixin, APITestCase):
keys
=
','
.
join
([
course
.
key
for
course
in
courses
])
url
=
'{root}?keys={keys}'
.
format
(
root
=
reverse
(
'api:v1:course-list'
),
keys
=
keys
)
with
self
.
assertNumQueries
(
3
5
):
with
self
.
assertNumQueries
(
3
8
):
response
=
self
.
client
.
get
(
url
)
self
.
assertListEqual
(
response
.
data
[
'results'
],
self
.
serialize_course
(
courses
,
many
=
True
))
...
...
course_discovery/apps/api/v1/views.py
View file @
910299d7
...
...
@@ -236,6 +236,7 @@ class CourseViewSet(viewsets.ReadOnlyModelViewSet):
context
=
super
()
.
get_serializer_context
(
*
args
,
**
kwargs
)
context
.
update
({
'exclude_utm'
:
get_query_param
(
self
.
request
,
'exclude_utm'
),
'include_deleted_programs'
:
get_query_param
(
self
.
request
,
'include_deleted_programs'
),
})
return
context
...
...
@@ -262,6 +263,12 @@ class CourseViewSet(viewsets.ReadOnlyModelViewSet):
type: integer
paramType: query
multiple: false
- name: include_deleted_programs
description: Will include deleted programs in the associated programs array
required: false
type: integer
paramType: query
multiple: false
"""
return
super
(
CourseViewSet
,
self
)
.
list
(
request
,
*
args
,
**
kwargs
)
...
...
@@ -312,6 +319,7 @@ class CourseRunViewSet(viewsets.ReadOnlyModelViewSet):
context
=
super
()
.
get_serializer_context
(
*
args
,
**
kwargs
)
context
.
update
({
'exclude_utm'
:
get_query_param
(
self
.
request
,
'exclude_utm'
),
'include_deleted_programs'
:
get_query_param
(
self
.
request
,
'include_deleted_programs'
),
})
return
context
...
...
@@ -358,6 +366,12 @@ class CourseRunViewSet(viewsets.ReadOnlyModelViewSet):
type: integer
paramType: query
multiple: false
- name: include_deleted_programs
description: Will include deleted programs in the associated programs array
required: false
type: integer
paramType: query
multiple: false
"""
return
super
(
CourseRunViewSet
,
self
)
.
list
(
request
,
*
args
,
**
kwargs
)
...
...
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