canvas-lms/public/javascripts/grade_summary.js

404 lines
16 KiB
JavaScript

/**
* Copyright (C) 2011 Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define([
'INST' /* INST */,
'i18n!gradebook',
'jquery' /* $ */,
'underscore',
'compiled/grade_calculator',
'compiled/util/round',
'str/htmlEscape',
'jquery.ajaxJSON' /* ajaxJSON */,
'jquery.instructure_forms' /* getFormData */,
'jquery.instructure_misc_helpers' /* replaceTags, scrollSidebar */,
'jquery.instructure_misc_plugins' /* showIf */,
'jquery.templateData' /* fillTemplateData, getTemplateData */,
'media_comments' /* mediaComment, mediaCommentThumbnail */
], function(INST, I18n, $, _, GradeCalculator, round, htmlEscape) {
function removeAssignmentsNotInCurrentGradingPeriod() {
var gradingPeriod = ENV.grading_period;
var assignmentGroups = $.extend(true, [], ENV.assignment_groups);
var filteredAssignmentGroups;
if (gradingPeriod) {
_.each(assignmentGroups, function (assignmentGroup) {
var updatedAssignments = [];
_.each(assignmentGroup.assignments, function (assignment){
var gradingPeriodStartDate = new Date(gradingPeriod.start_date);
var gradingPeriodEndDate = new Date(gradingPeriod.end_date);
var assignmentDueDate = assignment.due_at ? new Date(assignment.due_at) : null;
var assignmentInGradingPeriod = gradingPeriodStartDate <= assignmentDueDate
&& gradingPeriodEndDate >= assignmentDueDate
if(assignmentInGradingPeriod || (gradingPeriod.is_last && !assignmentDueDate)) {
updatedAssignments.push(assignment);
}
});
assignmentGroup.assignments = updatedAssignments;
});
}
return assignmentGroups;
}
function updateStudentGrades() {
var ignoreUngradedSubmissions = $("#only_consider_graded_assignments").attr('checked');
var currentOrFinal = ignoreUngradedSubmissions ? 'current' : 'final';
var groupWeightingScheme = ENV.group_weighting_scheme;
var showTotalGradeAsPoints = ENV.show_total_grade_as_points;
var assignmentGroups = removeAssignmentsNotInCurrentGradingPeriod(ENV.assignment_groups);
var calculatedGrades = GradeCalculator.calculate(
ENV.submissions,
assignmentGroups,
groupWeightingScheme
);
$('.dropped').attr('aria-label', "");
$('.dropped').attr('title', "");
// mark dropped assignments
$('.student_assignment').find('.points_possible').attr('aria-label', '');
_.chain(calculatedGrades.group_sums).map(function(groupSum) {
return groupSum[currentOrFinal].submissions;
}).flatten().each(function(s) {
$('#submission_' + s.submission.assignment_id).toggleClass('dropped', !!s.drop);
});
var droppedMessage = I18n.t('This assignment is dropped and will not be considered in the total calculation');
$('.dropped').attr('aria-label', droppedMessage);
$('.dropped').attr('title', droppedMessage);
var calculateGrade = function(score, possible) {
if (possible === 0 || isNaN(score)) {
grade = "N/A"
} else {
grade = round((score / possible)*100, round.DEFAULT);
}
return grade;
};
for (var i = 0; i < calculatedGrades.group_sums.length; i++) {
var groupSum = calculatedGrades.group_sums[i];
var $groupRow = $('#submission_group-' + groupSum.group.id);
var groupGradeInfo = groupSum[currentOrFinal];
$groupRow.find('.grade').text(
calculateGrade(groupGradeInfo.score, groupGradeInfo.possible) + "%"
);
$groupRow.find('.score_teaser').text(
round(groupGradeInfo.score, round.DEFAULT) + ' / ' + round(groupGradeInfo.possible, round.DEFAULT)
);
}
var finalScore = calculatedGrades[currentOrFinal].score;
var finalPossible = calculatedGrades[currentOrFinal].possible;
var scoreAsPoints = round(finalScore, round.DEFAULT) + ' / ' + round(finalPossible, round.DEFAULT);
var scoreAsPercent = calculateGrade(finalScore, finalPossible);
var finalGrade = scoreAsPercent + "%";
var teaserText = scoreAsPoints;
if (showTotalGradeAsPoints && groupWeightingScheme != "percent"){
finalGrade = scoreAsPoints;
teaserText = scoreAsPercent + "%";
}
var $finalGradeRow = $(".student_assignment.final_grade");
$finalGradeRow.find(".grade").text(finalGrade);
$finalGradeRow.find(".score_teaser").text(teaserText);
if (groupWeightingScheme == "percent") {
$finalGradeRow.find(".score_teaser").hide()
}
if(ENV.grading_scheme) {
$(".final_letter_grade .grade").text(GradeCalculator.letter_grade(ENV.grading_scheme, scoreAsPercent));
}
$(".revert_all_scores").showIf($("#grades_summary .revert_score_link").length > 0);
}
$(document).ready(function() {
updateStudentGrades();
$(".revert_all_scores_link").click(function(event) {
event.preventDefault();
$("#grades_summary .revert_score_link").each(function() {
$(this).trigger('click', true);
});
$("#.show_guess_grades.exists").show();
updateStudentGrades();
});
// manages toggling and screenreader focus for comments, scoring, and rubric details
$(".toggle_comments_link, .toggle_score_details_link, .toggle_rubric_assessments_link").click(function(event) {
event.preventDefault();
var $row = $( '#' + $(this).attr('aria-controls') );
var originEl = this;
$(originEl).attr("aria-expanded", $row.css('display') == 'none');
$row.toggle();
if ($row.css('display') != 'none') {
$row.find(".screenreader-toggle").focus();
}
});
$(".screenreader-toggle").click(function(event) {
event.preventDefault();
ariaControl = $(this).data('aria');
originEl = $("a[aria-controls='" + ariaControl + "']");
$(originEl).attr('aria-expanded', false);
$(originEl).focus();
$(this).closest('.rubric_assessments, .comments').hide();
});
$('.student_assignment.editable .assignment_score').click(function(event) {
if ($('#grades_summary.editable').length === 0 || $(this).find('#grade_entry').length > 0 || $(event.target).closest('.revert_score_link').length > 0) {
return;
}
// Store the original score so that we can restore it after "What-If" calculations
if (!$(this).find('.grade').data('originalValue')){
$(this).find('.grade').data('originalValue', $(this).find('.grade').html());
}
var $screenreader_link_clone = $(this).find('.screenreader-only').clone(true);
$(this).find('.grade').data("screenreader_link", $screenreader_link_clone);
$(this).find('.grade').empty().append($("#grade_entry"));
$(this).find('.score_value').hide();
// Get the current shown score (possibly a "What-If" score) and use it as the default value in the text entry field
var val = $(this).parents('.student_assignment').find('.what_if_score').text();
$('#grade_entry').val(parseFloat(val) || '0').show().focus().select();
});
$("#grade_entry").keydown(function(event) {
if(event.keyCode == 13) {
$(this)[0].blur();
} else if(event.keyCode == 27) {
var val = $(this).parents(".student_assignment")
.addClass('dont_update')
.find(".original_score").text();
$(this).val(val || "")[0].blur();
}
});
$('#grades_summary .student_assignment').bind('score_change', function(event, update) {
var $assignment = $(this),
originalScore = $assignment.find('.original_score').text(),
originalVal = parseFloat(originalScore),
val = parseFloat($assignment.find('#grade_entry').val() || $(this).find('.what_if_score').text()),
isChanged;
if (isNaN(originalVal)) { originalVal = null; }
if (isNaN(val)) { val = null; }
if (!val && val !== 0) { val = originalVal; }
isChanged = (originalVal != val);
if (val || val === 0) { val = round(val, round.DEFAULT); }
if (val == parseInt(val, 10)) {
val = val + '.0';
}
$assignment.find('.what_if_score').text(val);
if ($assignment.hasClass('dont_update')) {
update = false;
$assignment.removeClass('dont_update');
}
var assignment_id = $assignment.getTemplateData({ textValues: ['assignment_id'] }).assignment_id;
if (update) {
var url = $.replaceTags($('.update_submission_url').attr('href'), 'assignment_id', assignment_id);
if (!isChanged) { val = null; }
$.ajaxJSON(url, 'PUT', { 'submission[student_entered_score]': val },
function(data) {
data = {student_entered_score: data.submission.student_entered_score};
$assignment.fillTemplateData({ data: data });
},
$.noop
);
if(!isChanged) { val = originalVal; }
}
$('#grade_entry').hide().appendTo($('body'));
if (isChanged) {
$assignment.find(".assignment_score").attr('title', '')
.find(".score_teaser").text(I18n.t('titles.hypothetical_score', "This is a What-If score")).end()
.find(".score_holder").append($("#revert_score_template").clone(true).attr('id', '').show())
.find(".grade").addClass('changed');
setTimeout(function() { $assignment.find(".revert_score_link").focus();}, 0)
} else {
var tooltip = $assignment.data('muted') ?
I18n.t('student_mute_notification', 'Instructor is working on grades') :
I18n.t('click_to_change', 'Click to test a different score');
$assignment.find(".assignment_score").attr('title', I18n.t('click_to_change', 'Click to test a different score'))
.find(".score_teaser").text(tooltip).end()
.find(".grade").removeClass('changed');
$assignment.find(".revert_score_link").remove();
}
if (val === 0) { val = '0.0'; }
if (val === originalVal) { val = originalScore; }
$assignment.find('.grade').html($.raw(htmlEscape(val) || $assignment.find('.grade').data('originalValue')));
if (!isChanged) {
var $screenreader_link_clone = $assignment.find('.grade').data("screenreader_link");
$assignment.find('.grade').prepend($screenreader_link_clone);
}
updateScoreForAssignment(assignment_id, val);
updateStudentGrades();
});
$("#grade_entry").blur(function() {
var $assignment = $(this).parents(".student_assignment");
$assignment.triggerHandler('score_change', true);
});
$("#grades_summary").delegate('.revert_score_link', 'click', function(event, skipEval) {
event.preventDefault();
event.stopPropagation();
var $assignment = $(this).parents(".student_assignment"),
val = $assignment.find(".original_score").text(),
submission_status = $assignment.find(".submission_status").text();
var tooltip;
if ($assignment.data('muted')) {
tooltip = I18n.t('student_mute_notification', 'Instructor is working on grades');
// Commented out until CNVS-16332 backend fixes are ready
//} else if(submission_status == 'pending_review') {
// tooltip = I18n.t('grading_in_progress', "Instructor is working on grades");
} else {
tooltip = I18n.t('click_to_change', 'Click to test a different score');
}
$assignment.find(".what_if_score").text(val);
$assignment.find(".assignment_score").attr('title', I18n.t('click_to_change', 'Click to test a different score'))
.find(".score_teaser").text(tooltip).end()
.find(".grade").removeClass('changed');
$assignment.find(".revert_score_link").remove();
$assignment.find(".score_value").text(val);
if (isNaN(parseFloat(val))) { val = null; }
if ($assignment.data('muted')) {
$assignment.find('.grade').html('<img alt="Muted" class="muted_icon" src="/images/sound_mute.png?1318436336">')
} else {
$assignment.find(".grade").text(val || "-");
}
var assignmentId = $assignment.getTemplateValue('assignment_id');
updateScoreForAssignment(assignmentId, val);
if(!skipEval) {
updateStudentGrades();
}
var $screenreader_link_clone = $assignment.find('.grade').data("screenreader_link");
$assignment.find('.grade').prepend($screenreader_link_clone);
setTimeout(function() { $assignment.find(".grade").focus();}, 0);
});
$("#grades_summary:not(.editable) .assignment_score").css('cursor', 'default');
$("#grades_summary tr").hover(function() {
$(this).find("th.title .context").addClass('context_hover');
}, function() {
$(this).find("th.title .context").removeClass('context_hover');
});
$(".show_guess_grades_link").click(function(event) {
$("#grades_summary .student_entered_score").each(function() {
var val = parseFloat($(this).text(), 10);
if(!isNaN(val) && (val || val === 0)) {
var $assignment = $(this).parents(".student_assignment");
$assignment.find(".what_if_score").text(val);
$assignment.find(".score_value").hide();
$assignment.triggerHandler('score_change', false);
}
});
$(".show_guess_grades").hide();
});
$("#grades_summary .student_entered_score").each(function() {
var val = parseFloat($(this).text(), 10);
if(!isNaN(val) && (val || val === 0)) {
$(".show_guess_grades").show().addClass('exists');
}
});
$(".comments .play_comment_link").mediaCommentThumbnail('normal');
$(".play_comment_link").live('click', function(event) {
event.preventDefault();
var $parent = $(this).parents(".comment_media"),
comment_id = $parent.getTemplateData({textValues: ['media_comment_id']}).media_comment_id;
if(comment_id) {
var mediaType = 'any';
if ($(this).hasClass('video_comment')) {
mediaType = 'video';
} else if ($(this).hasClass('audio_comment')) {
mediaType = 'audio';
}
$parent.children(":not(.media_comment_content)").remove();
$parent.find(".media_comment_content").mediaComment('show_inline', comment_id, mediaType);
}
});
$("#only_consider_graded_assignments").change(function() {
updateStudentGrades();
}).triggerHandler('change');
$.scrollSidebar();
$("#observer_user_url").change(function() {
if(location.href != $(this).val()) {
location.href = $(this).val();
}
});
$("#show_all_details_link").click(function(event) {
event.preventDefault();
$button = $('#show_all_details_link');
$button.toggleClass('showAll');
if ($button.hasClass('showAll')) {
$button.text(I18n.t('hide_all_details_button', 'Hide All Details'));
$("tr.rubric_assessments").show();
$("tr.comments").show();
} else {
$button.text(I18n.t('show_all_details_button', 'Show All Details'));
$("tr.rubric_assessments").hide();
$("tr.comments").hide();
}
});
});
function updateScoreForAssignment(assignmentId, score) {
var submission = _.find(ENV.submissions, function(s) {
return s.assignment_id == assignmentId;
});
if (submission) {
submission.score = score;
} else {
ENV.submissions.push({assignment_id: assignmentId, score: score});
}
}
$(document).on('change', '#grading_periods_selector', function(e){
var newGP = $(this).val();
if (matches = location.href.match(/grading_period_id=\d*/)) {
location.href = location.href.replace(matches[0], "grading_period_id=" + newGP);
} else if(matches = location.href.match(/#tab-assignments/)) {
location.href = location.href.replace(matches[0], "") + "?grading_period_id=" + newGP + matches[0];
} else {
location.href += "?grading_period_id=" + newGP;
}
});
});