From deb9148f64fcc12edcac95ac836f93cb3120734b Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Wed, 17 Feb 2016 22:37:35 -0500 Subject: [PATCH] Remove unused Mobile CourseBlocksAndNavigation endpoint. --- .../course_structure_api/v0/tests.py | 233 ---------- .../course_structure_api/v0/urls.py | 24 -- .../course_structure_api/v0/views.py | 400 ------------------ lms/envs/common.py | 3 - lms/envs/test.py | 1 - 5 files changed, 661 deletions(-) diff --git a/lms/djangoapps/course_structure_api/v0/tests.py b/lms/djangoapps/course_structure_api/v0/tests.py index 726db58583..921b34d005 100644 --- a/lms/djangoapps/course_structure_api/v0/tests.py +++ b/lms/djangoapps/course_structure_api/v0/tests.py @@ -3,10 +3,8 @@ Run these tests @ Devstack: paver test_system -s lms --fasttest --verbose --test_id=lms/djangoapps/course_structure_api """ # pylint: disable=missing-docstring,invalid-name,maybe-no-member,attribute-defined-outside-init -from abc import ABCMeta from datetime import datetime from mock import patch, Mock -from itertools import product from django.core.urlresolvers import reverse @@ -15,13 +13,11 @@ from oauth2_provider.tests.factories import AccessTokenFactory, ClientFactory from opaque_keys.edx.locator import CourseLocator from xmodule.error_module import ErrorDescriptor from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls from xmodule.modulestore.xml import CourseLocationManager from xmodule.tests import get_test_system -from student.tests.factories import UserFactory, CourseEnrollmentFactory from courseware.tests.factories import GlobalStaffFactory, StaffFactory from openedx.core.djangoapps.content.course_structures.models import CourseStructure from openedx.core.djangoapps.content.course_structures.tasks import update_course_structure @@ -457,232 +453,3 @@ class CourseGradingPolicyMissingFieldsTests(CourseDetailTestMixin, CourseViewTes } ] self.assertListEqual(response.data, expected) - - -##################################################################################### -# -# The following Mixins/Classes collectively test the CourseBlocksAndNavigation view. -# -# The class hierarchy is: -# -# -----------------> CourseBlocksOrNavigationTestMixin <-------------- -# | ^ | -# | | | -# | CourseNavigationTestMixin | CourseBlocksTestMixin | -# | ^ ^ | ^ ^ | -# | | | | | | | -# | | | | | | | -# CourseNavigationTests CourseBlocksAndNavigationTests CourseBlocksTests -# -# -# Each Test Mixin is an abstract class that implements tests specific to its -# corresponding functionality. -# -# The concrete Test classes are expected to define the following class fields: -# -# block_navigation_view_type - The view's name as it should be passed to the django -# reverse method. -# container_fields - A list of fields that are expected to be included in the view's -# response for all container block types. -# block_fields - A list of fields that are expected to be included in the view's -# response for all block types. -# -###################################################################################### - - -class CourseBlocksOrNavigationTestMixin(CourseDetailTestMixin, CourseViewTestsMixin): - """ - A Mixin class for testing all views related to Course blocks and/or navigation. - """ - __metaclass__ = ABCMeta - - view_supports_debug_mode = False - - def setUp(self): - """ - Override the base `setUp` method to enroll the user in the course, since these views - require enrollment for non-staff users. - """ - super(CourseBlocksOrNavigationTestMixin, self).setUp() - CourseEnrollmentFactory(user=self.user, course_id=self.course.id) - - def create_user(self): - """ - Override the base `create_user` method to test with non-staff users for these views. - """ - self.user = UserFactory.create() - - @property - def view(self): - """ - Returns the name of the view for testing to use in the django `reverse` call. - """ - return 'course_structure_api:v0:' + self.block_navigation_view_type - - def test_get(self): - with check_mongo_calls(4): - response = super(CourseBlocksOrNavigationTestMixin, self).test_get() - - # verify root element - self.assertIn('root', response.data) - root_string = unicode(self.course.location) - self.assertEquals(response.data['root'], root_string) - - # verify ~blocks element - self.assertTrue(self.block_navigation_view_type in response.data) - blocks = response.data[self.block_navigation_view_type] - - # verify number of blocks - self.assertEquals(len(blocks), 5) - - # verify fields in blocks - for field, block in product(self.block_fields, blocks.values()): - self.assertIn(field, block) - - # verify container fields in container blocks - for field in self.container_fields: - self.assertIn(field, blocks[root_string]) - - def test_parse_error(self): - """ - Verifies the view returns a 400 when a query parameter is incorrectly formatted. - """ - response = self.http_get_for_course(data={'block_json': 'incorrect'}) - self.assertEqual(response.status_code, 400) - - @SharedModuleStoreTestCase.modifies_courseware - def test_no_access_to_block(self): - """ - Verifies the view returns only the top-level course block, excluding the sequential block - and its descendants when the user does not have access to the sequential. - """ - self.sequential.visible_to_staff_only = True - modulestore().update_item(self.sequential, self.user.id) - - response = super(CourseBlocksOrNavigationTestMixin, self).test_get() - self.assertEquals(len(response.data[self.block_navigation_view_type]), 1) - - -class CourseBlocksTestMixin(object): - """ - A Mixin class for testing all views related to Course blocks. - """ - __metaclass__ = ABCMeta - - view_supports_debug_mode = False - block_fields = ['id', 'type', 'display_name', 'web_url', 'block_url', 'graded', 'format'] - - def test_block_json(self): - """ - Verifies the view's response when the block_json data is requested. - """ - response = self.http_get_for_course( - data={'block_json': '{"video":{"profiles":["mobile_low"]}}'} - ) - self.assertEquals(response.status_code, 200) - video_block = response.data[self.block_navigation_view_type][unicode(self.video.location)] - self.assertIn('block_json', video_block) - - def test_block_count(self): - """ - Verifies the view's response when the block_count data is requested. - """ - response = self.http_get_for_course( - data={'block_count': 'problem'} - ) - self.assertEquals(response.status_code, 200) - root_block = response.data[self.block_navigation_view_type][unicode(self.course.location)] - self.assertIn('block_count', root_block) - self.assertIn('problem', root_block['block_count']) - self.assertEquals(root_block['block_count']['problem'], 1) - - def test_multi_device_support(self): - """ - Verifies the view's response when multi_device support is requested. - """ - response = self.http_get_for_course( - data={'fields': 'multi_device'} - ) - self.assertEquals(response.status_code, 200) - - for block, expected_multi_device_support in ( - (self.problem, True), - (self.html, True), - (self.video, False) - ): - block_response = response.data[self.block_navigation_view_type][unicode(block.location)] - self.assertEquals(block_response['multi_device'], expected_multi_device_support) - - -class CourseNavigationTestMixin(object): - """ - A Mixin class for testing all views related to Course navigation. - """ - __metaclass__ = ABCMeta - - def test_depth_zero(self): - """ - Tests that all descendants are bundled into the root block when the navigation_depth is set to 0. - """ - response = self.http_get_for_course( - data={'navigation_depth': '0'} - ) - root_block = response.data[self.block_navigation_view_type][unicode(self.course.location)] - self.assertIn('descendants', root_block) - self.assertEquals(len(root_block['descendants']), 4) - - def test_depth(self): - """ - Tests that all container blocks have descendants listed in their data. - """ - response = self.http_get_for_course() - - container_descendants = ( - (self.course.location, 1), - (self.sequential.location, 3), - ) - for container_location, expected_num_descendants in container_descendants: - block = response.data[self.block_navigation_view_type][unicode(container_location)] - self.assertIn('descendants', block) - self.assertEquals(len(block['descendants']), expected_num_descendants) - - -class CourseBlocksTests(CourseBlocksOrNavigationTestMixin, CourseBlocksTestMixin, SharedModuleStoreTestCase): - """ - A Test class for testing the Course 'blocks' view. - """ - block_navigation_view_type = 'blocks' - container_fields = ['children'] - - @classmethod - def setUpClass(cls): - super(CourseBlocksTests, cls).setUpClass() - cls.create_course_data() - - -class CourseNavigationTests(CourseBlocksOrNavigationTestMixin, CourseNavigationTestMixin, SharedModuleStoreTestCase): - """ - A Test class for testing the Course 'navigation' view. - """ - block_navigation_view_type = 'navigation' - container_fields = ['descendants'] - block_fields = [] - - @classmethod - def setUpClass(cls): - super(CourseNavigationTests, cls).setUpClass() - cls.create_course_data() - - -class CourseBlocksAndNavigationTests(CourseBlocksOrNavigationTestMixin, CourseBlocksTestMixin, - CourseNavigationTestMixin, SharedModuleStoreTestCase): - """ - A Test class for testing the Course 'blocks+navigation' view. - """ - block_navigation_view_type = 'blocks+navigation' - container_fields = ['children', 'descendants'] - - @classmethod - def setUpClass(cls): - super(CourseBlocksAndNavigationTests, cls).setUpClass() - cls.create_course_data() diff --git a/lms/djangoapps/course_structure_api/v0/urls.py b/lms/djangoapps/course_structure_api/v0/urls.py index 035b1a023a..11ba6aef45 100644 --- a/lms/djangoapps/course_structure_api/v0/urls.py +++ b/lms/djangoapps/course_structure_api/v0/urls.py @@ -20,27 +20,3 @@ urlpatterns = patterns( name='grading_policy' ), ) - -if settings.FEATURES.get('ENABLE_COURSE_BLOCKS_NAVIGATION_API'): - # TODO (MA-789) This endpoint still needs to be approved by the arch council. - # TODO (MA-704) This endpoint still needs to be made performant. - urlpatterns += ( - url( - r'^courses/{}/blocks/$'.format(COURSE_ID_PATTERN), - views.CourseBlocksAndNavigation.as_view(), - {'return_blocks': True, 'return_nav': False}, - name='blocks' - ), - url( - r'^courses/{}/navigation/$'.format(COURSE_ID_PATTERN), - views.CourseBlocksAndNavigation.as_view(), - {'return_blocks': False, 'return_nav': True}, - name='navigation' - ), - url( - r'^courses/{}/blocks\+navigation/$'.format(COURSE_ID_PATTERN), - views.CourseBlocksAndNavigation.as_view(), - {'return_blocks': True, 'return_nav': True}, - name='blocks+navigation' - ), - ) diff --git a/lms/djangoapps/course_structure_api/v0/views.py b/lms/djangoapps/course_structure_api/v0/views.py index 50359ecb9c..a61e94433a 100644 --- a/lms/djangoapps/course_structure_api/v0/views.py +++ b/lms/djangoapps/course_structure_api/v0/views.py @@ -1,7 +1,5 @@ """ API implementation for course-oriented interactions. """ -from collections import namedtuple -import json import logging from django.conf import settings @@ -12,20 +10,15 @@ from rest_framework.exceptions import AuthenticationFailed, ParseError from rest_framework.generics import RetrieveAPIView, ListAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from rest_framework.reverse import reverse from xmodule.modulestore.django import modulestore from opaque_keys.edx.keys import CourseKey from course_structure_api.v0 import serializers from courseware import courses from courseware.access import has_access -from courseware.model_data import FieldDataCache -from courseware.module_render import get_module_for_descriptor -from openedx.core.lib.api.view_utils import view_course_access, view_auth_classes from openedx.core.djangoapps.content.course_structures.api.v0 import api, errors from openedx.core.lib.exceptions import CourseNotFoundError from student.roles import CourseInstructorRole, CourseStaffRole -from util.module_utils import get_dynamic_descriptor_children log = logging.getLogger(__name__) @@ -297,396 +290,3 @@ class CourseGradingPolicy(CourseViewMixin, ListAPIView): @CourseViewMixin.course_check def get(self, request, **kwargs): return Response(api.course_grading_policy(self.course_key)) - - -@view_auth_classes() -class CourseBlocksAndNavigation(ListAPIView): - """ - **Use Case** - - The following endpoints return the content of the course according to the requesting user's access level. - - * Blocks - Get the course's blocks. - - * Navigation - Get the course's navigation information per the navigation depth requested. - - * Blocks+Navigation - Get both the course's blocks and the course's navigation information. - - **Example requests**: - - GET api/course_structure/v0/courses/{course_id}/blocks/ - GET api/course_structure/v0/courses/{course_id}/navigation/ - GET api/course_structure/v0/courses/{course_id}/blocks+navigation/ - &block_count=video - &block_json={"video":{"profiles":["mobile_low"]}} - &fields=graded,format,multi_device - - **Parameters**: - - * block_json: (dict) Indicates for which block types to return student_view_json data. The key is the block - type and the value is the "context" that is passed to the block's student_view_json method. - - Example: block_json={"video":{"profiles":["mobile_high","mobile_low"]}} - - * block_count: (list) Indicates for which block types to return the aggregate count of the blocks. - - Example: block_count="video,problem" - - * fields: (list) Indicates which additional fields to return for each block. - Default is "children,graded,format,multi_device" - - Example: fields=graded,format,multi_device - - * navigation_depth (integer) Indicates how far deep to traverse into the course hierarchy before bundling - all the descendants. - Default is 3 since typical navigational views of the course show a maximum of chapter->sequential->vertical. - - Example: navigation_depth=3 - - **Response Values** - - The following fields are returned with a successful response. - Only either one of blocks, navigation, or blocks+navigation is returned depending on which endpoint is used. - The "root" field is returned for all endpoints. - - * root: The ID of the root node of the course blocks. - - * blocks: A dictionary that maps block usage IDs to a collection of information about each block. - Each block contains the following fields. Returned only if using the "blocks" endpoint. - - * id: (string) The usage ID of the block. - - * type: (string) The type of block. Possible values include course, chapter, sequential, vertical, html, - problem, video, and discussion. The type can also be the name of a custom type of block used for the course. - - * display_name: (string) The display name of the block. - - * children: (list) If the block has child blocks, a list of IDs of the child blocks. - Returned only if the "children" input parameter is True. - - * block_count: (dict) For each block type specified in the block_count parameter to the endpoint, the - aggregate number of blocks of that type for this block and all of its descendants. - Returned only if the "block_count" input parameter contains this block's type. - - * block_json: (dict) The JSON data for this block. - Returned only if the "block_json" input parameter contains this block's type. - - * block_url: (string) The URL to retrieve the HTML rendering of this block. The HTML could include - CSS and Javascript code. This URL can be used as a fallback if the custom block_json for this - block type is not requested and not supported. - - * web_url: (string) The URL to the website location of this block. This URL can be used as a further - fallback if the block_url and the block_json is not supported. - - * graded (boolean) Whether or not the block or any of its descendants is graded. - Returned only if "graded" is included in the "fields" parameter. - - * format: (string) The assignment type of the block. - Possible values can be "Homework", "Lab", "Midterm Exam", and "Final Exam". - Returned only if "format" is included in the "fields" parameter. - - * multi_device: (boolean) Whether or not the block's rendering obtained via block_url has support - for multiple devices. - Returned only if "multi_device" is included in the "fields" parameter. - - * navigation: A dictionary that maps block IDs to a collection of navigation information about each block. - Each block contains the following fields. Returned only if using the "navigation" endpoint. - - * descendants: (list) A list of IDs of the children of the block if the block's depth in the - course hierarchy is less than the navigation_depth. Otherwise, a list of IDs of the aggregate descendants - of the block. - - * blocks+navigation: A dictionary that combines both the blocks and navigation data. - Returned only if using the "blocks+navigation" endpoint. - - """ - class RequestInfo(object): - """ - A class for encapsulating the request information, including what optional fields are requested. - """ - DEFAULT_FIELDS = "children,graded,format,multi_device" - - def __init__(self, request, course): - self.request = request - self.course = course - self.field_data_cache = None - - # check what fields are requested - try: - # fields - self.fields = set(request.GET.get('fields', self.DEFAULT_FIELDS).split(",")) - - # block_count - self.block_count = request.GET.get('block_count', "") - self.block_count = ( - self.block_count.split(",") if self.block_count else [] - ) - - # navigation_depth - # See docstring for why we default to 3. - self.navigation_depth = int(request.GET.get('navigation_depth', '3')) - - # block_json - self.block_json = json.loads(request.GET.get('block_json', "{}")) - if self.block_json and not isinstance(self.block_json, dict): - raise ParseError - except: - raise ParseError - - class ResultData(object): - """ - A class for encapsulating the result information, specifically the blocks and navigation data. - """ - def __init__(self, return_blocks, return_nav): - self.blocks = {} - self.navigation = {} - if return_blocks and return_nav: - self.navigation = self.blocks - - def update_response(self, response, return_blocks, return_nav): - """ - Updates the response object with result information. - """ - if return_blocks and return_nav: - response["blocks+navigation"] = self.blocks - elif return_blocks: - response["blocks"] = self.blocks - elif return_nav: - response["navigation"] = self.navigation - - class BlockInfo(object): - """ - A class for encapsulating a block's information as needed during traversal of a block hierarchy. - """ - def __init__(self, block, request_info, parent_block_info=None): - # the block for which the recursion is being computed - self.block = block - - # the type of the block - self.type = block.category - - # the block's depth in the block hierarchy - self.depth = 0 - - # the block's children - self.children = [] - - # descendants_of_parent: the list of descendants for this block's parent - self.descendants_of_parent = [] - self.descendants_of_self = [] - - # if a parent block was provided, update this block's data based on the parent's data - if parent_block_info: - # increment this block's depth value - self.depth = parent_block_info.depth + 1 - - # set this blocks' descendants_of_parent - self.descendants_of_parent = parent_block_info.descendants_of_self - - # add ourselves to the parent's children, if requested. - if 'children' in request_info.fields: - parent_block_info.value.setdefault("children", []).append(unicode(block.location)) - - # the block's data to include in the response - self.value = { - "id": unicode(block.location), - "type": self.type, - "display_name": block.display_name, - "web_url": reverse( - "jump_to", - kwargs={"course_id": unicode(request_info.course.id), "location": unicode(block.location)}, - request=request_info.request, - ), - "block_url": reverse( - "courseware.views.render_xblock", - kwargs={"usage_key_string": unicode(block.location)}, - request=request_info.request, - ), - } - - @view_course_access(depth=None) - def list(self, request, course, return_blocks=True, return_nav=True, *args, **kwargs): - """ - REST API endpoint for listing all the blocks and/or navigation information in the course, - while regarding user access and roles. - - Arguments: - request - Django request object - course - course module object - return_blocks - If true, returns the blocks information for the course. - return_nav - If true, returns the navigation information for the course. - """ - # set starting point - start_block = course - - # initialize request and result objects - request_info = self.RequestInfo(request, course) - result_data = self.ResultData(return_blocks, return_nav) - - # create and populate a field data cache by pre-fetching for the course (with depth=None) - request_info.field_data_cache = FieldDataCache.cache_for_descriptor_descendents( - course.id, request.user, course, depth=None, - ) - - # start the recursion with the start_block - self.recurse_blocks_nav(request_info, result_data, self.BlockInfo(start_block, request_info)) - - # return response - response = {"root": unicode(start_block.location)} - result_data.update_response(response, return_blocks, return_nav) - return Response(response) - - def recurse_blocks_nav(self, request_info, result_data, block_info): - """ - A depth-first recursive function that supports calculation of both the list of blocks in the course - and the navigation information up to the requested navigation_depth of the course. - - Arguments: - request_info - Object encapsulating the request information. - result_data - Running result data that is updated during the recursion. - block_info - Information about the current block in the recursion. - """ - # bind user data to the block - block_info.block = get_module_for_descriptor( - request_info.request.user, - request_info.request, - block_info.block, - request_info.field_data_cache, - request_info.course.id, - course=request_info.course - ) - - # verify the user has access to this block - if (block_info.block is None or not has_access( - request_info.request.user, - 'load', - block_info.block, - course_key=request_info.course.id - )): - return - - # add the block's value to the result - result_data.blocks[unicode(block_info.block.location)] = block_info.value - - # descendants - self.update_descendants(request_info, result_data, block_info) - - # children: recursively call the function for each of the children, while supporting dynamic children. - if block_info.block.has_children: - block_info.children = get_dynamic_descriptor_children(block_info.block, request_info.request.user.id) - for child in block_info.children: - self.recurse_blocks_nav( - request_info, - result_data, - self.BlockInfo(child, request_info, parent_block_info=block_info) - ) - - # block count - self.update_block_count(request_info, result_data, block_info) - - # block JSON data - self.add_block_json(request_info, block_info) - - # multi-device support - if 'multi_device' in request_info.fields: - block_info.value['multi_device'] = block_info.block.has_support( - getattr(block_info.block, 'student_view', None), - 'multi_device' - ) - - # additional fields - self.add_additional_fields(request_info, block_info) - - def update_descendants(self, request_info, result_data, block_info): - """ - Updates the descendants data for the current block. - - The current block is added to its parent's descendants if it is visible in the navigation - (i.e., the 'hide_from_toc' setting is False). - - Additionally, the block's depth is compared with the navigation_depth parameter to determine whether the - descendants of the block should be added to its own descendants (if block.depth <= navigation_depth) - or to the descendants of the block's parents (if block.depth > navigation_depth). - - block_info.descendants_of_self is the list of descendants that is passed to this block's children. - It should be either: - descendants_of_parent - if this block's depth is greater than the requested navigation_depth. - a dangling [] - if this block's hide_from_toc is True. - a referenced [] in navigation[block.location]["descendants"] - if this block's depth is within - the requested navigation depth. - """ - # Blocks with the 'hide_from_toc' setting are accessible, just not navigatable from the table-of-contents. - # If the 'hide_from_toc' setting is set on the block, do not add this block to the parent's descendants - # list and let the block's descendants add themselves to a dangling (unreferenced) descendants list. - if not block_info.block.hide_from_toc: - # add this block to the parent's descendants - block_info.descendants_of_parent.append(unicode(block_info.block.location)) - - # if this block's depth in the hierarchy is greater than the requested navigation depth, - # have the block's descendants add themselves to the parent's descendants. - if block_info.depth > request_info.navigation_depth: - block_info.descendants_of_self = block_info.descendants_of_parent - - # otherwise, have the block's descendants add themselves to this block's descendants by - # referencing/attaching descendants_of_self from this block's navigation value. - else: - result_data.navigation.setdefault( - unicode(block_info.block.location), {} - )["descendants"] = block_info.descendants_of_self - - def update_block_count(self, request_info, result_data, block_info): - """ - For all the block types that are requested to be counted, include the count of that block type as - aggregated from the block's descendants. - - Arguments: - request_info - Object encapsulating the request information. - result_data - Running result data that is updated during the recursion. - block_info - Information about the current block in the recursion. - """ - for b_type in request_info.block_count: - block_info.value.setdefault("block_count", {})[b_type] = ( - sum( - result_data.blocks.get(unicode(child.location), {}).get("block_count", {}).get(b_type, 0) - for child in block_info.children - ) + - (1 if b_type == block_info.type else 0) - ) - - def add_block_json(self, request_info, block_info): - """ - If the JSON data for this block's type is requested, and the block supports the 'student_view_json' - method, add the response from the 'student_view_json" method as the data for the block. - """ - if block_info.type in request_info.block_json: - if getattr(block_info.block, 'student_view_data', None): - block_info.value["block_json"] = block_info.block.student_view_data( - context=request_info.block_json[block_info.type] - ) - - # A mapping of API-exposed field names to xBlock field names and API field defaults. - BlockApiField = namedtuple('BlockApiField', 'block_field_name api_field_default') - FIELD_MAP = { - 'graded': BlockApiField(block_field_name='graded', api_field_default=False), - 'format': BlockApiField(block_field_name='format', api_field_default=None), - } - - def add_additional_fields(self, request_info, block_info): - """ - Add additional field names and values of the block as requested in the request_info. - """ - for field_name in request_info.fields: - if field_name in self.FIELD_MAP: - block_info.value[field_name] = getattr( - block_info.block, - self.FIELD_MAP[field_name].block_field_name, - self.FIELD_MAP[field_name].api_field_default, - ) - - def perform_authentication(self, request): - """ - Ensures that the user is authenticated (e.g. not an AnonymousUser) - """ - super(CourseBlocksAndNavigation, self).perform_authentication(request) - if request.user.is_anonymous(): - raise AuthenticationFailed diff --git a/lms/envs/common.py b/lms/envs/common.py index 92742cf7aa..a9ea372ef9 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -272,9 +272,6 @@ FEATURES = { # ENABLE_OAUTH2_PROVIDER to True 'ENABLE_MOBILE_REST_API': False, - # Enable temporary APIs required for xBlocks on Mobile - 'ENABLE_COURSE_BLOCKS_NAVIGATION_API': False, - # Enable the combined login/registration form 'ENABLE_COMBINED_LOGIN_REGISTRATION': False, diff --git a/lms/envs/test.py b/lms/envs/test.py index dcf28d61d6..eff52628c1 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -294,7 +294,6 @@ OIDC_COURSE_HANDLER_CACHE_TIMEOUT = 0 ########################### External REST APIs ################################# FEATURES['ENABLE_MOBILE_REST_API'] = True FEATURES['ENABLE_VIDEO_ABSTRACTION_LAYER_API'] = True -FEATURES['ENABLE_COURSE_BLOCKS_NAVIGATION_API'] = True ###################### Payment ##############################3 # Enable fake payment processing page