From 655e4a344fa56b26a7b54e54af6b0ef20a5519cf Mon Sep 17 00:00:00 2001 From: Eugene Dyudyunov Date: Tue, 10 May 2022 22:08:59 +0300 Subject: [PATCH] refactor!: update CourseWaffleFlag (#30351) BREAKING: get rid of the LegacyWaffle-based CourseWaffleFlag. Both CourseWaffleFlag and FutureCourseWaffleFlag now use the modern WaffleFlag as parent class. FutureCourseWaffleFlag left to support ORA transition to modern waffle. Switch to the ORA version which supporting new Waffles. --- cms/djangoapps/contentstore/config/waffle.py | 2 +- cms/djangoapps/contentstore/views/videos.py | 2 +- cms/djangoapps/models/settings/waffle.py | 2 +- lms/djangoapps/course_home_api/toggles.py | 2 +- lms/djangoapps/courseware/toggles.py | 2 +- lms/djangoapps/discussion/toggles.py | 2 +- lms/djangoapps/experiments/flags.py | 2 +- .../experiments/tests/test_flags.py | 2 +- lms/djangoapps/grades/config/waffle.py | 2 +- .../instructor_task/config/waffle.py | 2 +- lms/djangoapps/ora_staff_grader/views.py | 2 +- lms/djangoapps/teams/toggles.py | 2 +- .../core/djangoapps/course_apps/toggles.py | 2 +- .../djangoapps/course_live/config/waffle.py | 2 +- .../djangoapps/discussions/config/waffle.py | 2 +- .../video_pipeline/config/waffle.py | 2 +- .../djangoapps/waffle_utils/__future__.py | 118 ++--------------- .../core/djangoapps/waffle_utils/__init__.py | 122 +++++++++++++++--- .../waffle_utils/tests/test_init.py | 22 ++-- .../features/course_experience/__init__.py | 2 +- openedx/features/effort_estimation/toggles.py | 2 +- requirements/edx/base.in | 2 +- 22 files changed, 143 insertions(+), 157 deletions(-) diff --git a/cms/djangoapps/contentstore/config/waffle.py b/cms/djangoapps/contentstore/config/waffle.py index 9f086943f6..a7e303d64b 100644 --- a/cms/djangoapps/contentstore/config/waffle.py +++ b/cms/djangoapps/contentstore/config/waffle.py @@ -6,7 +6,7 @@ waffle switches for the contentstore app. from edx_toggles.toggles import WaffleFlag, WaffleSwitch -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # Namespace WAFFLE_NAMESPACE = 'studio' diff --git a/cms/djangoapps/contentstore/views/videos.py b/cms/djangoapps/contentstore/views/videos.py index c39add5894..bc0cfdf304 100644 --- a/cms/djangoapps/contentstore/views/videos.py +++ b/cms/djangoapps/contentstore/views/videos.py @@ -50,7 +50,7 @@ from openedx.core.djangoapps.video_pipeline.config.waffle import ( DEPRECATE_YOUTUBE, ENABLE_DEVSTACK_VIDEO_UPLOADS, ) -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import 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 diff --git a/cms/djangoapps/models/settings/waffle.py b/cms/djangoapps/models/settings/waffle.py index 1df0c471b0..0ca488a0b7 100644 --- a/cms/djangoapps/models/settings/waffle.py +++ b/cms/djangoapps/models/settings/waffle.py @@ -1,7 +1,7 @@ """ Togglable settings for Course Grading behavior """ -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag WAFFLE_NAMESPACE = 'grades' diff --git a/lms/djangoapps/course_home_api/toggles.py b/lms/djangoapps/course_home_api/toggles.py index afeba72954..4cd1afe9c7 100644 --- a/lms/djangoapps/course_home_api/toggles.py +++ b/lms/djangoapps/course_home_api/toggles.py @@ -2,7 +2,7 @@ Toggles for course home experience. """ -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag WAFFLE_FLAG_NAMESPACE = 'course_home' diff --git a/lms/djangoapps/courseware/toggles.py b/lms/djangoapps/courseware/toggles.py index 4db48fa9cc..732132866d 100644 --- a/lms/djangoapps/courseware/toggles.py +++ b/lms/djangoapps/courseware/toggles.py @@ -4,7 +4,7 @@ Toggles for courseware in-course experience. from edx_toggles.toggles import SettingToggle, WaffleSwitch -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # Namespace for courseware waffle flags. WAFFLE_FLAG_NAMESPACE = 'courseware' diff --git a/lms/djangoapps/discussion/toggles.py b/lms/djangoapps/discussion/toggles.py index aaf1934fdd..0ab0638abe 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.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag WAFFLE_FLAG_NAMESPACE = "discussions" diff --git a/lms/djangoapps/experiments/flags.py b/lms/djangoapps/experiments/flags.py index 65d17289ea..b83a7751fa 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.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag log = logging.getLogger(__name__) diff --git a/lms/djangoapps/experiments/tests/test_flags.py b/lms/djangoapps/experiments/tests/test_flags.py index f1537b2ec1..006efa1777 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.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import 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 diff --git a/lms/djangoapps/grades/config/waffle.py b/lms/djangoapps/grades/config/waffle.py index ddd3432432..4cc46e9dea 100644 --- a/lms/djangoapps/grades/config/waffle.py +++ b/lms/djangoapps/grades/config/waffle.py @@ -6,7 +6,7 @@ waffle switches for the Grades app. 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 # Namespace WAFFLE_NAMESPACE = 'grades' diff --git a/lms/djangoapps/instructor_task/config/waffle.py b/lms/djangoapps/instructor_task/config/waffle.py index ee2319293b..b3938096eb 100644 --- a/lms/djangoapps/instructor_task/config/waffle.py +++ b/lms/djangoapps/instructor_task/config/waffle.py @@ -5,7 +5,7 @@ waffle switches for the instructor_task app. 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' diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py index 7dd6f72900..c68bb65a30 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.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser log = logging.getLogger(__name__) diff --git a/lms/djangoapps/teams/toggles.py b/lms/djangoapps/teams/toggles.py index 9f3a30f085..54d492b210 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.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # Course Waffle inherited from edx/edx-ora2 WAFFLE_NAMESPACE = "openresponseassessment" diff --git a/openedx/core/djangoapps/course_apps/toggles.py b/openedx/core/djangoapps/course_apps/toggles.py index 34f40b4d06..b85d2e55a3 100644 --- a/openedx/core/djangoapps/course_apps/toggles.py +++ b/openedx/core/djangoapps/course_apps/toggles.py @@ -1,7 +1,7 @@ """ Toggles for course apps. """ -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag #: Namespace for use by course apps for creating availability toggles COURSE_APPS_WAFFLE_NAMESPACE = 'course_apps' diff --git a/openedx/core/djangoapps/course_live/config/waffle.py b/openedx/core/djangoapps/course_live/config/waffle.py index 7cdb99f515..098201fb35 100644 --- a/openedx/core/djangoapps/course_live/config/waffle.py +++ b/openedx/core/djangoapps/course_live/config/waffle.py @@ -3,7 +3,7 @@ This module contains various configuration settings via waffle switches for the live app. """ -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag WAFFLE_NAMESPACE = 'course_live' diff --git a/openedx/core/djangoapps/discussions/config/waffle.py b/openedx/core/djangoapps/discussions/config/waffle.py index 47e9c56f41..6d56e48b99 100644 --- a/openedx/core/djangoapps/discussions/config/waffle.py +++ b/openedx/core/djangoapps/discussions/config/waffle.py @@ -3,7 +3,7 @@ This module contains various configuration settings via waffle switches for the discussions app. """ -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag WAFFLE_NAMESPACE = 'discussions' diff --git a/openedx/core/djangoapps/video_pipeline/config/waffle.py b/openedx/core/djangoapps/video_pipeline/config/waffle.py index 58a2ddb24c..5964cdfa5e 100644 --- a/openedx/core/djangoapps/video_pipeline/config/waffle.py +++ b/openedx/core/djangoapps/video_pipeline/config/waffle.py @@ -5,7 +5,7 @@ for the Video Pipeline app. from edx_toggles.toggles import WaffleFlag -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # Videos Namespace WAFFLE_NAMESPACE = 'videos' diff --git a/openedx/core/djangoapps/waffle_utils/__future__.py b/openedx/core/djangoapps/waffle_utils/__future__.py index 5d44636b26..38497ab5b0 100644 --- a/openedx/core/djangoapps/waffle_utils/__future__.py +++ b/openedx/core/djangoapps/waffle_utils/__future__.py @@ -1,116 +1,18 @@ """ Temporary module to switch from the LegacyWaffle* classes. """ -import logging +from edx_django_utils.monitoring import set_custom_attribute -from edx_toggles.toggles import WaffleFlag -from opaque_keys.edx.keys import CourseKey - -log = logging.getLogger(__name__) +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag -class FutureCourseWaffleFlag(WaffleFlag): +class FutureCourseWaffleFlag(CourseWaffleFlag): """ - 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. - + Temporary class to support ORA transition to the modern CourseWaffleFlag. """ - - 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() + def __init__(self, name, module_name, log_prefix=""): + 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/__init__.py b/openedx/core/djangoapps/waffle_utils/__init__.py index d964e6d5b3..c21d052cfc 100644 --- a/openedx/core/djangoapps/waffle_utils/__init__.py +++ b/openedx/core/djangoapps/waffle_utils/__init__.py @@ -4,29 +4,115 @@ we keep here some extra classes for usage within edx-platform. These classes cov """ import logging -from edx_django_utils.monitoring import set_custom_attribute -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag +from edx_toggles.toggles import WaffleFlag +from opaque_keys.edx.keys import CourseKey log = logging.getLogger(__name__) -class CourseWaffleFlag(FutureCourseWaffleFlag): +class CourseWaffleFlag(WaffleFlag): """ 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 + 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. - # 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}]" - ) + 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): + """ + Check whether the course flag was overriden. + + 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/tests/test_init.py b/openedx/core/djangoapps/waffle_utils/tests/test_init.py index 8db41727d4..aa0925d68b 100644 --- a/openedx/core/djangoapps/waffle_utils/tests/test_init.py +++ b/openedx/core/djangoapps/waffle_utils/tests/test_init.py @@ -12,8 +12,8 @@ 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 import CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel, WaffleFlagOrgOverrideModel from openedx.core.djangolib.testing.utils import CacheIsolationTestCase @@ -83,20 +83,18 @@ class TestCourseWaffleFlag(CacheIsolationTestCase): (False, WaffleFlagCourseOverrideModel.ALL_CHOICES.unset, False), ) @ddt.unpack - def test_legacy_course_waffle_flag(self, waffle_enabled, course_override, result): + def test_future_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. + Tests various combinations of a __future__ flag being set in waffle and overridden for a course. """ - test_legacy_course_flag = LegacyCourseWaffleFlag( - self.NAMESPACE_NAME, - self.FLAG_NAME, - __name__, + test_future_course_flag = FutureCourseWaffleFlag( + self.NAMESPACED_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 + assert test_future_course_flag.is_enabled(self.TEST_COURSE_KEY) == result + assert test_future_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( @@ -108,11 +106,11 @@ class TestCourseWaffleFlag(CacheIsolationTestCase): 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 + assert test_future_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 + assert test_future_course_flag.is_enabled(self.TEST_COURSE_2_KEY) is False @ddt.data( (False, WaffleFlagOrgOverrideModel.ALL_CHOICES.unset, False), diff --git a/openedx/features/course_experience/__init__.py b/openedx/features/course_experience/__init__.py index 83bb4d06f1..f6987f9816 100644 --- a/openedx/features/course_experience/__init__.py +++ b/openedx/features/course_experience/__init__.py @@ -5,7 +5,7 @@ Unified course experience settings and helper methods. from django.urls import reverse from django.utils.translation import gettext as _ from edx_toggles.toggles import WaffleFlag -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # Namespace for course experience waffle flags. diff --git a/openedx/features/effort_estimation/toggles.py b/openedx/features/effort_estimation/toggles.py index d9d5e72d10..ad83c1fef2 100644 --- a/openedx/features/effort_estimation/toggles.py +++ b/openedx/features/effort_estimation/toggles.py @@ -2,7 +2,7 @@ Feature toggles used for effort estimation. """ -from openedx.core.djangoapps.waffle_utils.__future__ import FutureCourseWaffleFlag as CourseWaffleFlag +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag WAFFLE_FLAG_NAMESPACE = 'effort_estimation' diff --git a/requirements/edx/base.in b/requirements/edx/base.in index d4b0d6407b..6ed6cf3b95 100644 --- a/requirements/edx/base.in +++ b/requirements/edx/base.in @@ -121,7 +121,7 @@ oauthlib # OAuth specification support for authentica openedx-calc # Library supporting mathematical calculations for Open edX openedx-events # Open edX Events from Hooks Extension Framework (OEP-50) openedx-filters # Open edX Filters from Hooks Extension Framework (OEP-50) -ora2 +ora2>=4.3.0 piexif # Exif image metadata manipulation, used in the profile_images app Pillow # Image manipulation library; used for course assets, profile images, invoice PDFs, etc. py2neo # Driver for converting Python modulestore structures to Neo4j's schema (for Coursegraph).