diff --git a/cms/djangoapps/api/v1/serializers/course_runs.py b/cms/djangoapps/api/v1/serializers/course_runs.py index 9c3c5a67c2..27e372efc1 100644 --- a/cms/djangoapps/api/v1/serializers/course_runs.py +++ b/cms/djangoapps/api/v1/serializers/course_runs.py @@ -85,6 +85,14 @@ class CourseRunImageField(serializers.ImageField): return request.build_absolute_uri(value) +class CourseRunPacingTypeField(serializers.ChoiceField): + def to_representation(self, value): + return 'self_paced' if value else 'instructor_paced' + + def to_internal_value(self, data): + return data == 'self_paced' + + class CourseRunImageSerializer(serializers.Serializer): # We set an empty default to prevent the parent serializer from attempting # to save this value to the Course object. @@ -100,10 +108,15 @@ class CourseRunImageSerializer(serializers.Serializer): return instance -class CourseRunSerializer(CourseRunTeamSerializerMixin, serializers.Serializer): +class CourseRunSerializerCommonFieldsMixin(serializers.Serializer): + schedule = CourseRunScheduleSerializer(source='*', required=False) + pacing_type = CourseRunPacingTypeField(source='self_paced', required=False, + choices=(('instructor_paced', False), ('self_paced', True),)) + + +class CourseRunSerializer(CourseRunSerializerCommonFieldsMixin, CourseRunTeamSerializerMixin, serializers.Serializer): id = serializers.CharField(read_only=True) title = serializers.CharField(source='display_name') - schedule = CourseRunScheduleSerializer(source='*', required=False) images = CourseRunImageSerializer(source='*', required=False) def update(self, instance, validated_data): @@ -135,10 +148,10 @@ class CourseRunCreateSerializer(CourseRunSerializer): return instance -class CourseRunRerunSerializer(CourseRunTeamSerializerMixin, serializers.Serializer): +class CourseRunRerunSerializer(CourseRunSerializerCommonFieldsMixin, CourseRunTeamSerializerMixin, + serializers.Serializer): title = serializers.CharField(source='display_name', required=False) run = serializers.CharField(source='id.run') - schedule = CourseRunScheduleSerializer(source='*', required=False) def validate_run(self, value): course_run_key = self.instance.id diff --git a/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py b/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py index 7cd5460689..152a3e967c 100644 --- a/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py +++ b/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py @@ -1,5 +1,6 @@ import datetime +import ddt import pytz from django.test import RequestFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase @@ -12,14 +13,27 @@ from ..utils import serialize_datetime from ...serializers.course_runs import CourseRunSerializer +@ddt.ddt class CourseRunSerializerTests(ModuleStoreTestCase): - def test_data(self): + + @ddt.data( + ('instructor_paced', False), + ('self_paced', True), + ) + @ddt.unpack + def test_data(self, expected_pacing_type, self_paced): start = datetime.datetime.now(pytz.UTC) end = start + datetime.timedelta(days=30) enrollment_start = start - datetime.timedelta(days=7) enrollment_end = end - datetime.timedelta(days=14) - course = CourseFactory(start=start, end=end, enrollment_start=enrollment_start, enrollment_end=enrollment_end) + course = CourseFactory( + start=start, + end=end, + enrollment_start=enrollment_start, + enrollment_end=enrollment_end, + self_paced=self_paced + ) instructor = UserFactory() CourseInstructorRole(course.id).add_users(instructor) staff = UserFactory() @@ -48,6 +62,7 @@ class CourseRunSerializerTests(ModuleStoreTestCase): ], 'images': { 'card_image': request.build_absolute_uri(course_image_url(course)), - } + }, + 'pacing_type': expected_pacing_type, } assert serializer.data == expected diff --git a/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py b/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py index 842efd0fd6..30256a117c 100644 --- a/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py +++ b/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py @@ -1,5 +1,6 @@ import datetime +import ddt import pytz from django.core.files.uploadedfile import SimpleUploadedFile from django.core.urlresolvers import reverse @@ -20,6 +21,7 @@ from ..utils import serialize_datetime from ...serializers.course_runs import CourseRunSerializer +@ddt.ddt class CourseRunViewSetTests(ModuleStoreTestCase): list_url = reverse('api:v1:course_run-list') @@ -139,7 +141,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): def test_partial_update(self): role = 'staff' start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) - course_run = CourseFactory(start=start, end=None, enrollment_start=None, enrollment_end=None) + course_run = CourseFactory(start=start, end=None, enrollment_start=None, enrollment_end=None, self_paced=False) # The request should only update or create new team members existing_user = UserFactory() @@ -159,6 +161,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): 'role': role, }, ], + 'pacing_type': 'self_paced', } url = reverse('api:v1:course_run-detail', kwargs={'pk': str(course_run.id)}) @@ -169,9 +172,15 @@ class CourseRunViewSetTests(ModuleStoreTestCase): self.assert_course_access_role_count(course_run, 2) course_run = modulestore().get_course(course_run.id) + assert course_run.self_paced is True self.assert_course_run_schedule(course_run, start, None, None, None) - def test_create(self): + @ddt.data( + ('instructor_paced', False), + ('self_paced', True), + ) + @ddt.unpack + def test_create(self, pacing_type, expected_self_paced_value): start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) end = start + datetime.timedelta(days=30) enrollment_start = start - datetime.timedelta(days=7) @@ -195,6 +204,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): 'role': role, } ], + 'pacing_type': pacing_type, } response = self.client.post(self.list_url, data, format='json') assert response.status_code == 201 @@ -205,6 +215,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): assert course_run.id.org == data['org'] assert course_run.id.course == data['number'] assert course_run.id.run == data['run'] + assert course_run.self_paced is expected_self_paced_value self.assert_course_run_schedule(course_run, start, end, enrollment_start, enrollment_end) self.assert_access_role(course_run, user, role) self.assert_course_access_role_count(course_run, 1) @@ -242,7 +253,12 @@ class CourseRunViewSetTests(ModuleStoreTestCase): # There should now be an image stored contentstore().find(content_key) - def test_rerun(self): + @ddt.data( + ('instructor_paced', False), + ('self_paced', True), + ) + @ddt.unpack + def test_rerun(self, pacing_type, expected_self_paced_value): course_run = ToyCourseFactory() start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) end = start + datetime.timedelta(days=30) @@ -264,6 +280,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): 'role': role, } ], + 'pacing_type': pacing_type, } response = self.client.post(url, data, format='json') assert response.status_code == 201 @@ -271,6 +288,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): course_run_key = CourseKey.from_string(response.data['id']) course_run = modulestore().get_course(course_run_key) assert course_run.id.run == run + assert course_run.self_paced is expected_self_paced_value self.assert_course_run_schedule(course_run, start, end, None, None) self.assert_access_role(course_run, user, role) self.assert_course_access_role_count(course_run, 1)