Skip to content

Commit

Permalink
Calculate completion for custom blocks on the Course Outline page (#246)
Browse files Browse the repository at this point in the history
This adds support for displaying completion on the course outline page, to remove the discrepancies between this view and the learning sequence. It also simplifies course outline page by reusing existing APIs for determining completion state and finding the "Resume block"'s target.

Co-authored-by: Agrendalath <[email protected]>
  • Loading branch information
Muhammad Haseeb and Agrendalath authored Jul 8, 2021
1 parent bde6992 commit 8f89b12
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 116 deletions.
8 changes: 3 additions & 5 deletions lms/djangoapps/course_api/blocks/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,9 @@ def __init__(
VisibilityTransformer,
requested_field_name='visible_to_staff_only',
),
SupportedFieldType(
BlockCompletionTransformer.COMPLETION,
BlockCompletionTransformer,
'completion'
)
SupportedFieldType(BlockCompletionTransformer.COMPLETION, BlockCompletionTransformer),
SupportedFieldType(BlockCompletionTransformer.COMPLETE),
SupportedFieldType(BlockCompletionTransformer.RESUME_BLOCK),
]

# This lists the names of all fields that are allowed
Expand Down
115 changes: 4 additions & 111 deletions openedx/features/course_experience/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,88 +53,6 @@ def populate_children(block, all_blocks):

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_learning_context_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).
Expand Down Expand Up @@ -182,23 +100,6 @@ def recurse_mark_auth_denial(block):
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',
'lti',
'lti_consumer',
]
all_blocks = get_blocks(
request,
course_usage_key,
Expand All @@ -214,11 +115,10 @@ def recurse_mark_auth_denial(block):
'graded',
'has_score',
'weight',
'special_exam_info',
'show_gated_sections',
'format'
'completion',
'complete',
'resume_block',
],
block_types_filter=block_types_filter,
allow_start_dates_in_future=allow_start_dates_in_future,
)

Expand All @@ -228,13 +128,6 @@ def recurse_mark_auth_denial(block):
recurse_mark_scored(course_outline_root_block)
recurse_num_graded_problems(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


Expand All @@ -243,7 +136,7 @@ def get_resume_block(block):
Gets the deepest block marked as 'resume_block'.
"""
if block.get('authorization_denial_reason') or not block['resume_block']:
if block.get('authorization_denial_reason') or not block.get('resume_block'):
return None
if not block.get('children'):
return block
Expand Down

0 comments on commit 8f89b12

Please sign in to comment.