Show number of problems in outline

Specifically, the number of 'problem' blocks that are graded, in
a given subsection. Also shows an icon next to the subsection if so.

AA-45
This commit is contained in:
Michael Terry
2020-03-16 15:27:45 -04:00
parent 9535c37c79
commit a199c6bfd9
3 changed files with 55 additions and 1 deletions

View File

@@ -10,7 +10,8 @@ import pytz
from datetime import date, datetime, timedelta
from django.utils import timezone
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.utils.translation import ngettext
from openedx.core.djangolib.markup import HTML, Text
%>
@@ -58,6 +59,7 @@ reset_deadlines_banner_displayed = False
needs_prereqs = not gated_content[subsection['id']]['completed_prereqs'] if gated_subsection else False
scored = 'scored' if subsection.get('scored', False) else ''
graded = 'graded' if subsection.get('graded') else ''
num_graded_problems = subsection.get('num_graded_problems', 0)
due_date = subsection.get('due')
overdue = due_date is not None and due_date < timezone.now() and not subsection.get('complete', True)
%>
@@ -77,8 +79,16 @@ reset_deadlines_banner_displayed = False
class="subsection-text outline-button"
id="${ subsection['id'] }"
>
% if num_graded_problems:
<span class="icon fa fa-pencil-square-o" aria-hidden="true"></span>
% endif
<h4 class="subsection-title">
${ subsection['display_name'] }
% if num_graded_problems:
${ngettext("({number} Question)",
"({number} Questions)",
num_graded_problems).format(number=num_graded_problems)}
% endif
</h4>
% if subsection.get('complete'):
<span class="complete-checkmark fa fa-check" aria-hidden="true"></span>

View File

@@ -135,6 +135,34 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
self.assertContains(response, sequential.format)
self.assertTrue(sequential.children)
def test_num_graded_problems(self):
course = CourseFactory.create()
with self.store.bulk_operations(course.id):
chapter = ItemFactory.create(category='chapter', parent_location=course.location)
sequential = ItemFactory.create(category='sequential', parent_location=chapter.location)
problem = ItemFactory.create(category='problem', parent_location=sequential.location)
sequential2 = ItemFactory.create(category='sequential', parent_location=chapter.location)
problem2 = ItemFactory.create(category='problem', graded=True, has_score=True,
parent_location=sequential2.location)
sequential3 = ItemFactory.create(category='sequential', parent_location=chapter.location)
problem3_1 = ItemFactory.create(category='problem', graded=True, has_score=True,
parent_location=sequential3.location)
problem3_2 = ItemFactory.create(category='problem', graded=True, has_score=True,
parent_location=sequential3.location)
course.children = [chapter]
chapter.children = [sequential, sequential2, sequential3]
sequential.children = [problem]
sequential2.children = [problem2]
sequential3.children = [problem3_1, problem3_2]
CourseEnrollment.enroll(self.user, course.id)
url = course_home_url(course)
response = self.client.get(url)
content = response.content.decode('utf8')
self.assertRegex(content, sequential.display_name + r'\s*</h4>')
self.assertRegex(content, sequential2.display_name + r'\s*\(1 Question\)\s*</h4>')
self.assertRegex(content, sequential3.display_name + r'\s*\(2 Questions\)\s*</h4>')
def test_reset_course_deadlines(self):
course = self.courses[0]
enrollment = CourseEnrollment.objects.get(course_id=course.id)

View File

@@ -136,6 +136,21 @@ def get_course_outline_block_tree(request, course_id, user=None):
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'
Must be run after recurse_mark_scored.
"""
children = block.get('children', [])
if children:
num_graded_problems = sum(recurse_num_graded_problems(child) for child in children)
else:
num_graded_problems = 1 if block.get('scored') and block.get('graded') else 0
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).
@@ -192,6 +207,7 @@ def get_course_outline_block_tree(request, course_id, user=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)
if user:
set_last_accessed_default(course_outline_root_block)