Merge pull request #24303 from edx/ddumesnil/due-dates-in-courseware-AA-44

AA-44: Adding due dates into courseware
This commit is contained in:
Dillon Dumesnil
2020-06-24 09:22:01 -07:00
committed by GitHub
5 changed files with 87 additions and 7 deletions

View File

@@ -561,6 +561,7 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
context['show_bookmark_button'] = show_bookmark_button
context['bookmarked'] = is_bookmarked
context['format'] = getattr(self, 'format', '')
if render_items:
rendered_item = item.render(view, context)

View File

@@ -6,7 +6,9 @@ Tests for vertical module.
from collections import namedtuple
from datetime import datetime, timedelta
import json
import pytz
import six
import ddt
@@ -67,6 +69,11 @@ class StubCompletionService(object):
def blocks_to_mark_complete_on_view(self, blocks):
return {} if self._completion_value == 1.0 else blocks
def vertical_is_complete(self, item):
if item.scope_ids.block_type != 'vertical':
raise ValueError('The passed in xblock is not a vertical type!')
return self._completion_value
class BaseVerticalBlockTest(XModuleXmlImportTest):
"""
@@ -120,18 +127,24 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
@ddt.unpack
@ddt.data(
{'context': None, 'view': STUDENT_VIEW},
{'context': {}, 'view': STUDENT_VIEW},
{'context': {}, 'view': PUBLIC_VIEW},
{'context': None, 'view': STUDENT_VIEW, 'completion_value': 0.0, 'days': 1},
{'context': {}, 'view': STUDENT_VIEW, 'completion_value': 0.0, 'days': 1},
{'context': {}, 'view': PUBLIC_VIEW, 'completion_value': 0.0, 'days': 1},
{'context': {'format': 'Quiz'}, 'view': STUDENT_VIEW, 'completion_value': 1.0, 'days': 1}, # completed
{'context': {'format': 'Exam'}, 'view': STUDENT_VIEW, 'completion_value': 0.0, 'days': 1}, # upcoming
{'context': {'format': 'Homework'}, 'view': STUDENT_VIEW, 'completion_value': 0.0, 'days': -1}, # past due
)
def test_render_student_preview_view(self, context, view):
def test_render_student_preview_view(self, context, view, completion_value, days):
"""
Test the rendering of the student and public view.
"""
self.module_system._services['bookmarks'] = Mock()
now = datetime.now(pytz.UTC)
self.vertical.due = now + timedelta(days=days)
if view == STUDENT_VIEW:
self.module_system._services['user'] = StubUserService()
self.module_system._services['completion'] = StubCompletionService(enabled=True, completion_value=0.0)
self.module_system._services['completion'] = StubCompletionService(enabled=True,
completion_value=completion_value)
elif view == PUBLIC_VIEW:
self.module_system._services['user'] = StubUserService(is_anonymous=True)
@@ -140,10 +153,16 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
).content
self.assertIn(self.test_html_1, html)
self.assertIn(self.test_html_2, html)
self.assertIn("'due': datetime.datetime({year}, {month}, {day}".format(
year=self.vertical.due.year, month=self.vertical.due.month, day=self.vertical.due.day), html)
if view == STUDENT_VIEW:
self.assert_bookmark_info(self.assertIn, html)
else:
self.assert_bookmark_info(self.assertNotIn, html)
if context:
self.assertIn("'subsection_format': '{}'".format(context['format']), html)
self.assertIn("'completed': {}".format(completion_value), html)
self.assertIn("'past_due': {}".format(self.vertical.due < now), html)
@ddt.unpack
@ddt.data(
@@ -163,7 +182,7 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
completion_value=completion_value,
)
self.module_system.render(self.vertical, STUDENT_VIEW, self.default_context)
if (mark_completed_enabled):
if mark_completed_enabled:
self.assertEqual(
mock_student_view.call_args[0][1]['wrap_xblock_data']['mark-completed-on-view-after-delay'], 9876
)

View File

@@ -5,8 +5,10 @@ VerticalBlock - an XBlock which renders its children in a column.
import logging
from copy import copy
from datetime import datetime
from functools import reduce
import pytz
import six
from lxml import etree
from web_fragments.fragment import Fragment
@@ -89,10 +91,16 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
'content': rendered_child.content
})
completed = completion_service and completion_service.vertical_is_complete(self)
past_due = not completed and self.due and self.due < datetime.now(pytz.UTC)
fragment_context = {
'items': contents,
'xblock_context': context,
'unit_title': self.display_name_with_default if not is_child_of_vertical else None,
'due': self.due,
'completed': completed,
'past_due': past_due,
'subsection_format': context.get('format', ''),
}
if view == STUDENT_VIEW:

View File

@@ -523,6 +523,33 @@ html.video-fullscreen {
}
}
.vert-due-date {
color: #686b73;
display: flex;
font-size: 16px;
.pill {
font-size: 12px;
padding: 2px 8px;
border-radius: 5px;
margin-left: 8px;
font-style: italic;
font-weight: bold;
vertical-align: top;
&.completed {
background-color: #f3f3f4;
color: #2d323e;
}
&.past-due {
background-color: #d1d2d4;
color: black;
}
}
}
.vert-mod {
padding: 0;
margin: 0;

View File

@@ -1,10 +1,31 @@
<%page expression_filter="h"/>
<%! from openedx.core.djangolib.markup import HTML %>
<%namespace name='static' file='/static_content.html'/>
<%!
from django.utils.translation import gettext as _
from openedx.core.djangolib.markup import HTML
%>
%if unit_title and show_title:
<h2 class="hd hd-2 unit-title">${unit_title}</h2>
% endif
% if due:
<%
data_string = _("{subsection_format} due {{date}}").format(subsection_format=subsection_format)
%>
<div class="vert-due-date">
<div class="localized-datetime" data-datetime="${due}" data-string="${data_string}" data-timezone="${user_timezone}"></div>
% if completed:
<div class="pill completed">${_('Completed')}</div>
% elif past_due:
<div class="pill past-due">${_('Past Due')}</div>
%endif
</div>
% endif
% if show_bookmark_button:
<%include file='bookmark_button.html' args="bookmark_id=bookmark_id, is_bookmarked=bookmarked"/>
% endif
@@ -18,3 +39,7 @@
%endif
% endfor
</div>
<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory">
DateUtilFactory.transform('.localized-datetime');
</%static:require_module_async>