Merge pull request #17484 from edx/sofiya/educator-2203
Add visual progress checkmarks to course outline
This commit is contained in:
@@ -365,8 +365,6 @@
|
||||
@include margin-left(16px);
|
||||
|
||||
list-style-type: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
|
||||
a.outline-item {
|
||||
display: flex;
|
||||
@@ -377,6 +375,22 @@
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
|
||||
a.outline-item:hover {
|
||||
text-decoration: none;
|
||||
|
||||
.vertical-details {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.complete-checkmark {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.vertical-details {
|
||||
float:left;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: palette(primary, x-back);
|
||||
@@ -399,11 +413,11 @@
|
||||
|
||||
// Course outline accordion
|
||||
button.accordion-trigger, button.prerequisite-button {
|
||||
margin: 2px;
|
||||
padding: 10px 0 10px 0;
|
||||
border: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
margin: 0px;
|
||||
|
||||
.fa {
|
||||
color: $blue;
|
||||
@@ -414,6 +428,15 @@ button.accordion-trigger, button.prerequisite-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fa-check.fa.complete-checkmark {
|
||||
border: 1px solid $green;
|
||||
margin-right: $baseline / 2;
|
||||
border-radius: 100px;
|
||||
color: white;
|
||||
background-color: $green;
|
||||
float: right;
|
||||
}
|
||||
|
||||
// Course outline
|
||||
.course-outline {
|
||||
.block-tree {
|
||||
|
||||
@@ -27,6 +27,9 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
id="${ section['id'] }">
|
||||
<span class="fa fa-chevron-right ${ 'fa-rotate-90' if section_is_auto_opened else '' }" aria-hidden="true"></span>
|
||||
<h3 class="section-title">${ section['display_name'] }</h3>
|
||||
% if show_visual_progress and section.get('complete'):
|
||||
<span class="complete-checkmark fa fa-check"></span>
|
||||
% endif
|
||||
</button>
|
||||
<ol class="outline-item accordion-panel ${ '' if section_is_auto_opened else 'is-hidden' }"
|
||||
id="${ section['id'] }_contents"
|
||||
@@ -70,7 +73,10 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<span class="subsection-title">
|
||||
${ subsection['display_name'] }
|
||||
</span>
|
||||
% endif
|
||||
% if subsection.get('complete') and show_visual_progress:
|
||||
<span class="complete-checkmark fa fa-check"></span>
|
||||
% endif
|
||||
% endif
|
||||
<div class="details">
|
||||
|
||||
## There are behavior differences between rendering of subsections which have
|
||||
@@ -150,6 +156,9 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
${ vertical['display_name'] }
|
||||
</span>
|
||||
</div>
|
||||
% if vertical.get('complete') and show_visual_progress:
|
||||
<span class="complete-checkmark fa fa-check"></span>
|
||||
% endif
|
||||
</a>
|
||||
</li>
|
||||
% endfor
|
||||
|
||||
@@ -379,12 +379,13 @@ class TestCourseOutlineResumeCourse(SharedModuleStoreTestCase, CompletionWaffleT
|
||||
# first navigate to a sequential to make it the last accessed
|
||||
chapter = course.children[0]
|
||||
sequential = chapter.children[0]
|
||||
vertical = sequential.children[0]
|
||||
self.visit_sequential(course, chapter, sequential)
|
||||
|
||||
# check resume course buttons
|
||||
response = self.visit_course_home(course, resume_count=2)
|
||||
content = pq(response.content)
|
||||
self.assertTrue(content('.action-resume-course').attr('href').endswith('/sequential/' + sequential.url_name))
|
||||
self.assertTrue(content('.action-resume-course').attr('href').endswith('/vertical/' + vertical.url_name))
|
||||
|
||||
@override_switch(
|
||||
'{}.{}'.format(
|
||||
|
||||
@@ -6,7 +6,7 @@ from completion.waffle import visual_progress_enabled
|
||||
|
||||
from lms.djangoapps.course_api.blocks.api import get_blocks
|
||||
from lms.djangoapps.course_blocks.utils import get_student_module_as_dict
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from openedx.core.djangoapps.request_cache.middleware import request_cached
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
@@ -118,26 +118,18 @@ def get_course_outline_block_tree(request, course_id):
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
course_usage_key = modulestore().make_course_usage_key(course_key)
|
||||
|
||||
if visual_progress_enabled(course_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'
|
||||
]
|
||||
else:
|
||||
# Shallower query is sufficient for last accessed block
|
||||
block_types_filter = [
|
||||
'course',
|
||||
'chapter',
|
||||
'sequential'
|
||||
]
|
||||
# Deeper query for course tree traversing/marking complete
|
||||
# and last completed block
|
||||
block_types_filter = [
|
||||
'course',
|
||||
'chapter',
|
||||
'sequential',
|
||||
'vertical',
|
||||
'html',
|
||||
'problem',
|
||||
'video',
|
||||
'discussion'
|
||||
]
|
||||
all_blocks = get_blocks(
|
||||
request,
|
||||
course_usage_key,
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
Views to show a course outline.
|
||||
"""
|
||||
import re
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.context_processors import csrf
|
||||
from django.template.loader import render_to_string
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
@@ -10,7 +13,9 @@ from web_fragments.fragment import Fragment
|
||||
|
||||
from courseware.courses import get_course_overview_with_access
|
||||
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
|
||||
from openedx.features.course_experience import waffle as waffle
|
||||
from openedx.features.course_experience import waffle as course_experience_waffle
|
||||
from completion import waffle as completion_waffle
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
from ..utils import get_course_outline_block_tree
|
||||
from util.milestones_helpers import get_course_content_milestones
|
||||
@@ -32,14 +37,19 @@ class CourseOutlineFragmentView(EdxFragmentView):
|
||||
if not course_block_tree:
|
||||
return None
|
||||
|
||||
# TODO: EDUCATOR-2283 Remove 'show_visual_progress' from context
|
||||
# and remove the check for it in the HTML file
|
||||
show_visual_progress = (completion_waffle.visual_progress_enabled(course_key) and
|
||||
self.user_enrolled_after_completion_collection(request.user, course_key))
|
||||
context = {
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'course': course_overview,
|
||||
'blocks': course_block_tree
|
||||
'blocks': course_block_tree,
|
||||
'show_visual_progress': show_visual_progress
|
||||
}
|
||||
|
||||
# TODO: EDUCATOR-2283 Remove this check when the waffle flag is turned on in production
|
||||
if waffle.new_course_outline_enabled(course_key=course_key):
|
||||
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)
|
||||
|
||||
gated_content = self.get_content_milestones(request, course_key)
|
||||
@@ -120,3 +130,23 @@ class CourseOutlineFragmentView(EdxFragmentView):
|
||||
}
|
||||
|
||||
return course_content_milestones
|
||||
|
||||
def user_enrolled_after_completion_collection(self, user, course_key):
|
||||
"""
|
||||
Checks that the user has enrolled in the course after 01/24/2018, the date that
|
||||
the completion API began data collection. If the user has enrolled in the course
|
||||
before this date, they may see incomplete collection data. This is a temporary
|
||||
check until all active enrollments are created after the date.
|
||||
"""
|
||||
begin_collection_date = datetime.datetime(2018, 01, 24, tzinfo=pytz.utc)
|
||||
user = User.objects.get(username=user)
|
||||
user_enrollment = CourseEnrollment.objects.get(
|
||||
user=user,
|
||||
course_id=course_key,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
if user_enrollment and user_enrollment.created > begin_collection_date:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user