Commit 2167584e by Andy Armstrong

Unify the home and course tabs

LEARNER-609
parent b10c71d8
......@@ -116,6 +116,7 @@ from student.models import anonymous_id_for_user, UserAttribute, EnrollStatusCha
from shoppingcart.models import DonationConfiguration, CourseRegistrationCode
from openedx.core.djangoapps.embargo import api as embargo_api
from openedx.features.course_experience import course_home_url_name
from openedx.features.enterprise_support.api import get_dashboard_consent_notification
import analytics
......@@ -2194,12 +2195,12 @@ def auto_auth(request):
# Redirect to specific page if specified
if redirect_to:
redirect_url = redirect_to
# Redirect to course info page if course_id is known
# Redirect to course home page if course_id is known
elif course_id:
try:
# redirect to course info page in LMS
# redirect to course home page in LMS
redirect_url = reverse(
'info',
course_home_url_name(request),
kwargs={'course_id': course_id}
)
except NoReverseMatch:
......
......@@ -64,7 +64,7 @@ class CourseHomeTest(CourseHomeBaseTest):
def test_course_home(self):
"""
Smoke test of course outline, breadcrumbs to and from cours outline, and bookmarks.
Smoke test of course outline, breadcrumbs to and from course outline, and bookmarks.
"""
self.course_home_page.visit()
......
......@@ -231,18 +231,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
# # of sql queries to default,
# # of mongo queries,
# )
('no_overrides', 1, True, False): (23, 1),
('no_overrides', 2, True, False): (23, 1),
('no_overrides', 3, True, False): (23, 1),
('ccx', 1, True, False): (23, 1),
('ccx', 2, True, False): (23, 1),
('ccx', 3, True, False): (23, 1),
('no_overrides', 1, False, False): (23, 1),
('no_overrides', 2, False, False): (23, 1),
('no_overrides', 3, False, False): (23, 1),
('ccx', 1, False, False): (23, 1),
('ccx', 2, False, False): (23, 1),
('ccx', 3, False, False): (23, 1),
('no_overrides', 1, True, False): (24, 1),
('no_overrides', 2, True, False): (24, 1),
('no_overrides', 3, True, False): (24, 1),
('ccx', 1, True, False): (24, 1),
('ccx', 2, True, False): (24, 1),
('ccx', 3, True, False): (24, 1),
('no_overrides', 1, False, False): (24, 1),
('no_overrides', 2, False, False): (24, 1),
('no_overrides', 3, False, False): (24, 1),
('ccx', 1, False, False): (24, 1),
('ccx', 2, False, False): (24, 1),
('ccx', 3, False, False): (24, 1),
}
......@@ -254,19 +254,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
__test__ = True
TEST_DATA = {
('no_overrides', 1, True, False): (23, 3),
('no_overrides', 2, True, False): (23, 3),
('no_overrides', 3, True, False): (23, 3),
('ccx', 1, True, False): (23, 3),
('ccx', 2, True, False): (23, 3),
('ccx', 3, True, False): (23, 3),
('ccx', 1, True, True): (24, 3),
('ccx', 2, True, True): (24, 3),
('ccx', 3, True, True): (24, 3),
('no_overrides', 1, False, False): (23, 3),
('no_overrides', 2, False, False): (23, 3),
('no_overrides', 3, False, False): (23, 3),
('ccx', 1, False, False): (23, 3),
('ccx', 2, False, False): (23, 3),
('ccx', 3, False, False): (23, 3),
('no_overrides', 1, True, False): (24, 3),
('no_overrides', 2, True, False): (24, 3),
('no_overrides', 3, True, False): (24, 3),
('ccx', 1, True, False): (24, 3),
('ccx', 2, True, False): (24, 3),
('ccx', 3, True, False): (24, 3),
('ccx', 1, True, True): (25, 3),
('ccx', 2, True, True): (25, 3),
('ccx', 3, True, True): (25, 3),
('no_overrides', 1, False, False): (24, 3),
('no_overrides', 2, False, False): (24, 3),
('no_overrides', 3, False, False): (24, 3),
('ccx', 1, False, False): (24, 3),
('ccx', 2, False, False): (24, 3),
('ccx', 3, False, False): (24, 3),
}
......@@ -10,7 +10,7 @@ from django.utils.translation import ugettext as _, ugettext_noop
from courseware.access import has_access
from courseware.entrance_exams import user_can_skip_entrance_exam
from openedx.core.lib.course_tabs import CourseTabPluginManager
from openedx.features.course_experience import UNIFIED_COURSE_VIEW_FLAG
from openedx.features.course_experience import defaut_course_url_name, UNIFIED_COURSE_EXPERIENCE_FLAG
from request_cache.middleware import RequestCache
from student.models import CourseEnrollment
from xmodule.tabs import CourseTab, CourseTabList, key_checker, link_reverse_func
......@@ -39,23 +39,13 @@ class CoursewareTab(EnrolledTab):
is_default = False
supports_preview_menu = True
@staticmethod
def main_course_url_name(request):
"""
Returns the main course URL for the current user.
"""
if waffle.flag_is_active(request, UNIFIED_COURSE_VIEW_FLAG):
return 'openedx.course_experience.course_home'
else:
return 'courseware'
@property
def link_func(self):
"""
Returns a function that computes the URL for this tab.
"""
request = RequestCache.get_current_request()
url_name = self.main_course_url_name(request)
url_name = defaut_course_url_name(request)
return link_reverse_func(url_name)
......@@ -73,7 +63,11 @@ class CourseInfoTab(CourseTab):
@classmethod
def is_enabled(cls, course, user=None):
return True
"""
The "Home" tab is not shown for the new unified course experience.
"""
request = RequestCache.get_current_request()
return not waffle.flag_is_active(request, UNIFIED_COURSE_EXPERIENCE_FLAG)
class SyllabusTab(EnrolledTab):
......
......@@ -367,7 +367,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
self.assertEqual(resp.status_code, 200)
def test_num_queries_instructor_paced(self):
self.fetch_course_info_with_queries(self.instructor_paced_course, 18, 4)
self.fetch_course_info_with_queries(self.instructor_paced_course, 20, 4)
def test_num_queries_self_paced(self):
self.fetch_course_info_with_queries(self.self_paced_course, 18, 4)
self.fetch_course_info_with_queries(self.self_paced_course, 20, 4)
......@@ -2,6 +2,8 @@
Test cases for tabs.
"""
import waffle
from django.core.urlresolvers import reverse
from django.http import Http404
from mock import MagicMock, Mock, patch
......@@ -16,6 +18,7 @@ from courseware.tests.helpers import LoginEnrollmentTestCase
from courseware.tests.factories import InstructorFactory, StaffFactory
from courseware.views.views import get_static_tab_fragment, StaticCourseTabView
from openedx.core.djangolib.testing.utils import get_mock_request
from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
from student.models import CourseEnrollment
from student.tests.factories import UserFactory
from util.milestones_helpers import (
......@@ -767,6 +770,26 @@ class StaticTabTestCase(TabTestCase):
@attr(shard=1)
class CourseInfoTabTestCase(TabTestCase):
"""Test cases for the course info tab."""
def setUp(self):
self.user = self.create_mock_user()
self.request = get_mock_request(self.user)
def test_default_tab(self):
# Verify that the course info tab is the first tab
tabs = get_course_tab_list(self.request, self.course)
self.assertEqual(tabs[0].type, 'course_info')
@patch('waffle.flag_is_active')
def test_default_tab_for_new_course_experience(self, patched_flag_is_active):
# Verify that the unified course experience hides the course info tab
patched_flag_is_active.return_value = True
tabs = get_course_tab_list(self.request, self.course)
self.assertEqual(tabs[0].type, 'courseware')
@attr(shard=1)
class DiscussionLinkTestCase(TabTestCase):
"""Test cases for discussion link tab."""
......
......@@ -206,8 +206,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
NUM_PROBLEMS = 20
@ddt.data(
(ModuleStoreEnum.Type.mongo, 10, 141),
(ModuleStoreEnum.Type.split, 4, 141),
(ModuleStoreEnum.Type.mongo, 10, 142),
(ModuleStoreEnum.Type.split, 4, 142),
)
@ddt.unpack
def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
......@@ -1408,12 +1408,12 @@ class ProgressPageTests(ModuleStoreTestCase):
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
SelfPacedConfiguration(enabled=self_paced_enabled).save()
self.setup_course(self_paced=self_paced)
with self.assertNumQueries(39), check_mongo_calls(1):
with self.assertNumQueries(40), check_mongo_calls(1):
self._get_progress_page()
@ddt.data(
(False, 39, 25),
(True, 32, 21)
(False, 40, 26),
(True, 33, 22)
)
@ddt.unpack
def test_progress_queries(self, enable_waffle, initial, subsequent):
......
......@@ -91,11 +91,15 @@ from openedx.core.djangoapps.programs.utils import ProgramMarketingDataExtender
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangoapps.monitoring_utils import set_custom_metrics_for_course_key
from openedx.features.course_experience import (
course_home_url_name,
UNIFIED_COURSE_EXPERIENCE_FLAG,
UNIFIED_COURSE_VIEW_FLAG,
)
from openedx.features.enterprise_support.api import data_sharing_consent_required
from shoppingcart.models import CourseRegistrationCode
from shoppingcart.utils import is_shopping_cart_enabled
from student.models import UserTestGroup, CourseEnrollment
from student.roles import GlobalStaff
from survey.utils import must_answer_survey
from util.cache import cache, cache_if_anonymous
from util.date_utils import strftime_localized
......@@ -226,7 +230,7 @@ def jump_to(request, course_id, location):
except InvalidKeyError:
raise Http404(u"Invalid course_key or usage_key")
try:
unified_course_view = waffle.flag_is_active(request, 'unified_course_view')
unified_course_view = waffle.flag_is_active(request, UNIFIED_COURSE_VIEW_FLAG)
redirect_url = get_redirect_url(course_key, usage_key, unified_course_view=unified_course_view)
except ItemNotFoundError:
raise Http404(u"No data at this location: {0}".format(usage_key))
......@@ -245,6 +249,10 @@ def course_info(request, course_id):
Assumes the course_id is in a valid format.
"""
# If the unified course experience is enabled, redirect to the "Course" tab
if waffle.flag_is_active(request, UNIFIED_COURSE_EXPERIENCE_FLAG):
return redirect(reverse(course_home_url_name(request), args=[course_id]))
course_key = CourseKey.from_string(course_id)
with modulestore().bulk_operations(course_key):
course = get_course_by_id(course_key, depth=2)
......@@ -636,7 +644,7 @@ def course_about(request, course_id):
modes = CourseMode.modes_for_course_dict(course_key)
if configuration_helpers.get_value('ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False)):
return redirect(reverse('info', args=[course.id.to_deprecated_string()]))
return redirect(reverse(course_home_url_name(request), args=[unicode(course.id)]))
registered = registered_for_course(course, request.user)
......@@ -644,7 +652,7 @@ def course_about(request, course_id):
studio_url = get_studio_url(course, 'settings/details')
if has_access(request.user, 'load', course):
course_target = reverse('info', args=[course.id.to_deprecated_string()])
course_target = reverse(course_home_url_name(request), args=[course.id.to_deprecated_string()])
else:
course_target = reverse('about_course', args=[course.id.to_deprecated_string()])
......@@ -1208,7 +1216,7 @@ def course_survey(request, course_id):
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key)
redirect_url = reverse('info', args=[course_id])
redirect_url = reverse(course_home_url_name(request), args=[course_id])
# if there is no Survey associated with this course,
# then redirect to the course instead
......
......@@ -10,6 +10,7 @@ from course_modes.models import CourseMode
from course_modes.helpers import enrollment_mode_display
from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string
from openedx.core.djangolib.markup import HTML, Text
from openedx.features.course_experience import course_home_url_name
from student.helpers import (
VERIFY_STATUS_NEED_TO_VERIFY,
VERIFY_STATUS_SUBMITTED,
......@@ -55,7 +56,7 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
% endif
<div class="course-container">
<article class="course${mode_class}">
<% course_target = reverse('info', args=[unicode(course_overview.id)]) %>
<% course_target = reverse(course_home_url_name(), args=[unicode(course_overview.id)]) %>
<section class="details" aria-labelledby="details-heading-${course_overview.number}">
<h2 class="hd hd-2 sr" id="details-heading-${course_overview.number}">${_('Course details')}</h2>
<div class="wrapper-course-image" aria-hidden="true">
......
......@@ -2,7 +2,9 @@
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from openedx.core.lib.courses import course_image_url
from openedx.features.course_experience import course_home_url_name
%>
<%inherit file="../main.html" />
<%namespace name='static' file='/static_content.html'/>
<%block name="pagetitle">${_("Confirm Enrollment")}</%block>
......@@ -73,7 +75,7 @@ from openedx.core.lib.courses import course_image_url
</div>
% if not reg_code_already_redeemed:
%if redemption_success:
<% course_url = reverse('info', args=[course.id.to_deprecated_string()]) %>
<% course_url = reverse(course_home_url_name(), args=[course.id.to_deprecated_string()]) %>
<a href="${course_url}" class="link-button course-link-bg-color">${_("View Course")} <span class="icon fa fa-caret-right" aria-hidden="true"></span></a>
%elif not registered_for_course:
<form method="post">
......
......@@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse
from edxmako import add_lookup, LOOKUP
from lms import startup
from openedx.features.course_experience import course_home_url_name
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
......@@ -54,6 +55,6 @@ class HelpModalTests(ModuleStoreTestCase):
Simple test to make sure that you don't get a 500 error when the modal
is enabled.
"""
url = reverse('info', args=[self.course.id.to_deprecated_string()])
url = reverse(course_home_url_name(), args=[self.course.id.to_deprecated_string()])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
......@@ -13,9 +13,9 @@ from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import View
from courseware.courses import get_course_with_access
from lms.djangoapps.courseware.tabs import CoursewareTab
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from openedx.features.course_experience import defaut_course_url_name
from util.views import ensure_valid_course_key
from web_fragments.fragment import Fragment
......@@ -38,7 +38,7 @@ class CourseBookmarksView(View):
"""
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
course_url_name = CoursewareTab.main_course_url_name(request)
course_url_name = defaut_course_url_name(request)
course_url = reverse(course_url_name, kwargs={'course_id': unicode(course.id)})
# Render the bookmarks list as a fragment
......
# Unified course experience settings.
"""
Unified course experience settings and helper methods.
"""
import waffle
from request_cache.middleware import RequestCache
# Waffle flag to enable a single unified "Course" tab.
UNIFIED_COURSE_EXPERIENCE_FLAG = 'unified_course_experience'
......@@ -6,3 +12,23 @@ UNIFIED_COURSE_EXPERIENCE_FLAG = 'unified_course_experience'
# Waffle flag to enable the full screen course content view
# along with a unified course home page.
UNIFIED_COURSE_VIEW_FLAG = 'unified_course_view'
def defaut_course_url_name(request=None):
"""
Returns the default course URL name for the current user.
"""
if waffle.flag_is_active(request or RequestCache.get_current_request(), UNIFIED_COURSE_VIEW_FLAG):
return 'openedx.course_experience.course_home'
else:
return 'courseware'
def course_home_url_name(request=None):
"""
Returns the course home page's URL name for the current user.
"""
if waffle.flag_is_active(request or RequestCache.get_current_request(), UNIFIED_COURSE_EXPERIENCE_FLAG):
return 'openedx.course_experience.course_home'
else:
return 'info'
......@@ -10,10 +10,10 @@ from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control
from courseware.courses import get_course_info_section, get_course_with_access
from lms.djangoapps.courseware.tabs import CoursewareTab
from lms.djangoapps.courseware.views.views import CourseTabView
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from openedx.features.course_experience import defaut_course_url_name
from web_fragments.fragment import Fragment
......@@ -45,7 +45,7 @@ class CourseUpdatesFragmentView(EdxFragmentView):
"""
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
course_url_name = CoursewareTab.main_course_url_name(request)
course_url_name = defaut_course_url_name(request)
course_url = reverse(course_url_name, kwargs={'course_id': unicode(course.id)})
# Fetch the updates as HTML
......
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