Commit c7179fce by Clinton Blackburn Committed by GitHub

Merge pull request #180 from edx/clintonb/api-updates

API Updates
parents bdbdb5b9 30cc8db5
......@@ -39,11 +39,15 @@ COURSE_RUN_FACET_FIELD_QUERIES = {
'availability_archived': {'query': 'end:<=now'},
}
COURSE_RUN_SEARCH_FIELDS = (
'key', 'title', 'short_description', 'full_description', 'start', 'end', 'enrollment_start', 'enrollment_end',
'pacing_type', 'language', 'transcript_languages', 'marketing_url', 'content_type', 'org', 'number', 'seat_types',
'image_url', 'type', 'text',
'text', 'key', 'title', 'short_description', 'full_description', 'start', 'end', 'enrollment_start',
'enrollment_end', 'pacing_type', 'language', 'transcript_languages', 'marketing_url', 'content_type', 'org',
'number', 'seat_types', 'image_url', 'type', 'level_type', 'availability',
)
PROGRAM_FACET_FIELD_OPTIONS = {
'category': {},
}
PROGRAM_SEARCH_FIELDS = (
'uuid', 'title', 'subtitle', 'category', 'marketing_url', 'organizations', 'content_type', 'image_url', 'text',
)
......@@ -194,6 +198,7 @@ class CourseRunSerializer(TimestampModelSerializer):
instructors = PersonSerializer(many=True)
staff = PersonSerializer(many=True)
marketing_url = serializers.SerializerMethodField()
level_type = serializers.SlugRelatedField(read_only=True, slug_field='name')
class Meta(object):
model = CourseRun
......@@ -201,7 +206,7 @@ class CourseRunSerializer(TimestampModelSerializer):
'course', 'key', 'title', 'short_description', 'full_description', 'start', 'end',
'enrollment_start', 'enrollment_end', 'announcement', 'image', 'video', 'seats',
'content_language', 'transcript_languages', 'instructors', 'staff',
'pacing_type', 'min_effort', 'max_effort', 'modified', 'marketing_url',
'pacing_type', 'min_effort', 'max_effort', 'modified', 'marketing_url', 'level_type', 'availability',
)
def get_marketing_url(self, obj):
......@@ -479,6 +484,11 @@ class CourseFacetSerializer(BaseHaystackFacetSerializer):
class CourseRunSearchSerializer(HaystackSerializer):
availability = serializers.SerializerMethodField()
def get_availability(self, result):
return result.object.availability
class Meta:
field_aliases = COMMON_SEARCH_FIELD_ALIASES
fields = COURSE_RUN_SEARCH_FIELDS
......@@ -499,6 +509,7 @@ class CourseRunFacetSerializer(BaseHaystackFacetSerializer):
class ProgramSearchSerializer(HaystackSerializer):
class Meta:
field_aliases = COMMON_SEARCH_FIELD_ALIASES
field_options = PROGRAM_FACET_FIELD_OPTIONS
fields = PROGRAM_SEARCH_FIELDS
ignore_fields = COMMON_IGNORED_FIELDS
index_classes = [ProgramIndex]
......@@ -509,6 +520,7 @@ class ProgramFacetSerializer(BaseHaystackFacetSerializer):
class Meta:
field_aliases = COMMON_SEARCH_FIELD_ALIASES
field_options = PROGRAM_FACET_FIELD_OPTIONS
fields = PROGRAM_SEARCH_FIELDS
ignore_fields = COMMON_IGNORED_FIELDS
index_classes = [ProgramIndex]
......@@ -517,7 +529,7 @@ class ProgramFacetSerializer(BaseHaystackFacetSerializer):
class AggregateSearchSerializer(HaystackSerializer):
class Meta:
field_aliases = COMMON_SEARCH_FIELD_ALIASES
fields = COURSE_RUN_SEARCH_FIELDS
fields = COURSE_RUN_SEARCH_FIELDS + PROGRAM_SEARCH_FIELDS
ignore_fields = COMMON_IGNORED_FIELDS
serializers = {
CourseRunIndex: CourseRunSearchSerializer,
......@@ -531,7 +543,7 @@ class AggregateFacetSearchSerializer(BaseHaystackFacetSerializer):
class Meta:
field_aliases = COMMON_SEARCH_FIELD_ALIASES
field_options = COURSE_RUN_FACET_FIELD_OPTIONS
field_options = {**COURSE_RUN_FACET_FIELD_OPTIONS, **PROGRAM_FACET_FIELD_OPTIONS}
field_queries = COURSE_RUN_FACET_FIELD_QUERIES
ignore_fields = COMMON_IGNORED_FIELDS
serializers = {
......
......@@ -152,6 +152,8 @@ class CourseRunSerializerTests(TestCase):
'utm_medium': request.user.referral_tracking_id,
})
),
'level_type': course_run.level_type.name,
'availability': course_run.availability,
}
self.assertDictEqual(serializer.data, expected)
......@@ -375,6 +377,8 @@ class CourseRunSearchSerializerTests(TestCase):
'seat_types': course_run.seat_types,
'image_url': course_run.image_url,
'type': course_run.type,
'level_type': course_run.level_type.name,
'availability': course_run.availability,
}
self.assertDictEqual(serializer.data, expected)
......@@ -398,7 +402,7 @@ class ProgramSearchSerializerTests(TestCase):
'category': program.category,
'marketing_url': program.marketing_url,
'organizations': [OrganizationsMixin.format_organization(organization)],
'content_type': 'program_{category}'.format(category=program.category),
'content_type': 'program',
'image_url': program.image_url,
}
self.assertDictEqual(serializer.data, expected)
......@@ -356,6 +356,24 @@ class CourseRun(TimeStampedModel):
return None
@property
def level_type(self):
return self.course.level_type
@property
def availability(self):
now = datetime.datetime.now(pytz.UTC)
upcoming_cutoff = now + datetime.timedelta(days=60)
if self.end and self.end <= now:
return _('Archived')
elif self.start and self.end and (self.start <= now < self.end):
return _('Current')
elif self.start and (now < self.start < upcoming_cutoff):
return _('Starting Soon')
else:
return _('Upcoming')
@classmethod
def search(cls, query):
""" Queries the search index.
......
......@@ -39,6 +39,7 @@ class BaseCourseIndex(OrganizationsMixin, BaseIndex):
full_description = indexes.CharField(model_attr='full_description', null=True)
subjects = indexes.MultiValueField(faceted=True)
organizations = indexes.MultiValueField(faceted=True)
level_type = indexes.CharField(model_attr='level_type__name', null=True, faceted=True)
def prepare_subjects(self, obj):
return [subject.name for subject in obj.subjects.all()]
......@@ -47,7 +48,6 @@ class BaseCourseIndex(OrganizationsMixin, BaseIndex):
class CourseIndex(BaseCourseIndex, indexes.Indexable):
model = Course
level_type = indexes.CharField(model_attr='level_type__name', null=True, faceted=True)
course_runs = indexes.MultiValueField()
expected_learning_items = indexes.MultiValueField()
......@@ -114,6 +114,3 @@ class ProgramIndex(OrganizationsMixin, BaseIndex, indexes.Indexable):
marketing_url = indexes.CharField(model_attr='marketing_url', null=True)
organizations = indexes.MultiValueField(faceted=True)
image_url = indexes.CharField(model_attr='image_url', null=True)
def prepare_content_type(self, obj):
return 'program_{category}'.format(category=obj.category)
......@@ -3,9 +3,11 @@ import datetime
import ddt
import mock
import pytz
from django.db import IntegrityError
from dateutil.parser import parse
from django.conf import settings
from django.db import IntegrityError
from django.test import TestCase
from freezegun import freeze_time
from course_discovery.apps.core.utils import SearchQuerySetWrapper
from course_discovery.apps.course_metadata.models import (
......@@ -178,6 +180,30 @@ class CourseRunTests(TestCase):
factories.SeatFactory(course_run=self.course_run, type=seat_type)
self.assert_course_run_has_no_type(self.course_run, set([seat_type]))
def test_level_type(self):
""" Verify the property returns the associated Course's level type. """
self.assertEqual(self.course_run.level_type, self.course_run.course.level_type)
@freeze_time('2016-06-21 00:00:00Z')
@ddt.data(
(None, None, 'Upcoming'),
('2030-01-01 00:00:00Z', None, 'Upcoming'),
(None, '2016-01-01 00:00:00Z', 'Archived'),
('2015-01-01 00:00:00Z', '2016-01-01 00:00:00Z', 'Archived'),
('2016-01-01 00:00:00Z', '2017-01-01 00:00:00Z', 'Current'),
('2016-07-21 00:00:00Z', '2017-01-01 00:00:00Z', 'Starting Soon'),
)
@ddt.unpack
def test_availability(self, start, end, expected_availability):
""" Verify the property returns the appropriate availability string based on the start/end dates. """
if start:
start = parse(start)
if end:
end = parse(end)
course_run = factories.CourseRunFactory(start=start, end=end)
self.assertEqual(course_run.availability, expected_availability)
class OrganizationTests(TestCase):
""" Tests for the `Organization` model. """
......@@ -272,6 +298,7 @@ class ProgramTests(TestCase):
class PersonSocialNetworkTests(TestCase):
"""Tests of the PersonSocialNetwork model."""
def setUp(self):
super(PersonSocialNetworkTests, self).setUp()
self.network = factories.PersonSocialNetworkFactory()
......@@ -294,6 +321,7 @@ class PersonSocialNetworkTests(TestCase):
class CourseSocialNetworkTests(TestCase):
"""Tests of the CourseSocialNetwork model."""
def setUp(self):
super(CourseSocialNetworkTests, self).setUp()
self.network = factories.CourseRunSocialNetworkFactory()
......
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