diff --git a/cms/djangoapps/contentstore/config/waffle.py b/cms/djangoapps/contentstore/config/waffle.py
index 3dc567a14f..9f086943f6 100644
--- a/cms/djangoapps/contentstore/config/waffle.py
+++ b/cms/djangoapps/contentstore/config/waffle.py
@@ -4,52 +4,26 @@ waffle switches for the contentstore app.
"""
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace, LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleFlag, WaffleSwitch
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
# Namespace
WAFFLE_NAMESPACE = 'studio'
+LOG_PREFIX = 'Studio: '
# Switches
-# TODO: Replace with WaffleSwitch(). See waffle() docstring.
-ENABLE_ACCESSIBILITY_POLICY_PAGE = 'enable_policy_page'
-
-
-def waffle():
- """
- Deprecated: Returns the namespaced, cached, audited Waffle Switch class for Studio pages.
-
- IMPORTANT: Do NOT copy this pattern and do NOT use this to reference new switches.
- Instead, replace the string constant above with the actual switch instance.
- For example::
-
- ENABLE_ACCESSIBILITY_POLICY_PAGE = WaffleSwitch(f'{WAFFLE_NAMESPACE}.enable_policy_page')
- """
- return LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix='Studio: ')
-
-
-def waffle_flags():
- """
- Deprecated: Returns the namespaced, cached, audited Waffle Flag class for Studio pages.
-
- IMPORTANT: Do NOT copy this pattern and do NOT use this to reference new flags.
- See waffle() docstring for more details.
- """
- return LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix='Studio: ')
-
+ENABLE_ACCESSIBILITY_POLICY_PAGE = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.enable_policy_page', __name__
+)
# TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment.
ENABLE_CHECKLISTS_QUALITY = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace=waffle_flags(),
- flag_name='enable_checklists_quality',
- module_name=__name__,
+ f'{WAFFLE_NAMESPACE}.enable_checklists_quality', __name__, LOG_PREFIX
)
SHOW_REVIEW_RULES_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace=waffle_flags(),
- flag_name='show_review_rules',
- module_name=__name__,
+ f'{WAFFLE_NAMESPACE}.show_review_rules', __name__, LOG_PREFIX
)
# Waffle flag to redirect to the library authoring MFE.
@@ -62,10 +36,8 @@ SHOW_REVIEW_RULES_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=togg
# .. toggle_target_removal_date: 2020-12-31
# .. toggle_warnings: Also set settings.LIBRARY_AUTHORING_MICROFRONTEND_URL and ENABLE_LIBRARY_AUTHORING_MICROFRONTEND.
# .. toggle_tickets: https://openedx.atlassian.net/wiki/spaces/COMM/pages/1545011241/BD-14+Blockstore+Powered+Content+Libraries+Taxonomies
-REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND = LegacyWaffleFlag(
- waffle_namespace=waffle_flags(),
- flag_name='library_authoring_mfe',
- module_name=__name__,
+REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND = WaffleFlag(
+ f'{WAFFLE_NAMESPACE}.library_authoring_mfe', __name__, LOG_PREFIX
)
@@ -80,4 +52,4 @@ REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND = LegacyWaffleFlag(
# .. toggle_target_removal_date: 2021-12-31
# .. toggle_warnings: Flag course_experience.relative_dates should also be active for relative dates functionalities to work.
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-844
-CUSTOM_RELATIVE_DATES = CourseWaffleFlag(WAFFLE_NAMESPACE, 'custom_relative_dates', module_name=__name__,)
+CUSTOM_RELATIVE_DATES = CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.custom_relative_dates', __name__)
diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py
index 5a37a96cce..03855403a9 100644
--- a/cms/djangoapps/contentstore/tests/test_contentstore.py
+++ b/cms/djangoapps/contentstore/tests/test_contentstore.py
@@ -1055,9 +1055,7 @@ class MiscCourseTests(ContentStoreTestCase):
resp = self.client.get_html('/c4x/InvalidOrg/InvalidCourse/asset/invalid.png')
self.assertEqual(resp.status_code, 404)
- @override_switch(
- f'{waffle.WAFFLE_NAMESPACE}.{waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE}',
- active=False)
+ @override_switch(waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE.name, active=False)
def test_disabled_accessibility_page(self):
"""
Test that accessibility page returns 404 when waffle switch is disabled
@@ -2191,9 +2189,7 @@ class EntryPageTestCase(TestCase):
# Logout redirects.
self._test_page("/logout", 200)
- @override_switch(
- f'{waffle.WAFFLE_NAMESPACE}.{waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE}',
- active=True)
+ @override_switch(waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE.name, active=True)
def test_accessibility(self):
self._test_page('/accessibility')
diff --git a/cms/djangoapps/contentstore/toggles.py b/cms/djangoapps/contentstore/toggles.py
index 2cda1199d8..8e2abc6419 100644
--- a/cms/djangoapps/contentstore/toggles.py
+++ b/cms/djangoapps/contentstore/toggles.py
@@ -1,7 +1,7 @@
"""
CMS feature toggles.
"""
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace, SettingDictToggle, WaffleFlag
+from edx_toggles.toggles import SettingDictToggle, WaffleFlag
# .. toggle_name: FEATURES['ENABLE_EXPORT_GIT']
# .. toggle_implementation: SettingDictToggle
@@ -19,23 +19,23 @@ EXPORT_GIT = SettingDictToggle(
)
# Namespace for studio dashboard waffle flags.
-WAFFLE_NAMESPACE = 'contentstore'
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix='Contentstore: ')
+CONTENTSTORE_NAMESPACE = 'contentstore'
+CONTENTSTORE_LOG_PREFIX = 'Contentstore: '
-# .. toggle_name: split_library_on_studio_dashboard
+# .. toggle_name: contentstore.split_library_on_studio_dashboard
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: Enables data new view for library on studio dashboard.
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2020-07-8
# .. toggle_tickets: TNL-7536
-SPLIT_LIBRARY_ON_DASHBOARD = LegacyWaffleFlag(
- waffle_namespace=LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE),
- flag_name='split_library_on_studio_dashboard',
- module_name=__name__
+SPLIT_LIBRARY_ON_DASHBOARD = WaffleFlag(
+ f'{CONTENTSTORE_NAMESPACE}.split_library_on_studio_dashboard',
+ __name__,
+ CONTENTSTORE_LOG_PREFIX,
)
-# .. toggle_name: bypass_olx_failure
+# .. toggle_name: contentstore.bypass_olx_failure
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: Enables bypassing olx validation failures during course import.
@@ -43,10 +43,10 @@ SPLIT_LIBRARY_ON_DASHBOARD = LegacyWaffleFlag(
# .. toggle_creation_date: 2021-04-15
# .. toggle_target_removal_date: 2021-05-15
# .. toggle_tickets: TNL-8214
-BYPASS_OLX_FAILURE = LegacyWaffleFlag(
- waffle_namespace=LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE),
- flag_name='bypass_olx_failure',
- module_name=__name__
+BYPASS_OLX_FAILURE = WaffleFlag(
+ f'{CONTENTSTORE_NAMESPACE}.bypass_olx_failure',
+ __name__,
+ CONTENTSTORE_LOG_PREFIX,
)
diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py
index 7643a6a1e2..10b78596ae 100644
--- a/cms/djangoapps/contentstore/views/course.py
+++ b/cms/djangoapps/contentstore/views/course.py
@@ -25,7 +25,7 @@ from django.utils.translation import gettext as _
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_GET, require_http_methods
from edx_django_utils.monitoring import function_trace
-from edx_toggles.toggles import LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleSwitch
from milestones import api as milestones_api
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
@@ -72,7 +72,6 @@ from openedx.core.lib.courses import course_image_url
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from openedx.features.content_type_gating.partitions import CONTENT_TYPE_GATING_SCHEME
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
-from openedx.features.course_experience.waffle import waffle as course_experience_waffle
from xmodule.contentstore.content import StaticContent # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.course_module import CourseBlock, DEFAULT_START_DATE, CourseFields # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.error_module import ErrorBlock # lint-amnesty, pylint: disable=wrong-import-order
@@ -131,6 +130,9 @@ __all__ = ['course_info_handler', 'course_handler', 'course_listing',
'get_course_and_check_access']
WAFFLE_NAMESPACE = 'studio_home'
+ENABLE_GLOBAL_STAFF_OPTIMIZATION = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.enable_global_staff_optimization', __name__
+)
class AccessListFallback(Exception):
@@ -518,8 +520,7 @@ def course_listing(request):
List all courses and libraries available to the logged in user
"""
- optimization_enabled = GlobalStaff().has_user(request.user) and \
- LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE).is_enabled('enable_global_staff_optimization')
+ optimization_enabled = GlobalStaff().has_user(request.user) and ENABLE_GLOBAL_STAFF_OPTIMIZATION.is_enabled()
org = request.GET.get('org', '') if optimization_enabled else None
courses_iter, in_process_course_actions = get_courses_accessible_to_user(request, org)
@@ -1150,8 +1151,7 @@ def settings_handler(request, course_key_string): # lint-amnesty, pylint: disab
'EDITABLE_SHORT_DESCRIPTION',
settings.FEATURES.get('EDITABLE_SHORT_DESCRIPTION', True)
)
- sidebar_html_enabled = course_experience_waffle().is_enabled(ENABLE_COURSE_ABOUT_SIDEBAR_HTML)
- # self_paced_enabled = SelfPacedConfiguration.current().enabled
+ sidebar_html_enabled = ENABLE_COURSE_ABOUT_SIDEBAR_HTML.is_enabled()
verified_mode = CourseMode.verified_mode_for_course(course_key, include_expired=True)
upgrade_deadline = (verified_mode and verified_mode.expiration_datetime and
diff --git a/cms/djangoapps/contentstore/views/public.py b/cms/djangoapps/contentstore/views/public.py
index 2d5e69bc23..f4d1f7c778 100644
--- a/cms/djangoapps/contentstore/views/public.py
+++ b/cms/djangoapps/contentstore/views/public.py
@@ -2,15 +2,15 @@
Public views
"""
+from urllib.parse import quote_plus
from django.conf import settings
+from django.http.response import Http404
from django.shortcuts import redirect
-from urllib.parse import quote_plus # lint-amnesty, pylint: disable=wrong-import-order
-from waffle.decorators import waffle_switch
from common.djangoapps.edxmako.shortcuts import render_to_response
-from ..config import waffle
+from ..config.waffle import ENABLE_ACCESSIBILITY_POLICY_PAGE
__all__ = [
'register_redirect_to_lms', 'login_redirect_to_lms', 'howitworks', 'accessibility',
@@ -69,12 +69,12 @@ def howitworks(request):
return render_to_response('howitworks.html', {})
-@waffle_switch(f'{waffle.WAFFLE_NAMESPACE}.{waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE}')
def accessibility(request):
"""
Display the accessibility accommodation form.
"""
-
- return render_to_response('accessibility.html', {
- 'language_code': request.LANGUAGE_CODE
- })
+ if ENABLE_ACCESSIBILITY_POLICY_PAGE.is_enabled():
+ return render_to_response('accessibility.html', {
+ 'language_code': request.LANGUAGE_CODE
+ })
+ raise Http404
diff --git a/cms/djangoapps/contentstore/views/tests/test_videos.py b/cms/djangoapps/contentstore/views/tests/test_videos.py
index 91b4d4b305..5b116094df 100644
--- a/cms/djangoapps/contentstore/views/tests/test_videos.py
+++ b/cms/djangoapps/contentstore/views/tests/test_videos.py
@@ -16,7 +16,6 @@ import ddt
import pytz
from django.conf import settings
from django.test.utils import override_settings
-from edx_toggles.toggles import LegacyWaffleSwitch
from edx_toggles.toggles.testutils import override_waffle_flag, override_waffle_switch
from edxval.api import (
create_or_update_transcript_preferences,
@@ -36,7 +35,6 @@ from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file
from openedx.core.djangoapps.video_pipeline.config.waffle import (
DEPRECATE_YOUTUBE,
ENABLE_DEVSTACK_VIDEO_UPLOADS,
- waffle_flags
)
from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
@@ -45,15 +43,12 @@ from ..videos import (
ENABLE_VIDEO_UPLOAD_PAGINATION,
KEY_EXPIRATION_IN_SECONDS,
VIDEO_IMAGE_UPLOAD_ENABLED,
- WAFFLE_SWITCHES,
StatusDisplayStrings,
TranscriptProvider,
_get_default_video_image_url,
convert_video_status
)
-VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH = LegacyWaffleSwitch(WAFFLE_SWITCHES, VIDEO_IMAGE_UPLOAD_ENABLED) # lint-amnesty, pylint: disable=toggle-missing-annotation
-
class VideoUploadTestBase:
"""
@@ -546,7 +541,7 @@ class VideosHandlerTestCase(VideoUploadTestMixin, VideoUploadPostTestsMixin, Cou
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret', AWS_SECURITY_TOKEN='token')
@patch('boto.s3.key.Key')
@patch('boto.s3.connection.S3Connection')
- @override_flag(waffle_flags()[ENABLE_DEVSTACK_VIDEO_UPLOADS].name, active=True)
+ @override_flag(ENABLE_DEVSTACK_VIDEO_UPLOADS.name, active=True)
def test_devstack_upload_connection(self, mock_conn, mock_key):
files = [{'file_name': 'first.mp4', 'content_type': 'video/mp4'}]
mock_key_instances = [
@@ -651,9 +646,8 @@ class VideosHandlerTestCase(VideoUploadTestMixin, VideoUploadPostTestsMixin, Cou
# expected args to be passed to `set_metadata`.
expected_args = ('course_video_upload_token', self.test_token)
- DEPRECATE_YOUTUBE_FLAG = waffle_flags()[DEPRECATE_YOUTUBE]
with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=data['course_override']):
- with override_flag(DEPRECATE_YOUTUBE_FLAG.name, active=data['global_waffle']):
+ with override_flag(DEPRECATE_YOUTUBE.name, active=data['global_waffle']):
response = self.client.post(
self.url,
json.dumps({'files': [file_data]}),
@@ -911,7 +905,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
self.assertIn('error', response)
self.assertEqual(response['error'], error_message)
- @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, False)
+ @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED, False)
def test_video_image_upload_disabled(self):
"""
Tests the video image upload when the feature is disabled.
@@ -920,7 +914,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
response = self.client.post(video_image_upload_url, {'file': 'dummy_file'}, format='multipart')
self.assertEqual(response.status_code, 404)
- @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True)
+ @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True)
def test_video_image(self):
"""
Test video image is saved.
@@ -942,7 +936,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
self.assertNotEqual(image_url1, image_url2)
- @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True)
+ @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True)
def test_video_image_no_file(self):
"""
Test that an error error message is returned if upload request is incorrect.
@@ -951,7 +945,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
response = self.client.post(video_image_upload_url, {})
self.verify_error_message(response, 'An image file is required.')
- @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True)
+ @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True)
def test_no_video_image(self):
"""
Test image url is set to None if no video image.
@@ -1133,7 +1127,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
)
)
@ddt.unpack
- @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True)
+ @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True)
def test_video_image_validation_message(self, image_data, error_message):
"""
Test video image validation gives proper error message.
diff --git a/cms/djangoapps/contentstore/views/videos.py b/cms/djangoapps/contentstore/views/videos.py
index 16ab6e4c7c..c39add5894 100644
--- a/cms/djangoapps/contentstore/views/videos.py
+++ b/cms/djangoapps/contentstore/views/videos.py
@@ -21,7 +21,7 @@ from django.urls import reverse
from django.utils.translation import gettext as _
from django.utils.translation import gettext_noop
from django.views.decorators.http import require_GET, require_http_methods, require_POST
-from edx_toggles.toggles import LegacyWaffleFlagNamespace, LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleSwitch
from edxval.api import (
SortDirection,
VideoSortField,
@@ -49,9 +49,8 @@ from openedx.core.djangoapps.video_config.models import VideoTranscriptEnabledFl
from openedx.core.djangoapps.video_pipeline.config.waffle import (
DEPRECATE_YOUTUBE,
ENABLE_DEVSTACK_VIDEO_UPLOADS,
- waffle_flags
)
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
from openedx.core.lib.api.view_utils import view_auth_classes
from xmodule.video_module.transcripts_utils import Transcript # lint-amnesty, pylint: disable=wrong-import-order
@@ -72,18 +71,17 @@ LOGGER = logging.getLogger(__name__)
# Waffle switches namespace for videos
WAFFLE_NAMESPACE = 'videos'
-WAFFLE_SWITCHES = LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE)
# Waffle switch for enabling/disabling video image upload feature
-VIDEO_IMAGE_UPLOAD_ENABLED = 'video_image_upload_enabled'
+VIDEO_IMAGE_UPLOAD_ENABLED = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.video_image_upload_enabled', __name__
+)
# Waffle flag namespace for studio
-WAFFLE_STUDIO_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='studio')
+WAFFLE_STUDIO_FLAG_NAMESPACE = 'studio'
ENABLE_VIDEO_UPLOAD_PAGINATION = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace=WAFFLE_STUDIO_FLAG_NAMESPACE,
- flag_name='enable_video_upload_pagination',
- module_name=__name__,
+ f'{WAFFLE_STUDIO_FLAG_NAMESPACE}.enable_video_upload_pagination', __name__
)
# Default expiration, in seconds, of one-time URLs used for uploading videos.
KEY_EXPIRATION_IN_SECONDS = 86400
@@ -245,7 +243,7 @@ def video_images_handler(request, course_key_string, edx_video_id=None):
"""Function to handle image files"""
# respond with a 404 if image upload is not enabled.
- if not WAFFLE_SWITCHES.is_enabled(VIDEO_IMAGE_UPLOAD_ENABLED):
+ if not VIDEO_IMAGE_UPLOAD_ENABLED.is_enabled():
return HttpResponseNotFound()
if 'file' not in request.FILES:
@@ -634,7 +632,7 @@ def videos_index_html(course, pagination_conf=None):
'video_supported_file_formats': list(VIDEO_SUPPORTED_FILE_FORMATS.keys()),
'video_upload_max_file_size': VIDEO_UPLOAD_MAX_FILE_SIZE_GB,
'video_image_settings': {
- 'video_image_upload_enabled': WAFFLE_SWITCHES.is_enabled(VIDEO_IMAGE_UPLOAD_ENABLED),
+ 'video_image_upload_enabled': VIDEO_IMAGE_UPLOAD_ENABLED.is_enabled(),
'max_size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MAX_BYTES'],
'min_size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES'],
'max_width': settings.VIDEO_IMAGE_MAX_WIDTH,
@@ -750,12 +748,11 @@ def videos_post(course, request):
('course_key', str(course.id)),
]
- deprecate_youtube = waffle_flags()[DEPRECATE_YOUTUBE]
course_video_upload_token = course.video_upload_pipeline.get('course_video_upload_token')
# Only include `course_video_upload_token` if youtube has not been deprecated
# for this course.
- if not deprecate_youtube.is_enabled(course.id) and course_video_upload_token:
+ if not DEPRECATE_YOUTUBE.is_enabled(course.id) and course_video_upload_token:
metadata_list.append(('course_video_upload_token', course_video_upload_token))
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
@@ -791,7 +788,7 @@ def storage_service_bucket():
"""
Returns an S3 bucket for video upload.
"""
- if waffle_flags()[ENABLE_DEVSTACK_VIDEO_UPLOADS].is_enabled():
+ if ENABLE_DEVSTACK_VIDEO_UPLOADS.is_enabled():
params = {
'aws_access_key_id': settings.AWS_ACCESS_KEY_ID,
'aws_secret_access_key': settings.AWS_SECRET_ACCESS_KEY,
diff --git a/cms/djangoapps/models/settings/waffle.py b/cms/djangoapps/models/settings/waffle.py
index 8d9f069802..1df0c471b0 100644
--- a/cms/djangoapps/models/settings/waffle.py
+++ b/cms/djangoapps/models/settings/waffle.py
@@ -1,7 +1,8 @@
"""
Togglable settings for Course Grading behavior
"""
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
+
WAFFLE_NAMESPACE = 'grades'
@@ -9,9 +10,7 @@ WAFFLE_NAMESPACE = 'grades'
MATERIAL_RECOMPUTE_ONLY = 'MATERIAL_RECOMPUTE_ONLY'
MATERIAL_RECOMPUTE_ONLY_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace=WAFFLE_NAMESPACE,
- flag_name=MATERIAL_RECOMPUTE_ONLY,
- module_name=__name__,
+ f'{WAFFLE_NAMESPACE}.{MATERIAL_RECOMPUTE_ONLY}', __name__
)
diff --git a/cms/templates/widgets/footer.html b/cms/templates/widgets/footer.html
index 36671427e3..b6c7d2f45e 100644
--- a/cms/templates/widgets/footer.html
+++ b/cms/templates/widgets/footer.html
@@ -6,7 +6,7 @@ from django.urls import reverse
from datetime import datetime
from django.conf import settings
import pytz
-from cms.djangoapps.contentstore.config.waffle import waffle, ENABLE_ACCESSIBILITY_POLICY_PAGE
+from cms.djangoapps.contentstore.config.waffle import ENABLE_ACCESSIBILITY_POLICY_PAGE
from openedx.core.djangolib.markup import HTML, Text
%>
@@ -30,7 +30,7 @@ from openedx.core.djangolib.markup import HTML, Text
${_("Privacy Policy")}
% endif
- % if waffle().is_enabled(ENABLE_ACCESSIBILITY_POLICY_PAGE):
+ % if ENABLE_ACCESSIBILITY_POLICY_PAGE.is_enabled():
${_("Accessibility Accommodation Request")}
@@ -62,4 +62,3 @@ from openedx.core.djangolib.markup import HTML, Text
-
diff --git a/common/djangoapps/student/views/dashboard.py b/common/djangoapps/student/views/dashboard.py
index 1c738704ed..92f0760af0 100644
--- a/common/djangoapps/student/views/dashboard.py
+++ b/common/djangoapps/student/views/dashboard.py
@@ -16,7 +16,7 @@ from django.utils.translation import gettext as _
from django.views.decorators.csrf import ensure_csrf_cookie
from edx_django_utils import monitoring as monitoring_utils
from edx_django_utils.plugins import get_plugins_view_context
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
@@ -61,7 +61,7 @@ from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disa
log = logging.getLogger("edx.student")
-experiments_namespace = LegacyWaffleFlagNamespace(name='student.experiments')
+EXPERIMENTS_NAMESPACE = 'student.experiments'
def get_org_black_and_whitelist_for_site():
@@ -652,7 +652,7 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem
inverted_programs = meter.invert_programs()
urls, programs_data = {}, {}
- bundles_on_dashboard_flag = LegacyWaffleFlag(experiments_namespace, 'bundles_on_dashboard', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ bundles_on_dashboard_flag = WaffleFlag(f'{EXPERIMENTS_NAMESPACE}.bundles_on_dashboard', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
# TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete
if bundles_on_dashboard_flag.is_enabled() and inverted_programs and list(inverted_programs.items()):
diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py
index 3c15b9c239..d46ca96485 100644
--- a/common/lib/xmodule/xmodule/seq_module.py
+++ b/common/lib/xmodule/xmodule/seq_module.py
@@ -22,7 +22,7 @@ from xblock.core import XBlock
from xblock.exceptions import NoSuchServiceError
from xblock.fields import Boolean, Integer, List, Scope, String
-from edx_toggles.toggles import LegacyWaffleFlag, SettingDictToggle
+from edx_toggles.toggles import WaffleFlag, SettingDictToggle
from xmodule.util.xmodule_django import add_webpack_to_fragment
from xmodule.x_module import (
HTMLSnippet,
@@ -58,10 +58,8 @@ class_priority = ['video', 'problem']
# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file
_ = lambda text: text
-TIMED_EXAM_GATING_WAFFLE_FLAG = LegacyWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace="xmodule",
- flag_name='rev_1377_rollout',
- module_name=__name__,
+TIMED_EXAM_GATING_WAFFLE_FLAG = WaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ 'xmodule.rev_1377_rollout', __name__
)
# .. toggle_name: FEATURES['SHOW_PROGRESS_BAR']
diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py
index f4516a74d8..833c4af8fd 100644
--- a/common/lib/xmodule/xmodule/video_module/video_module.py
+++ b/common/lib/xmodule/xmodule/video_module/video_module.py
@@ -31,7 +31,7 @@ from xblock.runtime import KvsFieldData
from common.djangoapps.xblock_django.constants import ATTR_KEY_REQUEST_COUNTRY_CODE
from openedx.core.djangoapps.video_config.models import HLSPlaybackEnabledFlag, CourseYoutubeBlockedFlag
-from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE, waffle_flags
+from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE
from openedx.core.lib.cache_utils import request_cached
from openedx.core.lib.license import LicenseMixin
from xmodule.contentstore.content import StaticContent
@@ -198,7 +198,7 @@ class VideoBlock(
# check if youtube has been deprecated and hls as primary playback
# is enabled for this course
- return waffle_flags()[DEPRECATE_YOUTUBE].is_enabled(self.location.course_key)
+ return DEPRECATE_YOUTUBE.is_enabled(self.location.course_key)
def youtube_disabled_for_course(self): # lint-amnesty, pylint: disable=missing-function-docstring
if not self.location.context_key.is_course:
diff --git a/lms/djangoapps/course_api/__init__.py b/lms/djangoapps/course_api/__init__.py
index 340f67e2e1..e2e1b30451 100644
--- a/lms/djangoapps/course_api/__init__.py
+++ b/lms/djangoapps/course_api/__init__.py
@@ -1,9 +1,10 @@
""" Course API """
-from edx_toggles.toggles import LegacyWaffleSwitch, LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleSwitch
-WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name='course_list_api_rate_limit')
+
+WAFFLE_SWITCH_NAMESPACE = 'course_list_api_rate_limit'
# .. toggle_name: course_list_api_rate_limit.rate_limit_2
# .. toggle_implementation: WaffleSwitch
@@ -13,7 +14,7 @@ WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name='course_list_api_rate
# .. toggle_use_cases: circuit_breaker
# .. toggle_creation_date: 2018-06-12
# .. toggle_tickets: https://openedx.atlassian.net/browse/LEARNER-5527
-USE_RATE_LIMIT_2_FOR_COURSE_LIST_API = LegacyWaffleSwitch(WAFFLE_SWITCH_NAMESPACE, 'rate_limit_2', __name__)
+USE_RATE_LIMIT_2_FOR_COURSE_LIST_API = WaffleSwitch(f'{WAFFLE_SWITCH_NAMESPACE}.rate_limit_2', __name__)
# .. toggle_name: course_list_api_rate_limit.rate_limit_10
# .. toggle_implementation: WaffleSwitch
# .. toggle_default: False
@@ -22,4 +23,4 @@ USE_RATE_LIMIT_2_FOR_COURSE_LIST_API = LegacyWaffleSwitch(WAFFLE_SWITCH_NAMESPAC
# .. toggle_use_cases: circuit_breaker
# .. toggle_creation_date: 2018-06-12
# .. toggle_tickets: https://openedx.atlassian.net/browse/LEARNER-5527
-USE_RATE_LIMIT_10_FOR_COURSE_LIST_API = LegacyWaffleSwitch(WAFFLE_SWITCH_NAMESPACE, 'rate_limit_10', __name__)
+USE_RATE_LIMIT_10_FOR_COURSE_LIST_API = WaffleSwitch(f'{WAFFLE_SWITCH_NAMESPACE}.rate_limit_10', __name__)
diff --git a/lms/djangoapps/course_api/blocks/toggles.py b/lms/djangoapps/course_api/blocks/toggles.py
index 117c295394..35a38ffeaa 100644
--- a/lms/djangoapps/course_api/blocks/toggles.py
+++ b/lms/djangoapps/course_api/blocks/toggles.py
@@ -3,9 +3,9 @@ Toggles for Course API.
"""
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
-COURSE_BLOCKS_API_NAMESPACE = LegacyWaffleFlagNamespace(name='course_blocks_api')
+COURSE_BLOCKS_API_NAMESPACE = 'course_blocks_api'
# .. toggle_name: course_blocks_api.hide_access_denials
# .. toggle_implementation: WaffleFlag
@@ -15,8 +15,6 @@ COURSE_BLOCKS_API_NAMESPACE = LegacyWaffleFlagNamespace(name='course_blocks_api'
# .. toggle_creation_date: 2019-09-27
# .. toggle_target_removal_date: None
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
-HIDE_ACCESS_DENIALS_FLAG = LegacyWaffleFlag(
- waffle_namespace=COURSE_BLOCKS_API_NAMESPACE,
- flag_name='hide_access_denials',
- module_name=__name__,
+HIDE_ACCESS_DENIALS_FLAG = WaffleFlag(
+ f'{COURSE_BLOCKS_API_NAMESPACE}.hide_access_denials', __name__
)
diff --git a/lms/djangoapps/course_home_api/toggles.py b/lms/djangoapps/course_home_api/toggles.py
index bc47acf30b..afeba72954 100644
--- a/lms/djangoapps/course_home_api/toggles.py
+++ b/lms/djangoapps/course_home_api/toggles.py
@@ -2,14 +2,13 @@
Toggles for course home experience.
"""
-from edx_toggles.toggles import LegacyWaffleFlagNamespace
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+WAFFLE_FLAG_NAMESPACE = 'course_home'
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='course_home')
-
-COURSE_HOME_MICROFRONTEND_PROGRESS_TAB = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'course_home_mfe_progress_tab', # lint-amnesty, pylint: disable=toggle-missing-annotation
- __name__)
+COURSE_HOME_MICROFRONTEND_PROGRESS_TAB = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_FLAG_NAMESPACE}.course_home_mfe_progress_tab', __name__
+)
def course_home_mfe_progress_tab_is_active(course_key):
diff --git a/lms/djangoapps/courseware/tests/test_about.py b/lms/djangoapps/courseware/tests/test_about.py
index 244a5fd2b3..cbe59e042c 100644
--- a/lms/djangoapps/courseware/tests/test_about.py
+++ b/lms/djangoapps/courseware/tests/test_about.py
@@ -31,7 +31,6 @@ from common.djangoapps.course_modes.models import CourseMode
from openedx.core.djangoapps.models.course_details import CourseDetails
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, course_home_url
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
-from openedx.features.course_experience.waffle import WAFFLE_NAMESPACE as COURSE_EXPERIENCE_WAFFLE_NAMESPACE
from common.djangoapps.student.tests.factories import CourseEnrollmentAllowedFactory, UserFactory
from common.djangoapps.track.tests import EventTrackingTestCase
from common.djangoapps.util.milestones_helpers import get_prerequisite_courses_display, set_prerequisite_courses
@@ -397,13 +396,7 @@ class AboutSidebarHTMLTestCase(SharedModuleStoreTestCase):
)
@ddt.unpack
def test_html_sidebar_enabled(self, itemfactory_display_name, itemfactory_data, waffle_switch_value):
- with override_switch(
- '{}.{}'.format(
- COURSE_EXPERIENCE_WAFFLE_NAMESPACE,
- ENABLE_COURSE_ABOUT_SIDEBAR_HTML
- ),
- active=waffle_switch_value
- ):
+ with override_switch(ENABLE_COURSE_ABOUT_SIDEBAR_HTML.name, active=waffle_switch_value):
if itemfactory_display_name:
ItemFactory.create(
category="about",
diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py
index 3b79f238ea..764d60ed4a 100644
--- a/lms/djangoapps/courseware/tests/test_video_mongo.py
+++ b/lms/djangoapps/courseware/tests/test_video_mongo.py
@@ -36,7 +36,7 @@ from waffle.testutils import override_flag
from common.djangoapps.xblock_django.constants import ATTR_KEY_REQUEST_COUNTRY_CODE
from lms.djangoapps.courseware.tests.helpers import get_context_dict_from_string
-from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE, waffle_flags
+from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE
from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from xmodule.contentstore.content import StaticContent # lint-amnesty, pylint: disable=wrong-import-order
@@ -1177,9 +1177,8 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
video_xml = ''.format(
data['youtube']
)
- DEPRECATE_YOUTUBE_FLAG = waffle_flags()[DEPRECATE_YOUTUBE]
with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=data['course_override']):
- with override_flag(DEPRECATE_YOUTUBE_FLAG.name, active=data['waffle_enabled']):
+ with override_flag(DEPRECATE_YOUTUBE.name, active=data['waffle_enabled']):
self.initialize_block(data=video_xml, metadata=metadata)
context = self.item_descriptor.render(STUDENT_VIEW).content
assert '"prioritizeHls": {}'.format(data['result']) in context
diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py
index b84f51b3a4..c9fa87a0e1 100644
--- a/lms/djangoapps/courseware/tests/test_views.py
+++ b/lms/djangoapps/courseware/tests/test_views.py
@@ -76,7 +76,6 @@ from lms.djangoapps.courseware.testutils import RenderXBlockTestMixin
from lms.djangoapps.courseware.toggles import COURSEWARE_OPTIMIZED_RENDER_XBLOCK
from lms.djangoapps.courseware.user_state_client import DjangoXBlockUserStateClient
from lms.djangoapps.grades.config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT
-from lms.djangoapps.grades.config.waffle import waffle_switch as grades_waffle_switch
from lms.djangoapps.instructor.access import allow_access
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
@@ -1524,7 +1523,7 @@ class ProgressPageTests(ProgressPageBaseTests):
def test_progress_queries(self, enable_waffle, initial, subsequent):
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
self.setup_course()
- with override_waffle_switch(grades_waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=enable_waffle):
+ with override_waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT, active=enable_waffle):
with self.assertNumQueries(
initial, table_ignorelist=QUERY_COUNT_TABLE_IGNORELIST
), check_mongo_calls(2):
diff --git a/lms/djangoapps/courseware/toggles.py b/lms/djangoapps/courseware/toggles.py
index 63babaceac..4db48fa9cc 100644
--- a/lms/djangoapps/courseware/toggles.py
+++ b/lms/djangoapps/courseware/toggles.py
@@ -2,12 +2,12 @@
Toggles for courseware in-course experience.
"""
-from edx_toggles.toggles import LegacyWaffleFlagNamespace, SettingToggle
+from edx_toggles.toggles import SettingToggle, WaffleSwitch
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
# Namespace for courseware waffle flags.
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='courseware')
+WAFFLE_FLAG_NAMESPACE = 'courseware'
# Waffle flag to enable the course exit page in the learning MFE.
@@ -22,7 +22,7 @@ WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='courseware')
# .. toggle_warnings: Also set settings.LEARNING_MICROFRONTEND_URL.
# .. toggle_tickets: AA-188
COURSEWARE_MICROFRONTEND_COURSE_EXIT_PAGE = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE, 'microfrontend_course_exit_page', __name__
+ f'{WAFFLE_FLAG_NAMESPACE}.microfrontend_course_exit_page', __name__
)
# .. toggle_name: courseware.mfe_progress_milestones
@@ -36,7 +36,7 @@ COURSEWARE_MICROFRONTEND_COURSE_EXIT_PAGE = CourseWaffleFlag(
# .. toggle_warnings: Also set settings.LEARNING_MICROFRONTEND_URL.
# .. toggle_tickets: AA-371
COURSEWARE_MICROFRONTEND_PROGRESS_MILESTONES = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE, 'mfe_progress_milestones', __name__
+ f'{WAFFLE_FLAG_NAMESPACE}.mfe_progress_milestones', __name__
)
# .. toggle_name: courseware.mfe_progress_milestones_streak_celebration
@@ -52,7 +52,7 @@ COURSEWARE_MICROFRONTEND_PROGRESS_MILESTONES = CourseWaffleFlag(
# COURSEWARE_MICROFRONTEND_PROGRESS_MILESTONES.
# .. toggle_tickets: AA-304
COURSEWARE_MICROFRONTEND_PROGRESS_MILESTONES_STREAK_CELEBRATION = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE, 'mfe_progress_milestones_streak_celebration', __name__
+ f'{WAFFLE_FLAG_NAMESPACE}.mfe_progress_milestones_streak_celebration', __name__
)
# .. toggle_name: courseware.mfe_progress_milestones_streak_discount_enabled
@@ -65,8 +65,7 @@ COURSEWARE_MICROFRONTEND_PROGRESS_MILESTONES_STREAK_CELEBRATION = CourseWaffleFl
# .. toggle_target_removal_date: None
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-950
COURSEWARE_MFE_MILESTONES_STREAK_DISCOUNT = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE, 'streak_discount_enabled',
- __name__,
+ f'{WAFFLE_FLAG_NAMESPACE}.streak_discount_enabled', __name__
)
@@ -81,7 +80,7 @@ COURSEWARE_MFE_MILESTONES_STREAK_DISCOUNT = CourseWaffleFlag(
# .. toggle_creation_date: 2021-02-09
# .. toggle_target_removal_date: 2021-05-01
COURSEWARE_OPTIMIZED_RENDER_XBLOCK = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE, 'optimized_render_xblock', __name__
+ f'{WAFFLE_FLAG_NAMESPACE}.optimized_render_xblock', __name__
)
# .. toggle_name: COURSES_INVITE_ONLY
@@ -97,12 +96,17 @@ COURSEWARE_OPTIMIZED_RENDER_XBLOCK = CourseWaffleFlag(
# .. toggle_status: unsupported
COURSES_INVITE_ONLY = SettingToggle('COURSES_INVITE_ONLY', default=False)
+ENABLE_OPTIMIZELY_IN_COURSEWARE = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ 'RET.enable_optimizely_in_courseware', __name__
+)
+
def courseware_mfe_is_active() -> bool:
"""
Should we serve the Learning MFE as the canonical courseware experience?
"""
from lms.djangoapps.courseware.access_utils import in_preview_mode # avoid a circular import
+
# We only use legacy views for the Studio "preview mode" feature these days, while everyone else gets the MFE
return not in_preview_mode()
diff --git a/lms/djangoapps/courseware/views/index.py b/lms/djangoapps/courseware/views/index.py
index b789a86598..d2d72eae94 100644
--- a/lms/djangoapps/courseware/views/index.py
+++ b/lms/djangoapps/courseware/views/index.py
@@ -6,8 +6,8 @@ View for Courseware Index
import logging
-
import urllib
+
from django.conf import settings
from django.contrib.auth.views import redirect_to_login
from django.db import transaction
@@ -21,12 +21,16 @@ from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import View
from edx_django_utils.monitoring import set_custom_attributes_for_course_key
-from edx_toggles.toggles import LegacyWaffleSwitchNamespace
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from web_fragments.fragment import Fragment
+from xmodule.course_module import COURSE_VISIBILITY_PUBLIC
+from xmodule.modulestore.django import modulestore
+from xmodule.x_module import PUBLIC_VIEW, STUDENT_VIEW
from common.djangoapps.edxmako.shortcuts import render_to_response, render_to_string
+from common.djangoapps.student.models import CourseEnrollment
+from common.djangoapps.util.views import ensure_valid_course_key
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect, Redirect
from lms.djangoapps.experiments.utils import get_experiment_user_metadata_context
from lms.djangoapps.gating.api import get_entrance_exam_score, get_entrance_exam_usage_key
@@ -42,14 +46,9 @@ from openedx.features.course_experience import (
DISABLE_COURSE_OUTLINE_PAGE_FLAG,
default_course_url
)
-from openedx.features.course_experience.views.course_sock import CourseSockFragmentView
from openedx.features.course_experience.url_helpers import make_learning_mfe_courseware_url
+from openedx.features.course_experience.views.course_sock import CourseSockFragmentView
from openedx.features.enterprise_support.api import data_sharing_consent_required
-from common.djangoapps.student.models import CourseEnrollment
-from common.djangoapps.util.views import ensure_valid_course_key
-from xmodule.course_module import COURSE_VISIBILITY_PUBLIC # lint-amnesty, pylint: disable=wrong-import-order
-from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
-from xmodule.x_module import PUBLIC_VIEW, STUDENT_VIEW # lint-amnesty, pylint: disable=wrong-import-order
from ..access import has_access
from ..access_utils import check_public_access
@@ -64,7 +63,7 @@ from ..masquerade import check_content_start_date_for_masquerade_user, setup_mas
from ..model_data import FieldDataCache
from ..module_render import get_module_for_descriptor, toc_for_course
from ..permissions import MASQUERADE_AS_STUDENT
-from ..toggles import courseware_mfe_is_active
+from ..toggles import ENABLE_OPTIMIZELY_IN_COURSEWARE, courseware_mfe_is_active
from .views import CourseTabView
log = logging.getLogger("edx.courseware.views.index")
@@ -425,7 +424,7 @@ class CoursewareIndex(View):
'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"),
'bookmarks_api_url': reverse('bookmarks'),
'language_preference': self._get_language_preference(),
- 'disable_optimizely': not LegacyWaffleSwitchNamespace('RET').is_enabled('enable_optimizely_in_courseware'),
+ 'disable_optimizely': not ENABLE_OPTIMIZELY_IN_COURSEWARE.is_enabled(),
'section_title': None,
'sequence_title': None,
'disable_accordion': not DISABLE_COURSE_OUTLINE_PAGE_FLAG.is_enabled(self.course.id),
diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py
index 32a5bade9e..27dadbf05f 100644
--- a/lms/djangoapps/courseware/views/views.py
+++ b/lms/djangoapps/courseware/views/views.py
@@ -126,7 +126,6 @@ from openedx.features.course_experience.url_helpers import (
from openedx.features.course_experience.utils import dates_banner_should_display
from openedx.features.course_experience.views.course_dates import CourseDatesFragmentView
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
-from openedx.features.course_experience.waffle import waffle as course_experience_waffle
from openedx.features.enterprise_support.api import data_sharing_consent_required
from ..entrance_exams import user_can_skip_entrance_exam
@@ -967,7 +966,7 @@ def course_about(request, course_id):
# Overview
overview = CourseOverview.get_from_id(course.id)
- sidebar_html_enabled = course_experience_waffle().is_enabled(ENABLE_COURSE_ABOUT_SIDEBAR_HTML)
+ sidebar_html_enabled = ENABLE_COURSE_ABOUT_SIDEBAR_HTML.is_enabled()
allow_anonymous = check_public_access(course, [COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE])
diff --git a/lms/djangoapps/discussion/toggles.py b/lms/djangoapps/discussion/toggles.py
index b22151cd15..aaf1934fdd 100644
--- a/lms/djangoapps/discussion/toggles.py
+++ b/lms/djangoapps/discussion/toggles.py
@@ -1,7 +1,7 @@
"""
Discussions feature toggles
"""
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
WAFFLE_FLAG_NAMESPACE = "discussions"
@@ -12,7 +12,7 @@ WAFFLE_FLAG_NAMESPACE = "discussions"
# .. toggle_use_cases: temporary, open_edx
# .. toggle_creation_date: 2021-11-05
# .. toggle_target_removal_date: 2022-03-05
-ENABLE_DISCUSSIONS_MFE = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_discussions_mfe', __name__)
+ENABLE_DISCUSSIONS_MFE = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_discussions_mfe', __name__)
# .. toggle_name: discussions.enable_discussions_mfe_for_everyone
# .. toggle_implementation: CourseWaffleFlag
@@ -22,7 +22,7 @@ ENABLE_DISCUSSIONS_MFE = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_discuss
# .. toggle_creation_date: 2021-04-21
# .. toggle_target_removal_date: 2022-03-05
ENABLE_DISCUSSIONS_MFE_FOR_EVERYONE = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE, 'enable_discussions_mfe_for_everyone', __name__
+ f'{WAFFLE_FLAG_NAMESPACE}.enable_discussions_mfe_for_everyone', __name__
)
# .. toggle_name: discussions.enable_new_structure_discussions
@@ -32,7 +32,9 @@ ENABLE_DISCUSSIONS_MFE_FOR_EVERYONE = CourseWaffleFlag(
# .. toggle_use_cases: temporary, open_edx
# .. toggle_creation_date: 2022-02-22
# .. toggle_target_removal_date: 2022-09-22
-ENABLE_NEW_STRUCTURE_DISCUSSIONS = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_new_structure_discussions', __name__)
+ENABLE_NEW_STRUCTURE_DISCUSSIONS = CourseWaffleFlag(
+ f'{WAFFLE_FLAG_NAMESPACE}.enable_new_structure_discussions', __name__
+)
# .. toggle_name: discussions.enable_learners_tab_in_discussions_mfe
# .. toggle_implementation: CourseWaffleFlag
@@ -42,7 +44,9 @@ ENABLE_NEW_STRUCTURE_DISCUSSIONS = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enab
# .. toggle_creation_date: 2022-02-21
# .. toggle_target_removal_date: 2022-05-21
# lint-amnesty, pylint: disable=line-too-long
-ENABLE_LEARNERS_TAB_IN_DISCUSSIONS_MFE = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_learners_tab_in_discussions_mfe', __name__)
+ENABLE_LEARNERS_TAB_IN_DISCUSSIONS_MFE = CourseWaffleFlag(
+ f'{WAFFLE_FLAG_NAMESPACE}.enable_learners_tab_in_discussions_mfe', __name__
+)
# .. toggle_name: discussions.enable_moderation_reason_codes
# .. toggle_implementation: CourseWaffleFlag
@@ -52,9 +56,7 @@ ENABLE_LEARNERS_TAB_IN_DISCUSSIONS_MFE = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE,
# .. toggle_creation_date: 2022-02-22
# .. toggle_target_removal_date: 2022-09-22
ENABLE_DISCUSSION_MODERATION_REASON_CODES = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE,
- 'enable_moderation_reason_codes',
- __name__,
+ f'{WAFFLE_FLAG_NAMESPACE}.enable_moderation_reason_codes', __name__
)
# .. toggle_name: discussions.enable_reported_content_email_notifications
@@ -65,7 +67,5 @@ ENABLE_DISCUSSION_MODERATION_REASON_CODES = CourseWaffleFlag(
# .. toggle_creation_date: 2022-03-08
# .. toggle_target_removal_date: 2022-12-31
ENABLE_REPORTED_CONTENT_EMAIL_NOTIFICATIONS = CourseWaffleFlag(
- WAFFLE_FLAG_NAMESPACE,
- 'enable_reported_content_email_notifications',
- __name__,
+ f'{WAFFLE_FLAG_NAMESPACE}.enable_reported_content_email_notifications', __name__
)
diff --git a/lms/djangoapps/experiments/flags.py b/lms/djangoapps/experiments/flags.py
index 323100f849..65d17289ea 100644
--- a/lms/djangoapps/experiments/flags.py
+++ b/lms/djangoapps/experiments/flags.py
@@ -12,7 +12,7 @@ from edx_django_utils.cache import RequestCache
from common.djangoapps.track import segment
from lms.djangoapps.experiments.stable_bucketing import stable_bucketing_hash_group
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
log = logging.getLogger(__name__)
@@ -64,7 +64,6 @@ class ExperimentWaffleFlag(CourseWaffleFlag):
def __init__(
self,
- waffle_namespace,
flag_name,
module_name,
num_buckets=2,
@@ -72,11 +71,11 @@ class ExperimentWaffleFlag(CourseWaffleFlag):
use_course_aware_bucketing=True,
**kwargs
):
- super().__init__(waffle_namespace, flag_name, module_name, **kwargs)
+ super().__init__(flag_name, module_name, **kwargs)
self.num_buckets = num_buckets
self.experiment_id = experiment_id
self.bucket_flags = [
- CourseWaffleFlag(waffle_namespace, f'{flag_name}.{bucket}', module_name) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ CourseWaffleFlag(f'{flag_name}.{bucket}', module_name) # lint-amnesty, pylint: disable=toggle-missing-annotation
for bucket in range(num_buckets)
]
self.use_course_aware_bucketing = use_course_aware_bucketing
diff --git a/lms/djangoapps/experiments/tests/test_flags.py b/lms/djangoapps/experiments/tests/test_flags.py
index da70f4bb31..f1537b2ec1 100644
--- a/lms/djangoapps/experiments/tests/test_flags.py
+++ b/lms/djangoapps/experiments/tests/test_flags.py
@@ -18,7 +18,7 @@ from lms.djangoapps.experiments.factories import ExperimentKeyValueFactory
from lms.djangoapps.experiments.flags import ExperimentWaffleFlag
from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
@@ -38,7 +38,7 @@ class ExperimentWaffleFlagTests(SharedModuleStoreTestCase):
self.addCleanup(set_current_request, None)
set_current_request(self.request)
- self.flag = ExperimentWaffleFlag('experiments', 'test', __name__, num_buckets=2, experiment_id=0) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ self.flag = ExperimentWaffleFlag('experiments.test', __name__, num_buckets=2, experiment_id=0) # lint-amnesty, pylint: disable=toggle-missing-annotation
self.key = CourseKey.from_string('a/b/c')
bucket_patch = patch('lms.djangoapps.experiments.flags.stable_bucketing_hash_group', return_value=1)
@@ -105,7 +105,7 @@ class ExperimentWaffleFlagTests(SharedModuleStoreTestCase):
)
@ddt.unpack
def test_forcing_bucket(self, active, expected_bucket):
- bucket_flag = CourseWaffleFlag('experiments', 'test.0', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ bucket_flag = CourseWaffleFlag('experiments.test.0', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
with override_waffle_flag(bucket_flag, active=active):
assert self.get_bucket() == expected_bucket
@@ -163,7 +163,7 @@ class ExperimentWaffleFlagTests(SharedModuleStoreTestCase):
assert 'experiments' == self.flag._app_label
assert 'test' == self.flag._experiment_name
- flag = ExperimentWaffleFlag("namespace", "flag.name", __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ flag = ExperimentWaffleFlag("namespace.flag.name", __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
assert 'namespace' == flag._app_label
assert 'flag.name' == flag._experiment_name
@@ -174,14 +174,14 @@ class ExperimentWaffleFlagCourseAwarenessTest(SharedModuleStoreTestCase):
ExperimentWaffleFlag class.
"""
course_aware_flag = ExperimentWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- 'exp', 'aware', __name__, num_buckets=20, use_course_aware_bucketing=True,
+ 'exp.aware', __name__, num_buckets=20, use_course_aware_bucketing=True,
)
- course_aware_subflag = CourseWaffleFlag('exp', 'aware.1', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ course_aware_subflag = CourseWaffleFlag('exp.aware.1', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
course_unaware_flag = ExperimentWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- 'exp', 'unaware', __name__, num_buckets=20, use_course_aware_bucketing=False,
+ 'exp.unaware', __name__, num_buckets=20, use_course_aware_bucketing=False,
)
- course_unaware_subflag = CourseWaffleFlag('exp', 'unaware.1', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ course_unaware_subflag = CourseWaffleFlag('exp.unaware.1', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
course_key_1 = CourseKey.from_string("x/y/1")
course_key_2 = CourseKey.from_string("x/y/22")
diff --git a/lms/djangoapps/experiments/views_custom.py b/lms/djangoapps/experiments/views_custom.py
index 0d4c436887..def5621df8 100644
--- a/lms/djangoapps/experiments/views_custom.py
+++ b/lms/djangoapps/experiments/views_custom.py
@@ -6,7 +6,7 @@ from django.http import HttpResponseBadRequest
from django.utils.decorators import method_decorator
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from rest_framework.response import Response
@@ -33,11 +33,7 @@ from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin
# .. toggle_target_removal_date: None
# .. toggle_tickets: REV-934
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
-MOBILE_UPSELL_FLAG = LegacyWaffleFlag(
- waffle_namespace=LegacyWaffleFlagNamespace(name='experiments'),
- flag_name='mobile_upsell_rev934',
- module_name=__name__,
-)
+MOBILE_UPSELL_FLAG = WaffleFlag('experiments.mobile_upsell_rev934', __name__)
MOBILE_UPSELL_EXPERIMENT = 'mobile_upsell_experiment'
diff --git a/lms/djangoapps/grades/api.py b/lms/djangoapps/grades/api.py
index 9f14e33945..fb06e28013 100644
--- a/lms/djangoapps/grades/api.py
+++ b/lms/djangoapps/grades/api.py
@@ -134,10 +134,12 @@ def undo_override_subsection_grade(user_id, course_key_or_id, usage_key_or_id, f
def should_override_grade_on_rejected_exam(course_key_or_id):
- """Convenience function to return the state of the CourseWaffleFlag REJECTED_EXAM_OVERRIDES_GRADE"""
- from .config.waffle import waffle_flags, REJECTED_EXAM_OVERRIDES_GRADE
+ """
+ Convenience function to return the state of the CourseWaffleFlag REJECTED_EXAM_OVERRIDES_GRADE
+ """
+ from .config.waffle import REJECTED_EXAM_OVERRIDES_GRADE
course_key = _get_key(course_key_or_id, CourseKey)
- return waffle_flags()[REJECTED_EXAM_OVERRIDES_GRADE].is_enabled(course_key)
+ return REJECTED_EXAM_OVERRIDES_GRADE.is_enabled(course_key)
def _create_subsection_grade(user_id, course_key, usage_key):
diff --git a/lms/djangoapps/grades/config/__init__.py b/lms/djangoapps/grades/config/__init__.py
index 829277576a..7f976601dc 100644
--- a/lms/djangoapps/grades/config/__init__.py
+++ b/lms/djangoapps/grades/config/__init__.py
@@ -7,7 +7,6 @@ from django.conf import settings
from lms.djangoapps.grades.config.models import PersistentGradesEnabledFlag
from lms.djangoapps.grades.config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT
-from lms.djangoapps.grades.config.waffle import waffle as waffle_func
def assume_zero_if_absent(course_key):
@@ -17,7 +16,7 @@ def assume_zero_if_absent(course_key):
return (
should_persist_grades(course_key) and (
settings.FEATURES.get('ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS') or
- waffle_func().is_enabled(ASSUME_ZERO_GRADE_IF_ABSENT)
+ ASSUME_ZERO_GRADE_IF_ABSENT.is_enabled()
)
)
diff --git a/lms/djangoapps/grades/config/waffle.py b/lms/djangoapps/grades/config/waffle.py
index 1ca42ff7b8..ddd3432432 100644
--- a/lms/djangoapps/grades/config/waffle.py
+++ b/lms/djangoapps/grades/config/waffle.py
@@ -4,12 +4,13 @@ waffle switches for the Grades app.
"""
-from edx_toggles.toggles import LegacyWaffleFlagNamespace, LegacyWaffleSwitch, LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleSwitch
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
# Namespace
WAFFLE_NAMESPACE = 'grades'
+LOG_PREFIX = 'Grades: '
# Switches
@@ -23,8 +24,7 @@ WAFFLE_NAMESPACE = 'grades'
# .. toggle_creation_date: 2017-04-11
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/14771
# .. toggle_warnings: This requires the PersistentGradesEnabledFlag to be enabled.
-# TODO: Replace with WaffleSwitch(). See waffle_switch(name) docstring.
-ASSUME_ZERO_GRADE_IF_ABSENT = 'assume_zero_grade_if_absent'
+ASSUME_ZERO_GRADE_IF_ABSENT = WaffleSwitch(f'{WAFFLE_NAMESPACE}.assume_zero_grade_if_absent', __name__)
# .. toggle_name: grades.disable_regrade_on_policy_change
# .. toggle_implementation: WaffleSwitch
# .. toggle_default: False
@@ -32,8 +32,7 @@ ASSUME_ZERO_GRADE_IF_ABSENT = 'assume_zero_grade_if_absent'
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2017-08-03
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/15733
-# TODO: Replace with WaffleSwitch(). See waffle_switch(name) docstring.
-DISABLE_REGRADE_ON_POLICY_CHANGE = 'disable_regrade_on_policy_change'
+DISABLE_REGRADE_ON_POLICY_CHANGE = WaffleSwitch(f'{WAFFLE_NAMESPACE}.disable_regrade_on_policy_change', __name__)
# Course Flags
@@ -45,8 +44,10 @@ DISABLE_REGRADE_ON_POLICY_CHANGE = 'disable_regrade_on_policy_change'
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2019-05-29
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/20719
-# TODO: Replace with CourseWaffleFlag() from below. See waffle_flag(name) docstring.
-REJECTED_EXAM_OVERRIDES_GRADE = 'rejected_exam_overrides_grade'
+# TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment.
+REJECTED_EXAM_OVERRIDES_GRADE = CourseWaffleFlag(
+ f'{WAFFLE_NAMESPACE}.rejected_exam_overrides_grade', __name__, LOG_PREFIX
+)
# .. toggle_name: grades.rejected_exam_overrides_grade
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_default: False
@@ -55,8 +56,10 @@ REJECTED_EXAM_OVERRIDES_GRADE = 'rejected_exam_overrides_grade'
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2018-10-01
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/19026
-# TODO: Replace with CourseWaffleFlag() from below. See waffle_flag(name) docstring.
-ENFORCE_FREEZE_GRADE_AFTER_COURSE_END = 'enforce_freeze_grade_after_course_end'
+# TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment.
+ENFORCE_FREEZE_GRADE_AFTER_COURSE_END = CourseWaffleFlag(
+ f'{WAFFLE_NAMESPACE}.enforce_freeze_grade_after_course_end', __name__, LOG_PREFIX
+)
# .. toggle_name: grades.writable_gradebook
# .. toggle_implementation: CourseWaffleFlag
@@ -67,8 +70,8 @@ ENFORCE_FREEZE_GRADE_AFTER_COURSE_END = 'enforce_freeze_grade_after_course_end'
# .. toggle_creation_date: 2018-10-03
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/19054
# .. toggle_warnings: Enabling this requires that the `WRITABLE_GRADEBOOK_URL` setting be properly defined.
-# TODO: Replace with CourseWaffleFlag() from below. See waffle_flag(name) docstring.
-WRITABLE_GRADEBOOK = 'writable_gradebook'
+# TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment.
+WRITABLE_GRADEBOOK = CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.writable_gradebook', __name__, LOG_PREFIX)
# .. toggle_name: grades.bulk_management
# .. toggle_implementation: CourseWaffleFlag
@@ -78,80 +81,18 @@ WRITABLE_GRADEBOOK = 'writable_gradebook'
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2019-08-20
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/21389
-# TODO: Replace with CourseWaffleFlag() from below. See waffle_flag(name) docstring.
-BULK_MANAGEMENT = 'bulk_management'
-
-
-def waffle():
- """
- Deprecated: Returns the namespaced, cached, audited Waffle class for Grades.
-
- Note: Replace uses of this function with direct references to each switch.
- See waffle_switch(name) docstring for details.
- """
- return LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix='Grades: ')
-
-
-def waffle_switch(name):
- """
- Deprecated: Return the corresponding namespaced waffle switch.
-
- IMPORTANT: Do NOT copy this pattern and do NOT use this to reference new switches.
- Instead, replace the string constants above with the actual switch instances.
- For example::
-
- ASSUME_ZERO_GRADE_IF_ABSENT = WaffleSwitch(f'{WAFFLE_NAMESPACE}.assume_zero_grade_if_absent')
- """
- return LegacyWaffleSwitch(waffle(), name, module_name=__name__)
-
-
-def waffle_flags():
- """
- Deprecated: Returns the namespaced, cached, audited Waffle flags dictionary for Grades.
-
- IMPORTANT: Do NOT copy this pattern and do NOT use this to reference new flags.
- Instead, replace the string constants above with the flag declarations below, and use
- them directly.
- """
- namespace = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix='Grades: ')
- return {
- # By default, enable rejected exam grade overrides. Can be disabled on a course-by-course basis.
- # TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment.
- REJECTED_EXAM_OVERRIDES_GRADE: CourseWaffleFlag(
- namespace,
- REJECTED_EXAM_OVERRIDES_GRADE,
- __name__,
- ),
- # TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment.
- ENFORCE_FREEZE_GRADE_AFTER_COURSE_END: CourseWaffleFlag(
- namespace,
- ENFORCE_FREEZE_GRADE_AFTER_COURSE_END,
- __name__,
- ),
- # Have this course override flag so we can selectively turn off the gradebook for courses.
- # TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment.
- WRITABLE_GRADEBOOK: CourseWaffleFlag(
- namespace,
- WRITABLE_GRADEBOOK,
- __name__,
- ),
- BULK_MANAGEMENT: CourseWaffleFlag(
- namespace,
- BULK_MANAGEMENT,
- __name__,
- ),
- }
+BULK_MANAGEMENT = CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.bulk_management', __name__, LOG_PREFIX)
def is_writable_gradebook_enabled(course_key):
"""
Returns whether the writable gradebook app is enabled for the given course.
"""
- return waffle_flags()[WRITABLE_GRADEBOOK].is_enabled(course_key)
+ return WRITABLE_GRADEBOOK.is_enabled(course_key)
def gradebook_bulk_management_enabled(course_key):
"""
Returns whether bulk management features should be specially enabled for a given course.
"""
- return waffle_flags()[BULK_MANAGEMENT].is_enabled(course_key)
+ return BULK_MANAGEMENT.is_enabled(course_key)
diff --git a/lms/djangoapps/grades/grade_utils.py b/lms/djangoapps/grades/grade_utils.py
index cd432c9e2d..0344cf6c20 100644
--- a/lms/djangoapps/grades/grade_utils.py
+++ b/lms/djangoapps/grades/grade_utils.py
@@ -10,14 +10,16 @@ from django.utils import timezone
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
-from .config.waffle import ENFORCE_FREEZE_GRADE_AFTER_COURSE_END, waffle_flags
+from .config.waffle import ENFORCE_FREEZE_GRADE_AFTER_COURSE_END
log = logging.getLogger(__name__)
def are_grades_frozen(course_key):
- """ Returns whether grades are frozen for the given course. """
- if waffle_flags()[ENFORCE_FREEZE_GRADE_AFTER_COURSE_END].is_enabled(course_key):
+ """
+ Returns whether grades are frozen for the given course.
+ """
+ if ENFORCE_FREEZE_GRADE_AFTER_COURSE_END.is_enabled(course_key):
course = CourseOverview.get_from_id(course_key)
if course.end:
freeze_grade_date = course.end + timedelta(30)
diff --git a/lms/djangoapps/grades/migrations/0018_add_waffle_flag_defaults.py b/lms/djangoapps/grades/migrations/0018_add_waffle_flag_defaults.py
index c64149216b..3903fc970b 100644
--- a/lms/djangoapps/grades/migrations/0018_add_waffle_flag_defaults.py
+++ b/lms/djangoapps/grades/migrations/0018_add_waffle_flag_defaults.py
@@ -4,7 +4,6 @@ from lms.djangoapps.grades.config.waffle import (
ENFORCE_FREEZE_GRADE_AFTER_COURSE_END,
REJECTED_EXAM_OVERRIDES_GRADE,
WRITABLE_GRADEBOOK,
- waffle_flags,
)
@@ -12,13 +11,13 @@ def create_flag(apps, schema_editor):
Flag = apps.get_model('waffle', 'Flag')
# Replacement for flag_undefined_default=True on flag definition
Flag.objects.get_or_create(
- name=waffle_flags()[REJECTED_EXAM_OVERRIDES_GRADE].name, defaults={'everyone': True}
+ name=REJECTED_EXAM_OVERRIDES_GRADE.name, defaults={'everyone': True}
)
Flag.objects.get_or_create(
- name=waffle_flags()[ENFORCE_FREEZE_GRADE_AFTER_COURSE_END].name, defaults={'everyone': True}
+ name=ENFORCE_FREEZE_GRADE_AFTER_COURSE_END.name, defaults={'everyone': True}
)
Flag.objects.get_or_create(
- name=waffle_flags()[WRITABLE_GRADEBOOK].name, defaults={'everyone': True}
+ name=WRITABLE_GRADEBOOK.name, defaults={'everyone': True}
)
diff --git a/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py b/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py
index 7b8e631527..0ff96467e7 100644
--- a/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py
+++ b/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py
@@ -34,7 +34,7 @@ from common.djangoapps.student.tests.factories import InstructorFactory
from common.djangoapps.student.tests.factories import StaffFactory
from lms.djangoapps.certificates.data import CertificateStatuses
from lms.djangoapps.certificates.models import GeneratedCertificate
-from lms.djangoapps.grades.config.waffle import BULK_MANAGEMENT, WRITABLE_GRADEBOOK, waffle_flags
+from lms.djangoapps.grades.config.waffle import BULK_MANAGEMENT, WRITABLE_GRADEBOOK
from lms.djangoapps.grades.constants import GradeOverrideFeatureEnum
from lms.djangoapps.grades.course_data import CourseData
from lms.djangoapps.grades.course_grade import CourseGrade
@@ -270,7 +270,7 @@ class CourseGradingViewTest(SharedModuleStoreTestCase, APITestCase):
assert resp.status_code == status.HTTP_200_OK
assert resp.data['can_see_bulk_management'] is True
- @override_waffle_flag(waffle_flags()[BULK_MANAGEMENT], active=True)
+ @override_waffle_flag(BULK_MANAGEMENT, active=True)
def test_can_see_bulk_management_force_enabled(self):
# Given a course without (or with) a master's track where bulk management is enabled with the config flag
# When getting course grading view
@@ -290,7 +290,7 @@ class GradebookViewTestBase(GradeViewTestMixin, APITestCase):
def setUpClass(cls):
super().setUpClass()
cls.namespaced_url = 'grades_api:v1:course_gradebook'
- cls.waffle_flag = waffle_flags()[WRITABLE_GRADEBOOK]
+ cls.waffle_flag = WRITABLE_GRADEBOOK
cls.course = CourseFactory.create(display_name='test-course', run='run-1')
cls.course_key = cls.course.id
diff --git a/lms/djangoapps/grades/tasks.py b/lms/djangoapps/grades/tasks.py
index 41c5457e49..bf0b8993f1 100644
--- a/lms/djangoapps/grades/tasks.py
+++ b/lms/djangoapps/grades/tasks.py
@@ -28,7 +28,7 @@ from openedx.core.djangoapps.content.course_overviews.models import \
CourseOverview # lint-amnesty, pylint: disable=unused-import
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
-from .config.waffle import DISABLE_REGRADE_ON_POLICY_CHANGE, waffle
+from .config.waffle import DISABLE_REGRADE_ON_POLICY_CHANGE
from .constants import ScoreDatabaseTableEnum
from .course_grade_factory import CourseGradeFactory
from .exceptions import DatabaseNotReadyError
@@ -58,7 +58,7 @@ def compute_all_grades_for_course(**kwargs):
Kicks off a series of compute_grades_for_course_v2 tasks
to cover all of the students in the course.
"""
- if waffle().is_enabled(DISABLE_REGRADE_ON_POLICY_CHANGE):
+ if DISABLE_REGRADE_ON_POLICY_CHANGE.is_enabled():
log.debug('Grades: ignoring policy change regrade due to waffle switch')
else:
course_key = CourseKey.from_string(kwargs.pop('course_key'))
diff --git a/lms/djangoapps/grades/tests/test_course_grade.py b/lms/djangoapps/grades/tests/test_course_grade.py
index c59f3ac73b..77ea291957 100644
--- a/lms/djangoapps/grades/tests/test_course_grade.py
+++ b/lms/djangoapps/grades/tests/test_course_grade.py
@@ -12,7 +12,7 @@ from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangolib.testing.utils import get_mock_request
-from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT, waffle_switch
+from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT
from ..course_data import CourseData
from ..course_grade import ZeroCourseGrade
from ..course_grade_factory import CourseGradeFactory
@@ -33,7 +33,7 @@ class ZeroGradeTest(GradeTestBase):
"""
Creates a ZeroCourseGrade and ensures it's empty.
"""
- with override_waffle_switch(waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=assume_zero_enabled):
+ with override_waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT, active=assume_zero_enabled):
course_data = CourseData(self.request.user, structure=self.course_structure)
chapter_grades = ZeroCourseGrade(self.request.user, course_data).chapter_grades
for chapter in chapter_grades:
@@ -48,7 +48,7 @@ class ZeroGradeTest(GradeTestBase):
"""
Creates a zero course grade and ensures that null scores aren't included in the section problem scores.
"""
- with override_waffle_switch(waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=assume_zero_enabled):
+ with override_waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT, active=assume_zero_enabled):
with patch('lms.djangoapps.grades.subsection_grade.get_score', return_value=None):
course_data = CourseData(self.request.user, structure=self.course_structure)
chapter_grades = ZeroCourseGrade(self.request.user, course_data).chapter_grades
diff --git a/lms/djangoapps/grades/tests/test_course_grade_factory.py b/lms/djangoapps/grades/tests/test_course_grade_factory.py
index 880b31f0d2..df9acb4905 100644
--- a/lms/djangoapps/grades/tests/test_course_grade_factory.py
+++ b/lms/djangoapps/grades/tests/test_course_grade_factory.py
@@ -18,7 +18,7 @@ from openedx.core.djangoapps.signals.signals import COURSE_GRADE_NOW_PASSED
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
-from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT, waffle_switch
+from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT
from ..course_grade import CourseGrade, ZeroCourseGrade
from ..course_grade_factory import CourseGradeFactory
from ..subsection_grade import ReadSubsectionGrade, ZeroSubsectionGrade
@@ -162,7 +162,7 @@ class TestCourseGradeFactory(GradeTestBase):
@ddt.data(*itertools.product((True, False), (True, False)))
@ddt.unpack
def test_read_zero(self, assume_zero_enabled, create_if_needed):
- with override_waffle_switch(waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=assume_zero_enabled):
+ with override_waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT, active=assume_zero_enabled):
grade_factory = CourseGradeFactory()
course_grade = grade_factory.read(self.request.user, self.course, create_if_needed=create_if_needed)
if create_if_needed or assume_zero_enabled:
diff --git a/lms/djangoapps/grades/tests/test_services.py b/lms/djangoapps/grades/tests/test_services.py
index e77af77e28..459cf6d08c 100644
--- a/lms/djangoapps/grades/tests/test_services.py
+++ b/lms/djangoapps/grades/tests/test_services.py
@@ -17,7 +17,6 @@ from lms.djangoapps.grades.services import GradesService
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory # lint-amnesty, pylint: disable=wrong-import-order
-from ..config.waffle import REJECTED_EXAM_OVERRIDES_GRADE
from ..constants import ScoreDatabaseTableEnum
@@ -68,11 +67,10 @@ class GradesServiceTests(ModuleStoreTestCase):
self.mock_create_id.return_value = 1
self.type_patcher = patch('lms.djangoapps.grades.api.set_event_transaction_type')
self.mock_set_type = self.type_patcher.start()
- self.flag_patcher = patch('lms.djangoapps.grades.config.waffle.waffle_flags')
- self.mock_waffle_flags = self.flag_patcher.start()
- self.mock_waffle_flags.return_value = {
- REJECTED_EXAM_OVERRIDES_GRADE: MockWaffleFlag(True)
- }
+ self.waffle_flag_patch = patch(
+ 'lms.djangoapps.grades.config.waffle.REJECTED_EXAM_OVERRIDES_GRADE', MockWaffleFlag(True)
+ )
+ self.waffle_flag_patch.start()
def tearDown(self):
super().tearDown()
@@ -80,7 +78,7 @@ class GradesServiceTests(ModuleStoreTestCase):
self.signal_patcher.stop()
self.id_patcher.stop()
self.type_patcher.stop()
- self.flag_patcher.stop()
+ self.waffle_flag_patch.stop()
def subsection_grade_to_dict(self, grade):
return {
@@ -304,7 +302,5 @@ class GradesServiceTests(ModuleStoreTestCase):
def test_should_override_grade_on_rejected_exam(self):
assert self.service.should_override_grade_on_rejected_exam('course-v1:edX+DemoX+Demo_Course')
- self.mock_waffle_flags.return_value = {
- REJECTED_EXAM_OVERRIDES_GRADE: MockWaffleFlag(False)
- }
- assert not self.service.should_override_grade_on_rejected_exam('course-v1:edX+DemoX+Demo_Course')
+ with patch('lms.djangoapps.grades.config.waffle.REJECTED_EXAM_OVERRIDES_GRADE', MockWaffleFlag(False)):
+ assert not self.service.should_override_grade_on_rejected_exam('course-v1:edX+DemoX+Demo_Course')
diff --git a/lms/djangoapps/grades/tests/test_tasks.py b/lms/djangoapps/grades/tests/test_tasks.py
index 637e162ccc..b76d549b33 100644
--- a/lms/djangoapps/grades/tests/test_tasks.py
+++ b/lms/djangoapps/grades/tests/test_tasks.py
@@ -26,7 +26,7 @@ from common.djangoapps.track.event_transaction_utils import create_new_event_tra
from common.djangoapps.util.date_utils import to_timestamp
from lms.djangoapps.grades import tasks
from lms.djangoapps.grades.config.models import PersistentGradesEnabledFlag
-from lms.djangoapps.grades.config.waffle import ENFORCE_FREEZE_GRADE_AFTER_COURSE_END, waffle_flags
+from lms.djangoapps.grades.config.waffle import ENFORCE_FREEZE_GRADE_AFTER_COURSE_END
from lms.djangoapps.grades.constants import ScoreDatabaseTableEnum
from lms.djangoapps.grades.models import PersistentCourseGrade, PersistentSubsectionGrade
from lms.djangoapps.grades.signals.signals import PROBLEM_WEIGHTED_SCORE_CHANGED
@@ -490,7 +490,7 @@ class FreezeGradingAfterCourseEndTest(HasCourseWithProblemsMixin, ModuleStoreTes
super().setUp()
self.users = [UserFactory.create() for _ in range(12)]
self.user = self.users[0]
- self.freeze_grade_flag = waffle_flags()[ENFORCE_FREEZE_GRADE_AFTER_COURSE_END]
+ self.freeze_grade_flag = ENFORCE_FREEZE_GRADE_AFTER_COURSE_END
def _assert_log(self, mock_log, method_name):
assert mock_log.info.called
diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
index 4f0e49152e..da495802e6 100644
--- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
+++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
@@ -21,7 +21,6 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, chec
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student.models import CourseEnrollment
-from common.djangoapps.student.roles import CourseFinanceAdminRole # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.student.tests.factories import AdminFactory, CourseAccessRoleFactory, CourseEnrollmentFactory
from common.djangoapps.student.tests.factories import StaffFactory
from common.djangoapps.student.tests.factories import UserFactory
@@ -30,7 +29,7 @@ from lms.djangoapps.courseware.courses import get_studio_url
from lms.djangoapps.courseware.tabs import get_course_tab_list
from lms.djangoapps.courseware.tests.factories import StudentModuleFactory
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
-from lms.djangoapps.grades.config.waffle import WRITABLE_GRADEBOOK, waffle_flags
+from lms.djangoapps.grades.config.waffle import WRITABLE_GRADEBOOK
from lms.djangoapps.instructor.toggles import DATA_DOWNLOAD_V2
from lms.djangoapps.instructor.views.gradebook_api import calculate_page_info
from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted
@@ -335,8 +334,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT
Test that, when the writable gradebook feature is enabled and
deployed in another domain, a staff member can see it.
"""
- waffle_flag = waffle_flags()[WRITABLE_GRADEBOOK]
- with override_waffle_flag(waffle_flag, active=True):
+ with override_waffle_flag(WRITABLE_GRADEBOOK, active=True):
response = self.client.get(self.url)
expected_gradebook_url = f'http://gradebook.local.edx.org/{self.course.id}'
@@ -357,8 +355,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT
Test that, when the writable gradebook feature is enabled and
deployed in a subdirectory, a staff member can see it.
"""
- waffle_flag = waffle_flags()[WRITABLE_GRADEBOOK]
- with override_waffle_flag(waffle_flag, active=True):
+ with override_waffle_flag(WRITABLE_GRADEBOOK, active=True):
response = self.client.get(self.url)
expected_gradebook_url = f'{settings.WRITABLE_GRADEBOOK_URL}/{self.course.id}'
@@ -393,8 +390,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT
a message that the feature is only available for courses with small
numbers of learners.
"""
- waffle_flag = waffle_flags()[WRITABLE_GRADEBOOK]
- with override_waffle_flag(waffle_flag, active=True):
+ with override_waffle_flag(WRITABLE_GRADEBOOK, active=True):
response = self.client.get(self.url)
assert TestInstructorDashboard.GRADEBOOK_LEARNER_COUNT_MESSAGE not in response.content.decode('utf-8')
self.assertContains(response, 'View Gradebook')
diff --git a/lms/djangoapps/instructor/toggles.py b/lms/djangoapps/instructor/toggles.py
index 8f360ff504..5149e8765c 100644
--- a/lms/djangoapps/instructor/toggles.py
+++ b/lms/djangoapps/instructor/toggles.py
@@ -2,11 +2,11 @@
Waffle flags for instructor dashboard.
"""
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
+
-WAFFLE_NAMESPACE = 'instructor'
# Namespace for instructor waffle flags.
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE)
+WAFFLE_FLAG_NAMESPACE = 'instructor'
# Waffle flag enable new data download UI on specific course.
# .. toggle_name: instructor.enable_data_download_v2
@@ -16,11 +16,7 @@ WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE)
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2020-07-8
# .. toggle_tickets: PROD-1309
-DATA_DOWNLOAD_V2 = LegacyWaffleFlag(
- waffle_namespace=WAFFLE_FLAG_NAMESPACE,
- flag_name='enable_data_download_v2',
- module_name=__name__,
-)
+DATA_DOWNLOAD_V2 = WaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_data_download_v2', __name__)
# .. toggle_name: verify_student.optimised_is_small_course
# .. toggle_implementation: WaffleFlag
@@ -31,11 +27,7 @@ DATA_DOWNLOAD_V2 = LegacyWaffleFlag(
# .. toggle_warnings: Description mentions staged rollout, but the use case is not set as temporary.
# This may actually be a temporary toggle.
# .. toggle_tickets: PROD-1740
-OPTIMISED_IS_SMALL_COURSE = LegacyWaffleFlag(
- waffle_namespace=WAFFLE_FLAG_NAMESPACE,
- flag_name='optimised_is_small_course',
- module_name=__name__,
-)
+OPTIMISED_IS_SMALL_COURSE = WaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.optimised_is_small_course', __name__)
def data_download_v2_is_enabled():
diff --git a/lms/djangoapps/instructor_task/config/waffle.py b/lms/djangoapps/instructor_task/config/waffle.py
index bd569d6c03..ee2319293b 100644
--- a/lms/djangoapps/instructor_task/config/waffle.py
+++ b/lms/djangoapps/instructor_task/config/waffle.py
@@ -3,53 +3,33 @@ This module contains various configuration settings via
waffle switches for the instructor_task app.
"""
-from edx_toggles.toggles import LegacyWaffleFlagNamespace, LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleSwitch
+
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
WAFFLE_NAMESPACE = 'instructor_task'
-# TODO: Remove and replace with direct references to each flag.
-INSTRUCTOR_TASK_WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE)
-# TODO: Remove and replace with direct references to each switch.
-WAFFLE_SWITCHES = LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE)
# Waffle switches
-# TODO: Replace with WaffleSwitch(). See WAFFLE_SWITCHES comment.
-OPTIMIZE_GET_LEARNERS_FOR_COURSE = 'optimize_get_learners_for_course'
+OPTIMIZE_GET_LEARNERS_FOR_COURSE = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.optimize_get_learners_for_course', __name__
+)
# Course override flags
-# TODO: Replace with WaffleFlag(). See waffle_flags() docstring.
-GENERATE_PROBLEM_GRADE_REPORT_VERIFIED_ONLY = 'generate_problem_grade_report_verified_only'
-# TODO: Replace with WaffleFlag(). See waffle_flags() docstring.
-GENERATE_COURSE_GRADE_REPORT_VERIFIED_ONLY = 'generate_course_grade_report_verified_only'
+GENERATE_PROBLEM_GRADE_REPORT_VERIFIED_ONLY = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.generate_problem_grade_report_verified_only', __name__
+)
-
-def waffle_flags():
- """
- Returns the namespaced, cached, audited Waffle flags dictionary for Grades.
-
- IMPORTANT: Do NOT copy this dict pattern and do NOT add new flags to this dict.
- Instead, replace the string constants above with the actual flag instances.
- """
- return {
- GENERATE_PROBLEM_GRADE_REPORT_VERIFIED_ONLY: CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace=INSTRUCTOR_TASK_WAFFLE_FLAG_NAMESPACE,
- flag_name=GENERATE_PROBLEM_GRADE_REPORT_VERIFIED_ONLY,
- module_name=__name__,
- ),
- GENERATE_COURSE_GRADE_REPORT_VERIFIED_ONLY: CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace=INSTRUCTOR_TASK_WAFFLE_FLAG_NAMESPACE,
- flag_name=GENERATE_COURSE_GRADE_REPORT_VERIFIED_ONLY,
- module_name=__name__,
- ),
- }
+GENERATE_COURSE_GRADE_REPORT_VERIFIED_ONLY = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.generate_course_grade_report_verified_only', __name__
+)
def optimize_get_learners_switch_enabled():
"""
Returns True if optimize get learner switch is enabled, otherwise False.
"""
- return WAFFLE_SWITCHES.is_enabled(OPTIMIZE_GET_LEARNERS_FOR_COURSE)
+ return OPTIMIZE_GET_LEARNERS_FOR_COURSE.is_enabled()
def problem_grade_report_verified_only(course_id):
@@ -58,7 +38,7 @@ def problem_grade_report_verified_only(course_id):
return rows for verified students in the given course,
False otherwise.
"""
- return waffle_flags()[GENERATE_PROBLEM_GRADE_REPORT_VERIFIED_ONLY].is_enabled(course_id)
+ return GENERATE_PROBLEM_GRADE_REPORT_VERIFIED_ONLY.is_enabled(course_id)
def course_grade_report_verified_only(course_id):
@@ -67,4 +47,4 @@ def course_grade_report_verified_only(course_id):
return rows for verified students in the given course,
False otherwise.
"""
- return waffle_flags()[GENERATE_COURSE_GRADE_REPORT_VERIFIED_ONLY].is_enabled(course_id)
+ return GENERATE_COURSE_GRADE_REPORT_VERIFIED_ONLY.is_enabled(course_id)
diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py
index cf4d08fbc7..7dd6f72900 100644
--- a/lms/djangoapps/ora_staff_grader/views.py
+++ b/lms/djangoapps/ora_staff_grader/views.py
@@ -57,7 +57,7 @@ from lms.djangoapps.ora_staff_grader.utils import require_params
from openedx.core.djangoapps.content.course_overviews.api import (
get_course_overview_or_none,
)
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
log = logging.getLogger(__name__)
@@ -98,10 +98,12 @@ class InitializeView(StaffGraderBaseView):
def _is_staff_grader_enabled(self, course_key):
""" Helper to evaluate if the staff grader flag / overrides are enabled """
# This toggle is documented on the edx-ora2 repo in openassessment/xblock/config_mixin.py
+ # Note: Do not copy this practice of directly using a toggle from a library.
+ # Instead, see docs for exposing a wrapper api:
+ # https://edx.readthedocs.io/projects/edx-toggles/en/latest/how_to/implement_the_right_toggle_type.html#using-other-toggles pylint: disable=line-too-long
# pylint: disable=toggle-missing-annotation
enhanced_staff_grader_flag = CourseWaffleFlag(
- WAFFLE_NAMESPACE,
- ENHANCED_STAFF_GRADER,
+ f"{WAFFLE_NAMESPACE}.{ENHANCED_STAFF_GRADER}",
module_name='openassessment.xblock.config_mixin'
)
return enhanced_staff_grader_flag.is_enabled(course_key)
diff --git a/lms/djangoapps/teams/toggles.py b/lms/djangoapps/teams/toggles.py
index f9a100e15b..9f3a30f085 100644
--- a/lms/djangoapps/teams/toggles.py
+++ b/lms/djangoapps/teams/toggles.py
@@ -3,7 +3,7 @@ Togglable settings for Teams behavior
"""
from edx_toggles.toggles import SettingDictToggle
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
# Course Waffle inherited from edx/edx-ora2
WAFFLE_NAMESPACE = "openresponseassessment"
@@ -29,7 +29,7 @@ def are_team_submissions_enabled(course_key):
"""
Checks to see if the CourseWaffleFlag or Django setting for team submissions is enabled
"""
- if CourseWaffleFlag(WAFFLE_NAMESPACE, TEAM_SUBMISSIONS_FLAG, __name__).is_enabled(
+ if CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.{TEAM_SUBMISSIONS_FLAG}', __name__).is_enabled(
course_key
):
return True
diff --git a/lms/djangoapps/verify_student/toggles.py b/lms/djangoapps/verify_student/toggles.py
index 9439262c1e..1632d26a97 100644
--- a/lms/djangoapps/verify_student/toggles.py
+++ b/lms/djangoapps/verify_student/toggles.py
@@ -2,10 +2,10 @@
Toggles for verify_student app
"""
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
# Namespace for verify_students waffle flags.
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='verify_student')
+WAFFLE_FLAG_NAMESPACE = 'verify_student'
# Waffle flag to use new email templates for sending ID verification emails.
# .. toggle_name: verify_student.use_new_email_templates
@@ -18,11 +18,7 @@ WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='verify_student')
# .. toggle_target_removal_date: None
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
# .. toggle_tickets: PROD-1639
-USE_NEW_EMAIL_TEMPLATES = LegacyWaffleFlag(
- waffle_namespace=WAFFLE_FLAG_NAMESPACE,
- flag_name='use_new_email_templates',
- module_name=__name__,
-)
+USE_NEW_EMAIL_TEMPLATES = WaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.use_new_email_templates', __name__)
def use_new_templates_for_id_verification_emails():
diff --git a/openedx/core/djangoapps/course_apps/toggles.py b/openedx/core/djangoapps/course_apps/toggles.py
index daa5fe3f8e..34f40b4d06 100644
--- a/openedx/core/djangoapps/course_apps/toggles.py
+++ b/openedx/core/djangoapps/course_apps/toggles.py
@@ -1,12 +1,10 @@
"""
Toggles for course apps.
"""
-from edx_toggles.toggles import LegacyWaffleSwitchNamespace
-
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
#: Namespace for use by course apps for creating availability toggles
-COURSE_APPS_WAFFLE_NAMESPACE = LegacyWaffleSwitchNamespace("course_apps")
+COURSE_APPS_WAFFLE_NAMESPACE = 'course_apps'
# .. toggle_name: course_apps.proctoring_settings_modal_view
# .. toggle_use_cases: temporary
@@ -18,7 +16,7 @@ COURSE_APPS_WAFFLE_NAMESPACE = LegacyWaffleSwitchNamespace("course_apps")
# .. toggle_creation_date: 2021-08-17
# .. toggle_target_removal_date: None
PROCTORING_SETTINGS_MODAL_VIEW = CourseWaffleFlag(
- COURSE_APPS_WAFFLE_NAMESPACE, 'proctoring_settings_modal_view', module_name=__name__,
+ f'{COURSE_APPS_WAFFLE_NAMESPACE}.proctoring_settings_modal_view', __name__
)
diff --git a/openedx/core/djangoapps/course_live/config/waffle.py b/openedx/core/djangoapps/course_live/config/waffle.py
index d2c05196f3..7cdb99f515 100644
--- a/openedx/core/djangoapps/course_live/config/waffle.py
+++ b/openedx/core/djangoapps/course_live/config/waffle.py
@@ -3,11 +3,9 @@ This module contains various configuration settings via
waffle switches for the live app.
"""
-from edx_toggles.toggles import LegacyWaffleFlagNamespace
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
-
-WAFFLE_NAMESPACE = LegacyWaffleFlagNamespace(name='course_live')
+WAFFLE_NAMESPACE = 'course_live'
# .. toggle_name: course_live.enable_course_live
# .. toggle_implementation: CourseWaffleFlag
@@ -18,8 +16,4 @@ WAFFLE_NAMESPACE = LegacyWaffleFlagNamespace(name='course_live')
# .. toggle_target_removal_date: 2022-06-02
# .. toggle_warnings: When the flag is ON, the course live app will be visible in the course authoring mfe
# .. toggle_tickets: TNL-9603
-ENABLE_COURSE_LIVE = CourseWaffleFlag(
- waffle_namespace=WAFFLE_NAMESPACE,
- flag_name='enable_course_live',
- module_name=__name__,
-)
+ENABLE_COURSE_LIVE = CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.enable_course_live', __name__)
diff --git a/openedx/core/djangoapps/discussions/config/waffle.py b/openedx/core/djangoapps/discussions/config/waffle.py
index b642baabca..47e9c56f41 100644
--- a/openedx/core/djangoapps/discussions/config/waffle.py
+++ b/openedx/core/djangoapps/discussions/config/waffle.py
@@ -3,12 +3,10 @@ This module contains various configuration settings via
waffle switches for the discussions app.
"""
-from edx_toggles.toggles import LegacyWaffleFlagNamespace
-
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
-WAFFLE_NAMESPACE = LegacyWaffleFlagNamespace(name='discussions')
+WAFFLE_NAMESPACE = 'discussions'
# .. toggle_name: discussions.override_discussion_legacy_settings
# .. toggle_implementation: CourseWaffleFlag
@@ -20,9 +18,7 @@ WAFFLE_NAMESPACE = LegacyWaffleFlagNamespace(name='discussions')
# .. toggle_warnings: When the flag is ON, the discussion settings will be available on legacy experience.
# .. toggle_tickets: TNL-8389
OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG = CourseWaffleFlag(
- waffle_namespace=WAFFLE_NAMESPACE,
- flag_name='override_discussion_legacy_settings',
- module_name=__name__,
+ f'{WAFFLE_NAMESPACE}.override_discussion_legacy_settings', __name__
)
@@ -36,7 +32,5 @@ OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG = CourseWaffleFlag(
# .. toggle_warnings: When the flag is ON, the new experience for Pages and Resources will be enabled.
# .. toggle_tickets: TNL-7791
ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND = CourseWaffleFlag(
- waffle_namespace=WAFFLE_NAMESPACE,
- flag_name='pages_and_resources_mfe',
- module_name=__name__,
+ f'{WAFFLE_NAMESPACE}.pages_and_resources_mfe', __name__
)
diff --git a/openedx/core/djangoapps/programs/__init__.py b/openedx/core/djangoapps/programs/__init__.py
index 04def48a7a..8d12a150ee 100644
--- a/openedx/core/djangoapps/programs/__init__.py
+++ b/openedx/core/djangoapps/programs/__init__.py
@@ -8,15 +8,14 @@ if and only if the service is deployed in the Open edX installation.
To ensure maximum separation of concerns, and a minimum of interdependencies,
this package should be kept small, thin, and stateless.
"""
+from edx_toggles.toggles import WaffleSwitch
+
default_app_config = 'openedx.core.djangoapps.programs.apps.ProgramsConfig'
-from edx_toggles.toggles import LegacyWaffleSwitch, LegacyWaffleSwitchNamespace # lint-amnesty, pylint: disable=wrong-import-position
-
-PROGRAMS_WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name='programs')
+PROGRAMS_WAFFLE_SWITCH_NAMESPACE = 'programs'
# This is meant to be enabled until https://openedx.atlassian.net/browse/LEARNER-5573 needs to be resolved
-ALWAYS_CALCULATE_PROGRAM_PRICE_AS_ANONYMOUS_USER = LegacyWaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
- PROGRAMS_WAFFLE_SWITCH_NAMESPACE,
- 'always_calculate_program_price_as_anonymous_user',
+ALWAYS_CALCULATE_PROGRAM_PRICE_AS_ANONYMOUS_USER = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{PROGRAMS_WAFFLE_SWITCH_NAMESPACE}.always_calculate_program_price_as_anonymous_user',
__name__
)
diff --git a/openedx/core/djangoapps/schedules/config.py b/openedx/core/djangoapps/schedules/config.py
index 6d03f79191..802559ebcf 100644
--- a/openedx/core/djangoapps/schedules/config.py
+++ b/openedx/core/djangoapps/schedules/config.py
@@ -3,13 +3,12 @@ Contains configuration for schedules app
"""
from crum import get_current_request
-from edx_toggles.toggles import LegacyWaffleSwitch, LegacyWaffleFlagNamespace, LegacyWaffleSwitchNamespace, WaffleFlag
+from edx_toggles.toggles import WaffleFlag, WaffleSwitch
from lms.djangoapps.experiments.flags import ExperimentWaffleFlag
from lms.djangoapps.experiments.models import ExperimentData
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='schedules')
-WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name='schedules')
+WAFFLE_NAMESPACE = 'schedules'
# .. toggle_name: schedules.enable_debugging
# .. toggle_implementation: WaffleFlag
@@ -17,12 +16,10 @@ WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name='schedules')
# .. toggle_description: Enable debug level of logging for schedules messages.
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2017-09-17
-DEBUG_MESSAGE_WAFFLE_FLAG = WaffleFlag('schedules.enable_debugging', __name__)
+DEBUG_MESSAGE_WAFFLE_FLAG = WaffleFlag(f'{WAFFLE_NAMESPACE}.enable_debugging', __name__)
-COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH = LegacyWaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
- WAFFLE_SWITCH_NAMESPACE,
- 'course_update_show_unsubscribe',
- __name__
+COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.course_update_show_unsubscribe', __name__
)
# This experiment waffle is supporting an A/B test we are running on sending course updates from an external service,
@@ -31,9 +28,11 @@ COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH = LegacyWaffleSwitch( # lint-amnes
# methods below. We save this flag decision at enrollment time and don't change it even if the flag changes. So you
# can't just directly look at flag result.
_EXTERNAL_COURSE_UPDATES_EXPERIMENT_ID = 18
-_EXTERNAL_COURSE_UPDATES_FLAG = ExperimentWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'external_updates', __name__, # lint-amnesty, pylint: disable=toggle-missing-annotation
- experiment_id=_EXTERNAL_COURSE_UPDATES_EXPERIMENT_ID,
- use_course_aware_bucketing=False)
+_EXTERNAL_COURSE_UPDATES_FLAG = ExperimentWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.external_updates', __name__,
+ experiment_id=_EXTERNAL_COURSE_UPDATES_EXPERIMENT_ID,
+ use_course_aware_bucketing=False
+)
def set_up_external_updates_for_enrollment(user, course_key):
diff --git a/openedx/core/djangoapps/user_api/accounts/toggles.py b/openedx/core/djangoapps/user_api/accounts/toggles.py
index f8221a02ab..d21eeb96aa 100644
--- a/openedx/core/djangoapps/user_api/accounts/toggles.py
+++ b/openedx/core/djangoapps/user_api/accounts/toggles.py
@@ -2,7 +2,8 @@
Toggles for accounts related code.
"""
-from edx_toggles.toggles import LegacyWaffleFlag
+from edx_toggles.toggles import WaffleFlag
+
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
# .. toggle_name: order_history.redirect_to_microfrontend
@@ -15,7 +16,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
# .. toggle_warnings: Also set settings.ORDER_HISTORY_MICROFRONTEND_URL and site's
# ENABLE_ORDER_HISTORY_MICROFRONTEND.
# .. toggle_tickets: DEPR-17
-REDIRECT_TO_ORDER_HISTORY_MICROFRONTEND = LegacyWaffleFlag('order_history', 'redirect_to_microfrontend', __name__)
+REDIRECT_TO_ORDER_HISTORY_MICROFRONTEND = WaffleFlag('order_history.redirect_to_microfrontend', __name__)
def should_redirect_to_order_history_microfrontend():
@@ -35,7 +36,7 @@ def should_redirect_to_order_history_microfrontend():
# .. toggle_target_removal_date: 2021-12-31
# .. toggle_warnings: Also set settings.ACCOUNT_MICROFRONTEND_URL.
# .. toggle_tickets: DEPR-17
-REDIRECT_TO_ACCOUNT_MICROFRONTEND = LegacyWaffleFlag('account', 'redirect_to_microfrontend', __name__)
+REDIRECT_TO_ACCOUNT_MICROFRONTEND = WaffleFlag('account.redirect_to_microfrontend', __name__)
def should_redirect_to_account_microfrontend():
diff --git a/openedx/core/djangoapps/user_authn/config/waffle.py b/openedx/core/djangoapps/user_authn/config/waffle.py
index dc409d4eaa..1183d94319 100644
--- a/openedx/core/djangoapps/user_authn/config/waffle.py
+++ b/openedx/core/djangoapps/user_authn/config/waffle.py
@@ -3,10 +3,9 @@ Waffle flags and switches for user authn.
"""
-from edx_toggles.toggles import LegacyWaffleSwitch, LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleSwitch
_WAFFLE_NAMESPACE = 'user_authn'
-_WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name=_WAFFLE_NAMESPACE, log_prefix='UserAuthN: ')
# .. toggle_name: user_authn.enable_login_using_thirdparty_auth_only
# .. toggle_implementation: WaffleSwitch
@@ -18,10 +17,8 @@ _WAFFLE_SWITCH_NAMESPACE = LegacyWaffleSwitchNamespace(name=_WAFFLE_NAMESPACE, l
# .. toggle_target_removal_date: 2020-01-31
# .. toggle_warnings: Requires THIRD_PARTY_AUTH_ONLY_DOMAIN to also be set.
# .. toggle_tickets: ENT-2461
-ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY = LegacyWaffleSwitch(
- _WAFFLE_SWITCH_NAMESPACE,
- 'enable_login_using_thirdparty_auth_only',
- __name__
+ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY = WaffleSwitch(
+ f'{_WAFFLE_NAMESPACE}.enable_login_using_thirdparty_auth_only', __name__
)
# .. toggle_name: user_authn.enable_pwned_password_api
@@ -32,8 +29,6 @@ ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY = LegacyWaffleSwitch(
# .. toggle_creation_date: 2021-09-22
# .. toggle_target_removal_date: 2021-12-31
# .. toggle_tickets: VAN-664
-ENABLE_PWNED_PASSWORD_API = LegacyWaffleSwitch(
- _WAFFLE_SWITCH_NAMESPACE,
- 'enable_pwned_password_api',
- __name__
+ENABLE_PWNED_PASSWORD_API = WaffleSwitch(
+ f'{_WAFFLE_NAMESPACE}.enable_pwned_password_api', __name__
)
diff --git a/openedx/core/djangoapps/user_authn/views/register.py b/openedx/core/djangoapps/user_authn/views/register.py
index 7ba3a8920e..6310ce2c02 100644
--- a/openedx/core/djangoapps/user_authn/views/register.py
+++ b/openedx/core/djangoapps/user_authn/views/register.py
@@ -22,7 +22,7 @@ from django.utils.translation import gettext as _
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
from django.views.decorators.debug import sensitive_post_parameters
from edx_django_utils.monitoring import set_custom_attribute
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
from openedx_events.learning.data import UserData, UserPersonalData
from openedx_events.learning.signals import STUDENT_REGISTRATION_COMPLETED
from openedx_filters.learning.filters import StudentRegistrationRequested
@@ -115,11 +115,7 @@ REGISTER_USER = Signal()
# .. toggle_creation_date: 2020-04-30
# .. toggle_target_removal_date: 2020-06-01
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
-REGISTRATION_FAILURE_LOGGING_FLAG = LegacyWaffleFlag(
- waffle_namespace=LegacyWaffleFlagNamespace(name='registration'),
- flag_name='enable_failure_logging',
- module_name=__name__,
-)
+REGISTRATION_FAILURE_LOGGING_FLAG = WaffleFlag('registration.enable_failure_logging', __name__)
REAL_IP_KEY = 'openedx.core.djangoapps.util.ratelimit.real_ip'
diff --git a/openedx/core/djangoapps/video_pipeline/config/waffle.py b/openedx/core/djangoapps/video_pipeline/config/waffle.py
index 34b734cf1d..58a2ddb24c 100644
--- a/openedx/core/djangoapps/video_pipeline/config/waffle.py
+++ b/openedx/core/djangoapps/video_pipeline/config/waffle.py
@@ -3,11 +3,13 @@ This module contains configuration settings via waffle flags
for the Video Pipeline app.
"""
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from edx_toggles.toggles import WaffleFlag
+
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
# Videos Namespace
WAFFLE_NAMESPACE = 'videos'
+LOG_PREFIX = 'Videos: '
# .. toggle_name: videos.deprecate_youtube
# .. toggle_implementation: CourseWaffleFlag
@@ -17,8 +19,8 @@ WAFFLE_NAMESPACE = 'videos'
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2018-08-03
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/18765
-# TODO: Replace with CourseWaffleFlag() from waffle_flags().
-DEPRECATE_YOUTUBE = 'deprecate_youtube'
+DEPRECATE_YOUTUBE = CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.deprecate_youtube', __name__, LOG_PREFIX)
+
# .. toggle_name: videos.enable_devstack_video_uploads
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
@@ -30,34 +32,8 @@ DEPRECATE_YOUTUBE = 'deprecate_youtube'
# .. toggle_warnings: Enabling this feature requires that the ROLE_ARN, MFA_SERIAL_NUMBER, MFA_TOKEN settings are
# properly defined.
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/23375
-# TODO: Replace with WaffleFlag() from waffle_flags().
-ENABLE_DEVSTACK_VIDEO_UPLOADS = 'enable_devstack_video_uploads'
-# TODO: Replace with CourseWaffleFlag() from waffle_flags().
-ENABLE_VEM_PIPELINE = 'enable_vem_pipeline'
+ENABLE_DEVSTACK_VIDEO_UPLOADS = WaffleFlag(f'{WAFFLE_NAMESPACE}.enable_devstack_video_uploads', __name__, LOG_PREFIX)
-
-def waffle_flags():
- """
- Returns the namespaced, cached, audited Waffle flags dictionary for Videos.
-
- IMPORTANT: Do NOT copy this dict pattern and do NOT add new flags to this dict.
- Instead, replace the string constants above with the actual flag instances.
- """
- namespace = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix='Videos: ')
- return {
- DEPRECATE_YOUTUBE: CourseWaffleFlag(
- waffle_namespace=namespace,
- flag_name=DEPRECATE_YOUTUBE,
- module_name=__name__,
- ),
- ENABLE_DEVSTACK_VIDEO_UPLOADS: LegacyWaffleFlag(
- waffle_namespace=namespace,
- flag_name=ENABLE_DEVSTACK_VIDEO_UPLOADS,
- module_name=__name__,
- ),
- ENABLE_VEM_PIPELINE: CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- waffle_namespace=namespace,
- flag_name=ENABLE_VEM_PIPELINE,
- module_name=__name__,
- )
- }
+ENABLE_VEM_PIPELINE = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
+ f'{WAFFLE_NAMESPACE}.enable_vem_pipeline', __name__, LOG_PREFIX
+)
diff --git a/openedx/core/djangoapps/waffle_utils/__future__.py b/openedx/core/djangoapps/waffle_utils/__future__.py
new file mode 100644
index 0000000000..5d44636b26
--- /dev/null
+++ b/openedx/core/djangoapps/waffle_utils/__future__.py
@@ -0,0 +1,116 @@
+"""
+Temporary module to switch from the LegacyWaffle* classes.
+"""
+import logging
+
+from edx_toggles.toggles import WaffleFlag
+from opaque_keys.edx.keys import CourseKey
+
+log = logging.getLogger(__name__)
+
+
+class FutureCourseWaffleFlag(WaffleFlag):
+ """
+ Represents a single waffle flag that can be forced on/off for a course. This class should be used instead of
+ WaffleFlag when in the context of a course. This class will also respect any org-level overrides, though
+ course-level overrides will take precedence.
+
+ Uses a cached waffle namespace.
+
+ Usage:
+
+ SOME_COURSE_FLAG = CourseWaffleFlag('my_namespace.some_course_feature', __name__, log_prefix='')
+
+ And then we can check this flag in code with::
+
+ SOME_COURSE_FLAG.is_enabled(course_key)
+
+ To configure a course-level override, go to Django Admin "waffle_utils" -> "Waffle flag course overrides".
+
+ Waffle flag: Set this to the flag name (e.g. my_namespace.some_course_feature).
+ Course id: Set this to the course id (e.g. course-v1:edx+100+Demo)
+ Override choice: (Force on/Force off). "Force on" will enable the waffle flag for all users in a course,
+ overriding any behavior configured on the waffle flag itself. "Force off" will disable the waffle flag
+ for all users in a course, overriding any behavior configured on the waffle flag itself. Requires
+ "Enabled" (see below) to apply.
+ Enabled: Must be marked as "enabled" in order for the override to be applied. These settings can't be
+ deleted, so instead, you need to add another disabled override entry to disable the override.
+
+ To configure an org-level override, go to Django Admin "waffle_utils" -> "Waffle flag org overrides".
+
+ Waffle flag: Set this to the flag name (e.g. my_namespace.some_course_feature).
+ Org name: Set this to the organization name (e.g. edx)
+ Override choice: (Force on/Force off). "Force on" will enable the waffle flag for all users in an org's courses,
+ overriding any behavior configured on the waffle flag itself. "Force off" will disable the waffle flag
+ for all users in a org's courses, overriding any behavior configured on the waffle flag itself. Requires
+ "Enabled" (see below) to apply.
+ Enabled: Must be marked as "enabled" in order for the override to be applied. These settings can't be
+ deleted, so instead, you need to add another disabled override entry to disable the override.
+
+ """
+
+ def _get_course_override_value(self, course_key):
+ """
+ Returns True/False if the flag was forced on or off for the provided course. Returns None if the flag was not
+ overridden.
+
+ Note: Has side effect of caching the override value.
+
+ Arguments:
+ course_key (CourseKey): The course to check for override before checking waffle.
+ """
+ # Import is placed here to avoid model import at project startup.
+ from .models import WaffleFlagCourseOverrideModel, WaffleFlagOrgOverrideModel
+
+ course_cache_key = f"{self.name}.cwaffle.{str(course_key)}"
+ course_override = self.cached_flags().get(course_cache_key)
+
+ if course_override is None:
+ course_override = WaffleFlagCourseOverrideModel.override_value(
+ self.name, course_key
+ )
+ self.cached_flags()[course_cache_key] = course_override
+
+ if course_override == WaffleFlagCourseOverrideModel.ALL_CHOICES.on:
+ return True
+ if course_override == WaffleFlagCourseOverrideModel.ALL_CHOICES.off:
+ return False
+
+ # Since no course-specific override was found, fall back to checking at the org-level.
+ if course_key:
+ org = course_key.org
+ org_cache_key = f"{self.name}.owaffle.{org}"
+ org_override = self.cached_flags().get(org_cache_key)
+
+ if org_override is None:
+ org_override = WaffleFlagOrgOverrideModel.override_value(
+ self.name, org
+ )
+ self.cached_flags()[org_cache_key] = org_override
+
+ if org_override == WaffleFlagOrgOverrideModel.ALL_CHOICES.on:
+ return True
+ if org_override == WaffleFlagOrgOverrideModel.ALL_CHOICES.off:
+ return False
+
+ return None
+
+ def is_enabled(self, course_key=None): # pylint: disable=arguments-differ
+ """
+ Returns whether or not the flag is enabled within the context of a given course.
+
+ Arguments:
+ course_key (Optional[CourseKey]): The course to check for override before
+ checking waffle. If omitted, check whether the flag is enabled
+ outside the context of any course.
+ """
+ if course_key:
+ assert isinstance(
+ course_key, CourseKey
+ ), "Provided course_key '{}' is not instance of CourseKey.".format(
+ course_key
+ )
+ is_enabled_for_course = self._get_course_override_value(course_key)
+ if is_enabled_for_course is not None:
+ return is_enabled_for_course
+ return super().is_enabled()
diff --git a/openedx/core/djangoapps/waffle_utils/__init__.py b/openedx/core/djangoapps/waffle_utils/__init__.py
index f0df220933..d964e6d5b3 100644
--- a/openedx/core/djangoapps/waffle_utils/__init__.py
+++ b/openedx/core/djangoapps/waffle_utils/__init__.py
@@ -3,123 +3,30 @@ Extra utilities for waffle: most classes are defined in edx_toggles.toggles (htt
we keep here some extra classes for usage within edx-platform. These classes cover course override use cases.
"""
import logging
-import warnings
-from contextlib import contextmanager
from edx_django_utils.monitoring import set_custom_attribute
-from edx_toggles.toggles import (
- LegacyWaffleFlag,
- LegacyWaffleFlagNamespace,
- LegacyWaffleSwitch,
- LegacyWaffleSwitchNamespace,
-)
-from opaque_keys.edx.keys import CourseKey
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag
log = logging.getLogger(__name__)
-class CourseWaffleFlag(LegacyWaffleFlag):
+class CourseWaffleFlag(FutureCourseWaffleFlag):
"""
- Represents a single waffle flag that can be forced on/off for a course. This class should be used instead of
- WaffleFlag when in the context of a course. This class will also respect any org-level overrides, though
- course-level overrides will take precedence.
-
- Uses a cached waffle namespace.
-
- Usage:
-
- SOME_COURSE_FLAG = CourseWaffleFlag('my_namespace', 'some_course_feature', __name__)
-
- And then we can check this flag in code with::
-
- SOME_COURSE_FLAG.is_enabled(course_key)
-
- To configure a course-level override, go to Django Admin "waffle_utils" -> "Waffle flag course overrides".
-
- Waffle flag: Set this to the flag name (e.g. my_namespace.some_course_feature).
- Course id: Set this to the course id (e.g. course-v1:edx+100+Demo)
- Override choice: (Force on/Force off). "Force on" will enable the waffle flag for all users in a course,
- overriding any behavior configured on the waffle flag itself. "Force off" will disable the waffle flag
- for all users in a course, overriding any behavior configured on the waffle flag itself. Requires
- "Enabled" (see below) to apply.
- Enabled: Must be marked as "enabled" in order for the override to be applied. These settings can't be
- deleted, so instead, you need to add another disabled override entry to disable the override.
-
- To configure an org-level override, go to Django Admin "waffle_utils" -> "Waffle flag org overrides".
-
- Waffle flag: Set this to the flag name (e.g. my_namespace.some_course_feature).
- Org name: Set this to the organization name (e.g. edx)
- Override choice: (Force on/Force off). "Force on" will enable the waffle flag for all users in an org's courses,
- overriding any behavior configured on the waffle flag itself. "Force off" will disable the waffle flag
- for all users in a org's courses, overriding any behavior configured on the waffle flag itself. Requires
- "Enabled" (see below) to apply.
- Enabled: Must be marked as "enabled" in order for the override to be applied. These settings can't be
- deleted, so instead, you need to add another disabled override entry to disable the override.
+ Represents a single waffle flag that can be forced on/off for a course.
+ Deprecated: use the FutureCourseWaffleFlag instead.
"""
+ def __init__(self, waffle_namespace, flag_name, module_name=None):
+ log_prefix = ""
+ if not isinstance(waffle_namespace, str):
+ log_prefix = waffle_namespace.log_prefix or log_prefix
+ waffle_namespace = waffle_namespace.name
- def _get_course_override_value(self, course_key):
- """
- Returns True/False if the flag was forced on or off for the provided course. Returns None if the flag was not
- overridden.
-
- Note: Has side effect of caching the override value.
-
- Arguments:
- course_key (CourseKey): The course to check for override before checking waffle.
- """
- # Import is placed here to avoid model import at project startup.
- from .models import WaffleFlagCourseOverrideModel, WaffleFlagOrgOverrideModel
-
- course_cache_key = f"{self.name}.cwaffle.{str(course_key)}"
- course_override = self.cached_flags().get(course_cache_key)
-
- if course_override is None:
- course_override = WaffleFlagCourseOverrideModel.override_value(
- self.name, course_key
- )
- self.cached_flags()[course_cache_key] = course_override
-
- if course_override == WaffleFlagCourseOverrideModel.ALL_CHOICES.on:
- return True
- if course_override == WaffleFlagCourseOverrideModel.ALL_CHOICES.off:
- return False
-
- # Since no course-specific override was found, fall back to checking at the org-level.
- if course_key:
- org = course_key.org
- org_cache_key = f"{self.name}.owaffle.{org}"
- org_override = self.cached_flags().get(org_cache_key)
-
- if org_override is None:
- org_override = WaffleFlagOrgOverrideModel.override_value(
- self.name, org
- )
- self.cached_flags()[org_cache_key] = org_override
-
- if org_override == WaffleFlagOrgOverrideModel.ALL_CHOICES.on:
- return True
- if org_override == WaffleFlagOrgOverrideModel.ALL_CHOICES.off:
- return False
-
- return None
-
- def is_enabled(self, course_key=None): # pylint: disable=arguments-differ
- """
- Returns whether or not the flag is enabled within the context of a given course.
-
- Arguments:
- course_key (Optional[CourseKey]): The course to check for override before
- checking waffle. If omitted, check whether the flag is enabled
- outside the context of any course.
- """
- if course_key:
- assert isinstance(
- course_key, CourseKey
- ), "Provided course_key '{}' is not instance of CourseKey.".format(
- course_key
- )
- is_enabled_for_course = self._get_course_override_value(course_key)
- if is_enabled_for_course is not None:
- return is_enabled_for_course
- return super().is_enabled()
+ # Non-namespaced flag_name attribute preserved for backward compatibility
+ self._flag_name = flag_name
+ name = f"{waffle_namespace}.{flag_name}"
+ super().__init__(name, module_name=module_name, log_prefix=log_prefix)
+ set_custom_attribute(
+ "deprecated_legacy_waffle_class",
+ f"{self.__class__.__module__}.{self.__class__.__name__}[{self.name}]"
+ )
diff --git a/openedx/core/djangoapps/waffle_utils/tests/test_init.py b/openedx/core/djangoapps/waffle_utils/tests/test_init.py
index 546ec2b7b5..8db41727d4 100644
--- a/openedx/core/djangoapps/waffle_utils/tests/test_init.py
+++ b/openedx/core/djangoapps/waffle_utils/tests/test_init.py
@@ -4,6 +4,7 @@ Tests for waffle utils features.
# pylint: disable=toggle-missing-annotation
from unittest.mock import patch
+
import crum
import ddt
from django.test.client import RequestFactory
@@ -11,11 +12,11 @@ from edx_django_utils.cache import RequestCache
from opaque_keys.edx.keys import CourseKey
from waffle.testutils import override_flag
+from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag as LegacyCourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel, WaffleFlagOrgOverrideModel
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
-from .. import CourseWaffleFlag
-from ..models import WaffleFlagCourseOverrideModel, WaffleFlagOrgOverrideModel
-
@ddt.ddt
class TestCourseWaffleFlag(CacheIsolationTestCase):
@@ -33,7 +34,7 @@ class TestCourseWaffleFlag(CacheIsolationTestCase):
TEST_COURSE_KEY = CourseKey.from_string(f"{TEST_ORG}/DemoX/Demo_Course")
TEST_COURSE_2_KEY = CourseKey.from_string(f"{TEST_ORG}/DemoX/Demo_Course_2")
TEST_COURSE_3_KEY = CourseKey.from_string("CollegeX/DemoX/Demo_Course")
- TEST_COURSE_FLAG = CourseWaffleFlag(NAMESPACE_NAME, FLAG_NAME, __name__)
+ TEST_COURSE_FLAG = CourseWaffleFlag(NAMESPACED_FLAG_NAME, __name__)
def setUp(self):
super().setUp()
@@ -75,6 +76,44 @@ class TestCourseWaffleFlag(CacheIsolationTestCase):
# course which should get the default value of False.
assert self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_2_KEY) is False
+ @ddt.data(
+ (False, WaffleFlagCourseOverrideModel.ALL_CHOICES.on, True),
+ (True, WaffleFlagCourseOverrideModel.ALL_CHOICES.off, False),
+ (True, WaffleFlagCourseOverrideModel.ALL_CHOICES.unset, True),
+ (False, WaffleFlagCourseOverrideModel.ALL_CHOICES.unset, False),
+ )
+ @ddt.unpack
+ def test_legacy_course_waffle_flag(self, waffle_enabled, course_override, result):
+ """
+ Tests various combinations of a legacy flag being set in waffle and overridden for a course.
+ """
+ test_legacy_course_flag = LegacyCourseWaffleFlag(
+ self.NAMESPACE_NAME,
+ self.FLAG_NAME,
+ __name__,
+ )
+ with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=course_override):
+ with override_flag(self.NAMESPACED_FLAG_NAME, active=waffle_enabled):
+ # check twice to test that the result is properly cached
+ assert test_legacy_course_flag.is_enabled(self.TEST_COURSE_KEY) == result
+ assert test_legacy_course_flag.is_enabled(self.TEST_COURSE_KEY) == result
+ # result is cached, so override check should happen only once
+ # pylint: disable=no-member
+ WaffleFlagCourseOverrideModel.override_value.assert_called_once_with(
+ self.NAMESPACED_FLAG_NAME,
+ self.TEST_COURSE_KEY
+ )
+
+ # check flag for a second course
+ if course_override == WaffleFlagCourseOverrideModel.ALL_CHOICES.unset:
+ # When course override wasn't set for the first course, the second course will get the same
+ # cached value from waffle.
+ assert test_legacy_course_flag.is_enabled(self.TEST_COURSE_2_KEY) == waffle_enabled
+ else:
+ # When course override was set for the first course, it should not apply to the second
+ # course which should get the default value of False.
+ assert test_legacy_course_flag.is_enabled(self.TEST_COURSE_2_KEY) is False
+
@ddt.data(
(False, WaffleFlagOrgOverrideModel.ALL_CHOICES.unset, False),
(True, WaffleFlagOrgOverrideModel.ALL_CHOICES.unset, True),
@@ -196,11 +235,7 @@ class TestCourseWaffleFlag(CacheIsolationTestCase):
"""
Test flag with undefined waffle flag.
"""
- test_course_flag = CourseWaffleFlag(
- self.NAMESPACE_NAME,
- self.FLAG_NAME,
- __name__,
- )
+ test_course_flag = CourseWaffleFlag(self.NAMESPACED_FLAG_NAME, __name__)
with patch.object(
WaffleFlagCourseOverrideModel,
@@ -222,11 +257,7 @@ class TestCourseWaffleFlag(CacheIsolationTestCase):
Test the flag behavior when outside a request context and waffle data undefined.
"""
crum.set_current_request(None)
- test_course_flag = CourseWaffleFlag(
- self.NAMESPACE_NAME,
- self.FLAG_NAME,
- __name__,
- )
+ test_course_flag = CourseWaffleFlag(self.NAMESPACED_FLAG_NAME, __name__)
assert test_course_flag.is_enabled(self.TEST_COURSE_KEY) is False
def test_without_request_and_everyone_active_waffle(self):
@@ -234,10 +265,7 @@ class TestCourseWaffleFlag(CacheIsolationTestCase):
Test the flag behavior when outside a request context and waffle active for everyone.
"""
crum.set_current_request(None)
- test_course_flag = CourseWaffleFlag(
- self.NAMESPACE_NAME,
- self.FLAG_NAME,
- __name__,
- )
+
+ test_course_flag = CourseWaffleFlag(self.NAMESPACED_FLAG_NAME, __name__)
with override_flag(self.NAMESPACED_FLAG_NAME, active=True):
assert test_course_flag.is_enabled(self.TEST_COURSE_KEY) is True
diff --git a/openedx/features/course_experience/__init__.py b/openedx/features/course_experience/__init__.py
index 0c9eca091d..83bb4d06f1 100644
--- a/openedx/features/course_experience/__init__.py
+++ b/openedx/features/course_experience/__init__.py
@@ -4,28 +4,28 @@ Unified course experience settings and helper methods.
from django.urls import reverse
from django.utils.translation import gettext as _
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from edx_toggles.toggles import WaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
# Namespace for course experience waffle flags.
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='course_experience')
+WAFFLE_FLAG_NAMESPACE = 'course_experience'
# Waffle flag to disable the separate course outline page and full width content.
DISABLE_COURSE_OUTLINE_PAGE_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- WAFFLE_FLAG_NAMESPACE, 'disable_course_outline_page', __name__
+ f'{WAFFLE_FLAG_NAMESPACE}.disable_course_outline_page', __name__
)
# Waffle flag to enable a single unified "Course" tab.
DISABLE_UNIFIED_COURSE_TAB_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- WAFFLE_FLAG_NAMESPACE, 'disable_unified_course_tab', __name__
+ f'{WAFFLE_FLAG_NAMESPACE}.disable_unified_course_tab', __name__
)
# Waffle flag to enable the sock on the footer of the home and courseware pages.
-DISPLAY_COURSE_SOCK_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'display_course_sock', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+DISPLAY_COURSE_SOCK_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.display_course_sock', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
# Waffle flag to let learners access a course before its start date.
-COURSE_PRE_START_ACCESS_FLAG = LegacyWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'pre_start_access', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+COURSE_PRE_START_ACCESS_FLAG = WaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.pre_start_access', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
# .. toggle_name: course_experience.enable_course_goals
# .. toggle_implementation: CourseWaffleFlag
@@ -35,14 +35,12 @@ COURSE_PRE_START_ACCESS_FLAG = LegacyWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'pre_star
# .. toggle_creation_date: 2017-09-11
# .. toggle_target_removal_date: None
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
-ENABLE_COURSE_GOALS = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_course_goals', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ENABLE_COURSE_GOALS = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_course_goals', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
# Waffle flag to enable anonymous access to a course
-SEO_WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='seo')
+SEO_WAFFLE_FLAG_NAMESPACE = 'seo'
COURSE_ENABLE_UNENROLLED_ACCESS_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
- SEO_WAFFLE_FLAG_NAMESPACE,
- 'enable_anonymous_courseware_access',
- __name__,
+ f'{SEO_WAFFLE_FLAG_NAMESPACE}.enable_anonymous_courseware_access', __name__
)
# .. toggle_name: course_experience.relative_dates
@@ -55,7 +53,7 @@ COURSE_ENABLE_UNENROLLED_ACCESS_FLAG = CourseWaffleFlag( # lint-amnesty, pylint
# .. toggle_warnings: To set a relative due date for self-paced courses, the weeks_to_complete field for a course run
# needs to be set. Currently it can be set through the publisher app.
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-27
-RELATIVE_DATES_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'relative_dates', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+RELATIVE_DATES_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.relative_dates', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
# .. toggle_name: course_experience.calendar_sync
# .. toggle_implementation: CourseWaffleFlag
@@ -69,7 +67,7 @@ RELATIVE_DATES_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'relative_dates',
# .. toggle_creation_date: 2021-01-26
# .. toggle_target_removal_date: 2021-04-26
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-36
-CALENDAR_SYNC_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'calendar_sync', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+CALENDAR_SYNC_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.calendar_sync', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
def course_home_page_title(_course):
diff --git a/openedx/features/course_experience/waffle.py b/openedx/features/course_experience/waffle.py
index 8fc7e14a63..26423f882a 100644
--- a/openedx/features/course_experience/waffle.py
+++ b/openedx/features/course_experience/waffle.py
@@ -3,7 +3,7 @@ Miscellaneous waffle switches that both LMS and Studio need to access
"""
-from edx_toggles.toggles import LegacyWaffleSwitchNamespace
+from edx_toggles.toggles import WaffleSwitch
# Namespace
WAFFLE_NAMESPACE = 'course_experience'
@@ -15,18 +15,4 @@ WAFFLE_NAMESPACE = 'course_experience'
# .. toggle_description: Used to determine whether to show custom HTML in the sidebar on the internal course about page.
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2018-01-26
-# TODO: Replace with WaffleSwitch(). See waffle() docstring.
-ENABLE_COURSE_ABOUT_SIDEBAR_HTML = 'enable_about_sidebar_html'
-
-
-def waffle():
- """
- Deprecated: Returns the namespaced, cached, audited shared Waffle Switch class.
-
- IMPORTANT: Do NOT copy this pattern and do NOT use this to reference new switches.
- Instead, replace the string constant above with the actual switch instance.
- For example::
-
- ENABLE_COURSE_ABOUT_SIDEBAR_HTML = WaffleSwitch(f'{WAFFLE_NAMESPACE}.enable_about_sidebar_html')
- """
- return LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix='Course Experience: ')
+ENABLE_COURSE_ABOUT_SIDEBAR_HTML = WaffleSwitch(f'{WAFFLE_NAMESPACE}.enable_about_sidebar_html', __name__)
diff --git a/openedx/features/discounts/applicability.py b/openedx/features/discounts/applicability.py
index 2dc1469bd8..f4fd4733cb 100644
--- a/openedx/features/discounts/applicability.py
+++ b/openedx/features/discounts/applicability.py
@@ -15,7 +15,7 @@ import pytz
from crum import get_current_request, impersonate
from django.utils import timezone
from django.utils.dateparse import parse_datetime
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.entitlements.models import CourseEntitlement
@@ -37,11 +37,7 @@ from common.djangoapps.track import segment
# .. toggle_target_removal_date: None
# .. toggle_tickets: REVEM-282
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
-DISCOUNT_APPLICABILITY_FLAG = LegacyWaffleFlag(
- waffle_namespace=LegacyWaffleFlagNamespace(name='discounts'),
- flag_name='enable_discounting',
- module_name=__name__,
-)
+DISCOUNT_APPLICABILITY_FLAG = WaffleFlag('discounts.enable_discounting', __name__)
DISCOUNT_APPLICABILITY_HOLDBACK = 'first_purchase_discount_holdback'
REV1008_EXPERIMENT_ID = 16
diff --git a/openedx/features/effort_estimation/toggles.py b/openedx/features/effort_estimation/toggles.py
index be1d67acd5..d9d5e72d10 100644
--- a/openedx/features/effort_estimation/toggles.py
+++ b/openedx/features/effort_estimation/toggles.py
@@ -2,12 +2,10 @@
Feature toggles used for effort estimation.
"""
-from edx_toggles.toggles import LegacyWaffleFlagNamespace
-
-from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
+from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='effort_estimation')
+WAFFLE_FLAG_NAMESPACE = 'effort_estimation'
# .. toggle_name: effort_estimation.disabled
# .. toggle_implementation: CourseWaffleFlag
@@ -16,4 +14,4 @@ WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='effort_estimation')
# estimates), you can turn them off case by case here.
# .. toggle_use_cases: opt_out
# .. toggle_creation_date: 2021-07-27
-EFFORT_ESTIMATION_DISABLED_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'disabled', __name__)
+EFFORT_ESTIMATION_DISABLED_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.disabled', __name__)
diff --git a/openedx/features/enterprise_support/utils.py b/openedx/features/enterprise_support/utils.py
index 018ee7967f..4604b5c4fb 100644
--- a/openedx/features/enterprise_support/utils.py
+++ b/openedx/features/enterprise_support/utils.py
@@ -13,7 +13,7 @@ from django.core.cache import cache
from django.urls import NoReverseMatch, reverse
from django.utils.translation import gettext as _
from edx_django_utils.cache import TieredCache, get_cache_key
-from edx_toggles.toggles import LegacyWaffleFlag
+from edx_toggles.toggles import WaffleFlag
from enterprise.api.v1.serializers import EnterpriseCustomerBrandingConfigurationSerializer
from enterprise.models import EnterpriseCustomer, EnterpriseCustomerUser
from social_django.models import UserSocialAuth
@@ -25,7 +25,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from openedx.core.djangoapps.user_authn.cookies import standard_cookie_settings
from openedx.core.djangolib.markup import HTML, Text
-ENTERPRISE_HEADER_LINKS = LegacyWaffleFlag('enterprise', 'enterprise_header_links', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+ENTERPRISE_HEADER_LINKS = WaffleFlag('enterprise.enterprise_header_links', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
def get_data_consent_share_cache_key(user_id, course_id, enterprise_customer_uuid=None):
diff --git a/openedx/features/learner_profile/toggles.py b/openedx/features/learner_profile/toggles.py
index 23d4afe128..6b29f922ac 100644
--- a/openedx/features/learner_profile/toggles.py
+++ b/openedx/features/learner_profile/toggles.py
@@ -3,11 +3,11 @@ Toggles for Learner Profile page.
"""
-from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
+from edx_toggles.toggles import WaffleFlag
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
# Namespace for learner profile waffle flags.
-WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='learner_profile')
+WAFFLE_FLAG_NAMESPACE = 'learner_profile'
# Waffle flag to redirect to another learner profile experience.
# .. toggle_name: learner_profile.redirect_to_microfrontend
@@ -19,7 +19,7 @@ WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='learner_profile')
# .. toggle_target_removal_date: 2020-12-31
# .. toggle_warnings: Also set settings.PROFILE_MICROFRONTEND_URL and site's ENABLE_PROFILE_MICROFRONTEND.
# .. toggle_tickets: DEPR-17
-REDIRECT_TO_PROFILE_MICROFRONTEND = LegacyWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'redirect_to_microfrontend', __name__)
+REDIRECT_TO_PROFILE_MICROFRONTEND = WaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.redirect_to_microfrontend', __name__)
def should_redirect_to_profile_microfrontend():