Skip to content
Snippets Groups Projects
Commit 8c93eac6 authored by Andy Armstrong's avatar Andy Armstrong
Browse files

Add an empty course message to the course home page

LEARNER-27
parent 8fbf01cd
No related merge requests found
......@@ -25,4 +25,4 @@
// Features
@import 'features/bookmarks';
@import 'features/course-outline';
@import 'features/course-experience';
// Course sidebar
.course-sidebar {
@include margin-left(0);
@include padding-left($baseline);
}
// Course outline
.course-outline {
color: $lms-gray;
......
......@@ -64,7 +64,7 @@ from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
<main class="layout-col layout-col-b">
${HTML(outline_fragment.body_html())}
</main>
<aside class="layout-col layout-col-a">
<aside class="course-sidebar layout-col layout-col-a">
<div class="section section-tools">
<h3 class="hd-6">${_("Course Tools")}</h3>
<ul class="list-unstyled">
......
......@@ -5,7 +5,11 @@
<%namespace name='static' file='../static_content.html'/>
<%!
from datetime import date
from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import HTML, Text
%>
<%static:require_module_async module_name="course_experience/js/course_outline_factory" class_name="CourseOutlineFactory">
......@@ -13,65 +17,77 @@ from django.utils.translation import ugettext as _
</%static:require_module_async>
<main role="main" class="course-outline" id="main" tabindex="-1">
<ol class="block-tree" role="tree">
% for section in blocks.get('children') or []:
<li
aria-expanded="true"
class="outline-item focusable section"
id="${ section['id'] }"
role="treeitem"
tabindex="0"
>
<div class="section-name">
<h3>${ section['display_name'] }</h3>
</div>
<ol class="outline-item focusable" role="group" tabindex="0">
% for subsection in section.get('children') or []:
<li
class="subsection ${ 'current' if subsection['current'] else '' }"
role="treeitem"
tabindex="-1"
aria-expanded="true"
>
<a
class="outline-item focusable"
href="${ subsection['lms_web_url'] }"
id="${ subsection['id'] }"
% if blocks.get('children'):
<ol class="block-tree" role="tree">
% for section in blocks.get('children'):
<li
aria-expanded="true"
class="outline-item focusable section"
id="${ section['id'] }"
role="treeitem"
tabindex="0"
>
<div class="section-name">
<h3>${ section['display_name'] }</h3>
</div>
<ol class="outline-item focusable" role="group" tabindex="0">
% for subsection in section.get('children') or []:
<li
class="subsection ${ 'current' if subsection['current'] else '' }"
role="treeitem"
tabindex="-1"
aria-expanded="true"
>
<div class="subsection-text">
## Subsection title
<span class="subsection-title">${ subsection['display_name'] }</span>
<a
class="outline-item focusable"
href="${ subsection['lms_web_url'] }"
id="${ subsection['id'] }"
>
<div class="subsection-text">
## Subsection title
<span class="subsection-title">${ subsection['display_name'] }</span>
<div class="details">
## There are behavior differences between rendering of subsections which have
## exams (timed, graded, etc) and those that do not.
##
## Exam subsections expose exam status message field as well as a status icon
<%
if subsection.get('due') is None:
# examples: Homework, Lab, etc.
data_string = subsection.get('format')
else:
if 'special_exam_info' in subsection:
data_string = _('due {date}')
<div class="details">
## There are behavior differences between rendering of subsections which have
## exams (timed, graded, etc) and those that do not.
##
## Exam subsections expose exam status message field as well as a status icon
<%
if subsection.get('due') is None:
# examples: Homework, Lab, etc.
data_string = subsection.get('format')
else:
data_string = _("{subsection_format} due {{date}}").format(subsection_format=subsection.get('format'))
%>
% if subsection.get('format') or 'special_exam_info' in subsection:
<span class="subtitle">
% if 'special_exam' in subsection:
## Display the exam status icon and status message
<span
class="menu-icon icon fa ${subsection['special_exam_info'].get('suggested_icon', 'fa-pencil-square-o')} ${subsection['special_exam_info'].get('status', 'eligible')}"
aria-hidden="true"
></span>
<span class="subtitle-name">
${subsection['special_exam_info'].get('short_description', '')}
</span>
if 'special_exam_info' in subsection:
data_string = _('due {date}')
else:
data_string = _("{subsection_format} due {{date}}").format(subsection_format=subsection.get('format'))
%>
% if subsection.get('format') or 'special_exam_info' in subsection:
<span class="subtitle">
% if 'special_exam' in subsection:
## Display the exam status icon and status message
<span
class="menu-icon icon fa ${subsection['special_exam_info'].get('suggested_icon', 'fa-pencil-square-o')} ${subsection['special_exam_info'].get('status', 'eligible')}"
aria-hidden="true"
></span>
<span class="subtitle-name">
${subsection['special_exam_info'].get('short_description', '')}
</span>
## completed exam statuses should not show the due date
## since the exam has already been submitted by the user
% if not subsection['special_exam_info'].get('in_completed_state', False):
## completed exam statuses should not show the due date
## since the exam has already been submitted by the user
% if not subsection['special_exam_info'].get('in_completed_state', False):
<span
class="localized-datetime subtitle-name"
data-datetime="${subsection.get('due')}"
data-string="${data_string}"
data-timezone="${user_timezone}"
data-language="${user_language}"
></span>
% endif
% else:
## non-graded section, we just show the exam format and the due date
## this is the standard case in edx-platform
<span
class="localized-datetime subtitle-name"
data-datetime="${subsection.get('due')}"
......@@ -79,47 +95,69 @@ from django.utils.translation import ugettext as _
data-timezone="${user_timezone}"
data-language="${user_language}"
></span>
% endif
% else:
## non-graded section, we just show the exam format and the due date
## this is the standard case in edx-platform
<span
class="localized-datetime subtitle-name"
data-datetime="${subsection.get('due')}"
data-string="${data_string}"
data-timezone="${user_timezone}"
data-language="${user_language}"
></span>
% if 'graded' in subsection and subsection['graded']:
<span
class="menu-icon icon fa fa-pencil-square-o"
aria-hidden="true"
></span>
<span class="sr">${_("This content is graded")}</span>
% if 'graded' in subsection and subsection['graded']:
<span
class="menu-icon icon fa fa-pencil-square-o"
aria-hidden="true"
></span>
<span class="sr">${_("This content is graded")}</span>
% endif
% endif
</span>
% endif
</span>
% endif
</div> <!-- /details -->
</div> <!-- /subsection-text -->
<div class="subsection-actions">
## Resume button (if last visited section)
% if subsection['current']:
<span class="sr-only">${ _("This is your last visited course section.") }</span>
<span class="resume-right">
<b>${ _("Resume Course") }</b>
<span class="icon fa fa-arrow-circle-right" aria-hidden="true"></span>
</span>
%endif
</div>
</a>
</li>
% endfor
</ol>
</li>
% endfor
</ol>
</div> <!-- /details -->
</div> <!-- /subsection-text -->
<div class="subsection-actions">
## Resume button (if last visited section)
% if subsection['current']:
<span class="sr-only">${ _("This is your last visited course section.") }</span>
<span class="resume-right">
<b>${ _("Resume Course") }</b>
<span class="icon fa fa-arrow-circle-right" aria-hidden="true"></span>
</span>
%endif
</div>
</a>
</li>
% endfor
</ol>
</li>
% endfor
</ol>
% else:
<div class="well depth-0 message-area">
<div class="copy-large">
<span class="icon fa fa-calendar-o" aria-hidden="true"></span>
<%
course_started = course.start.date() <= date.today()
%>
% if course.start_date_is_still_default:
${_("This course has not started yet.")}
% elif course_started:
${_("We're still working on course content.")}
% else:
${Text(_("This course has not started yet, and will launch on {launch_date_html}.")).format(
launch_date_html=HTML(
'<span'
' class="localized-datetime start-date"'
' data-datetime="{start_date}"'
' data-format="shortDate"'
' data-timezone="{user_timezone}"'
' data-language="{user_language}"'
'></span>'
).format(
start_date=course.start,
user_timezone=user_timezone,
user_language=user_language,
),
)}
% endif
</div>
</div>
% endif
</main>
<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory">
......
......@@ -2,6 +2,7 @@
Tests for the Course Outline view and supporting views.
"""
import datetime
import ddt
from mock import patch
import json
......@@ -12,10 +13,13 @@ from student.models import CourseEnrollment
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.course_module import DEFAULT_START_DATE
from .test_course_home import course_home_url
TEST_PASSWORD = 'test'
FUTURE_DAY = datetime.datetime.now() + datetime.timedelta(days=30)
PAST_DAY = datetime.datetime.now() - datetime.timedelta(days=30)
class TestCourseOutlinePage(SharedModuleStoreTestCase):
......@@ -160,3 +164,25 @@ class TestCourseOutlinePreview(SharedModuleStoreTestCase):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'Future Chapter')
@ddt.ddt
class TestEmptyCourseOutlinePage(SharedModuleStoreTestCase):
"""
Test the new course outline view.
"""
@ddt.data(
(FUTURE_DAY, 'This course has not started yet, and will launch on'),
(PAST_DAY, "We're still working on course content."),
(DEFAULT_START_DATE, 'This course has not started yet.'),
)
@ddt.unpack
def test_empty_course_rendering(self, start_date, expected_text):
course = CourseFactory.create(start=start_date)
test_user = UserFactory(password=TEST_PASSWORD)
CourseEnrollment.enroll(test_user, course.id)
self.client.login(username=test_user.username, password=TEST_PASSWORD)
url = course_home_url(course)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, expected_text)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment