Skip to content
Snippets Groups Projects
serializers.py 4.97 KiB
"""
Serializers for Course Blocks related return objects.
"""
from __future__ import absolute_import

import six
from django.conf import settings
from rest_framework import serializers
from rest_framework.reverse import reverse

from .transformers import SUPPORTED_FIELDS

# This lists the names of all fields that are allowed
# to be show to users who do not have access to a particular piece
# of content
FIELDS_ALLOWED_IN_AUTH_DENIED_CONTENT = [
    "display_name",
    "block_id",
    "student_view_url",
    "student_view_multi_device",
    "lms_web_url",
    "type",
    "id",
    "block_counts",
    "graded",
    "descendants",
    "authorization_denial_reason",
    "authorization_denial_message",
]


class BlockSerializer(serializers.Serializer):  # pylint: disable=abstract-method
    """
    Serializer for single course block
    """
    def _get_field(self, block_key, transformer, field_name, default):
        """
        Get the field value requested.  The field may be an XBlock field, a
        transformer block field, or an entire tranformer block data dict.
        """
        value = None
        if transformer is None:
            value = self.context['block_structure'].get_xblock_field(block_key, field_name)
        elif field_name is None:
            try:
                value = self.context['block_structure'].get_transformer_block_data(block_key, transformer).fields
            except KeyError:
                pass
        else:
            value = self.context['block_structure'].get_transformer_block_field(block_key, transformer, field_name)

        return value if (value is not None) else default

    def to_representation(self, block_key):
        """
        Return a serializable representation of the requested block
        """
        # create response data dict for basic fields

        block_structure = self.context['block_structure']
        authorization_denial_reason = block_structure.get_xblock_field(block_key, 'authorization_denial_reason')
        authorization_denial_message = block_structure.get_xblock_field(block_key, 'authorization_denial_message')

        data = {
            'id': six.text_type(block_key),
            'block_id': six.text_type(block_key.block_id),
            'lms_web_url': reverse(
                'jump_to',
                kwargs={'course_id': six.text_type(block_key.course_key), 'location': six.text_type(block_key)},
                request=self.context['request'],
            ),
            'student_view_url': reverse(
                'render_xblock',
                kwargs={'usage_key_string': six.text_type(block_key)},
                request=self.context['request'],
            ),
        }

        if settings.FEATURES.get("ENABLE_LTI_PROVIDER") and 'lti_url' in self.context['requested_fields']:
            data['lti_url'] = reverse(
                'lti_provider_launch',
                kwargs={'course_id': six.text_type(block_key.course_key), 'usage_id': six.text_type(block_key)},
                request=self.context['request'],
            )

        # add additional requested fields that are supported by the various transformers
        for supported_field in SUPPORTED_FIELDS:
            if supported_field.requested_field_name in self.context['requested_fields']:
                field_value = self._get_field(
                    block_key,
                    supported_field.transformer,
                    supported_field.block_field_name,
                    supported_field.default_value,
                )
                if field_value is not None:
                    # only return fields that have data
                    data[supported_field.serializer_field_name] = field_value

        if 'children' in self.context['requested_fields']:
            children = block_structure.get_children(block_key)
            if children:
                data['children'] = [six.text_type(child) for child in children]

        if authorization_denial_reason and authorization_denial_message:
            data['authorization_denial_reason'] = authorization_denial_reason
            data['authorization_denial_message'] = authorization_denial_message
            cleaned_data = data.copy()
            for field in data.keys():  # pylint: disable=consider-iterating-dictionary
                if field not in FIELDS_ALLOWED_IN_AUTH_DENIED_CONTENT:
                    del cleaned_data[field]
            data = cleaned_data

        return data


class BlockDictSerializer(serializers.Serializer):  # pylint: disable=abstract-method
    """
    Serializer that formats a BlockStructure object to a dictionary, rather
    than a list, of blocks
    """
    root = serializers.CharField(source='root_block_usage_key')
    blocks = serializers.SerializerMethodField()

    def get_blocks(self, structure):
        """
        Serialize to a dictionary of blocks keyed by the block's usage_key.
        """
        return {
            six.text_type(block_key): BlockSerializer(block_key, context=self.context).data
            for block_key in structure
        }