From 87b5463d4223b2f2e755fb9ed3cd0f34ffc29123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Behmo?= Date: Mon, 12 Oct 2020 23:14:36 +0200 Subject: [PATCH] Use SettingDictToggle to document ENTRANCE_EXAMS --- cms/djangoapps/contentstore/views/course.py | 6 ++--- .../contentstore/views/entrance_exam.py | 27 +++++++++---------- cms/djangoapps/contentstore/views/helpers.py | 6 ++--- cms/djangoapps/contentstore/views/item.py | 4 +-- cms/urls.py | 4 ++- common/djangoapps/student/models.py | 4 +-- common/djangoapps/util/milestones_helpers.py | 10 +------ lms/djangoapps/courseware/entrance_exams.py | 5 ++-- lms/djangoapps/gating/api.py | 3 ++- openedx/core/toggles.py | 17 ++++++++++++ 10 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 openedx/core/toggles.py diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 2d73c62d73..2a7f4a5af6 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -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) diff --git a/cms/djangoapps/contentstore/views/entrance_exam.py b/cms/djangoapps/contentstore/views/entrance_exam.py index 2ef82a7163..b33b44307c 100644 --- a/cms/djangoapps/contentstore/views/entrance_exam.py +++ b/cms/djangoapps/contentstore/views/entrance_exam.py @@ -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 diff --git a/cms/djangoapps/contentstore/views/helpers.py b/cms/djangoapps/contentstore/views/helpers.py index 8716dc21f8..cfa681c8b0 100644 --- a/cms/djangoapps/contentstore/views/helpers.py +++ b/cms/djangoapps/contentstore/views/helpers.py @@ -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: diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 9d31784268..f71d3dee35 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -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 diff --git a/cms/urls.py b/cms/urls.py index b564957822..bb51c55f85 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -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)) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 93c42cd7a0..b62ea89707 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -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 diff --git a/common/djangoapps/util/milestones_helpers.py b/common/djangoapps/util/milestones_helpers.py index ae4cebddf3..df56591de8 100644 --- a/common/djangoapps/util/milestones_helpers.py +++ b/common/djangoapps/util/milestones_helpers.py @@ -18,6 +18,7 @@ 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' } @@ -33,7 +34,6 @@ REQUEST_CACHE_NAME = "milestones" # .. 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__) -ENABLE_ENTRANCE_EXAMS = SettingDictToggle("FEATURES", "ENTRANCE_EXAMS", default=False, module_name=__name__) def get_namespace_choices(): @@ -43,14 +43,6 @@ 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 ENABLE_ENTRANCE_EXAMS.is_enabled() - - def is_prerequisite_courses_enabled(): """ Returns boolean indicating prerequisite courses enabled system wide or not. diff --git a/lms/djangoapps/courseware/entrance_exams.py b/lms/djangoapps/courseware/entrance_exams.py index 0f87090517..8ff5dd9753 100644 --- a/lms/djangoapps/courseware/entrance_exams.py +++ b/lms/djangoapps/courseware/entrance_exams.py @@ -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: diff --git a/lms/djangoapps/gating/api.py b/lms/djangoapps/gating/api.py index 2253a30b7e..9ee405f5fa 100644 --- a/lms/djangoapps/gating/api.py +++ b/lms/djangoapps/gating/api.py @@ -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) diff --git a/openedx/core/toggles.py b/openedx/core/toggles.py new file mode 100644 index 0000000000..b3ef6e507e --- /dev/null +++ b/openedx/core/toggles.py @@ -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__ +)