Example #1
0
 def test_visual_progress_happy_path_visual_switch_disabled(self):
     self._make_site_config(True)
     with waffle.waffle().override(waffle.ENABLE_COMPLETION_TRACKING, True):
         with waffle.waffle().override(waffle.ENABLE_VISUAL_PROGRESS,
                                       False):
             with override_waffle_flag(waffle.waffle_flag(), active=True):
                 assert waffle.visual_progress_enabled(
                     self.course_key) is True
Example #2
0
    def render_to_fragment(self,
                           request,
                           course_id=None,
                           page_context=None,
                           **kwargs):
        """
        Renders the course outline as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course_overview = get_course_overview_with_access(
            request.user, 'load', course_key, check_if_enrolled=True)

        course_block_tree = get_course_outline_block_tree(request, course_id)
        if not course_block_tree:
            return None

        # TODO: EDUCATOR-2283 Remove 'show_visual_progress' from context
        # and remove the check for it in the HTML file
        show_visual_progress = (
            completion_waffle.visual_progress_enabled(course_key)
            and self.user_enrolled_after_completion_collection(
                request.user, course_key))
        context = {
            'csrf': csrf(request)['csrf_token'],
            'course': course_overview,
            'blocks': course_block_tree,
            'show_visual_progress': show_visual_progress
        }

        # TODO: EDUCATOR-2283 Remove this check when the waffle flag is turned on in production
        if course_experience_waffle.new_course_outline_enabled(
                course_key=course_key):
            resume_block = get_resume_block(course_block_tree)
            if not resume_block:
                self.mark_first_unit_to_resume(course_block_tree)

            xblock_display_names = self.create_xblock_id_and_name_dict(
                course_block_tree)
            gated_content = self.get_content_milestones(request, course_key)

            context['gated_content'] = gated_content
            context['xblock_display_names'] = xblock_display_names

            # TODO: EDUCATOR-2283 Rename this file to course-outline-fragment.html
            html = render_to_string(
                'course_experience/course-outline-fragment-new.html', context)
            return Fragment(html)
        else:
            content_milestones = self.get_content_milestones_old(
                request, course_key)

            context['gated_content'] = content_milestones

            # TODO: EDUCATOR-2283 Remove this file
            html = render_to_string(
                'course_experience/course-outline-fragment-old.html', context)
            return Fragment(html)
    def render_to_fragment(self, request, course_id=None, page_context=None, **kwargs):
        """
        Renders the course outline as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course_overview = get_course_overview_with_access(request.user, 'load', course_key, check_if_enrolled=True)

        course_block_tree = get_course_outline_block_tree(request, course_id)
        if not course_block_tree:
            return None

        # TODO: EDUCATOR-2283 Remove 'show_visual_progress' from context
        # and remove the check for it in the HTML file
        show_visual_progress = (
            completion_waffle.visual_progress_enabled(course_key) and
            self.user_enrolled_after_completion_collection(request.user, course_key)
        )
        context = {
            'csrf': csrf(request)['csrf_token'],
            'course': course_overview,
            'blocks': course_block_tree,
            'show_visual_progress': show_visual_progress
        }

        # TODO: EDUCATOR-2283 Remove this check when the waffle flag is turned on in production
        if course_experience_waffle.new_course_outline_enabled(course_key=course_key):
            xblock_display_names = self.create_xblock_id_and_name_dict(course_block_tree)

            gated_content = self.get_content_milestones(request, course_key)

            context['gated_content'] = gated_content
            context['xblock_display_names'] = xblock_display_names

            # TODO: EDUCATOR-2283 Rename this file to course-outline-fragment.html
            html = render_to_string('course_experience/course-outline-fragment-new.html', context)
            return Fragment(html)
        else:
            content_milestones = self.get_content_milestones_old(request, course_key)

            context['gated_content'] = content_milestones

            # TODO: EDUCATOR-2283 Remove this file
            html = render_to_string('course_experience/course-outline-fragment-old.html', context)
            return Fragment(html)
Example #4
0
def get_course_outline_block_tree(request, course_id):
    """
    Returns the root block of the course outline, with children as blocks.
    """
    def populate_children(block, all_blocks):
        """
        Replace each child id with the full block for the child.

        Given a block, replaces each id in its children array with the full
        representation of that child, which will be looked up by id in the
        passed all_blocks dict. Recursively do the same replacement for children
        of those children.
        """
        children = block.get('children', [])

        for i in range(len(children)):
            child_id = block['children'][i]
            child_detail = populate_children(all_blocks[child_id], all_blocks)
            block['children'][i] = child_detail

        return block

    def set_last_accessed_default(block):
        """
        Set default of False for resume_block on all blocks.
        """
        block['resume_block'] = False
        block['complete'] = False
        for child in block.get('children', []):
            set_last_accessed_default(child)

    def mark_blocks_completed(block, user, course_key):
        """
        Walk course tree, marking block completion.
        Mark 'most recent completed block as 'resume_block'

        """
        last_completed_child_position = BlockCompletion.get_latest_block_completed(
            user, course_key)

        if last_completed_child_position:
            # Mutex w/ NOT 'course_block_completions'
            recurse_mark_complete(
                course_block_completions=BlockCompletion.
                get_course_completions(user, course_key),
                latest_completion=last_completed_child_position,
                block=block)

    def recurse_mark_complete(course_block_completions, latest_completion,
                              block):
        """
        Helper function to walk course tree dict,
        marking blocks as 'complete' and 'last_complete'

        If all blocks are complete, mark parent block complete
        mark parent blocks of 'last_complete' as 'last_complete'

        :param course_block_completions: dict[course_completion_object] =  completion_value
        :param latest_completion: course_completion_object
        :param block: course_outline_root_block block object or child block

        :return:
            block: course_outline_root_block block object or child block
        """
        block_key = block.serializer.instance

        if course_block_completions.get(block_key):
            block['complete'] = True
            if block_key == latest_completion.full_block_key:
                block['resume_block'] = True

        if block.get('children'):
            for idx in range(len(block['children'])):
                recurse_mark_complete(course_block_completions,
                                      latest_completion,
                                      block=block['children'][idx])
                if block['children'][idx]['resume_block'] is True:
                    block['resume_block'] = True

            if len([
                    child['complete'] for child in block['children']
                    if child['complete']
            ]) == len(block['children']):
                block['complete'] = True

    def mark_last_accessed(user, course_key, block):
        """
        Recursively marks the branch to the last accessed block.
        """
        block_key = block.serializer.instance
        student_module_dict = get_student_module_as_dict(
            user, course_key, block_key)

        last_accessed_child_position = student_module_dict.get('position')
        if last_accessed_child_position and block.get('children'):
            block['resume_block'] = True
            if last_accessed_child_position <= len(block['children']):
                last_accessed_child_block = block['children'][
                    last_accessed_child_position - 1]
                last_accessed_child_block['resume_block'] = True
                mark_last_accessed(user, course_key, last_accessed_child_block)
            else:
                # We should be using an id in place of position for last accessed.
                # However, while using position, if the child block is no longer accessible
                # we'll use the last child.
                block['children'][-1]['resume_block'] = True

    course_key = CourseKey.from_string(course_id)
    course_usage_key = modulestore().make_course_usage_key(course_key)

    # Deeper query for course tree traversing/marking complete
    # and last completed block
    block_types_filter = [
        'course', 'chapter', 'sequential', 'vertical', 'html', 'problem',
        'video', 'discussion', 'drag-and-drop-v2'
    ]
    all_blocks = get_blocks(request,
                            course_usage_key,
                            user=request.user,
                            nav_depth=3,
                            requested_fields=[
                                'children', 'display_name', 'type', 'due',
                                'graded', 'special_exam_info',
                                'show_gated_sections', 'format'
                            ],
                            block_types_filter=block_types_filter)

    course_outline_root_block = all_blocks['blocks'].get(
        all_blocks['root'], None)
    if course_outline_root_block:
        populate_children(course_outline_root_block, all_blocks['blocks'])
        set_last_accessed_default(course_outline_root_block)

        if visual_progress_enabled(course_key=course_key):
            mark_blocks_completed(block=course_outline_root_block,
                                  user=request.user,
                                  course_key=course_key)
        else:
            mark_last_accessed(request.user, course_key,
                               course_outline_root_block)
    return course_outline_root_block
Example #5
0
 def test_visual_progress_happy_path_with_site_config(self):
     self._make_site_config(True)
     with waffle.waffle().override(waffle.ENABLE_COMPLETION_TRACKING, True):
         with waffle.waffle().override(waffle.ENABLE_VISUAL_PROGRESS, True):
             assert waffle.visual_progress_enabled(self.course_key) is True
Example #6
0
 def test_visual_progress_gating_site_disabled(self):
     self._make_site_config(False)
     assert waffle.visual_progress_enabled(self.course_key) is False
Example #7
0
 def test_visual_progress_gating_tracking_disabled(self):
     with waffle.waffle().override(waffle.ENABLE_COMPLETION_TRACKING,
                                   False):
         assert waffle.visual_progress_enabled(self.course_key) is False
Example #8
0
def get_course_outline_block_tree(request, course_id):
    """
    Returns the root block of the course outline, with children as blocks.
    """

    def populate_children(block, all_blocks):
        """
        Replace each child id with the full block for the child.

        Given a block, replaces each id in its children array with the full
        representation of that child, which will be looked up by id in the
        passed all_blocks dict. Recursively do the same replacement for children
        of those children.
        """
        children = block.get('children', [])

        for i in range(len(children)):
            child_id = block['children'][i]
            child_detail = populate_children(all_blocks[child_id], all_blocks)
            block['children'][i] = child_detail

        return block

    def set_last_accessed_default(block):
        """
        Set default of False for resume_block on all blocks.
        """
        block['resume_block'] = False
        block['complete'] = False
        for child in block.get('children', []):
            set_last_accessed_default(child)

    def mark_blocks_completed(block, user, course_key):
        """
        Walk course tree, marking block completion.
        Mark 'most recent completed block as 'resume_block'

        """
        last_completed_child_position = BlockCompletion.get_latest_block_completed(user, course_key)

        if last_completed_child_position:
            # Mutex w/ NOT 'course_block_completions'
            recurse_mark_complete(
                course_block_completions=BlockCompletion.get_course_completions(user, course_key),
                latest_completion=last_completed_child_position,
                block=block
            )

    def recurse_mark_complete(course_block_completions, latest_completion, block):
        """
        Helper function to walk course tree dict,
        marking blocks as 'complete' and 'last_complete'

        If all blocks are complete, mark parent block complete
        mark parent blocks of 'last_complete' as 'last_complete'

        :param course_block_completions: dict[course_completion_object] =  completion_value
        :param latest_completion: course_completion_object
        :param block: course_outline_root_block block object or child block

        :return:
            block: course_outline_root_block block object or child block
        """
        block_key = block.serializer.instance

        if course_block_completions.get(block_key):
            block['complete'] = True
            if block_key == latest_completion.full_block_key:
                block['resume_block'] = True

        if block.get('children'):
            for idx in range(len(block['children'])):
                recurse_mark_complete(
                    course_block_completions,
                    latest_completion,
                    block=block['children'][idx]
                )
                if block['children'][idx]['resume_block'] is True:
                    block['resume_block'] = True

            if len([child['complete'] for child in block['children'] if child['complete']]) == len(block['children']):
                block['complete'] = True

    def mark_last_accessed(user, course_key, block):
        """
        Recursively marks the branch to the last accessed block.
        """
        block_key = block.serializer.instance
        student_module_dict = get_student_module_as_dict(user, course_key, block_key)

        last_accessed_child_position = student_module_dict.get('position')
        if last_accessed_child_position and block.get('children'):
            block['resume_block'] = True
            if last_accessed_child_position <= len(block['children']):
                last_accessed_child_block = block['children'][last_accessed_child_position - 1]
                last_accessed_child_block['resume_block'] = True
                mark_last_accessed(user, course_key, last_accessed_child_block)
            else:
                # We should be using an id in place of position for last accessed.
                # However, while using position, if the child block is no longer accessible
                # we'll use the last child.
                block['children'][-1]['resume_block'] = True

    course_key = CourseKey.from_string(course_id)
    course_usage_key = modulestore().make_course_usage_key(course_key)

    # Deeper query for course tree traversing/marking complete
    # and last completed block
    block_types_filter = [
        'course',
        'chapter',
        'sequential',
        'vertical',
        'html',
        'problem',
        'video',
        'discussion'
    ]
    all_blocks = get_blocks(
        request,
        course_usage_key,
        user=request.user,
        nav_depth=3,
        requested_fields=[
            'children',
            'display_name',
            'type',
            'due',
            'graded',
            'special_exam_info',
            'show_gated_sections',
            'format'
        ],
        block_types_filter=block_types_filter
    )

    course_outline_root_block = all_blocks['blocks'].get(all_blocks['root'], None)
    if course_outline_root_block:
        populate_children(course_outline_root_block, all_blocks['blocks'])
        set_last_accessed_default(course_outline_root_block)

        if visual_progress_enabled(course_key=course_key):
            mark_blocks_completed(
                block=course_outline_root_block,
                user=request.user,
                course_key=course_key
            )
        else:
            mark_last_accessed(request.user, course_key, course_outline_root_block)
    return course_outline_root_block