fix import grade of zero from ungraded should work
A student not graded for an assignment should be able to be graded with a zero during gradebook import if the imported CSV contains a zero in that column. Previously, the import would think there was no change and the student would stay ungraded. fixes GRADE-1184 Test Plan - Create an assignment. - Make sure not to grade a student for that assignment. - Export the gradebook. - Edit the CSV so that the assignment name is sufficiently malformed such that the import process later will ask you to match an assignment to an existing one. - Still editing the CSV, give the student a score of zero. - Import the CSV to the gradebook. - Match the malformed assignment name to the actual assignment. - Verify that the ungraded student now has a grade of zero after importing and matching. Save the changes. - Verify in the Gradebook that the student now has a grade of zero. Change-Id: I2ef79dbc2b66e9f1a41fce2a5ffc59dbceadae26 Reviewed-on: https://gerrit.instructure.com/155475 Tested-by: Jenkins Reviewed-by: Adrian Packel <apackel@instructure.com> Reviewed-by: Keith T. Garner <kgarner@instructure.com> QA-Review: Anju Reddy <areddy@instructure.com> Product-Review: Keith T. Garner <kgarner@instructure.com>
This commit is contained in:
parent
9ccd0e2e0e
commit
a91002e0bd
|
@ -30,7 +30,7 @@ class GradebookUploadsController < ApplicationController
|
|||
if previous_upload
|
||||
if previous_upload.stale?
|
||||
previous_upload.destroy
|
||||
elsif previous_upload
|
||||
else
|
||||
# let them continue on with their old upload
|
||||
redirect_to course_gradebook_upload_path(@context)
|
||||
return
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 - present 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/>.
|
||||
*/
|
||||
|
||||
import $ from 'jquery'
|
||||
import I18n from 'i18n!gradebook_uploads'
|
||||
import 'spin.js/jquery.spin'
|
||||
function waitForProcessing(progress) {
|
||||
var dfd = $.Deferred();
|
||||
var spinner = $("#spinner").spin();
|
||||
|
||||
var amIDoneYet = (progress) => {
|
||||
if (progress.workflow_state == "completed") {
|
||||
$.ajaxJSON(ENV.uploaded_gradebook_data_path, "GET").then((uploadedGradebook) => {
|
||||
spinner.hide();
|
||||
dfd.resolve(uploadedGradebook)
|
||||
});
|
||||
} else if (progress.workflow_state == "failed") {
|
||||
dfd.reject(I18n.t("Invalid CSV file. Grades could not be updated."));
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
$.ajaxJSON(`/api/v1/progress/${progress.id}`, "GET")
|
||||
.then(amIDoneYet);
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
amIDoneYet(progress);
|
||||
|
||||
return dfd;
|
||||
}
|
||||
|
||||
export default waitForProcessing
|
|
@ -19,28 +19,27 @@
|
|||
import $ from 'jquery'
|
||||
import I18n from 'i18n!gradezilla_uploads'
|
||||
import 'spin.js/jquery.spin'
|
||||
function waitForProcessing(progress) {
|
||||
var dfd = $.Deferred();
|
||||
var spinner = $("#spinner").spin();
|
||||
|
||||
var amIDoneYet = (progress) => {
|
||||
if (progress.workflow_state == "completed") {
|
||||
$.ajaxJSON(ENV.uploaded_gradebook_data_path, "GET").then((uploadedGradebook) => {
|
||||
spinner.hide();
|
||||
dfd.resolve(uploadedGradebook)
|
||||
});
|
||||
} else if (progress.workflow_state == "failed") {
|
||||
dfd.reject(I18n.t("Invalid CSV file. Grades could not be updated."));
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
$.ajaxJSON(`/api/v1/progress/${progress.id}`, "GET")
|
||||
export function waitForProcessing(progress) {
|
||||
const dfd = $.Deferred();
|
||||
const spinner = $("#spinner").spin();
|
||||
|
||||
const amIDoneYet = (currentProgress) => {
|
||||
if (currentProgress.workflow_state === "completed") {
|
||||
$.ajaxJSON(ENV.uploaded_gradebook_data_path, "GET").then((uploadedGradebook) => {
|
||||
spinner.hide();
|
||||
dfd.resolve(uploadedGradebook)
|
||||
});
|
||||
} else if (currentProgress.workflow_state === "failed") {
|
||||
dfd.reject(I18n.t("Invalid CSV file. Grades could not be updated."));
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
$.ajaxJSON(`/api/v1/progress/${currentProgress.id}`, "GET")
|
||||
.then(amIDoneYet);
|
||||
}, 2000);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
amIDoneYet(progress);
|
||||
|
||||
return dfd;
|
||||
}
|
||||
amIDoneYet(progress);
|
||||
|
||||
export default waitForProcessing
|
||||
return dfd;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import $ from 'jquery'
|
|||
import _ from 'underscore'
|
||||
import htmlEscape from './str/htmlEscape'
|
||||
import numberHelper from 'jsx/shared/helpers/numberHelper'
|
||||
import waitForProcessing from 'jsx/gradebook/uploads/wait_for_processing'
|
||||
import {waitForProcessing} from 'jsx/gradezilla/uploads/wait_for_processing'
|
||||
import ProcessGradebookUpload from 'jsx/gradebook/uploads/process_gradebook_upload'
|
||||
import GradeFormatHelper from 'jsx/gradebook/shared/helpers/GradeFormatHelper'
|
||||
import './vendor/slickgrid' /* global Slick */
|
||||
|
@ -328,7 +328,7 @@ import './jquery.templateData' /* fillTemplateData */
|
|||
return sub.user_id == student.id && sub.assignment_id == val;
|
||||
});
|
||||
if (original_submission) {
|
||||
submission.original_grade = I18n.n(original_submission.score);
|
||||
submission.original_grade = original_submission.score !== '' ? I18n.n(original_submission.score) : '';
|
||||
}
|
||||
});
|
||||
} else if (thing === 'student') {
|
||||
|
@ -339,7 +339,7 @@ import './jquery.templateData' /* fillTemplateData */
|
|||
return sub.user_id == obj.id && sub.assignment_id == submission.assignment_id;
|
||||
});
|
||||
if (original_submission) {
|
||||
submission.original_grade = I18n.n(original_submission.score);
|
||||
submission.original_grade = original_submission.score !== '' ? I18n.n(original_submission.score) : '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,36 +16,112 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
define([
|
||||
'gradebook_uploads',
|
||||
'jsx/gradebook/shared/helpers/GradeFormatHelper'
|
||||
], (gradebook_uploads, GradeFormatHelper) => { // eslint-disable-line camelcase
|
||||
QUnit.module('gradebook_uploads#createGeneralFormatter');
|
||||
import $ from 'jquery';
|
||||
import gradebook_uploads from 'gradebook_uploads';
|
||||
import GradeFormatHelper from 'jsx/gradebook/shared/helpers/GradeFormatHelper';
|
||||
import * as waitForProcessing from 'jsx/gradezilla/uploads/wait_for_processing';
|
||||
|
||||
test('formatter returns expected lookup value', function () {
|
||||
const formatter = gradebook_uploads.createGeneralFormatter('foo');
|
||||
const formatted = formatter(null, null, {foo: 'bar'});
|
||||
equal(formatted, 'bar');
|
||||
const fixtures = document.getElementById('fixtures');
|
||||
|
||||
QUnit.module('gradebook_uploads#createGeneralFormatter');
|
||||
|
||||
test('formatter returns expected lookup value', function () {
|
||||
const formatter = gradebook_uploads.createGeneralFormatter('foo');
|
||||
const formatted = formatter(null, null, {foo: 'bar'});
|
||||
equal(formatted, 'bar');
|
||||
});
|
||||
|
||||
test('formatter returns empty string when lookup value missing', function () {
|
||||
const formatter = gradebook_uploads.createGeneralFormatter('foo');
|
||||
const formatted = formatter(null, null, null);
|
||||
equal(formatted, '');
|
||||
});
|
||||
|
||||
QUnit.module('gradebook_uploads#handleThingsNeedingToBeResolved', (hooks) => {
|
||||
let defaultUploadedGradebook;
|
||||
|
||||
hooks.beforeEach(() => {
|
||||
fixtures.innerHTML = `
|
||||
<form id='gradebook_importer_resolution_section'>
|
||||
<select name='assignment_-1'>
|
||||
<option>73</option>
|
||||
</select>
|
||||
</form>
|
||||
<div id='gradebook_grid'>
|
||||
<div id='gradebook_grid_header'></div>
|
||||
</div>
|
||||
<div id='no_changes_detected' style='display:none;'></div>
|
||||
`;
|
||||
|
||||
defaultUploadedGradebook = {
|
||||
assignments: [{grading_type: null, id: '-1', points_possible: 10, previous_id: null, title: 'imported'}],
|
||||
missing_objects: {
|
||||
assignments: [{grading_type: 'points', id: '73', points_possible: 10, previous_id: null, title: 'existing'}],
|
||||
students: []
|
||||
},
|
||||
original_submissions: [{assignment_id: '73', gradeable: true, score: '', user_id: '1'}],
|
||||
students: [{
|
||||
id: '1',
|
||||
last_name_first: 'Efron, Zac',
|
||||
name: 'Zac Efron',
|
||||
previous_id: '1',
|
||||
submissions: [{assignment_id: '-1', grade: '0.0', gradeable: true, original_grade: null}]
|
||||
}],
|
||||
warning_messages: {
|
||||
prevented_grading_ungradeable_submission: false,
|
||||
prevented_new_assignment_creation_in_closed_period: false
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test('formatter returns empty string when lookup value missing', function () {
|
||||
const formatter = gradebook_uploads.createGeneralFormatter('foo');
|
||||
const formatted = formatter(null, null, null);
|
||||
equal(formatted, '');
|
||||
hooks.afterEach(() => {
|
||||
fixtures.innerHTML = '';
|
||||
});
|
||||
|
||||
QUnit.module('grade_summary#createNumberFormatter');
|
||||
test('recognizes that there are no changed assignments when the grades are the same', () => {
|
||||
const uploadedGradebook = {
|
||||
...defaultUploadedGradebook,
|
||||
original_submissions: [{assignment_id: '73', gradeable: true, score: '0.0', user_id: '1'}]
|
||||
};
|
||||
const waitForProcessingStub = sinon.stub(waitForProcessing, 'waitForProcessing').returns(
|
||||
$.Deferred().resolve(uploadedGradebook)
|
||||
);
|
||||
|
||||
test('number formatter returns empty string when value missing', function () {
|
||||
const formatter = gradebook_uploads.createNumberFormatter('foo');
|
||||
const formatted = formatter(null, null, null);
|
||||
equal(formatted, '');
|
||||
gradebook_uploads.handleThingsNeedingToBeResolved();
|
||||
$('#gradebook_importer_resolution_section').submit();
|
||||
strictEqual($('#no_changes_detected:visible').length, 1);
|
||||
|
||||
waitForProcessingStub.restore();
|
||||
});
|
||||
|
||||
test('number formatter delegates to GradeFormatHelper#formatGrade', function () {
|
||||
const formatGradeSpy = this.spy(GradeFormatHelper, 'formatGrade');
|
||||
const formatter = gradebook_uploads.createNumberFormatter('foo');
|
||||
formatter(null, null, {});
|
||||
ok(formatGradeSpy.calledOnce);
|
||||
test('recognizes that there are changed assignments when original grade was ungraded', () => {
|
||||
const uploadedGradebook = {
|
||||
...defaultUploadedGradebook,
|
||||
original_submissions: [{assignment_id: '73', gradeable: true, score: '', user_id: '1'}]
|
||||
};
|
||||
const waitForProcessingStub = sinon.stub(waitForProcessing, 'waitForProcessing').returns(
|
||||
$.Deferred().resolve(uploadedGradebook)
|
||||
);
|
||||
|
||||
gradebook_uploads.handleThingsNeedingToBeResolved();
|
||||
$('#gradebook_importer_resolution_section').submit();
|
||||
strictEqual($('#no_changes_detected:visible').length, 0);
|
||||
|
||||
waitForProcessingStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.module('grade_summary#createNumberFormatter');
|
||||
|
||||
test('number formatter returns empty string when value missing', function () {
|
||||
const formatter = gradebook_uploads.createNumberFormatter('foo');
|
||||
const formatted = formatter(null, null, null);
|
||||
equal(formatted, '');
|
||||
});
|
||||
|
||||
test('number formatter delegates to GradeFormatHelper#formatGrade', function () {
|
||||
const formatGradeSpy = this.spy(GradeFormatHelper, 'formatGrade');
|
||||
const formatter = gradebook_uploads.createNumberFormatter('foo');
|
||||
formatter(null, null, {});
|
||||
ok(formatGradeSpy.calledOnce);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue