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