Commit 9d1520d0 by Clinton Blackburn Committed by Clinton Blackburn

Updated course and course run API endpoints to use stored images

If a course has an image on the image field, that image's URL will be
used in the API response. Course runs now use the related course's image.

LEARNER-2751
parent b9655bb1
...@@ -419,7 +419,7 @@ class NestedProgramSerializer(serializers.ModelSerializer): ...@@ -419,7 +419,7 @@ class NestedProgramSerializer(serializers.ModelSerializer):
class MinimalCourseRunSerializer(TimestampModelSerializer): class MinimalCourseRunSerializer(TimestampModelSerializer):
image = ImageField(read_only=True, source='card_image_url') image = ImageField(read_only=True, source='image_url')
marketing_url = serializers.SerializerMethodField() marketing_url = serializers.SerializerMethodField()
seats = SeatSerializer(many=True) seats = SeatSerializer(many=True)
...@@ -528,7 +528,7 @@ class ContainedCourseRunsSerializer(serializers.Serializer): ...@@ -528,7 +528,7 @@ class ContainedCourseRunsSerializer(serializers.Serializer):
class MinimalCourseSerializer(TimestampModelSerializer): class MinimalCourseSerializer(TimestampModelSerializer):
course_runs = MinimalCourseRunSerializer(many=True) course_runs = MinimalCourseRunSerializer(many=True)
owners = MinimalOrganizationSerializer(many=True, source='authoring_organizations') owners = MinimalOrganizationSerializer(many=True, source='authoring_organizations')
image = ImageField(read_only=True, source='card_image_url') image = ImageField(read_only=True, source='image_url')
@classmethod @classmethod
def prefetch_queryset(cls, queryset=None, course_runs=None): def prefetch_queryset(cls, queryset=None, course_runs=None):
......
...@@ -111,7 +111,7 @@ class MinimalCourseSerializerTests(SiteMixin, TestCase): ...@@ -111,7 +111,7 @@ class MinimalCourseSerializerTests(SiteMixin, TestCase):
'title': course.title, 'title': course.title,
'course_runs': MinimalCourseRunSerializer(course.course_runs, many=True, context=context).data, 'course_runs': MinimalCourseRunSerializer(course.course_runs, many=True, context=context).data,
'owners': MinimalOrganizationSerializer(course.authoring_organizations, many=True, context=context).data, 'owners': MinimalOrganizationSerializer(course.authoring_organizations, many=True, context=context).data,
'image': ImageField().to_representation(course.card_image_url), 'image': ImageField().to_representation(course.image_url),
'short_description': course.short_description 'short_description': course.short_description
} }
...@@ -220,7 +220,7 @@ class MinimalCourseRunSerializerTests(TestCase): ...@@ -220,7 +220,7 @@ class MinimalCourseRunSerializerTests(TestCase):
'uuid': str(course_run.uuid), 'uuid': str(course_run.uuid),
'title': course_run.title, 'title': course_run.title,
'short_description': course_run.short_description, 'short_description': course_run.short_description,
'image': ImageField().to_representation(course_run.card_image_url), 'image': ImageField().to_representation(course_run.image_url),
'marketing_url': '{url}?{params}'.format( 'marketing_url': '{url}?{params}'.format(
url=course_run.marketing_url, url=course_run.marketing_url,
params=urlencode({ params=urlencode({
......
...@@ -294,6 +294,7 @@ class Course(TimeStampedModel): ...@@ -294,6 +294,7 @@ class Course(TimeStampedModel):
) )
slug = AutoSlugField(populate_from='key', editable=True) slug = AutoSlugField(populate_from='key', editable=True)
video = models.ForeignKey(Video, default=None, null=True, blank=True) video = models.ForeignKey(Video, default=None, null=True, blank=True)
# TODO Remove this field. # TODO Remove this field.
number = models.CharField( number = models.CharField(
max_length=50, null=True, blank=True, help_text=_( max_length=50, null=True, blank=True, help_text=_(
...@@ -313,6 +314,13 @@ class Course(TimeStampedModel): ...@@ -313,6 +314,13 @@ class Course(TimeStampedModel):
return '{key}: {title}'.format(key=self.key, title=self.title) return '{key}: {title}'.format(key=self.key, title=self.title)
@property @property
def image_url(self):
if self.image:
return self.image.url
return self.card_image_url
@property
def marketing_url(self): def marketing_url(self):
url = None url = None
if self.partner.marketing_site_url_root: if self.partner.marketing_site_url_root:
...@@ -404,6 +412,8 @@ class CourseRun(TimeStampedModel): ...@@ -404,6 +412,8 @@ class CourseRun(TimeStampedModel):
pacing_type = models.CharField(max_length=255, db_index=True, null=True, blank=True, pacing_type = models.CharField(max_length=255, db_index=True, null=True, blank=True,
choices=CourseRunPacing.choices, validators=[CourseRunPacing.validator]) choices=CourseRunPacing.choices, validators=[CourseRunPacing.validator])
syllabus = models.ForeignKey(SyllabusItem, default=None, null=True, blank=True) syllabus = models.ForeignKey(SyllabusItem, default=None, null=True, blank=True)
# TODO Ditch this, and fallback to the course
card_image_url = models.URLField(null=True, blank=True) card_image_url = models.URLField(null=True, blank=True)
video = models.ForeignKey(Video, default=None, null=True, blank=True) video = models.ForeignKey(Video, default=None, null=True, blank=True)
video_translation_languages = models.ManyToManyField( video_translation_languages = models.ManyToManyField(
...@@ -496,6 +506,10 @@ class CourseRun(TimeStampedModel): ...@@ -496,6 +506,10 @@ class CourseRun(TimeStampedModel):
return enrollable_seats return enrollable_seats
@property @property
def image_url(self):
return self.course.image_url
@property
def program_types(self): def program_types(self):
""" """
Exclude unpublished and deleted programs from list Exclude unpublished and deleted programs from list
......
...@@ -59,6 +59,13 @@ class CourseTests(ElasticsearchTestMixin, TestCase): ...@@ -59,6 +59,13 @@ class CourseTests(ElasticsearchTestMixin, TestCase):
self.assertEqual(actual, courses) self.assertEqual(actual, courses)
def test_image_url(self):
course = factories.CourseFactory()
assert course.image_url == course.image.url
course.image = None
assert course.image_url == course.card_image_url
@ddt.ddt @ddt.ddt
class CourseRunTests(TestCase): class CourseRunTests(TestCase):
...@@ -321,6 +328,9 @@ class CourseRunTests(TestCase): ...@@ -321,6 +328,9 @@ class CourseRunTests(TestCase):
# We don't want to delete course run nodes when CourseRuns are deleted. # We don't want to delete course run nodes when CourseRuns are deleted.
assert not mock_delete_obj.called assert not mock_delete_obj.called
def test_image_url(self):
assert self.course_run.image_url == self.course_run.course.image_url
class OrganizationTests(TestCase): class OrganizationTests(TestCase):
""" Tests for the `Organization` model. """ """ Tests for the `Organization` model. """
......
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