Commit e12a704c by Diana Huang Committed by Andy Armstrong

Add new course dates fragment.

Also add it to the course home page.
parent 97c08451
......@@ -527,19 +527,19 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
self.assertNotContains(response, "Schools & Partners")
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@freezegun.freeze_time('2015-01-02')
def test_course_closed(self):
for mode in ["honor", "verified"]:
CourseModeFactory(mode_slug=mode, course_id=self.course.id)
with freezegun.freeze_time('2015-01-02'):
for mode in ["honor", "verified"]:
CourseModeFactory(mode_slug=mode, course_id=self.course.id)
self.course.enrollment_end = datetime(2015, 01, 01)
modulestore().update_item(self.course, self.user.id)
self.course.enrollment_end = datetime(2015, 01, 01)
modulestore().update_item(self.course, self.user.id)
url = reverse('course_modes_choose', args=[unicode(self.course.id)])
response = self.client.get(url)
# URL-encoded version of 1/1/15, 12:00 AM
redirect_url = reverse('dashboard') + '?course_closed=1%2F1%2F15%2C+12%3A00+AM'
self.assertRedirects(response, redirect_url)
url = reverse('course_modes_choose', args=[unicode(self.course.id)])
response = self.client.get(url)
# URL-encoded version of 1/1/15, 12:00 AM
redirect_url = reverse('dashboard') + '?course_closed=1%2F1%2F15%2C+12%3A00+AM'
self.assertRedirects(response, redirect_url)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
......
......@@ -31,7 +31,6 @@ class InMemoryBackend(object):
self.events.append(event)
@freeze_time(FROZEN_TIME)
@override_settings(
EVENT_TRACKING_BACKENDS=IN_MEMORY_BACKEND_CONFIG
)
......@@ -46,6 +45,10 @@ class EventTrackingTestCase(TestCase):
# Make this more robust to the addition of new events that the test doesn't care about.
def setUp(self):
freezer = freeze_time(FROZEN_TIME)
freezer.start()
self.addCleanup(freezer.stop)
super(EventTrackingTestCase, self).setUp()
self.recreate_tracker()
......
......@@ -107,7 +107,7 @@ def server_track(request, event_type, event, page=None):
"event": event,
"agent": _get_request_header(request, 'HTTP_USER_AGENT').decode('latin1'),
"page": page,
"time": datetime.datetime.utcnow(),
"time": datetime.datetime.utcnow().replace(tzinfo=pytz.utc),
"host": _get_request_header(request, 'SERVER_NAME'),
"context": eventtracker.get_tracker().resolve_context(),
}
......@@ -155,7 +155,7 @@ def task_track(request_info, task_info, event_type, event, page=None):
"event": full_event,
"agent": request_info.get('agent', 'unknown'),
"page": page,
"time": datetime.datetime.utcnow(),
"time": datetime.datetime.utcnow().replace(tzinfo=pytz.utc),
"host": request_info.get('host', 'unknown'),
"context": eventtracker.get_tracker().resolve_context(),
}
......
......@@ -139,42 +139,42 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
self.assertIn("seq_module.html", html)
self.assertIn("'banner_text': None", html)
@freeze_time(COURSE_END_DATE)
def test_hidden_content_past_due(self):
progress_url = 'http://test_progress_link'
html = self._get_rendered_student_view(
self.sequence_4_1,
extra_context=dict(progress_url=progress_url),
)
self.assertIn("hidden_content.html", html)
self.assertIn(progress_url, html)
with freeze_time(COURSE_END_DATE):
progress_url = 'http://test_progress_link'
html = self._get_rendered_student_view(
self.sequence_4_1,
extra_context=dict(progress_url=progress_url),
)
self.assertIn("hidden_content.html", html)
self.assertIn(progress_url, html)
@freeze_time(COURSE_END_DATE)
def test_masquerade_hidden_content_past_due(self):
html = self._get_rendered_student_view(
self.sequence_4_1,
extra_context=dict(specific_masquerade=True),
)
self.assertIn("seq_module.html", html)
self.assertIn(
"'banner_text': 'Because the due date has passed, "
"this assignment is hidden from the learner.'",
html
)
with freeze_time(COURSE_END_DATE):
html = self._get_rendered_student_view(
self.sequence_4_1,
extra_context=dict(specific_masquerade=True),
)
self.assertIn("seq_module.html", html)
self.assertIn(
"'banner_text': 'Because the due date has passed, "
"this assignment is hidden from the learner.'",
html
)
@freeze_time(PAST_DUE_BEFORE_END_DATE)
def test_hidden_content_self_paced_past_due_before_end(self):
html = self._get_rendered_student_view(self.sequence_4_1, self_paced=True)
self.assertIn("seq_module.html", html)
self.assertIn("'banner_text': None", html)
with freeze_time(PAST_DUE_BEFORE_END_DATE):
html = self._get_rendered_student_view(self.sequence_4_1, self_paced=True)
self.assertIn("seq_module.html", html)
self.assertIn("'banner_text': None", html)
@freeze_time(COURSE_END_DATE + timedelta(days=7))
def test_hidden_content_self_paced_past_end(self):
progress_url = 'http://test_progress_link'
html = self._get_rendered_student_view(
self.sequence_4_1,
extra_context=dict(progress_url=progress_url),
self_paced=True,
)
self.assertIn("hidden_content.html", html)
self.assertIn(progress_url, html)
with freeze_time(COURSE_END_DATE + timedelta(days=7)):
progress_url = 'http://test_progress_link'
html = self._get_rendered_student_view(
self.sequence_4_1,
extra_context=dict(progress_url=progress_url),
self_paced=True,
)
self.assertIn("hidden_content.html", html)
self.assertIn(progress_url, html)
......@@ -35,8 +35,11 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase):
display_name='Verified Course'
)
@freeze_time(now)
def setUp(self):
freezer = freeze_time(self.now)
freezer.start()
self.addCleanup(freezer.stop)
super(CertificatesRestApiTest, self).setUp()
self.student = UserFactory.create(password=USER_PASSWORD)
......
......@@ -326,8 +326,10 @@ class CertificateGetTests(SharedModuleStoreTestCase):
now = timezone.now()
@classmethod
@freeze_time(now)
def setUpClass(cls):
cls.freezer = freeze_time(cls.now)
cls.freezer.start()
super(CertificateGetTests, cls).setUpClass()
cls.student = UserFactory()
cls.student_no_cert = UserFactory()
......@@ -365,6 +367,10 @@ class CertificateGetTests(SharedModuleStoreTestCase):
verify_uuid=cls.uuid,
)
@classmethod
def tearDownClass(cls):
cls.freezer.stop()
def test_get_certificate_for_user(self):
"""
Test to get a certificate for a user for a specific course.
......
......@@ -33,39 +33,38 @@ class EdxRestApiClientTest(TestCase):
self.user = UserFactory()
@httpretty.activate
@freeze_time('2015-7-2')
def test_tracking_context(self):
"""
Ensure the tracking context is set up in the api client correctly and
automatically.
"""
with freeze_time('2015-7-2'):
# fake an E-Commerce API request.
httpretty.register_uri(
httpretty.POST,
'{}/baskets/1/'.format(settings.ECOMMERCE_API_URL.strip('/')),
status=200, body='{}',
adding_headers={'Content-Type': JSON}
)
# fake an E-Commerce API request.
httpretty.register_uri(
httpretty.POST,
'{}/baskets/1/'.format(settings.ECOMMERCE_API_URL.strip('/')),
status=200, body='{}',
adding_headers={'Content-Type': JSON}
)
mock_tracker = mock.Mock()
mock_tracker.resolve_context = mock.Mock(return_value={'client_id': self.TEST_CLIENT_ID, 'ip': '127.0.0.1'})
with mock.patch('openedx.core.djangoapps.commerce.utils.tracker.get_tracker', return_value=mock_tracker):
ecommerce_api_client(self.user).baskets(1).post()
mock_tracker = mock.Mock()
mock_tracker.resolve_context = mock.Mock(return_value={'client_id': self.TEST_CLIENT_ID, 'ip': '127.0.0.1'})
with mock.patch('openedx.core.djangoapps.commerce.utils.tracker.get_tracker', return_value=mock_tracker):
ecommerce_api_client(self.user).baskets(1).post()
# Verify the JWT includes the tracking context for the user
actual_header = httpretty.last_request().headers['Authorization']
# Verify the JWT includes the tracking context for the user
actual_header = httpretty.last_request().headers['Authorization']
claims = {
'tracking_context': {
'lms_user_id': self.user.id, # pylint: disable=no-member
'lms_client_id': self.TEST_CLIENT_ID,
'lms_ip': '127.0.0.1',
claims = {
'tracking_context': {
'lms_user_id': self.user.id, # pylint: disable=no-member
'lms_client_id': self.TEST_CLIENT_ID,
'lms_ip': '127.0.0.1',
}
}
}
expected_jwt = JwtBuilder(self.user).build_token(['email', 'profile'], additional_claims=claims)
expected_header = 'JWT {}'.format(expected_jwt)
self.assertEqual(actual_header, expected_header)
expected_jwt = JwtBuilder(self.user).build_token(['email', 'profile'], additional_claims=claims)
expected_header = 'JWT {}'.format(expected_jwt)
self.assertEqual(actual_header, expected_header)
@httpretty.activate
def test_client_unicode(self):
......
......@@ -1834,24 +1834,24 @@ class TestXmoduleRuntimeEvent(TestSubmittingProblems):
self.assertIsNone(student_module.grade)
self.assertIsNone(student_module.max_grade)
@freeze_time(datetime.now().replace(tzinfo=pytz.UTC))
@patch('lms.djangoapps.grades.signals.handlers.PROBLEM_RAW_SCORE_CHANGED.send')
def test_score_change_signal(self, send_mock):
"""Test that a Django signal is generated when a score changes"""
self.set_module_grade_using_publish(self.grade_dict)
expected_signal_kwargs = {
'sender': None,
'raw_possible': self.grade_dict['max_value'],
'raw_earned': self.grade_dict['value'],
'weight': None,
'user_id': self.student_user.id,
'course_id': unicode(self.course.id),
'usage_id': unicode(self.problem.location),
'only_if_higher': None,
'modified': datetime.now().replace(tzinfo=pytz.UTC),
'score_db_table': 'csm',
}
send_mock.assert_called_with(**expected_signal_kwargs)
with freeze_time(datetime.now().replace(tzinfo=pytz.UTC)):
self.set_module_grade_using_publish(self.grade_dict)
expected_signal_kwargs = {
'sender': None,
'raw_possible': self.grade_dict['max_value'],
'raw_earned': self.grade_dict['value'],
'weight': None,
'user_id': self.student_user.id,
'course_id': unicode(self.course.id),
'usage_id': unicode(self.problem.location),
'only_if_higher': None,
'modified': datetime.now().replace(tzinfo=pytz.UTC),
'score_db_table': 'csm',
}
send_mock.assert_called_with(**expected_signal_kwargs)
@attr(shard=1)
......
......@@ -706,32 +706,32 @@ class ViewsTestCase(ModuleStoreTestCase):
('Canada/Yukon', -8), # UTC - 8
('Europe/Moscow', 4)) # UTC + 3 + 1 for daylight savings
@ddt.unpack
@freeze_time('2012-01-01')
def test_submission_history_timezone(self, timezone, hour_diff):
with (override_settings(TIME_ZONE=timezone)):
course = CourseFactory.create()
course_key = course.id
client = Client()
admin = AdminFactory.create()
self.assertTrue(client.login(username=admin.username, password='test'))
state_client = DjangoXBlockUserStateClient(admin)
usage_key = course_key.make_usage_key('problem', 'test-history')
state_client.set(
username=admin.username,
block_key=usage_key,
state={'field_a': 'x', 'field_b': 'y'}
)
url = reverse('submission_history', kwargs={
'course_id': unicode(course_key),
'student_username': admin.username,
'location': unicode(usage_key),
})
response = client.get(url)
response_content = HTMLParser().unescape(response.content)
expected_time = datetime.now() + timedelta(hours=hour_diff)
expected_tz = expected_time.strftime('%Z')
self.assertIn(expected_tz, response_content)
self.assertIn(str(expected_time), response_content)
with freeze_time('2012-01-01'):
with (override_settings(TIME_ZONE=timezone)):
course = CourseFactory.create()
course_key = course.id
client = Client()
admin = AdminFactory.create()
self.assertTrue(client.login(username=admin.username, password='test'))
state_client = DjangoXBlockUserStateClient(admin)
usage_key = course_key.make_usage_key('problem', 'test-history')
state_client.set(
username=admin.username,
block_key=usage_key,
state={'field_a': 'x', 'field_b': 'y'}
)
url = reverse('submission_history', kwargs={
'course_id': unicode(course_key),
'student_username': admin.username,
'location': unicode(usage_key),
})
response = client.get(url)
response_content = HTMLParser().unescape(response.content)
expected_time = datetime.now() + timedelta(hours=hour_diff)
expected_tz = expected_time.strftime('%Z')
self.assertIn(expected_tz, response_content)
self.assertIn(str(expected_time), response_content)
def _email_opt_in_checkbox(self, response, org_name_string=None):
"""Check if the email opt-in checkbox appears in the response content."""
......
......@@ -97,6 +97,7 @@ from openedx.features.course_experience import (
UNIFIED_COURSE_VIEW_FLAG,
)
from openedx.features.enterprise_support.api import data_sharing_consent_required
from openedx.features.course_experience.views.course_dates import CourseDatesFragmentView
from shoppingcart.utils import is_shopping_cart_enabled
from student.models import UserTestGroup, CourseEnrollment
from survey.utils import must_answer_survey
......@@ -330,6 +331,12 @@ def course_info(request, course_id):
store_upgrade_cookie = False
upgrade_cookie_name = 'show_upgrade_notification'
upgrade_link = None
# Construct the dates fragment
dates_fragment = None
if SelfPacedConfiguration.current().enable_course_home_improvements:
dates_fragment = CourseDatesFragmentView().render_to_fragment(request, course_id=course_id)
if request.user.is_authenticated():
show_upgrade_notification = False
if request.GET.get('upgrade', 'false') == 'true':
......@@ -357,6 +364,7 @@ def course_info(request, course_id):
'supports_preview_menu': True,
'studio_url': get_studio_url(course, 'course_info'),
'show_enroll_banner': show_enroll_banner,
'dates_fragment': dates_fragment,
'url_to_enroll': url_to_enroll,
'upgrade_link': upgrade_link,
}
......
......@@ -266,13 +266,13 @@ class PersistentSubsectionGradeTest(GradesModelTestCase):
(True, datetime(2000, 1, 1, 12, 30, 45, tzinfo=pytz.UTC)),
(False, None), # Use as now(). Freeze time needs this calculation to happen at test time.
)
@freeze_time(now())
def test_update_or_create_attempted(self, is_active, expected_first_attempted):
if expected_first_attempted is None:
expected_first_attempted = now()
with waffle.waffle().override(waffle.ESTIMATE_FIRST_ATTEMPTED, active=is_active):
grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
self.assertEqual(grade.first_attempted, expected_first_attempted)
with freeze_time(now()):
if expected_first_attempted is None:
expected_first_attempted = now()
with waffle.waffle().override(waffle.ESTIMATE_FIRST_ATTEMPTED, active=is_active):
grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
self.assertEqual(grade.first_attempted, expected_first_attempted)
def test_unattempted(self):
self.params['first_attempted'] = None
......@@ -407,10 +407,10 @@ class PersistentCourseGradesTest(GradesModelTestCase):
self.assertEqual(grade.letter_grade, u'')
self.assertEqual(grade.passed_timestamp, passed_timestamp)
@freeze_time(now())
def test_passed_timestamp_is_now(self):
grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertEqual(now(), grade.passed_timestamp)
with freeze_time(now()):
grade = PersistentCourseGrade.update_or_create(**self.params)
self.assertEqual(now(), grade.passed_timestamp)
def test_create_and_read_grade(self):
created_grade = PersistentCourseGrade.update_or_create(**self.params)
......
......@@ -2332,10 +2332,10 @@ class TestInstructorOra2Report(SharedModuleStoreTestCase):
response = upload_ora2_data(None, None, self.course.id, None, 'generated')
self.assertEqual(response, UPDATE_STATUS_FAILED)
@freeze_time('2001-01-01 00:00:00')
def test_report_stores_results(self):
test_header = ['field1', 'field2']
test_rows = [['row1_field1', 'row1_field2'], ['row2_field1', 'row2_field2']]
with freeze_time('2001-01-01 00:00:00'):
test_header = ['field1', 'field2']
test_rows = [['row1_field1', 'row1_field2'], ['row2_field1', 'row2_field2']]
with patch('lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task') as mock_current_task:
mock_current_task.return_value = self.current_task
......@@ -2344,7 +2344,6 @@ class TestInstructorOra2Report(SharedModuleStoreTestCase):
'lms.djangoapps.instructor_task.tasks_helper.misc.OraAggregateData.collect_ora2_data'
) as mock_collect_data:
mock_collect_data.return_value = (test_header, test_rows)
with patch(
'lms.djangoapps.instructor_task.models.DjangoStorageReportStore.store_rows'
) as mock_store_rows:
......
......@@ -87,3 +87,63 @@
}
}
}
// date summary
.date-summary-container {
.date-summary {
@include clearfix;
padding: 10px;
@include border-left(3px solid $gray-l3);
.heading {
@extend %t-title7;
color: $gray-d2;
}
.description {
margin-top: $baseline/2;
margin-bottom: $baseline/2;
display: inline-block;
color: $gray-d1;
@extend %t-title8;
}
.date-summary-link {
@extend %t-title8;
font-weight: $font-semibold;
a {
color: $link-color;
font-weight: normal;
}
}
.date {
color: $gray-d1;
@extend %t-title9;
}
&-todays-date {
@include border-left(3px solid $blue);
.heading {
@extend %t-title8;
}
}
&-verified-upgrade-deadline {
@include border-left(3px solid $green);
}
&-verification-deadline-passed {
@include border-left(3px solid $red);
}
&-verification-deadline-retry {
@include border-left(3px solid $red);
}
&-verification-deadline-upcoming {
@include border-left(3px solid $orange);
}
}
}
......@@ -115,34 +115,8 @@ from openedx.core.djangolib.markup import HTML, Text
<section aria-label="${_('Handout Navigation')}" class="handouts">
% if SelfPacedConfiguration.current().enable_course_home_improvements:
<h3 class="hd hd-3 handouts-header">${_("Important Course Dates")}</h3>
## Should be organized by date, last date appearing at the bottom
% for course_date in get_course_date_blocks(course, user):
<div class="date-summary-container">
<div class="date-summary date-summary-${course_date.css_class}">
% if course_date.title:
% if course_date.title == 'current_datetime':
<span class="hd hd-4 heading localized-datetime" data-datetime="${course_date.date}" data-string="${_(u'Today is {date}')}" data-timezone="${user_timezone}" data-language="${user_language}"></span>
% else:
<span class="hd hd-4 heading">${course_date.title}</span>
% endif
% endif
% if course_date.date and course_date.title != 'current_datetime':
<p class="hd hd-4 date localized-datetime" data-format="shortDate" data-datetime="${course_date.date}" data-timezone="${user_timezone}" data-language="${user_language}" data-string="${_(course_date.relative_datestring)}"></p>
% endif
% if course_date.description:
<p class="description">${course_date.description}</p>
% endif
% if course_date.link and course_date.link_text:
<span class="date-summary-link">
<a href="${course_date.link}">${course_date.link_text}</a>
</span>
% endif
</div>
</div>
% endfor
% endif
${HTML(dates_fragment.body_html())}
% endif
<h3 class="hd hd-3 handouts-header">${_(course.info_sidebar_name)}</h3>
${HTML(get_course_info_section(request, masquerade_user, course, 'handouts'))}
</section>
......
......@@ -17,7 +17,6 @@ FROZEN_TIME = '2015-01-01'
VERIFY_STUDENT = {'DAYS_GOOD_FOR': 365}
@freezegun.freeze_time(FROZEN_TIME)
@override_settings(VERIFY_STUDENT=VERIFY_STUDENT)
class PhotoVerificationStatusViewTests(TestCase):
""" Tests for the PhotoVerificationStatusView endpoint. """
......@@ -25,6 +24,10 @@ class PhotoVerificationStatusViewTests(TestCase):
PASSWORD = 'test'
def setUp(self):
freezer = freezegun.freeze_time(FROZEN_TIME)
freezer.start()
self.addCleanup(freezer.stop)
super(PhotoVerificationStatusViewTests, self).setUp()
self.user = UserFactory(password=self.PASSWORD)
self.staff = UserFactory(is_staff=True, password=self.PASSWORD)
......
......@@ -44,38 +44,38 @@ class TestTimeZoneUtils(TestCase):
self.assertEqual(display_tz_info['abbr'], expected_abbr)
self.assertEqual(display_tz_info['offset'], expected_offset)
@freeze_time("2015-02-09")
def test_display_time_zone_without_dst(self):
"""
Test to ensure get_display_time_zone() returns full display string when no kwargs specified
and returns just abbreviation or offset when specified
"""
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PST', '-0800')
with freeze_time("2015-02-09"):
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PST', '-0800')
@freeze_time("2015-04-02")
def test_display_time_zone_with_dst(self):
"""
Test to ensure get_display_time_zone() returns modified abbreviations and
offsets during daylight savings time.
"""
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PDT', '-0700')
with freeze_time("2015-04-02"):
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PDT', '-0700')
@freeze_time("2015-11-01 08:59:00")
def test_display_time_zone_ambiguous_before(self):
"""
Test to ensure get_display_time_zone() returns correct abbreviations and offsets
during ambiguous time periods (e.g. when DST is about to start/end) before the change
"""
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PDT', '-0700')
with freeze_time("2015-11-01 08:59:00"):
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PDT', '-0700')
@freeze_time("2015-11-01 09:00:00")
def test_display_time_zone_ambiguous_after(self):
"""
Test to ensure get_display_time_zone() returns correct abbreviations and offsets
during ambiguous time periods (e.g. when DST is about to start/end) after the change
"""
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PST', '-0800')
with freeze_time("2015-11-01 09:00:00"):
tz_info = self._display_time_zone_helper('America/Los_Angeles')
self._assert_time_zone_info_equal(tz_info, 'America/Los Angeles', 'PST', '-0800')
## mako
<%page expression_filter="h"/>
<%namespace name='static' file='../static_content.html'/>
<%!
from django.utils.translation import ugettext as _
%>
<h3 class="hd hd-6 handouts-header">${_("Important Course Dates")}</h3>
## Should be organized by date, last date appearing at the bottom
% for course_date in course_date_blocks:
<div class="date-summary-container">
<div class="date-summary date-summary-${course_date.css_class}">
% if course_date.title:
% if course_date.title == 'current_datetime':
<span class="hd hd-6 heading localized-datetime" data-datetime="${course_date.date}" data-string="${_(u'Today is {date}')}" data-timezone="${user_timezone}" data-language="${user_language}"></span>
% else:
<span class="hd hd-6 heading">${course_date.title}</span>
% endif
% endif
% if course_date.date and course_date.title != 'current_datetime':
<p class="hd hd-6 date localized-datetime" data-format="shortDate" data-datetime="${course_date.date}" data-timezone="${user_timezone}" data-language="${user_language}" data-string="${_(course_date.relative_datestring)}"></p>
% endif
% if course_date.description:
<p class="description">${course_date.description}</p>
% endif
% if course_date.link and course_date.link_text:
<span class="date-summary-link">
<a href="${course_date.link}">${course_date.link_text}</a>
</span>
% endif
</div>
</div>
% endfor
......@@ -90,6 +90,9 @@ from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
</li>
</ul>
</div>
<div class="section section-dates">
${HTML(dates_fragment.body_html())}
</div>
% if handouts_html:
<div class="section section-handouts">
<h3 class="hd-6">${_("Course Handouts")}</h3>
......
......@@ -65,7 +65,7 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
get_course_in_cache(self.course.id)
# Fetch the view and verify the query counts
with self.assertNumQueries(35):
with self.assertNumQueries(37):
with check_mongo_calls(3):
url = course_home_url(self.course)
self.client.get(url)
"""
Fragment for rendering the course dates sidebar.
"""
from django.template.loader import render_to_string
from opaque_keys.edx.keys import CourseKey
from web_fragments.fragment import Fragment
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from courseware.courses import get_course_with_access, get_course_date_blocks
class CourseDatesFragmentView(EdxFragmentView):
"""
A fragment to important dates within a course.
"""
def render_to_fragment(self, request, course_id=None, **kwargs):
"""
Render the course dates fragment.
"""
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False)
course_date_blocks = get_course_date_blocks(course, request.user)
context = {
'course_date_blocks': course_date_blocks
}
html = render_to_string('course_experience/course-dates-fragment.html', context)
return Fragment(html)
......@@ -17,6 +17,7 @@ from util.views import ensure_valid_course_key
from web_fragments.fragment import Fragment
from .course_outline import CourseOutlineFragmentView
from .course_dates import CourseDatesFragmentView
from ..utils import get_course_outline_block_tree
......@@ -92,6 +93,9 @@ class CourseHomeFragmentView(EdxFragmentView):
# Get resume course information
has_visited_course, resume_course_url = self._get_resume_course_info(request, course_id)
# Render the course dates as a fragment
dates_fragment = CourseDatesFragmentView().render_to_fragment(request, course_id=course_id, **kwargs)
# Get the handouts
# TODO: Use get_course_overview_with_access and blocks api
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
......@@ -101,10 +105,12 @@ class CourseHomeFragmentView(EdxFragmentView):
context = {
'csrf': csrf(request)['csrf_token'],
'course_key': course_key,
'course': course,
'outline_fragment': outline_fragment,
'handouts_html': handouts_html,
'has_visited_course': has_visited_course,
'resume_course_url': resume_course_url,
'dates_fragment': dates_fragment,
'disable_courseware_js': True,
'uses_pattern_library': True,
}
......
......@@ -163,7 +163,7 @@ django-crum==0.5
django_nose==1.4.1
factory_boy==2.8.1
flaky==3.3.0
freezegun==0.1.11
freezegun==0.3.8
mock-django==0.6.9
mock==1.0.1
moto==0.3.1
......
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