def get_subsection_completion_percentage(subsection_usage_key, user): """ Computes completion percentage for a subsection in a given course for a user Arguments: subsection_usage_key: key of subsection user: The user whose completion percentage needs to be computed Returns: User's completion percentage for given subsection """ subsection_completion_percentage = 0.0 try: subsection_structure = get_course_blocks(user, subsection_usage_key) if any(subsection_structure): completable_blocks = [ block for block in subsection_structure if block.block_type not in ['chapter', 'sequential', 'vertical'] ] if not completable_blocks: return 0 subsection_completion_total = 0 course_block_completions = BlockCompletion.get_course_completions(user, subsection_usage_key.course_key) for block in completable_blocks: if course_block_completions.get(block): subsection_completion_total += course_block_completions.get(block) subsection_completion_percentage = min( 100 * (subsection_completion_total / float(len(completable_blocks))), 100 ) except ItemNotFoundError as err: log.warning("Could not find course_block for subsection=%s error=%s", subsection_usage_key, err) return subsection_completion_percentage
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_completions = BlockCompletion.get_course_completions(user_id, CourseKey.from_string(course_key)) aggregated_completion = get_completion(course_completions, blocks['blocks'], blocks['root']) return Response({"completion": aggregated_completion}, status=status.HTTP_200_OK)
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 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 get_subsection_completion_percentage(subsection_usage_key, user): """ Computes completion percentage for a subsection in a given course for a user Arguments: subsection_usage_key: key of subsection user: The user whose completion percentage needs to be computed Returns: User's completion percentage for given subsection """ subsection_completion_percentage = 0.0 try: subsection_structure = get_course_blocks(user, subsection_usage_key) if any(subsection_structure): completable_blocks = [] for block in subsection_structure: completion_mode = subsection_structure.get_xblock_field( block, 'completion_mode') # always exclude html blocks (in addition to EXCLUDED blocks) for gating calculations # See https://openedx.atlassian.net/browse/WL-1798 if completion_mode not in (CompletionMode.AGGREGATOR, CompletionMode.EXCLUDED) \ and not block.block_type == 'html': completable_blocks.append(block) if not completable_blocks: return 100 subsection_completion_total = 0 course_block_completions = BlockCompletion.get_course_completions( user, subsection_usage_key.course_key) for block in completable_blocks: if course_block_completions.get(block): subsection_completion_total += course_block_completions.get( block) subsection_completion_percentage = min( 100 * (subsection_completion_total / float(len(completable_blocks))), 100) except ItemNotFoundError as err: log.warning("Could not find course_block for subsection=%s error=%s", subsection_usage_key, err) return subsection_completion_percentage
def get_subsection_completion_percentage(subsection_usage_key, user): """ Computes completion percentage for a subsection in a given course for a user Arguments: subsection_usage_key: key of subsection user: The user whose completion percentage needs to be computed Returns: User's completion percentage for given subsection """ subsection_completion_percentage = 0.0 try: subsection_structure = get_course_blocks(user, subsection_usage_key) if any(subsection_structure): completable_blocks = [] for block in subsection_structure: completion_mode = subsection_structure.get_xblock_field( block, 'completion_mode') if completion_mode not in (CompletionMode.AGGREGATOR, CompletionMode.EXCLUDED): completable_blocks.append(block) if not completable_blocks: return 0 subsection_completion_total = 0 course_block_completions = BlockCompletion.get_course_completions( user, subsection_usage_key.course_key) for block in completable_blocks: if course_block_completions.get(block): subsection_completion_total += course_block_completions.get( block) subsection_completion_percentage = min( 100 * (subsection_completion_total / float(len(completable_blocks))), 100) except ItemNotFoundError as err: log.warning("Could not find course_block for subsection=%s error=%s", subsection_usage_key, err) return subsection_completion_percentage
def get_subsection_completion_percentage(subsection_usage_key, user): """ Computes completion percentage for a subsection in a given course for a user Arguments: subsection_usage_key: key of subsection user: The user whose completion percentage needs to be computed Returns: User's completion percentage for given subsection """ subsection_completion_percentage = 0.0 try: subsection_structure = get_course_blocks(user, subsection_usage_key) if any(subsection_structure): completable_blocks = [] for block in subsection_structure: completion_mode = subsection_structure.get_xblock_field( block, 'completion_mode' ) # always exclude html blocks (in addition to EXCLUDED blocks) for gating calculations # See https://openedx.atlassian.net/browse/WL-1798 if completion_mode not in (CompletionMode.AGGREGATOR, CompletionMode.EXCLUDED) \ and not block.block_type == 'html': completable_blocks.append(block) if not completable_blocks: return 100 subsection_completion_total = 0 course_block_completions = BlockCompletion.get_course_completions(user, subsection_usage_key.course_key) for block in completable_blocks: if course_block_completions.get(block): subsection_completion_total += course_block_completions.get(block) subsection_completion_percentage = min( 100 * (subsection_completion_total / float(len(completable_blocks))), 100 ) except ItemNotFoundError as err: log.warning(u"Could not find course_block for subsection=%s error=%s", subsection_usage_key, err) return subsection_completion_percentage
def get_subsection_completion_percentage(subsection_usage_key, user): """ Computes completion percentage for a subsection in a given course for a user Arguments: subsection_usage_key: key of subsection user: The user whose completion percentage needs to be computed Returns: User's completion percentage for given subsection """ subsection_completion_percentage = 0.0 try: subsection_structure = get_course_blocks(user, subsection_usage_key) if any(subsection_structure): completable_blocks = [] for block in subsection_structure: completion_mode = subsection_structure.get_xblock_field( block, 'completion_mode' ) if completion_mode not in (CompletionMode.AGGREGATOR, CompletionMode.EXCLUDED): completable_blocks.append(block) if not completable_blocks: return 0 subsection_completion_total = 0 course_block_completions = BlockCompletion.get_course_completions(user, subsection_usage_key.course_key) for block in completable_blocks: if course_block_completions.get(block): subsection_completion_total += course_block_completions.get(block) subsection_completion_percentage = min( 100 * (subsection_completion_total / float(len(completable_blocks))), 100 ) except ItemNotFoundError as err: log.warning("Could not find course_block for subsection=%s error=%s", subsection_usage_key, err) return subsection_completion_percentage
def get_subsection_completion_percentage(subsection_usage_key, user): """ Computes completion percentage for a subsection in a given course for a user Arguments: subsection_usage_key: key of subsection user: The user whose completion percentage needs to be computed Returns: User's completion percentage for given subsection """ subsection_completion_percentage = 0.0 try: subsection_structure = get_course_blocks(user, subsection_usage_key) if any(subsection_structure): completable_blocks = [ block for block in subsection_structure if block.block_type not in ['chapter', 'sequential', 'vertical'] ] if not completable_blocks: return 0 subsection_completion_total = 0 course_block_completions = BlockCompletion.get_course_completions( user, subsection_usage_key.course_key) for block in completable_blocks: if course_block_completions.get(block): subsection_completion_total += course_block_completions.get( block) subsection_completion_percentage = min( 100 * (subsection_completion_total / float(len(completable_blocks))), 100) except ItemNotFoundError as err: log.warning("Could not find course_block for subsection=%s error=%s", subsection_usage_key, err) return subsection_completion_percentage
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)