diff --git a/common/lib/xmodule/xmodule/conditional_module.py b/common/lib/xmodule/xmodule/conditional_module.py index b3e0e0e06b..2433a143ed 100644 --- a/common/lib/xmodule/xmodule/conditional_module.py +++ b/common/lib/xmodule/xmodule/conditional_module.py @@ -125,7 +125,8 @@ class ConditionalModule(ConditionalFields, XModule): an AJAX call. """ if not self.is_condition_satisfied(): - message = self.descriptor.xml_attributes.get('message') + defmsg = "{link} must be attempted before this will become visible." + message = self.descriptor.xml_attributes.get('message', defmsg) context = {'module': self, 'message': message} html = self.system.render_template('conditional_module.html', diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index ed5a37e580..4712bbe426 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -652,7 +652,12 @@ class CourseDescriptor(CourseFields, SequenceDescriptor): @property def end_date_text(self): - return time.strftime("%b %d, %Y", self.end) + """ + Returns the end date for the course formatted as a string. + + If the course does not have an end date set (course.end is None), an empty string will be returned. + """ + return '' if self.end is None else time.strftime("%b %d, %Y", self.end) @property def forum_posts_allowed(self): diff --git a/common/lib/xmodule/xmodule/tests/test_course_module.py b/common/lib/xmodule/xmodule/tests/test_course_module.py index 17930f1b89..15bab32c14 100644 --- a/common/lib/xmodule/xmodule/tests/test_course_module.py +++ b/common/lib/xmodule/xmodule/tests/test_course_module.py @@ -54,7 +54,7 @@ class IsNewCourseTestCase(unittest.TestCase): self.addCleanup(datetime_patcher.stop) @staticmethod - def get_dummy_course(start, announcement=None, is_new=None, advertised_start=None): + def get_dummy_course(start, announcement=None, is_new=None, advertised_start=None, end=None): """Get a dummy course""" system = DummySystem(load_error_modules=True) @@ -65,6 +65,7 @@ class IsNewCourseTestCase(unittest.TestCase): is_new = to_attrb('is_new', is_new) announcement = to_attrb('announcement', announcement) advertised_start = to_attrb('advertised_start', advertised_start) + end = to_attrb('end', end) start_xml = ''' + {advertised_start} + {end}> Two houses, ... '''.format(org=ORG, course=COURSE, start=start, is_new=is_new, - announcement=announcement, advertised_start=advertised_start) + announcement=announcement, advertised_start=advertised_start, end=end) return system.process_xml(start_xml) @@ -161,3 +163,11 @@ class IsNewCourseTestCase(unittest.TestCase): descriptor = self.get_dummy_course(start='2012-12-31T12:00') assert(descriptor.is_newish is True) + + def test_end_date_text(self): + # No end date set, returns empty string. + d = self.get_dummy_course('2012-12-02T12:00') + self.assertEqual('', d.end_date_text) + + d = self.get_dummy_course('2012-12-02T12:00', end='2014-9-04T12:00') + self.assertEqual('Sep 04, 2014', d.end_date_text) diff --git a/common/test/data/test_about_blob_end_date/README.md b/common/test/data/test_about_blob_end_date/README.md new file mode 100644 index 0000000000..69cb3b7cb7 --- /dev/null +++ b/common/test/data/test_about_blob_end_date/README.md @@ -0,0 +1,5 @@ +Test course for checking the end date displayed on the course about page. +This course has both an end_date HTML "blob", and it also has a course end date set. +The end_date "blob" has higher precedence and will show. + +See also test_end course. diff --git a/common/test/data/test_about_blob_end_date/about/end_date.html b/common/test/data/test_about_blob_end_date/about/end_date.html new file mode 100644 index 0000000000..918737595e --- /dev/null +++ b/common/test/data/test_about_blob_end_date/about/end_date.html @@ -0,0 +1 @@ +Learning never ends diff --git a/common/test/data/test_about_blob_end_date/course.xml b/common/test/data/test_about_blob_end_date/course.xml new file mode 100644 index 0000000000..e6ffae8481 --- /dev/null +++ b/common/test/data/test_about_blob_end_date/course.xml @@ -0,0 +1 @@ + diff --git a/common/test/data/test_about_blob_end_date/course/2012_Fall.xml b/common/test/data/test_about_blob_end_date/course/2012_Fall.xml new file mode 100644 index 0000000000..c9d2e8702d --- /dev/null +++ b/common/test/data/test_about_blob_end_date/course/2012_Fall.xml @@ -0,0 +1,2 @@ + + diff --git a/common/test/data/test_about_blob_end_date/policies/2012_Fall.json b/common/test/data/test_about_blob_end_date/policies/2012_Fall.json new file mode 100644 index 0000000000..7df05a1392 --- /dev/null +++ b/common/test/data/test_about_blob_end_date/policies/2012_Fall.json @@ -0,0 +1,9 @@ +{ + "course/2012_Fall": { + "graceperiod": "2 days 5 hours 59 minutes 59 seconds", + "start": "2015-07-17T12:00", + "end": "2015-09-17T12:00", + "display_name": "Test About Blob End Date", + "graded": "true" + } +} diff --git a/common/test/data/test_end/README.md b/common/test/data/test_end/README.md new file mode 100644 index 0000000000..6fc2f28728 --- /dev/null +++ b/common/test/data/test_end/README.md @@ -0,0 +1,5 @@ +Test course for checking the end date displayed on the course about page. +This course does not have an end_date HTML "blob", but it does have a course end date set. +Therefore the course end date should show on the course about page. + +See also test_about_blob_end_date course. diff --git a/common/test/data/test_end/course.xml b/common/test/data/test_end/course.xml new file mode 100644 index 0000000000..3071ba09ea --- /dev/null +++ b/common/test/data/test_end/course.xml @@ -0,0 +1 @@ + diff --git a/common/test/data/test_end/course/2012_Fall.xml b/common/test/data/test_end/course/2012_Fall.xml new file mode 100644 index 0000000000..c9d2e8702d --- /dev/null +++ b/common/test/data/test_end/course/2012_Fall.xml @@ -0,0 +1,2 @@ + + diff --git a/common/test/data/test_end/policies/2012_Fall.json b/common/test/data/test_end/policies/2012_Fall.json new file mode 100644 index 0000000000..a114f23465 --- /dev/null +++ b/common/test/data/test_end/policies/2012_Fall.json @@ -0,0 +1,9 @@ +{ + "course/2012_Fall": { + "graceperiod": "2 days 5 hours 59 minutes 59 seconds", + "start": "2015-07-17T12:00", + "end": "2015-09-17T12:00", + "display_name": "Test End", + "graded": "true" + } +} diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 979f347d1f..1d3166893e 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -1,26 +1,19 @@ -import logging -from mock import MagicMock, patch +from mock import MagicMock import datetime -import factory -import unittest -import os from django.test import TestCase -from django.http import Http404, HttpResponse +from django.http import Http404 from django.conf import settings from django.test.utils import override_settings from django.contrib.auth.models import User from django.test.client import RequestFactory from student.models import CourseEnrollment -from xmodule.modulestore.django import modulestore, _MODULESTORES -from xmodule.modulestore.exceptions import InvalidLocationError,\ - ItemNotFoundError, NoPathToItem +from xmodule.modulestore.django import modulestore + import courseware.views as views from xmodule.modulestore import Location -from .factories import UserFactory - class Stub(): pass @@ -55,7 +48,6 @@ class TestJumpTo(TestCase): def test_jumpto_invalid_location(self): location = Location('i4x', 'edX', 'toy', 'NoSuchPlace', None) jumpto_url = '%s/%s/jump_to/%s' % ('/courses', self.course_name, location) - expected = 'courses/edX/toy/2012_Fall/courseware/Overview/' response = self.client.get(jumpto_url) self.assertEqual(response.status_code, 404) @@ -124,3 +116,26 @@ class ViewsTestCase(TestCase): request, 'bar', ()) self.assertRaisesRegexp(Http404, 'No data*', views.jump_to, request, 'dummy', self.location) + + def test_no_end_on_about_page(self): + # Toy course has no course end date or about/end_date blob + self.verify_end_date(self.course_id) + + def test_no_end_about_blob(self): + # test_end has a course end date, no end_date HTML blob + self.verify_end_date("edX/test_end/2012_Fall", "Sep 17, 2015") + + def test_about_blob_end_date(self): + # test_about_blob_end_date has both a course end date and an end_date HTML blob. + # HTML blob wins + self.verify_end_date("edX/test_about_blob_end_date/2012_Fall", "Learning never ends") + + def verify_end_date(self, course_id, expected_end_text=None): + request = self.request_factory.get("foo") + request.user = self.user + result = views.course_about(request, course_id) + if expected_end_text is not None: + self.assertContains(result, "Classes End") + self.assertContains(result, expected_end_text) + else: + self.assertNotContains(result, "Classes End") diff --git a/lms/templates/courseware/course_about.html b/lms/templates/courseware/course_about.html index b35c7a1b6f..dc1dc17532 100644 --- a/lms/templates/courseware/course_about.html +++ b/lms/templates/courseware/course_about.html @@ -144,10 +144,20 @@
  • Course Number

    ${course.number}
  • Classes Start

    ${course.start_date_text}
  • - ## End date should come from course.xml, but this is a quick hack - % if get_course_about_section(course, "end_date"): -
  • Classes End

    ${get_course_about_section(course, "end_date")}
  • - % endif + ## We plan to ditch end_date (which is not stored in course metadata), + ## but for backwards compatibility, show about/end_date blob if it exists. + % if get_course_about_section(course, "end_date") or course.end: +
  • +
    +

    Classes End

    + % if get_course_about_section(course, "end_date"): + ${get_course_about_section(course, "end_date")} + % else: + ${course.end_date_text} + % endif + +
  • + % endif % if get_course_about_section(course, "effort"):
  • Estimated Effort

    ${get_course_about_section(course, "effort")}