Commit 7ebb34b9 by Matt Tuchfarber Committed by Matt Tuchfarber

Adds an instructors field to the program

This allows the program creator to determine the order of the instructors on the program marketing page.
parent 7f7dbed3
......@@ -891,6 +891,7 @@ class ProgramSerializer(MinimalProgramSerializer):
)
subjects = SubjectSerializer(many=True)
staff = PersonSerializer(many=True)
instructors = PersonSerializer(many=True)
applicable_seat_types = serializers.SerializerMethodField()
@classmethod
......@@ -907,6 +908,7 @@ class ProgramSerializer(MinimalProgramSerializer):
'expected_learning_items',
'faq',
'job_outlook_items',
'instructors',
# `type` is serialized by a third-party serializer. Providing this field name allows us to
# prefetch `applicable_seat_types`, a m2m on `ProgramType`, through `type`, a foreign key to
# `ProgramType` on `Program`.
......@@ -930,7 +932,7 @@ class ProgramSerializer(MinimalProgramSerializer):
'min_hours_effort_per_week', 'max_hours_effort_per_week', 'video', 'expected_learning_items',
'faq', 'credit_backing_organizations', 'corporate_endorsements', 'job_outlook_items',
'individual_endorsements', 'languages', 'transcript_languages', 'subjects', 'price_ranges',
'staff', 'credit_redemption_overview', 'applicable_seat_types'
'staff', 'credit_redemption_overview', 'applicable_seat_types', 'instructors'
)
......
......@@ -680,6 +680,7 @@ class ProgramSerializerTests(MinimalProgramSerializerTests):
program.individual_endorsements, many=True, context={'request': request}
).data,
'staff': PersonSerializer(program.staff, many=True, context={'request': request}).data,
'instructors': PersonSerializer(program.instructors, many=True, context={'request': request}).data,
'job_outlook_items': [item.value for item in program.job_outlook_items.all()],
'languages': [serialize_language_to_code(l) for l in program.languages],
'weeks_to_complete': program.weeks_to_complete,
......
......@@ -59,6 +59,7 @@ class TestProgramViewSet(SerializationMixin):
individual_endorsements=EndorsementFactory.create_batch(1),
expected_learning_items=ExpectedLearningItemFactory.create_batch(1),
job_outlook_items=JobOutlookItemFactory.create_batch(1),
instructors=PersonFactory.create_batch(1),
banner_image=make_image_file('test_banner.jpg'),
video=VideoFactory(),
partner=self.partner
......@@ -88,7 +89,7 @@ class TestProgramViewSet(SerializationMixin):
def test_retrieve(self, django_assert_num_queries):
""" Verify the endpoint returns the details for a single program. """
program = self.create_program()
with django_assert_num_queries(41):
with django_assert_num_queries(47):
response = self.assert_retrieve_success(program)
# property does not have the right values while being indexed
del program._course_run_weeks_to_complete
......@@ -114,7 +115,7 @@ class TestProgramViewSet(SerializationMixin):
partner=self.partner)
# property does not have the right values while being indexed
del program._course_run_weeks_to_complete
with django_assert_num_queries(30):
with django_assert_num_queries(31):
response = self.assert_retrieve_success(program)
assert response.data == self.serialize_program(program)
assert course_list == list(program.courses.all()) # pylint: disable=no-member
......@@ -123,7 +124,7 @@ class TestProgramViewSet(SerializationMixin):
""" Verify the endpoint returns data for a program even if the program's courses have no course runs. """
course = CourseFactory(partner=self.partner)
program = ProgramFactory(courses=[course], partner=self.partner)
with django_assert_num_queries(23):
with django_assert_num_queries(24):
response = self.assert_retrieve_success(program)
assert response.data == self.serialize_program(program)
......
......@@ -119,7 +119,7 @@ class ProgramAdmin(admin.ModelAdmin):
raw_id_fields = ('video',)
search_fields = ('uuid', 'title', 'marketing_slug')
filter_horizontal = ('job_outlook_items', 'expected_learning_items',)
filter_horizontal = ('job_outlook_items', 'expected_learning_items', 'instructors')
# ordering the field display on admin page.
fields = (
......
......@@ -6,7 +6,7 @@ from course_discovery.apps.core.models import Partner
from course_discovery.apps.course_metadata.models import Program
from course_discovery.apps.course_metadata.tests.factories import (
CorporateEndorsementFactory, CourseFactory, CourseRunFactory, EndorsementFactory, ExpectedLearningItemFactory,
FAQFactory, JobOutlookItemFactory, OrganizationFactory, ProgramFactory
FAQFactory, JobOutlookItemFactory, OrganizationFactory, PersonFactory, ProgramFactory
)
logger = logging.getLogger(__name__)
......@@ -47,5 +47,6 @@ class Command(BaseCommand):
individual_endorsements=[EndorsementFactory(endorser__partner=partner)],
expected_learning_items=[ExpectedLearningItemFactory()],
faq=[FAQFactory()],
job_outlook_items=[JobOutlookItemFactory()]
job_outlook_items=[JobOutlookItemFactory()],
instructors=[PersonFactory()],
)
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2017-11-29 15:20
from __future__ import unicode_literals
import sortedm2m.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('course_metadata', '0072_courseentitlement_partner'),
]
operations = [
migrations.AddField(
model_name='program',
name='instructors',
field=sortedm2m.fields.SortedManyToManyField(blank=True, help_text='Used to organize the instructors on the program about page.', to='course_metadata.Person'),
),
]
......@@ -959,6 +959,11 @@ class Program(TimeStampedModel):
video = models.ForeignKey(Video, default=None, null=True, blank=True)
expected_learning_items = SortedManyToManyField(ExpectedLearningItem, blank=True)
faq = SortedManyToManyField(FAQ, blank=True)
instructors = SortedManyToManyField(
Person,
blank=True,
help_text='Used to organize the instructors on the program about page.'
)
credit_backing_organizations = SortedManyToManyField(
Organization, blank=True, related_name='credit_backed_programs'
......
......@@ -39,7 +39,7 @@
{{ subject.name }}
{% endfor %}
{% for instructor in object.instructors %}
{% for instructor in object.instructors.all %}
{{ instructor.full_name }}
{% endfor %}
......
......@@ -323,6 +323,11 @@ class ProgramFactory(factory.django.DjangoModelFactory):
if create: # pragma: no cover
add_m2m_data(self.job_outlook_items, extracted)
@factory.post_generation
def instructors(self, create, extracted, **kwargs):
if create: # pragma: no cover
add_m2m_data(self.instructors, extracted)
class AbstractSocialNetworkModelFactory(factory.DjangoModelFactory):
type = FuzzyChoice([name for name, __ in AbstractSocialNetworkModel.SOCIAL_NETWORK_CHOICES])
......
......@@ -294,6 +294,7 @@ class ProgramAdminFunctionalTests(SiteMixin, LiveServerTestCase):
'field-excluded_course_runs', 'field-authoring_organizations', 'field-credit_backing_organizations',
'field-one_click_purchase_enabled', 'field-hidden', 'field-corporate_endorsements', 'field-faq',
'field-individual_endorsements', 'field-job_outlook_items', 'field-expected_learning_items',
'field-instructors',
]
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