Commit 6e0013c4 by Clinton Blackburn

Merge pull request #9174 from edx/clintonb/commerce-api-course-name

Exposing course name via Commerce API
parents 53db053c 880da5a0
""" API v1 models. """ """ API v1 models. """
from itertools import groupby from itertools import groupby
import logging
import logging
from django.db import transaction from django.db import transaction
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from course_modes.models import CourseMode from course_modes.models import CourseMode
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -22,6 +22,19 @@ class Course(object): ...@@ -22,6 +22,19 @@ class Course(object):
self.modes = list(modes) self.modes = list(modes)
self._deleted_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): def get_mode_display_name(self, mode):
""" Returns display name for the given mode. """ """ Returns display name for the given mode. """
slug = mode.mode_slug.strip().lower() slug = mode.mode_slug.strip().lower()
......
...@@ -25,6 +25,7 @@ class CourseModeSerializer(serializers.ModelSerializer): ...@@ -25,6 +25,7 @@ class CourseModeSerializer(serializers.ModelSerializer):
class CourseSerializer(serializers.Serializer): class CourseSerializer(serializers.Serializer):
""" Course serializer. """ """ Course serializer. """
id = serializers.CharField() # pylint: disable=invalid-name id = serializers.CharField() # pylint: disable=invalid-name
name = serializers.CharField(read_only=True)
modes = CourseModeSerializer(many=True, allow_add_remove=True) modes = CourseModeSerializer(many=True, allow_add_remove=True)
def restore_object(self, attrs, instance=None): def restore_object(self, attrs, instance=None):
......
...@@ -47,6 +47,17 @@ class CourseApiViewTestMixin(object): ...@@ -47,6 +47,17 @@ class CourseApiViewTestMixin(object):
u'expires': expires, 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): class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
""" Tests for CourseListView. """ """ Tests for CourseListView. """
...@@ -66,12 +77,7 @@ class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase): ...@@ -66,12 +77,7 @@ class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
actual = json.loads(response.content) actual = json.loads(response.content)
expected = [ expected = [self._serialize_course(self.course, [self.course_mode])]
{
u'id': unicode(self.course.id),
u'modes': [self._serialize_course_mode(self.course_mode)]
}
]
self.assertListEqual(actual, expected) self.assertListEqual(actual, expected)
...@@ -104,10 +110,7 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase) ...@@ -104,10 +110,7 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
actual = json.loads(response.content) actual = json.loads(response.content)
expected = { expected = self._serialize_course(self.course, [self.course_mode])
u'id': unicode(self.course.id),
u'modes': [self._serialize_course_mode(self.course_mode)]
}
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_retrieve_invalid_course(self): def test_retrieve_invalid_course(self):
...@@ -128,10 +131,8 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase) ...@@ -128,10 +131,8 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
sku=u'ABC123', sku=u'ABC123',
expiration_datetime=expiration_datetime expiration_datetime=expiration_datetime
) )
expected = { expected = self._serialize_course(self.course, [expected_course_mode])
u'id': unicode(self.course.id),
u'modes': [self._serialize_course_mode(expected_course_mode)]
}
response = self.client.put(self.path, json.dumps(expected), content_type=JSON_CONTENT_TYPE) response = self.client.put(self.path, json.dumps(expected), content_type=JSON_CONTENT_TYPE)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -145,11 +146,8 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase) ...@@ -145,11 +146,8 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
self.user.user_permissions.add(permission) self.user.user_permissions.add(permission)
course_id = unicode(self.course.id) course_id = unicode(self.course.id)
expected = { expected_course_mode = CourseMode(mode_slug=u'credit', min_price=500, currency=u'USD', sku=u'ABC123')
u'id': course_id, expected = self._serialize_course(self.course, [expected_course_mode])
u'modes': [self._serialize_course_mode(
CourseMode(mode_slug=u'credit', min_price=500, currency=u'USD', sku=u'ABC123')), ]
}
path = reverse('commerce_api:v1:courses:retrieve_update', args=[course_id]) path = reverse('commerce_api:v1:courses:retrieve_update', args=[course_id])
response = self.client.put(path, json.dumps(expected), content_type=JSON_CONTENT_TYPE) response = self.client.put(path, json.dumps(expected), content_type=JSON_CONTENT_TYPE)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -190,19 +188,14 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase) ...@@ -190,19 +188,14 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
def assert_can_create_course(self, **request_kwargs): def assert_can_create_course(self, **request_kwargs):
""" Verify a course can be created by the view. """ """ Verify a course can be created by the view. """
course = CourseFactory.create() course = CourseFactory.create()
course_id = unicode(course.id) expected_modes = [CourseMode(mode_slug=u'verified', min_price=150, currency=u'USD', sku=u'ABC123'),
expected = { CourseMode(mode_slug=u'honor', min_price=0, currency=u'USD', sku=u'DEADBEEF')]
u'id': course_id, expected = self._serialize_course(course, expected_modes)
u'modes': [ path = reverse('commerce_api:v1:courses:retrieve_update', args=[unicode(course.id)])
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])
response = self.client.put(path, json.dumps(expected), content_type=JSON_CONTENT_TYPE, **request_kwargs) response = self.client.put(path, json.dumps(expected), content_type=JSON_CONTENT_TYPE, **request_kwargs)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
actual = json.loads(response.content) actual = json.loads(response.content)
self.assertEqual(actual, expected) 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