Пример #1
0
def get_course_blocks(
        user,
        root_block_usage_key,
        transformers=None,
):
    """
    A higher order function implemented on top of the
    block_structure.get_blocks function returning a transformed block
    structure for the given user starting at root_block_usage_key.

    Note: The current implementation requires the root_block_usage_key
    to be the root block of its corresponding course. However, this
    is a short-term limitation, which will be addressed in a coming
    ticket (https://openedx.atlassian.net/browse/MA-1604). Once that
    ticket is implemented, callers will be able to get course blocks
    starting at any arbitrary location within a block structure.

    Arguments:
        user (django.contrib.auth.models.User) - User object for
            which the block structure is to be transformed.

        root_block_usage_key (UsageKey) - The usage_key for the root
            of the block structure that is being accessed.

        transformers (BlockStructureTransformers) - A collection of
            transformers whose transform methods are to be called.
            If None, COURSE_BLOCK_ACCESS_TRANSFORMERS is used.

    Returns:
        BlockStructureBlockData - A transformed block structure,
            starting at root_block_usage_key, that has undergone the
            transform methods for the given user and the course
            associated with the block structure.  If using the default
            transformers, the transformed block structure will be
            exactly equivalent to the blocks that the given user has
            access.
    """
    if root_block_usage_key != modulestore().make_course_usage_key(root_block_usage_key.course_key):
        # Enforce this check for now until MA-1604 is implemented.
        # Otherwise, callers will get incorrect block data after a
        # new version of the course is published, since
        # clear_course_from_cache only clears the cached block
        # structures starting at the root block of the course.
        raise NotImplementedError

    if not transformers:
        transformers = BlockStructureTransformers(COURSE_BLOCK_ACCESS_TRANSFORMERS)
    transformers.usage_info = CourseUsageInfo(root_block_usage_key.course_key, user)

    return _get_block_structure_manager(root_block_usage_key.course_key).get_transformed(transformers)
Пример #2
0
def get_course_blocks(
    user,
    starting_block_usage_key,
    transformers=None,
    collected_block_structure=None,
):
    """
    A higher order function implemented on top of the
    block_structure.get_blocks function returning a transformed block
    structure for the given user starting at starting_block_usage_key.

    Arguments:
        user (django.contrib.auth.models.User) - User object for
            which the block structure is to be transformed.

        starting_block_usage_key (UsageKey) - Specifies the starting block
            of the block structure that is to be transformed.

        transformers (BlockStructureTransformers) - A collection of
            transformers whose transform methods are to be called.
            If None, COURSE_BLOCK_ACCESS_TRANSFORMERS is used.

        collected_block_structure (BlockStructureBlockData) - A
            block structure retrieved from a prior call to
            BlockStructureManager.get_collected.  Can be optionally
            provided if already available, for optimization.

    Returns:
        BlockStructureBlockData - A transformed block structure,
            starting at starting_block_usage_key, that has undergone the
            transform methods for the given user and the course
            associated with the block structure.  If using the default
            transformers, the transformed block structure will be
            exactly equivalent to the blocks that the given user has
            access.
    """
    if not transformers:
        transformers = BlockStructureTransformers(
            COURSE_BLOCK_ACCESS_TRANSFORMERS)
    transformers.usage_info = CourseUsageInfo(
        starting_block_usage_key.course_key, user)

    return get_block_structure_manager(
        starting_block_usage_key.course_key).get_transformed(
            transformers,
            starting_block_usage_key,
            collected_block_structure,
        )
Пример #3
0
 def setUp(self):
     super(TransformerRegistryTestMixin, self).setUp()
     self.patcher = patch(
         'openedx.core.lib.block_structure.transformer_registry.TransformerRegistry.get_registered_transformers'
     )
     mock_registry = self.patcher.start()
     mock_registry.return_value = {self.TRANSFORMER_CLASS_TO_TEST}
     self.transformers = BlockStructureTransformers([self.TRANSFORMER_CLASS_TO_TEST()])
Пример #4
0
    def test_content_library(self):
        """
        Test when course has content library section.
        First test user can't see any content library section,
        and after that mock response from MySQL db.
        Check user can see mocked sections in content library.
        """
        raw_block_structure = get_course_blocks(
            self.user,
            self.course.location,
            transformers=BlockStructureTransformers(),
        )
        self.assertEqual(len(list(raw_block_structure.get_block_keys())), len(self.blocks))

        clear_course_from_cache(self.course.id)
        trans_block_structure = get_course_blocks(
            self.user,
            self.course.location,
            self.transformers,
        )

        # Should dynamically assign a block to student
        trans_keys = set(trans_block_structure.get_block_keys())
        block_key_set = self.get_block_key_set(
            self.blocks, 'course', 'chapter1', 'lesson1', 'vertical1', 'library_content1'
        )
        for key in block_key_set:
            self.assertIn(key, trans_keys)

        vertical2_selected = self.get_block_key_set(self.blocks, 'vertical2').pop() in trans_keys
        vertical3_selected = self.get_block_key_set(self.blocks, 'vertical3').pop() in trans_keys

        self.assertNotEquals(vertical2_selected, vertical3_selected)  # only one of them should be selected
        selected_vertical = 'vertical2' if vertical2_selected else 'vertical3'
        selected_child = 'html1' if vertical2_selected else 'html2'

        # Check course structure again.
        clear_course_from_cache(self.course.id)
        for i in range(5):
            trans_block_structure = get_course_blocks(
                self.user,
                self.course.location,
                self.transformers,
            )
            self.assertEqual(
                set(trans_block_structure.get_block_keys()),
                self.get_block_key_set(
                    self.blocks,
                    'course',
                    'chapter1',
                    'lesson1',
                    'vertical1',
                    'library_content1',
                    selected_vertical,
                    selected_child,
                ),
                "Expected 'selected' equality failed in iteration {}.".format(i)
            )
Пример #5
0
def get_course_blocks(
        user,
        starting_block_usage_key,
        transformers=None,
        collected_block_structure=None,
):
    """
    A higher order function implemented on top of the
    block_structure.get_blocks function returning a transformed block
    structure for the given user starting at starting_block_usage_key.

    Arguments:
        user (django.contrib.auth.models.User) - User object for
            which the block structure is to be transformed.

        starting_block_usage_key (UsageKey) - Specifies the starting block
            of the block structure that is to be transformed.

        transformers (BlockStructureTransformers) - A collection of
            transformers whose transform methods are to be called.
            If None, COURSE_BLOCK_ACCESS_TRANSFORMERS is used.

        collected_block_structure (BlockStructureBlockData) - A
            block structure retrieved from a prior call to
            BlockStructureManager.get_collected.  Can be optionally
            provided if already available, for optimization.

    Returns:
        BlockStructureBlockData - A transformed block structure,
            starting at starting_block_usage_key, that has undergone the
            transform methods for the given user and the course
            associated with the block structure.  If using the default
            transformers, the transformed block structure will be
            exactly equivalent to the blocks that the given user has
            access.
    """
    if not transformers:
        transformers = BlockStructureTransformers(COURSE_BLOCK_ACCESS_TRANSFORMERS)
    transformers.usage_info = CourseUsageInfo(starting_block_usage_key.course_key, user)

    return get_block_structure_manager(starting_block_usage_key.course_key).get_transformed(
        transformers,
        starting_block_usage_key,
        collected_block_structure,
    )
Пример #6
0
    def test_content_library(self):
        """
        Test when course has content library section.
        First test user can't see any content library section,
        and after that mock response from MySQL db.
        Check user can see mocked sections in content library.
        """
        raw_block_structure = get_course_blocks(
            self.user,
            self.course.location,
            transformers=BlockStructureTransformers(),
        )
        self.assertEqual(len(list(raw_block_structure.get_block_keys())),
                         len(self.blocks))

        clear_course_from_cache(self.course.id)
        trans_block_structure = get_course_blocks(
            self.user,
            self.course.location,
            self.transformers,
        )

        # Should dynamically assign a block to student
        trans_keys = set(trans_block_structure.get_block_keys())
        block_key_set = self.get_block_key_set(self.blocks, 'course',
                                               'chapter1', 'lesson1',
                                               'vertical1', 'library_content1')
        for key in block_key_set:
            self.assertIn(key, trans_keys)

        vertical2_selected = self.get_block_key_set(
            self.blocks, 'vertical2').pop() in trans_keys
        vertical3_selected = self.get_block_key_set(
            self.blocks, 'vertical3').pop() in trans_keys
        self.assertTrue(vertical2_selected or vertical3_selected)

        # Check course structure again, with mocked selected modules for a user.
        with mock.patch(
                'lms.djangoapps.course_blocks.transformers.library_content.ContentLibraryTransformer._get_student_module',
                return_value=MockedModule(
                    '{"selected": [["vertical", "vertical_vertical2"]]}'),
        ):
            clear_course_from_cache(self.course.id)
            trans_block_structure = get_course_blocks(
                self.user,
                self.course.location,
                self.transformers,
            )
            self.assertEqual(
                set(trans_block_structure.get_block_keys()),
                self.get_block_key_set(self.blocks, 'course', 'chapter1',
                                       'lesson1', 'vertical1',
                                       'library_content1', 'vertical2',
                                       'html1'))
Пример #7
0
    def create_staff_context(self):
        """
        Create staff user and course blocks accessible by that user
        """
        # Create a staff user to be able to test visible_to_staff_only
        staff_user = UserFactory.create()
        CourseStaffRole(self.course.location.course_key).add_users(staff_user)

        block_structure = get_course_blocks(
            staff_user,
            self.course.location,
            BlockStructureTransformers(COURSE_BLOCK_ACCESS_TRANSFORMERS),
        )
        return {
            'request': MagicMock(),
            'block_structure': block_structure,
            'requested_fields': ['type'],
        }
Пример #8
0
    def setUp(self):
        super(TestBlockSerializerBase, self).setUp()

        self.user = UserFactory.create()
        blocks_api_transformer = BlocksAPITransformer(
            block_types_to_count=['video'],
            requested_student_view_data=['video'],
        )
        self.block_structure = get_course_blocks(
            self.user,
            self.course.location,
            BlockStructureTransformers(COURSE_BLOCK_ACCESS_TRANSFORMERS +
                                       [blocks_api_transformer]),
        )
        self.serializer_context = {
            'request': MagicMock(),
            'block_structure': self.block_structure,
            'requested_fields': ['type'],
        }
Пример #9
0
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,
):
    """
    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.
    """
    # create ordered list of transformers, adding BlocksAPITransformer at end.
    transformers = BlockStructureTransformers()
    if user is not None:
        transformers += COURSE_BLOCK_ACCESS_TRANSFORMERS + [
            ProctoredExamTransformer()
        ]
    transformers += [
        BlocksAPITransformer(block_counts, student_view_data, depth, nav_depth)
    ]

    # transform
    blocks = get_course_blocks(user, usage_key, transformers)

    # 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