Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] Update last accessed courseware on the home page. #11131

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions cms/djangoapps/contentstore/course_info_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,25 +160,13 @@ def _get_index(passed_id=None):
return 0


def _get_html(course_updates_items):
"""
Method to create course_updates_html from course_updates items
"""
list_items = []
for update in reversed(course_updates_items):
# filter course update items which have status "deleted".
if update.get("status") != CourseInfoModule.STATUS_DELETED:
list_items.append(u"<article><h2>{date}</h2>{content}</article>".format(**update))
return u"<section>{list_items}</section>".format(list_items="".join(list_items))


def save_course_update_items(location, course_updates, course_update_items, user=None):
"""
Save list of course_updates data dictionaries in new field ("course_updates.items")
and html related to course update in 'data' ("course_updates.data") field.
"""
course_updates.items = course_update_items
course_updates.data = _get_html(course_update_items)
course_updates.data = ""

# update db record
modulestore().update_item(course_updates, user.id)
Expand Down
8 changes: 4 additions & 4 deletions cms/djangoapps/contentstore/features/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,25 @@ def _verify_page_names(first, second):

@step(u'the built-in pages are in the default order$')
def built_in_pages_in_default_order(step):
expected_pages = ['Courseware', 'Course Info', 'Wiki', 'Progress']
expected_pages = ['Home', 'Course', 'Wiki', 'Progress']
see_pages_in_expected_order(expected_pages)


@step(u'the built-in pages are switched$')
def built_in_pages_switched(step):
expected_pages = ['Courseware', 'Course Info', 'Progress', 'Wiki']
expected_pages = ['Home', 'Course', 'Progress', 'Wiki']
see_pages_in_expected_order(expected_pages)


@step(u'the pages are in the default order$')
def pages_in_default_order(step):
expected_pages = ['Courseware', 'Course Info', 'Wiki', 'Progress', 'First', 'Empty']
expected_pages = ['Home', 'Course', 'Wiki', 'Progress', 'First', 'Empty']
see_pages_in_expected_order(expected_pages)


@step(u'the pages are switched$$')
def pages_are_switched(step):
expected_pages = ['Courseware', 'Course Info', 'Progress', 'First', 'Empty', 'Wiki']
expected_pages = ['Home', 'Course', 'Progress', 'First', 'Empty', 'Wiki']
see_pages_in_expected_order(expected_pages)


Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/tests/test_course_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -942,8 +942,8 @@ def test_advanced_components_munge_tabs_validation_failure(self):
self.assertNotIn("notes", course.advanced_modules)

@ddt.data(
[{'type': 'courseware'}, {'type': 'course_info'}, {'type': 'wiki', 'is_hidden': True}],
[{'type': 'courseware', 'name': 'Courses'}, {'type': 'course_info', 'name': 'Info'}],
[{'type': 'course_info'}, {'type': 'courseware'}, {'type': 'wiki', 'is_hidden': True}],
[{'type': 'course_info', 'name': 'Home'}, {'type': 'courseware', 'name': 'Course'}],
)
def test_course_tab_configurations(self, tab_list):
self.course.tabs = tab_list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,8 @@ def test_course_updates_compatibility(self):
self.assertHTMLEqual(update_content, json.loads(resp.content)['content'])
course_updates = modulestore().get_item(location)
self.assertEqual(course_updates.items, [{u'date': update_date, u'content': update_content, u'id': 1}])
# course_updates 'data' field should update accordingly
update_data = u"<section><article><h2>{date}</h2>{content}</article></section>".format(date=update_date, content=update_content)
self.assertEqual(course_updates.data, update_data)
# course_updates 'data' field should not update automatically
self.assertEqual(course_updates.data, '')

# test delete course update item (soft delete)
course_updates = modulestore().get_item(location)
Expand Down
2 changes: 1 addition & 1 deletion cms/templates/course_info.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"${handouts_locator | escapejs}",
"${base_asset_url}",
${escape_json_dumps(push_notification_enabled) | n}
);
);
});
</%block>

Expand Down
8 changes: 4 additions & 4 deletions common/lib/xmodule/xmodule/course_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,13 +414,13 @@ class CourseFields(object):
scope=Scope.settings
)
has_children = True
info_sidebar_name = String(
display_name=_("Course Info Sidebar Name"),
info_section_name = String(
display_name=_("Course Home Sidebar Name"),
help=_(
"Enter the heading that you want students to see above your course handouts on the Course Info page. "
"Enter the heading that you want students to see above your course handouts on the Course Home page. "
"Your course handouts appear in the right panel of the page."
),
scope=Scope.settings, default='Course Handouts')
scope=Scope.settings, default=_('Course Resources and Tools'))
show_timezone = Boolean(
help=_(
"True if timezones should be shown on dates in the courseware. "
Expand Down
48 changes: 39 additions & 9 deletions common/lib/xmodule/xmodule/html_module.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import os
import sys
import re
import copy
from datetime import datetime
from fs.errors import ResourceNotFoundError
import logging
import textwrap
from lxml import etree
import os
from path import Path as path
from fs.errors import ResourceNotFoundError
from pkg_resources import resource_string
import re
import sys
import textwrap

import dogstats_wrapper as dog_stats_api
from xmodule.util.misc import escape_html_characters
Expand Down Expand Up @@ -75,10 +76,10 @@ def student_view(self, _context):
return Fragment(self.get_html())

def get_html(self):
"""
When we switch this to an XBlock, we can merge this with student_view,
but for now the XModule mixin requires that this method be defined.
"""
""" Returns html required for rendering XModule. """

# When we switch this to an XBlock, we can merge this with student_view,
# but for now the XModule mixin requires that this method be defined.
# pylint: disable=no-member
if self.system.anonymous_student_id:
return self.data.replace("%%USER_ID%%", self.system.anonymous_student_id)
Expand Down Expand Up @@ -417,6 +418,35 @@ class CourseInfoModule(CourseInfoFields, HtmlModuleMixin):
# statuses
STATUS_VISIBLE = 'visible'
STATUS_DELETED = 'deleted'
TEMPLATE_DIR = 'courseware'

@XBlock.supports("multi_device")
def student_view(self, _context):
"""
Return a fragment that contains the html for the student view
"""
return Fragment(self.get_html())

def get_html(self):
""" Returns html required for rendering XModule. """

# When we switch this to an XBlock, we can merge this with student_view,
# but for now the XModule mixin requires that this method be defined.
# pylint: disable=no-member
if self.data != "":
if self.system.anonymous_student_id:
return self.data.replace("%%USER_ID%%", self.system.anonymous_student_id)
return self.data
else:
course_updates = [item for item in self.items if item.get('status') == self.STATUS_VISIBLE]
course_updates.sort(key=lambda item: datetime.strptime(item['date'], '%B %d, %Y'), reverse=True)

context = {
'visible_updates': course_updates[:3],
'hidden_updates': course_updates[3:],
}

return self.system.render_template("{0}/course_updates.html".format(self.TEMPLATE_DIR), context)


@XBlock.tag("detached")
Expand Down
26 changes: 20 additions & 6 deletions common/lib/xmodule/xmodule/tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@ def initialize_default(course):
"""

course.tabs.extend([
CourseTab.load('courseware'),
CourseTab.load('course_info')
CourseTab.load('course_info'),
CourseTab.load('courseware')
])

# Presence of syllabus tab is indicated by a course attribute
Expand Down Expand Up @@ -389,6 +389,19 @@ def iterate_displayable(course, user=None, inline_collections=True):
else:
yield tab

@classmethod
def upgrade_tabs(cls, tabs):
"""
Reverse and Rename Courseware to Course and Course Info to Home Tabs.
"""
if tabs and len(tabs) > 1:
if tabs[0].get('type') == 'courseware' and tabs[1].get('type') == 'course_info':
tabs[0], tabs[1] = tabs[1], tabs[0]
tabs[0]['name'] = _('Home')
tabs[1]['name'] = _('Course')

return tabs

@classmethod
def validate_tabs(cls, tabs):
"""
Expand All @@ -406,13 +419,13 @@ def validate_tabs(cls, tabs):
if len(tabs) < 2:
raise InvalidTabsException("Expected at least two tabs. tabs: '{0}'".format(tabs))

if tabs[0].get('type') != 'courseware':
if tabs[0].get('type') != 'course_info':
raise InvalidTabsException(
"Expected first tab to have type 'courseware'. tabs: '{0}'".format(tabs))
"Expected first tab to have type 'course_info'. tabs: '{0}'".format(tabs))

if tabs[1].get('type') != 'course_info':
if tabs[1].get('type') != 'courseware':
raise InvalidTabsException(
"Expected second tab to have type 'course_info'. tabs: '{0}'".format(tabs))
"Expected second tab to have type 'courseware'. tabs: '{0}'".format(tabs))

# the following tabs should appear only once
# TODO: don't import openedx capabilities from common
Expand Down Expand Up @@ -455,6 +468,7 @@ def from_json(self, values):
"""
Overrides the from_json method to de-serialize the CourseTab objects from a json-like representation.
"""
self.upgrade_tabs(values)
self.validate_tabs(values)
tabs = []
for tab_dict in values:
Expand Down
2 changes: 1 addition & 1 deletion common/test/acceptance/pages/lms/tab_nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def go_to_tab(self, tab_name):
Navigate to the tab `tab_name`.
"""

if tab_name not in ['Courseware', 'Course Info', 'Discussion', 'Wiki', 'Progress']:
if tab_name not in ['Course', 'Home', 'Discussion', 'Wiki', 'Progress']:
self.warning("'{0}' is not a valid tab name".format(tab_name))

# The only identifier for individual tabs is the link href
Expand Down
2 changes: 1 addition & 1 deletion common/test/acceptance/pages/studio/settings_advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def expected_settings_names(self):
'advertised_start',
'announcement',
'display_name',
'info_sidebar_name',
'info_section_name',
'is_new',
'issue_badges',
'max_student_enrollments_allowed',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def complete_course_problems(self):
Problems were added in the setUp
"""
self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')

# Navigate to Test Subsection in Test Section Section
self.course_nav.go_to_section('Test Section', 'Test Subsection')
Expand Down
6 changes: 3 additions & 3 deletions common/test/acceptance/tests/lms/test_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ def _goto_library_block_page(self, block_id=None):
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)
Expand Down
22 changes: 11 additions & 11 deletions common/test/acceptance/tests/lms/test_lms.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ def test_course_info(self):

# Navigate to the course info page from the progress page
self.progress_page.visit()
self.tab_nav.go_to_tab('Course Info')
self.tab_nav.go_to_tab('Home')

# Expect just one update
self.assertEqual(self.course_info_page.num_updates, 1)
Expand Down Expand Up @@ -650,13 +650,13 @@ def test_wiki_tab_first_time(self):

def test_courseware_nav(self):
"""
Navigate to a particular unit in the courseware.
Navigate to a particular unit in the course.
"""
# Navigate to the courseware page from the info page
# Navigate to the course page from the info page
self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')

# Check that the courseware navigation appears correctly
# Check that the course navigation appears correctly
EXPECTED_SECTIONS = {
'Test Section': ['Test Subsection'],
'Test Section 2': ['Test Subsection 2', 'Test Subsection 3']
Expand Down Expand Up @@ -844,7 +844,7 @@ def test_tooltip(self):
Verify that tooltips are displayed when you hover over the sequence nav bar.
"""
self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')

self.assertTrue(self.courseware_page.tooltips_displayed())

Expand Down Expand Up @@ -993,7 +993,7 @@ def check_function(expect, ans):
def test_python_execution_in_problem(self):
# Navigate to the problem page
self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')
self.course_nav.go_to_section('Test Section', 'Test Subsection')

problem_page = ProblemPage(self.browser)
Expand Down Expand Up @@ -1043,14 +1043,14 @@ def setUp(self):

def test_entrance_exam_section(self):
"""
Scenario: Any course that is enabled for an entrance exam, should have entrance exam chapter at courseware
Scenario: Any course that is enabled for an entrance exam, should have entrance exam chapter at course
page.
Given that I am on the courseware page
When I view the courseware that has an entrance exam
Given that I am on the course page
When I view the course that has an entrance exam
Then there should be an "Entrance Exam" chapter.'
"""
entrance_exam_link_selector = '.accordion .course-navigation .chapter .group-heading'
# visit courseware page and make sure there is not entrance exam chapter.
# visit course page and make sure there is not entrance exam chapter.
self.courseware_page.visit()
self.courseware_page.wait_for_page()
self.assertFalse(element_has_text(
Expand Down
6 changes: 3 additions & 3 deletions common/test/acceptance/tests/lms/test_lms_acid_xblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def test_acid_block(self):
"""

self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')

acid_block = AcidView(self.browser, '.xblock-student_view[data-block-type=acid]')
self.validate_acid_block_view(acid_block)
Expand Down Expand Up @@ -119,7 +119,7 @@ def test_acid_block(self):
"""

self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')

acid_parent_block = AcidView(self.browser, '.xblock-student_view[data-block-type=acid_parent]')
self.validate_acid_parent_block_view(acid_parent_block)
Expand Down Expand Up @@ -159,7 +159,7 @@ def test_acid_block(self):
"""

self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')

acid_aside = AcidView(self.browser, '.xblock_asides-v1-student_view[data-block-type=acid_aside]')
self.validate_acid_aside_view(acid_aside)
Expand Down
2 changes: 1 addition & 1 deletion common/test/acceptance/tests/video/test_video_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def _navigate_to_courseware_video(self):
self.auth_page.visit()
self.user_info = self.auth_page.user_info
self.course_info_page.visit()
self.tab_nav.go_to_tab('Courseware')
self.tab_nav.go_to_tab('Course')

def _navigate_to_courseware_video_and_render(self):
""" Wait for the video player to render """
Expand Down
4 changes: 2 additions & 2 deletions lms/djangoapps/course_wiki/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ def has_course_navigator(self, resp):
"""
Ensure that the response has the course navigator.
"""
self.assertContains(resp, "Course Info")
self.assertContains(resp, "courseware")
self.assertContains(resp, "Home")
self.assertContains(resp, "Course")

@patch.dict("django.conf.settings.FEATURES", {'ALLOW_WIKI_ROOT_ACCESS': True})
def test_course_navigator(self):
Expand Down
5 changes: 4 additions & 1 deletion lms/djangoapps/courseware/date_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
"""
css_class = 'verified-upgrade-deadline'
title = _('Verification Upgrade Deadline')
description = _('You are still eligible to upgrade to a Verified Certificate!')
description = _(
'You are still eligible to upgrade to a Verified Certificate! '
'Pursue it to highlight the knowledge and skills you gain in this course.'
)
link_text = _('Upgrade to Verified Certificate')

@property
Expand Down
Loading