def get_assignments_with_due_date(self, request, course_key): """ Returns a list of assignment (graded) blocks with due dates, including due date and location. Args: request (HttpRequest) course_key (CourseKey) """ course_usage_key = modulestore().make_course_usage_key(course_key) all_blocks = get_blocks( request, course_usage_key, user=request.user, nav_depth=3, requested_fields=['display_name', 'due', 'graded', 'format'], block_types_filter=['sequential']) assignment_blocks = [] for (location, block) in all_blocks['blocks'].iteritems(): if block.get('graded', False): assignment_blocks.append(block) block['due'] = block['due'].isoformat() if block.get( 'due') is not None else None block['location'] = unicode(location) return assignment_blocks
def get_assignments_with_due_date(self, request, course_key): """ Returns a list of assignment (graded) blocks with due dates, including due date and location. Args: request (HttpRequest) course_key (CourseKey) """ course_usage_key = modulestore().make_course_usage_key(course_key) all_blocks = get_blocks( request, course_usage_key, user=request.user, nav_depth=3, requested_fields=['display_name', 'due', 'graded', 'format'], block_types_filter=['sequential'] ) assignment_blocks = [] for (location, block) in all_blocks['blocks'].iteritems(): if block.get('graded', False): assignment_blocks.append(block) block['due'] = block['due'].isoformat() if block.get('due') is not None else None block['location'] = unicode(location) return assignment_blocks
def render_to_fragment(self, request, course_id=None, **kwargs): """ Renders the course outline as a fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) course_usage_key = modulestore().make_course_usage_key(course_key) all_blocks = get_blocks( request, course_usage_key, user=request.user, nav_depth=3, requested_fields=['children', 'display_name', 'type'], block_types_filter=['course', 'chapter', 'sequential'] ) course_block_tree = all_blocks['blocks'][all_blocks['root']] # Get the root of the block tree context = { 'csrf': csrf(request)['csrf_token'], 'course': course, # Recurse through the block tree, fleshing out each child object 'blocks': self.populate_children(course_block_tree, all_blocks['blocks']) } html = render_to_string('course_experience/course-outline-fragment.html', context) return Fragment(html)
def _assert_block_is_gated(store, block, is_gated, user, course, request_factory, has_upgrade_link=True): """ Asserts that a block in a specific course is gated for a specific user Arguments: block: some sort of xblock descriptor, must implement .scope_ids.usage_id is_gated (bool): if True, this user is expected to be gated from this block user (int): user course_id (CourseLocator): id of course """ checkout_link = '#' if has_upgrade_link else None for content_getter in (_get_content_from_fragment, _get_content_from_lms_index): with patch.object(ContentTypeGatingPartition, '_get_checkout_link', return_value=checkout_link): content = content_getter(store, block, user.id, course, request_factory) # pylint: disable=no-value-for-parameter if is_gated: assert 'content-paywall' in content if has_upgrade_link: assert 'certA_1' in content else: assert 'certA_1' not in content else: assert 'content-paywall' not in content fake_request = request_factory.get('') requested_fields = [ 'block_id', 'contains_gated_content', 'display_name', 'student_view_data', 'student_view_url' ] blocks = get_blocks(fake_request, course.location, user=user, requested_fields=requested_fields, student_view_data=['html']) course_api_block = blocks['blocks'][str(block.location)] assert course_api_block.get('contains_gated_content', False) == is_gated if is_gated: assert 'authorization_denial_reason' in course_api_block assert 'block_id' in course_api_block assert 'display_name' in course_api_block assert 'student_view_data' not in course_api_block assert 'student_view_url' in course_api_block else: assert 'authorization_denial_reason' not in course_api_block if block.category == 'html': assert 'student_view_data' in course_api_block
def get(self, request, username, course_key, subsection_id): """ Returns completion for a (user, subsection, course). """ def get_completion(course_completions, all_blocks, block_id): """ Recursively get the aggregate completion for a subsection, given the subsection block and a list of all blocks. Parameters: course_completions: a dictionary of completion values by block IDs all_blocks: a dictionary of the block structure for a subsection block_id: an ID of a block for which to get completion """ block = all_blocks.get(block_id) child_ids = block.get('children', []) if not child_ids: return course_completions.get(block.serializer.instance, 0) completion = 0 total_children = 0 for child_id in child_ids: completion += get_completion(course_completions, all_blocks, child_id) total_children += 1 return int(completion == total_children) user_id = User.objects.get(username=username).id block_types_filter = [ 'course', 'chapter', 'sequential', 'vertical', 'html', 'problem', 'video', 'discussion', 'drag-and-drop-v2' ] blocks = get_blocks( request, UsageKey.from_string(subsection_id), nav_depth=2, requested_fields=[ 'children' ], block_types_filter=block_types_filter ) course_key = LearningContextKey.from_string(course_key) context_completions = BlockCompletion.get_learning_context_completions(user_id, course_key) aggregated_completion = get_completion(context_completions, blocks['blocks'], blocks['root']) return Response({"completion": aggregated_completion}, status=status.HTTP_200_OK)
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 """ locatable_block_string = BlockUsageLocator.from_string(block['id']) if course_block_completions.get(locatable_block_string): block['complete'] = True if locatable_block_string == latest_completion.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) 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=[ 'course', 'chapter', 'sequential', 'vertical', 'html', 'problem', 'video', 'discussion' ] ) 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
def get_course_outline_block_tree(request, course_id, user=None): """ Returns the root block of the course outline, with children as blocks. """ assert user is None or user.is_authenticated 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].get('resume_block') is True: block['resume_block'] = True completable_blocks = [ child for child in block['children'] if child.get('type') != 'discussion' ] if all(child.get('complete') for child in completable_blocks): 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 def recurse_mark_scored(block): """ Mark this block as 'scored' if any of its descendents are 'scored' (that is, 'has_score' and 'weight' > 0). """ is_scored = block.get('has_score', False) and block.get('weight', 1) > 0 # Use a list comprehension to force the recursion over all children, rather than just stopping # at the first child that is scored. children_scored = any([ recurse_mark_scored(child) for child in block.get('children', []) ]) if is_scored or children_scored: block['scored'] = True return True else: block['scored'] = False return False def recurse_mark_auth_denial(block): """ Mark this block as 'scored' if any of its descendents are 'scored' (that is, 'has_score' and 'weight' > 0). """ own_denial_reason = { block['authorization_denial_reason'] } if 'authorization_denial_reason' in block else set() # Use a list comprehension to force the recursion over all children, rather than just stopping # at the first child that is scored. child_denial_reasons = own_denial_reason.union( *(recurse_mark_auth_denial(child) for child in block.get('children', []))) if child_denial_reasons: block['all_denial_reasons'] = child_denial_reasons return child_denial_reasons 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', 'poll', 'word_cloud' ] all_blocks = get_blocks(request, course_usage_key, user=request.user, nav_depth=3, requested_fields=[ 'children', 'display_name', 'type', 'due', 'graded', 'has_score', 'weight', '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']) recurse_mark_scored(course_outline_root_block) recurse_mark_auth_denial(course_outline_root_block) if user: set_last_accessed_default(course_outline_root_block) mark_blocks_completed(block=course_outline_root_block, user=request.user, course_key=course_key) return course_outline_root_block
def get_course_outline_block_tree(request, course_id, user=None): """ Returns the root block of the course outline, with children as blocks. """ assert user is None or user.is_authenticated 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].get('resume_block') is True: block['resume_block'] = True completable_blocks = [child for child in block['children'] if child.get('type') != 'discussion'] if all(child.get('complete') for child in completable_blocks): 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', 'poll', 'word_cloud' ] 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']) if user: set_last_accessed_default(course_outline_root_block) mark_blocks_completed( block=course_outline_root_block, user=request.user, course_key=course_key ) return course_outline_root_block
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 last_accessed on all blocks. """ block['last_accessed'] = False for child in block.get('children', []): set_last_accessed_default(child) 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['last_accessed'] = True if last_accessed_child_position <= len(block['children']): last_accessed_child_block = block['children'][ last_accessed_child_position - 1] last_accessed_child_block['last_accessed'] = 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]['last_accessed'] = True course_key = CourseKey.from_string(course_id) course_usage_key = modulestore().make_course_usage_key(course_key) 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', 'format' ], block_types_filter=['course', 'chapter', 'sequential']) course_outline_root_block = all_blocks['blocks'][all_blocks['root']] populate_children(course_outline_root_block, all_blocks['blocks']) set_last_accessed_default(course_outline_root_block) mark_last_accessed(request.user, course_key, course_outline_root_block) return course_outline_root_block
def get_course_outline_block_tree(request, course_id, user=None, allow_start_dates_in_future=False): # lint-amnesty, pylint: disable=too-many-statements """ Returns the root block of the course outline, with children as blocks. 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. """ assert user is None or user.is_authenticated 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 recurse_mark_scored(block): """ Mark this block as 'scored' if any of its descendents are 'scored' (that is, 'has_score' and 'weight' > 0). """ is_scored = block.get('has_score', False) and block.get('weight', 1) > 0 # Use a list comprehension to force the recursion over all children, rather than just stopping # at the first child that is scored. children_scored = any( recurse_mark_scored(child) for child in block.get('children', [])) if is_scored or children_scored: block['scored'] = True return True else: block['scored'] = False return False def recurse_num_graded_problems(block): """ Marks each block with the number of graded and scored leaf blocks below it as 'num_graded_problems' """ is_scored = block.get('has_score') and block.get('weight', 1) > 0 is_graded = block.get('graded') is_countable = block.get('type') not in ('lti', 'lti_consumer') is_graded_problem = is_scored and is_graded and is_countable num_graded_problems = 1 if is_graded_problem else 0 num_graded_problems += sum( recurse_num_graded_problems(child) for child in block.get('children', [])) block['num_graded_problems'] = num_graded_problems return num_graded_problems def recurse_mark_auth_denial(block): """ Mark this block as 'scored' if any of its descendents are 'scored' (that is, 'has_score' and 'weight' > 0). """ own_denial_reason = { block['authorization_denial_reason'] } if 'authorization_denial_reason' in block else set() # Use a list comprehension to force the recursion over all children, rather than just stopping # at the first child that is scored. child_denial_reasons = own_denial_reason.union( *(recurse_mark_auth_denial(child) for child in block.get('children', []))) if child_denial_reasons: block['all_denial_reasons'] = child_denial_reasons return child_denial_reasons course_key = CourseKey.from_string(course_id) course_usage_key = modulestore().make_course_usage_key(course_key) all_blocks = get_blocks( request, course_usage_key, user=user, nav_depth=3, requested_fields=[ 'children', 'contains_gated_content', 'display_name', 'due', 'effort_activities', 'effort_time', 'format', 'graded', 'has_scheduled_content', 'has_score', 'show_gated_sections', 'special_exam_info', 'start', 'type', 'weight', 'completion', 'complete', 'resume_block', ], allow_start_dates_in_future=allow_start_dates_in_future, ) 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']) recurse_mark_scored(course_outline_root_block) recurse_num_graded_problems(course_outline_root_block) recurse_mark_auth_denial(course_outline_root_block) return course_outline_root_block
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 last_accessed on all blocks. """ block['last_accessed'] = False for child in block.get('children', []): set_last_accessed_default(child) 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['last_accessed'] = True if last_accessed_child_position <= len(block['children']): last_accessed_child_block = block['children'][last_accessed_child_position - 1] last_accessed_child_block['last_accessed'] = 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]['last_accessed'] = True course_key = CourseKey.from_string(course_id) course_usage_key = modulestore().make_course_usage_key(course_key) 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', 'format'], block_types_filter=['course', 'chapter', 'sequential'] ) 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) mark_last_accessed(request.user, course_key, course_outline_root_block) return course_outline_root_block
def get(self, request, username, course_id): """ Gets a progress information. Args: request (Request): Django request object. username (string): URI element specifying the user's username. course_id (string): URI element specifying the course location. Return: A JSON serialized representation of the certificate. """ def aggregate_progress(course_completions, all_blocks, block_id): """ Recursively get the progress for a units (vertical), given list of all blocks. Parameters: course_completions: a dictionary of completion values by block IDs all_blocks: a dictionary of the block structure for a subsection block_id: an ID of a block for which to get completion """ block = all_blocks.get(block_id) child_ids = block.get('children', []) if block.get('type', None) == 'vertical': self.units_progress_list.append([block_id, 0, 0]) if not child_ids and (block.get('type', None) in block_xblocks_types_filter): self.units_progress_list[-1][1] += 1 self.units_progress_list[-1][2] += course_completions.get(block.serializer.instance, 0) for child_id in child_ids: aggregate_progress(course_completions, all_blocks, child_id) def calculate_progress(): """ Calculate course progress from units progress """ number_of_units = len(self.units_progress_list) if number_of_units == 0: return float(0.0) else: cumulative_sum = 0 for unit_progress in self.units_progress_list: if unit_progress[1] == 0: number_of_units -= 1 else: cumulative_sum += unit_progress[2]/unit_progress[1] return round(cumulative_sum/number_of_units, 3) course_object_id = CourseKey.from_string(course_id) self.check_object_permissions(self.request, courses.get_course_by_id(course_object_id)) course_usage_key = modulestore().make_course_usage_key(course_object_id) try: user_id = User.objects.get(username=username).id except User.DoesNotExist: return Response( status=404, data={'error_code': u'Not found.'} ) block_navigation_types_filter = [ 'course', 'chapter', 'sequential', 'vertical', ] block_xblocks_types_filter = [ 'html', 'problem', 'video', 'drag-and-drop-v2', 'poll', 'videojs', 'embedded_answers', 'inline-dropdown', 'openassessment', 'audioplayer', ] block_types_filter = block_navigation_types_filter + block_xblocks_types_filter try: blocks = get_blocks(request, course_usage_key, nav_depth=3, requested_fields=[ 'children', 'type', ], block_types_filter=block_types_filter ) except ItemNotFoundError: return Response( status=404, data={'error_code': u'Not found.'} ) course_completions = BlockCompletion.get_course_completions(user_id, course_object_id) aggregate_progress(course_completions, blocks['blocks'], blocks['root']) calculated_progress = calculate_progress() response_dict = {"username": username, "course_id": course_id, "completion_value": calculated_progress} return Response(response_dict, status=status.HTTP_200_OK)