""" CourseBlocks API views """ from django.core.exceptions import ValidationError from django.http import Http404 from rest_framework.generics import ListAPIView from rest_framework.response import Response from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from openedx.core.lib.api.view_utils import view_auth_classes, DeveloperErrorViewMixin from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError from .api import get_blocks from .forms import BlockListGetForm @view_auth_classes() class BlocksView(DeveloperErrorViewMixin, ListAPIView): """ **Use Case** Returns the blocks within the requested block tree according to the requesting user's access level. **Example requests**: GET /api/courses/v1/blocks//?depth=all GET /api/courses/v1/blocks//? username=anjali &depth=all &requested_fields=graded,format,student_view_multi_device &block_counts=video &student_view_data=video **Parameters**: * username: (string) The name of the user on whose behalf we want to see the data. Example: username=anjali * student_view_data: (list) Indicates for which block types to return student_view_data. Example: student_view_data=video * block_counts: (list) Indicates for which block types to return the aggregate count of the blocks. Example: block_counts=video,problem * requested_fields: (list) Indicates which additional fields to return for each block. For a list of available fields see under `Response Values -> blocks`, below. The following fields are always returned: id, type, display_name Example: requested_fields=graded,format,student_view_multi_device * depth: (integer or all) Indicates how deep to traverse into the blocks hierarchy. A value of all means the entire hierarchy. Default is 0 Example: depth=all * nav_depth: (integer) WARNING: nav_depth is not supported, and may be removed at any time. 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: nav_depth=3 * return_type (string) Indicates in what data type to return the blocks. Default is dict. Supported values are: dict, list Example: return_type=dict **Response Values** The following fields are returned with a successful response. * root: The ID of the root node of the requested course block structure. * blocks: A dictionary that maps block usage IDs to a collection of information about each block. Each block contains the following fields. * 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 "children" is included in the "requested_fields" parameter. * block_counts: (dict) For each block type specified in the block_counts 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_counts" input parameter contains this block's type. * graded (boolean) Whether or not the block or any of its descendants is graded. Returned only if "graded" is included in the "requested_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 "requested_fields" parameter. * student_view_data: (dict) The JSON data for this block. Returned only if the "student_view_data" input parameter contains this block's type. * student_view_url: (string) The URL to retrieve the HTML rendering of this block's student view. The HTML could include CSS and Javascript code. This field can be used in combination with the student_view_multi_device field to decide whether to display this content to the user. This URL can be used as a fallback if the student_view_data for this block type is not supported by the client or the block. * student_view_multi_device: (boolean) Whether or not the block's rendering obtained via block_url has support for multiple devices. Returned only if "student_view_multi_device" is included in the "requested_fields" parameter. * lms_web_url: (string) The URL to the navigational container of the xBlock on the web LMS. This URL can be used as a further fallback if the student_view_url and the student_view_data fields are not supported. """ def list(self, request, usage_key_string): # pylint: disable=arguments-differ """ REST API endpoint for listing all the blocks information in the course, while regarding user access and roles. Arguments: request - Django request object usage_key_string - The usage key for a block. """ # validate request parameters requested_params = request.query_params.copy() requested_params.update({'usage_key': usage_key_string}) params = BlockListGetForm(requested_params, initial={'requesting_user': request.user}) if not params.is_valid(): raise ValidationError(params.errors) try: return Response( get_blocks( request, params.cleaned_data['usage_key'], params.cleaned_data['user'], params.cleaned_data['depth'], params.cleaned_data.get('nav_depth'), params.cleaned_data['requested_fields'], params.cleaned_data.get('block_counts', []), params.cleaned_data.get('student_view_data', []), params.cleaned_data['return_type'] ) ) except ItemNotFoundError as exception: raise Http404("Block not found: {}".format(exception.message)) @view_auth_classes() class BlocksInCourseView(BlocksView): """ **Use Case** Returns the blocks in the course according to the requesting user's access level. **Example requests**: GET /api/courses/v1/blocks/?course_id= GET /api/courses/v1/blocks/?course_id= &username=anjali &depth=all &requested_fields=graded,format,student_view_multi_device &block_counts=video &student_view_data=video **Parameters**: This view redirects to /api/courses/v1/blocks// for the root usage key of the course specified by course_id. The view accepts all parameters accepted by :class:`BlocksView`, plus the following required parameter * course_id: (string, required) The ID of the course whose block data we want to return **Response Values** Responses are identical to those returned by :class:`BlocksView` when passed the root_usage_key of the requested course. If the course_id is not supplied, a 400: Bad Request is returned, with a message indicating that course_id is required. If an invalid course_id is supplied, a 400: Bad Request is returned, with a message indicating that the course_id is not valid. """ def list(self, request): # pylint: disable=arguments-differ """ Retrieves the usage_key for the requested course, and then returns the same information that would be returned by BlocksView.list, called with that usage key Arguments: request - Django request object """ # convert the requested course_key to the course's root block's usage_key course_key_string = request.query_params.get('course_id', None) if not course_key_string: raise ValidationError('course_id is required.') try: course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) except InvalidKeyError: raise ValidationError("'{}' is not a valid course key.".format(unicode(course_key_string))) return super(BlocksInCourseView, self).list(request, course_usage_key)