Commit 786c4456 by Peter Fogg

Last-accessed courseware on the home page.

ECOM-2806
parent 96d030b6
......@@ -127,9 +127,9 @@ class LibraryContentTestBase(UniqueCourseTest):
Open library page in LMS
"""
self.courseware_page.visit()
paragraphs = self.courseware_page.q(css='.course-content p')
if paragraphs and "You were most recently in" in paragraphs.text[0]:
paragraphs[0].find_element_by_tag_name('a').click()
paragraphs = self.courseware_page.q(css='.course-content p').results
if not paragraphs:
self.courseware_page.q(css='.menu-item a').results[0].click()
block_id = block_id if block_id is not None else self.lib_block.locator
#pylint: disable=attribute-defined-outside-init
self.library_content_page = LibraryContentXBlockWrapper(self.browser, block_id)
......
......@@ -19,10 +19,3 @@ Feature: LMS.Navigate Course
When I navigate to an item in a sequence
Then I see the content of the sequence item
And a "seq_goto" browser event is emitted
Scenario: I can return to the last section I visited
Given I am viewing a course with multiple sections
When I navigate to a section
And I see the content of the section
And I return to the course
Then I see that I was most recently in the subsection
......@@ -136,12 +136,6 @@ def and_i_return_to_the_course(step):
world.css_click(course)
@step(u'I see that I was most recently in the subsection')
def then_i_see_that_i_was_most_recently_in_the_subsection(step):
message = world.css_text('section.course-content > p')
assert_in("You were most recently in Test Subsection 2", message)
def create_course():
world.clear_courses()
world.scenario_dict['COURSE'] = world.CourseFactory.create(
......
......@@ -228,11 +228,16 @@ class FieldOverrideProvider(object):
@abstractmethod
def enabled_for(self, course): # pragma no cover
"""
Return True if this provider should be enabled for a given course
Return True if this provider should be enabled for a given course,
and False otherwise.
Return False otherwise
Concrete implementations are responsible for implementing this method.
Concrete implementations are responsible for implementing this method
Arguments:
course (CourseModule or None)
Returns:
bool
"""
return False
......
......@@ -25,4 +25,4 @@ class SelfPacedDateOverrideProvider(FieldOverrideProvider):
@classmethod
def enabled_for(cls, course):
"""This provider is enabled for self-paced courses only."""
return SelfPacedConfiguration.current().enabled and course.self_paced
return course is not None and course.self_paced and SelfPacedConfiguration.current().enabled
......@@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse
from django.test.utils import override_settings
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from util.date_utils import strftime_localized
from xmodule.modulestore.tests.django_utils import (
ModuleStoreTestCase,
......@@ -92,6 +93,33 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_last_accessed_courseware_not_shown(self):
SelfPacedConfiguration(enable_course_home_improvements=True).save()
url = reverse('info', args=(unicode(self.course.id),))
response = self.client.get(url)
self.assertNotIn('Jump back to where you were last:', response.content)
def test_last_accessed_shown(self):
SelfPacedConfiguration(enable_course_home_improvements=True).save()
chapter = ItemFactory.create(
category="chapter", parent_location=self.course.location
)
section = ItemFactory.create(
category='section', parent_location=chapter.location
)
section_url = reverse(
'courseware_section',
kwargs={
'section': section.url_name,
'chapter': chapter.url_name,
'course_id': self.course.id
}
)
self.client.get(section_url)
info_url = reverse('info', args=(unicode(self.course.id),))
info_page_response = self.client.get(info_url)
self.assertIn('Jump back to where you were last:', info_page_response.content)
class CourseInfoTestCaseCCX(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
"""
......@@ -169,6 +197,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
"""
def setUp(self):
SelfPacedConfiguration(enabled=True).save()
super(SelfPacedCourseInfoTestCase, self).setUp()
self.instructor_paced_course = CourseFactory.create(self_paced=False)
self.self_paced_course = CourseFactory.create(self_paced=True)
......
......@@ -72,6 +72,7 @@ from openedx.core.djangoapps.credit.api import (
)
from shoppingcart.models import CourseRegistrationCode
from shoppingcart.utils import is_shopping_cart_enabled
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from student.models import UserTestGroup, CourseEnrollment
from student.views import is_course_blocked
from util.cache import cache, cache_if_anonymous
......@@ -549,8 +550,6 @@ def _index_bulk_op(request, course_key, chapter, section, position):
context['fragment'] = section_module.render(STUDENT_VIEW, section_render_context)
context['section_title'] = section_descriptor.display_name_with_default_escaped
else:
# section is none, so display a message
studio_url = get_studio_url(course, 'course')
prev_section = get_current_child(chapter_module)
if prev_section is None:
# Something went wrong -- perhaps this chapter has no sections visible to the user.
......@@ -559,22 +558,6 @@ def _index_bulk_op(request, course_key, chapter, section, position):
course_module.position = None
course_module.save()
return redirect(reverse('courseware', args=[course.id.to_deprecated_string()]))
prev_section_url = reverse('courseware_section', kwargs={
'course_id': course_key.to_deprecated_string(),
'chapter': chapter_descriptor.url_name,
'section': prev_section.url_name
})
context['fragment'] = Fragment(content=render_to_string(
'courseware/welcome-back.html',
{
'course': course,
'studio_url': studio_url,
'chapter_module': chapter_module,
'prev_section': prev_section,
'prev_section_url': prev_section_url
}
))
result = render_to_response('courseware/courseware.html', context)
except Exception as e:
......@@ -729,6 +712,14 @@ def course_info(request, course_id):
'url_to_enroll': url_to_enroll,
}
# Get the URL of the user's last position in order to display the 'where you were last' message
context['last_accessed_courseware'] = None
if SelfPacedConfiguration.current().enable_course_home_improvements:
(section_module, section_url) = get_last_accessed_courseware(course, request)
if section_module is not None and section_url is not None:
context['last_accessed_courseware'] = section_module
context['last_accessed_url'] = section_url
now = datetime.now(UTC())
effective_start = _adjust_start_date_for_beta_testers(user, course, course_key)
if not in_preview_mode() and staff_access and now < effective_start:
......@@ -739,6 +730,30 @@ def course_info(request, course_id):
return render_to_response('courseware/info.html', context)
def get_last_accessed_courseware(course, request):
"""
Return a pair of the last-accessed courseware for this request's
user, and a URL for that module.
"""
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
course.id, request.user, course, depth=2
)
course_module = get_module_for_descriptor(
request.user, request, course, field_data_cache, course.id, course=course
)
chapter_module = get_current_child(course_module)
if chapter_module is not None:
section_module = get_current_child(chapter_module)
if section_module is not None:
url = reverse('courseware_section', kwargs={
'course_id': unicode(course.id),
'chapter': chapter_module.url_name,
'section': section_module.url_name
})
return (section_module, url)
return (None, None)
@ensure_csrf_cookie
@ensure_valid_course_key
def static_tab(request, course_id, tab_slug):
......
......@@ -465,6 +465,7 @@ $courseware-navigation-color: $blue !default;
$homepage__header--gradient__color--alpha: lighten($gray, 15%) !default;
$homepage__header--gradient__color--bravo: saturate($gray, 30%) !default;
$homepage__header--background: lighten($gray, 15%) !default;
$homepage-background: rgb(252, 252, 252);
$course-card-height: ($baseline*18) !default;
$course-image-height: ($baseline*8) !default;
$course-info-height: ($baseline*10) !default;
......
.home-wrapper {
max-width: 1180px;
.home {
@include clearfix();
max-width: 1140px;
margin: 0 auto;
padding: $baseline $baseline ($baseline/2) $baseline;
.home {
margin: $baseline;
.page-header-main {
display: inline-block;
width: flex-grid(8, 12);
margin: 0;
.page-title {
margin-bottom: 5px;
......@@ -17,11 +21,29 @@
text-transform: none;
}
}
.page-header-secondary {
display: inline-block;
width: flex-grid(4, 12);
margin: 0;
padding: ($baseline/2) ($baseline*0.75);
border: 1px solid $blue;
background-color: $homepage-background;
@extend %t-title8;
color: $blue-d1;
@extend %cont-truncated;
vertical-align: text-bottom;
.last-accessed-message {
display: inline-block;
@include margin-left($baseline*0.75);
}
}
}
div.info-wrapper {
background-color: rgb(252, 252, 252);
background-color: $homepage-background;
section.updates {
@extend .content;
......@@ -63,7 +85,7 @@ div.info-wrapper {
margin-bottom: ($baseline/4);
text-transform: none;
background: url('#{$static-path}/images/calendar-icon.png') 0 center no-repeat;
padding-left: $baseline;
@include padding-left($baseline);
}
section.update-description {
......
......@@ -22,7 +22,6 @@
list-style: none;
&.prominent {
margin-right: 16px;
@include margin-right(16px);
background: rgba(255, 255, 255, 0.5);
border-radius: 3px;
......
......@@ -45,11 +45,18 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
<%block name="bodyclass">view-in-course view-course-info ${course.css_class or ''}</%block>
<section class="container">
<div class="home-wrapper">
<section class="home">
<div class="home">
<div class="page-header-main">
<h1 class="page-title">${_("Welcome to {org}'s {course_name}!").format(org=course.id.org, course_name=course.id.course) | h}</h1>
<h2 class="page-subtitle">${course.display_name | h}</h2>
</section>
</div>
% if last_accessed_courseware:
<div class="page-header-secondary">
<i class="fa fa-clock-o"></i>
<p class="last-accessed-message">${_("Jump back to where you were last:")}</p>
<a href="${last_accessed_url}">${last_accessed_courseware.display_name | h}</a>
</div>
% endif
</div>
<div class="info-wrapper">
% if user.is_authenticated():
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment