From 51e53587dcac457671df86c0f6468130db262233 Mon Sep 17 00:00:00 2001 From: Afzal Wali Date: Tue, 9 Dec 2014 16:10:20 +0500 Subject: [PATCH] WL-105 Show course cards sorted by start dates, oldest first. --- AUTHORS | 1 + common/djangoapps/student/views.py | 8 +++- lms/djangoapps/branding/tests.py | 69 ++++++++++++++++++++++++++++ lms/djangoapps/courseware/courses.py | 9 ++++ lms/envs/common.py | 5 ++ 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 33d5742e4d..2d24a82d0d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -182,3 +182,4 @@ Louis Pilfold Akiva Leffert Mike Bifulco Jim Zheng +Afzal Wali diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 8b6292eecf..450d29fe8c 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -68,7 +68,7 @@ from xmodule.modulestore import ModuleStoreEnum from collections import namedtuple -from courseware.courses import get_courses, sort_by_announcement +from courseware.courses import get_courses, sort_by_announcement, sort_by_start_date # pylint: disable=import-error from courseware.access import has_access from django_comment_common.models import Role @@ -146,7 +146,11 @@ def index(request, extra_context=None, user=AnonymousUser()): domain = request.META.get('HTTP_HOST') courses = get_courses(user, domain=domain) - courses = sort_by_announcement(courses) + if microsite.get_value("ENABLE_COURSE_SORTING_BY_START_DATE", + settings.FEATURES["ENABLE_COURSE_SORTING_BY_START_DATE"]): + courses = sort_by_start_date(courses) + else: + courses = sort_by_announcement(courses) context = {'courses': courses} diff --git a/lms/djangoapps/branding/tests.py b/lms/djangoapps/branding/tests.py index 520b00a4b5..7d4ff3460c 100644 --- a/lms/djangoapps/branding/tests.py +++ b/lms/djangoapps/branding/tests.py @@ -8,6 +8,8 @@ from django.http import HttpResponseRedirect from django.test.utils import override_settings from django.test.client import RequestFactory from pytz import UTC +from mock import patch, Mock +from edxmako.shortcuts import render_to_response from branding.views import index from xmodule.modulestore.tests.django_utils import TEST_DATA_MOCK_MODULESTORE @@ -22,6 +24,15 @@ FEATURES_WO_STARTDATE = settings.FEATURES.copy() FEATURES_WO_STARTDATE['DISABLE_START_DATES'] = True +def mock_render_to_response(*args, **kwargs): + """ + Mock the render_to_response function + """ + return render_to_response(*args, **kwargs) + +RENDER_MOCK = Mock(side_effect=mock_render_to_response) + + @override_settings(MODULESTORE=TEST_DATA_MOCK_MODULESTORE) class AnonymousIndexPageTest(ModuleStoreTestCase): """ @@ -94,3 +105,61 @@ class AnonymousIndexPageTest(ModuleStoreTestCase): self.assertIsInstance(response, HttpResponseRedirect) # Location should be "/login". self.assertEqual(response._headers.get("location")[1], "/login") + + +@override_settings(MODULESTORE=TEST_DATA_MOCK_MODULESTORE) +class IndexPageCourseCardsSortingTests(ModuleStoreTestCase): + """ + Test for Index page course cards sorting + """ + def setUp(self): + super(IndexPageCourseCardsSortingTests, self).setUp() + self.starting_later = CourseFactory.create( + org='MITx', + number='1000', + display_name='Starting later, Announced later', + metadata={ + 'start': datetime.datetime.now(UTC) + datetime.timedelta(days=4), + 'announcement': datetime.datetime.now(UTC) + datetime.timedelta(days=3), + } + ) + self.starting_earlier = CourseFactory.create( + org='MITx', + number='1001', + display_name='Starting earlier, Announced earlier', + metadata={ + 'start': datetime.datetime.now(UTC) + datetime.timedelta(days=2), + 'announcement': datetime.datetime.now(UTC) + datetime.timedelta(days=1), + } + ) + self.course_with_default_start_date = CourseFactory.create( + org='MITx', + number='1002', + display_name='Tech Beta Course', + ) + self.factory = RequestFactory() + + @patch('student.views.render_to_response', RENDER_MOCK) + def test_course_cards_sorted_by_default_sorting(self): + response = self.client.get('/') + self.assertEqual(response.status_code, 200) + ((template, context), _) = RENDER_MOCK.call_args + self.assertEqual(template, 'index.html') + + # Now the courses will be stored in their announcement dates. + self.assertEqual(context['courses'][0].id, self.starting_later.id) + self.assertEqual(context['courses'][1].id, self.starting_earlier.id) + self.assertEqual(context['courses'][2].id, self.course_with_default_start_date.id) + + @patch('student.views.render_to_response', RENDER_MOCK) + @patch.dict('django.conf.settings.FEATURES', {'ENABLE_COURSE_SORTING_BY_START_DATE': True}) + def test_course_cards_sorted_by_start_date_show_earliest_first(self): + response = self.client.get('/') + self.assertEqual(response.status_code, 200) + ((template, context), _) = RENDER_MOCK.call_args + self.assertEqual(template, 'index.html') + + # now the courses will be sorted by their creation dates, earliest first. + self.assertEqual(context['courses'][0].id, self.starting_earlier.id) + self.assertEqual(context['courses'][1].id, self.starting_later.id) + self.assertEqual(context['courses'][2].id, self.course_with_default_start_date.id) diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index 616040d13e..5cd8ec0572 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -373,6 +373,15 @@ def sort_by_announcement(courses): return courses +def sort_by_start_date(courses): + """ + Returns a list of courses sorted by their start date, latest first. + """ + courses = sorted(courses, key=lambda course: (course.start is None, course.start), reverse=False) + + return courses + + def get_cms_course_link(course, page='course'): """ Returns a link to course_index for editing the course in cms, diff --git a/lms/envs/common.py b/lms/envs/common.py index 4040b8a8f7..7bfdd83070 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -273,6 +273,11 @@ FEATURES = { # False to not redirect the user 'ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER': True, + # When a user goes to the homepage ('/') the user see the + # courses listed in the announcement dates order - this is default Open edX behavior. + # Set to True to change the course sorting behavior by their start dates, latest first. + 'ENABLE_COURSE_SORTING_BY_START_DATE': False, + # Expose Mobile REST API. Note that if you use this, you must also set # ENABLE_OAUTH2_PROVIDER to True 'ENABLE_MOBILE_REST_API': False,