Commit aef4da86 by Nimisha Asthagiri

Refactor Course Catalog fields in CourseDescriptor

parent 8289c321
......@@ -5,9 +5,10 @@ This is a place to put simple functions that operate on course metadata. It
allows us to share code between the CourseDescriptor and CourseOverview
classes, which both need these type of functions.
"""
from datetime import datetime
from datetime import timedelta
from base64 import b32encode
from datetime import datetime, timedelta
import dateutil.parser
from math import exp
from django.utils.timezone import UTC
......@@ -222,3 +223,43 @@ def may_certify_for_course(certificates_display_behavior, certificates_show_befo
or certificates_show_before_end
)
return show_early or has_ended
def sorting_score(start, advertised_start, announcement):
"""
Returns a tuple that can be used to sort the courses according
to how "new" they are. The "newness" score is computed using a
heuristic that takes into account the announcement and
(advertised) start dates of the course if available.
The lower the number the "newer" the course.
"""
# Make courses that have an announcement date have a lower
# score than courses than don't, older courses should have a
# higher score.
announcement, start, now = sorting_dates(start, advertised_start, announcement)
scale = 300.0 # about a year
if announcement:
days = (now - announcement).days
score = -exp(-days / scale)
else:
days = (now - start).days
score = exp(days / scale)
return score
def sorting_dates(start, advertised_start, announcement):
"""
Utility function to get datetime objects for dates used to
compute the is_new flag and the sorting_score.
"""
try:
start = dateutil.parser.parse(advertised_start)
if start.tzinfo is None:
start = start.replace(tzinfo=UTC())
except (ValueError, AttributeError):
start = start
now = datetime.now(UTC())
return announcement, start, now
......@@ -3,12 +3,10 @@ Django module container for classes and operations related to the "Course Module
"""
import logging
from cStringIO import StringIO
from math import exp
from lxml import etree
from path import Path as path
import requests
from datetime import datetime
import dateutil.parser
from lazy import lazy
from xmodule import course_metadata_utils
......@@ -1264,7 +1262,9 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
flag = self.is_new
if flag is None:
# Use a heuristic if the course has not been flagged
announcement, start, now = self._sorting_dates()
announcement, start, now = course_metadata_utils.sorting_dates(
self.start, self.advertised_start, self.announcement
)
if announcement and (now - announcement).days < 30:
# The course has been announced for less that month
return True
......@@ -1284,41 +1284,11 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
Returns a tuple that can be used to sort the courses according
the how "new" they are. The "newness" score is computed using a
heuristic that takes into account the announcement and
(advertized) start dates of the course if available.
(advertised) start dates of the course if available.
The lower the number the "newer" the course.
"""
# Make courses that have an announcement date shave a lower
# score than courses than don't, older courses should have a
# higher score.
announcement, start, now = self._sorting_dates()
scale = 300.0 # about a year
if announcement:
days = (now - announcement).days
score = -exp(-days / scale)
else:
days = (now - start).days
score = exp(days / scale)
return score
def _sorting_dates(self):
# utility function to get datetime objects for dates used to
# compute the is_new flag and the sorting_score
announcement = self.announcement
if announcement is not None:
announcement = announcement
try:
start = dateutil.parser.parse(self.advertised_start)
if start.tzinfo is None:
start = start.replace(tzinfo=UTC())
except (ValueError, AttributeError):
start = self.start
now = datetime.now(UTC())
return announcement, start, now
return course_metadata_utils.sorting_score(self.start, self.advertised_start, self.announcement)
@lazy
def grading_context(self):
......
......@@ -150,14 +150,14 @@ class IsNewCourseTestCase(unittest.TestCase):
# Needed for test_is_newish
datetime_patcher = patch.object(
xmodule.course_module, 'datetime',
xmodule.course_metadata_utils, 'datetime',
Mock(wraps=datetime)
)
mocked_datetime = datetime_patcher.start()
mocked_datetime.now.return_value = NOW
self.addCleanup(datetime_patcher.stop)
@patch('xmodule.course_module.datetime.now')
@patch('xmodule.course_metadata_utils.datetime.now')
def test_sorting_score(self, gmtime_mock):
gmtime_mock.return_value = NOW
......@@ -208,7 +208,7 @@ class IsNewCourseTestCase(unittest.TestCase):
(xmodule.course_module.CourseFields.start.default, 'January 2014', 'January 2014', False, 'January 2014'),
]
@patch('xmodule.course_module.datetime.now')
@patch('xmodule.course_metadata_utils.datetime.now')
def test_start_date_text(self, gmtime_mock):
gmtime_mock.return_value = NOW
for s in self.start_advertised_settings:
......@@ -216,7 +216,7 @@ class IsNewCourseTestCase(unittest.TestCase):
print "Checking start=%s advertised=%s" % (s[0], s[1])
self.assertEqual(d.start_datetime_text(), s[2])
@patch('xmodule.course_module.datetime.now')
@patch('xmodule.course_metadata_utils.datetime.now')
def test_start_date_time_text(self, gmtime_mock):
gmtime_mock.return_value = NOW
for setting in self.start_advertised_settings:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment