add custom statuses to gradebook imports - FE 1/2

allows the front end importer to recognize that there are
custom statuses for overrides that have changed from the current
import, showing which rows/columns changed. this is part 1/2, as
the next commit will add the last part which is saving those changes

closes EVAL-3441

flag=custom_gradebook_statuses

test plan:
- CANNOT BE QA'd UNTIL g/326378 IS MERGED
- go to a course with grading periods and for the gradebook filter,
  select "All Grading Periods"
- add a custom status to a final grade override using the tray
- export the entire gradebook
- open a CSV editor and change the custom status you added to
  something else and add another status for another student
    - MAKE SURE THAT BOTH STUDENTS WHO WERE CHANGED ARE USERS WITH
      LOGINS (otherwise they are not gradable students and the importer
      does not recognize them)
- use the importer to import the CSV you just edited
- it should recognize the changes and show the columns and rows
  that changed
- now do the same but for a single grading period, using the option
  to export the current gradebook view instead of entire gradebook
- it should recognize the changes and show the columns and rows
  that changed
- note: "applying" the changes or saving them will not work until
  the next commit is merged

Change-Id: I081b05d63ac0a75d08ae23acb79388fcbf58ca4b
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/326452
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Christopher Soto <christopher.soto@instructure.com>
Reviewed-by: Spencer Olson <solson@instructure.com>
QA-Review: Christopher Soto <christopher.soto@instructure.com>
Product-Review: Ravi Koll <ravi.koll@instructure.com>
This commit is contained in:
rohan.chugh 2023-08-29 10:19:52 -05:00 committed by Christopher Soto
parent 685406a1df
commit b2dbfa076b
2 changed files with 273 additions and 0 deletions

View File

@ -191,6 +191,14 @@ QUnit.module('override score changes', hooks => {
],
includes_course_scores: false,
},
override_statuses: {
grading_periods: [
{id: 1, title: 'first GP'},
{id: 2, title: 'second GP'},
{id: 3, title: 'third GP'},
],
includes_course_score_status: false,
},
students: [
{
custom_column_data: [],
@ -213,6 +221,26 @@ QUnit.module('override score changes', hooks => {
new_score: null,
},
],
override_statuses: [
{
grading_period_id: '1',
student_id: '1',
current_grade_status: 'CARROT',
new_grade_status: 'POTATO',
},
{
grading_period_id: '2',
student_id: '1',
current_grade_status: null,
new_grade_status: 'CARROT',
},
{
grading_period_id: '3',
student_id: '1',
current_grade_status: 'POTATO',
new_grade_status: null,
},
],
previous_id: '1',
submissions: [{assignment_id: '-1', grade: '0.0', gradeable: true, original_grade: null}],
},
@ -317,6 +345,70 @@ QUnit.module('override score changes', hooks => {
)
notOk(gradingPeriodColumn)
})
test('creates a pair of columns for each grading period in the grading_periods hash of override status', () => {
initGradebook()
const columnIds = mainGridArgs.columns
.map(column => column.id)
.filter(id => id.includes('override_status'))
deepEqual(columnIds, [
'override_status_1_conflicting',
'override_status_1',
'override_status_2_conflicting',
'override_status_2',
'override_status_3_conflicting',
'override_status_3',
])
})
test('adds a header for each grading period including the title of the grading period of override status', () => {
initGradebook()
const headers = headerGridArgs.columns
.map(column => column.name)
.filter(name => name.includes('Override Status'))
deepEqual(headers, [
'Override Status (first GP)',
'Override Status (second GP)',
'Override Status (third GP)',
])
})
test('creates a column for course status if includes_course_score_status is true', () => {
defaultUploadedGradebook.override_statuses.includes_course_score_status = true
defaultUploadedGradebook.override_statuses.grading_periods = []
initGradebook()
const gradingPeriodColumn = mainGridArgs.columns.find(
column => column.id === 'override_status_course'
)
ok(gradingPeriodColumn)
})
test('adds a header for course status with the label of plain old "Override Status"', () => {
defaultUploadedGradebook.override_statuses.includes_course_score_status = true
defaultUploadedGradebook.override_statuses.grading_periods = []
initGradebook()
const gradingPeriodColumn = headerGridArgs.columns.find(
column => column.name === 'Override Status'
)
ok(gradingPeriodColumn)
})
test('does not create a column for course status if includes_course_score_status is false', () => {
initGradebook()
const gradingPeriodColumn = mainGridArgs.columns.find(
column => column.id === 'override_status_course'
)
notOk(gradingPeriodColumn)
})
})
QUnit.module('value population', () => {
@ -389,6 +481,7 @@ QUnit.module('override score changes', hooks => {
new_score: '70',
},
]
defaultUploadedGradebook.students[0].override_statuses = []
initGradebook()
// setCellCssStyles should be called with an empty hash since nothing to
@ -404,11 +497,126 @@ QUnit.module('override score changes', hooks => {
new_score: '70',
},
]
defaultUploadedGradebook.students[0].override_statuses = []
initGradebook()
// setCellCssStyles should be called with an empty hash since nothing to
// highlight
deepEqual(gradeReviewRow, {}, 'no highlightable changes')
})
test('populates the grid data with course override statuses for each student', () => {
defaultUploadedGradebook.override_statuses.includes_course_score_status = true
defaultUploadedGradebook.override_statuses.grading_periods = []
defaultUploadedGradebook.students[0].override_statuses = [
{
grading_period_id: null,
student_id: '1',
current_grade_status: 'BROCCOLI',
new_grade_status: 'POTATO',
},
]
initGradebook()
const dataForStudent = mainGridArgs.data.find(datum => datum.id === '1')
deepEqual(dataForStudent.override_status_course, {
current_grade_status: 'BROCCOLI',
new_grade_status: 'POTATO',
grading_period_id: null,
student_id: '1',
})
})
test('populates the grid data with grading period override statuses for each student', () => {
initGradebook()
const dataForStudent = mainGridArgs.data.find(datum => datum.id === '1')
deepEqual(dataForStudent.override_status_1, {
grading_period_id: '1',
student_id: '1',
current_grade_status: 'CARROT',
new_grade_status: 'POTATO',
})
})
test('highlights cells if the override status is changed', () => {
initGradebook()
const firstStudentRow = gradeReviewRow[0]
strictEqual(
firstStudentRow.override_status_1_conflicting,
'left-highlight',
'current status is highlighted'
)
strictEqual(
firstStudentRow.override_status_1,
'right-highlight',
'updated status is highlighted'
)
})
test('highlights cells if the override status has been removed', () => {
initGradebook()
const firstStudentRow = gradeReviewRow[0]
strictEqual(
firstStudentRow.override_status_3_conflicting,
'left-highlight',
'current status is highlighted'
)
strictEqual(
firstStudentRow.override_status_3,
'right-highlight',
'updated (removed) status is highlighted'
)
})
})
test('does not highlight the cells if the override status has been newly added', () => {
defaultUploadedGradebook.students[0].override_scores = []
defaultUploadedGradebook.students[0].override_statuses = [
{
grading_period_id: '1',
student_id: '1',
current_grade_status: null,
new_grade_status: 'POTATO',
},
]
initGradebook()
deepEqual(gradeReviewRow, {}, 'no highlightable changes')
})
test('does not highlight cells if the override status has not changed', () => {
defaultUploadedGradebook.students[0].override_scores = []
defaultUploadedGradebook.students[0].override_statuses = [
{
grading_period_id: '1',
student_id: '1',
current_grade_status: 'POTATO',
new_grade_status: 'POTATO',
},
]
initGradebook()
// setCellCssStyles should be called with an empty hash since nothing to
// highlight
deepEqual(gradeReviewRow, {}, 'no highlightable changes')
})
test('does not highlight cells if the override status case changed', () => {
defaultUploadedGradebook.students[0].override_scores = []
defaultUploadedGradebook.students[0].override_statuses = [
{
grading_period_id: '1',
student_id: '1',
current_grade_status: 'POTATO',
new_grade_status: 'potato',
},
]
initGradebook()
// setCellCssStyles should be called with an empty hash since nothing to
// highlight
deepEqual(gradeReviewRow, {}, 'no highlightable changes')
})
})

View File

@ -197,6 +197,16 @@ const GradebookUploader = {
this.addOverrideScoreChangeColumn(labelData, gridData, gradingPeriod)
})
}
if (uploadedGradebook.override_statuses != null) {
const overrideStatuses = uploadedGradebook.override_statuses
if (overrideStatuses.includes_course_score_status) {
this.addOverrideStatusChangeColumn(labelData, gridData)
}
overrideStatuses.grading_periods.forEach(gradingPeriod => {
this.addOverrideStatusChangeColumn(labelData, gridData, gradingPeriod)
})
}
$.each(uploadedGradebook.students, function (index) {
const row = {
@ -235,6 +245,21 @@ const GradebookUploader = {
row[`${columnId}_conflicting`] = overrideScore
})
currentStudent.override_statuses?.forEach(overrideStatus => {
const id = overrideStatus.grading_period_id || 'course'
const columnId = `override_status_${id}`
if (
overrideStatus.current_grade_status !== null &&
overrideStatus.current_grade_status?.toLowerCase() !==
overrideStatus.new_grade_status?.toLowerCase()
) {
rowsToHighlight.push({rowIndex: index, id: columnId})
}
row[columnId] = overrideStatus
row[`${columnId}_conflicting`] = overrideStatus
})
gridData.data.push(row)
row.active = true
})
@ -530,6 +555,46 @@ const GradebookUploader = {
}
labelData.columns.push(overrideScoreHeaderColumn)
},
addOverrideStatusChangeColumn(labelData, gridData, gradingPeriod = null) {
// A null grading period means these changes are for override grades for the course
const id = gradingPeriod?.id || 'course'
const title = gradingPeriod?.title
? I18n.t('Override Status (%{gradingPeriod})', {gradingPeriod: gradingPeriod.title})
: I18n.t('Override Status')
const newOverrideStatusColumn = {
id: `override_status_${id}`,
type: 'assignment',
name: htmlEscape(I18n.t('To')),
field: `override_status_${id}`,
width: 125,
editor: Slick.Editors.UploadGradeCellEditor,
editorFormatter: 'override_status',
editorParser: 'override_status',
formatter: GradebookUploader.createGeneralFormatter('new_grade_status'),
active: true,
cssClass: 'new-grade',
}
const conflictingOverrideScoreColumn = {
id: `override_status_${id}_conflicting`,
width: 125,
formatter: GradebookUploader.createGeneralFormatter('current_grade_status'),
field: `override_status_${id}_conflicting`,
name: htmlEscape(I18n.t('From')),
cssClass: 'conflicting-grade',
}
gridData.columns.push(conflictingOverrideScoreColumn, newOverrideStatusColumn)
const overrideScoreHeaderColumn = {
id: `override_status_${id}`,
width: 250,
name: htmlEscape(title),
headerCssClass: 'assignment',
}
labelData.columns.push(overrideScoreHeaderColumn)
},
}
export default GradebookUploader