Converts the dates on the dashboard, sidebar navigation, and important course dates to user specified time zone.
This commit is contained in:
@@ -7,9 +7,10 @@ from datetime import datetime
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.utils.timezone import UTC
|
||||
from pytz import utc
|
||||
|
||||
from lazy import lazy
|
||||
from openedx.core.lib.time_zone_utils import get_time_zone_abbr
|
||||
from xmodule_django.models import CourseKeyField, LocationKeyField
|
||||
from xmodule.error_module import ErrorDescriptor
|
||||
from xmodule.modulestore.django import modulestore
|
||||
@@ -72,43 +73,43 @@ class CustomCourseForEdX(models.Model):
|
||||
|
||||
def has_started(self):
|
||||
"""Return True if the CCX start date is in the past"""
|
||||
return datetime.now(UTC()) > self.start
|
||||
return datetime.now(utc) > self.start
|
||||
|
||||
def has_ended(self):
|
||||
"""Return True if the CCX due date is set and is in the past"""
|
||||
if self.due is None:
|
||||
return False
|
||||
|
||||
return datetime.now(UTC()) > self.due
|
||||
return datetime.now(utc) > self.due
|
||||
|
||||
def start_datetime_text(self, format_string="SHORT_DATE"):
|
||||
def start_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
|
||||
"""Returns the desired text representation of the CCX start datetime
|
||||
|
||||
The returned value is always expressed in UTC
|
||||
The returned value is in specified time zone, defaulted to UTC.
|
||||
"""
|
||||
i18n = self.course.runtime.service(self.course, "i18n")
|
||||
strftime = i18n.strftime
|
||||
value = strftime(self.start, format_string)
|
||||
value = strftime(self.start.astimezone(time_zone), format_string)
|
||||
if format_string == 'DATE_TIME':
|
||||
value += u' UTC'
|
||||
value += ' ' + get_time_zone_abbr(time_zone, self.start)
|
||||
return value
|
||||
|
||||
def end_datetime_text(self, format_string="SHORT_DATE"):
|
||||
def end_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
|
||||
"""Returns the desired text representation of the CCX due datetime
|
||||
|
||||
If the due date for the CCX is not set, the value returned is the empty
|
||||
string.
|
||||
|
||||
The returned value is always expressed in UTC
|
||||
The returned value is in specified time zone, defaulted to UTC.
|
||||
"""
|
||||
if self.due is None:
|
||||
return ''
|
||||
|
||||
i18n = self.course.runtime.service(self.course, "i18n")
|
||||
strftime = i18n.strftime
|
||||
value = strftime(self.due, format_string)
|
||||
value = strftime(self.due.astimezone(time_zone), format_string)
|
||||
if format_string == 'DATE_TIME':
|
||||
value += u' UTC'
|
||||
value += ' ' + get_time_zone_abbr(time_zone, self.due)
|
||||
return value
|
||||
|
||||
@property
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"""
|
||||
tests for the models
|
||||
"""
|
||||
import ddt
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from django.utils.timezone import UTC
|
||||
from mock import patch
|
||||
from nose.plugins.attrib import attr
|
||||
from pytz import timezone, utc
|
||||
from student.roles import CourseCcxCoachRole
|
||||
from student.tests.factories import (
|
||||
AdminFactory,
|
||||
@@ -23,6 +24,7 @@ from .factories import (
|
||||
from ..overrides import override_field_for_ccx
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@attr('shard_1')
|
||||
class TestCCX(ModuleStoreTestCase):
|
||||
"""Unit tests for the CustomCourseForEdX model
|
||||
@@ -65,7 +67,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
For this reason we test the difference between and make sure it is less
|
||||
than one second.
|
||||
"""
|
||||
expected = datetime.now(UTC())
|
||||
expected = datetime.now(utc)
|
||||
self.set_ccx_override('start', expected)
|
||||
actual = self.ccx.start # pylint: disable=no-member
|
||||
diff = expected - actual
|
||||
@@ -73,7 +75,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
|
||||
def test_ccx_start_caching(self):
|
||||
"""verify that caching the start property works to limit queries"""
|
||||
now = datetime.now(UTC())
|
||||
now = datetime.now(utc)
|
||||
self.set_ccx_override('start', now)
|
||||
with check_mongo_calls(1):
|
||||
# these statements are used entirely to demonstrate the
|
||||
@@ -90,7 +92,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
|
||||
def test_ccx_due_is_correct(self):
|
||||
"""verify that the due datetime for a ccx is correctly retrieved"""
|
||||
expected = datetime.now(UTC())
|
||||
expected = datetime.now(utc)
|
||||
self.set_ccx_override('due', expected)
|
||||
actual = self.ccx.due # pylint: disable=no-member
|
||||
diff = expected - actual
|
||||
@@ -98,7 +100,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
|
||||
def test_ccx_due_caching(self):
|
||||
"""verify that caching the due property works to limit queries"""
|
||||
expected = datetime.now(UTC())
|
||||
expected = datetime.now(utc)
|
||||
self.set_ccx_override('due', expected)
|
||||
with check_mongo_calls(1):
|
||||
# these statements are used entirely to demonstrate the
|
||||
@@ -110,7 +112,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
|
||||
def test_ccx_has_started(self):
|
||||
"""verify that a ccx marked as starting yesterday has started"""
|
||||
now = datetime.now(UTC())
|
||||
now = datetime.now(utc)
|
||||
delta = timedelta(1)
|
||||
then = now - delta
|
||||
self.set_ccx_override('start', then)
|
||||
@@ -118,7 +120,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
|
||||
def test_ccx_has_not_started(self):
|
||||
"""verify that a ccx marked as starting tomorrow has not started"""
|
||||
now = datetime.now(UTC())
|
||||
now = datetime.now(utc)
|
||||
delta = timedelta(1)
|
||||
then = now + delta
|
||||
self.set_ccx_override('start', then)
|
||||
@@ -126,7 +128,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
|
||||
def test_ccx_has_ended(self):
|
||||
"""verify that a ccx that has a due date in the past has ended"""
|
||||
now = datetime.now(UTC())
|
||||
now = datetime.now(utc)
|
||||
delta = timedelta(1)
|
||||
then = now - delta
|
||||
self.set_ccx_override('due', then)
|
||||
@@ -135,7 +137,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
def test_ccx_has_not_ended(self):
|
||||
"""verify that a ccx that has a due date in the future has not eneded
|
||||
"""
|
||||
now = datetime.now(UTC())
|
||||
now = datetime.now(utc)
|
||||
delta = timedelta(1)
|
||||
then = now + delta
|
||||
self.set_ccx_override('due', then)
|
||||
@@ -152,7 +154,7 @@ class TestCCX(ModuleStoreTestCase):
|
||||
}))
|
||||
def test_start_datetime_short_date(self):
|
||||
"""verify that the start date for a ccx formats properly by default"""
|
||||
start = datetime(2015, 1, 1, 12, 0, 0, tzinfo=UTC())
|
||||
start = datetime(2015, 1, 1, 12, 0, 0, tzinfo=utc)
|
||||
expected = "Jan 01, 2015"
|
||||
self.set_ccx_override('start', start)
|
||||
actual = self.ccx.start_datetime_text() # pylint: disable=no-member
|
||||
@@ -163,18 +165,34 @@ class TestCCX(ModuleStoreTestCase):
|
||||
}))
|
||||
def test_start_datetime_date_time_format(self):
|
||||
"""verify that the DATE_TIME format also works as expected"""
|
||||
start = datetime(2015, 1, 1, 12, 0, 0, tzinfo=UTC())
|
||||
start = datetime(2015, 1, 1, 12, 0, 0, tzinfo=utc)
|
||||
expected = "Jan 01, 2015 at 12:00 UTC"
|
||||
self.set_ccx_override('start', start)
|
||||
actual = self.ccx.start_datetime_text('DATE_TIME') # pylint: disable=no-member
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@ddt.data((datetime(2015, 11, 1, 8, 59, 00, tzinfo=utc), "Nov 01, 2015", "Nov 01, 2015 at 01:59 PDT"),
|
||||
(datetime(2015, 11, 1, 9, 00, 00, tzinfo=utc), "Nov 01, 2015", "Nov 01, 2015 at 01:00 PST"))
|
||||
@ddt.unpack
|
||||
def test_start_date_time_zone(self, start_date_time, expected_short_date, expected_date_time):
|
||||
"""
|
||||
verify that start date is correctly converted when time zone specified
|
||||
during normal daylight hours and daylight savings hours
|
||||
"""
|
||||
time_zone = timezone('America/Los_Angeles')
|
||||
|
||||
self.set_ccx_override('start', start_date_time)
|
||||
actual_short_date = self.ccx.start_datetime_text(time_zone=time_zone) # pylint: disable=no-member
|
||||
actual_datetime = self.ccx.start_datetime_text('DATE_TIME', time_zone) # pylint: disable=no-member
|
||||
self.assertEqual(expected_short_date, actual_short_date)
|
||||
self.assertEqual(expected_date_time, actual_datetime)
|
||||
|
||||
@patch('util.date_utils.ugettext', fake_ugettext(translations={
|
||||
"SHORT_DATE_FORMAT": "%b %d, %Y",
|
||||
}))
|
||||
def test_end_datetime_short_date(self):
|
||||
"""verify that the end date for a ccx formats properly by default"""
|
||||
end = datetime(2015, 1, 1, 12, 0, 0, tzinfo=UTC())
|
||||
end = datetime(2015, 1, 1, 12, 0, 0, tzinfo=utc)
|
||||
expected = "Jan 01, 2015"
|
||||
self.set_ccx_override('due', end)
|
||||
actual = self.ccx.end_datetime_text() # pylint: disable=no-member
|
||||
@@ -185,12 +203,28 @@ class TestCCX(ModuleStoreTestCase):
|
||||
}))
|
||||
def test_end_datetime_date_time_format(self):
|
||||
"""verify that the DATE_TIME format also works as expected"""
|
||||
end = datetime(2015, 1, 1, 12, 0, 0, tzinfo=UTC())
|
||||
end = datetime(2015, 1, 1, 12, 0, 0, tzinfo=utc)
|
||||
expected = "Jan 01, 2015 at 12:00 UTC"
|
||||
self.set_ccx_override('due', end)
|
||||
actual = self.ccx.end_datetime_text('DATE_TIME') # pylint: disable=no-member
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@ddt.data((datetime(2015, 11, 1, 8, 59, 00, tzinfo=utc), "Nov 01, 2015", "Nov 01, 2015 at 01:59 PDT"),
|
||||
(datetime(2015, 11, 1, 9, 00, 00, tzinfo=utc), "Nov 01, 2015", "Nov 01, 2015 at 01:00 PST"))
|
||||
@ddt.unpack
|
||||
def test_end_datetime_time_zone(self, end_date_time, expected_short_date, expected_date_time):
|
||||
"""
|
||||
verify that end date is correctly converted when time zone specified
|
||||
during normal daylight hours and daylight savings hours
|
||||
"""
|
||||
time_zone = timezone('America/Los_Angeles')
|
||||
|
||||
self.set_ccx_override('due', end_date_time)
|
||||
actual_short_date = self.ccx.end_datetime_text(time_zone=time_zone) # pylint: disable=no-member
|
||||
actual_datetime = self.ccx.end_datetime_text('DATE_TIME', time_zone) # pylint: disable=no-member
|
||||
self.assertEqual(expected_short_date, actual_short_date)
|
||||
self.assertEqual(expected_date_time, actual_datetime)
|
||||
|
||||
@patch('util.date_utils.ugettext', fake_ugettext(translations={
|
||||
"DATE_TIME_FORMAT": "%b %d, %Y at %H:%M",
|
||||
}))
|
||||
|
||||
@@ -12,11 +12,12 @@ from django.utils.translation import ugettext_lazy
|
||||
from django.utils.translation import to_locale, get_language
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from lazy import lazy
|
||||
import pytz
|
||||
from pytz import utc
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from lms.djangoapps.commerce.utils import EcommerceService
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline, SoftwareSecurePhotoVerification
|
||||
from openedx.core.lib.time_zone_utils import get_time_zone_abbr, get_user_time_zone
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
|
||||
@@ -64,6 +65,11 @@ class DateSummary(object):
|
||||
"""The text of the link."""
|
||||
return ''
|
||||
|
||||
@property
|
||||
def time_zone(self):
|
||||
"""The time zone to display in"""
|
||||
return get_user_time_zone(self.user)
|
||||
|
||||
def __init__(self, course, user):
|
||||
self.course = course
|
||||
self.user = user
|
||||
@@ -93,7 +99,7 @@ class DateSummary(object):
|
||||
if self.date is None:
|
||||
return ''
|
||||
locale = to_locale(get_language())
|
||||
delta = self.date - datetime.now(pytz.UTC)
|
||||
delta = self.date - datetime.now(utc)
|
||||
try:
|
||||
relative_date = format_timedelta(delta, locale=locale)
|
||||
# Babel doesn't have translations for Esperanto, so we get
|
||||
@@ -111,7 +117,7 @@ class DateSummary(object):
|
||||
date_format = _(u"{relative} ago - {absolute}") if date_has_passed else _(u"in {relative} - {absolute}")
|
||||
return date_format.format(
|
||||
relative=relative_date,
|
||||
absolute=self.date.strftime(self.date_format.encode('utf-8')).decode('utf-8'),
|
||||
absolute=self.date.astimezone(self.time_zone).strftime(self.date_format.encode('utf-8')).decode('utf-8'),
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -123,7 +129,7 @@ class DateSummary(object):
|
||||
future.
|
||||
"""
|
||||
if self.date is not None:
|
||||
return datetime.now(pytz.UTC) <= self.date
|
||||
return datetime.now(utc) <= self.date
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
@@ -143,7 +149,7 @@ class TodaysDate(DateSummary):
|
||||
|
||||
@property
|
||||
def date_format(self):
|
||||
return u'%b %d, %Y (%H:%M {utc})'.format(utc=_('UTC'))
|
||||
return u'%b %d, %Y (%H:%M {tz_abbr})'.format(tz_abbr=get_time_zone_abbr(self.time_zone))
|
||||
|
||||
# The date is shown in the title, no need to display it again.
|
||||
def get_context(self):
|
||||
@@ -153,12 +159,12 @@ class TodaysDate(DateSummary):
|
||||
|
||||
@property
|
||||
def date(self):
|
||||
return datetime.now(pytz.UTC)
|
||||
return datetime.now(utc)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return _(u'Today is {date}').format(
|
||||
date=datetime.now(pytz.UTC).strftime(self.date_format.encode('utf-8')).decode('utf-8')
|
||||
date=self.date.astimezone(self.time_zone).strftime(self.date_format.encode('utf-8')).decode('utf-8')
|
||||
)
|
||||
|
||||
|
||||
@@ -187,7 +193,7 @@ class CourseEndDate(DateSummary):
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
if datetime.now(pytz.UTC) <= self.date:
|
||||
if datetime.now(utc) <= self.date:
|
||||
mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
if is_active and CourseMode.is_eligible_for_certificate(mode):
|
||||
return _('To earn a certificate, you must complete all requirements before this date.')
|
||||
@@ -332,7 +338,7 @@ class VerificationDeadlineDate(DateSummary):
|
||||
Return True if a verification deadline exists, and has already passed.
|
||||
"""
|
||||
deadline = self.date
|
||||
return deadline is not None and deadline <= datetime.now(pytz.UTC)
|
||||
return deadline is not None and deadline <= datetime.now(utc)
|
||||
|
||||
def must_retry(self):
|
||||
"""Return True if the user must re-submit verification, False otherwise."""
|
||||
|
||||
@@ -4,9 +4,9 @@ from datetime import datetime, timedelta
|
||||
|
||||
import ddt
|
||||
from django.core.urlresolvers import reverse
|
||||
import freezegun
|
||||
from freezegun import freeze_time
|
||||
from nose.plugins.attrib import attr
|
||||
import pytz
|
||||
from pytz import utc
|
||||
|
||||
from commerce.models import CommerceConfiguration
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
@@ -21,6 +21,7 @@ from courseware.date_summary import (
|
||||
VerifiedUpgradeDeadlineDate,
|
||||
)
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||
from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
|
||||
@@ -50,7 +51,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
sku=None
|
||||
):
|
||||
"""Set up the course and user for this test."""
|
||||
now = datetime.now(pytz.UTC)
|
||||
now = datetime.now(utc)
|
||||
self.course = CourseFactory.create( # pylint: disable=attribute-defined-outside-init
|
||||
start=now + timedelta(days=days_till_start)
|
||||
)
|
||||
@@ -175,21 +176,49 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
|
||||
## TodaysDate
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
def test_todays_date(self):
|
||||
def _today_date_helper(self, expected_display_date):
|
||||
"""
|
||||
Helper function to test that today's date block renders correctly
|
||||
and displays the correct time, accounting for daylight savings
|
||||
"""
|
||||
self.setup_course_and_user()
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = TodaysDate(self.course, self.user)
|
||||
self.assertTrue(block.is_enabled)
|
||||
self.assertEqual(block.date, datetime.now(pytz.UTC))
|
||||
self.assertEqual(block.title, 'Today is Jan 02, 2015 (00:00 UTC)')
|
||||
self.assertEqual(block.date, datetime.now(utc))
|
||||
self.assertEqual(block.title, 'Today is {date}'.format(date=expected_display_date))
|
||||
self.assertNotIn('date-summary-date', block.render())
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
@freeze_time('2015-11-01 08:59:00')
|
||||
def test_todays_date_time_zone_daylight(self):
|
||||
"""
|
||||
Test today's date block displays correctly during
|
||||
daylight savings hours
|
||||
"""
|
||||
self._today_date_helper('Nov 01, 2015 (01:59 PDT)')
|
||||
|
||||
@freeze_time('2015-11-01 09:00:00')
|
||||
def test_todays_date_time_zone_normal(self):
|
||||
"""
|
||||
Test today's date block displays correctly during
|
||||
normal daylight hours
|
||||
"""
|
||||
self._today_date_helper('Nov 01, 2015 (01:00 PST)')
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
def test_todays_date_render(self):
|
||||
self.setup_course_and_user()
|
||||
block = TodaysDate(self.course, self.user)
|
||||
self.assertIn('Jan 02, 2015', block.render())
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
def test_todays_date_render_time_zone(self):
|
||||
self.setup_course_and_user()
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = TodaysDate(self.course, self.user)
|
||||
# Today is 'Jan 01, 2015' because of time zone offset
|
||||
self.assertIn('Jan 01, 2015', block.render())
|
||||
|
||||
## CourseStartDate
|
||||
|
||||
def test_course_start_date(self):
|
||||
@@ -197,12 +226,20 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
block = CourseStartDate(self.course, self.user)
|
||||
self.assertEqual(block.date, self.course.start)
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
@freeze_time('2015-01-02')
|
||||
def test_start_date_render(self):
|
||||
self.setup_course_and_user()
|
||||
block = CourseStartDate(self.course, self.user)
|
||||
self.assertIn('in 1 day - Jan 03, 2015', block.render())
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
def test_start_date_render_time_zone(self):
|
||||
self.setup_course_and_user()
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = CourseStartDate(self.course, self.user)
|
||||
# Jan 02 is in 1 day because of time zone offset
|
||||
self.assertIn('in 1 day - Jan 02, 2015', block.render())
|
||||
|
||||
## CourseEndDate
|
||||
|
||||
def test_course_end_date_for_certificate_eligible_mode(self):
|
||||
@@ -231,11 +268,11 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
|
||||
## VerifiedUpgradeDeadlineDate
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
@freeze_time('2015-01-02')
|
||||
def test_verified_upgrade_deadline_date(self):
|
||||
self.setup_course_and_user(days_till_upgrade_deadline=1)
|
||||
block = VerifiedUpgradeDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.date, datetime.now(pytz.UTC) + timedelta(days=1))
|
||||
self.assertEqual(block.date, datetime.now(utc) + timedelta(days=1))
|
||||
self.assertEqual(block.link, reverse('verify_student_upgrade_and_verify', args=(self.course.id,)))
|
||||
|
||||
def test_without_upgrade_deadline(self):
|
||||
@@ -267,13 +304,13 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertFalse(block.is_enabled)
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
@freeze_time('2015-01-02')
|
||||
def test_verification_deadline_date_upcoming(self):
|
||||
self.setup_course_and_user(days_till_start=-1)
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.css_class, 'verification-deadline-upcoming')
|
||||
self.assertEqual(block.title, 'Verification Deadline')
|
||||
self.assertEqual(block.date, datetime.now(pytz.UTC) + timedelta(days=14))
|
||||
self.assertEqual(block.date, datetime.now(utc) + timedelta(days=14))
|
||||
self.assertEqual(
|
||||
block.description,
|
||||
'You must successfully complete verification before this date to qualify for a Verified Certificate.'
|
||||
@@ -281,13 +318,13 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
self.assertEqual(block.link_text, 'Verify My Identity')
|
||||
self.assertEqual(block.link, reverse('verify_student_verify_now', args=(self.course.id,)))
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
@freeze_time('2015-01-02')
|
||||
def test_verification_deadline_date_retry(self):
|
||||
self.setup_course_and_user(days_till_start=-1, verification_status='denied')
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.css_class, 'verification-deadline-retry')
|
||||
self.assertEqual(block.title, 'Verification Deadline')
|
||||
self.assertEqual(block.date, datetime.now(pytz.UTC) + timedelta(days=14))
|
||||
self.assertEqual(block.date, datetime.now(utc) + timedelta(days=14))
|
||||
self.assertEqual(
|
||||
block.description,
|
||||
'You must successfully complete verification before this date to qualify for a Verified Certificate.'
|
||||
@@ -295,7 +332,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
self.assertEqual(block.link_text, 'Retry Verification')
|
||||
self.assertEqual(block.link, reverse('verify_student_reverify'))
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
@freeze_time('2015-01-02')
|
||||
def test_verification_deadline_date_denied(self):
|
||||
self.setup_course_and_user(
|
||||
days_till_start=-10,
|
||||
@@ -305,7 +342,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.css_class, 'verification-deadline-passed')
|
||||
self.assertEqual(block.title, 'Missed Verification Deadline')
|
||||
self.assertEqual(block.date, datetime.now(pytz.UTC) + timedelta(days=-1))
|
||||
self.assertEqual(block.date, datetime.now(utc) + timedelta(days=-1))
|
||||
self.assertEqual(
|
||||
block.description,
|
||||
"Unfortunately you missed this course's deadline for a successful verification."
|
||||
@@ -313,7 +350,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
self.assertEqual(block.link_text, 'Learn More')
|
||||
self.assertEqual(block.link, '')
|
||||
|
||||
@freezegun.freeze_time('2015-01-02')
|
||||
@freeze_time('2015-01-02')
|
||||
@ddt.data(
|
||||
(-1, '1 day ago - Jan 01, 2015'),
|
||||
(1, 'in 1 day - Jan 03, 2015')
|
||||
@@ -327,3 +364,20 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
)
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.get_context()['date'], expected_date_string)
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
@ddt.data(
|
||||
# dates reflected from Jan 01, 2015 because of time zone offset
|
||||
(-1, '1 day ago - Dec 31, 2014'),
|
||||
(1, 'in 1 day - Jan 02, 2015')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_render_date_string_time_zone(self, delta, expected_date_string):
|
||||
self.setup_course_and_user(
|
||||
days_till_start=-10,
|
||||
verification_status='denied',
|
||||
days_till_verification_deadline=delta,
|
||||
)
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.get_context()['date'], expected_date_string)
|
||||
|
||||
@@ -26,6 +26,7 @@ from lang_pref import LANGUAGE_KEY
|
||||
from xblock.fragment import Fragment
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from openedx.core.lib.gating import api as gating_api
|
||||
from openedx.core.lib.time_zone_utils import get_user_time_zone
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
|
||||
from shoppingcart.models import CourseRegistrationCode
|
||||
from student.models import CourseEnrollment
|
||||
@@ -514,6 +515,7 @@ def render_accordion(request, course, table_of_contents):
|
||||
('course_id', unicode(course.id)),
|
||||
('csrf', csrf(request)['csrf_token']),
|
||||
('due_date_display_format', course.due_date_display_format),
|
||||
('time_zone', get_user_time_zone(request.user).zone),
|
||||
] + TEMPLATE_IMPORTS.items()
|
||||
)
|
||||
return render_to_string('courseware/accordion.html', context)
|
||||
|
||||
@@ -31,7 +31,7 @@ from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangoapps.theming.helpers import is_request_in_themed_site, get_value as get_themed_value
|
||||
from openedx.core.djangoapps.user_api.accounts.api import request_password_change
|
||||
from openedx.core.djangoapps.user_api.errors import UserNotFound
|
||||
from openedx.core.djangoapps.user_api.models import UserPreference
|
||||
from openedx.core.lib.time_zone_utils import TIME_ZONE_CHOICES
|
||||
from openedx.core.lib.edx_api_utils import get_edx_api_data
|
||||
from student.models import UserProfile
|
||||
from student.views import (
|
||||
@@ -449,7 +449,7 @@ def account_settings_context(request):
|
||||
}, 'preferred_language': {
|
||||
'options': all_languages(),
|
||||
}, 'time_zone': {
|
||||
'options': UserPreference.TIME_ZONE_CHOICES,
|
||||
'options': TIME_ZONE_CHOICES,
|
||||
'enabled': settings.FEATURES.get('ENABLE_TIME_ZONE_PREFERENCE'),
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user