Commit 23f57d59 by Mike Dikan Committed by mikedikan

Add subject list/details endpoints

ECOM-7905

Adding endpoints to get individual subjects and a full subject listing.  The subjects will be used on the marketing site to render the subject landing pages and eventually the all-subjects page.
parent f68f553d
......@@ -13,7 +13,7 @@ from rest_framework.exceptions import NotFound, PermissionDenied
from course_discovery.apps.api.utils import cast2int
from course_discovery.apps.course_metadata.choices import ProgramStatus
from course_discovery.apps.course_metadata.models import Course, CourseRun, Organization, Person, Program
from course_discovery.apps.course_metadata.models import Course, CourseRun, Organization, Person, Program, Subject
logger = logging.getLogger(__name__)
User = get_user_model()
......@@ -178,3 +178,10 @@ class PersonFilter(filters.FilterSet):
class Meta:
model = Person
fields = ('slug',)
class SubjectFilter(filters.FilterSet):
class Meta:
model = Subject
fields = ('slug', )
......@@ -164,9 +164,13 @@ class FAQSerializer(serializers.ModelSerializer):
class SubjectSerializer(serializers.ModelSerializer):
"""Serializer for the ``Subject`` model."""
@classmethod
def prefetch_queryset(cls):
return Subject.objects.filter()
class Meta(object):
model = Subject
fields = ('name', 'subtitle', 'description', 'banner_image_url', 'card_image_url', 'slug',)
fields = ('name', 'subtitle', 'description', 'banner_image_url', 'card_image_url', 'slug', 'uuid')
class PrerequisiteSerializer(NamedModelSerializer):
......
......@@ -949,6 +949,7 @@ class SubjectSerializerTests(TestCase):
'card_image_url': subject.card_image_url,
'subtitle': subject.subtitle,
'slug': subject.slug,
'uuid': str(subject.uuid),
}
self.assertDictEqual(serializer.data, expected)
......
......@@ -10,7 +10,7 @@ from rest_framework.test import APIRequestFactory
from course_discovery.apps.api.serializers import (
CatalogCourseSerializer, CatalogSerializer, CourseRunWithProgramsSerializer,
CourseWithProgramsSerializer, FlattenedCourseRunWithCourseSerializer, MinimalProgramSerializer,
OrganizationSerializer, PersonSerializer, ProgramSerializer, ProgramTypeSerializer
OrganizationSerializer, PersonSerializer, ProgramSerializer, ProgramTypeSerializer, SubjectSerializer
)
from course_discovery.apps.api.tests.mixins import SiteMixin
......@@ -67,6 +67,9 @@ class SerializationMixin(object):
def serialize_organization(self, organization, many=False, format=None, extra_context=None):
return self._serialize_object(OrganizationSerializer, organization, many, format, extra_context)
def serialize_subject(self, subject, many=False, format=None, extra_context=None):
return self._serialize_object(SubjectSerializer, subject, many, format, extra_context)
class OAuth2Mixin(object):
def generate_oauth2_token_header(self, user):
......
from django.urls import reverse
from course_discovery.apps.api.v1.tests.test_views.mixins import APITestCase, SerializationMixin
from course_discovery.apps.core.tests.factories import USER_PASSWORD, UserFactory
from course_discovery.apps.course_metadata.models import Subject
from course_discovery.apps.course_metadata.tests.factories import SubjectFactory
class SubjectViewSetTests(SerializationMixin, APITestCase):
list_path = reverse('api:v1:subject-list')
def setUp(self):
super(SubjectViewSetTests, self).setUp()
self.user = UserFactory(is_staff=True, is_superuser=True)
self.client.login(username=self.user.username, password=USER_PASSWORD)
def test_authentication(self):
""" Verify the endpoint requires the user to be authenticated. """
response = self.client.get(self.list_path)
assert response.status_code == 200
self.client.logout()
response = self.client.get(self.list_path)
assert response.status_code == 403
def test_list(self):
""" Verify the endpoint returns a list of all subjects. """
SubjectFactory.create_batch(8)
expected = Subject.objects.all()
with self.assertNumQueries(5):
response = self.client.get(self.list_path)
assert response.status_code == 200
assert response.data['results'] == self.serialize_subject(expected, many=True)
def test_retrieve(self):
""" The request should return details for a single subject. """
subject = SubjectFactory()
url = reverse('api:v1:subject-detail', kwargs={'uuid': subject.uuid})
with self.assertNumQueries(4):
response = self.client.get(url)
assert response.status_code == 200
assert response.data == self.serialize_subject(subject)
......@@ -11,6 +11,7 @@ from course_discovery.apps.api.v1.views.organizations import OrganizationViewSet
from course_discovery.apps.api.v1.views.people import PersonViewSet
from course_discovery.apps.api.v1.views.program_types import ProgramTypeViewSet
from course_discovery.apps.api.v1.views.programs import ProgramViewSet
from course_discovery.apps.api.v1.views.subjects import SubjectViewSet
partners_router = routers.SimpleRouter()
partners_router.register(r'affiliate_window/catalogs', AffiliateWindowViewSet, base_name='affiliate_window')
......@@ -26,6 +27,7 @@ router.register(r'courses', CourseViewSet, base_name='course')
router.register(r'course_runs', CourseRunViewSet, base_name='course_run')
router.register(r'organizations', OrganizationViewSet, base_name='organization')
router.register(r'people', PersonViewSet, base_name='person')
router.register(r'subjects', SubjectViewSet, base_name='subject')
router.register(r'programs', ProgramViewSet, base_name='program')
router.register(r'program_types', ProgramTypeViewSet, base_name='program_type')
router.register(r'search/all', search_views.AggregateSearchViewSet, base_name='search-all')
......
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from course_discovery.apps.api import filters, serializers
from course_discovery.apps.api.pagination import ProxiedPagination
# pylint: disable=no-member
class SubjectViewSet(viewsets.ReadOnlyModelViewSet):
""" Subject resource. """
filter_backends = (DjangoFilterBackend,)
filter_class = filters.SubjectFilter
lookup_field = 'uuid'
lookup_value_regex = '[0-9a-f-]+'
permission_classes = (IsAuthenticated,)
serializer_class = serializers.SubjectSerializer
# Explicitly support PageNumberPagination and LimitOffsetPagination. Future
# versions of this API should only support the system default, PageNumberPagination.
pagination_class = ProxiedPagination
def get_queryset(self):
return serializers.SubjectSerializer.prefetch_queryset()
def list(self, request, *args, **kwargs):
""" Retrieve a list of all subjects. """
return super(SubjectViewSet, self).list(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
""" Retrieve details for an subject. """
return super(SubjectViewSet, self).retrieve(request, *args, **kwargs)
......@@ -52,6 +52,7 @@ class SubjectFactory(factory.DjangoModelFactory):
banner_image_url = FuzzyURL()
card_image_url = FuzzyURL()
partner = factory.SubFactory(PartnerFactory)
uuid = factory.LazyFunction(uuid4)
class LevelTypeFactory(AbstractNamedModelFactory):
......
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