diff --git a/cms/envs/common.py b/cms/envs/common.py index 74f7e33cb8..96287aae80 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1010,6 +1010,9 @@ INSTALLED_APPS = ( # Customized celery tasks, including persisting failed tasks so they can # be retried 'celery_utils', + + # Waffle related utilities + 'openedx.core.djangoapps.waffle_utils', ) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 593931f79e..e388487c32 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -2267,7 +2267,7 @@ def auto_auth(request): elif course_id: # Redirect to the course homepage (in LMS) or outline page (in Studio) try: - redirect_url = reverse(course_home_url_name(request), kwargs={'course_id': course_id}) + redirect_url = reverse(course_home_url_name(course_key), kwargs={'course_id': course_id}) except NoReverseMatch: redirect_url = reverse('course_handler', kwargs={'course_key_string': course_id}) else: diff --git a/lms/djangoapps/ccx/tests/test_field_override_performance.py b/lms/djangoapps/ccx/tests/test_field_override_performance.py index 0fba797f97..339a10b853 100644 --- a/lms/djangoapps/ccx/tests/test_field_override_performance.py +++ b/lms/djangoapps/ccx/tests/test_field_override_performance.py @@ -231,18 +231,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase): # # of sql queries to default, # # of mongo queries, # ) - ('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), + ('no_overrides', 1, True, False): (25, 1), + ('no_overrides', 2, True, False): (25, 1), + ('no_overrides', 3, True, False): (25, 1), + ('ccx', 1, True, False): (25, 1), + ('ccx', 2, True, False): (25, 1), + ('ccx', 3, True, False): (25, 1), + ('no_overrides', 1, False, False): (25, 1), + ('no_overrides', 2, False, False): (25, 1), + ('no_overrides', 3, False, False): (25, 1), + ('ccx', 1, False, False): (25, 1), + ('ccx', 2, False, False): (25, 1), + ('ccx', 3, False, False): (25, 1), } @@ -254,19 +254,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase): __test__ = True TEST_DATA = { - ('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), + ('no_overrides', 1, True, False): (25, 3), + ('no_overrides', 2, True, False): (25, 3), + ('no_overrides', 3, True, False): (25, 3), + ('ccx', 1, True, False): (25, 3), + ('ccx', 2, True, False): (25, 3), + ('ccx', 3, True, False): (25, 3), + ('ccx', 1, True, True): (26, 3), + ('ccx', 2, True, True): (26, 3), + ('ccx', 3, True, True): (26, 3), + ('no_overrides', 1, False, False): (25, 3), + ('no_overrides', 2, False, False): (25, 3), + ('no_overrides', 3, False, False): (25, 3), + ('ccx', 1, False, False): (25, 3), + ('ccx', 2, False, False): (25, 3), + ('ccx', 3, False, False): (25, 3), } diff --git a/lms/djangoapps/courseware/tabs.py b/lms/djangoapps/courseware/tabs.py index b889653fd4..049d6a0aa6 100644 --- a/lms/djangoapps/courseware/tabs.py +++ b/lms/djangoapps/courseware/tabs.py @@ -2,15 +2,14 @@ This module is essentially a broker to xmodule/tabs.py -- it was originally introduced to perform some LMS-specific tab display gymnastics for the Entrance Exams feature """ -import waffle - from django.conf import settings 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 default_course_url_name, UNIFIED_COURSE_EXPERIENCE_FLAG +from openedx.features.course_experience import default_course_url_name +from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG from request_cache.middleware import RequestCache from student.models import CourseEnrollment from xmodule.tabs import CourseTab, CourseTabList, key_checker, link_reverse_func @@ -66,8 +65,7 @@ class CourseInfoTab(CourseTab): """ 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) + return not UNIFIED_COURSE_TAB_FLAG.is_enabled(course.id) class SyllabusTab(EnrolledTab): diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py index d4619d882d..f8f17420b1 100644 --- a/lms/djangoapps/courseware/tests/test_date_summary.py +++ b/lms/djangoapps/courseware/tests/test_date_summary.py @@ -7,8 +7,6 @@ from django.core.urlresolvers import reverse from freezegun import freeze_time from nose.plugins.attrib import attr from pytz import utc -from waffle.testutils import override_flag -from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG from commerce.models import CommerceConfiguration from course_modes.tests.factories import CourseModeFactory @@ -24,6 +22,8 @@ from courseware.date_summary import ( ) from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.user_api.preferences.api import set_user_preference +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag +from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG from student.tests.factories import CourseEnrollmentFactory, UserFactory from lms.djangoapps.verify_student.models import VerificationDeadline from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory @@ -194,7 +194,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): 'info', 'openedx.course_experience.course_home', ) - @override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=True) + @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True) def test_todays_date_no_timezone(self, url_name): with freeze_time('2015-01-02'): self.setup_course_and_user() @@ -218,7 +218,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): 'info', 'openedx.course_experience.course_home', ) - @override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=True) + @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True) def test_todays_date_timezone(self, url_name): with freeze_time('2015-01-02'): self.setup_course_and_user() @@ -249,7 +249,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): 'info', 'openedx.course_experience.course_home', ) - @override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=True) + @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True) def test_start_date_render(self, url_name): with freeze_time('2015-01-02'): self.setup_course_and_user() @@ -267,7 +267,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): 'info', 'openedx.course_experience.course_home', ) - @override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=True) + @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True) def test_start_date_render_time_zone(self, url_name): with freeze_time('2015-01-02'): self.setup_course_and_user() diff --git a/lms/djangoapps/courseware/tests/test_tabs.py b/lms/djangoapps/courseware/tests/test_tabs.py index 58bc1908e8..28a33222f5 100644 --- a/lms/djangoapps/courseware/tests/test_tabs.py +++ b/lms/djangoapps/courseware/tests/test_tabs.py @@ -2,8 +2,6 @@ Test cases for tabs. """ -from waffle.testutils import override_flag - from django.core.urlresolvers import reverse from django.http import Http404 from mock import MagicMock, Mock, patch @@ -18,7 +16,8 @@ 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 openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag +from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG from student.models import CourseEnrollment from student.tests.factories import UserFactory from util.milestones_helpers import ( @@ -776,12 +775,13 @@ class CourseInfoTabTestCase(TabTestCase): self.user = self.create_mock_user() self.request = get_mock_request(self.user) + @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=False) 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') - @override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=True) + @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True) def test_default_tab_for_new_course_experience(self): # Verify that the unified course experience hides the course info tab tabs = get_course_tab_list(self.request, self.course) diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 24c35a79a0..48a7432f2e 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -210,8 +210,8 @@ class IndexQueryTestCase(ModuleStoreTestCase): NUM_PROBLEMS = 20 @ddt.data( - (ModuleStoreEnum.Type.mongo, 10, 142), - (ModuleStoreEnum.Type.split, 4, 142), + (ModuleStoreEnum.Type.mongo, 10, 143), + (ModuleStoreEnum.Type.split, 4, 143), ) @ddt.unpack def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count): @@ -1420,12 +1420,12 @@ class ProgressPageTests(ProgressPageBaseTests): """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(40), check_mongo_calls(1): + with self.assertNumQueries(41), check_mongo_calls(1): self._get_progress_page() @ddt.data( - (False, 40, 26), - (True, 33, 22) + (False, 41, 27), + (True, 34, 23) ) @ddt.unpack def test_progress_queries(self, enable_waffle, initial, subsequent): diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 423e211626..6bc59e3077 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -86,9 +86,9 @@ from openedx.core.djangoapps.programs.utils import ProgramMarketingDataExtender from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.features.course_experience import ( - UNIFIED_COURSE_EXPERIENCE_FLAG, + UNIFIED_COURSE_TAB_FLAG, UNIFIED_COURSE_VIEW_FLAG, - course_home_url_name + course_home_url_name, ) from openedx.features.course_experience.views.course_dates import CourseDatesFragmentView from openedx.features.enterprise_support.api import data_sharing_consent_required @@ -263,11 +263,12 @@ def course_info(request, course_id): return url return None - # 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) + + # If the unified course experience is enabled, redirect to the "Course" tab + if UNIFIED_COURSE_TAB_FLAG.is_enabled(course_key): + return redirect(reverse(course_home_url_name(course_key), args=[course_id])) + with modulestore().bulk_operations(course_key): course = get_course_by_id(course_key, depth=2) access_response = has_access(request.user, 'load', course, course_key) @@ -669,7 +670,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(course_home_url_name(request), args=[unicode(course.id)])) + return redirect(reverse(course_home_url_name(course.id), args=[unicode(course.id)])) registered = registered_for_course(course, request.user) @@ -677,7 +678,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(course_home_url_name(request), args=[course.id.to_deprecated_string()]) + course_target = reverse(course_home_url_name(course.id), args=[course.id.to_deprecated_string()]) else: course_target = reverse('about_course', args=[course.id.to_deprecated_string()]) @@ -1241,7 +1242,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(course_home_url_name(request), args=[course_id]) + redirect_url = reverse(course_home_url_name(course.id), args=[course_id]) # if there is no Survey associated with this course, # then redirect to the course instead diff --git a/lms/djangoapps/grades/config/waffle.py b/lms/djangoapps/grades/config/waffle.py index c7ea272c40..54c9b5b3b8 100644 --- a/lms/djangoapps/grades/config/waffle.py +++ b/lms/djangoapps/grades/config/waffle.py @@ -2,7 +2,7 @@ This module contains various configuration settings via waffle switches for the Grades app. """ -from openedx.core.djangolib.waffle_utils import WaffleSwitchPlus +from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace # Namespace @@ -18,4 +18,4 @@ def waffle(): """ Returns the namespaced, cached, audited Waffle class for Grades. """ - return WaffleSwitchPlus(namespace=WAFFLE_NAMESPACE, log_prefix=u'Grades: ') + return WaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'Grades: ') diff --git a/lms/envs/common.py b/lms/envs/common.py index 7b4640aa17..973cac74c6 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2211,6 +2211,9 @@ INSTALLED_APPS = ( # Unusual migrations 'database_fixups', + # Waffle related utilities + 'openedx.core.djangoapps.waffle_utils', + # Features 'openedx.features.course_bookmarks', 'openedx.features.course_experience', diff --git a/lms/templates/dashboard/_dashboard_course_listing.html b/lms/templates/dashboard/_dashboard_course_listing.html index 53e178f89c..542fe920ed 100644 --- a/lms/templates/dashboard/_dashboard_course_listing.html +++ b/lms/templates/dashboard/_dashboard_course_listing.html @@ -56,7 +56,7 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_ % endif