Commit 806c35b3 by cahrens

Warn if unsaved changes exist in assessment.

TNL-3870
parent 1aee957a
......@@ -8,17 +8,7 @@
</span>
</h3>
{% block title %}
<span class="staff__grade__status">
<span class="staff__grade__value">
<span class="copy">
{% blocktrans with ungraded=staff_assessment_ungraded|stringformat:"s" in_progress=staff_assessment_in_progress|stringformat:"s" %}
{{ ungraded }} Available and {{ in_progress }} Checked Out
{% endblocktrans %}
</span>
</span>
</span>
{% endblock %}
{% include "openassessmentblock/staff_area/oa_staff_grade_learners_count.html" with staff_assessment_ungraded=staff_assessment_ungraded staff_assessment_in_progress=staff_assessment_in_progress %}
</header>
<div class="ui-staff__content__section staff__grade__content ui-toggle-visibility__content">
......
{% load i18n %}
{% spaceless %}
{% block body %}
<div class="staff__grade__form ui-toggle-visibility__content" data-submission-uuid="{{ submission.uuid }}">
<div class="wrapper--staff-assessment">
<div>
......@@ -53,6 +52,5 @@
</div>
</div>
</div>
{% endblock %}
{% endspaceless %}
{% load i18n %}
<span class="staff__grade__status">
<span class="staff__grade__value">
<span class="copy">
{% blocktrans with ungraded=staff_assessment_ungraded|stringformat:"s" in_progress=staff_assessment_in_progress|stringformat:"s" %}
{{ ungraded }} Available and {{ in_progress }} Checked Out
{% endblocktrans %}
</span>
</span>
</span>
{% load i18n %}
{% spaceless %}
{% block body %}
<div class="wrapper--staff-assessment">
<div class="step__instruction">
<p>{% trans "Override this learner's current grade using the problem's rubric." %}</p>
......@@ -46,5 +45,4 @@
</ul>
</div>
</div>
{% endblock %}
{% endspaceless %}
......@@ -169,14 +169,24 @@ class StaffAreaMixin(object):
staff_assessment_required = "staff-assessment" in self.assessment_steps
context['staff_assessment_required'] = staff_assessment_required
if staff_assessment_required:
grading_stats = staff_api.get_staff_grading_statistics(
student_item["course_id"], student_item["item_id"]
context.update(
self.get_staff_assessment_statistics_context(student_item["course_id"], student_item["item_id"])
)
context['staff_assessment_ungraded'] = grading_stats['ungraded']
context['staff_assessment_in_progress'] = grading_stats['in-progress']
return path, context
@staticmethod
def get_staff_assessment_statistics_context(course_id, item_id):
"""
Returns a context with staff assessment "ungraded" and "in-progress" counts.
"""
grading_stats = staff_api.get_staff_grading_statistics(course_id, item_id)
return {
'staff_assessment_ungraded': grading_stats['ungraded'],
'staff_assessment_in_progress': grading_stats['in-progress']
}
@XBlock.json_handler
@require_global_admin("SCHEDULE_TRAINING")
def schedule_training(self, data, suffix=''): # pylint: disable=W0613
......@@ -272,6 +282,27 @@ class StaffAreaMixin(object):
except PeerAssessmentInternalError:
return self.render_error(self._(u"Error getting staff grade information."))
@XBlock.handler
@require_course_staff("STUDENT_GRADE")
def render_staff_grade_counts(self, data, suffix=''): # pylint: disable=W0613
"""
Renders a form to show the number of ungraded and checked out assessments.
Must be course staff to render this view.
"""
try:
student_item_dict = self.get_student_item_dict()
context = self.get_staff_assessment_statistics_context(
student_item_dict.get('course_id'), student_item_dict.get('item_id')
)
path = 'openassessmentblock/staff_area/oa_staff_grade_learners_count.html'
return self.render_assessment(path, context)
except PeerAssessmentInternalError:
return self.render_error(self._(u"Error getting staff grade ungraded and checked out counts."))
def get_student_submission_context(self, student_username, submission):
"""
Get a context dict for rendering a student submission and associated rubric (for staff grading).
......
......@@ -723,8 +723,8 @@
"template": "openassessmentblock/staff_area/oa_staff_area.html",
"context": {
"staff_assessment_required": true,
"staff_assessment_ungraded": 9,
"staff_assessment_in_progress": 0,
"staff_assessment_ungraded": 5,
"staff_assessment_in_progress": 2,
"status_counts": {
"self": 1,
"peer": 2,
......@@ -754,6 +754,22 @@
"output": "oa_staff_area_full_grading_2.html"
},
{
"template": "openassessmentblock/staff_area/oa_staff_grade_learners_count.html",
"context": {
"staff_assessment_ungraded": 9,
"staff_assessment_in_progress": 3
},
"output": "oa_staff_grade_learners_count_1.html"
},
{
"template": "openassessmentblock/staff_area/oa_staff_grade_learners_count.html",
"context": {
"staff_assessment_ungraded": 9,
"staff_assessment_in_progress": 2
},
"output": "oa_staff_grade_learners_count_2.html"
},
{
"template": "openassessmentblock/staff_area/oa_student_info.html",
"context": {
"rubric_criteria": [
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -75,6 +75,10 @@ describe("OpenAssessment.PeerView", function() {
server.renderLatex = jasmine.createSpy('renderLatex');
});
afterEach(function() {
OpenAssessment.clearUnsavedChanges();
});
it("sends a peer assessment to the server", function() {
var view = createPeerAssessmentView('oa_peer_assessment.html');
submitPeerAssessment(view);
......@@ -115,4 +119,34 @@ describe("OpenAssessment.PeerView", function() {
var view = createPeerAssessmentView('oa_turbo_mode.html');
submitPeerAssessment(view);
});
it("warns of unsubmitted assessments", function() {
var view = createPeerAssessmentView('oa_peer_assessment.html');
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
// Click on radio buttons, to create unsubmitted changes.
$('.question__answers', view.el).each(function() {
$('input[type="radio"]', this).first().click();
});
expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// When submitPeerAssessment is executed, the views will all re-render. However,
// as the test does not mock out the surrounding elements, the re-render
// of the peer assessment module will keep the original HTML intact (with selected
// options), causing the unsavedWarnings callback to be triggered again (after it is properly
// cleared during the submit operation). To avoid this, have the view re-render fail.
server.render = function() {
return $.Deferred(
function(defer) {
defer.fail();
}
).promise();
};
submitPeerAssessment(view);
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
});
});
......@@ -142,7 +142,7 @@ describe("OpenAssessment.ResponseView", function() {
view.setAutoSaveEnabled(false);
// Disable the unsaved page warning (if set)
view.unsavedWarningEnabled(false);
OpenAssessment.clearUnsavedChanges();
});
it("updates and retrieves response text correctly", function() {
......@@ -299,36 +299,36 @@ describe("OpenAssessment.ResponseView", function() {
it("enables the unsaved work warning when the user changes the response text", function() {
// Initially, the unsaved work warning should be disabled
expect(view.unsavedWarningEnabled()).toBe(false);
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
// Change the text, then expect the unsaved warning to be enabled.
view.response(['Lorem ipsum 1', 'Lorem ipsum 2']);
view.handleResponseChanged();
// Expect the unsaved work warning to be enabled
expect(view.unsavedWarningEnabled()).toBe(true);
expect(view.baseView.unsavedWarningEnabled()).toBe(true);
});
it("disables the unsaved work warning when the user saves a response", function() {
// Change the text, then expect the unsaved warning to be enabled.
view.response(['Lorem ipsum 1', 'Lorem ipsum 2']);
view.handleResponseChanged();
expect(view.unsavedWarningEnabled()).toBe(true);
expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// Save the response and expect the unsaved warning to be disabled
view.save();
expect(view.unsavedWarningEnabled()).toBe(false);
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
});
it("disables the unsaved work warning when the user submits a response", function() {
// Change the text, then expect the unsaved warning to be enabled.
view.response(['Lorem ipsum 1', 'Lorem ipsum 2']);
view.handleResponseChanged();
expect(view.unsavedWarningEnabled()).toBe(true);
expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// Submit the response and expect the unsaved warning to be disabled
view.submit();
expect(view.unsavedWarningEnabled()).toBe(false);
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
});
describe("auto save", function() {
......@@ -360,7 +360,7 @@ describe("OpenAssessment.ResponseView", function() {
expect(view.saveStatus()).toContain('saved but not submitted');
// Expect that the unsaved warning is disabled
expect(view.unsavedWarningEnabled()).toBe(false);
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
});
it("schedules autosave polling", function() {
......
......@@ -43,6 +43,10 @@ describe("OpenAssessment.SelfView", function() {
view.installHandlers();
});
afterEach(function() {
OpenAssessment.clearUnsavedChanges();
});
it("Sends a self assessment to the server", function() {
spyOn(server, 'selfAssess').and.callThrough();
......@@ -82,4 +86,33 @@ describe("OpenAssessment.SelfView", function() {
// Expect the submit button to have been re-enabled
expect(view.selfSubmitEnabled()).toBe(true);
});
it("warns of unsubmitted assessments", function() {
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
// Click on radio buttons, to create unsubmitted changes.
$('.question__answers', view.el).each(function() {
$('input[type="radio"]', this).first().click();
});
expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// When selfAssess is executed, the views will all re-render. However,
// as the test does not mock out the surrounding elements, the re-render
// of the self assessment module will keep the original HTML intact (with selected
// options), causing the unsavedWarnings callback to be triggered again (after it is properly
// cleared during the submit operation). To avoid this, have the view re-render fail.
server.render = function() {
return $.Deferred(
function(defer) {
defer.fail();
}
).promise();
};
view.selfAssess();
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
});
});
......@@ -28,6 +28,16 @@ OpenAssessment.BaseView = function(runtime, element, server, data) {
this.staffAreaView = new OpenAssessment.StaffAreaView(this.element, this.server, this);
};
if (typeof OpenAssessment.unsavedChanges === 'undefined' || !OpenAssessment.unsavedChanges) {
OpenAssessment.unsavedChanges = {};
}
// This is used by unit tests to reset state.
OpenAssessment.clearUnsavedChanges = function() {
OpenAssessment.unsavedChanges = {};
window.onbeforeunload = null;
};
OpenAssessment.BaseView.prototype = {
/**
......@@ -148,6 +158,55 @@ OpenAssessment.BaseView.prototype = {
$container.toggleClass('has--error', true);
$container.find('.step__status__value i').removeClass().addClass('icon fa fa-exclamation-triangle');
$container.find('.step__status__value .copy').html(_.escape(errorMessage));
},
/**
* Enable/disable the "navigate away" warning to alert the user of unsaved changes.
*
* @param {boolean} enabled If specified, set whether the warning is enabled.
* @param {string} key A unique key related to the type of unsaved changes. Must be supplied
* if "enabled" is also supplied.
* @param {string} message The message to show if navigating away with unsaved changes. Only needed
* if "enabled" is true.
* @returns {boolean} Whether the warning is enabled (only if "enabled" argument is not supplied).
*/
unsavedWarningEnabled: function(enabled, key, message) {
if (typeof enabled === 'undefined') {
return (window.onbeforeunload !== null);
}
else {
// To support multiple ORA XBlocks on the same page, store state by XBlock usage-id.
var usageID = $(this.element).data("usage-id");
if (enabled) {
if (typeof OpenAssessment.unsavedChanges[usageID] === 'undefined' ||
!OpenAssessment.unsavedChanges[usageID]) {
OpenAssessment.unsavedChanges[usageID] = {};
}
OpenAssessment.unsavedChanges[usageID][key] = message;
window.onbeforeunload = function() {
for (var xblockUsageID in OpenAssessment.unsavedChanges) {
if (OpenAssessment.unsavedChanges.hasOwnProperty(xblockUsageID)) {
for (var key in OpenAssessment.unsavedChanges[xblockUsageID]) {
if (OpenAssessment.unsavedChanges[xblockUsageID].hasOwnProperty(key)) {
return OpenAssessment.unsavedChanges[xblockUsageID][key];
}
}
}
}
};
}
else {
if (typeof OpenAssessment.unsavedChanges[usageID] !== 'undefined') {
delete OpenAssessment.unsavedChanges[usageID][key];
if ($.isEmptyObject(OpenAssessment.unsavedChanges[usageID])) {
delete OpenAssessment.unsavedChanges[usageID];
}
if ($.isEmptyObject(OpenAssessment.unsavedChanges)) {
window.onbeforeunload = null;
}
}
}
}
}
};
......
......@@ -18,6 +18,8 @@ OpenAssessment.PeerView = function(element, server, baseView) {
OpenAssessment.PeerView.prototype = {
UNSAVED_WARNING_KEY: "peer-assessment",
/**
Load the peer assessment view.
**/
......@@ -101,10 +103,16 @@ OpenAssessment.PeerView.prototype = {
var rubricElement = rubricSelector.get(0);
this.rubric = new OpenAssessment.Rubric(rubricElement);
}
else {
// If there was previously a rubric visible, clear the reference to it.
this.rubric = null;
}
// Install a change handler for rubric options to enable/disable the submit button
if (this.rubric !== null) {
this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled, view));
this.rubric.changesExistCallback($.proxy(view.assessmentRubricChanges, view));
}
// Install a click handler for assessment
......@@ -154,12 +162,29 @@ OpenAssessment.PeerView.prototype = {
},
/**
* Called when something is selected or typed in the assessment rubric.
* Used to set the unsaved changes warning dialog.
*
* @param {boolean} changesExist true if unsaved changes exist
*/
assessmentRubricChanges: function(changesExist) {
if (changesExist) {
this.baseView.unsavedWarningEnabled(
true,
this.UNSAVED_WARNING_KEY,
gettext("If you leave this page without submitting your peer assessment, you will lose any work you have done.") // jscs:ignore maximumLineLength
);
}
},
/**
Send an assessment to the server and update the view.
**/
peerAssess: function() {
var view = this;
var baseView = view.baseView;
this.peerAssessRequest(function() {
baseView.unsavedWarningEnabled(false, view.UNSAVED_WARNING_KEY);
baseView.loadAssessmentModules();
baseView.scrollToTop();
});
......@@ -174,6 +199,7 @@ OpenAssessment.PeerView.prototype = {
var gradeView = this.baseView.gradeView;
var baseView = view.baseView;
view.peerAssessRequest(function() {
baseView.unsavedWarningEnabled(false, view.UNSAVED_WARNING_KEY);
view.loadContinuedAssessment();
gradeView.load();
baseView.scrollToTop();
......
......@@ -38,6 +38,8 @@ OpenAssessment.ResponseView.prototype = {
// Maximum file size (5 MB) for an attached file.
MAX_FILE_SIZE: 5242880,
UNSAVED_WARNING_KEY: "learner-response",
/**
Load the response (submission) view.
**/
......@@ -235,37 +237,6 @@ OpenAssessment.ResponseView.prototype = {
},
/**
Enable/disable the "navigate away" warning to alert the user of unsaved changes.
Args:
enabled (bool): If specified, set whether the warning is enabled.
Returns:
bool: Whether the warning is enabled.
Examples:
>> view.unsavedWarningEnabled(true); // enable the "unsaved" warning
>> view.unsavedWarningEnabled();
>> true
**/
unsavedWarningEnabled: function(enabled) {
if (typeof enabled === 'undefined') {
return (window.onbeforeunload !== null);
}
else {
if (enabled) {
window.onbeforeunload = function() {
// Keep this on one big line to avoid gettext bug: http://stackoverflow.com/a/24579117
return gettext("If you leave this page without saving or submitting your response, you'll lose any work you've done on the response."); // jscs:ignore maximumLineLength
};
}
else {
window.onbeforeunload = null;
}
}
},
/**
Set the response texts.
Retrieve the response texts.
......@@ -339,7 +310,11 @@ OpenAssessment.ResponseView.prototype = {
this.saveEnabled(isNotBlank);
this.previewEnabled(isNotBlank);
this.saveStatus(gettext('This response has not been saved.'));
this.unsavedWarningEnabled(true);
this.baseView.unsavedWarningEnabled(
true,
this.UNSAVED_WARNING_KEY,
gettext("If you leave this page without saving or submitting your response, you will lose any work you have done on the response.") // jscs:ignore maximumLineLength
);
}
// Record the current time (used for autosave)
......@@ -360,7 +335,7 @@ OpenAssessment.ResponseView.prototype = {
this.baseView.toggleActionError('save', null);
// Disable the "unsaved changes" warning
this.unsavedWarningEnabled(false);
this.baseView.unsavedWarningEnabled(false, this.UNSAVED_WARNING_KEY);
var view = this;
var savedResponse = this.response();
......@@ -463,7 +438,7 @@ OpenAssessment.ResponseView.prototype = {
// Disable the "unsaved changes" warning if the user
// tries to navigate to another page.
this.unsavedWarningEnabled(false);
this.baseView.unsavedWarningEnabled(false, this.UNSAVED_WARNING_KEY);
},
/**
......
......@@ -156,6 +156,43 @@ OpenAssessment.Rubric.prototype = {
},
/**
* Install a callback handler to be notified when unsaved changes exist in a rubric form.
*
* @param {function} callback a function that accepts one argument, a boolean indicating
* whether the user has selected options or inserted text.
*/
changesExistCallback: function(callback) {
var rubric = this;
// Set the initial state
callback(rubric.changesExist());
// Install a handler to update on change
$(this.element).on('change keyup drop paste',
function() { callback(rubric.changesExist()); }
);
},
/**
* Helper method for determining of unsubmitted changes exist in the rubric.
*
* @returns {boolean} true if unsubmitted changes exist.
*/
changesExist: function() {
var numChecked = $('input[type=radio]:checked', this.element).length;
var textExists = false;
$('textarea', this.element).each(function() {
var trimmedText = $.trim($(this).val());
if (trimmedText !== "") {
textExists = true;
}
});
return (numChecked > 0 || textExists);
},
/**
Updates the rubric to display positive and negative messages on each
criterion. For each correction provided, the associated criterion will have
an appropriate message displayed.
......
......@@ -18,6 +18,8 @@ OpenAssessment.SelfView = function(element, server, baseView) {
OpenAssessment.SelfView.prototype = {
UNSAVED_WARNING_KEY: "self-assessment",
/**
Load the self assessment view.
**/
......@@ -51,10 +53,16 @@ OpenAssessment.SelfView.prototype = {
var rubricElement = rubricSelector.get(0);
this.rubric = new OpenAssessment.Rubric(rubricElement);
}
else {
// If there was previously a rubric visible, clear the reference to it.
this.rubric = null;
}
// Install a change handler for rubric options to enable/disable the submit button
if (this.rubric !== null) {
this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled, this));
this.rubric.changesExistCallback($.proxy(this.assessmentRubricChanges, this));
}
// Install a click handler for the submit button
......@@ -95,6 +103,22 @@ OpenAssessment.SelfView.prototype = {
},
/**
* Called when something is selected or typed in the assessment rubric.
* Used to set the unsaved changes warning dialog.
*
* @param {boolean} changesExist true if unsaved changes exist
*/
assessmentRubricChanges: function(changesExist) {
if (changesExist) {
this.baseView.unsavedWarningEnabled(
true,
this.UNSAVED_WARNING_KEY,
gettext("If you leave this page without submitting your self assessment, you will lose any work you have done.") // jscs:ignore maximumLineLength
);
}
},
/**
Send a self-assessment to the server and update the view.
**/
selfAssess: function() {
......@@ -110,6 +134,7 @@ OpenAssessment.SelfView.prototype = {
this.rubric.overallFeedback()
).done(
function() {
baseView.unsavedWarningEnabled(false, view.UNSAVED_WARNING_KEY);
baseView.loadAssessmentModules();
baseView.scrollToTop();
}
......
......@@ -20,14 +20,13 @@
OpenAssessment.StaffAreaView.prototype = {
FULL_GRADE_UNSAVED_WARNING_KEY: "staff-grade",
OVERRIDE_UNSAVED_WARNING_KEY: "staff-override",
/**
* Load the staff area.
*
* @param {function} onSuccessCallback an optional callback to be executed when the
* server successfully returns the staff area HTML. This callback will be the last thing
* executed, after rendering and installing click handlers.
*/
load: function(onSuccessCallback) {
load: function() {
var view = this;
// If we're course staff, the base template should contain a section
......@@ -39,9 +38,6 @@
$('.openassessment__staff-area', view.element).replaceWith(html);
view.server.renderLatex($('.openassessment__staff-area', view.element));
view.installHandlers();
if (onSuccessCallback) {
onSuccessCallback();
}
}).fail(function() {
view.baseView.showLoadError('staff_area');
});
......@@ -97,6 +93,10 @@
// Install a change handler for rubric options to enable/disable the submit button
rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view, $manageLearnersTab));
rubric.changesExistCallback(
$.proxy(view.assessmentRubricChanges, view, view.OVERRIDE_UNSAVED_WARNING_KEY)
);
// Install a click handler for the submit button
$manageLearnersTab.find('.wrapper--staff-assessment .action--submit', view.element).click(
function(eventObject) {
......@@ -125,19 +125,27 @@
* Upon request, loads the staff grade/assessment section of the staff area.
* This allows staff grading when staff assessment is a required step.
*
* @param {boolean} clearAndCollapse if true, clear the staff grade form and collapse it. Otherwise
* render the staff grade form if is not already loaded.
* @returns {promise} A promise representing the successful loading
* of the staff grade (assessment) section.
*/
loadStaffGradeForm: function() {
loadStaffGradeForm: function(clearAndCollapse) {
var view = this;
var $staffGradeTab = $('.openassessment__staff-grading', this.element);
var isCollapsed = $staffGradeTab.find('.staff__grade__control').hasClass("is--collapsed");
var deferred = $.Deferred();
var showFormError = function(errorMessage) {
$staffGradeTab.find('.staff__grade__form--error').text(errorMessage);
};
if (isCollapsed && !this.staffGradeFormLoaded) {
if (clearAndCollapse) {
// Collapse the editor and update the counts.
$staffGradeTab.find('.staff__grade__control').toggleClass('is--collapsed', true);
$staffGradeTab.find('.staff__grade__form').replaceWith('<div class="staff__grade__form"></div>');
view.updateStaffGradeCounts();
deferred.resolve();
}
else if (!this.staffGradeFormLoaded) {
this.staffGradeFormLoaded = true;
this.server.staffGradeForm().done(function(html) {
showFormError('');
......@@ -145,6 +153,9 @@
// Load the HTML and install event handlers
$staffGradeTab.find('.staff__grade__form').replaceWith(html);
// Update the number of ungraded and checked out assigments.
view.updateStaffGradeCounts();
var $rubric = $staffGradeTab.find('.staff-assessment__assessment');
if ($rubric.size() > 0) {
var rubricElement = $rubric.get(0);
......@@ -153,6 +164,10 @@
// Install a change handler for rubric options to enable/disable the submit button
rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view, $staffGradeTab));
rubric.changesExistCallback(
$.proxy(view.assessmentRubricChanges, view, view.FULL_GRADE_UNSAVED_WARNING_KEY)
);
// Install a click handler for the submit buttons
$staffGradeTab.find('.wrapper--staff-assessment .action--submit').click(
function(eventObject) {
......@@ -171,10 +186,29 @@
deferred.reject();
});
}
return deferred.promise();
},
/**
* Update the counts of ungraded and checked out assessments.
*/
updateStaffGradeCounts: function() {
var view = this;
var $staffGradeTab = $('.openassessment__staff-grading', this.element);
view.server.staffGradeCounts().done(function(html) {
$staffGradeTab.find('.staff__grade__status').replaceWith(html);
}).fail(function() {
$staffGradeTab.find('.staff__grade__status').replaceWith(
'<span class="staff__grade__status"><span class="staff__grade__value"><span class="copy">' +
gettext("Error getting the number of ungraded responses") +
'</span></span></span>'
);
});
},
/**
* Install event handlers for the view.
*/
installHandlers: function() {
......@@ -255,8 +289,11 @@
// Install a click handler for showing the staff grading form.
$staffGradeTool.find('.staff__grade__show-form').click(
function() {
var wasCollapsed = $staffGradeTool.find('.staff__grade__control').hasClass("is--collapsed");
if (wasCollapsed) {
view.loadStaffGradeForm();
}
}
);
},
......@@ -380,6 +417,23 @@
},
/**
* Called when something is selected or typed in the assessment rubric.
* Used to set the unsaved changes warning dialog.
*
* @param {string} key the unsaved changes key
* @param {boolean} changesExist true if unsaved changes exist
*/
assessmentRubricChanges: function(key, changesExist) {
if (changesExist) {
this.baseView.unsavedWarningEnabled(
true,
key,
gettext("If you leave this page without submitting your staff assessment, you will lose any work you have done.") // jscs:ignore maximumLineLength
);
}
},
/**
* Submit the staff assessment override.
*
* @param {string} submissionID The ID of the submission to be submitted.
......@@ -390,6 +444,7 @@
submitStaffOverride: function(submissionID, rubric, scope) {
var view = this;
var successCallback = function() {
view.baseView.unsavedWarningEnabled(false, view.OVERRIDE_UNSAVED_WARNING_KEY);
// Note: we ignore any message returned from the server and instead
// re-render the student info with the "Learner's Final Grade"
// section expanded. This section will show the learner's
......@@ -413,15 +468,9 @@
submitStaffGrade: function(submissionID, rubric, scope, continueGrading) {
var view = this;
var successCallback = function() {
view.baseView.unsavedWarningEnabled(false, view.FULL_GRADE_UNSAVED_WARNING_KEY);
view.staffGradeFormLoaded = false;
var showFullGradeTab = function() {
// Need to show the staff grade component again, unfortunately requiring a global selector.
$('.button-staff-grading').click();
if (continueGrading) {
$('.staff__grade__show-form', view.element).click();
}
};
view.load(showFullGradeTab);
view.loadStaffGradeForm(!continueGrading);
};
this.callStaffAssess(submissionID, rubric, scope, successCallback, '.staff-grade-error');
},
......
......@@ -137,6 +137,29 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
},
/**
* Renders the count of ungraded and checked out assessemtns.
*
* @returns {promise} A JQuery promise, which resolves with the HTML of the rendered section
* fails with an error message.
*/
staffGradeCounts: function() {
var url = this.url('render_staff_grade_counts');
return $.Deferred(function(defer) {
$.ajax({
url: url,
type: "POST",
dataType: "html"
}).done(function(data) {
defer.resolveWith(this, [data]);
}).fail(function() {
defer.rejectWith(
this, [gettext('The display of ungraded and checked out responses could not be loaded.')]
);
});
}).promise();
},
/**
* Send a submission to the XBlock.
*
* @param {string} submission The text of the student's submission.
......
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