diff --git a/lms/static/sass/features/_course-experience.scss b/lms/static/sass/features/_course-experience.scss index e6442e5f57..1bec52b475 100644 --- a/lms/static/sass/features/_course-experience.scss +++ b/lms/static/sass/features/_course-experience.scss @@ -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 { diff --git a/openedx/features/course_experience/templates/course_experience/course-outline-fragment-new.html b/openedx/features/course_experience/templates/course_experience/course-outline-fragment-new.html index d981f1eac9..5fa0a0d8b2 100644 --- a/openedx/features/course_experience/templates/course_experience/course-outline-fragment-new.html +++ b/openedx/features/course_experience/templates/course_experience/course-outline-fragment-new.html @@ -27,6 +27,9 @@ from openedx.core.djangolib.markup import HTML, Text id="${ section['id'] }">

${ section['display_name'] }

+ % if show_visual_progress and section.get('complete'): + + % endif
    ${ subsection['display_name'] } - % endif + % if subsection.get('complete') and show_visual_progress: + + % endif + % endif
    ## 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'] }
    + % if vertical.get('complete') and show_visual_progress: + + % endif % endfor diff --git a/openedx/features/course_experience/tests/views/test_course_outline.py b/openedx/features/course_experience/tests/views/test_course_outline.py index 34a6a6f700..698d557b60 100644 --- a/openedx/features/course_experience/tests/views/test_course_outline.py +++ b/openedx/features/course_experience/tests/views/test_course_outline.py @@ -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( diff --git a/openedx/features/course_experience/utils.py b/openedx/features/course_experience/utils.py index 518e91c4f6..2e938d5299 100644 --- a/openedx/features/course_experience/utils.py +++ b/openedx/features/course_experience/utils.py @@ -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, diff --git a/openedx/features/course_experience/views/course_outline.py b/openedx/features/course_experience/views/course_outline.py index 7822f6cd11..b95522c709 100644 --- a/openedx/features/course_experience/views/course_outline.py +++ b/openedx/features/course_experience/views/course_outline.py @@ -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