Hide After Due setting for Timed Exams

TNL-4366
Changes in studio to allow the hide_after_due setting to be utilized.

Includes:
    -python changes to contentstore, where the data is stored.
    -refactoring of timed-examination-preference-editor.underscore, to add this
        setting and make the editor more accessible.
    -javascript changes to link the new setting to the correct data model.
    -sass updates to fix a11y issues on the editor modal.
    -addition of post-due visibility information to course outline in studio.
    -new tests: python, js, acceptance, and a11y
This commit is contained in:
Eric Fischer
2016-04-21 09:26:22 -04:00
parent ecfd953e80
commit ea77c3ec42
15 changed files with 480 additions and 337 deletions

View File

@@ -67,96 +67,96 @@ if (is_proctored_exam) {
}
%>
<% if (parentInfo) { %>
<li class="outline-item outline-<%= xblockType %> <%= visibilityClass %> is-draggable <%= includesChildren ? 'is-collapsible' : '' %> <%= isCollapsed ? 'is-collapsed' : '' %>"
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
<li class="outline-item outline-<%- xblockType %> <%- visibilityClass %> is-draggable <%- includesChildren ? 'is-collapsible' : '' %> <%- isCollapsed ? 'is-collapsed' : '' %>"
data-parent="<%- parentInfo.get('id') %>" data-locator="<%- xblockInfo.get('id') %>">
<span class="draggable-drop-indicator draggable-drop-indicator-before"><i class="icon fa fa-caret-right"></i></span>
<% if (xblockInfo.isHeaderVisible()) { %>
<div class="<%= xblockType %>-header">
<div class="<%- xblockType %>-header">
<% if (includesChildren) { %>
<h3 class="<%= xblockType %>-header-details expand-collapse <%= isCollapsed ? 'expand' : 'collapse' %> ui-toggle-expansion"
title="<%= interpolate(
<h3 class="<%- xblockType %>-header-details expand-collapse <%- isCollapsed ? 'expand' : 'collapse' %> ui-toggle-expansion"
title="<%- interpolate(
gettext('Collapse/Expand this %(xblock_type)s'), { xblock_type: xblockTypeDisplayName }, true
) %>"
>
<i class="icon fa fa-caret-down"></i>
<% } else { %>
<h3 class="<%= xblockType %>-header-details">
<h3 class="<%- xblockType %>-header-details">
<% } %>
<% if (xblockInfo.isVertical()) { %>
<span class="unit-title item-title">
<a href="<%= xblockInfo.get('studio_url') %>"><%- xblockInfo.get('display_name') %></a>
<a href="<%- xblockInfo.get('studio_url') %>"><%- xblockInfo.get('display_name') %></a>
</span>
<% } else { %>
<span class="wrapper-<%= xblockType %>-title wrapper-xblock-field incontext-editor is-editable" data-field="display_name" data-field-display-name="<%= gettext("Display Name") %>">
<span class="<%= xblockType %>-title item-title xblock-field-value incontext-editor-value"><%- xblockInfo.get('display_name') %></span>
<span class="wrapper-<%- xblockType %>-title wrapper-xblock-field incontext-editor is-editable" data-field="display_name" data-field-display-name="<%- gettext("Display Name") %>">
<span class="<%- xblockType %>-title item-title xblock-field-value incontext-editor-value"><%- xblockInfo.get('display_name') %></span>
</span>
<% } %>
</h3>
<div class="<%= xblockType %>-header-actions">
<div class="<%- xblockType %>-header-actions">
<ul class="actions-list">
<% if (xblockInfo.isPublishable()) { %>
<li class="action-item action-publish">
<a href="#" data-tooltip="<%= gettext('Publish') %>" class="publish-button action-button">
<a href="#" data-tooltip="<%- gettext('Publish') %>" class="publish-button action-button">
<i class="icon fa fa-upload"></i>
<span class="sr action-button-text"><%= gettext('Publish') %></span>
<span class="sr action-button-text"><%- gettext('Publish') %></span>
</a>
</li>
<% } %>
<% if (xblockInfo.isEditableOnCourseOutline()) { %>
<li class="action-item action-configure">
<a href="#" data-tooltip="<%= gettext('Configure') %>" class="configure-button action-button">
<a href="#" data-tooltip="<%- gettext('Configure') %>" class="configure-button action-button">
<i class="icon fa fa-gear"></i>
<span class="sr action-button-text"><%= gettext('Configure') %></span>
<span class="sr action-button-text"><%- gettext('Configure') %></span>
</a>
</li>
<% } %>
<% if (xblockInfo.isDeletable()) { %>
<li class="action-item action-delete">
<a href="#" data-tooltip="<%= gettext('Delete') %>" class="delete-button action-button">
<a href="#" data-tooltip="<%- gettext('Delete') %>" class="delete-button action-button">
<i class="icon fa fa-trash-o" aria-hidden="true"></i>
<span class="sr action-button-text"><%= gettext('Delete') %></span>
<span class="sr action-button-text"><%- gettext('Delete') %></span>
</a>
</li>
<% } %>
<% if (xblockInfo.isDraggable()) { %>
<li class="action-item action-drag">
<span data-tooltip="<%= gettext('Drag to reorder') %>"
class="drag-handle <%= xblockType %>-drag-handle action">
<span class="sr"><%= gettext('Drag to reorder') %></span>
<span data-tooltip="<%- gettext('Drag to reorder') %>"
class="drag-handle <%- xblockType %>-drag-handle action">
<span class="sr"><%- gettext('Drag to reorder') %></span>
</span>
</li>
<% } %>
</ul>
</div>
</div>
<div class="<%= xblockType %>-status">
<div class="<%- xblockType %>-status">
<% if (!xblockInfo.isVertical()) { %>
<% if (xblockInfo.get('explanatory_message') !=null) { %>
<div class="explanatory-message">
<span>
<%= xblockInfo.get('explanatory_message') %>
<%- xblockInfo.get('explanatory_message') %>
</span>
</div>
<% } else { %>
<div class="status-release">
<p>
<span class="sr status-release-label"><%= gettext('Release Status:') %></span>
<span class="sr status-release-label"><%- gettext('Release Status:') %></span>
<span class="status-release-value">
<% if (!course.get('self_paced')) { %>
<% if (xblockInfo.get('released_to_students')) { %>
<i class="icon fa fa-check-square-o"></i>
<%= gettext('Released:') %>
<i class="icon fa fa-check"></i>
<%- gettext('Released:') %>
<% } else if (xblockInfo.get('release_date')) { %>
<i class="icon fa fa-clock-o"></i>
<%= gettext('Scheduled:') %>
<%- gettext('Scheduled:') %>
<% } else { %>
<i class="icon fa fa-clock-o"></i>
<%= gettext('Unscheduled') %>
<%- gettext('Unscheduled') %>
<% } %>
<% if (xblockInfo.get('release_date')) { %>
<%= xblockInfo.get('release_date') %>
<%- xblockInfo.get('release_date') %>
<% } %>
<% } %>
</span>
@@ -166,25 +166,36 @@ if (is_proctored_exam) {
<% if (xblockInfo.get('is_time_limited')) { %>
<div class="status-timed-proctored-exam">
<p>
<span class="sr status-grading-label"> <%= gettext('Graded as:') %> </span>
<span class="sr status-grading-label"> <%- gettext('Graded as:') %> </span>
<i class="icon fa fa-check"></i>
<span class="status-grading-value"> <%= gradingType %> </span>
<span class="status-grading-value"> <%- gradingType %> </span>
-
<span class="sr status-proctored-exam-label"> <%- exam_value %> </span>
<span class="status-proctored-exam-value"> <%- exam_value %> </span>
<% if (xblockInfo.get('due_date')) { %>
<span class="status-grading-date"> <%= gettext('Due:') %> <%= xblockInfo.get('due_date') %> </span>
<span class="status-grading-date"> <%- gettext('Due:') %> <%- xblockInfo.get('due_date') %> </span>
<% } %>
</p>
</div>
<div class="status-hide-after-due">
<p>
<% if (!is_proctored_exam && xblockInfo.get('hide_after_due')) { %>
<i class="icon fa fa-eye-slash"></i>
<span class="status-hide-after-due-value"> <%- gettext("Exam will remain hidden after due date") %> </span>
<% } else { %>
<i class="icon fa fa-eye"></i>
<span class="status-hide-after-due-value"> <%- gettext("Exam will be visible after due date") %> </span>
<% } %>
</p>
</div>
<% } else if (xblockInfo.get('due_date') || xblockInfo.get('graded')) { %>
<div class="status-grading">
<p>
<span class="sr status-grading-label"> <%= gettext('Graded as:') %> </span>
<span class="sr status-grading-label"> <%- gettext('Graded as:') %> </span>
<i class="icon fa fa-check"></i>
<span class="status-grading-value"> <%= gradingType %> </span>
<span class="status-grading-value"> <%- gradingType %> </span>
<% if (xblockInfo.get('due_date') && !course.get('self_paced')) { %>
<span class="status-grading-date"> <%= gettext('Due:') %> <%= xblockInfo.get('due_date') %> </span>
<span class="status-grading-date"> <%- gettext('Due:') %> <%- xblockInfo.get('due_date') %> </span>
<% } %>
</p>
</div>
@@ -192,7 +203,7 @@ if (is_proctored_exam) {
<% } %>
<% if (statusMessage) { %>
<div class="status-message">
<i class="icon fa <%= statusIconClass %>"></i>
<i class="icon fa <%- statusIconClass %>"></i>
<p class="status-message-copy"><%- statusMessage %></p>
</div>
<% } %>
@@ -202,20 +213,20 @@ if (is_proctored_exam) {
<% if (!parentInfo && xblockInfo.get('child_info') && xblockInfo.get('child_info').children.length === 0) { %>
<div class="no-content add-section">
<p><%= gettext("You haven't added any content to this course yet.") %>
<a href="#" class="button button-new" data-category="<%= childCategory %>"
data-parent="<%= xblockInfo.get('id') %>" data-default-name="<%= defaultNewChildName %>"
title="<%= interpolate(
<p><%- gettext("You haven't added any content to this course yet.") %>
<a href="#" class="button button-new" data-category="<%- childCategory %>"
data-parent="<%- xblockInfo.get('id') %>" data-default-name="<%- defaultNewChildName %>"
title="<%- interpolate(
gettext('Click to add a new %(xblock_type)s'), { xblock_type: defaultNewChildName }, true
) %>"
>
<i class="icon fa fa-plus"></i><%= addChildLabel %>
<i class="icon fa fa-plus"></i><%- addChildLabel %>
</a>
</p>
</div>
<% } else if (!xblockInfo.isVertical()) { %>
<div class="outline-content <%= xblockType %>-content">
<ol class="<%= typeListClass %> is-sortable">
<div class="outline-content <%- xblockType %>-content">
<ol class="<%- typeListClass %> is-sortable">
<li class="ui-splint ui-splint-indicator">
<span class="draggable-drop-indicator draggable-drop-indicator-initial"><i class="icon fa fa-caret-right"></i></span>
</li>
@@ -223,14 +234,14 @@ if (is_proctored_exam) {
<% if (childType) { %>
<% if (xblockInfo.isChildAddable()) { %>
<div class="add-<%= childType %> add-item">
<a href="#" class="button button-new" data-category="<%= childCategory %>"
data-parent="<%= xblockInfo.get('id') %>" data-default-name="<%= defaultNewChildName %>"
title="<%= interpolate(
<div class="add-<%- childType %> add-item">
<a href="#" class="button button-new" data-category="<%- childCategory %>"
data-parent="<%- xblockInfo.get('id') %>" data-default-name="<%- defaultNewChildName %>"
title="<%- interpolate(
gettext('Click to add a new %(xblock_type)s'), { xblock_type: defaultNewChildName }, true
) %>"
>
<i class="icon fa fa-plus"></i><%= addChildLabel %>
<i class="icon fa fa-plus"></i><%- addChildLabel %>
</a>
</div>
<% } %>

View File

@@ -1,73 +1,65 @@
<form>
<h3 class="modal-section-title"><%= gettext('Set as a Special Exam') %></h3>
<div class="modal-section-content has-actions">
<div class='exam-time-list-fields'>
<ul class="list-fields list-input">
<li class="field-radio">
<input type="radio" id="id_not_timed" name="proctored" class="input input-radio" checked="checked"/>
<label for="id_not_timed" class="label">
<%- gettext('None') %>
</label>
</li>
</ul>
</div>
<div class='exam-time-list-fields'>
<ul class="list-fields list-input">
<li class="field-radio">
<input type="radio" id="id_timed_exam" name="proctored" class="input input-radio" />
<label for="id_timed_exam" class="label" aria-describedby="timed-exam-description">
<%- gettext('Timed') %>
</label>
</li>
<ul class="list-fields list-input exam-types" role="group" aria-label="<%- gettext('Exam Types') %>">
<li class="field-radio">
<label class="label">
<input type="radio" name="exam_type" class="input input-radio no_special_exam" checked="checked"/>
<%- gettext('None') %>
</label>
</li>
<li class="field-radio">
<label class="label">
<input type="radio" name="exam_type" class="input input-radio timed_exam"
aria-describedby="timed-exam-description" />
<%- gettext('Timed') %>
</label>
<p class='field-message' id='timed-exam-description'> <%- gettext('Use a timed exam to limit the time learners can spend on problems in this subsection. Learners must submit answers before the time expires. You can allow additional time for individual learners through the Instructor Dashboard.') %> </p>
</ul>
</div>
<% if (enable_proctored_exam) { %>
<div class='exam-time-list-fields'>
<ul class="list-fields list-input">
<li class="field-radio">
<input type="radio" id="id_proctored_exam" name="proctored" class="input input-radio" />
<label for="id_proctored_exam" class="label" aria-describedby="proctored-exam-description">
<%- gettext('Proctored') %>
</label>
</li>
</li>
<% if (enable_proctored_exam) { %>
<li class="field-radio">
<label class="label">
<input type="radio" name="exam_type" class="input input-radio proctored_exam"
aria-describedby="proctored-exam-description" />
<%- gettext('Proctored') %>
</label>
<p class='field-message' id='proctored-exam-description'> <%- gettext('Proctored exams are timed and they record video of each learner taking the exam. The videos are then reviewed to ensure that learners follow all examination rules.') %> </p>
</ul>
</div>
<div class='exam-time-list-fields'>
<ul class="list-fields list-input">
<li class="field-radio">
<input type="radio" id="id_practice_exam" name="proctored" class="input input-radio" />
<label for="id_practice_exam" class="label" aria-describedby="practice-exam-description">
<%- gettext('Practice Proctored') %>
</label>
</li>
</li>
<li class="field-radio">
<label class="label">
<input type="radio" name="exam_type" class="input input-radio practice_exam"
aria-describedby="practice-exam-description"/>
<%- gettext('Practice Proctored') %>
</label>
<p class='field-message' id='practice-exam-description'> <%- gettext("Use a practice proctored exam to introduce learners to the proctoring tools and processes. Results of a practice exam do not affect a learner's grade.") %> </p>
</ul>
</div>
<% } %>
<div class='exam-time-list-fields is-hidden' id='id_time_limit_div'>
<ul class="list-fields list-input time-limit">
<li class="field field-text field-time-limit">
<label for="id_time_limit" class="label"><%- gettext('Time Allotted (HH:MM):') %> </label>
<input type="text" id="id_time_limit" name="time_limit"
value="" aria-describedby="time-limit-description"
placeholder="HH:MM" class="time_limit release-time time input input-text" autocomplete="off" />
</li>
<% } %>
</ul>
<ul class="list-fields list-input exam-options">
<li class="field field-text field-time-limit">
<label class="label">
<%- gettext('Time Allotted (HH:MM):') %>
<input type="text" value="" aria-describedby="time-limit-description" placeholder="HH:MM"
class="time_limit release-time time input input-text" autocomplete="off" />
</label>
<p class='field-message' id='time-limit-description'><%- gettext('Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.') %></p>
</ul>
</div>
<div class='exam-review-rules-list-fields is-hidden'>
<ul class="list-fields list-input exam-review-rules">
<li class="field field-text field-exam-review-rules">
<label for="id_exam_review_rules" class="label"><%- gettext('Review Rules') %> </label>
<textarea id="id_exam_review_rules" cols="50" maxlength="255" name="review_rules" aria-describedby="review-rules-description"
</li>
<li class="field field-text field-exam-review-rules">
<label class="label">
<%- gettext('Review Rules') %>
<textarea cols="50" maxlength="255" aria-describedby="review-rules-description"
class="review-rules input input-text" autocomplete="off" />
</li>
</label>
<p class='field-message' id='review-rules-description'><%- gettext('Specify any additional rules or rule exceptions that the proctoring review team should enforce when reviewing the videos. For example, you could specify that calculators are allowed.') %></p>
</ul>
</div>
</li>
<li class="field-checkbox field-hide-after-due">
<label class="label">
<input type="checkbox" class="input input-checkbox"
aria-describedby="hide-after-due-description"/>
<%- gettext('Hide Exam After Due Date') %>
</label>
<p class='field-message' id='hide-after-due-description'><%- gettext('By default, submitted exams are available for review after the due date has passed. Select this option to keep exams hidden after that date.') %></p>
</li>
</ul>
</div>
</form>