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).