From 445386233e7f53181bd12f6f2d5ce5f8e7e618e7 Mon Sep 17 00:00:00 2001 From: Peter Fogg Date: Wed, 4 Nov 2015 14:25:30 -0500 Subject: [PATCH] Fix date summary blocks for courses without an end date. I also took this opportunity to do some small cleanups of the date summary code, specifically converting some attributes to properties in order to remove a Pylint directive disabling warnings about missing docstrings. --- lms/djangoapps/courseware/courses.py | 17 +++++- lms/djangoapps/courseware/date_summary.py | 55 +++++++++++++------ .../courseware/tests/test_date_summary.py | 24 +++++--- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index dce1a75ab2..d5c674f6fb 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -1,9 +1,15 @@ +""" +Functions for accessing and displaying courses within the +courseware. +""" +from datetime import datetime from collections import defaultdict from fs.errors import ResourceNotFoundError import logging import inspect from path import Path as path +import pytz from django.http import Http404 from django.conf import settings @@ -334,7 +340,16 @@ def _get_course_date_summary_blocks(course, user): ) blocks = (cls(course, user) for cls in block_classes) - return sorted((b for b in blocks if b.is_enabled), key=lambda b: b.date) + + def block_key_fn(block): + """ + If the block's date is None, return the maximum datetime in order + to force it to the end of the list of displayed blocks. + """ + if block.date is None: + return datetime.max.replace(tzinfo=pytz.UTC) + return block.date + return sorted((b for b in blocks if b.is_enabled), key=block_key_fn) # TODO: Fix this such that these are pulled in as extra course-specific tabs. diff --git a/lms/djangoapps/courseware/date_summary.py b/lms/djangoapps/courseware/date_summary.py index 68cc759a8f..6db3ca1315 100644 --- a/lms/djangoapps/courseware/date_summary.py +++ b/lms/djangoapps/courseware/date_summary.py @@ -1,4 +1,3 @@ -# pylint: disable=missing-docstring """ This module provides date summary blocks for the Course Info page. Each block gives information about a particular @@ -20,27 +19,46 @@ from student.models import CourseEnrollment class DateSummary(object): """Base class for all date summary blocks.""" - # The CSS class of this summary. Indicates the type of information - # this summary block contains, and its urgency. - css_class = '' + @property + def css_class(self): + """ + The CSS class of this summary. Indicates the type of information + this summary block contains, and its urgency. + """ + return '' - # The title of this summary. - title = '' + @property + def title(self): + """The title of this summary.""" + return '' - # The detail text displayed by this summary. - description = '' + @property + def description(self): + """The detail text displayed by this summary.""" + return '' - # This summary's date. - date = None + @property + def date(self): + """This summary's date.""" + return None - # The format to display this date in. By default, displays like Jan 01, 2015. - date_format = '%b %d, %Y' + @property + def date_format(self): + """ + The format to display this date in. By default, displays like Jan + 01, 2015. + """ + return '%b %d, %Y' - # The location to link to for more information. - link = '' + @property + def link(self): + """The location to link to for more information.""" + return '' - # The text of the link. - link_text = '' + @property + def link_text(self): + """The text of the link.""" + return '' def __init__(self, course, user): self.course = course @@ -126,7 +144,10 @@ class CourseEndDate(DateSummary): """ css_class = 'end-date' title = _('Course End') - is_enabled = True + + @property + def is_enabled(self): + return self.date is not None @property def description(self): diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py index d96c8b5c46..0d0fa634f0 100644 --- a/lms/djangoapps/courseware/tests/test_date_summary.py +++ b/lms/djangoapps/courseware/tests/test_date_summary.py @@ -48,11 +48,15 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): """Set up the course and user for this test.""" now = datetime.now(pytz.UTC) self.course = CourseFactory.create( # pylint: disable=attribute-defined-outside-init - start=now + timedelta(days=days_till_start), - end=now + timedelta(days=days_till_end), + start=now + timedelta(days=days_till_start) ) self.user = UserFactory.create() # pylint: disable=attribute-defined-outside-init + if days_till_end is not None: + self.course.end = now + timedelta(days=days_till_end) + else: + self.course.end = None + if enrollment_mode is not None and days_till_upgrade_deadline is not None: CourseModeFactory.create( course_id=self.course.id, @@ -97,6 +101,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): 'days_till_verification_deadline': -5, 'verification_status': 'approved'}, (TodaysDate, CourseEndDate)), + # No course end date + ({'days_till_end': None}, + (CourseStartDate, TodaysDate, VerificationDeadlineDate, VerifiedUpgradeDeadlineDate)), # During course run ({'days_till_start': -1}, (TodaysDate, CourseEndDate, VerificationDeadlineDate, VerifiedUpgradeDeadlineDate)), @@ -131,13 +138,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): self.assertHTMLEqual(block.render(), html) self.assertFalse(block.is_enabled) - @freezegun.freeze_time('2015-01-02') - def test_date_render(self): - self.setup_course_and_user() - block = DateSummary(self.course, self.user) - block.date = datetime.now(pytz.UTC) - self.assertIn('Jan 02, 2015', block.render()) - ## TodaysDate @freezegun.freeze_time('2015-01-02') @@ -149,6 +149,12 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): self.assertEqual(block.title, 'Today is Jan 02, 2015') self.assertNotIn('date-summary-date', block.render()) + @freezegun.freeze_time('2015-01-02') + def test_date_render(self): + self.setup_course_and_user() + block = TodaysDate(self.course, self.user) + self.assertIn('Jan 02, 2015', block.render()) + ## CourseStartDate def test_course_start_date(self):