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.
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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}]"
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user