Commit fce457f6 by Matthew Piatetsky

Add flag to hide unpublished course runs on the course endpoint

parent 80246abd
......@@ -472,8 +472,24 @@ class CourseSerializer(MinimalCourseSerializer):
class CourseWithProgramsSerializer(CourseSerializer):
"""A ``CourseSerializer`` which includes programs."""
course_runs = serializers.SerializerMethodField()
programs = serializers.SerializerMethodField()
def get_course_runs(self, course):
course_runs = course.course_runs.exclude(hidden=True)
if self.context.get('published_course_runs_only'):
course_runs = course_runs.filter(status=CourseRunStatus.Published)
return CourseRunSerializer(
course_runs,
many=True,
context={
'request': self.context.get('request'),
'exclude_utm': self.context.get('exclude_utm'),
}
).data
def get_programs(self, obj):
if self.context.get('include_deleted_programs'):
eligible_programs = obj.programs.all()
......
......@@ -155,6 +155,7 @@ class CourseSerializerTests(MinimalCourseSerializerTests):
self.assertEqual(serializer.data['marketing_url'], course.marketing_url)
@ddt.ddt
class CourseWithProgramsSerializerTests(CourseSerializerTests):
serializer_class = CourseWithProgramsSerializer
......@@ -198,6 +199,21 @@ class CourseWithProgramsSerializerTests(CourseSerializerTests):
)
self.assertEqual(serializer.data, self.get_expected_data(self.course, self.request))
@ddt.data(0, 1)
def test_published_course_runs_only(self, published_course_runs_only):
"""
Test that the published_course_runs_only flag hides unpublished course runs
"""
unpublished_course_run = CourseRunFactory(status=CourseRunStatus.Unpublished)
published_course_run = CourseRunFactory(status=CourseRunStatus.Published)
self.course.course_runs.add(unpublished_course_run, published_course_run)
self.request = make_request()
serializer = self.serializer_class(
self.course,
context={'request': self.request, 'published_course_runs_only': published_course_runs_only}
)
self.assertEqual(len(serializer.data['course_runs']), 2 - published_course_runs_only)
class MinimalCourseRunSerializerTests(TestCase):
serializer_class = MinimalCourseRunSerializer
......
import ddt
from django.db.models.functions import Lower
from rest_framework.reverse import reverse
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.choices import ProgramStatus, CourseRunStatus
from course_discovery.apps.course_metadata.models import Course
from course_discovery.apps.course_metadata.tests.factories import CourseFactory, ProgramFactory
from course_discovery.apps.course_metadata.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory
@ddt.ddt
class CourseViewSetTests(SerializationMixin, APITestCase):
maxDiff = None
......@@ -22,7 +24,7 @@ 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(19):
with self.assertNumQueries(20):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, self.serialize_course(self.course))
......@@ -31,7 +33,7 @@ class CourseViewSetTests(SerializationMixin, APITestCase):
""" 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):
with self.assertNumQueries(13):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data.get('programs'), [])
......@@ -44,7 +46,7 @@ class CourseViewSetTests(SerializationMixin, APITestCase):
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):
with self.assertNumQueries(23):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(
......@@ -52,11 +54,29 @@ class CourseViewSetTests(SerializationMixin, APITestCase):
self.serialize_course(self.course, extra_context={'include_deleted_programs': True})
)
@ddt.data(1, 0)
def test_get_include_published_course_run(self, published_course_runs_only):
"""
Verify the endpoint returns hides unpublished programs if
the 'published_course_runs_only' flag is set to True
"""
published_course_run = CourseRunFactory(status=CourseRunStatus.Published)
unpublished_course_run = CourseRunFactory(status=CourseRunStatus.Unpublished)
self.course.course_runs.add(published_course_run, unpublished_course_run)
url = reverse('api:v1:course-detail', kwargs={'key': self.course.key})
url += '?published_course_runs_only={}'.format(published_course_runs_only)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.data,
self.serialize_course(self.course, extra_context={'published_course_runs_only': published_course_runs_only})
)
def test_list(self):
""" Verify the endpoint returns a list of all courses. """
url = reverse('api:v1:course-list')
with self.assertNumQueries(25):
with self.assertNumQueries(26):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertListEqual(
......@@ -83,7 +103,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(38):
with self.assertNumQueries(41):
response = self.client.get(url)
self.assertListEqual(response.data['results'], self.serialize_course(courses, many=True))
......
......@@ -46,6 +46,7 @@ class CourseViewSet(viewsets.ReadOnlyModelViewSet):
context.update({
'exclude_utm': get_query_param(self.request, 'exclude_utm'),
'include_deleted_programs': get_query_param(self.request, 'include_deleted_programs'),
'published_course_runs_only': get_query_param(self.request, 'published_course_runs_only'),
})
return context
......@@ -66,6 +67,12 @@ class CourseViewSet(viewsets.ReadOnlyModelViewSet):
type: string
paramType: query
multiple: false
- name: published_course_runs_only
description: Filter course runs by published ones only
required: false
type: integer
paramType: query
mulitple: false
- name: exclude_utm
description: Exclude UTM parameters from marketing URLs.
required: false
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment