174 lines
7.1 KiB
Python
174 lines
7.1 KiB
Python
"""
|
|
API function for retrieving course blocks data
|
|
"""
|
|
|
|
|
|
import lms.djangoapps.course_blocks.api as course_blocks_api
|
|
from lms.djangoapps.course_blocks.transformers.access_denied_filter import AccessDeniedMessageFilterTransformer
|
|
from lms.djangoapps.course_blocks.transformers.hidden_content import HiddenContentTransformer
|
|
from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers
|
|
from openedx.core.djangoapps.discussions.transformers import DiscussionsTopicLinkTransformer
|
|
from openedx.features.effort_estimation.api import EffortEstimationTransformer
|
|
|
|
from .serializers import BlockDictSerializer, BlockSerializer
|
|
from .toggles import HIDE_ACCESS_DENIALS_FLAG
|
|
from .transformers.blocks_api import BlocksAPITransformer
|
|
from .transformers.milestones import MilestonesAndSpecialExamsTransformer
|
|
|
|
|
|
def get_blocks(
|
|
request,
|
|
usage_key,
|
|
user=None,
|
|
depth=None,
|
|
nav_depth=None,
|
|
requested_fields=None,
|
|
block_counts=None,
|
|
student_view_data=None,
|
|
return_type='dict',
|
|
block_types_filter=None,
|
|
hide_access_denials=False,
|
|
allow_start_dates_in_future=False,
|
|
):
|
|
"""
|
|
Return a serialized representation of the course blocks.
|
|
|
|
Arguments:
|
|
request (HTTPRequest): Used for calling django reverse.
|
|
usage_key (UsageKey): Identifies the starting block of interest.
|
|
user (User): Optional user object for whom the blocks are being
|
|
retrieved. If None, blocks are returned regardless of access checks.
|
|
depth (integer or None): Identifies the depth of the tree to return
|
|
starting at the root block. If None, the entire tree starting at
|
|
the root is returned.
|
|
nav_depth (integer): Optional parameter that indicates how far deep to
|
|
traverse into the block hierarchy before bundling all the
|
|
descendants for navigation.
|
|
requested_fields (list): Optional list of names of additional fields
|
|
to return for each block. Supported fields are listed in
|
|
transformers.SUPPORTED_FIELDS.
|
|
block_counts (list): Optional list of names of block types for which to
|
|
return an aggregate count of blocks.
|
|
student_view_data (list): Optional list of names of block types for
|
|
which blocks to return their student_view_data.
|
|
return_type (string): Possible values are 'dict' or 'list'. Indicates
|
|
the format for returning the blocks.
|
|
block_types_filter (list): Optional list of block type names used to filter
|
|
the final result of returned blocks.
|
|
hide_access_denials (bool): When True, filter out any blocks that were
|
|
denied access to the user, even if they have access denial messages
|
|
attached.
|
|
allow_start_dates_in_future (bool): When True, will allow blocks to be
|
|
returned that can bypass the StartDateTransformer's filter to show
|
|
blocks with start dates in the future.
|
|
"""
|
|
|
|
if HIDE_ACCESS_DENIALS_FLAG.is_enabled():
|
|
hide_access_denials = True
|
|
|
|
# create ordered list of transformers, adding BlocksAPITransformer at end.
|
|
transformers = BlockStructureTransformers()
|
|
if requested_fields is None:
|
|
requested_fields = []
|
|
include_completion = 'completion' in requested_fields
|
|
include_effort_estimation = (EffortEstimationTransformer.EFFORT_TIME in requested_fields or
|
|
EffortEstimationTransformer.EFFORT_ACTIVITIES in requested_fields)
|
|
include_gated_sections = 'show_gated_sections' in requested_fields
|
|
include_has_scheduled_content = 'has_scheduled_content' in requested_fields
|
|
include_special_exams = 'special_exam_info' in requested_fields
|
|
include_discussions_context = (
|
|
DiscussionsTopicLinkTransformer.EMBED_URL in requested_fields or
|
|
DiscussionsTopicLinkTransformer.EXTERNAL_ID in requested_fields
|
|
)
|
|
|
|
if user is not None:
|
|
transformers += course_blocks_api.get_course_block_access_transformers(user)
|
|
transformers += [
|
|
MilestonesAndSpecialExamsTransformer(
|
|
include_special_exams=include_special_exams,
|
|
include_gated_sections=include_gated_sections
|
|
),
|
|
HiddenContentTransformer()
|
|
]
|
|
else:
|
|
transformers += [course_blocks_api.visibility.VisibilityTransformer()]
|
|
|
|
# Note: A change to the BlockCompletionTransformer (https://github.com/openedx/edx-platform/pull/27622/)
|
|
# will be introducing a bug if hide_access_denials is True. I'm accepting this risk because in
|
|
# the AccessDeniedMessageFilterTransformer, there is note about deleting it and I believe it is
|
|
# technically deprecated functionality. The only use case where hide_access_denials is True
|
|
# (outside of explicitly setting the temporary waffle flag) is in lms/djangoapps/course_api/blocks/urls.py
|
|
# for a v1 api that I also believe should have been deprecated and removed. When this code is removed,
|
|
# please also remove this comment. Thanks!
|
|
if hide_access_denials:
|
|
transformers += [AccessDeniedMessageFilterTransformer()]
|
|
|
|
if include_effort_estimation:
|
|
transformers += [EffortEstimationTransformer()]
|
|
|
|
if include_discussions_context:
|
|
transformers += [DiscussionsTopicLinkTransformer()]
|
|
|
|
transformers += [
|
|
BlocksAPITransformer(
|
|
block_counts,
|
|
student_view_data,
|
|
depth,
|
|
nav_depth
|
|
),
|
|
]
|
|
|
|
# transform
|
|
blocks = course_blocks_api.get_course_blocks(
|
|
user,
|
|
usage_key,
|
|
transformers,
|
|
allow_start_dates_in_future=allow_start_dates_in_future,
|
|
include_completion=include_completion,
|
|
include_has_scheduled_content=include_has_scheduled_content
|
|
)
|
|
|
|
# filter blocks by types
|
|
if block_types_filter:
|
|
block_keys_to_remove = []
|
|
for block_key in blocks:
|
|
block_type = blocks.get_xblock_field(block_key, 'category')
|
|
if block_type not in block_types_filter:
|
|
block_keys_to_remove.append(block_key)
|
|
for block_key in block_keys_to_remove:
|
|
blocks.remove_block(block_key, keep_descendants=True)
|
|
|
|
# serialize
|
|
serializer_context = {
|
|
'request': request,
|
|
'block_structure': blocks,
|
|
'requested_fields': requested_fields or [],
|
|
}
|
|
|
|
if return_type == 'dict':
|
|
serializer = BlockDictSerializer(blocks, context=serializer_context, many=False)
|
|
else:
|
|
serializer = BlockSerializer(blocks, context=serializer_context, many=True)
|
|
|
|
# return serialized data
|
|
return serializer.data
|
|
|
|
|
|
def get_block_metadata(block, includes=()):
|
|
"""
|
|
Get metadata about the specified XBlock.
|
|
|
|
Args:
|
|
block (XBlock): block object
|
|
includes (list|set): list or set of metadata keys to include. Valid keys are:
|
|
index_dictionary: a dictionary of data used to add this XBlock's content
|
|
to a search index.
|
|
"""
|
|
data = {
|
|
"id": str(block.scope_ids.usage_id),
|
|
"type": block.scope_ids.block_type,
|
|
}
|
|
if "index_dictionary" in includes:
|
|
data["index_dictionary"] = block.index_dictionary()
|
|
return data
|