Commit 880da5a0 by Clinton Blackburn Committed by Clinton Blackburn

Exposing course name via Commerce API

The course now includes a read-only name attribute.

XCOM-536
parent 53db053c
""" API v1 models. """
from itertools import groupby
import logging
import logging
from django.db import transaction
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from course_modes.models import CourseMode
log = logging.getLogger(__name__)
......@@ -22,6 +22,19 @@ class Course(object):
self.modes = list(modes)
self._deleted_modes = []
@property
def name(self):
""" Return course name. """
course_id = CourseKey.from_string(unicode(self.id)) # pylint: disable=invalid-name
try:
return CourseOverview.get_from_id(course_id).display_name
except CourseOverview.DoesNotExist:
# NOTE (CCB): Ideally, the course modes table should only contain data for courses that exist in
# modulestore. If that is not the case, say for local development/testing, carry on without failure.
log.warning('Failed to retrieve CourseOverview for [%s]. Using empty course name.', course_id)
return None
def get_mode_display_name(self, mode):
""" Returns display name for the given mode. """
slug = mode.mode_slug.strip().lower()
......
......@@ -25,6 +25,7 @@ class CourseModeSerializer(serializers.ModelSerializer):
class CourseSerializer(serializers.Serializer):
""" Course serializer. """
id = serializers.CharField() # pylint: disable=invalid-name
name = serializers.CharField(read_only=True)
modes = CourseModeSerializer(many=True, allow_add_remove=True)
def restore_object(self, attrs, instance=None):
......
......@@ -47,6 +47,17 @@ class CourseApiViewTestMixin(object):
u'expires': expires,
}
@classmethod
def _serialize_course(cls, course, modes=None):
""" Serializes a course to a Python dict. """
modes = modes or []
return {
u'id': unicode(course.id),
u'name': unicode(course.display_name),
u'modes': [cls._serialize_course_mode(mode) for mode in modes]
}
class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
""" Tests for CourseListView. """
......@@ -66,12 +77,7 @@ class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
self.assertEqual(response.status_code, 200)
actual = json.loads(response.content)
expected = [
{
u'id': unicode(self.course.id),
u'modes': [self._serialize_course_mode(self.course_mode)]
}
]
expected = [self._serialize_course(self.course, [self.course_mode])]
self.assertListEqual(actual, expected)
......@@ -104,10 +110,7 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
self.assertEqual(response.status_code, 200)
actual = json.loads(response.content)
expected = {
u'id': unicode(self.course.id),
u'modes': [self._serialize_course_mode(self.course_mode)]
}
expected = self._serialize_course(self.course, [self.course_mode])
self.assertEqual(actual, expected)
def test_retrieve_invalid_course(self):
......@@ -128,10 +131,8 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
sku=u'ABC123',
expiration_datetime=expiration_datetime
)
expected = {
u'id': unicode(self.course.id),
u'modes': [self._serialize_course_mode(expected_course_mode)]
}
expected = self._serialize_course(self.course, [expected_course_mode])
response = self.client.put(self.path, json.dumps(expected), content_type=JSON_CONTENT_TYPE)
self.assertEqual(response.status_code, 200)
......@@ -145,11 +146,8 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
self.user.user_permissions.add(permission)
course_id = unicode(self.course.id)
expected = {
u'id': course_id,
u'modes': [self._serialize_course_mode(
CourseMode(mode_slug=u'credit', min_price=500, currency=u'USD', sku=u'ABC123')), ]
}
expected_course_mode = CourseMode(mode_slug=u'credit', min_price=500, currency=u'USD', sku=u'ABC123')
expected = self._serialize_course(self.course, [expected_course_mode])
path = reverse('commerce_api:v1:courses:retrieve_update', args=[course_id])
response = self.client.put(path, json.dumps(expected), content_type=JSON_CONTENT_TYPE)
self.assertEqual(response.status_code, 200)
......@@ -190,19 +188,14 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
def assert_can_create_course(self, **request_kwargs):
""" Verify a course can be created by the view. """
course = CourseFactory.create()
course_id = unicode(course.id)
expected = {
u'id': course_id,
u'modes': [
self._serialize_course_mode(
CourseMode(mode_slug=u'verified', min_price=150, currency=u'USD', sku=u'ABC123')),
self._serialize_course_mode(
CourseMode(mode_slug=u'honor', min_price=0, currency=u'USD', sku=u'DEADBEEF')),
]
}
path = reverse('commerce_api:v1:courses:retrieve_update', args=[course_id])
expected_modes = [CourseMode(mode_slug=u'verified', min_price=150, currency=u'USD', sku=u'ABC123'),
CourseMode(mode_slug=u'honor', min_price=0, currency=u'USD', sku=u'DEADBEEF')]
expected = self._serialize_course(course, expected_modes)
path = reverse('commerce_api:v1:courses:retrieve_update', args=[unicode(course.id)])
response = self.client.put(path, json.dumps(expected), content_type=JSON_CONTENT_TYPE, **request_kwargs)
self.assertEqual(response.status_code, 201)
actual = json.loads(response.content)
self.assertEqual(actual, expected)
......
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