Commit faef420e by Matthew Piatetsky Committed by GitHub

Merge pull request #295 from edx/ECOM-5421

ECOM-5421 Add program types to search endpoint
parents 17013506 caf729cc
......@@ -45,7 +45,7 @@ COURSE_RUN_FACET_FIELD_QUERIES = {
COURSE_RUN_SEARCH_FIELDS = (
'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', 'published', 'partner',
'number', 'seat_types', 'image_url', 'type', 'level_type', 'availability', 'published', 'partner', 'program_types',
)
PROGRAM_FACET_FIELD_OPTIONS = {
......
......@@ -584,6 +584,7 @@ class AffiliateWindowSerializerTests(TestCase):
class CourseRunSearchSerializerTests(TestCase):
def test_data(self):
course_run = CourseRunFactory(transcript_languages=LanguageTag.objects.filter(code__in=['en-us', 'zh-cn']))
ProgramFactory(courses=[course_run.course])
serializer = self.serialize_course_run(course_run)
course_run_key = CourseKey.from_string(course_run.key)
......@@ -610,6 +611,7 @@ class CourseRunSearchSerializerTests(TestCase):
'availability': course_run.availability,
'published': course_run.status == CourseRun.Status.Published,
'partner': course_run.course.partner.short_code,
'program_types': course_run.program_types,
}
self.assertDictEqual(serializer.data, expected)
......
......@@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy as _
from django_extensions.db.fields import AutoSlugField
from django_extensions.db.models import TimeStampedModel
from djchoices import DjangoChoices, ChoiceItem
from haystack import connections
from haystack.query import SearchQuerySet
from simple_history.models import HistoricalRecords
from sortedm2m.fields import SortedManyToManyField
......@@ -307,6 +308,19 @@ class Course(TimeStampedModel):
ids = [result.pk for result in results]
return cls.objects.filter(pk__in=ids)
def save(self, *args, **kwargs):
super(Course, self).save(*args, **kwargs)
try:
self.reindex_course_runs()
except Exception: # pylint: disable=broad-except
logger.exception("An error occurred while attempting to reindex the course runs"
"of Course with key: [{key}].".format(key=self.key))
def reindex_course_runs(self):
index = connections['default'].get_unified_index().get_index(CourseRun)
for course_run in self.course_runs.all():
index.update_object(course_run)
class CourseRun(TimeStampedModel):
""" CourseRun model. """
......@@ -366,6 +380,10 @@ class CourseRun(TimeStampedModel):
objects = CourseRunQuerySet.as_manager()
@property
def program_types(self):
return [program.type.name for program in self.programs.all()]
@property
def marketing_url(self):
if self.slug:
path = 'course/{slug}'.format(slug=self.slug)
......@@ -724,6 +742,17 @@ class Program(TimeStampedModel):
publisher.publish_program(self)
else:
super(Program, self).save(*args, **kwargs)
self.reindex_courses()
def reindex_courses(self):
try:
index = connections['default'].get_unified_index().get_index(Course)
for course in self.courses.all():
index.update_object(course)
course.reindex_course_runs()
except Exception: # pylint: disable=broad-except
logger.exception("An error occurred while attempting to reindex the courses"
"of Program with uuid: [{uuid}].".format(uuid=self.uuid))
class PersonSocialNetwork(AbstractSocialNetworkModel):
......
......@@ -112,6 +112,7 @@ class CourseRunIndex(BaseCourseIndex, indexes.Indexable):
type = indexes.CharField(model_attr='type', null=True, faceted=True)
image_url = indexes.CharField(model_attr='card_image_url', null=True)
partner = indexes.CharField(null=True, faceted=True)
program_types = indexes.MultiValueField()
published = indexes.BooleanField(null=False, faceted=True)
def prepare_partner(self, obj):
......@@ -147,6 +148,9 @@ class CourseRunIndex(BaseCourseIndex, indexes.Indexable):
def prepare_marketing_url(self, obj):
return obj.marketing_url
def prepare_program_types(self, obj):
return obj.program_types
class ProgramIndex(BaseIndex, indexes.Indexable, OrganizationsMixin):
model = Program
......
......@@ -2,6 +2,7 @@ import itertools
from decimal import Decimal
import ddt
import mock
from dateutil.parser import parse
from django.conf import settings
from django.db import IntegrityError
......@@ -15,8 +16,8 @@ from course_discovery.apps.core.tests.helpers import make_image_file
from course_discovery.apps.core.utils import SearchQuerySetWrapper
from course_discovery.apps.course_metadata.models import (
AbstractMediaModel, AbstractNamedModel, AbstractValueModel,
CorporateEndorsement, Course, CourseRun, Endorsement,
FAQ, SeatType, Program
CorporateEndorsement, Program, Course, CourseRun, Endorsement,
FAQ, SeatType
)
from course_discovery.apps.course_metadata.tests import factories, toggle_switch
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, ImageFactory
......@@ -47,6 +48,11 @@ class CourseTests(TestCase):
actual = list(Course.search(query).order_by('key'))
self.assertEqual(actual, courses)
def test_course_run_update_caught_exception(self):
""" Test that the index update process failing will not cause the course save to error """
with mock.patch.object(Course, 'reindex_course_runs', side_effect=Exception):
self.course.save()
@ddt.ddt
class CourseRunTests(TestCase):
......@@ -150,6 +156,13 @@ class CourseRunTests(TestCase):
course_run = CourseRunFactory(slug=slug)
self.assertIsNone(course_run.marketing_url)
def test_program_types(self):
""" Verify the property retrieves program types correctly based on programs. """
courses = [self.course_run.course]
program = factories.ProgramFactory(courses=courses)
other_program = factories.ProgramFactory(courses=courses)
self.assertCountEqual(self.course_run.program_types, [program.type.name, other_program.type.name])
class OrganizationTests(TestCase):
""" Tests for the `Organization` model. """
......@@ -409,6 +422,11 @@ class ProgramTests(MarketingSitePublisherTestMixin, TestCase):
self.assertEqual(len(responses.calls), 0)
toggle_switch('publish_program_to_marketing_site', False)
def test_course_update_caught_exception(self):
""" Test that the index update process failing will not cause the program save to error """
with mock.patch.object(Course, 'reindex_course_runs', side_effect=Exception):
self.program.save()
class PersonSocialNetworkTests(TestCase):
"""Tests of the PersonSocialNetwork 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