diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py
index 06c59d7937..39805fd85f 100644
--- a/common/djangoapps/student/views.py
+++ b/common/djangoapps/student/views.py
@@ -78,7 +78,7 @@ def index(request, extra_context={}, user=None):
courses = get_courses(None, domain=domain)
# Sort courses by how far are they from they start day
- key = lambda course: course.metadata['days_to_start']
+ key = lambda course: course.days_until_start
courses = sorted(courses, key=key, reverse=True)
# Get the 3 most recent news
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index 5253d2976f..163e40b343 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -1,9 +1,9 @@
-from fs.errors import ResourceNotFoundError
import logging
from lxml import etree
-from path import path # NOTE (THK): Only used for detecting presence of syllabus
+from path import path # NOTE (THK): Only used for detecting presence of syllabus
import requests
import time
+from datetime import datetime
from xmodule.util.decorators import lazyproperty
from xmodule.graders import load_grading_policy
@@ -13,6 +13,7 @@ from xmodule.timeparse import parse_time, stringify_time
log = logging.getLogger(__name__)
+
class CourseDescriptor(SequenceDescriptor):
module_class = SequenceModule
@@ -165,6 +166,38 @@ class CourseDescriptor(SequenceDescriptor):
def show_calculator(self):
return self.metadata.get("show_calculator", None) == "Yes"
+ @property
+ def is_new(self):
+ # The course is "new" if either if the metadata flag is_new is
+ # true or if the course has not started yet
+ flag = self.metadata.get('is_new', None)
+ if flag is None:
+ return self.days_until_start > 1
+ elif isinstance(flag, basestring):
+ return flag.lower() in ['true', 'yes', 'y']
+ else:
+ return bool(flag)
+
+ @property
+ def days_until_start(self):
+ def convert_to_datetime(timestamp):
+ return datetime.fromtimestamp(time.mktime(timestamp))
+
+ start_date = convert_to_datetime(self.start)
+
+ # Try to use course advertised date if we can parse it
+ advertised_start = self.metadata.get('advertised_start', None)
+ if advertised_start:
+ try:
+ start_date = datetime.strptime(advertised_start,
+ "%Y-%m-%dT%H:%M")
+ except ValueError:
+ pass # Invalid date, keep using 'start''
+
+ now = convert_to_datetime(time.gmtime())
+ days_until_start = (start_date - now).days
+ return days_until_start
+
@lazyproperty
def grading_context(self):
"""
@@ -244,7 +277,6 @@ class CourseDescriptor(SequenceDescriptor):
raise ValueError("{0} is not a course location".format(loc))
return "/".join([loc.org, loc.course, loc.name])
-
@property
def id(self):
"""Return the course_id for this course"""
@@ -258,7 +290,7 @@ class CourseDescriptor(SequenceDescriptor):
# form text...
if parsed_advertised_start is None and \
('advertised_start' in self.metadata):
- return self.metadata['advertised_start']
+ return self.metadata['advertised_start']
displayed_start = parsed_advertised_start or self.start
@@ -341,4 +373,3 @@ class CourseDescriptor(SequenceDescriptor):
@property
def org(self):
return self.location.org
-
diff --git a/common/lib/xmodule/xmodule/tests/test_course_module.py b/common/lib/xmodule/xmodule/tests/test_course_module.py
new file mode 100644
index 0000000000..63eaec1f61
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_course_module.py
@@ -0,0 +1,90 @@
+import unittest
+from time import strptime, gmtime
+from fs.memoryfs import MemoryFS
+
+from mock import Mock, patch
+
+from xmodule.modulestore.xml import ImportSystem, XMLModuleStore
+
+
+ORG = 'test_org'
+COURSE = 'test_course'
+
+NOW = strptime('2013-01-01T01:00:00', '%Y-%m-%dT%H:%M:00')
+
+
+class DummySystem(ImportSystem):
+ @patch('xmodule.modulestore.xml.OSFS', lambda dir: MemoryFS())
+ def __init__(self, load_error_modules):
+
+ xmlstore = XMLModuleStore("data_dir", course_dirs=[],
+ load_error_modules=load_error_modules)
+ course_id = "/".join([ORG, COURSE, 'test_run'])
+ course_dir = "test_dir"
+ policy = {}
+ error_tracker = Mock()
+ parent_tracker = Mock()
+
+ super(DummySystem, self).__init__(
+ xmlstore,
+ course_id,
+ course_dir,
+ policy,
+ error_tracker,
+ parent_tracker,
+ load_error_modules=load_error_modules,
+ )
+
+
+class IsNewCourseTestCase(unittest.TestCase):
+ """Make sure the property is_new works on courses"""
+ @staticmethod
+ def get_dummy_course(start, is_new=None, load_error_modules=True):
+ """Get a dummy course"""
+
+ system = DummySystem(load_error_modules)
+ is_new = '' if is_new is None else 'is_new="{0}"'.format(is_new).lower()
+
+ start_xml = '''
+
+
+ Two houses, ...
+
+
+ '''.format(org=ORG, course=COURSE, start=start, is_new=is_new)
+
+ return system.process_xml(start_xml)
+
+ @patch('xmodule.course_module.time.gmtime')
+ def test_non_started_yet(self, gmtime_mock):
+ descriptor = self.get_dummy_course(start='2013-01-05T12:00')
+ gmtime_mock.return_value = NOW
+ assert(descriptor.is_new == True)
+ assert(descriptor.days_until_start == 4)
+
+ @patch('xmodule.course_module.time.gmtime')
+ def test_already_started(self, gmtime_mock):
+ gmtime_mock.return_value = NOW
+
+ descriptor = self.get_dummy_course(start='2012-12-02T12:00')
+ assert(descriptor.is_new == False)
+ assert(descriptor.days_until_start < 0)
+
+ @patch('xmodule.course_module.time.gmtime')
+ def test_is_new_set(self, gmtime_mock):
+ gmtime_mock.return_value = NOW
+
+ descriptor = self.get_dummy_course(start='2012-12-02T12:00', is_new=True)
+ assert(descriptor.is_new == True)
+ assert(descriptor.days_until_start < 0)
+
+ descriptor = self.get_dummy_course(start='2013-02-02T12:00', is_new=False)
+ assert(descriptor.is_new == False)
+ assert(descriptor.days_until_start > 0)
+
+ descriptor = self.get_dummy_course(start='2013-02-02T12:00', is_new=True)
+ assert(descriptor.is_new == True)
+ assert(descriptor.days_until_start > 0)
diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py
index dc530bdebc..7c0d30ebd8 100644
--- a/lms/djangoapps/courseware/courses.py
+++ b/lms/djangoapps/courseware/courses.py
@@ -233,35 +233,5 @@ def get_courses(user, domain=None):
courses = branding.get_visible_courses(domain)
courses = [c for c in courses if has_access(user, c, 'see_exists')]
- # Add metadata about the start day and if the course is new
- for course in courses:
- days_to_start = _get_course_days_to_start(course)
-
- metadata = course.metadata
- metadata['days_to_start'] = days_to_start
- metadata['is_new'] = course.metadata.get('is_new', days_to_start > 1)
-
courses = sorted(courses, key=lambda course:course.number)
return courses
-
-
-def _get_course_days_to_start(course):
- from datetime import datetime as dt
- from time import mktime, gmtime
-
- convert_to_datetime = lambda ts: dt.fromtimestamp(mktime(ts))
-
- start_date = convert_to_datetime(course.start)
-
- # If the course has a valid advertised date, use that instead
- advertised_start = course.metadata.get('advertised_start', None)
- if advertised_start:
- try:
- start_date = dt.strptime(advertised_start, "%Y-%m-%dT%H:%M")
- except ValueError:
- pass # Invalid date, keep using course.start
-
- now = convert_to_datetime(gmtime())
- days_to_start = (start_date - now).days
-
- return days_to_start
diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py
index f6e87dfe9f..9e52e2b281 100644
--- a/lms/djangoapps/courseware/views.py
+++ b/lms/djangoapps/courseware/views.py
@@ -70,7 +70,7 @@ def courses(request):
courses = get_courses(request.user, domain=request.META.get('HTTP_HOST'))
# Sort courses by how far are they from they start day
- key = lambda course: course.metadata['days_to_start']
+ key = lambda course: course.days_until_start
courses = sorted(courses, key=key, reverse=True)
return render_to_response("courseware/courses.html", {'courses': courses})
@@ -440,7 +440,7 @@ def university_profile(request, org_id):
domain=request.META.get('HTTP_HOST'))[org_id]
# Sort courses by how far are they from they start day
- key = lambda course: course.metadata['days_to_start']
+ key = lambda course: course.days_until_start
courses = sorted(courses, key=key, reverse=True)
context = dict(courses=courses, org_id=org_id)
diff --git a/lms/templates/course.html b/lms/templates/course.html
index a3217d2da5..a2eff572e1 100644
--- a/lms/templates/course.html
+++ b/lms/templates/course.html
@@ -5,7 +5,7 @@
%>
<%page args="course" />
- %if course.metadata.get('is_new'):
+ %if course.is_new:
New
%endif