Auto-open first unit in course outline if user has no completion data
This commit is contained in:
@@ -3,6 +3,7 @@ Tests for the Course Outline view and supporting views.
|
||||
"""
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
|
||||
from completion import waffle
|
||||
from completion.models import BlockCompletion
|
||||
@@ -60,8 +61,16 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
|
||||
chapter = ItemFactory.create(category='chapter', parent_location=course.location)
|
||||
sequential = ItemFactory.create(category='sequential', parent_location=chapter.location)
|
||||
sequential2 = ItemFactory.create(category='sequential', parent_location=chapter.location)
|
||||
vertical = ItemFactory.create(category='vertical', parent_location=sequential.location)
|
||||
vertical2 = ItemFactory.create(category='vertical', parent_location=sequential2.location)
|
||||
vertical = ItemFactory.create(
|
||||
category='vertical',
|
||||
parent_location=sequential.location,
|
||||
display_name="Vertical 1"
|
||||
)
|
||||
vertical2 = ItemFactory.create(
|
||||
category='vertical',
|
||||
parent_location=sequential2.location,
|
||||
display_name="Vertical 2"
|
||||
)
|
||||
course.children = [chapter]
|
||||
chapter.children = [sequential, sequential2]
|
||||
sequential.children = [vertical]
|
||||
@@ -518,6 +527,40 @@ class TestCourseOutlineResumeCourse(SharedModuleStoreTestCase, CompletionWaffleT
|
||||
content = pq(response.content)
|
||||
self.assertTrue(content('.action-resume-course').attr('href').endswith('/course/' + course.url_name))
|
||||
|
||||
@override_switch(
|
||||
'{}.{}'.format(
|
||||
waffle.WAFFLE_NAMESPACE, waffle.ENABLE_COMPLETION_TRACKING
|
||||
),
|
||||
active=True
|
||||
)
|
||||
def test_course_outline_auto_open(self):
|
||||
"""
|
||||
Tests that the course outline auto-opens to the first unit
|
||||
in a course if a user has no completion data, and to the
|
||||
last-accessed unit if a user does have completion data.
|
||||
"""
|
||||
def get_sequential_button(url, is_hidden):
|
||||
is_hidden_string = "is-hidden" if is_hidden else ""
|
||||
|
||||
return "<olclass=\"outline-itemaccordion-panel" + is_hidden_string + "\"" \
|
||||
"id=\"" + url + "_contents\"" \
|
||||
"role=\"region\"" \
|
||||
"aria-labelledby=\"" + url + "\"" \
|
||||
">"
|
||||
|
||||
with patch('openedx.features.course_experience.waffle.new_course_outline_enabled', Mock(return_value=True)):
|
||||
# Course tree
|
||||
course = self.course
|
||||
chapter = course.children[0]
|
||||
sequential1 = chapter.children[0]
|
||||
sequential2 = chapter.children[1]
|
||||
|
||||
response_content = self.client.get(course_home_url(course)).content
|
||||
stripped_response = text_type(re.sub("\\s+", "", response_content), "utf-8")
|
||||
|
||||
self.assertTrue(get_sequential_button(text_type(sequential1.location), False) in stripped_response)
|
||||
self.assertTrue(get_sequential_button(text_type(sequential2.location), True) in stripped_response)
|
||||
|
||||
|
||||
class TestCourseOutlinePreview(SharedModuleStoreTestCase):
|
||||
"""
|
||||
|
||||
@@ -162,3 +162,20 @@ def get_course_outline_block_tree(request, course_id):
|
||||
else:
|
||||
mark_last_accessed(request.user, course_key, course_outline_root_block)
|
||||
return course_outline_root_block
|
||||
|
||||
|
||||
def get_resume_block(block):
|
||||
"""
|
||||
Gets the deepest block marked as 'resume_block'.
|
||||
|
||||
"""
|
||||
if not block['resume_block']:
|
||||
return None
|
||||
if not block.get('children'):
|
||||
return block
|
||||
|
||||
for child in block['children']:
|
||||
resume_block = get_resume_block(child)
|
||||
if resume_block:
|
||||
return resume_block
|
||||
return block
|
||||
|
||||
@@ -29,7 +29,7 @@ from student.models import CourseEnrollment
|
||||
from util.views import ensure_valid_course_key
|
||||
|
||||
from .. import LATEST_UPDATE_FLAG, SHOW_UPGRADE_MSG_ON_COURSE_HOME, USE_BOOTSTRAP_FLAG
|
||||
from ..utils import get_course_outline_block_tree
|
||||
from ..utils import get_course_outline_block_tree, get_resume_block
|
||||
from .course_dates import CourseDatesFragmentView
|
||||
from .course_home_messages import CourseHomeMessageFragmentView
|
||||
from .course_outline import CourseOutlineFragmentView
|
||||
@@ -81,23 +81,6 @@ class CourseHomeFragmentView(EdxFragmentView):
|
||||
otherwise the URL of the course root.
|
||||
|
||||
"""
|
||||
|
||||
def get_resume_block(block):
|
||||
"""
|
||||
Gets the deepest block marked as 'resume_block'.
|
||||
|
||||
"""
|
||||
if not block['resume_block']:
|
||||
return None
|
||||
if not block.get('children'):
|
||||
return block
|
||||
|
||||
for child in block['children']:
|
||||
resume_block = get_resume_block(child)
|
||||
if resume_block:
|
||||
return resume_block
|
||||
return block
|
||||
|
||||
course_outline_root_block = get_course_outline_block_tree(request, course_id)
|
||||
resume_block = get_resume_block(course_outline_root_block) if course_outline_root_block else None
|
||||
has_visited_course = bool(resume_block)
|
||||
|
||||
@@ -17,7 +17,7 @@ from openedx.features.course_experience import waffle as course_experience_waffl
|
||||
from completion import waffle as completion_waffle
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
from ..utils import get_course_outline_block_tree
|
||||
from ..utils import get_course_outline_block_tree, get_resume_block
|
||||
from util.milestones_helpers import get_course_content_milestones
|
||||
|
||||
|
||||
@@ -52,8 +52,11 @@ class CourseOutlineFragmentView(EdxFragmentView):
|
||||
|
||||
# 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)
|
||||
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
|
||||
@@ -151,3 +154,9 @@ class CourseOutlineFragmentView(EdxFragmentView):
|
||||
return user_enrollment.created > begin_collection_date
|
||||
except CourseEnrollment.DoesNotExist:
|
||||
return False
|
||||
|
||||
def mark_first_unit_to_resume(self, block_node):
|
||||
children = block_node.get('children')
|
||||
if children:
|
||||
children[0]['resume_block'] = True
|
||||
self.mark_first_unit_to_resume(children[0])
|
||||
|
||||
Reference in New Issue
Block a user