Merge pull request #25332 from regisb/regisb/use-settingdict

[BD-21] Use edx-toggle's SettingDictToggle to document setting-based feature toggles
This commit is contained in:
Robert Raposa
2020-10-30 08:59:38 -04:00
committed by GitHub
16 changed files with 126 additions and 108 deletions

View File

@@ -58,7 +58,6 @@ from util.course import get_link_for_about_page
from util.date_utils import get_default_time_display
from util.json_request import JsonResponse, JsonResponseBadRequest, expect_json
from util.milestones_helpers import (
is_entrance_exams_enabled,
is_prerequisite_courses_enabled,
is_valid_course_key,
remove_prerequisite_course,
@@ -66,6 +65,7 @@ from util.milestones_helpers import (
)
from util.organizations_helpers import add_organization_course, get_organization_by_short_name, organizations_enabled
from util.string_utils import _has_non_ascii_characters
from openedx.core import toggles as core_toggles
from xblock_django.api import deprecated_xblocks
from xmodule.contentstore.content import StaticContent
from xmodule.course_module import DEFAULT_START_DATE, CourseFields
@@ -1121,7 +1121,7 @@ def settings_handler(request, course_key_string):
'show_min_grade_warning': False,
'enrollment_end_editable': enrollment_end_editable,
'is_prerequisite_courses_enabled': is_prerequisite_courses_enabled(),
'is_entrance_exams_enabled': is_entrance_exams_enabled(),
'is_entrance_exams_enabled': core_toggles.ENTRANCE_EXAMS.is_enabled(),
'enable_extended_course_details': enable_extended_course_details,
'upgrade_deadline': upgrade_deadline,
'course_authoring_microfrontend_url': course_authoring_microfrontend_url,
@@ -1183,7 +1183,7 @@ def settings_handler(request, course_key_string):
# feature-specific settings and handle them accordingly
# We have to be careful that we're only executing the following logic if we actually
# need to create or delete an entrance exam from the specified course
if is_entrance_exams_enabled():
if core_toggles.ENTRANCE_EXAMS.is_enabled():
course_entrance_exam_present = course_module.entrance_exam_enabled
entrance_exam_enabled = request.json.get('entrance_exam_enabled', '') == 'true'
ee_min_score_pct = request.json.get('entrance_exam_minimum_score_pct', None)

View File

@@ -20,6 +20,7 @@ from cms.djangoapps.models.settings.course_metadata import CourseMetadata
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from student.auth import has_course_author_access
from util import milestones_helpers
from openedx.core import toggles as core_toggles
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
@@ -42,23 +43,21 @@ def _get_default_entrance_exam_minimum_pct():
return entrance_exam_minimum_score_pct
def check_feature_enabled(feature_name):
def check_entrance_exams_enabled(view_func):
"""
Ensure the specified feature is turned on. Return an HTTP 400 code if not.
Ensure the entrance exams feature is turned on. Return an HTTP 400 code if not.
"""
def _check_feature_enabled(view_func):
def _decorator(request, *args, **kwargs):
# Deny access if the entrance exam feature is disabled
if not settings.FEATURES.get(feature_name, False):
return HttpResponseBadRequest()
return view_func(request, *args, **kwargs)
return wraps(view_func)(_decorator)
return _check_feature_enabled
def _decorator(request, *args, **kwargs):
# Deny access if the entrance exam feature is disabled
if not core_toggles.ENTRANCE_EXAMS.is_enabled():
return HttpResponseBadRequest()
return view_func(request, *args, **kwargs)
return wraps(view_func)(_decorator)
@login_required
@ensure_csrf_cookie
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
@check_entrance_exams_enabled
def entrance_exam(request, course_key_string):
"""
The restful handler for entrance exams.
@@ -105,7 +104,7 @@ def entrance_exam(request, course_key_string):
return HttpResponse(status=405)
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
@check_entrance_exams_enabled
def create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct):
"""
api method to create an entrance exam.
@@ -186,7 +185,7 @@ def _get_entrance_exam(request, course_key):
return HttpResponse(status=404)
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
@check_entrance_exams_enabled
def update_entrance_exam(request, course_key, exam_data):
"""
Operation to update course fields pertaining to entrance exams
@@ -200,7 +199,7 @@ def update_entrance_exam(request, course_key, exam_data):
CourseMetadata.update_from_dict(metadata, course, request.user)
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
@check_entrance_exams_enabled
def delete_entrance_exam(request, course_key):
"""
api method to delete an entrance exam

View File

@@ -14,7 +14,7 @@ from xblock.core import XBlock
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from edxmako.shortcuts import render_to_string
from util.milestones_helpers import is_entrance_exams_enabled
from openedx.core.toggles import ENTRANCE_EXAMS
from xmodule.modulestore.django import modulestore
from xmodule.tabs import StaticTab
from xmodule.x_module import DEPRECATION_VSCOMPAT_EVENT
@@ -214,7 +214,7 @@ def create_xblock(parent_locator, user, category, display_name, boilerplate=None
# Entrance Exams: Chapter module positioning
child_position = None
if is_entrance_exams_enabled():
if ENTRANCE_EXAMS.is_enabled():
if category == 'chapter' and is_entrance_exam:
fields['is_entrance_exam'] = is_entrance_exam
fields['in_entrance_exam'] = True # Inherited metadata, all children will have it
@@ -238,7 +238,7 @@ def create_xblock(parent_locator, user, category, display_name, boilerplate=None
)
# Entrance Exams: Grader assignment
if is_entrance_exams_enabled():
if ENTRANCE_EXAMS.is_enabled():
course_key = usage_key.course_key
course = store.get_course(course_key)
if hasattr(course, 'entrance_exam_enabled') and course.entrance_exam_enabled:

View File

@@ -43,7 +43,7 @@ from static_replace import replace_static_urls
from student.auth import has_studio_read_access, has_studio_write_access
from util.date_utils import get_default_time_display
from util.json_request import JsonResponse, expect_json
from util.milestones_helpers import is_entrance_exams_enabled
from openedx.core.toggles import ENTRANCE_EXAMS
from xblock_django.user_service import DjangoXBlockUserService
from xmodule.course_module import DEFAULT_START_DATE
from xmodule.library_tools import LibraryToolsService
@@ -100,7 +100,7 @@ def _filter_entrance_exam_grader(graders):
views/controls like the 'Grade as' dropdown that allows a course author to select
the grader type for a given section of a course
"""
if is_entrance_exams_enabled():
if ENTRANCE_EXAMS.is_enabled():
graders = [grader for grader in graders if grader.get('type') != u'Entrance Exam']
return graders

View File

@@ -352,18 +352,6 @@ FEATURES = {
'ENABLE_CHANGE_USER_PASSWORD_ADMIN': False,
### ORA Feature Flags ###
# .. toggle_name: ENABLE_ORA_TEAM_SUBMISSIONS
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
# .. toggle_description: Set to True to enable team-based ORA submissions.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2020-03-03
# .. toggle_target_removal_date: None
# .. toggle_tickets: https://openedx.atlassian.net/browse/EDUCATOR-4951
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
'ENABLE_ORA_TEAM_SUBMISSIONS': False,
# .. toggle_name: ENABLE_ORA_ALL_FILE_URLS
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False

View File

@@ -18,6 +18,8 @@ from cms.djangoapps.contentstore.views.organization import OrganizationListView
from openedx.core.apidocs import api_info
from openedx.core.djangoapps.password_policy import compliance as password_policy_compliance
from openedx.core.djangoapps.password_policy.forms import PasswordPolicyAwareAdminAuthForm
from openedx.core import toggles as core_toggles
django_autodiscover()
admin.site.site_header = _('Studio Administration')
@@ -220,7 +222,7 @@ urlpatterns.append(url(r'^admin/password_change/$', handler404))
urlpatterns.append(url(r'^admin/', admin.site.urls))
# enable entrance exams
if settings.FEATURES.get('ENTRANCE_EXAMS'):
if core_toggles.ENTRANCE_EXAMS.is_enabled():
urlpatterns.append(url(r'^course/{}/entrance_exam/?$'.format(settings.COURSE_KEY_PATTERN),
contentstore_views.entrance_exam))

View File

@@ -76,7 +76,7 @@ from openedx.core.djangoapps.xmodule_django.models import NoneToEmptyManager
from openedx.core.djangolib.model_mixins import DeletableByUserValue
from student.signals import ENROLL_STATUS_CHANGE, ENROLLMENT_TRACK_UPDATED, UNENROLL_DONE
from track import contexts, segment
from util.milestones_helpers import is_entrance_exams_enabled
from openedx.core.toggles import ENTRANCE_EXAMS
from util.model_utils import emit_field_changed_events, get_changed_fields_dict
from util.query import use_read_replica_if_available
@@ -2659,7 +2659,7 @@ class EntranceExamConfiguration(models.Model):
Return True if given user can skip entrance exam for given course otherwise False.
"""
can_skip = False
if is_entrance_exams_enabled():
if ENTRANCE_EXAMS.is_enabled():
try:
record = EntranceExamConfiguration.objects.get(user=user, course_id=course_key)
can_skip = record.skip_entrance_exam

View File

@@ -6,6 +6,7 @@ Utility library for working with the edx-milestones app
import six
from django.conf import settings
from django.utils.translation import ugettext as _
from edx_toggles.toggles import SettingDictToggle
from milestones import api as milestones_api
from milestones.exceptions import InvalidMilestoneRelationshipTypeException, InvalidUserException
from milestones.models import MilestoneRelationshipType
@@ -17,12 +18,23 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi
from openedx.core.lib.cache_utils import get_cache
from xmodule.modulestore.django import modulestore
NAMESPACE_CHOICES = {
'ENTRANCE_EXAM': 'entrance_exams'
}
REQUEST_CACHE_NAME = "milestones"
# .. toggle_name: FEATURES['ENABLE_MILESTONES_APP']
# .. toggle_implementation: SettingDictToggle
# .. toggle_default: False
# .. toggle_description: Enable the milestones application, which manages significant Course and/or Student events in
# the Open edX platform. (see https://github.com/edx/edx-milestones)
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2014-11-21
# TODO this should be moved to edx/edx-milestones
ENABLE_MILESTONES_APP = SettingDictToggle("FEATURES", "MILESTONES_APP", default=False, module_name=__name__)
def get_namespace_choices():
"""
@@ -31,19 +43,11 @@ def get_namespace_choices():
return NAMESPACE_CHOICES
def is_entrance_exams_enabled():
"""
Checks to see if the Entrance Exams feature is enabled
Use this operation instead of checking the feature flag all over the place
"""
return settings.FEATURES.get('ENTRANCE_EXAMS')
def is_prerequisite_courses_enabled():
"""
Returns boolean indicating prerequisite courses enabled system wide or not.
"""
return settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES') and settings.FEATURES.get('MILESTONES_APP')
return settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES') and ENABLE_MILESTONES_APP.is_enabled()
def add_prerequisite_course(course_key, prerequisite_course_key):
@@ -185,7 +189,7 @@ def fulfill_course_milestone(course_key, user):
Marks the course specified by the given course_key as complete for the given user.
If any other courses require this course as a prerequisite, their milestones will be appropriately updated.
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
try:
course_milestones = milestones_api.get_course_milestones(course_key=course_key, relationship="fulfills")
@@ -201,7 +205,7 @@ def remove_course_milestones(course_key, user, relationship):
"""
Remove all user milestones for the course specified by course_key.
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
course_milestones = milestones_api.get_course_milestones(course_key=course_key, relationship=relationship)
for milestone in course_milestones:
@@ -214,7 +218,7 @@ def get_required_content(course_key, user):
and if those milestones can be fulfilled via completion of a particular course content module
"""
required_content = []
if settings.FEATURES.get('MILESTONES_APP'):
if ENABLE_MILESTONES_APP.is_enabled():
course_run_id = six.text_type(course_key)
if user.is_authenticated:
@@ -248,7 +252,7 @@ def milestones_achieved_by_user(user, namespace):
"""
It would fetch list of milestones completed by user
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.get_user_milestones({'id': user.id}, namespace)
@@ -268,7 +272,7 @@ def seed_milestone_relationship_types():
"""
Helper method to pre-populate MRTs so the tests can run
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
MilestoneRelationshipType.objects.create(name='requires')
MilestoneRelationshipType.objects.create(name='fulfills')
@@ -296,7 +300,7 @@ def add_milestone(milestone_data):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.add_milestone(milestone_data)
@@ -305,7 +309,7 @@ def get_milestones(namespace):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return []
return milestones_api.get_milestones(namespace)
@@ -314,7 +318,7 @@ def get_milestone_relationship_types():
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return {}
return milestones_api.get_milestone_relationship_types()
@@ -323,7 +327,7 @@ def add_course_milestone(course_id, relationship, milestone):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.add_course_milestone(course_id, relationship, milestone)
@@ -332,7 +336,7 @@ def get_course_milestones(course_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return []
return milestones_api.get_course_milestones(course_id)
@@ -341,7 +345,7 @@ def add_course_content_milestone(course_id, content_id, relationship, milestone)
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.add_course_content_milestone(course_id, content_id, relationship, milestone)
@@ -355,7 +359,7 @@ def get_course_content_milestones(course_id, content_id=None, relationship='requ
Returns all content blocks in a course if content_id is None, otherwise it just returns that
specific content block.
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return []
if user_id is None:
@@ -382,7 +386,7 @@ def remove_course_content_user_milestones(course_key, content_key, user, relatio
"""
Removes the specified User-Milestone link from the system for the specified course content module.
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return []
course_content_milestones = milestones_api.get_course_content_milestones(course_key, content_key, relationship)
@@ -394,14 +398,14 @@ def remove_content_references(content_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.remove_content_references(content_id)
def any_unfulfilled_milestones(course_id, user_id):
""" Returns a boolean if user has any unfulfilled milestones """
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return False
user_id = None if user_id is None else int(user_id)
@@ -416,7 +420,7 @@ def get_course_milestones_fulfillment_paths(course_id, user_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.get_course_milestones_fulfillment_paths(
course_id,
@@ -428,7 +432,7 @@ def add_user_milestone(user, milestone):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.add_user_milestone(user, milestone)
@@ -437,7 +441,7 @@ def remove_user_milestone(user, milestone):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP'):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return milestones_api.remove_user_milestone(user, milestone)
@@ -450,6 +454,6 @@ def get_service():
Note: MilestonesService only has access to the functions
explicitly requested in the MilestonesServices class
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
if not ENABLE_MILESTONES_APP.is_enabled():
return None
return MilestonesService()

View File

@@ -7,7 +7,8 @@ from opaque_keys.edx.keys import UsageKey
from lms.djangoapps.courseware.access import has_access
from student.models import EntranceExamConfiguration
from util.milestones_helpers import get_required_content, is_entrance_exams_enabled
from util.milestones_helpers import get_required_content
from openedx.core.toggles import ENTRANCE_EXAMS
from xmodule.modulestore.django import modulestore
@@ -15,7 +16,7 @@ def course_has_entrance_exam(course):
"""
Checks to see if a course is properly configured for an entrance exam
"""
if not is_entrance_exams_enabled():
if not ENTRANCE_EXAMS.is_enabled():
return False
entrance_exam_enabled = getattr(course, 'entrance_exam_enabled', None)
if not entrance_exam_enabled:

View File

@@ -11,6 +11,7 @@ from opaque_keys.edx.keys import UsageKey
from lms.djangoapps.courseware.entrance_exams import get_entrance_exam_content
from openedx.core.lib.gating import api as gating_api
from util import milestones_helpers
from openedx.core.toggles import ENTRANCE_EXAMS
log = logging.getLogger(__name__)
@@ -48,7 +49,7 @@ def evaluate_entrance_exam(course_grade, user):
fulfilled for the user.
"""
course = course_grade.course_data.course
if milestones_helpers.is_entrance_exams_enabled() and getattr(course, 'entrance_exam_enabled', False):
if ENTRANCE_EXAMS.is_enabled() and getattr(course, 'entrance_exam_enabled', False):
if get_entrance_exam_content(user, course):
exam_chapter_key = get_entrance_exam_usage_key(course)
exam_score_ratio = get_entrance_exam_score_ratio(course_grade, exam_chapter_key)

View File

@@ -0,0 +1,43 @@
"""
Togglable settings for Teams behavior
"""
from edx_toggles.toggles import SettingDictToggle
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
# Course Waffle inherited from edx/edx-ora2
WAFFLE_NAMESPACE = "openresponseassessment"
TEAM_SUBMISSIONS_FLAG = "team_submissions"
# .. toggle_name: FEATURES['ENABLE_ORA_TEAM_SUBMISSIONS']
# .. toggle_implementation: SettingDictToggle
# .. toggle_default: False
# .. toggle_description: Set to True to enable team-based ORA submissions.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2020-03-03
# .. toggle_target_removal_date: None
# .. toggle_tickets: https://openedx.atlassian.net/browse/EDUCATOR-4951
# .. toggle_warnings: This temporary feature toggle does not have a target removal date. This can be overridden by a
# course waffle flags or a waffle switch with identical name.
# TODO: this should be moved to edx/edx-ora2
TEAM_SUBMISSIONS_FEATURE = SettingDictToggle(
"FEATURES", "ENABLE_ORA_TEAM_SUBMISSIONS", default=False, module_name=__name__
)
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(
course_key
):
return True
# TODO: this behaviour differs from edx-ora2, where the WaffleSwitch overrides the setting.
# https://github.com/edx/edx-ora2/blob/ac502d8301cb987c9885aaefbaeddaf456c13fb9/openassessment/xblock/config_mixin.py#L96
if TEAM_SUBMISSIONS_FEATURE.is_enabled():
return True
return False

View File

@@ -69,7 +69,7 @@ from .serializers import (
TopicSerializer
)
from .utils import emit_team_event
from .waffle import are_team_submissions_enabled
from .toggles import are_team_submissions_enabled
TEAM_MEMBERSHIPS_PER_PAGE = 5
TOPICS_PER_PAGE = 12

View File

@@ -1,26 +0,0 @@
"""
Togglable settings for Teams behavior
"""
from django.conf import settings
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
# Course Waffle inherited from edx/edx-ora2
WAFFLE_NAMESPACE = 'openresponseassessment'
TEAM_SUBMISSIONS_FLAG = 'team_submissions'
# edx/edx-platform feature
TEAM_SUBMISSIONS_FEATURE = 'ENABLE_ORA_TEAM_SUBMISSIONS'
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(course_key):
return True
if settings.FEATURES.get(TEAM_SUBMISSIONS_FEATURE, False):
return True
return False

View File

@@ -606,18 +606,6 @@ FEATURES = {
'ENABLE_LOGISTRATION_MICROFRONTEND': False,
### ORA Feature Flags ###
# .. toggle_name: ENABLE_ORA_TEAM_SUBMISSIONS
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
# .. toggle_description: Set to True to enable team-based ORA submissions.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2020-03-03
# .. toggle_target_removal_date: None
# .. toggle_tickets: https://openedx.atlassian.net/browse/EDUCATOR-4951
# .. toggle_warnings: This temporary feature toggle does not have a target removal date.
'ENABLE_ORA_TEAM_SUBMISSIONS': False,
# .. toggle_name: ENABLE_ORA_ALL_FILE_URLS
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False

17
openedx/core/toggles.py Normal file
View File

@@ -0,0 +1,17 @@
"""
Feature toggles used across the platform. Toggles should only be added to this module if we don't have a better place
for them. Generally speaking, they should be added to the most appropriate app or repo.
"""
from edx_toggles.toggles import SettingDictToggle
# .. toggle_name: FEATURES['ENTRANCE_EXAMS']
# .. toggle_implementation: SettingDictToggle
# .. toggle_default: False
# .. toggle_description: Enable entrance exams feature. When enabled, students see an exam xblock as the first unit
# of the course.
# .. toggle_use_cases: open_edx
# .. toggle_creation_date: 2015-12-01
# .. toggle_tickets: https://openedx.atlassian.net/browse/SOL-40
ENTRANCE_EXAMS = SettingDictToggle(
"FEATURES", "ENTRANCE_EXAMS", default=False, module_name=__name__
)

View File

@@ -2,4 +2,5 @@ from sys_path_hacks.warn import warn_deprecated_import
warn_deprecated_import('lms.djangoapps', 'teams.waffle')
from lms.djangoapps.teams.waffle import *
# pylint: disable=wildcard-import
from lms.djangoapps.teams.toggles import *