prettify new gradebook

refs GRADE-1934

test plan:
 * Verify Jenkins passes

Change-Id: I5d4d69e45955bb2beae61f5a3764e81c1e9d9def
Reviewed-on: https://gerrit.instructure.com/177584
Tested-by: Jenkins
Reviewed-by: Derek Bender <djbender@instructure.com>
QA-Review: Jeremy Neander <jneander@instructure.com>
Product-Review: Jeremy Neander <jneander@instructure.com>
This commit is contained in:
Jeremy Neander 2019-01-11 08:25:10 -06:00
parent 08e9fbcca2
commit f54e1ec1b9
47 changed files with 3808 additions and 3075 deletions

View File

@ -30,7 +30,7 @@ const getGradebookTab = () => UserSettings.contextGet('gradebook_tab')
const setGradebookTab = view => UserSettings.contextSet('gradebook_tab', view) const setGradebookTab = view => UserSettings.contextSet('gradebook_tab', view)
class GradebookRouter extends Backbone.Router { class GradebookRouter extends Backbone.Router {
static initClass () { static initClass() {
this.prototype.routes = { this.prototype.routes = {
'': 'tab', '': 'tab',
'tab-assignment': 'tabAssignment', 'tab-assignment': 'tabAssignment',
@ -39,13 +39,13 @@ class GradebookRouter extends Backbone.Router {
} }
} }
initialize () { initialize() {
this.isLoaded = false this.isLoaded = false
this.views = {} this.views = {}
ENV.GRADEBOOK_OPTIONS.assignmentOrOutcome = getGradebookTab(); ENV.GRADEBOOK_OPTIONS.assignmentOrOutcome = getGradebookTab()
ENV.GRADEBOOK_OPTIONS.navigate = this.navigate.bind(this); ENV.GRADEBOOK_OPTIONS.navigate = this.navigate.bind(this)
this.views.assignment = new Gradebook(this.gradebookOptions()); this.views.assignment = new Gradebook(this.gradebookOptions())
this.views.assignment.initialize(); this.views.assignment.initialize()
if (ENV.GRADEBOOK_OPTIONS.outcome_gradebook_enabled) { if (ENV.GRADEBOOK_OPTIONS.outcome_gradebook_enabled) {
this.views.outcome = this.initOutcomes() this.views.outcome = this.initOutcomes()
this.renderPagination(0, 0) this.renderPagination(0, 0)
@ -54,15 +54,15 @@ class GradebookRouter extends Backbone.Router {
return this return this
} }
gradebookOptions () { gradebookOptions() {
return { return {
...ENV.GRADEBOOK_OPTIONS, ...ENV.GRADEBOOK_OPTIONS,
locale: ENV.LOCALE, locale: ENV.LOCALE,
currentUserId: ENV.current_user_id currentUserId: ENV.current_user_id
}; }
} }
initOutcomes () { initOutcomes() {
const book = new OutcomeGradebookView({ const book = new OutcomeGradebookView({
el: $('.outcome-gradebook'), el: $('.outcome-gradebook'),
gradebook: this.views.assignment, gradebook: this.views.assignment,
@ -74,12 +74,16 @@ class GradebookRouter extends Backbone.Router {
renderPagination(page, pageCount) { renderPagination(page, pageCount) {
ReactDOM.render( ReactDOM.render(
<Paginator page={page} pageCount={pageCount} loadPage={(p) => this.views.outcome.loadPage(p)} />, <Paginator
document.getElementById("outcome-gradebook-paginator") page={page}
pageCount={pageCount}
loadPage={p => this.views.outcome.loadPage(p)}
/>,
document.getElementById('outcome-gradebook-paginator')
) )
} }
tabOutcome () { tabOutcome() {
window.tab = 'outcome' window.tab = 'outcome'
$('.assignment-gradebook-container').addClass('hidden') $('.assignment-gradebook-container').addClass('hidden')
$('.outcome-gradebook-container > div').removeClass('hidden') $('.outcome-gradebook-container > div').removeClass('hidden')
@ -87,7 +91,7 @@ class GradebookRouter extends Backbone.Router {
return setGradebookTab('outcome') return setGradebookTab('outcome')
} }
tabAssignment () { tabAssignment() {
window.tab = 'assignment' window.tab = 'assignment'
$('.outcome-gradebook-container > div').addClass('hidden') $('.outcome-gradebook-container > div').addClass('hidden')
$('.assignment-gradebook-container').removeClass('hidden') $('.assignment-gradebook-container').removeClass('hidden')
@ -95,10 +99,10 @@ class GradebookRouter extends Backbone.Router {
return setGradebookTab('assignment') return setGradebookTab('assignment')
} }
tab () { tab() {
let view = getGradebookTab() let view = getGradebookTab()
window.tab = view window.tab = view
if ((view !== 'outcome') || !this.views.outcome) { if (view !== 'outcome' || !this.views.outcome) {
view = 'assignment' view = 'assignment'
} }
$('.assignment-gradebook-container, .outcome-gradebook-container > div').addClass('hidden') $('.assignment-gradebook-container, .outcome-gradebook-container > div').addClass('hidden')
@ -107,7 +111,7 @@ class GradebookRouter extends Backbone.Router {
this.views[view].onShow() this.views[view].onShow()
return setGradebookTab(view) return setGradebookTab(view)
} }
} }
GradebookRouter.initClass() GradebookRouter.initClass()
new GradebookRouter() new GradebookRouter()

View File

@ -20,71 +20,78 @@ import $ from 'jquery'
import cheaterDepaginate from '../shared/CheatDepaginator' import cheaterDepaginate from '../shared/CheatDepaginator'
import StudentContentDataLoader from './default_gradebook/DataLoader/StudentContentDataLoader' import StudentContentDataLoader from './default_gradebook/DataLoader/StudentContentDataLoader'
function getStudentIds (courseId) { function getStudentIds(courseId) {
const url = `/courses/${courseId}/gradebook/user_ids`; const url = `/courses/${courseId}/gradebook/user_ids`
return $.ajaxJSON(url, 'GET', {}); return $.ajaxJSON(url, 'GET', {})
} }
function getGradingPeriodAssignments (courseId) { function getGradingPeriodAssignments(courseId) {
const url = `/courses/${courseId}/gradebook/grading_period_assignments`; const url = `/courses/${courseId}/gradebook/grading_period_assignments`
return $.ajaxJSON(url, 'GET', {}); return $.ajaxJSON(url, 'GET', {})
} }
function getAssignmentGroups (url, params) { function getAssignmentGroups(url, params) {
return cheaterDepaginate(url, params); return cheaterDepaginate(url, params)
} }
function getContextModules (url) { function getContextModules(url) {
return cheaterDepaginate(url); return cheaterDepaginate(url)
} }
function getCustomColumns (url) { function getCustomColumns(url) {
return cheaterDepaginate(url, { include_hidden: true }); return cheaterDepaginate(url, {include_hidden: true})
} }
function getDataForColumn (columnId, url, params, cb) { function getDataForColumn(columnId, url, params, cb) {
const columnUrl = url.replace(/:id/, columnId); const columnUrl = url.replace(/:id/, columnId)
const augmentedCallback = data => cb(columnId, data); const augmentedCallback = data => cb(columnId, data)
return cheaterDepaginate(columnUrl, params, augmentedCallback); return cheaterDepaginate(columnUrl, params, augmentedCallback)
} }
function getCustomColumnData (options, customColumnsDfd, waitForDfds) { function getCustomColumnData(options, customColumnsDfd, waitForDfds) {
const url = options.customColumnDataURL; const url = options.customColumnDataURL
const params = options.customColumnDataParams; const params = options.customColumnDataParams
const cb = options.customColumnDataPageCb; const cb = options.customColumnDataPageCb
const customColumnDataLoaded = $.Deferred(); const customColumnDataLoaded = $.Deferred()
if (url) { if (url) {
// waitForDfds ensures that custom column data is loaded *last* // waitForDfds ensures that custom column data is loaded *last*
$.when(...waitForDfds).then(() => { $.when(...waitForDfds).then(() => {
if (options.customColumnIds) { if (options.customColumnIds) {
const customColumnDataDfds = options.customColumnIds.map(columnId => getDataForColumn(columnId, url, params, cb)); const customColumnDataDfds = options.customColumnIds.map(columnId =>
$.when(...customColumnDataDfds).then(() => customColumnDataLoaded.resolve()); getDataForColumn(columnId, url, params, cb)
)
$.when(...customColumnDataDfds).then(() => customColumnDataLoaded.resolve())
} else { } else {
customColumnsDfd.then((customColumns) => { customColumnsDfd.then(customColumns => {
const customColumnDataDfds = customColumns.map(col => getDataForColumn(col.id, url, params, cb)); const customColumnDataDfds = customColumns.map(col =>
$.when(...customColumnDataDfds).then(() => customColumnDataLoaded.resolve()); getDataForColumn(col.id, url, params, cb)
}); )
$.when(...customColumnDataDfds).then(() => customColumnDataLoaded.resolve())
})
} }
}); })
} }
return customColumnDataLoaded; return customColumnDataLoaded
} }
function loadGradebookData (opts) { function loadGradebookData(opts) {
const gotAssignmentGroups = getAssignmentGroups(opts.assignmentGroupsURL, opts.assignmentGroupsParams); const gotAssignmentGroups = getAssignmentGroups(
opts.assignmentGroupsURL,
opts.assignmentGroupsParams
)
if (opts.onlyLoadAssignmentGroups) { if (opts.onlyLoadAssignmentGroups) {
return { gotAssignmentGroups }; return {gotAssignmentGroups}
} }
// Begin loading Students before any other data. // Begin loading Students before any other data.
const gotStudentIds = getStudentIds(opts.courseId); const gotStudentIds = getStudentIds(opts.courseId)
let gotGradingPeriodAssignments; let gotGradingPeriodAssignments
if (opts.getGradingPeriodAssignments) { if (opts.getGradingPeriodAssignments) {
gotGradingPeriodAssignments = getGradingPeriodAssignments(opts.courseId); gotGradingPeriodAssignments = getGradingPeriodAssignments(opts.courseId)
} }
const gotCustomColumns = getCustomColumns(opts.customColumnsURL); const gotCustomColumns = getCustomColumns(opts.customColumnsURL)
const studentContentDataLoader = new StudentContentDataLoader({ const studentContentDataLoader = new StudentContentDataLoader({
courseId: opts.courseId, courseId: opts.courseId,
@ -100,7 +107,7 @@ function loadGradebookData (opts) {
submissionsUrl: opts.submissionsURL submissionsUrl: opts.submissionsURL
}) })
const gotContextModules = getContextModules(opts.contextModulesURL); const gotContextModules = getContextModules(opts.contextModulesURL)
const gotStudents = $.Deferred() const gotStudents = $.Deferred()
const gotSubmissions = $.Deferred() const gotSubmissions = $.Deferred()
@ -112,7 +119,7 @@ function loadGradebookData (opts) {
}) })
// Custom Column Data will load only after custom columns and all submissions. // Custom Column Data will load only after custom columns and all submissions.
const gotCustomColumnData = getCustomColumnData(opts, gotCustomColumns, [gotSubmissions]); const gotCustomColumnData = getCustomColumnData(opts, gotCustomColumns, [gotSubmissions])
return { return {
gotAssignmentGroups, gotAssignmentGroups,
@ -123,10 +130,10 @@ function loadGradebookData (opts) {
gotStudents, gotStudents,
gotSubmissions, gotSubmissions,
gotCustomColumnData gotCustomColumnData
}; }
} }
export default { export default {
getDataForColumn, getDataForColumn,
loadGradebookData loadGradebookData
}; }

View File

@ -17,7 +17,7 @@
*/ */
import React from 'react' import React from 'react'
import { bool, func, shape, string } from 'prop-types' import {bool, func, shape, string} from 'prop-types'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import $ from 'jquery' import $ from 'jquery'
import I18n from 'i18n!modules' import I18n from 'i18n!modules'
@ -31,19 +31,19 @@ class PostGradesApp extends React.Component {
labelText: string.isRequired, labelText: string.isRequired,
store: shape({ store: shape({
addChangeListener: func.isRequired, addChangeListener: func.isRequired,
removeChangeListener: func.isRequired, removeChangeListener: func.isRequired
}).isRequired, }).isRequired,
renderAsButton: bool, renderAsButton: bool,
returnFocusTo: shape({ returnFocusTo: shape({
focus: func.isRequired focus: func.isRequired
}).isRequired }).isRequired
}; }
static defaultProps = { static defaultProps = {
renderAsButton: false renderAsButton: false
}; }
static AppLaunch (store, returnFocusTo) { static AppLaunch(store, returnFocusTo) {
const $dialog = $('<div class="post-grades-dialog">').dialog({ const $dialog = $('<div class="post-grades-dialog">').dialog({
title: I18n.t('Sync Grades to SIS'), title: I18n.t('Sync Grades to SIS'),
maxWidth: 650, maxWidth: 650,
@ -54,58 +54,55 @@ class PostGradesApp extends React.Component {
height: 450, height: 450,
resizable: false, resizable: false,
buttons: [], buttons: [],
close () { close() {
ReactDOM.unmountComponentAtNode(this); ReactDOM.unmountComponentAtNode(this)
$(this).remove(); $(this).remove()
if (returnFocusTo) { if (returnFocusTo) {
returnFocusTo.focus(); returnFocusTo.focus()
} }
} }
}); })
function closeDialog (e) { function closeDialog(e) {
e.preventDefault(); e.preventDefault()
$dialog.dialog('close'); $dialog.dialog('close')
} }
store.reset() store.reset()
ReactDOM.render(<PostGradesDialog store={store} closeDialog={closeDialog} />, $dialog[0]); ReactDOM.render(<PostGradesDialog store={store} closeDialog={closeDialog} />, $dialog[0])
} }
componentDidMount () { componentDidMount() {
this.boundForceUpdate = this.forceUpdate.bind(this) this.boundForceUpdate = this.forceUpdate.bind(this)
this.props.store.addChangeListener(this.boundForceUpdate) this.props.store.addChangeListener(this.boundForceUpdate)
} }
componentWillUnmount () { this.props.store.removeChangeListener(this.boundForceUpdate) } componentWillUnmount() {
this.props.store.removeChangeListener(this.boundForceUpdate)
openDialog (e) {
e.preventDefault();
PostGradesApp.AppLaunch(this.props.store, this.props.returnFocusTo);
} }
render () { openDialog(e) {
e.preventDefault()
PostGradesApp.AppLaunch(this.props.store, this.props.returnFocusTo)
}
render() {
const navClass = classnames({ const navClass = classnames({
'ui-button': this.props.renderAsButton 'ui-button': this.props.renderAsButton
}); })
if (this.props.renderAsButton) { if (this.props.renderAsButton) {
return ( return (
<button <button id="post-grades-button" className={navClass} onClick={this.openDialog}>
id="post-grades-button" {this.props.labelText}
className={navClass} </button>
onClick={this.openDialog} )
>{this.props.labelText}</button>
);
} else { } else {
return ( return (
<a <a tabIndex={0} id="post-grades-button" className={navClass} onClick={this.openDialog}>
tabIndex={0} {this.props.labelText}
id="post-grades-button" </a>
className={navClass} )
onClick={this.openDialog}
>{this.props.labelText}</a>
);
} }
} }
} }

View File

@ -135,7 +135,8 @@ class PostGradesDialogCorrectionsPage extends React.Component {
onClick={this.ignoreErrorsThenProceed} onClick={this.ignoreErrorsThenProceed}
> >
{errorCount > 0 ? I18n.t('Ignore These') : I18n.t('Continue')} {errorCount > 0 ? I18n.t('Ignore These') : I18n.t('Continue')}
&nbsp;<i className="icon-arrow-right" /> &nbsp;
<i className="icon-arrow-right" />
</button> </button>
</div> </div>
</form> </form>

View File

@ -63,7 +63,8 @@ class PostGradesDialogNeedsGradingPage extends React.Component {
className="btn btn-primary" className="btn btn-primary"
onClick={this.props.leaveNeedsGradingPage} onClick={this.props.leaveNeedsGradingPage}
> >
{I18n.t('Continue')}&nbsp;<i className="icon-arrow-right" /> {I18n.t('Continue')}&nbsp;
<i className="icon-arrow-right" />
</button> </button>
</div> </div>
</form> </form>

View File

@ -21,19 +21,18 @@ import _ from 'underscore'
import createStore from '../../shared/helpers/createStore' import createStore from '../../shared/helpers/createStore'
import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils' import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
var PostGradesStore = (state) => { var PostGradesStore = state => {
var store = $.extend(createStore(state), { var store = $.extend(createStore(state), {
reset() {
reset () {
var assignments = this.getAssignments() var assignments = this.getAssignments()
_.each(assignments, (a) => a.please_ignore = false) _.each(assignments, a => (a.please_ignore = false))
this.setState({ this.setState({
assignments: assignments, assignments: assignments,
pleaseShowNeedsGradingPage: false pleaseShowNeedsGradingPage: false
}) })
}, },
hasAssignments () { hasAssignments() {
var assignments = this.getAssignments() var assignments = this.getAssignments()
if (assignments != undefined && assignments.length > 0) { if (assignments != undefined && assignments.length > 0) {
return true return true
@ -42,16 +41,14 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
} }
}, },
getSISSectionId (section_id) { getSISSectionId(section_id) {
var sections = this.getState().sections var sections = this.getState().sections
return (sections && sections[section_id]) ? return sections && sections[section_id] ? sections[section_id].sis_section_id : null
sections[section_id].sis_section_id :
null;
}, },
allOverrideIds(a) { allOverrideIds(a) {
var overrides = [] var overrides = []
_.each(a.overrides, (o) => { _.each(a.overrides, o => {
overrides.push(o.course_section_id) overrides.push(o.course_section_id)
}) })
return overrides return overrides
@ -60,19 +57,21 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
overrideForEveryone(a) { overrideForEveryone(a) {
var overrides = this.allOverrideIds(a) var overrides = this.allOverrideIds(a)
var sections = _.keys(this.getState().sections) var sections = _.keys(this.getState().sections)
var section_ids_with_no_overrides = $(sections).not(overrides).get(); var section_ids_with_no_overrides = $(sections)
.not(overrides)
.get()
var section_for_everyone = _.find(section_ids_with_no_overrides, (o) => { var section_for_everyone = _.find(section_ids_with_no_overrides, o => {
return state.selected.id == o return state.selected.id == o
}); })
return section_for_everyone return section_for_everyone
}, },
selectedSISId () { selectedSISId() {
return this.getState().selected.sis_id return this.getState().selected.sis_id
}, },
setGradeBookAssignments (gradebookAssignments) { setGradeBookAssignments(gradebookAssignments) {
var assignments = [] var assignments = []
for (var id in gradebookAssignments) { for (var id in gradebookAssignments) {
var gba = gradebookAssignments[id] var gba = gradebookAssignments[id]
@ -84,25 +83,31 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
} }
// A second loop is needed to ensure non-unique name errors are included // A second loop is needed to ensure non-unique name errors are included
// in hasError // in hasError
_.each(assignments, (a) => { _.each(assignments, a => {
a.original_error = assignmentUtils.hasError(assignments, a) a.original_error = assignmentUtils.hasError(assignments, a)
}) })
this.setState({ assignments: assignments }) this.setState({assignments: assignments})
}, },
setSections (sections) { setSections(sections) {
this.setState({ sections: sections }) this.setState({sections: sections})
this.setSelectedSection( this.getState().sectionToShow ) this.setSelectedSection(this.getState().sectionToShow)
}, },
validCheck(a) { validCheck(a) {
if(a.overrideForThisSection != undefined && a.currentlySelected.type == 'course' && a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id){ if (
a.overrideForThisSection != undefined &&
a.currentlySelected.type == 'course' &&
a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id
) {
return a.due_at != null ? true : false return a.due_at != null ? true : false
} } else if (
else if(a.overrideForThisSection != undefined && a.currentlySelected.type == 'section' && a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id){ a.overrideForThisSection != undefined &&
a.currentlySelected.type == 'section' &&
a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id
) {
return a.overrideForThisSection.due_at != null ? true : false return a.overrideForThisSection.due_at != null ? true : false
} } else {
else{
return true return true
} }
}, },
@ -110,75 +115,83 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
getAssignments() { getAssignments() {
var assignments = this.getState().assignments var assignments = this.getState().assignments
var state = this.getState() var state = this.getState()
if (state.selected.type == "section") { if (state.selected.type == 'section') {
_.each(assignments, (a) => { _.each(assignments, a => {
a.recentlyUpdated = false a.recentlyUpdated = false
a.currentlySelected = state.selected a.currentlySelected = state.selected
a.sectionCount = _.keys(state.sections).length a.sectionCount = _.keys(state.sections).length
a.overrideForThisSection = _.find(a.overrides, (override) => { a.overrideForThisSection = _.find(a.overrides, override => {
return override.course_section_id == state.selected.id; return override.course_section_id == state.selected.id
}); })
//Handle assignment with overrides and the 'Everyone Else' scenario with a section that does not have any overrides //Handle assignment with overrides and the 'Everyone Else' scenario with a section that does not have any overrides
//cleanup overrideForThisSection logic //cleanup overrideForThisSection logic
if(a.overrideForThisSection == undefined){ a.selectedSectionForEveryone = this.overrideForEveryone(a) } if (a.overrideForThisSection == undefined) {
}); a.selectedSectionForEveryone = this.overrideForEveryone(a)
}
})
} else { } else {
_.each(assignments, (a) => { _.each(assignments, a => {
a.recentlyUpdated = false a.recentlyUpdated = false
a.currentlySelected = state.selected a.currentlySelected = state.selected
a.sectionCount = _.keys(state.sections).length a.sectionCount = _.keys(state.sections).length
//Course is currentlySlected with sections that have overrides AND are invalid //Course is currentlySlected with sections that have overrides AND are invalid
a.overrideForThisSection = _.find(a.overrides, (override) => { a.overrideForThisSection = _.find(a.overrides, override => {
return override.due_at == null || typeof(override.due_at) == 'object'; return override.due_at == null || typeof override.due_at == 'object'
}); })
//Handle assignment with overrides and the 'Everyone Else' scenario with the course currentlySelected //Handle assignment with overrides and the 'Everyone Else' scenario with the course currentlySelected
if(a.overrideForThisSection == undefined){ a.selectedSectionForEveryone = this.overrideForEveryone(a) } if (a.overrideForThisSection == undefined) {
}); a.selectedSectionForEveryone = this.overrideForEveryone(a)
} }
return assignments; })
}
return assignments
}, },
getAssignment(assignment_id) { getAssignment(assignment_id) {
var assignments = this.getAssignments() var assignments = this.getAssignments()
return _.find(assignments, (a) => a.id == assignment_id) return _.find(assignments, a => a.id == assignment_id)
}, },
setSelectedSection (section) { setSelectedSection(section) {
var state = this.getState() var state = this.getState()
var section_id = parseInt(section) var section_id = parseInt(section)
var selected; var selected
if (section) { if (section) {
selected = { selected = {
type: "section", type: 'section',
id: section_id, id: section_id,
sis_id: this.getSISSectionId(section_id) sis_id: this.getSISSectionId(section_id)
}; }
} else { } else {
selected = { selected = {
type: "course", type: 'course',
id: state.course.id, id: state.course.id,
sis_id: state.course.sis_id sis_id: state.course.sis_id
}; }
} }
this.setState({ selected: selected, sectionToShow: section }) this.setState({selected: selected, sectionToShow: section})
}, },
updateAssignment (assignment_id, newAttrs) { updateAssignment(assignment_id, newAttrs) {
var assignments = this.getAssignments() var assignments = this.getAssignments()
var assignment = _.find(assignments, (a) => a.id == assignment_id) var assignment = _.find(assignments, a => a.id == assignment_id)
$.extend(assignment, newAttrs) $.extend(assignment, newAttrs)
this.setState({assignments: assignments}) this.setState({assignments: assignments})
}, },
updateAssignmentDate(assignment_id, date){ updateAssignmentDate(assignment_id, date) {
var assignments = this.getState().assignments var assignments = this.getState().assignments
var assignment = _.find(assignments, (a) => a.id == assignment_id) var assignment = _.find(assignments, a => a.id == assignment_id)
//the assignment has an override and the override being updated is for the section that is currentlySelected update it //the assignment has an override and the override being updated is for the section that is currentlySelected update it
if(assignment.overrideForThisSection != undefined && assignment.currentlySelected.id.toString() == assignment.overrideForThisSection.course_section_id) { if (
assignment.overrideForThisSection != undefined &&
assignment.currentlySelected.id.toString() ==
assignment.overrideForThisSection.course_section_id
) {
assignment.overrideForThisSection.due_at = date assignment.overrideForThisSection.due_at = date
assignment.please_ignore = false assignment.please_ignore = false
assignment.hadOriginalErrors = true assignment.hadOriginalErrors = true
@ -187,7 +200,11 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
} }
//the section override being set from the course level of the sction dropdown //the section override being set from the course level of the sction dropdown
else if(assignment.overrideForThisSection != undefined && assignment.currentlySelected.id.toString() != assignment.overrideForThisSection.course_section_id){ else if (
assignment.overrideForThisSection != undefined &&
assignment.currentlySelected.id.toString() !=
assignment.overrideForThisSection.course_section_id
) {
assignment.overrideForThisSection.due_at = date assignment.overrideForThisSection.due_at = date
assignment.please_ignore = false assignment.please_ignore = false
assignment.hadOriginalErrors = true assignment.hadOriginalErrors = true
@ -197,7 +214,11 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
//update normal assignment and the 'Everyone Else' scenario if the course is currentlySelected //update normal assignment and the 'Everyone Else' scenario if the course is currentlySelected
else { else {
this.updateAssignment(assignment_id, {due_at: date, please_ignore: false, hadOriginalErrors: true}) this.updateAssignment(assignment_id, {
due_at: date,
please_ignore: false,
hadOriginalErrors: true
})
} }
}, },
@ -205,13 +226,13 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
a.hadOriginalErrors = a.hadOriginalErrors == true a.hadOriginalErrors = a.hadOriginalErrors == true
}, },
saveAssignments () { saveAssignments() {
var assignments = assignmentUtils.withOriginalErrorsNotIgnored(this.getAssignments()) var assignments = assignmentUtils.withOriginalErrorsNotIgnored(this.getAssignments())
var course_id = this.getState().course.id var course_id = this.getState().course.id
_.each(assignments, (a) => { _.each(assignments, a => {
this.assignmentOverrideOrigianlErrorCheck(a) this.assignmentOverrideOrigianlErrorCheck(a)
assignmentUtils.saveAssignmentToCanvas(course_id, a) assignmentUtils.saveAssignmentToCanvas(course_id, a)
}); })
}, },
postGrades() { postGrades() {
@ -220,23 +241,23 @@ import assignmentUtils from '../../gradezilla/SISGradePassback/assignmentUtils'
assignmentUtils.postGradesThroughCanvas(selected, assignments) assignmentUtils.postGradesThroughCanvas(selected, assignments)
}, },
getPage () { getPage() {
var state = this.getState() var state = this.getState()
if (state.pleaseShowNeedsGradingPage) { if (state.pleaseShowNeedsGradingPage) {
return "needsGrading" return 'needsGrading'
} else { } else {
var originals = assignmentUtils.withOriginalErrors(this.getAssignments()) var originals = assignmentUtils.withOriginalErrors(this.getAssignments())
var withErrorsCount = _.keys(assignmentUtils.withErrors(this.getAssignments())).length var withErrorsCount = _.keys(assignmentUtils.withErrors(this.getAssignments())).length
if (withErrorsCount == 0 && (state.pleaseShowSummaryPage || originals.length == 0)) { if (withErrorsCount == 0 && (state.pleaseShowSummaryPage || originals.length == 0)) {
return "summary" return 'summary'
} else { } else {
return "corrections" return 'corrections'
} }
} }
} }
}) })
return store return store
}; }
export default PostGradesStore export default PostGradesStore

View File

@ -20,56 +20,48 @@ import $ from 'jquery'
import _ from 'underscore' import _ from 'underscore'
import '../../shared/helpers/createStore' import '../../shared/helpers/createStore'
let assignmentUtils = { let assignmentUtils = {
copyFromGradebook (assignment) { copyFromGradebook(assignment) {
var a = _.pick(assignment, [ var a = _.pick(assignment, ['id', 'name', 'due_at', 'needs_grading_count', 'overrides'])
"id",
"name",
"due_at",
"needs_grading_count",
"overrides"
])
a.please_ignore = false a.please_ignore = false
a.original_error = false a.original_error = false
return a return a
}, },
namesMatch (a, b) { namesMatch(a, b) {
return a.name === b.name && a !== b return a.name === b.name && a !== b
}, },
nameTooLong (a) { nameTooLong(a) {
if (_.unescape(a.name).length > 30){ if (_.unescape(a.name).length > 30) {
return true
}
else{
return false
}
},
nameEmpty (a) {
if (a.name.length == 0){
return true
}
else{
return false
}
},
notUniqueName (assignments, a) {
return assignments.some(_.partial(assignmentUtils.namesMatch, a))
},
noDueDateForEveryoneElseOverride(a) {
var has_overrides = a.overrides != undefined ? a.overrides.length > 0 : false
if(has_overrides && a.overrides.length != a.sectionCount && !a.due_at){
return true return true
} else { } else {
return false return false
} }
}, },
withOriginalErrors (assignments) { nameEmpty(a) {
if (a.name.length == 0) {
return true
} else {
return false
}
},
notUniqueName(assignments, a) {
return assignments.some(_.partial(assignmentUtils.namesMatch, a))
},
noDueDateForEveryoneElseOverride(a) {
var has_overrides = a.overrides != undefined ? a.overrides.length > 0 : false
if (has_overrides && a.overrides.length != a.sectionCount && !a.due_at) {
return true
} else {
return false
}
},
withOriginalErrors(assignments) {
// This logic handles an assignment with multiple overrides // This logic handles an assignment with multiple overrides
// because #setGradeBookAssignments runs on load // because #setGradeBookAssignments runs on load
// it does not have a reference to what the currently viewed section is. // it does not have a reference to what the currently viewed section is.
@ -78,76 +70,167 @@ import '../../shared/helpers/createStore'
// being viewed for that section. If the override is valid make // being viewed for that section. If the override is valid make
// original error false so that the override is not shown. Vice versa // original error false so that the override is not shown. Vice versa
// for the invalid override on the assignment. // for the invalid override on the assignment.
_.each(assignments, (a) => { _.each(assignments, a => {
if(a.overrideForThisSection != undefined && a.recentlyUpdated != undefined && a.recentlyUpdated == true && a.overrideForThisSection.due_at != null){a.original_error = false} if (
else if(a.overrideForThisSection != undefined && a.recentlyUpdated != undefined && a.recentlyUpdated == false && a.overrideForThisSection.due_at == null){a.original_error = true} a.overrideForThisSection != undefined &&
a.recentlyUpdated != undefined &&
a.recentlyUpdated == true &&
a.overrideForThisSection.due_at != null
) {
a.original_error = false
} else if (
a.overrideForThisSection != undefined &&
a.recentlyUpdated != undefined &&
a.recentlyUpdated == false &&
a.overrideForThisSection.due_at == null
) {
a.original_error = true
}
//for handling original error detection of a valid override for one section and an invalid override for another section //for handling original error detection of a valid override for one section and an invalid override for another section
else if(a.overrideForThisSection != undefined && a.overrideForThisSection.due_at != null && !assignmentUtils.noDueDateForEveryoneElseOverride(a) && a.recentlyUpdated == false && a.hadOriginalErrors == false){a.original_error = false} else if (
a.overrideForThisSection != undefined &&
a.overrideForThisSection.due_at != null &&
!assignmentUtils.noDueDateForEveryoneElseOverride(a) &&
a.recentlyUpdated == false &&
a.hadOriginalErrors == false
) {
a.original_error = false
}
//for handling original error detection of a valid override for one section and the EveryoneElse "override" scenario //for handling original error detection of a valid override for one section and the EveryoneElse "override" scenario
else if(a.overrideForThisSection != undefined && a.overrideForThisSection.due_at != null && assignmentUtils.noDueDateForEveryoneElseOverride(a) && a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id && a.recentlyUpdated == false && a.hadOriginalErrors == false){a.original_error = false} else if (
a.overrideForThisSection != undefined &&
a.overrideForThisSection.due_at != null &&
assignmentUtils.noDueDateForEveryoneElseOverride(a) &&
a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id &&
a.recentlyUpdated == false &&
a.hadOriginalErrors == false
) {
a.original_error = false
}
//for handling original error detection of an override for one section and the EveryoneElse "override" scenario but the second section is currentlySelected and IS NOT valid //for handling original error detection of an override for one section and the EveryoneElse "override" scenario but the second section is currentlySelected and IS NOT valid
else if(a.overrideForThisSection == undefined && assignmentUtils.noDueDateForEveryoneElseOverride(a) && a.due_at == null && a.currentlySelected.id.toString() == a.selectedSectionForEveryone){a.original_error = true} else if (
a.overrideForThisSection == undefined &&
assignmentUtils.noDueDateForEveryoneElseOverride(a) &&
a.due_at == null &&
a.currentlySelected.id.toString() == a.selectedSectionForEveryone
) {
a.original_error = true
}
//for handling original error detection of an override for one section and the EveryoneElse "override" scenario but the second section is currentlySelected and IS valid //for handling original error detection of an override for one section and the EveryoneElse "override" scenario but the second section is currentlySelected and IS valid
else if(a.overrideForThisSection == undefined && a.due_at != null && a.currentlySelected.id.toString() == a.selectedSectionForEveryone && a.hadOriginalErrors == false){a.original_error = false} else if (
a.overrideForThisSection == undefined &&
a.due_at != null &&
a.currentlySelected.id.toString() == a.selectedSectionForEveryone &&
a.hadOriginalErrors == false
) {
a.original_error = false
}
//for handling original error detection of an "override" in the 'EveryoneElse "override" scenario but the course is currentlySelected and IS NOT valid //for handling original error detection of an "override" in the 'EveryoneElse "override" scenario but the course is currentlySelected and IS NOT valid
else if(a.overrideForThisSection == undefined && assignmentUtils.noDueDateForEveryoneElseOverride(a) && a.due_at == null && a.currentlySelected.type == 'course' && a.currentlySelected.id.toString() != a.selectedSectionForEveryone){a.original_error = true} else if (
a.overrideForThisSection == undefined &&
assignmentUtils.noDueDateForEveryoneElseOverride(a) &&
a.due_at == null &&
a.currentlySelected.type == 'course' &&
a.currentlySelected.id.toString() != a.selectedSectionForEveryone
) {
a.original_error = true
}
//for handling original error detection of an "override" in the 'EveryoneElse "override" scenario but the course is currentlySelected and IS valid //for handling original error detection of an "override" in the 'EveryoneElse "override" scenario but the course is currentlySelected and IS valid
else if(a.overrideForThisSection == undefined && a.due_at != null && a.currentlySelected.type == 'course' && a.currentlySelected.id.toString() != a.selectedSectionForEveryone && a.hadOriginalErrors == false){a.original_error = false} else if (
}); a.overrideForThisSection == undefined &&
return _.filter(assignments, (a) => a.original_error && !a.please_ignore) a.due_at != null &&
a.currentlySelected.type == 'course' &&
a.currentlySelected.id.toString() != a.selectedSectionForEveryone &&
a.hadOriginalErrors == false
) {
a.original_error = false
}
})
return _.filter(assignments, a => a.original_error && !a.please_ignore)
}, },
withOriginalErrorsNotIgnored (assignments) { withOriginalErrorsNotIgnored(assignments) {
return _.filter(assignments, function(a){ return (a.original_error || a.hadOriginalErrors) && !a.please_ignore}) return _.filter(assignments, function(a) {
return (a.original_error || a.hadOriginalErrors) && !a.please_ignore
})
}, },
withErrors (assignments) { withErrors(assignments) {
return _.filter(assignments, (a) => assignmentUtils.hasError(assignments, a)) return _.filter(assignments, a => assignmentUtils.hasError(assignments, a))
}, },
notIgnored (assignments) { notIgnored(assignments) {
return _.filter(assignments, (a) => !a.please_ignore) return _.filter(assignments, a => !a.please_ignore)
}, },
needsGrading (assignments) { needsGrading(assignments) {
return _.filter(assignments, (a) => a.needs_grading_count > 0) return _.filter(assignments, a => a.needs_grading_count > 0)
}, },
hasError (assignments, a) { hasError(assignments, a) {
////Decided to ignore ////Decided to ignore
if(a.please_ignore) return false if (a.please_ignore) return false
////Not unique ////Not unique
if(assignmentUtils.notUniqueName(assignments, a)) return true if (assignmentUtils.notUniqueName(assignments, a)) return true
////Name too long ////Name too long
if(assignmentUtils.nameTooLong(a)) return true if (assignmentUtils.nameTooLong(a)) return true
////Name empty ////Name empty
if(assignmentUtils.nameEmpty(a)) return true if (assignmentUtils.nameEmpty(a)) return true
////Non-override missing due_at ////Non-override missing due_at
var has_overrides = a.overrides != undefined ? a.overrides.length > 0 : false var has_overrides = a.overrides != undefined ? a.overrides.length > 0 : false
if(!has_overrides && !a.due_at) return true if (!has_overrides && !a.due_at) return true
////Override missing due_at ////Override missing due_at
var has_this_override = a.overrideForThisSection != undefined var has_this_override = a.overrideForThisSection != undefined
if(has_this_override && a.overrideForThisSection.due_at == null && a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id) return true if (
has_this_override &&
a.overrideForThisSection.due_at == null &&
a.currentlySelected.id.toString() == a.overrideForThisSection.course_section_id
)
return true
////Override missing due_at while currentlySelecteed is at the course level ////Override missing due_at while currentlySelecteed is at the course level
if(has_this_override && a.overrideForThisSection.due_at == null && a.currentlySelected.id.toString() != a.overrideForThisSection.course_section_id) return true if (
has_this_override &&
a.overrideForThisSection.due_at == null &&
a.currentlySelected.id.toString() != a.overrideForThisSection.course_section_id
)
return true
////Has one override and another override for 'Everyone Else' ////Has one override and another override for 'Everyone Else'
//// ////
////The override for 'Everyone Else' isn't really an override and references ////The override for 'Everyone Else' isn't really an override and references
////the assignments actual due_at. So we must check for this behavior ////the assignments actual due_at. So we must check for this behavior
if(assignmentUtils.noDueDateForEveryoneElseOverride(a) && a.currentlySelected != undefined && a.overrideForThisSection != undefined && a.currentlySelected.id.toString() != a.overrideForThisSection.course_section_id) return true if (
assignmentUtils.noDueDateForEveryoneElseOverride(a) &&
a.currentlySelected != undefined &&
a.overrideForThisSection != undefined &&
a.currentlySelected.id.toString() != a.overrideForThisSection.course_section_id
)
return true
////Has only one override but the section that is currently selected does not have an override thus causing the assignment to have due_at that is null making it invalid ////Has only one override but the section that is currently selected does not have an override thus causing the assignment to have due_at that is null making it invalid
if(assignmentUtils.noDueDateForEveryoneElseOverride(a) && a.overrideForThisSection == undefined && a.currentlySelected != undefined && a.currentlySelected.id.toString() == a.selectedSectionForEveryone) return true if (
assignmentUtils.noDueDateForEveryoneElseOverride(a) &&
a.overrideForThisSection == undefined &&
a.currentlySelected != undefined &&
a.currentlySelected.id.toString() == a.selectedSectionForEveryone
)
return true
////'Everyone Else' scenario and the course is currentlySelected but due_at is null making it invalid ////'Everyone Else' scenario and the course is currentlySelected but due_at is null making it invalid
if(assignmentUtils.noDueDateForEveryoneElseOverride(a) && a.overrideForThisSection == undefined && a.currentlySelected != undefined && a.currentlySelected.type == 'course' && a.currentlySelected.id.toString() != a.selectedSectionForEveryone) return true if (
assignmentUtils.noDueDateForEveryoneElseOverride(a) &&
a.overrideForThisSection == undefined &&
a.currentlySelected != undefined &&
a.currentlySelected.type == 'course' &&
a.currentlySelected.id.toString() != a.selectedSectionForEveryone
)
return true
////Passes all tests, looks good. ////Passes all tests, looks good.
return false return false
@ -157,24 +240,39 @@ import '../../shared/helpers/createStore'
return assignment.published && assignment.post_to_sis return assignment.published && assignment.post_to_sis
}, },
saveAssignmentToCanvas (course_id, assignment) { saveAssignmentToCanvas(course_id, assignment) {
// if the date on an override is being updated confirm by checking if the due_at is an object // if the date on an override is being updated confirm by checking if the due_at is an object
if(assignment.overrideForThisSection != undefined && typeof(assignment.overrideForThisSection.due_at) == "object") { if (
assignment.overrideForThisSection != undefined &&
typeof assignment.overrideForThisSection.due_at == 'object'
) {
//allows the validation process to determine when it has been updated and can display the correct page //allows the validation process to determine when it has been updated and can display the correct page
assignment.hadOriginalErrors = false assignment.hadOriginalErrors = false
var url = '/api/v1/courses/' + course_id + '/assignments/' + assignment.id + '/overrides/' + assignment.overrideForThisSection.id var url =
'/api/v1/courses/' +
course_id +
'/assignments/' +
assignment.id +
'/overrides/' +
assignment.overrideForThisSection.id
//sets up form data to allow a single override to be updated //sets up form data to allow a single override to be updated
var fd = new FormData(); var fd = new FormData()
fd.append( 'assignment_override[due_at]', assignment.overrideForThisSection.due_at.toISOString() ) fd.append(
'assignment_override[due_at]',
assignment.overrideForThisSection.due_at.toISOString()
)
$.ajax(url, { $.ajax(url, {
type: 'PUT', type: 'PUT',
data: fd, data: fd,
processData: false, processData: false,
contentType: false, contentType: false,
error: (err) => { error: err => {
var msg = 'An error occurred saving assignment override, (' + assignment.overrideForThisSection.id + '). ' var msg =
msg += "HTTP Error " + data.status + " : " + data.statusText 'An error occurred saving assignment override, (' +
assignment.overrideForThisSection.id +
'). '
msg += 'HTTP Error ' + data.status + ' : ' + data.statusText
$.flashError(msg) $.flashError(msg)
} }
}) })
@ -182,68 +280,70 @@ import '../../shared/helpers/createStore'
// that was just set AND the naming conflict is fixed we must also update the assignment // that was just set AND the naming conflict is fixed we must also update the assignment
// to mock natural behavior to the user so that the naming conflict does not appear again // to mock natural behavior to the user so that the naming conflict does not appear again
url = '/api/v1/courses/' + course_id + '/assignments/' + assignment.id url = '/api/v1/courses/' + course_id + '/assignments/' + assignment.id
data = { assignment: { data = {
assignment: {
name: assignment.name, name: assignment.name,
due_at: assignment.due_at due_at: assignment.due_at
}} }
}
$.ajax(url, { $.ajax(url, {
type: 'PUT', type: 'PUT',
data: JSON.stringify(data), data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
error: (err) => { error: err => {
var msg = 'An error occurred saving assignment (' + assignment.id + '). ' var msg = 'An error occurred saving assignment (' + assignment.id + '). '
msg += "HTTP Error " + data.status + " : " + data.statusText msg += 'HTTP Error ' + data.status + ' : ' + data.statusText
$.flashError(msg) $.flashError(msg)
} }
}) })
} } else {
else {
//allows the validation process to determine when it has been updated and can display the correct page //allows the validation process to determine when it has been updated and can display the correct page
assignment.hadOriginalErrors = false assignment.hadOriginalErrors = false
var url = '/api/v1/courses/' + course_id + '/assignments/' + assignment.id var url = '/api/v1/courses/' + course_id + '/assignments/' + assignment.id
var data = { assignment: { var data = {
assignment: {
name: assignment.name, name: assignment.name,
due_at: assignment.due_at due_at: assignment.due_at
}} }
}
$.ajax(url, { $.ajax(url, {
type: 'PUT', type: 'PUT',
data: JSON.stringify(data), data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
error: (err) => { error: err => {
var msg = 'An error occurred saving assignment (' + assignment.id + '). ' var msg = 'An error occurred saving assignment (' + assignment.id + '). '
msg += "HTTP Error " + data.status + " : " + data.statusText msg += 'HTTP Error ' + data.status + ' : ' + data.statusText
$.flashError(msg) $.flashError(msg)
} }
}) })
} }
}, },
// Sends a post-grades request to Canvas that is then forwarded to SIS App. // Sends a post-grades request to Canvas that is then forwarded to SIS App.
// Expects a list of assignments that will later be queried for grades via // Expects a list of assignments that will later be queried for grades via
// SIS App's workers // SIS App's workers
postGradesThroughCanvas (selected, assignments) { postGradesThroughCanvas(selected, assignments) {
var url = "/api/v1/" + selected.type + "s/" + selected.id + "/post_grades/" var url = '/api/v1/' + selected.type + 's/' + selected.id + '/post_grades/'
var data = { assignments: _.map(assignments, (assignment) => assignment.id) } var data = {assignments: _.map(assignments, assignment => assignment.id)}
$.ajax(url, { $.ajax(url, {
type: 'POST', type: 'POST',
data: JSON.stringify(data), data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
success: (msg) =>{ success: msg => {
if (msg.error){ if (msg.error) {
$.flashError(msg.error) $.flashError(msg.error)
}else{ } else {
$.flashMessage(msg.message) $.flashMessage(msg.message)
} }
}, },
error: (err) => { error: err => {
var msg = 'An error occurred posting grades for (' + selected.type + ' : ' + selected.id +'). ' var msg =
msg += "HTTP Error " + data.status + " : " + data.statusText 'An error occurred posting grades for (' + selected.type + ' : ' + selected.id + '). '
msg += 'HTTP Error ' + data.status + ' : ' + data.statusText
$.flashError(msg) $.flashError(msg)
} }
}) })
} }
}
};
export default assignmentUtils export default assignmentUtils

View File

@ -16,70 +16,92 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import _ from 'underscore'; import _ from 'underscore'
import GradingPeriodsHelper from '../grading/helpers/GradingPeriodsHelper'; import GradingPeriodsHelper from '../grading/helpers/GradingPeriodsHelper'
function submissionGradingPeriodInformation (assignment, student) { function submissionGradingPeriodInformation(assignment, student) {
const submissionInfo = assignment.effectiveDueDates[student.id] || {}; const submissionInfo = assignment.effectiveDueDates[student.id] || {}
return { return {
gradingPeriodID: submissionInfo.grading_period_id, gradingPeriodID: submissionInfo.grading_period_id,
inClosedGradingPeriod: submissionInfo.in_closed_grading_period inClosedGradingPeriod: submissionInfo.in_closed_grading_period
}; }
} }
function hiddenFromStudent (assignment, student) { function hiddenFromStudent(assignment, student) {
if (assignment.only_visible_to_overrides) { if (assignment.only_visible_to_overrides) {
return !_.contains(assignment.assignment_visibility, student.id); return !_.contains(assignment.assignment_visibility, student.id)
} }
return false return false
} }
function gradingPeriodInfoForCell (assignment, student, selectedGradingPeriodID) { function gradingPeriodInfoForCell(assignment, student, selectedGradingPeriodID) {
const specificPeriodSelected = !GradingPeriodsHelper.isAllGradingPeriods(selectedGradingPeriodID); const specificPeriodSelected = !GradingPeriodsHelper.isAllGradingPeriods(selectedGradingPeriodID)
const { gradingPeriodID, inClosedGradingPeriod } = submissionGradingPeriodInformation(assignment, student); const {gradingPeriodID, inClosedGradingPeriod} = submissionGradingPeriodInformation(
const inNoGradingPeriod = !gradingPeriodID; assignment,
const inOtherGradingPeriod = !!gradingPeriodID && specificPeriodSelected && student
selectedGradingPeriodID !== gradingPeriodID; )
const inNoGradingPeriod = !gradingPeriodID
const inOtherGradingPeriod =
!!gradingPeriodID && specificPeriodSelected && selectedGradingPeriodID !== gradingPeriodID
return { return {
inNoGradingPeriod, inNoGradingPeriod,
inOtherGradingPeriod, inOtherGradingPeriod,
inClosedGradingPeriod inClosedGradingPeriod
}; }
} }
function cellMappingsForMultipleGradingPeriods (assignment, student, selectedGradingPeriodID, isAdmin) { function cellMappingsForMultipleGradingPeriods(
const specificPeriodSelected = !GradingPeriodsHelper.isAllGradingPeriods(selectedGradingPeriodID); assignment,
const { gradingPeriodID, inClosedGradingPeriod } = submissionGradingPeriodInformation(assignment, student); student,
const gradingPeriodInfo = gradingPeriodInfoForCell(assignment, student, selectedGradingPeriodID); selectedGradingPeriodID,
let cellMapping; isAdmin
) {
const specificPeriodSelected = !GradingPeriodsHelper.isAllGradingPeriods(selectedGradingPeriodID)
const {gradingPeriodID, inClosedGradingPeriod} = submissionGradingPeriodInformation(
assignment,
student
)
const gradingPeriodInfo = gradingPeriodInfoForCell(assignment, student, selectedGradingPeriodID)
let cellMapping
if (specificPeriodSelected && (!gradingPeriodID || selectedGradingPeriodID !== gradingPeriodID)) { if (specificPeriodSelected && (!gradingPeriodID || selectedGradingPeriodID !== gradingPeriodID)) {
cellMapping = { locked: true, hideGrade: true }; cellMapping = {locked: true, hideGrade: true}
} else if (!isAdmin && inClosedGradingPeriod) { } else if (!isAdmin && inClosedGradingPeriod) {
cellMapping = { locked: true, hideGrade: false }; cellMapping = {locked: true, hideGrade: false}
} else { } else {
cellMapping = { locked: false, hideGrade: false }; cellMapping = {locked: false, hideGrade: false}
} }
return { ...cellMapping, ...gradingPeriodInfo }; return {...cellMapping, ...gradingPeriodInfo}
} }
function cellMapForSubmission (assignment, student, hasGradingPeriods, selectedGradingPeriodID, isAdmin) { function cellMapForSubmission(
assignment,
student,
hasGradingPeriods,
selectedGradingPeriodID,
isAdmin
) {
if (!assignment.published || assignment.anonymize_students) { if (!assignment.published || assignment.anonymize_students) {
return { locked: true, hideGrade: true }; return {locked: true, hideGrade: true}
} else if (assignment.moderated_grading && !assignment.grades_published) { } else if (assignment.moderated_grading && !assignment.grades_published) {
return { locked: true, hideGrade: false }; return {locked: true, hideGrade: false}
} else if (hiddenFromStudent(assignment, student)) { } else if (hiddenFromStudent(assignment, student)) {
return { locked: true, hideGrade: true }; return {locked: true, hideGrade: true}
} else if (hasGradingPeriods) { } else if (hasGradingPeriods) {
return cellMappingsForMultipleGradingPeriods(assignment, student, selectedGradingPeriodID, isAdmin); return cellMappingsForMultipleGradingPeriods(
assignment,
student,
selectedGradingPeriodID,
isAdmin
)
} else { } else {
return { locked: false, hideGrade: false }; return {locked: false, hideGrade: false}
} }
} }
function missingSubmission (student, assignment) { function missingSubmission(student, assignment) {
const submission = { const submission = {
assignment_id: assignment.id, assignment_id: assignment.id,
user_id: student.id, user_id: student.id,
@ -87,53 +109,54 @@ function missingSubmission (student, assignment) {
late: false, late: false,
missing: false, missing: false,
seconds_late: 0 seconds_late: 0
};
const dueDates = assignment.effectiveDueDates[student.id] || {};
if (dueDates.due_at != null && new Date(dueDates.due_at) < new Date()) {
submission.missing = true;
} }
return submission; const dueDates = assignment.effectiveDueDates[student.id] || {}
if (dueDates.due_at != null && new Date(dueDates.due_at) < new Date()) {
submission.missing = true
}
return submission
} }
class SubmissionStateMap { class SubmissionStateMap {
constructor ({ hasGradingPeriods, selectedGradingPeriodID, isAdmin }) { constructor({hasGradingPeriods, selectedGradingPeriodID, isAdmin}) {
this.hasGradingPeriods = hasGradingPeriods; this.hasGradingPeriods = hasGradingPeriods
this.selectedGradingPeriodID = selectedGradingPeriodID; this.selectedGradingPeriodID = selectedGradingPeriodID
this.isAdmin = isAdmin; this.isAdmin = isAdmin
this.submissionCellMap = {}; this.submissionCellMap = {}
this.submissionMap = {}; this.submissionMap = {}
} }
setup (students, assignments) { setup(students, assignments) {
students.forEach((student) => { students.forEach(student => {
this.submissionCellMap[student.id] = {}; this.submissionCellMap[student.id] = {}
this.submissionMap[student.id] = {}; this.submissionMap[student.id] = {}
_.each(assignments, (assignment) => { _.each(assignments, assignment => {
this.setSubmissionCellState(student, assignment, student[`assignment_${assignment.id}`]); this.setSubmissionCellState(student, assignment, student[`assignment_${assignment.id}`])
}); })
}); })
} }
setSubmissionCellState (student, assignment, submission) { setSubmissionCellState(student, assignment, submission) {
this.submissionMap[student.id][assignment.id] = submission || missingSubmission(student, assignment); this.submissionMap[student.id][assignment.id] =
submission || missingSubmission(student, assignment)
const params = [ const params = [
assignment, assignment,
student, student,
this.hasGradingPeriods, this.hasGradingPeriods,
this.selectedGradingPeriodID, this.selectedGradingPeriodID,
this.isAdmin this.isAdmin
]; ]
this.submissionCellMap[student.id][assignment.id] = cellMapForSubmission(...params); this.submissionCellMap[student.id][assignment.id] = cellMapForSubmission(...params)
} }
getSubmission (userId, assignmentId) { getSubmission(userId, assignmentId) {
return (this.submissionMap[userId] || {})[assignmentId]; return (this.submissionMap[userId] || {})[assignmentId]
} }
getSubmissionState ({ user_id: userId, assignment_id: assignmentId }) { getSubmissionState({user_id: userId, assignment_id: assignmentId}) {
return (this.submissionCellMap[userId] || {})[assignmentId]; return (this.submissionCellMap[userId] || {})[assignmentId]
} }
} }
export default SubmissionStateMap; export default SubmissionStateMap

View File

@ -21,21 +21,30 @@ import CurveGradesDialog from 'compiled/shared/CurveGradesDialog'
import I18n from 'i18n!gradebook' import I18n from 'i18n!gradebook'
import 'compiled/jquery.rails_flash_notifications' import 'compiled/jquery.rails_flash_notifications'
const CurveGradesDialogManager = { const CurveGradesDialogManager = {
createCurveGradesAction (assignment, students, {isAdmin, contextUrl, submissionsLoaded} = {}) { createCurveGradesAction(assignment, students, {isAdmin, contextUrl, submissionsLoaded} = {}) {
const { grading_type: gradingType, points_possible: pointsPossible } = assignment; const {grading_type: gradingType, points_possible: pointsPossible} = assignment
return { return {
isDisabled: !submissionsLoaded || gradingType === 'pass_fail' || pointsPossible == null || pointsPossible === 0, isDisabled:
!submissionsLoaded ||
gradingType === 'pass_fail' ||
pointsPossible == null ||
pointsPossible === 0,
onSelect (onClose) { // eslint-disable-line consistent-return onSelect(onClose) {
// eslint-disable-line consistent-return
if (!isAdmin && assignment.inClosedGradingPeriod) { if (!isAdmin && assignment.inClosedGradingPeriod) {
return $.flashError(I18n.t('Unable to curve grades because this assignment is due in a closed ' + return $.flashError(
'grading period for at least one student')); I18n.t(
'Unable to curve grades because this assignment is due in a closed ' +
'grading period for at least one student'
)
)
} }
const dialog = new CurveGradesDialog({assignment, students, context_url: contextUrl}); const dialog = new CurveGradesDialog({assignment, students, context_url: contextUrl})
dialog.show(onClose); dialog.show(onClose)
}
};
} }
} }
}
}
export default CurveGradesDialogManager export default CurveGradesDialogManager

View File

@ -77,7 +77,7 @@ export function createGradebook(options = {}) {
} }
export function setFixtureHtml($fixture) { export function setFixtureHtml($fixture) {
return $fixture.innerHTML = ` return ($fixture.innerHTML = `
<div id="application"> <div id="application">
<div id="wrapper"> <div id="wrapper">
<div data-component="GridColor"></div> <div data-component="GridColor"></div>
@ -98,7 +98,7 @@ export function setFixtureHtml($fixture) {
<div id="gradebook_grid"></div> <div id="gradebook_grid"></div>
</div> </div>
</div> </div>
` `)
} }
export function stubDataLoader() { export function stubDataLoader() {

View File

@ -16,34 +16,34 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import axios from 'axios'; import axios from 'axios'
import I18n from 'i18n!gradebook'; import I18n from 'i18n!gradebook'
import { underscore } from 'convert_case'; import {underscore} from 'convert_case'
function createTeacherNotesColumn (courseId) { function createTeacherNotesColumn(courseId) {
const url = `/api/v1/courses/${courseId}/custom_gradebook_columns`; const url = `/api/v1/courses/${courseId}/custom_gradebook_columns`
const data = { const data = {
column: { column: {
position: 1, position: 1,
teacher_notes: true, teacher_notes: true,
title: I18n.t('Notes') title: I18n.t('Notes')
} }
}; }
return axios.post(url, data); return axios.post(url, data)
} }
function updateTeacherNotesColumn (courseId, columnId, attr) { function updateTeacherNotesColumn(courseId, columnId, attr) {
const url = `/api/v1/courses/${courseId}/custom_gradebook_columns/${columnId}`; const url = `/api/v1/courses/${courseId}/custom_gradebook_columns/${columnId}`
return axios.put(url, { column: attr }); return axios.put(url, {column: attr})
} }
function updateSubmission (courseId, assignmentId, userId, submission) { function updateSubmission(courseId, assignmentId, userId, submission) {
const url = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`; const url = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`
return axios.put(url, { submission: underscore(submission), include: ['visibility'] }); return axios.put(url, {submission: underscore(submission), include: ['visibility']})
} }
export default { export default {
createTeacherNotesColumn, createTeacherNotesColumn,
updateTeacherNotesColumn, updateTeacherNotesColumn,
updateSubmission updateSubmission
}; }

View File

@ -16,8 +16,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import axios from 'axios'; import axios from 'axios'
import { camelize, underscore } from 'convert_case'; import {camelize, underscore} from 'convert_case'
export const DEFAULT_LATE_POLICY_DATA = Object.freeze({ export const DEFAULT_LATE_POLICY_DATA = Object.freeze({
lateSubmissionDeductionEnabled: false, lateSubmissionDeductionEnabled: false,
@ -28,40 +28,39 @@ export const DEFAULT_LATE_POLICY_DATA = Object.freeze({
missingSubmissionDeductionEnabled: false, missingSubmissionDeductionEnabled: false,
missingSubmissionDeduction: 0, missingSubmissionDeduction: 0,
newRecord: true newRecord: true
}); })
function camelizeLatePolicyResponseData (latePolicyResponseData) { function camelizeLatePolicyResponseData(latePolicyResponseData) {
const camelizedData = camelize(latePolicyResponseData.late_policy); const camelizedData = camelize(latePolicyResponseData.late_policy)
return { latePolicy: camelizedData }; return {latePolicy: camelizedData}
} }
export function fetchLatePolicy (courseId) { export function fetchLatePolicy(courseId) {
const url = `/api/v1/courses/${courseId}/late_policy`; const url = `/api/v1/courses/${courseId}/late_policy`
return axios.get(url) return axios
.then(response => ( .get(url)
{ data: camelizeLatePolicyResponseData(response.data) } .then(response => ({data: camelizeLatePolicyResponseData(response.data)}))
)) .catch(error => {
.catch((error) => {
// if we get a 404 then we know the course does not // if we get a 404 then we know the course does not
// currently have a late policy set up // currently have a late policy set up
if (error.response && error.response.status === 404) { if (error.response && error.response.status === 404) {
return Promise.resolve({ data: { latePolicy: DEFAULT_LATE_POLICY_DATA } }); return Promise.resolve({data: {latePolicy: DEFAULT_LATE_POLICY_DATA}})
} else { } else {
return Promise.reject(error); return Promise.reject(error)
} }
}); })
} }
export function createLatePolicy (courseId, latePolicyData) { export function createLatePolicy(courseId, latePolicyData) {
const url = `/api/v1/courses/${courseId}/late_policy`; const url = `/api/v1/courses/${courseId}/late_policy`
const data = { late_policy: underscore(latePolicyData) }; const data = {late_policy: underscore(latePolicyData)}
return axios.post(url, data).then(response => ( return axios
{ data: camelizeLatePolicyResponseData(response.data) } .post(url, data)
)); .then(response => ({data: camelizeLatePolicyResponseData(response.data)}))
} }
export function updateLatePolicy (courseId, latePolicyData) { export function updateLatePolicy(courseId, latePolicyData) {
const url = `/api/v1/courses/${courseId}/late_policy`; const url = `/api/v1/courses/${courseId}/late_policy`
const data = { late_policy: underscore(latePolicyData) }; const data = {late_policy: underscore(latePolicyData)}
return axios.patch(url, data); return axios.patch(url, data)
} }

View File

@ -16,19 +16,19 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import axios from 'axios'; import axios from 'axios'
import timezone from 'timezone'; import timezone from 'timezone'
function deserializeComment (comment) { function deserializeComment(comment) {
const baseComment = { const baseComment = {
id: comment.id, id: comment.id,
createdAt: timezone.parse(comment.created_at), createdAt: timezone.parse(comment.created_at),
comment: comment.comment, comment: comment.comment,
editedAt: comment.edited_at && timezone.parse(comment.edited_at) editedAt: comment.edited_at && timezone.parse(comment.edited_at)
}; }
if (!comment.author) { if (!comment.author) {
return baseComment; return baseComment
} }
return { return {
@ -37,36 +37,38 @@ function deserializeComment (comment) {
author: comment.author.display_name, author: comment.author.display_name,
authorAvatarUrl: comment.author.avatar_image_url, authorAvatarUrl: comment.author.avatar_image_url,
authorUrl: comment.author.html_url authorUrl: comment.author.html_url
}; }
} }
function deserializeComments (comments) { function deserializeComments(comments) {
return comments.map(deserializeComment); return comments.map(deserializeComment)
} }
export function getSubmissionComments (courseId, assignmentId, studentId) { export function getSubmissionComments(courseId, assignmentId, studentId) {
const commentOptions = { params: { include: 'submission_comments' } }; const commentOptions = {params: {include: 'submission_comments'}}
const url = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${studentId}`; const url = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${studentId}`
return axios.get(url, commentOptions) return axios
.then(response => deserializeComments(response.data.submission_comments)); .get(url, commentOptions)
.then(response => deserializeComments(response.data.submission_comments))
} }
export function createSubmissionComment (courseId, assignmentId, studentId, comment) { export function createSubmissionComment(courseId, assignmentId, studentId, comment) {
const url = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${studentId}`; const url = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${studentId}`
const data = { group_comment: 0, comment: { text_comment: comment } }; const data = {group_comment: 0, comment: {text_comment: comment}}
return axios.put(url, data) return axios
.then(response => deserializeComments(response.data.submission_comments)); .put(url, data)
.then(response => deserializeComments(response.data.submission_comments))
} }
export function deleteSubmissionComment (commentId) { export function deleteSubmissionComment(commentId) {
const url = `/submission_comments/${commentId}`; const url = `/submission_comments/${commentId}`
return axios.delete(url); return axios.delete(url)
} }
export function updateSubmissionComment (commentId, comment) { export function updateSubmissionComment(commentId, comment) {
const url = `/submission_comments/${commentId}`; const url = `/submission_comments/${commentId}`
const data = { id: commentId, submission_comment: { comment } }; const data = {id: commentId, submission_comment: {comment}}
return axios.put(url, data).then(response => ( return axios
{ data: deserializeComment(response.data.submission_comment) } .put(url, data)
)); .then(response => ({data: deserializeComment(response.data.submission_comment)}))
} }

View File

@ -16,15 +16,15 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import I18n from 'i18n!gradebook'; import I18n from 'i18n!gradebook'
export const filterLabels = { export const filterLabels = {
assignmentGroups: I18n.t('Assignment Groups'), assignmentGroups: I18n.t('Assignment Groups'),
gradingPeriods: I18n.t('Grading Periods'), gradingPeriods: I18n.t('Grading Periods'),
modules: I18n.t('Modules'), modules: I18n.t('Modules'),
sections: I18n.t('Sections') sections: I18n.t('Sections')
}; }
export default { export default {
filterLabels filterLabels
}; }

View File

@ -16,7 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import Color from 'tinycolor2'; import Color from 'tinycolor2'
export const defaultColors = { export const defaultColors = {
salmon: '#FFE8E5', salmon: '#FFE8E5',
@ -29,7 +29,7 @@ export const defaultColors = {
pink: '#F8EAF6', pink: '#F8EAF6',
lavender: '#F0E8EF', lavender: '#F0E8EF',
white: '#FFFFFF' white: '#FFFFFF'
}; }
const defaultStatusColors = { const defaultStatusColors = {
dropped: defaultColors.orange, dropped: defaultColors.orange,
@ -37,15 +37,15 @@ const defaultStatusColors = {
late: defaultColors.blue, late: defaultColors.blue,
missing: defaultColors.salmon, missing: defaultColors.salmon,
resubmitted: defaultColors.green resubmitted: defaultColors.green
}; }
export function statusColors (userColors = {}) { export function statusColors(userColors = {}) {
return { return {
...defaultStatusColors, ...defaultStatusColors,
...userColors ...userColors
}; }
} }
export function darken (color, percent) { export function darken(color, percent) {
return Color(color).darken(percent); return Color(color).darken(percent)
} }

View File

@ -16,15 +16,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import I18n from 'i18n!gradebook'; import I18n from 'i18n!gradebook'
export const statuses = [ export const statuses = ['late', 'missing', 'resubmitted', 'dropped', 'excused']
'late',
'missing',
'resubmitted',
'dropped',
'excused'
];
export const statusesTitleMap = { export const statusesTitleMap = {
late: I18n.t('Late'), late: I18n.t('Late'),
@ -32,5 +26,4 @@ export const statusesTitleMap = {
resubmitted: I18n.t('Resubmitted'), resubmitted: I18n.t('Resubmitted'),
dropped: I18n.t('Dropped'), dropped: I18n.t('Dropped'),
excused: I18n.t('Excused') excused: I18n.t('Excused')
}; }

View File

@ -16,15 +16,15 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import I18n from 'i18n!gradebook'; import I18n from 'i18n!gradebook'
const primaryInfoLabels = { const primaryInfoLabels = {
first_last: I18n.t('First, Last Name'), first_last: I18n.t('First, Last Name'),
last_first: I18n.t('Last, First Name'), last_first: I18n.t('Last, First Name')
}; }
const primaryInfoKeys = ['first_last', 'last_first']; const primaryInfoKeys = ['first_last', 'last_first']
const defaultPrimaryInfo = 'first_last'; const defaultPrimaryInfo = 'first_last'
const secondaryInfoLabels = { const secondaryInfoLabels = {
section: I18n.t('Section'), section: I18n.t('Section'),
@ -32,18 +32,18 @@ const secondaryInfoLabels = {
integration_id: I18n.t('Integration ID'), integration_id: I18n.t('Integration ID'),
login_id: I18n.t('Login ID'), login_id: I18n.t('Login ID'),
none: I18n.t('None') none: I18n.t('None')
}; }
const secondaryInfoKeys = ['section', 'sis_id', 'integration_id', 'login_id', 'none']; const secondaryInfoKeys = ['section', 'sis_id', 'integration_id', 'login_id', 'none']
const defaultSecondaryInfo = 'none'; const defaultSecondaryInfo = 'none'
const sectionSecondaryInfo = 'section'; const sectionSecondaryInfo = 'section'
const enrollmentFilterLabels = { const enrollmentFilterLabels = {
inactive: I18n.t('Inactive enrollments'), inactive: I18n.t('Inactive enrollments'),
concluded: I18n.t('Concluded enrollments') concluded: I18n.t('Concluded enrollments')
}; }
const enrollmentFilterKeys = ['inactive', 'concluded']; const enrollmentFilterKeys = ['inactive', 'concluded']
export default { export default {
primaryInfoKeys, primaryInfoKeys,
@ -55,4 +55,4 @@ export default {
sectionSecondaryInfo, sectionSecondaryInfo,
enrollmentFilterKeys, enrollmentFilterKeys,
enrollmentFilterLabels enrollmentFilterLabels
}; }

View File

@ -16,10 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { import {instanceOf, string} from 'prop-types'
instanceOf,
string
} from 'prop-types';
const SubmissionTrayCommentPropTypes = { const SubmissionTrayCommentPropTypes = {
id: string.isRequired, id: string.isRequired,
@ -29,6 +26,6 @@ const SubmissionTrayCommentPropTypes = {
createdAt: instanceOf(Date).isRequired, createdAt: instanceOf(Date).isRequired,
comment: string.isRequired, comment: string.isRequired,
editedAt: instanceOf(Date) editedAt: instanceOf(Date)
}; }
export default SubmissionTrayCommentPropTypes; export default SubmissionTrayCommentPropTypes

View File

@ -16,9 +16,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import _ from 'lodash'; import _ from 'lodash'
function createStudentPlaceholder (id) { function createStudentPlaceholder(id) {
return { return {
enrollments: [], enrollments: [],
id, id,
@ -27,48 +27,48 @@ function createStudentPlaceholder (id) {
isPlaceholder: true, isPlaceholder: true,
loaded: false, loaded: false,
sections: [] sections: []
}; }
} }
export default class StudentDatastore { export default class StudentDatastore {
studentIds = []; studentIds = []
constructor (userStudentMap, testStudentMap) { constructor(userStudentMap, testStudentMap) {
this.userStudentMap = userStudentMap; this.userStudentMap = userStudentMap
this.testStudentMap = testStudentMap; this.testStudentMap = testStudentMap
} }
listStudentIds () { listStudentIds() {
return this.studentIds; return this.studentIds
} }
setStudentIds (studentIds) { setStudentIds(studentIds) {
this.studentIds = studentIds; this.studentIds = studentIds
const idsOfStoredStudents = Object.keys(this.userStudentMap); const idsOfStoredStudents = Object.keys(this.userStudentMap)
_.difference(idsOfStoredStudents, studentIds).forEach((removedStudentId) => { _.difference(idsOfStoredStudents, studentIds).forEach(removedStudentId => {
delete this.userStudentMap[removedStudentId]; delete this.userStudentMap[removedStudentId]
}); })
const idsOfStoredTestStudents = Object.keys(this.testStudentMap); const idsOfStoredTestStudents = Object.keys(this.testStudentMap)
_.difference(idsOfStoredTestStudents, studentIds).forEach((removedStudentId) => { _.difference(idsOfStoredTestStudents, studentIds).forEach(removedStudentId => {
delete this.testStudentMap[removedStudentId]; delete this.testStudentMap[removedStudentId]
}); })
} }
addUserStudents (students) { addUserStudents(students) {
students.forEach((student) => { students.forEach(student => {
this.userStudentMap[student.id] = student; this.userStudentMap[student.id] = student
}); })
} }
addTestStudents (students) { addTestStudents(students) {
students.forEach((student) => { students.forEach(student => {
this.testStudentMap[student.id] = student; this.testStudentMap[student.id] = student
}); })
} }
listStudents () { listStudents() {
return this.studentIds.map(id => ( return this.studentIds.map(
this.userStudentMap[id] || this.testStudentMap[id] || createStudentPlaceholder(id) id => this.userStudentMap[id] || this.testStudentMap[id] || createStudentPlaceholder(id)
)); )
} }
} }

View File

@ -20,25 +20,27 @@ import INST from 'INST'
import $ from 'jquery' import $ from 'jquery'
import 'jquery.instructure_misc_helpers' import 'jquery.instructure_misc_helpers'
class DownloadSubmissionsDialogManager { class DownloadSubmissionsDialogManager {
constructor (assignment, downloadUrlTemplate, submissionsDownloading) { constructor(assignment, downloadUrlTemplate, submissionsDownloading) {
this.assignment = assignment; this.assignment = assignment
this.downloadUrl = $.replaceTags(downloadUrlTemplate, 'assignment_id', assignment.id); this.downloadUrl = $.replaceTags(downloadUrlTemplate, 'assignment_id', assignment.id)
this.showDialog = this.showDialog.bind(this); this.showDialog = this.showDialog.bind(this)
this.validSubmissionTypes = ['online_upload', 'online_text_entry', 'online_url']; this.validSubmissionTypes = ['online_upload', 'online_text_entry', 'online_url']
this.submissionsDownloading = submissionsDownloading; this.submissionsDownloading = submissionsDownloading
} }
isDialogEnabled () { isDialogEnabled() {
return this.assignment.submission_types && this.assignment.submission_types.some( return (
t => this.validSubmissionTypes.includes(t) this.assignment.submission_types &&
) && this.assignment.has_submitted_submissions; this.assignment.submission_types.some(t => this.validSubmissionTypes.includes(t)) &&
this.assignment.has_submitted_submissions
)
} }
showDialog (cb) { showDialog(cb) {
this.submissionsDownloading(this.assignment.id); this.submissionsDownloading(this.assignment.id)
INST.downloadSubmissions(this.downloadUrl, cb); INST.downloadSubmissions(this.downloadUrl, cb)
}
} }
}
export default DownloadSubmissionsDialogManager export default DownloadSubmissionsDialogManager

View File

@ -22,7 +22,7 @@ const gradingTypeOptionMap = {
pass_fail: ['passFail'], pass_fail: ['passFail'],
percent: ['points', 'percent'], percent: ['points', 'percent'],
points: ['points', 'percent'] points: ['points', 'percent']
}; }
const gradingTypeDefaultOptionMap = { const gradingTypeDefaultOptionMap = {
gpa_scale: 'gradingScheme', gpa_scale: 'gradingScheme',
@ -30,12 +30,12 @@ const gradingTypeDefaultOptionMap = {
pass_fail: 'passFail', pass_fail: 'passFail',
percent: 'percent', percent: 'percent',
points: 'points' points: 'points'
};
export function defaultOptionForGradingType (gradingType) {
return gradingTypeDefaultOptionMap[gradingType] || null;
} }
export function optionsForGradingType (gradingType) { export function defaultOptionForGradingType(gradingType) {
return gradingTypeOptionMap[gradingType] || []; return gradingTypeDefaultOptionMap[gradingType] || null
}
export function optionsForGradingType(gradingType) {
return gradingTypeOptionMap[gradingType] || []
} }

View File

@ -19,117 +19,127 @@
import axios from 'axios' import axios from 'axios'
import I18n from 'i18n!gradebook' import I18n from 'i18n!gradebook'
class GradebookExportManager { class GradebookExportManager {
static DEFAULT_POLLING_INTERVAL = 2000; static DEFAULT_POLLING_INTERVAL = 2000
static DEFAULT_MONITORING_BASE_URL = '/api/v1/progress'; static DEFAULT_MONITORING_BASE_URL = '/api/v1/progress'
static DEFAULT_ATTACHMENT_BASE_URL = '/api/v1/users'; static DEFAULT_ATTACHMENT_BASE_URL = '/api/v1/users'
static exportCompleted (workflowState) { static exportCompleted(workflowState) {
return workflowState === 'completed'; return workflowState === 'completed'
} }
// Returns false if the workflowState is 'failed' or an unknown state // Returns false if the workflowState is 'failed' or an unknown state
static exportFailed (workflowState) { static exportFailed(workflowState) {
if (workflowState === 'failed') return true; if (workflowState === 'failed') return true
return !['completed', 'queued', 'running'].includes(workflowState); return !['completed', 'queued', 'running'].includes(workflowState)
} }
constructor (exportingUrl, currentUserId, existingExport, pollingInterval = GradebookExportManager.DEFAULT_POLLING_INTERVAL) { constructor(
this.pollingInterval = pollingInterval; exportingUrl,
currentUserId,
existingExport,
pollingInterval = GradebookExportManager.DEFAULT_POLLING_INTERVAL
) {
this.pollingInterval = pollingInterval
this.exportingUrl = exportingUrl; this.exportingUrl = exportingUrl
this.monitoringBaseUrl = GradebookExportManager.DEFAULT_MONITORING_BASE_URL; this.monitoringBaseUrl = GradebookExportManager.DEFAULT_MONITORING_BASE_URL
this.attachmentBaseUrl = `${GradebookExportManager.DEFAULT_ATTACHMENT_BASE_URL}/${currentUserId}/files`; this.attachmentBaseUrl = `${
this.currentUserId = currentUserId; GradebookExportManager.DEFAULT_ATTACHMENT_BASE_URL
}/${currentUserId}/files`
this.currentUserId = currentUserId
if (existingExport) { if (existingExport) {
const workflowState = existingExport.workflowState; const workflowState = existingExport.workflowState
if (workflowState !== 'completed' && workflowState !== 'failed') { if (workflowState !== 'completed' && workflowState !== 'failed') {
this.export = existingExport; this.export = existingExport
} }
} }
} }
monitoringUrl () { monitoringUrl() {
if (!(this.export && this.export.progressId)) return undefined; if (!(this.export && this.export.progressId)) return undefined
return `${this.monitoringBaseUrl}/${this.export.progressId}`; return `${this.monitoringBaseUrl}/${this.export.progressId}`
} }
attachmentUrl () { attachmentUrl() {
if (!(this.attachmentBaseUrl && this.export && this.export.attachmentId)) return undefined; if (!(this.attachmentBaseUrl && this.export && this.export.attachmentId)) return undefined
return `${this.attachmentBaseUrl}/${this.export.attachmentId}`; return `${this.attachmentBaseUrl}/${this.export.attachmentId}`
} }
clearMonitor () { clearMonitor() {
if (this.exportStatusPoll) { if (this.exportStatusPoll) {
window.clearInterval(this.exportStatusPoll); window.clearInterval(this.exportStatusPoll)
this.exportStatusPoll = null; this.exportStatusPoll = null
} }
} }
monitorExport (resolve, reject) { monitorExport(resolve, reject) {
if (!this.monitoringUrl()) { if (!this.monitoringUrl()) {
this.export = undefined; this.export = undefined
reject(I18n.t('No way to monitor gradebook exports provided!')); reject(I18n.t('No way to monitor gradebook exports provided!'))
} }
this.exportStatusPoll = window.setInterval(() => { this.exportStatusPoll = window.setInterval(() => {
axios.get(this.monitoringUrl()).then((response) => { axios.get(this.monitoringUrl()).then(response => {
const workflowState = response.data.workflow_state; const workflowState = response.data.workflow_state
if (GradebookExportManager.exportCompleted(workflowState)) { if (GradebookExportManager.exportCompleted(workflowState)) {
this.clearMonitor(); this.clearMonitor()
// Export is complete => let's get the attachment url // Export is complete => let's get the attachment url
axios.get(this.attachmentUrl()).then((attachmentResponse) => { axios
.get(this.attachmentUrl())
.then(attachmentResponse => {
const resolution = { const resolution = {
attachmentUrl: attachmentResponse.data.url, attachmentUrl: attachmentResponse.data.url,
updatedAt: attachmentResponse.data.updated_at updatedAt: attachmentResponse.data.updated_at
}; }
this.export = undefined; this.export = undefined
resolve(resolution); resolve(resolution)
}).catch((error) => { })
reject(error); .catch(error => {
}); reject(error)
})
} else if (GradebookExportManager.exportFailed(workflowState)) { } else if (GradebookExportManager.exportFailed(workflowState)) {
this.clearMonitor(); this.clearMonitor()
reject(I18n.t('Error exporting gradebook: %{msg}', { msg: response.data.message })); reject(I18n.t('Error exporting gradebook: %{msg}', {msg: response.data.message}))
} }
}); })
}, this.pollingInterval); }, this.pollingInterval)
} }
startExport (gradingPeriodId) { startExport(gradingPeriodId) {
if (!this.exportingUrl) { if (!this.exportingUrl) {
return Promise.reject(I18n.t('No way to export gradebooks provided!')); return Promise.reject(I18n.t('No way to export gradebooks provided!'))
} }
if (this.export) { if (this.export) {
// We already have an ongoing export, ignoring this call to start a new one // We already have an ongoing export, ignoring this call to start a new one
return Promise.reject(I18n.t('An export is already in progress.')); return Promise.reject(I18n.t('An export is already in progress.'))
} }
const params = { const params = {
grading_period_id: gradingPeriodId grading_period_id: gradingPeriodId
}; }
return axios.get(this.exportingUrl, { params }).then((response) => { return axios.get(this.exportingUrl, {params}).then(response => {
this.export = { this.export = {
progressId: response.data.progress_id, progressId: response.data.progress_id,
attachmentId: response.data.attachment_id attachmentId: response.data.attachment_id
}; }
return new Promise(this.monitorExport.bind(this)); return new Promise(this.monitorExport.bind(this))
}); })
}
} }
}
export default GradebookExportManager export default GradebookExportManager

View File

@ -22,57 +22,57 @@ import I18n from 'i18n!gradezilla'
import $ from 'jquery' import $ from 'jquery'
import 'jquery.instructure_misc_helpers' import 'jquery.instructure_misc_helpers'
class ReuploadSubmissionsDialogManager { class ReuploadSubmissionsDialogManager {
constructor (assignment, reuploadUrlTemplate) { constructor(assignment, reuploadUrlTemplate) {
this.assignment = assignment; this.assignment = assignment
this.reuploadUrl = $.replaceTags(reuploadUrlTemplate, 'assignment_id', assignment.id); this.reuploadUrl = $.replaceTags(reuploadUrlTemplate, 'assignment_id', assignment.id)
this.showDialog = this.showDialog.bind(this); this.showDialog = this.showDialog.bind(this)
} }
isDialogEnabled () { isDialogEnabled() {
return this.assignment.hasDownloadedSubmissions; return this.assignment.hasDownloadedSubmissions
} }
getReuploadForm (cb) { getReuploadForm(cb) {
if (ReuploadSubmissionsDialogManager.reuploadForm) { if (ReuploadSubmissionsDialogManager.reuploadForm) {
return ReuploadSubmissionsDialogManager.reuploadForm; return ReuploadSubmissionsDialogManager.reuploadForm
} }
ReuploadSubmissionsDialogManager.reuploadForm = $( ReuploadSubmissionsDialogManager.reuploadForm = $(
re_upload_submissions_form({ authenticityToken: authenticity_token() }) re_upload_submissions_form({authenticityToken: authenticity_token()})
).dialog( )
{ .dialog({
width: 400, width: 400,
modal: true, modal: true,
resizable: false, resizable: false,
autoOpen: false, autoOpen: false,
close: () => { close: () => {
if (typeof cb === 'function') { if (typeof cb === 'function') {
cb(); cb()
} }
} }
} })
).submit(function () { .submit(function() {
const data = $(this).getFormData(); const data = $(this).getFormData()
let submitForm = true; let submitForm = true
if (!data.submissions_zip) { if (!data.submissions_zip) {
submitForm = false; submitForm = false
} else if (!data.submissions_zip.match(/\.zip$/)) { } else if (!data.submissions_zip.match(/\.zip$/)) {
$(this).formErrors({ submissions_zip: I18n.t('Please upload files as a .zip') }); $(this).formErrors({submissions_zip: I18n.t('Please upload files as a .zip')})
submitForm = false; submitForm = false
} }
return submitForm; return submitForm
}); })
return ReuploadSubmissionsDialogManager.reuploadForm; return ReuploadSubmissionsDialogManager.reuploadForm
} }
showDialog (cb) { showDialog(cb) {
const form = this.getReuploadForm(cb); const form = this.getReuploadForm(cb)
form.attr('action', this.reuploadUrl).dialog('open'); form.attr('action', this.reuploadUrl).dialog('open')
}
} }
}
export default ReuploadSubmissionsDialogManager export default ReuploadSubmissionsDialogManager

View File

@ -21,41 +21,52 @@ import I18n from 'i18n!gradebook'
import SetDefaultGradeDialog from 'compiled/gradezilla/SetDefaultGradeDialog' import SetDefaultGradeDialog from 'compiled/gradezilla/SetDefaultGradeDialog'
import 'compiled/jquery.rails_flash_notifications' import 'compiled/jquery.rails_flash_notifications'
class SetDefaultGradeDialogManager { class SetDefaultGradeDialogManager {
constructor (assignment, students, contextId, selectedSection, isAdmin = false, submissionsLoaded = false) { constructor(
this.assignment = assignment; assignment,
this.students = students; students,
this.contextId = contextId; contextId,
this.selectedSection = selectedSection; selectedSection,
this.isAdmin = isAdmin; isAdmin = false,
this.submissionsLoaded = submissionsLoaded; submissionsLoaded = false
) {
this.assignment = assignment
this.students = students
this.contextId = contextId
this.selectedSection = selectedSection
this.isAdmin = isAdmin
this.submissionsLoaded = submissionsLoaded
this.showDialog = this.showDialog.bind(this); this.showDialog = this.showDialog.bind(this)
} }
getSetDefaultGradeDialogOptions () { getSetDefaultGradeDialogOptions() {
return { return {
assignment: this.assignment, assignment: this.assignment,
students: this.students, students: this.students,
context_id: this.contextId, context_id: this.contextId,
selected_section: this.selectedSection, selected_section: this.selectedSection
}; }
} }
showDialog (cb) { showDialog(cb) {
if (this.isAdmin || !this.assignment.inClosedGradingPeriod) { if (this.isAdmin || !this.assignment.inClosedGradingPeriod) {
const dialog = new SetDefaultGradeDialog(this.getSetDefaultGradeDialogOptions()); const dialog = new SetDefaultGradeDialog(this.getSetDefaultGradeDialogOptions())
dialog.show(cb); dialog.show(cb)
} else { } else {
$.flashError(I18n.t('Unable to set default grade because this ' + $.flashError(
'assignment is due in a closed grading period for at least one student')); I18n.t(
'Unable to set default grade because this ' +
'assignment is due in a closed grading period for at least one student'
)
)
} }
} }
isDialogEnabled () { isDialogEnabled() {
return this.submissionsLoaded; return this.submissionsLoaded
}
} }
}
export default SetDefaultGradeDialogManager export default SetDefaultGradeDialogManager

View File

@ -16,14 +16,16 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import $ from 'jquery'; import $ from 'jquery'
export function getWidth (text) { export function getWidth(text) {
let $textMeasure = $('#text-measure'); let $textMeasure = $('#text-measure')
if (!$textMeasure.length) { if (!$textMeasure.length) {
$textMeasure = $('<span id="text-measure" style="padding: 10px; display: none;" />').appendTo('#content'); $textMeasure = $('<span id="text-measure" style="padding: 10px; display: none;" />').appendTo(
'#content'
)
} }
return $textMeasure.text(text).outerWidth(); return $textMeasure.text(text).outerWidth()
} }
export default { export default {

View File

@ -17,71 +17,83 @@
*/ */
import _ from 'underscore' import _ from 'underscore'
function uniqueEffectiveDueDates(assignment) { function uniqueEffectiveDueDates(assignment) {
const dueDates = _.map(assignment.effectiveDueDates, function(dueDateInfo) { const dueDates = _.map(assignment.effectiveDueDates, function(dueDateInfo) {
const dueAt = dueDateInfo.due_at; const dueAt = dueDateInfo.due_at
return dueAt ? new Date(dueAt) : dueAt; return dueAt ? new Date(dueAt) : dueAt
}); })
return _.uniq(dueDates, date => date ? date.toString() : date); return _.uniq(dueDates, date => (date ? date.toString() : date))
} }
function getDueDateFromAssignment(assignment) { function getDueDateFromAssignment(assignment) {
if (assignment.due_at) { if (assignment.due_at) {
return new Date(assignment.due_at); return new Date(assignment.due_at)
} }
const dueDates = uniqueEffectiveDueDates(assignment); const dueDates = uniqueEffectiveDueDates(assignment)
return dueDates.length === 1 ? dueDates[0] : null; return dueDates.length === 1 ? dueDates[0] : null
} }
const assignmentHelper = { const assignmentHelper = {
compareByDueDate (a, b) { compareByDueDate(a, b) {
let aDate = getDueDateFromAssignment(a); let aDate = getDueDateFromAssignment(a)
let bDate = getDueDateFromAssignment(b); let bDate = getDueDateFromAssignment(b)
const aDateIsNull = _.isNull(aDate); const aDateIsNull = _.isNull(aDate)
const bDateIsNull = _.isNull(bDate); const bDateIsNull = _.isNull(bDate)
if (aDateIsNull && !bDateIsNull) { return 1 } if (aDateIsNull && !bDateIsNull) {
if (!aDateIsNull && bDateIsNull) { return -1 } return 1
}
if (!aDateIsNull && bDateIsNull) {
return -1
}
if (aDateIsNull && bDateIsNull) { if (aDateIsNull && bDateIsNull) {
const aHasMultipleDates = this.hasMultipleDueDates(a); const aHasMultipleDates = this.hasMultipleDueDates(a)
const bHasMultipleDates = this.hasMultipleDueDates(b); const bHasMultipleDates = this.hasMultipleDueDates(b)
if (aHasMultipleDates && !bHasMultipleDates) { return -1 } if (aHasMultipleDates && !bHasMultipleDates) {
if (!aHasMultipleDates && bHasMultipleDates) { return 1 } return -1
} }
aDate = +aDate; if (!aHasMultipleDates && bHasMultipleDates) {
bDate = +bDate; return 1
}
}
aDate = +aDate
bDate = +bDate
if (aDate === bDate) { if (aDate === bDate) {
const aName = a.name.toLowerCase(); const aName = a.name.toLowerCase()
const bName = b.name.toLowerCase(); const bName = b.name.toLowerCase()
if (aName === bName) { return 0 } if (aName === bName) {
return aName > bName ? 1 : -1; return 0
} }
return aDate - bDate; return aName > bName ? 1 : -1
}
return aDate - bDate
}, },
hasMultipleDueDates (assignment) { hasMultipleDueDates(assignment) {
return uniqueEffectiveDueDates(assignment).length > 1; return uniqueEffectiveDueDates(assignment).length > 1
}, },
getComparator (arrangeBy) { getComparator(arrangeBy) {
if (arrangeBy === 'due_date') { if (arrangeBy === 'due_date') {
return this.compareByDueDate.bind(this); return this.compareByDueDate.bind(this)
} }
if (arrangeBy === 'assignment_group') { if (arrangeBy === 'assignment_group') {
return this.compareByAssignmentGroup.bind(this); return this.compareByAssignmentGroup.bind(this)
} }
}, },
compareByAssignmentGroup (a, b) { compareByAssignmentGroup(a, b) {
const diffOfAssignmentGroupPosition = a.assignment_group_position - b.assignment_group_position; const diffOfAssignmentGroupPosition = a.assignment_group_position - b.assignment_group_position
if (diffOfAssignmentGroupPosition === 0) { if (diffOfAssignmentGroupPosition === 0) {
const diffOfAssignmentPosition = a.position - b.position; const diffOfAssignmentPosition = a.position - b.position
if (diffOfAssignmentPosition === 0) { return 0 } if (diffOfAssignmentPosition === 0) {
return diffOfAssignmentPosition; return 0
} }
return diffOfAssignmentGroupPosition; return diffOfAssignmentPosition
} }
}; return diffOfAssignmentGroupPosition
}
}
export default assignmentHelper export default assignmentHelper

View File

@ -19,26 +19,26 @@
import _ from 'underscore' import _ from 'underscore'
import I18n from 'i18n!gradebook' import I18n from 'i18n!gradebook'
function hasSubmitted (submission) { function hasSubmitted(submission) {
if (submission.excused) { if (submission.excused) {
return true; return true
} else if (submission.latePolicyStatus) { } else if (submission.latePolicyStatus) {
return submission.latePolicyStatus !== 'missing'; return submission.latePolicyStatus !== 'missing'
} }
return !!(submission.submittedAt || submission.submitted_at); return !!(submission.submittedAt || submission.submitted_at)
} }
function getSubmissionTypes (assignment) { function getSubmissionTypes(assignment) {
return (assignment.submissionTypes || assignment.submission_types); return assignment.submissionTypes || assignment.submission_types
} }
function getCourseId (assignment) { function getCourseId(assignment) {
return (assignment.courseId || assignment.course_id); return assignment.courseId || assignment.course_id
} }
const MessageStudentsWhoHelper = { const MessageStudentsWhoHelper = {
settings (assignment, students) { settings(assignment, students) {
return { return {
options: this.options(assignment), options: this.options(assignment),
title: assignment.name, title: assignment.name,
@ -50,85 +50,88 @@ import I18n from 'i18n!gradebook'
} }
}, },
options (assignment) { options(assignment) {
const options = this.allOptions(); const options = this.allOptions()
const noSubmissions = !this.hasSubmission(assignment); const noSubmissions = !this.hasSubmission(assignment)
if (noSubmissions) options.splice(0, 1); if (noSubmissions) options.splice(0, 1)
return options; return options
}, },
allOptions () { allOptions() {
return [ return [
{ {
text: I18n.t("Haven't submitted yet"), text: I18n.t("Haven't submitted yet"),
subjectFn: assignment => I18n.t( subjectFn: assignment =>
'No submission for %{assignment}', I18n.t('No submission for %{assignment}', {assignment: assignment.name}),
{ assignment: assignment.name }
),
criteriaFn: student => !hasSubmitted(student) criteriaFn: student => !hasSubmitted(student)
}, },
{ {
text: I18n.t("Haven't been graded"), text: I18n.t("Haven't been graded"),
subjectFn: assignment => I18n.t( subjectFn: assignment =>
'No grade for %{assignment}', I18n.t('No grade for %{assignment}', {assignment: assignment.name}),
{ assignment: assignment.name }
),
criteriaFn: student => !this.exists(student.score) criteriaFn: student => !this.exists(student.score)
}, },
{ {
text: I18n.t('Scored less than'), text: I18n.t('Scored less than'),
cutoff: true, cutoff: true,
subjectFn: (assignment, cutoff) => I18n.t( subjectFn: (assignment, cutoff) =>
'Scored less than %{cutoff} on %{assignment}', I18n.t('Scored less than %{cutoff} on %{assignment}', {
{ assignment: assignment.name, cutoff: I18n.n(cutoff) } assignment: assignment.name,
), cutoff: I18n.n(cutoff)
criteriaFn: (student, cutoff) => this.scoreWithCutoff(student, cutoff) && student.score < cutoff }),
criteriaFn: (student, cutoff) =>
this.scoreWithCutoff(student, cutoff) && student.score < cutoff
}, },
{ {
text: I18n.t('Scored more than'), text: I18n.t('Scored more than'),
cutoff: true, cutoff: true,
subjectFn: (assignment, cutoff) => I18n.t( subjectFn: (assignment, cutoff) =>
'Scored more than %{cutoff} on %{assignment}', I18n.t('Scored more than %{cutoff} on %{assignment}', {
{ assignment: assignment.name, cutoff: I18n.n(cutoff) } assignment: assignment.name,
), cutoff: I18n.n(cutoff)
criteriaFn: (student, cutoff) => this.scoreWithCutoff(student, cutoff) && student.score > cutoff }),
criteriaFn: (student, cutoff) =>
this.scoreWithCutoff(student, cutoff) && student.score > cutoff
} }
]; ]
}, },
hasSubmission (assignment) { hasSubmission(assignment) {
const submissionTypes = getSubmissionTypes(assignment); const submissionTypes = getSubmissionTypes(assignment)
if (submissionTypes.length === 0) return false; if (submissionTypes.length === 0) return false
return _.any(submissionTypes, submissionType => submissionType !== 'none' && submissionType !== 'on_paper'); return _.any(
submissionTypes,
submissionType => submissionType !== 'none' && submissionType !== 'on_paper'
)
}, },
exists (value) { exists(value) {
return !_.isUndefined(value) && !_.isNull(value); return !_.isUndefined(value) && !_.isNull(value)
}, },
scoreWithCutoff (student, cutoff) { scoreWithCutoff(student, cutoff) {
return this.exists(student.score) return this.exists(student.score) && student.score !== '' && this.exists(cutoff)
&& student.score !== ''
&& this.exists(cutoff);
}, },
callbackFn (selected, cutoff, students) { callbackFn(selected, cutoff, students) {
const criteriaFn = this.findOptionByText(selected).criteriaFn; const criteriaFn = this.findOptionByText(selected).criteriaFn
const studentsMatchingCriteria = _.filter(students, student => criteriaFn(student.user_data, cutoff)); const studentsMatchingCriteria = _.filter(students, student =>
return _.map(studentsMatchingCriteria, student => student.user_data.id); criteriaFn(student.user_data, cutoff)
)
return _.map(studentsMatchingCriteria, student => student.user_data.id)
}, },
findOptionByText (text) { findOptionByText(text) {
return _.find(this.allOptions(), option => option.text === text); return _.find(this.allOptions(), option => option.text === text)
}, },
generateSubjectCallbackFn (assignment) { generateSubjectCallbackFn(assignment) {
return (selected, cutoff) => { return (selected, cutoff) => {
const cutoffString = cutoff || ''; const cutoffString = cutoff || ''
const subjectFn = this.findOptionByText(selected).subjectFn; const subjectFn = this.findOptionByText(selected).subjectFn
return subjectFn(assignment, cutoffString); return subjectFn(assignment, cutoffString)
} }
} }
}; }
export default MessageStudentsWhoHelper export default MessageStudentsWhoHelper

View File

@ -21,25 +21,24 @@ import I18n from 'i18n!gradezilla_uploads'
import 'spin.js/jquery.spin' import 'spin.js/jquery.spin'
export function waitForProcessing(progress) { export function waitForProcessing(progress) {
const dfd = $.Deferred(); const dfd = $.Deferred()
const spinner = $("#spinner").spin(); const spinner = $('#spinner').spin()
const amIDoneYet = (currentProgress) => { const amIDoneYet = currentProgress => {
if (currentProgress.workflow_state === "completed") { if (currentProgress.workflow_state === 'completed') {
$.ajaxJSON(ENV.uploaded_gradebook_data_path, "GET").then((uploadedGradebook) => { $.ajaxJSON(ENV.uploaded_gradebook_data_path, 'GET').then(uploadedGradebook => {
spinner.hide(); spinner.hide()
dfd.resolve(uploadedGradebook) dfd.resolve(uploadedGradebook)
}); })
} else if (currentProgress.workflow_state === "failed") { } else if (currentProgress.workflow_state === 'failed') {
dfd.reject(I18n.t("Invalid CSV file. Grades could not be updated.")); dfd.reject(I18n.t('Invalid CSV file. Grades could not be updated.'))
} else { } else {
setTimeout(() => { setTimeout(() => {
$.ajaxJSON(`/api/v1/progress/${currentProgress.id}`, "GET") $.ajaxJSON(`/api/v1/progress/${currentProgress.id}`, 'GET').then(amIDoneYet)
.then(amIDoneYet); }, 2000)
}, 2000);
} }
} }
amIDoneYet(progress); amIDoneYet(progress)
return dfd; return dfd
} }

View File

@ -25,7 +25,7 @@ let iframe
let info let info
QUnit.module('PostGradesFrameDialog screenreader only content', { QUnit.module('PostGradesFrameDialog screenreader only content', {
setup () { setup() {
fakeENV.setup({LTI_LAUNCH_FRAME_ALLOWANCES: ['midi', 'media']}) fakeENV.setup({LTI_LAUNCH_FRAME_ALLOWANCES: ['midi', 'media']})
dialog = new PostGradesFrameDialog({}) dialog = new PostGradesFrameDialog({})
el = dialog.$dialog el = dialog.$dialog
@ -33,7 +33,7 @@ QUnit.module('PostGradesFrameDialog screenreader only content', {
dialog.open() dialog.open()
}, },
teardown () { teardown() {
dialog.close() dialog.close()
dialog.$dialog.remove() dialog.$dialog.remove()
fakeENV.teardown() fakeENV.teardown()
@ -71,7 +71,12 @@ test('hides ending info alert and removes class from iframe', () => {
}) })
test("doesn't show infos or add border to iframe by default", () => { test("doesn't show infos or add border to iframe by default", () => {
equal(el.find('.before_external_content_info_alert.screenreader-only, .after_external_content_info_alert.screenreader-only').length, 2) equal(
el.find(
'.before_external_content_info_alert.screenreader-only, .after_external_content_info_alert.screenreader-only'
).length,
2
)
notOk(iframe.hasClass('info_alert_outline')) notOk(iframe.hasClass('info_alert_outline'))
}) })

View File

@ -16,194 +16,204 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'; import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'
const student = { const student = {
id: '1', id: '1',
group_ids: ['1'], group_ids: ['1'],
sections: ['1'] sections: ['1']
}; }
function createMap (opts = {}) { function createMap(opts = {}) {
const defaults = { const defaults = {
hasGradingPeriods: false, hasGradingPeriods: false,
selectedGradingPeriodID: '0', selectedGradingPeriodID: '0',
isAdmin: false isAdmin: false
}; }
const params = Object.assign(defaults, opts); const params = Object.assign(defaults, opts)
return new SubmissionStateMap(params); return new SubmissionStateMap(params)
} }
function createAndSetupMap (assignment, opts = {}) { function createAndSetupMap(assignment, opts = {}) {
const map = createMap(opts); const map = createMap(opts)
const assignments = {}; const assignments = {}
assignments[assignment.id] = assignment; assignments[assignment.id] = assignment
map.setup([student], assignments); map.setup([student], assignments)
return map; return map
} }
// TODO: the spec setup above should live in a spec helper -- at the // TODO: the spec setup above should live in a spec helper -- at the
// time this is being written a significant amount of work is needed // time this is being written a significant amount of work is needed
// to be able to require javascript files that live in the spec directory // to be able to require javascript files that live in the spec directory
QUnit.module('SubmissionStateMap without grading periods'); QUnit.module('SubmissionStateMap without grading periods')
test('submission in an unpublished assignment is hidden', function () { test('submission in an unpublished assignment is hidden', function() {
const assignment = { id: '1', published: false, effectiveDueDates: {} }; const assignment = {id: '1', published: false, effectiveDueDates: {}}
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
strictEqual(state.hideGrade, true); strictEqual(state.hideGrade, true)
}); })
test('submission in a published assignment is not hidden', function () { test('submission in a published assignment is not hidden', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
strictEqual(state.hideGrade, false); strictEqual(state.hideGrade, false)
}); })
test('submission has grade hidden for a student without assignment visibility', function () { test('submission has grade hidden for a student without assignment visibility', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {}, only_visible_to_overrides: true }; const assignment = {
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); id: '1',
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); published: true,
equal(state.hideGrade, true); effectiveDueDates: {},
}); only_visible_to_overrides: true
}
const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, true)
})
test('submission has grade visible for a student with assignment visibility', function () { test('submission has grade visible for a student with assignment visibility', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: null, due_at: null,
grading_period_id: null, grading_period_id: null,
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, false); equal(state.hideGrade, false)
}); })
QUnit.module('SubmissionStateMap with grading periods and all grading periods selected', { QUnit.module('SubmissionStateMap with grading periods and all grading periods selected', {
setup () { setup() {
this.DATE_IN_CLOSED_PERIOD = '2015-07-15'; this.DATE_IN_CLOSED_PERIOD = '2015-07-15'
this.DATE_NOT_IN_CLOSED_PERIOD = '2015-08-15'; this.DATE_NOT_IN_CLOSED_PERIOD = '2015-08-15'
this.mapOptions = { hasGradingPeriods: true, selectedGradingPeriodID: '0' }; this.mapOptions = {hasGradingPeriods: true, selectedGradingPeriodID: '0'}
} }
}); })
test('submission has grade hidden for a student without assignment visibility', function () { test('submission has grade hidden for a student without assignment visibility', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {}, only_visible_to_overrides: true }; const assignment = {
const map = createAndSetupMap(assignment, this.mapOptions); id: '1',
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); published: true,
equal(state.hideGrade, true); effectiveDueDates: {},
}); only_visible_to_overrides: true
}
const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, true)
})
test('submission has grade visible for an assigned student with assignment due in a closed grading period', function () { test('submission has grade visible for an assigned student with assignment due in a closed grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_CLOSED_PERIOD, due_at: this.DATE_IN_CLOSED_PERIOD,
grading_period_id: '1', grading_period_id: '1',
in_closed_grading_period: true in_closed_grading_period: true
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, false); equal(state.hideGrade, false)
}); })
test('submission has grade visible for an assigned student with assignment due outside of a closed grading period', function () { test('submission has grade visible for an assigned student with assignment due outside of a closed grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_NOT_IN_CLOSED_PERIOD, due_at: this.DATE_NOT_IN_CLOSED_PERIOD,
grading_period_id: '2', grading_period_id: '2',
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, false); equal(state.hideGrade, false)
}); })
QUnit.module('SubmissionStateMap with grading periods and a non-closed grading period selected', { QUnit.module('SubmissionStateMap with grading periods and a non-closed grading period selected', {
setup () { setup() {
this.SELECTED_PERIOD_ID = '1'; this.SELECTED_PERIOD_ID = '1'
this.DATE_IN_SELECTED_PERIOD = '2015-08-15'; this.DATE_IN_SELECTED_PERIOD = '2015-08-15'
this.DATE_NOT_IN_SELECTED_PERIOD = '2015-10-15'; this.DATE_NOT_IN_SELECTED_PERIOD = '2015-10-15'
this.mapOptions = { hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID }; this.mapOptions = {hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID}
} }
}); })
test('submission has grade hidden for a student without assignment visibility', function () { test('submission has grade hidden for a student without assignment visibility', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, true); equal(state.hideGrade, true)
}); })
test('submission has grade hidden for an assigned student with assignment due outside of the selected grading period', function () { test('submission has grade hidden for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_NOT_IN_SELECTED_PERIOD, due_at: this.DATE_NOT_IN_SELECTED_PERIOD,
grading_period_id: '2', grading_period_id: '2',
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, true); equal(state.hideGrade, true)
}); })
test('submission has grade visible for an assigned student with assignment due in the selected grading period', function () { test('submission has grade visible for an assigned student with assignment due in the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_SELECTED_PERIOD, due_at: this.DATE_IN_SELECTED_PERIOD,
grading_period_id: this.SELECTED_PERIOD_ID, grading_period_id: this.SELECTED_PERIOD_ID,
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, false); equal(state.hideGrade, false)
}); })
QUnit.module('SubmissionStateMap with grading periods and a closed grading period selected', { QUnit.module('SubmissionStateMap with grading periods and a closed grading period selected', {
setup () { setup() {
this.SELECTED_PERIOD_ID = '1'; this.SELECTED_PERIOD_ID = '1'
this.DATE_IN_SELECTED_PERIOD = '2015-07-15'; this.DATE_IN_SELECTED_PERIOD = '2015-07-15'
this.DATE_NOT_IN_SELECTED_PERIOD = '2015-08-15'; this.DATE_NOT_IN_SELECTED_PERIOD = '2015-08-15'
this.mapOptions = { hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID }; this.mapOptions = {hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID}
} }
}); })
test('submission has grade hidden for a student without assignment visibility', function () { test('submission has grade hidden for a student without assignment visibility', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, true); equal(state.hideGrade, true)
}); })
test('submission has grade hidden for an assigned student with assignment due outside of the selected grading period', function () { test('submission has grade hidden for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_NOT_IN_SELECTED_PERIOD, due_at: this.DATE_NOT_IN_SELECTED_PERIOD,
grading_period_id: '2', grading_period_id: '2',
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, true); equal(state.hideGrade, true)
}); })
test('submission has grade visible for an assigned student with assignment due in the selected grading period', function () { test('submission has grade visible for an assigned student with assignment due in the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_SELECTED_PERIOD, due_at: this.DATE_IN_SELECTED_PERIOD,
grading_period_id: this.SELECTED_PERIOD_ID, grading_period_id: this.SELECTED_PERIOD_ID,
in_closed_grading_period: true in_closed_grading_period: true
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.hideGrade, false); equal(state.hideGrade, false)
}); })

View File

@ -16,231 +16,273 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'; import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'
const student = { const student = {
id: '1', id: '1',
group_ids: ['1'], group_ids: ['1'],
sections: ['1'] sections: ['1']
}; }
function createMap (opts = {}) { function createMap(opts = {}) {
const params = { const params = {
hasGradingPeriods: false, hasGradingPeriods: false,
selectedGradingPeriodID: '0', selectedGradingPeriodID: '0',
isAdmin: false, isAdmin: false,
...opts ...opts
}; }
return new SubmissionStateMap(params); return new SubmissionStateMap(params)
} }
function createAndSetupMap (assignment, opts = {}) { function createAndSetupMap(assignment, opts = {}) {
const submissionStateMap = createMap(opts); const submissionStateMap = createMap(opts)
const assignments = {}; const assignments = {}
assignments[assignment.id] = assignment; assignments[assignment.id] = assignment
submissionStateMap.setup([student], assignments); submissionStateMap.setup([student], assignments)
return submissionStateMap; return submissionStateMap
} }
QUnit.module('SubmissionStateMap without grading periods', function (suiteHooks) { QUnit.module('SubmissionStateMap without grading periods', function(suiteHooks) {
const dueDate = '2015-07-15'; const dueDate = '2015-07-15'
let assignment; let assignment
let submissionStateMap; let submissionStateMap
let options; let options
suiteHooks.beforeEach(() => { suiteHooks.beforeEach(() => {
options = { hasGradingPeriods: false }; options = {hasGradingPeriods: false}
assignment = { id: '1', published: true, effectiveDueDates: {} }; assignment = {id: '1', published: true, effectiveDueDates: {}}
}); })
QUnit.module('inNoGradingPeriod', function (_hooks) { QUnit.module('inNoGradingPeriod', function(_hooks) {
test('returns undefined if submission has no grading period', function () { test('returns undefined if submission has no grading period', function() {
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: dueDate, due_at: dueDate,
in_closed_grading_period: false in_closed_grading_period: false
} }
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inNoGradingPeriod, undefined); strictEqual(state.inNoGradingPeriod, undefined)
}); })
test('returns undefined if submission has a grading period', function () { test('returns undefined if submission has a grading period', function() {
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: dueDate, due_at: dueDate,
grading_period_id: 1, grading_period_id: 1,
in_closed_grading_period: false in_closed_grading_period: false
} }
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inNoGradingPeriod, undefined); strictEqual(state.inNoGradingPeriod, undefined)
}); })
}); })
QUnit.module('inOtherGradingPeriod', function (hooks) { QUnit.module('inOtherGradingPeriod', function(hooks) {
hooks.beforeEach(() => { hooks.beforeEach(() => {
assignment.effectiveDueDates[student.id] = {
due_at: dueDate,
in_closed_grading_period: false
};
});
test('returns undefined if filtering by grading period and submission is not in any grading period', function () {
submissionStateMap = createAndSetupMap(assignment, options);
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
strictEqual(state.inOtherGradingPeriod, undefined);
});
test('returns undefined if filtering by grading period and submission is in another grading period', function () {
assignment.effectiveDueDates[student.id].grading_period_id = '1';
submissionStateMap = createAndSetupMap(assignment, options);
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
strictEqual(state.inOtherGradingPeriod, undefined);
});
test('returns undefined if filtering by grading period and submission is in the same grading period', function () {
assignment.effectiveDueDates[student.id].grading_period_id = '2';
submissionStateMap = createAndSetupMap(assignment, options);
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
strictEqual(state.inOtherGradingPeriod, undefined);
});
});
QUnit.module('inClosedGradingPeriod', function (hooks) {
hooks.beforeEach(() => {
assignment.effectiveDueDates[student.id] = {
due_at: dueDate,
};
});
test('returns undefined if submission is in a closed grading period', function () {
assignment.effectiveDueDates[student.id].in_closed_grading_period = true;
submissionStateMap = createAndSetupMap(assignment, options);
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
strictEqual(state.inClosedGradingPeriod, undefined);
});
test('returns undefined if submission is in a closed grading period', function () {
assignment.effectiveDueDates[student.id].in_closed_grading_period = false;
submissionStateMap = createAndSetupMap(assignment, options);
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
strictEqual(state.inClosedGradingPeriod, undefined);
});
});
});
QUnit.module('SubmissionStateMap with grading periods', function (suiteHooks) {
const dueDate = '2015-07-15';
let assignment;
let submissionStateMap;
let options;
suiteHooks.beforeEach(() => {
options = { hasGradingPeriods: true, selectedGradingPeriodID: '0' };
assignment = { id: '1', published: true, effectiveDueDates: {} };
});
QUnit.module('inNoGradingPeriod', function (_hooks2) {
test('returns true if submission has no grading period', function () {
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: dueDate, due_at: dueDate,
in_closed_grading_period: false in_closed_grading_period: false
} }
submissionStateMap = createAndSetupMap(assignment, options); })
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); test('returns undefined if filtering by grading period and submission is not in any grading period', function() {
submissionStateMap = createAndSetupMap(assignment, options)
strictEqual(state.inNoGradingPeriod, true); const state = submissionStateMap.getSubmissionState({
}); user_id: student.id,
assignment_id: assignment.id
})
test('returns false if submission has a grading period', function () { strictEqual(state.inOtherGradingPeriod, undefined)
})
test('returns undefined if filtering by grading period and submission is in another grading period', function() {
assignment.effectiveDueDates[student.id].grading_period_id = '1'
submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inOtherGradingPeriod, undefined)
})
test('returns undefined if filtering by grading period and submission is in the same grading period', function() {
assignment.effectiveDueDates[student.id].grading_period_id = '2'
submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inOtherGradingPeriod, undefined)
})
})
QUnit.module('inClosedGradingPeriod', function(hooks) {
hooks.beforeEach(() => {
assignment.effectiveDueDates[student.id] = {
due_at: dueDate
}
})
test('returns undefined if submission is in a closed grading period', function() {
assignment.effectiveDueDates[student.id].in_closed_grading_period = true
submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inClosedGradingPeriod, undefined)
})
test('returns undefined if submission is in a closed grading period', function() {
assignment.effectiveDueDates[student.id].in_closed_grading_period = false
submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inClosedGradingPeriod, undefined)
})
})
})
QUnit.module('SubmissionStateMap with grading periods', function(suiteHooks) {
const dueDate = '2015-07-15'
let assignment
let submissionStateMap
let options
suiteHooks.beforeEach(() => {
options = {hasGradingPeriods: true, selectedGradingPeriodID: '0'}
assignment = {id: '1', published: true, effectiveDueDates: {}}
})
QUnit.module('inNoGradingPeriod', function(_hooks2) {
test('returns true if submission has no grading period', function() {
assignment.effectiveDueDates[student.id] = {
due_at: dueDate,
in_closed_grading_period: false
}
submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inNoGradingPeriod, true)
})
test('returns false if submission has a grading period', function() {
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: dueDate, due_at: dueDate,
grading_period_id: 1, grading_period_id: 1,
in_closed_grading_period: false in_closed_grading_period: false
} }
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inNoGradingPeriod, false); strictEqual(state.inNoGradingPeriod, false)
}); })
}); })
QUnit.module('inOtherGradingPeriod', function (hooks) { QUnit.module('inOtherGradingPeriod', function(hooks) {
hooks.beforeEach(() => { hooks.beforeEach(() => {
options = { hasGradingPeriods: true, selectedGradingPeriodID: '2' }; options = {hasGradingPeriods: true, selectedGradingPeriodID: '2'}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: dueDate, due_at: dueDate,
in_closed_grading_period: false in_closed_grading_period: false
}; }
}); })
test('returns false if filtering by grading period and submission is not in any grading period', function () { test('returns false if filtering by grading period and submission is not in any grading period', function() {
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inOtherGradingPeriod, false); strictEqual(state.inOtherGradingPeriod, false)
}); })
test('returns true if filtering by grading period and submission is in another grading period', function () { test('returns true if filtering by grading period and submission is in another grading period', function() {
assignment.effectiveDueDates[student.id].grading_period_id = '1'; assignment.effectiveDueDates[student.id].grading_period_id = '1'
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inOtherGradingPeriod, true); strictEqual(state.inOtherGradingPeriod, true)
}); })
test('returns false if filtering by grading period and submission is in the same grading period', function () { test('returns false if filtering by grading period and submission is in the same grading period', function() {
assignment.effectiveDueDates[student.id].grading_period_id = '2'; assignment.effectiveDueDates[student.id].grading_period_id = '2'
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inOtherGradingPeriod, false); strictEqual(state.inOtherGradingPeriod, false)
}); })
}); })
QUnit.module('inClosedGradingPeriod', function (hooks) { QUnit.module('inClosedGradingPeriod', function(hooks) {
hooks.beforeEach(() => { hooks.beforeEach(() => {
options = { hasGradingPeriods: true, selectedGradingPeriodID: '2' }; options = {hasGradingPeriods: true, selectedGradingPeriodID: '2'}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: dueDate, due_at: dueDate
}; }
}); })
test('returns true if submission is in a closed grading period', function () { test('returns true if submission is in a closed grading period', function() {
assignment.effectiveDueDates[student.id].in_closed_grading_period = true; assignment.effectiveDueDates[student.id].in_closed_grading_period = true
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inClosedGradingPeriod, true); strictEqual(state.inClosedGradingPeriod, true)
}); })
test('returns true if submission is in a closed grading period', function () { test('returns true if submission is in a closed grading period', function() {
assignment.effectiveDueDates[student.id].in_closed_grading_period = false; assignment.effectiveDueDates[student.id].in_closed_grading_period = false
submissionStateMap = createAndSetupMap(assignment, options); submissionStateMap = createAndSetupMap(assignment, options)
const state = submissionStateMap.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = submissionStateMap.getSubmissionState({
user_id: student.id,
assignment_id: assignment.id
})
strictEqual(state.inClosedGradingPeriod, false); strictEqual(state.inClosedGradingPeriod, false)
}); })
}); })
}); })

View File

@ -16,208 +16,218 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'; import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'
const student = { const student = {
id: '1', id: '1',
group_ids: ['1'], group_ids: ['1'],
sections: ['1'] sections: ['1']
}; }
function createMap (opts = {}) { function createMap(opts = {}) {
const defaults = { const defaults = {
hasGradingPeriods: false, hasGradingPeriods: false,
selectedGradingPeriodID: '0', selectedGradingPeriodID: '0',
isAdmin: false isAdmin: false
}; }
const params = { ...defaults, ...opts }; const params = {...defaults, ...opts}
return new SubmissionStateMap(params); return new SubmissionStateMap(params)
} }
function createAndSetupMap (assignment, opts = {}) { function createAndSetupMap(assignment, opts = {}) {
const map = createMap(opts); const map = createMap(opts)
const assignments = {}; const assignments = {}
assignments[assignment.id] = assignment; assignments[assignment.id] = assignment
map.setup([student], assignments); map.setup([student], assignments)
return map; return map
} }
// TODO: the spec setup above should live in a spec helper -- at the // TODO: the spec setup above should live in a spec helper -- at the
// time this is being written a significant amount of work is needed // time this is being written a significant amount of work is needed
// to be able to require javascript files that live in the spec directory // to be able to require javascript files that live in the spec directory
QUnit.module('SubmissionStateMap without grading periods'); QUnit.module('SubmissionStateMap without grading periods')
test('submission in an unpublished assignment is locked', function () { test('submission in an unpublished assignment is locked', function() {
const assignment = { id: '1', published: false, effectiveDueDates: {} }; const assignment = {id: '1', published: false, effectiveDueDates: {}}
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
strictEqual(state.locked, true); strictEqual(state.locked, true)
}); })
test('submission in a published assignment is not locked', function () { test('submission in a published assignment is not locked', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
strictEqual(state.locked, false); strictEqual(state.locked, false)
}); })
test('submission is locked for a student without assignment visibility', function () { test('submission is locked for a student without assignment visibility', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {}, only_visible_to_overrides: true }; const assignment = {
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); id: '1',
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); published: true,
equal(state.locked, true); effectiveDueDates: {},
}); only_visible_to_overrides: true
}
const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, true)
})
test('submission is unlocked for an assigned student', function () { test('submission is unlocked for an assigned student', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: null, due_at: null,
grading_period_id: null, grading_period_id: null,
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, { hasGradingPeriods: false }); const map = createAndSetupMap(assignment, {hasGradingPeriods: false})
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, false); equal(state.locked, false)
}); })
QUnit.module('SubmissionStateMap with grading periods and all grading periods selected', { QUnit.module('SubmissionStateMap with grading periods and all grading periods selected', {
setup () { setup() {
this.DATE_IN_CLOSED_PERIOD = '2015-07-15'; this.DATE_IN_CLOSED_PERIOD = '2015-07-15'
this.DATE_NOT_IN_CLOSED_PERIOD = '2015-08-15'; this.DATE_NOT_IN_CLOSED_PERIOD = '2015-08-15'
this.mapOptions = { hasGradingPeriods: true, selectedGradingPeriodID: '0' }; this.mapOptions = {hasGradingPeriods: true, selectedGradingPeriodID: '0'}
} }
}); })
test('submission is locked for a student without assignment visibility', function () { test('submission is locked for a student without assignment visibility', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {}, only_visible_to_overrides: true }; const assignment = {
const map = createAndSetupMap(assignment, this.mapOptions); id: '1',
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); published: true,
equal(state.locked, true); effectiveDueDates: {},
}); only_visible_to_overrides: true
}
const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, true)
})
test('submission is locked for an assigned student with assignment due in a closed grading period', function () { test('submission is locked for an assigned student with assignment due in a closed grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_CLOSED_PERIOD, due_at: this.DATE_IN_CLOSED_PERIOD,
grading_period_id: '1', grading_period_id: '1',
in_closed_grading_period: true in_closed_grading_period: true
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, true); equal(state.locked, true)
}); })
test('user is admin: submission is unlocked for an assigned student with assignment due in a closed grading period', function () { test('user is admin: submission is unlocked for an assigned student with assignment due in a closed grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_CLOSED_PERIOD, due_at: this.DATE_IN_CLOSED_PERIOD,
grading_period_id: '1', grading_period_id: '1',
in_closed_grading_period: true in_closed_grading_period: true
}; }
const mapOptions = { ...this.mapOptions, isAdmin: true }; const mapOptions = {...this.mapOptions, isAdmin: true}
const map = createAndSetupMap(assignment, mapOptions); const map = createAndSetupMap(assignment, mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, false); equal(state.locked, false)
}); })
test('submission is unlocked for an assigned student with assignment due outside of a closed grading period', function () { test('submission is unlocked for an assigned student with assignment due outside of a closed grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_NOT_IN_CLOSED_PERIOD, due_at: this.DATE_NOT_IN_CLOSED_PERIOD,
grading_period_id: '1', grading_period_id: '1',
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, false); equal(state.locked, false)
}); })
QUnit.module('SubmissionStateMap with grading periods and a non-closed grading period selected', { QUnit.module('SubmissionStateMap with grading periods and a non-closed grading period selected', {
setup () { setup() {
this.DATE_IN_SELECTED_PERIOD = '2015-07-15'; this.DATE_IN_SELECTED_PERIOD = '2015-07-15'
this.DATE_NOT_IN_SELECTED_PERIOD = '2015-08-15'; this.DATE_NOT_IN_SELECTED_PERIOD = '2015-08-15'
this.SELECTED_PERIOD_ID = '1'; this.SELECTED_PERIOD_ID = '1'
this.mapOptions = { hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID }; this.mapOptions = {hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID}
} }
}); })
test('submission is locked for an assigned student with assignment due outside of the selected grading period', function () { test('submission is locked for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_NOT_IN_SELECTED_PERIOD, due_at: this.DATE_NOT_IN_SELECTED_PERIOD,
grading_period_id: '2', grading_period_id: '2',
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, true); equal(state.locked, true)
}); })
test('submission is unlocked for an assigned student with assignment due in the selected grading period', function () { test('submission is unlocked for an assigned student with assignment due in the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_SELECTED_PERIOD, due_at: this.DATE_IN_SELECTED_PERIOD,
grading_period_id: this.SELECTED_PERIOD_ID, grading_period_id: this.SELECTED_PERIOD_ID,
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, false); equal(state.locked, false)
}); })
QUnit.module('SubmissionStateMap with grading periods and a closed grading period selected', { QUnit.module('SubmissionStateMap with grading periods and a closed grading period selected', {
setup () { setup() {
this.DATE_IN_SELECTED_PERIOD = '2015-07-15'; this.DATE_IN_SELECTED_PERIOD = '2015-07-15'
this.DATE_NOT_IN_SELECTED_PERIOD = '2015-08-15'; this.DATE_NOT_IN_SELECTED_PERIOD = '2015-08-15'
this.SELECTED_PERIOD_ID = '1'; this.SELECTED_PERIOD_ID = '1'
this.mapOptions = { hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID }; this.mapOptions = {hasGradingPeriods: true, selectedGradingPeriodID: this.SELECTED_PERIOD_ID}
} }
}); })
test('submission is locked for an assigned student with assignment due outside of the selected grading period', function () { test('submission is locked for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_NOT_IN_SELECTED_PERIOD, due_at: this.DATE_NOT_IN_SELECTED_PERIOD,
grading_period_id: '2', grading_period_id: '2',
in_closed_grading_period: false in_closed_grading_period: false
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, true); equal(state.locked, true)
}); })
test('submission is locked for an assigned student with assignment due in the selected grading period', function () { test('submission is locked for an assigned student with assignment due in the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_SELECTED_PERIOD, due_at: this.DATE_IN_SELECTED_PERIOD,
grading_period_id: this.SELECTED_PERIOD_ID, grading_period_id: this.SELECTED_PERIOD_ID,
in_closed_grading_period: true in_closed_grading_period: true
}; }
const map = createAndSetupMap(assignment, this.mapOptions); const map = createAndSetupMap(assignment, this.mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, true); equal(state.locked, true)
}); })
test('user is admin: submission is unlocked for an assigned student with assignment due in the selected grading period', function () { test('user is admin: submission is unlocked for an assigned student with assignment due in the selected grading period', function() {
const assignment = { id: '1', published: true, effectiveDueDates: {} }; const assignment = {id: '1', published: true, effectiveDueDates: {}}
assignment.effectiveDueDates[student.id] = { assignment.effectiveDueDates[student.id] = {
due_at: this.DATE_IN_SELECTED_PERIOD, due_at: this.DATE_IN_SELECTED_PERIOD,
grading_period_id: this.SELECTED_PERIOD_ID, grading_period_id: this.SELECTED_PERIOD_ID,
in_closed_grading_period: true in_closed_grading_period: true
}; }
const mapOptions = { ...this.mapOptions, isAdmin: true }; const mapOptions = {...this.mapOptions, isAdmin: true}
const map = createAndSetupMap(assignment, mapOptions); const map = createAndSetupMap(assignment, mapOptions)
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id }); const state = map.getSubmissionState({user_id: student.id, assignment_id: assignment.id})
equal(state.locked, false); equal(state.locked, false)
}); })

View File

@ -16,30 +16,32 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import {fromJS} from 'immutable'; import {fromJS} from 'immutable'
import moment from 'moment'; import moment from 'moment'
import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'; import SubmissionStateMap from 'jsx/gradezilla/SubmissionStateMap'
const studentWithoutSubmission = { const studentWithoutSubmission = {
id: '1', id: '1',
group_ids: ['1'], group_ids: ['1'],
sections: ['1'] sections: ['1']
}; }
const studentWithSubmission = { const studentWithSubmission = {
id: '1', id: '1',
group_ids: ['1'], group_ids: ['1'],
sections: ['1'], sections: ['1'],
assignment_1: {} assignment_1: {}
}; }
const yesterday = moment(new Date()).subtract(1, 'day'); const yesterday = moment(new Date()).subtract(1, 'day')
const tomorrow = moment(new Date()).add(1, 'day'); const tomorrow = moment(new Date()).add(1, 'day')
const baseAssignment = fromJS({ const baseAssignment = fromJS({
id: '1', id: '1',
published: true, published: true,
effectiveDueDates: {1: {due_at: new Date(), grading_period_id: '2', in_closed_grading_period: true}} effectiveDueDates: {
1: {due_at: new Date(), grading_period_id: '2', in_closed_grading_period: true}
}
}) })
const unpublishedAssignment = baseAssignment.merge({published: false}) const unpublishedAssignment = baseAssignment.merge({published: false})
const anonymousMutedAssignment = baseAssignment.merge({ const anonymousMutedAssignment = baseAssignment.merge({
@ -47,29 +49,33 @@ const anonymousMutedAssignment = baseAssignment.merge({
anonymous_grading: true, anonymous_grading: true,
muted: true muted: true
}) })
const moderatedAndGradesUnpublishedAssignment = const moderatedAndGradesUnpublishedAssignment = baseAssignment.merge({
baseAssignment.merge({moderated_grading: true, grades_published: false}) moderated_grading: true,
const hiddenFromStudent = grades_published: false
baseAssignment.merge({only_visible_to_overrides: true, assignment_visibility: []}) })
const hiddenFromStudent = baseAssignment.merge({
only_visible_to_overrides: true,
assignment_visibility: []
})
const hasGradingPeriodsAssignment = baseAssignment const hasGradingPeriodsAssignment = baseAssignment
function createMap (opts = {}) { function createMap(opts = {}) {
const defaults = { const defaults = {
hasGradingPeriods: false, hasGradingPeriods: false,
selectedGradingPeriodID: '0', selectedGradingPeriodID: '0',
isAdmin: false isAdmin: false
}; }
const params = { ...defaults, ...opts }; const params = {...defaults, ...opts}
return new SubmissionStateMap(params); return new SubmissionStateMap(params)
} }
function createAndSetupMap (assignment, student, opts = {}) { function createAndSetupMap(assignment, student, opts = {}) {
const map = createMap(opts); const map = createMap(opts)
const assignments = {}; const assignments = {}
assignments[assignment.id] = assignment; assignments[assignment.id] = assignment
map.setup([student], assignments); map.setup([student], assignments)
return map; return map
} }
QUnit.module('#setSubmissionCellState', function() { QUnit.module('#setSubmissionCellState', function() {
@ -77,21 +83,27 @@ QUnit.module('#setSubmissionCellState', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: false published: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.locked, true); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.locked, true)
})
test('the submission state has hideGrade set if assignment is not published', function() { test('the submission state has hideGrade set if assignment is not published', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: false published: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.hideGrade, true); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.hideGrade, true)
})
test('the submission state is locked if assignment is not visible', function() { test('the submission state is locked if assignment is not visible', function() {
const assignment = { const assignment = {
@ -99,11 +111,14 @@ QUnit.module('#setSubmissionCellState', function() {
published: true, published: true,
only_visible_to_overrides: true, only_visible_to_overrides: true,
assignment_visibility: [] assignment_visibility: []
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.locked, true); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.locked, true)
})
test('the submission state has hideGrade set if assignment is not visible', function() { test('the submission state has hideGrade set if assignment is not visible', function() {
const assignment = { const assignment = {
@ -111,33 +126,42 @@ QUnit.module('#setSubmissionCellState', function() {
published: true, published: true,
only_visible_to_overrides: true, only_visible_to_overrides: true,
assignment_visibility: [] assignment_visibility: []
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.hideGrade, true); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.hideGrade, true)
})
test('the submission state is not locked if assignment is published and visible', function() { test('the submission state is not locked if assignment is published and visible', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
only_visible_to_overrides: false only_visible_to_overrides: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.locked, false); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.locked, false)
})
test('the submission state has hideGrade not set if assignment is published and visible', function() { test('the submission state has hideGrade not set if assignment is published and visible', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
only_visible_to_overrides: false only_visible_to_overrides: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.hideGrade, false); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.hideGrade, false)
})
test('the submission state is locked when the student is not assigned', function() { test('the submission state is locked when the student is not assigned', function() {
const assignment = { const assignment = {
@ -147,7 +171,10 @@ QUnit.module('#setSubmissionCellState', function() {
assignment_visibility: ['2'] assignment_visibility: ['2']
} }
const map = createAndSetupMap(assignment, studentWithSubmission) const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.id
})
strictEqual(submission.locked, true) strictEqual(submission.locked, true)
}) })
@ -156,22 +183,28 @@ QUnit.module('#setSubmissionCellState', function() {
id: '1', id: '1',
published: true, published: true,
moderated_grading: false moderated_grading: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.locked, false); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.locked, false)
})
test('the submission state has hideGrade not set if not moderated grading', function() { test('the submission state has hideGrade not set if not moderated grading', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
moderated_grading: false moderated_grading: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.hideGrade, false); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.hideGrade, false)
})
test('the submission state is not locked if moderated grading and grades published', function() { test('the submission state is not locked if moderated grading and grades published', function() {
const assignment = { const assignment = {
@ -179,11 +212,14 @@ QUnit.module('#setSubmissionCellState', function() {
published: true, published: true,
moderated_grading: true, moderated_grading: true,
grades_published: true grades_published: true
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.locked, false); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.locked, false)
})
test('the submission state has hideGrade not set if moderated grading and grades published', function() { test('the submission state has hideGrade not set if moderated grading and grades published', function() {
const assignment = { const assignment = {
@ -191,11 +227,14 @@ QUnit.module('#setSubmissionCellState', function() {
published: true, published: true,
moderated_grading: true, moderated_grading: true,
grades_published: true grades_published: true
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.hideGrade, false); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.hideGrade, false)
})
test('the submission state is locked if moderated grading and grades not published', function() { test('the submission state is locked if moderated grading and grades not published', function() {
const assignment = { const assignment = {
@ -203,11 +242,14 @@ QUnit.module('#setSubmissionCellState', function() {
published: true, published: true,
moderated_grading: true, moderated_grading: true,
grades_published: false grades_published: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.locked, true); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.locked, true)
})
test('the submission state has hideGrade not set if moderated grading and grades not published', function() { test('the submission state has hideGrade not set if moderated grading and grades not published', function() {
const assignment = { const assignment = {
@ -215,114 +257,132 @@ QUnit.module('#setSubmissionCellState', function() {
published: true, published: true,
moderated_grading: true, moderated_grading: true,
grades_published: false grades_published: false
}; }
const map = createAndSetupMap(assignment, studentWithSubmission); const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }); const submission = map.getSubmissionState({
strictEqual(submission.hideGrade, false); user_id: studentWithSubmission.id,
}); assignment_id: assignment.id
})
strictEqual(submission.hideGrade, false)
})
QUnit.module('when the assignment is anonymous', function(hooks) { QUnit.module('when the assignment is anonymous', function(hooks) {
let assignment let assignment
hooks.beforeEach(() => { hooks.beforeEach(() => {
assignment = { id: '1', published: true, anonymous_grading: true } assignment = {id: '1', published: true, anonymous_grading: true}
}) })
test('the submission state is locked when anonymize_students is true', function() { test('the submission state is locked when anonymize_students is true', function() {
assignment.anonymize_students = true assignment.anonymize_students = true
const map = createAndSetupMap(assignment, studentWithSubmission) const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.id
})
strictEqual(submission.locked, true) strictEqual(submission.locked, true)
}) })
test('the submission state is hidden when anonymize_students is true', function() { test('the submission state is hidden when anonymize_students is true', function() {
assignment.anonymize_students = true assignment.anonymize_students = true
const map = createAndSetupMap(assignment, studentWithSubmission) const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.id
})
strictEqual(submission.hideGrade, true) strictEqual(submission.hideGrade, true)
}) })
test('the submission state is unlocked when the assignment is unmuted', function() { test('the submission state is unlocked when the assignment is unmuted', function() {
const map = createAndSetupMap(assignment, studentWithSubmission) const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.id
})
strictEqual(submission.locked, false) strictEqual(submission.locked, false)
}) })
test('the submission state is not hidden when the assignment is unmuted', function() { test('the submission state is not hidden when the assignment is unmuted', function() {
const map = createAndSetupMap(assignment, studentWithSubmission) const map = createAndSetupMap(assignment, studentWithSubmission)
const submission = map.getSubmissionState({ user_id: studentWithSubmission.id, assignment_id: assignment.id }) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.id
})
strictEqual(submission.hideGrade, false) strictEqual(submission.hideGrade, false)
}) })
}) })
QUnit.module('no submission', function() { QUnit.module('no submission', function() {
test('the submission object is missing if the assignment is late', function () { test('the submission object is missing if the assignment is late', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
effectiveDueDates: { 1: { due_at: yesterday } } effectiveDueDates: {1: {due_at: yesterday}}
}; }
const map = createAndSetupMap(assignment, studentWithoutSubmission); const map = createAndSetupMap(assignment, studentWithoutSubmission)
const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id); const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id)
strictEqual(submission.missing, true); strictEqual(submission.missing, true)
}); })
test('the submission object is not missing if the assignment is not late', function () { test('the submission object is not missing if the assignment is not late', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
effectiveDueDates: { 1: { due_at: tomorrow } } effectiveDueDates: {1: {due_at: tomorrow}}
}; }
const map = createAndSetupMap(assignment, studentWithoutSubmission); const map = createAndSetupMap(assignment, studentWithoutSubmission)
const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id); const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id)
strictEqual(submission.missing, false); strictEqual(submission.missing, false)
}); })
test('the submission object is not missing, if the assignment is not late ' + test(
'and there are no due dates', function () { 'the submission object is not missing, if the assignment is not late ' +
'and there are no due dates',
function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
effectiveDueDates: {} effectiveDueDates: {}
}; }
const map = createAndSetupMap(assignment, studentWithoutSubmission); const map = createAndSetupMap(assignment, studentWithoutSubmission)
const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id); const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id)
strictEqual(submission.missing, false); strictEqual(submission.missing, false)
}); }
)
test('the submission object has seconds_late set to zero', function () { test('the submission object has seconds_late set to zero', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
effectiveDueDates: { 1: { due_at: new Date() } } effectiveDueDates: {1: {due_at: new Date()}}
}; }
const map = createAndSetupMap(assignment, studentWithoutSubmission); const map = createAndSetupMap(assignment, studentWithoutSubmission)
const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id); const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id)
strictEqual(submission.seconds_late, 0); strictEqual(submission.seconds_late, 0)
}); })
test('the submission object has late set to false', function () { test('the submission object has late set to false', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
effectiveDueDates: { 1: { due_at: new Date() } } effectiveDueDates: {1: {due_at: new Date()}}
}; }
const map = createAndSetupMap(assignment, studentWithoutSubmission); const map = createAndSetupMap(assignment, studentWithoutSubmission)
const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id); const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id)
strictEqual(submission.late, false); strictEqual(submission.late, false)
}); })
test('the submission object has excused set to false', function () { test('the submission object has excused set to false', function() {
const assignment = { const assignment = {
id: '1', id: '1',
published: true, published: true,
effectiveDueDates: { 1: { due_at: new Date() } } effectiveDueDates: {1: {due_at: new Date()}}
}; }
const map = createAndSetupMap(assignment, studentWithoutSubmission); const map = createAndSetupMap(assignment, studentWithoutSubmission)
const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id); const submission = map.getSubmission(studentWithoutSubmission.id, assignment.id)
strictEqual(submission.excused, false); strictEqual(submission.excused, false)
}); })
}); })
test('an unpublished assignment is locked and grades are hidden', () => { test('an unpublished assignment is locked and grades are hidden', () => {
const map = createAndSetupMap(unpublishedAssignment.toJS(), studentWithoutSubmission) const map = createAndSetupMap(unpublishedAssignment.toJS(), studentWithoutSubmission)
@ -334,7 +394,10 @@ QUnit.module('#setSubmissionCellState', function() {
}) })
test('a moderated and unpublished grades assignment is locked and grades not hidden when published', () => { test('a moderated and unpublished grades assignment is locked and grades not hidden when published', () => {
const map = createAndSetupMap(moderatedAndGradesUnpublishedAssignment.toJS(), studentWithoutSubmission) const map = createAndSetupMap(
moderatedAndGradesUnpublishedAssignment.toJS(),
studentWithoutSubmission
)
const submission = map.getSubmissionState({ const submission = map.getSubmissionState({
user_id: studentWithoutSubmission.id, user_id: studentWithoutSubmission.id,
assignment_id: moderatedAndGradesUnpublishedAssignment.get('id') assignment_id: moderatedAndGradesUnpublishedAssignment.get('id')
@ -367,35 +430,54 @@ QUnit.module('#setSubmissionCellState', function() {
test('is unpublished takes precedence over one that is moderated and has unpublished grades', () => { test('is unpublished takes precedence over one that is moderated and has unpublished grades', () => {
const assignment = moderatedAndGradesUnpublishedAssignment.merge(unpublishedAssignment) const assignment = moderatedAndGradesUnpublishedAssignment.merge(unpublishedAssignment)
const map = createAndSetupMap(assignment.toJS(), studentWithSubmission) const map = createAndSetupMap(assignment.toJS(), studentWithSubmission)
const submission = map.getSubmissionState({user_id: studentWithSubmission.id, assignment_id: assignment.get('id')}) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.get('id')
})
deepEqual(submission, {locked: true, hideGrade: true}) deepEqual(submission, {locked: true, hideGrade: true})
}) })
test('is anonymously graded and muted takes precedence over one that is moderated and has unpublished grades', () => { test('is anonymously graded and muted takes precedence over one that is moderated and has unpublished grades', () => {
const assignment = moderatedAndGradesUnpublishedAssignment.merge(anonymousMutedAssignment) const assignment = moderatedAndGradesUnpublishedAssignment.merge(anonymousMutedAssignment)
const map = createAndSetupMap(assignment.toJS(), studentWithSubmission) const map = createAndSetupMap(assignment.toJS(), studentWithSubmission)
const submission = map.getSubmissionState({user_id: studentWithSubmission.id, assignment_id: assignment.get('id')}) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.get('id')
})
deepEqual(submission, {locked: true, hideGrade: true}) deepEqual(submission, {locked: true, hideGrade: true})
}) })
test('is moderated and has unpublished grades takes precedence over one that is hidden from the student', () => { test('is moderated and has unpublished grades takes precedence over one that is hidden from the student', () => {
const assignment = hiddenFromStudent.merge(moderatedAndGradesUnpublishedAssignment) const assignment = hiddenFromStudent.merge(moderatedAndGradesUnpublishedAssignment)
const map = createAndSetupMap(assignment.toJS(), studentWithSubmission) const map = createAndSetupMap(assignment.toJS(), studentWithSubmission)
const submission = map.getSubmissionState({user_id: studentWithSubmission.id, assignment_id: assignment.get('id')}) const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.get('id')
})
deepEqual(submission, {locked: true, hideGrade: false}) deepEqual(submission, {locked: true, hideGrade: false})
}) })
test('is hidden from the student takes precendence over one that has grading periods', () => { test('is hidden from the student takes precendence over one that has grading periods', () => {
const assignment = hasGradingPeriodsAssignment.merge(hiddenFromStudent) const assignment = hasGradingPeriodsAssignment.merge(hiddenFromStudent)
const map = createAndSetupMap(assignment.toJS(), studentWithSubmission, {hasGradingPeriods: true}) const map = createAndSetupMap(assignment.toJS(), studentWithSubmission, {
const submission = map.getSubmissionState({user_id: studentWithSubmission.id, assignment_id: assignment.get('id')}) hasGradingPeriods: true
})
const submission = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.get('id')
})
deepEqual(submission, {locked: true, hideGrade: true}) deepEqual(submission, {locked: true, hideGrade: true})
}) })
test('has grading periods takes precendence over all other assignments', () => { test('has grading periods takes precendence over all other assignments', () => {
const assignment = hasGradingPeriodsAssignment.merge(baseAssignment) const assignment = hasGradingPeriodsAssignment.merge(baseAssignment)
const map = createAndSetupMap(assignment.toJS(), studentWithSubmission, {hasGradingPeriods: true}) const map = createAndSetupMap(assignment.toJS(), studentWithSubmission, {
const actualSubmissionState = map.getSubmissionState({user_id: studentWithSubmission.id, assignment_id: assignment.get('id')}) hasGradingPeriods: true
})
const actualSubmissionState = map.getSubmissionState({
user_id: studentWithSubmission.id,
assignment_id: assignment.get('id')
})
const expectedSubmissionState = { const expectedSubmissionState = {
locked: true, locked: true,
hideGrade: false, hideGrade: false,
@ -407,4 +489,4 @@ QUnit.module('#setSubmissionCellState', function() {
}) })
}) })
}) })
}); })

View File

@ -22,11 +22,12 @@ define([
'jsx/gradezilla/default_gradebook/CurveGradesDialogManager', 'jsx/gradezilla/default_gradebook/CurveGradesDialogManager',
'i18n!gradebook', 'i18n!gradebook',
'compiled/jquery.rails_flash_notifications' 'compiled/jquery.rails_flash_notifications'
], ($, CurveGradesDialog, { createCurveGradesAction }, I18n) => { ], ($, CurveGradesDialog, {createCurveGradesAction}, I18n) => {
QUnit.module('CurveGradesDialogManager.createCurveGradesAction.isDisabled', { QUnit.module('CurveGradesDialogManager.createCurveGradesAction.isDisabled', {
props ({points_possible, grading_type, submissionsLoaded}) { props({points_possible, grading_type, submissionsLoaded}) {
return [ return [
{ // assignment {
// assignment
points_possible, points_possible,
grading_type grading_type
}, },
@ -36,47 +37,73 @@ define([
contextUrl: 'http://contextUrl/', contextUrl: 'http://contextUrl/',
submissionsLoaded submissionsLoaded
} }
]; ]
} }
}); })
test('is not disabled when submissions are loaded, grading type is not pass/fail and there are ' + test(
'points that are not 0', function () { 'is not disabled when submissions are loaded, grading type is not pass/fail and there are ' +
const props = this.props({points_possible: 10, grading_type: 'points', submissionsLoaded: true}); 'points that are not 0',
notOk(createCurveGradesAction(...props).isDisabled); function() {
}); const props = this.props({
points_possible: 10,
grading_type: 'points',
submissionsLoaded: true
})
notOk(createCurveGradesAction(...props).isDisabled)
}
)
test('is disabled when submissions are not loaded', function () { test('is disabled when submissions are not loaded', function() {
const props = this.props({points_possible: 10, grading_type: 'points', submissionsLoaded: false }); const props = this.props({
ok(createCurveGradesAction(...props).isDisabled); points_possible: 10,
}); grading_type: 'points',
submissionsLoaded: false
})
ok(createCurveGradesAction(...props).isDisabled)
})
test('is disabled when grading type is pass/fail', function () { test('is disabled when grading type is pass/fail', function() {
const props = this.props({points_possible: 10, grading_type: 'pass_fail', submissionsLoaded: true }); const props = this.props({
ok(createCurveGradesAction(...props).isDisabled); points_possible: 10,
}); grading_type: 'pass_fail',
submissionsLoaded: true
})
ok(createCurveGradesAction(...props).isDisabled)
})
test('returns true when points_possible is null', function () { test('returns true when points_possible is null', function() {
const props = this.props({points_possible: null, grading_type: 'points', submissionsLoaded: true}); const props = this.props({
ok(createCurveGradesAction(...props).isDisabled); points_possible: null,
}); grading_type: 'points',
submissionsLoaded: true
})
ok(createCurveGradesAction(...props).isDisabled)
})
test('returns true when points_possible is 0', function () { test('returns true when points_possible is 0', function() {
const props = this.props({points_possible: 0, grading_type: 'points', submissionsLoaded: true}); const props = this.props({points_possible: 0, grading_type: 'points', submissionsLoaded: true})
ok(createCurveGradesAction(...props).isDisabled); ok(createCurveGradesAction(...props).isDisabled)
}); })
QUnit.module('CurveGradesDialogManager.createCurveGradesAction.onSelect', { QUnit.module('CurveGradesDialogManager.createCurveGradesAction.onSelect', {
setup () { setup() {
this.flashErrorSpy = sandbox.spy($, 'flashError'); this.flashErrorSpy = sandbox.spy($, 'flashError')
sandbox.stub(CurveGradesDialog.prototype, 'show'); sandbox.stub(CurveGradesDialog.prototype, 'show')
}, },
onSelect ({ isAdmin = false, inClosedGradingPeriod = false } = {}) { onSelect({isAdmin = false, inClosedGradingPeriod = false} = {}) {
createCurveGradesAction({ inClosedGradingPeriod }, [], isAdmin, 'http://contextUrl/', true).onSelect() createCurveGradesAction(
{inClosedGradingPeriod},
[],
isAdmin,
'http://contextUrl/',
true
).onSelect()
}, },
props ({ inClosedGradingPeriod = false, isAdmin = false } = {}) { props({inClosedGradingPeriod = false, isAdmin = false} = {}) {
return [ return [
{ // assignment {
// assignment
inClosedGradingPeriod inClosedGradingPeriod
}, },
[], // students [], // students
@ -85,56 +112,62 @@ define([
contextUrl: 'http://contextUrl/', contextUrl: 'http://contextUrl/',
submissionsLoaded: true submissionsLoaded: true
} }
]; ]
} }
}); })
test('calls flashError if is not admin and in a closed grading period', function () { test('calls flashError if is not admin and in a closed grading period', function() {
const props = this.props({ isAdmin: false, inClosedGradingPeriod: true }); const props = this.props({isAdmin: false, inClosedGradingPeriod: true})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
ok(this.flashErrorSpy.withArgs(I18n.t('Unable to curve grades because this assignment is due in a closed ' + ok(
'grading period for at least one student')).calledOnce); this.flashErrorSpy.withArgs(
}); I18n.t(
'Unable to curve grades because this assignment is due in a closed ' +
'grading period for at least one student'
)
).calledOnce
)
})
test('does not call curve grades dialog if is not admin and in a closed grading period', function () { test('does not call curve grades dialog if is not admin and in a closed grading period', function() {
const props = this.props({ isAdmin: false, inClosedGradingPeriod: true }); const props = this.props({isAdmin: false, inClosedGradingPeriod: true})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
strictEqual(CurveGradesDialog.prototype.show.callCount, 0); strictEqual(CurveGradesDialog.prototype.show.callCount, 0)
}); })
test('does not call flashError if is admin and in a closed grading period', function () { test('does not call flashError if is admin and in a closed grading period', function() {
const props = this.props({ isAdmin: true, inClosedGradingPeriod: true }); const props = this.props({isAdmin: true, inClosedGradingPeriod: true})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
ok(this.flashErrorSpy.notCalled); ok(this.flashErrorSpy.notCalled)
}); })
test('calls curve grades dialog if is admin and in a closed grading period', function () { test('calls curve grades dialog if is admin and in a closed grading period', function() {
const props = this.props({ isAdmin: true, inClosedGradingPeriod: true }); const props = this.props({isAdmin: true, inClosedGradingPeriod: true})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
strictEqual(CurveGradesDialog.prototype.show.callCount, 1); strictEqual(CurveGradesDialog.prototype.show.callCount, 1)
}); })
test('does not call flashError if is not admin and not in a closed grading period', function () { test('does not call flashError if is not admin and not in a closed grading period', function() {
const props = this.props({ isAdmin: false, inClosedGradingPeriod: false }); const props = this.props({isAdmin: false, inClosedGradingPeriod: false})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
ok(this.flashErrorSpy.notCalled); ok(this.flashErrorSpy.notCalled)
}); })
test('calls curve grades dialog if is not admin and not in a closed grading period', function () { test('calls curve grades dialog if is not admin and not in a closed grading period', function() {
const props = this.props({ isAdmin: false, inClosedGradingPeriod: false }); const props = this.props({isAdmin: false, inClosedGradingPeriod: false})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
strictEqual(CurveGradesDialog.prototype.show.callCount, 1); strictEqual(CurveGradesDialog.prototype.show.callCount, 1)
}); })
test('does not call flashError if is admin and not in a closed grading period', function () { test('does not call flashError if is admin and not in a closed grading period', function() {
const props = this.props({ isAdmin: true, inClosedGradingPeriod: false }); const props = this.props({isAdmin: true, inClosedGradingPeriod: false})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
ok(this.flashErrorSpy.notCalled); ok(this.flashErrorSpy.notCalled)
}); })
test('calls curve grades dialog if is admin and not in a closed grading period', function () { test('calls curve grades dialog if is admin and not in a closed grading period', function() {
const props = this.props({ isAdmin: true, inClosedGradingPeriod: false }); const props = this.props({isAdmin: true, inClosedGradingPeriod: false})
createCurveGradesAction(...props).onSelect(); createCurveGradesAction(...props).onSelect()
strictEqual(CurveGradesDialog.prototype.show.callCount, 1); strictEqual(CurveGradesDialog.prototype.show.callCount, 1)
}); })
}); })

View File

@ -16,8 +16,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import $ from 'jquery'; import $ from 'jquery'
import fakeENV from 'helpers/fakeENV'; import fakeENV from 'helpers/fakeENV'
import DataLoader from 'jsx/gradezilla/DataLoader' import DataLoader from 'jsx/gradezilla/DataLoader'
import { import {
createGradebook, createGradebook,
@ -25,35 +25,36 @@ import {
} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper' } from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper'
import SlickGridSpecHelper from '../../gradezilla/default_gradebook/GradebookGrid/GridSupport/SlickGridSpecHelper' import SlickGridSpecHelper from '../../gradezilla/default_gradebook/GradebookGrid/GridSupport/SlickGridSpecHelper'
QUnit.module('Gradebook Grid Column Ordering', function (suiteHooks) { QUnit.module('Gradebook Grid Column Ordering', function(suiteHooks) {
let $fixture; let $fixture
let gridSpecHelper; let gridSpecHelper
let gradebook; let gradebook
let dataLoader; let dataLoader
let assignmentGroups; let assignmentGroups
let assignments; let assignments
let contextModules; let contextModules
let customColumns; let customColumns
function createContextModules () { function createContextModules() {
contextModules = [ contextModules = [
{ id: '2601', position: 3, name: 'Final Module' }, {id: '2601', position: 3, name: 'Final Module'},
{ id: '2602', position: 2, name: 'Second Module' }, {id: '2602', position: 2, name: 'Second Module'},
{ id: '2603', position: 1, name: 'First Module' } {id: '2603', position: 1, name: 'First Module'}
]; ]
} }
function createCustomColumns () { function createCustomColumns() {
customColumns = [ customColumns = [
{ id: '2401', teacher_notes: true, title: 'Notes' }, {id: '2401', teacher_notes: true, title: 'Notes'},
{ id: '2402', teacher_notes: false, title: 'Other Notes' } {id: '2402', teacher_notes: false, title: 'Other Notes'}
]; ]
} }
function createAssignments () { function createAssignments() {
assignments = { assignments = {
homework: [{ homework: [
{
id: '2301', id: '2301',
assignment_group_id: '2201', assignment_group_id: '2201',
course_id: '1201', course_id: '1201',
@ -68,7 +69,8 @@ QUnit.module('Gradebook Grid Column Ordering', function (suiteHooks) {
position: 1, position: 1,
published: true, published: true,
submission_types: ['online_text_entry'] submission_types: ['online_text_entry']
}, { },
{
id: '2303', id: '2303',
assignment_group_id: '2201', assignment_group_id: '2201',
course_id: '1201', course_id: '1201',
@ -83,9 +85,11 @@ QUnit.module('Gradebook Grid Column Ordering', function (suiteHooks) {
position: 2, position: 2,
published: true, published: true,
submission_types: ['online_text_entry'] submission_types: ['online_text_entry']
}], }
],
quizzes: [{ quizzes: [
{
id: '2302', id: '2302',
assignment_group_id: '2202', assignment_group_id: '2202',
course_id: '1201', course_id: '1201',
@ -100,7 +104,8 @@ QUnit.module('Gradebook Grid Column Ordering', function (suiteHooks) {
position: 1, position: 1,
published: true, published: true,
submission_types: ['online_quiz'] submission_types: ['online_quiz']
}, { },
{
id: '2304', id: '2304',
assignment_group_id: '2202', assignment_group_id: '2202',
course_id: '1201', course_id: '1201',
@ -115,68 +120,69 @@ QUnit.module('Gradebook Grid Column Ordering', function (suiteHooks) {
position: 2, position: 2,
published: true, published: true,
submission_types: ['online_quiz'] submission_types: ['online_quiz']
}] }
}; ]
}
} }
function createAssignmentGroups () { function createAssignmentGroups() {
assignmentGroups = [ assignmentGroups = [
{ id: '2201', position: 2, name: 'Homework', assignments: assignments.homework }, {id: '2201', position: 2, name: 'Homework', assignments: assignments.homework},
{ id: '2202', position: 1, name: 'Quizzes', assignments: assignments.quizzes } {id: '2202', position: 1, name: 'Quizzes', assignments: assignments.quizzes}
]; ]
} }
function addStudentIds () { function addStudentIds() {
dataLoader.gotStudentIds.resolve({ dataLoader.gotStudentIds.resolve({
user_ids: ['1101'] user_ids: ['1101']
}); })
} }
function addGradingPeriodAssignments () { function addGradingPeriodAssignments() {
dataLoader.gotGradingPeriodAssignments.resolve({ dataLoader.gotGradingPeriodAssignments.resolve({
grading_period_assignments: { 1401: ['2301'], 1402: ['2302'] } grading_period_assignments: {1401: ['2301'], 1402: ['2302']}
}); })
} }
function addContextModules () { function addContextModules() {
dataLoader.gotContextModules.resolve(contextModules); dataLoader.gotContextModules.resolve(contextModules)
} }
function addCustomColumns () { function addCustomColumns() {
dataLoader.gotCustomColumns.resolve(customColumns); dataLoader.gotCustomColumns.resolve(customColumns)
} }
function addAssignmentGroups () { function addAssignmentGroups() {
dataLoader.gotAssignmentGroups.resolve(assignmentGroups); dataLoader.gotAssignmentGroups.resolve(assignmentGroups)
} }
function addGridData () { function addGridData() {
addStudentIds(); addStudentIds()
addContextModules(); addContextModules()
addCustomColumns(); addCustomColumns()
addAssignmentGroups(); addAssignmentGroups()
addGradingPeriodAssignments(); addGradingPeriodAssignments()
} }
function arrangeColumnsBy (sortType, direction) { function arrangeColumnsBy(sortType, direction) {
gradebook.arrangeColumnsBy({ sortType, direction }); gradebook.arrangeColumnsBy({sortType, direction})
} }
function createGradebookAndAddData (options) { function createGradebookAndAddData(options) {
gradebook = createGradebook(options); gradebook = createGradebook(options)
gradebook.initialize(); gradebook.initialize()
addGridData(); addGridData()
gridSpecHelper = new SlickGridSpecHelper(gradebook.gradebookGrid); gridSpecHelper = new SlickGridSpecHelper(gradebook.gradebookGrid)
} }
suiteHooks.beforeEach(function () { suiteHooks.beforeEach(function() {
$fixture = document.createElement('div'); $fixture = document.createElement('div')
document.body.appendChild($fixture); document.body.appendChild($fixture)
setFixtureHtml($fixture); setFixtureHtml($fixture)
fakeENV.setup({ fakeENV.setup({
current_user_id: '1101' current_user_id: '1101'
}); })
dataLoader = { dataLoader = {
gotAssignmentGroups: $.Deferred(), gotAssignmentGroups: $.Deferred(),
@ -187,272 +193,387 @@ QUnit.module('Gradebook Grid Column Ordering', function (suiteHooks) {
gotStudentIds: $.Deferred(), gotStudentIds: $.Deferred(),
gotStudents: $.Deferred(), gotStudents: $.Deferred(),
gotSubmissions: $.Deferred() gotSubmissions: $.Deferred()
}; }
sinon.stub(DataLoader, 'loadGradebookData').returns(dataLoader); sinon.stub(DataLoader, 'loadGradebookData').returns(dataLoader)
sinon.stub(DataLoader, 'getDataForColumn'); sinon.stub(DataLoader, 'getDataForColumn')
createAssignments(); createAssignments()
createAssignmentGroups(); createAssignmentGroups()
createContextModules(); createContextModules()
createCustomColumns(); createCustomColumns()
}); })
suiteHooks.afterEach(function () { suiteHooks.afterEach(function() {
gradebook.destroy(); gradebook.destroy()
DataLoader.loadGradebookData.restore(); DataLoader.loadGradebookData.restore()
DataLoader.getDataForColumn.restore(); DataLoader.getDataForColumn.restore()
fakeENV.teardown(); fakeENV.teardown()
$fixture.remove(); $fixture.remove()
}); })
QUnit.module('when initializing the grid', function () { QUnit.module('when initializing the grid', function() {
test('defaults assignment column order to assignment group positions when setting is not set', function () { test('defaults assignment column order to assignment group positions when setting is not set', function() {
createGradebookAndAddData(); createGradebookAndAddData()
const expectedOrder = [ const expectedOrder = [
'assignment_2302', 'assignment_2304', 'assignment_2301', 'assignment_2303', 'assignment_2302',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2304',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2303',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('sorts assignment columns by assignment name when setting is "name"', function () { test('sorts assignment columns by assignment name when setting is "name"', function() {
createGradebookAndAddData({ createGradebookAndAddData({
gradebook_column_order_settings: { sortType: 'name', direction: 'ascending' } gradebook_column_order_settings: {sortType: 'name', direction: 'ascending'}
}); })
const expectedOrder = [ const expectedOrder = [
'assignment_2303', 'assignment_2304', 'assignment_2301', 'assignment_2302', 'assignment_2303',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2304',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2302',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('sorts assignment columns by assignment due date when setting is "due date"', function () { test('sorts assignment columns by assignment due date when setting is "due date"', function() {
createGradebookAndAddData({ createGradebookAndAddData({
gradebook_column_order_settings: { sortType: 'due_date', direction: 'ascending' } gradebook_column_order_settings: {sortType: 'due_date', direction: 'ascending'}
}); })
const expectedOrder = [ const expectedOrder = [
'assignment_2301', 'assignment_2302', 'assignment_2304', 'assignment_2303', 'assignment_2301',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2304',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2303',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('sorts assignment columns by assignment points possible when setting is "points"', function () { test('sorts assignment columns by assignment points possible when setting is "points"', function() {
createGradebookAndAddData({ createGradebookAndAddData({
gradebook_column_order_settings: { sortType: 'points', direction: 'ascending' } gradebook_column_order_settings: {sortType: 'points', direction: 'ascending'}
}); })
const expectedOrder = [ const expectedOrder = [
'assignment_2301', 'assignment_2302', 'assignment_2303', 'assignment_2304', 'assignment_2301',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2303',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2304',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('sorts assignment columns by module position when setting is "module position"', function () { test('sorts assignment columns by module position when setting is "module position"', function() {
createGradebookAndAddData({ createGradebookAndAddData({
gradebook_column_order_settings: { sortType: 'module_position', direction: 'ascending' } gradebook_column_order_settings: {sortType: 'module_position', direction: 'ascending'}
}); })
const expectedOrder = [ const expectedOrder = [
'assignment_2304', 'assignment_2302', 'assignment_2301', 'assignment_2303', 'assignment_2304',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2303',
}); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
})
QUnit.module('when sorting by default', function () { QUnit.module('when sorting by default', function() {
test('sorts assignment columns by assignment group position', function () { test('sorts assignment columns by assignment group position', function() {
assignments.homework.splice(1, 1); assignments.homework.splice(1, 1)
assignments.quizzes.splice(1, 1); assignments.quizzes.splice(1, 1)
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('default', 'ascending'); arrangeColumnsBy('default', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2302', 'assignment_2301', 'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('additionally sorts assignment columns by position within assignment groups', function () { test('additionally sorts assignment columns by position within assignment groups', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('default', 'ascending'); arrangeColumnsBy('default', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2302', 'assignment_2304', 'assignment_2301', 'assignment_2303', 'assignment_2302',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2304',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2303',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('optionally sorts in descending order', function () { test('optionally sorts in descending order', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('default', 'descending'); arrangeColumnsBy('default', 'descending')
const expectedOrder = [ const expectedOrder = [
'assignment_2303', 'assignment_2301', 'assignment_2304', 'assignment_2302', 'assignment_2303',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2301',
]; 'assignment_2304',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2302',
}); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
})
QUnit.module('when sorting by name', function () { QUnit.module('when sorting by name', function() {
test('sorts assignment columns by assignment name', function () { test('sorts assignment columns by assignment name', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('name', 'ascending'); arrangeColumnsBy('name', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2303', 'assignment_2304', 'assignment_2301', 'assignment_2302', 'assignment_2303',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2304',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2302',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('optionally sorts in descending order', function () { test('optionally sorts in descending order', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('name', 'descending'); arrangeColumnsBy('name', 'descending')
const expectedOrder = [ const expectedOrder = [
'assignment_2302', 'assignment_2301', 'assignment_2304', 'assignment_2303', 'assignment_2302',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2301',
]; 'assignment_2304',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2303',
}); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
})
QUnit.module('when sorting by due date', function () { QUnit.module('when sorting by due date', function() {
test('sorts assignment columns by assignment due date', function () { test('sorts assignment columns by assignment due date', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('due_date', 'ascending'); arrangeColumnsBy('due_date', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2301', 'assignment_2302', 'assignment_2304', 'assignment_2303', 'assignment_2301',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2304',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2303',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('sorts assignments with due dates before assignments without due dates', function () { test('sorts assignments with due dates before assignments without due dates', function() {
assignments.quizzes[0].due_at = null; assignments.quizzes[0].due_at = null
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('due_date', 'ascending'); arrangeColumnsBy('due_date', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2301', 'assignment_2304', 'assignment_2303', 'assignment_2302', 'assignment_2301',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2304',
]; 'assignment_2303',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2302',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('optionally sorts in descending order', function () { test('optionally sorts in descending order', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('due_date', 'descending'); arrangeColumnsBy('due_date', 'descending')
const expectedOrder = [ const expectedOrder = [
'assignment_2303', 'assignment_2304', 'assignment_2302', 'assignment_2301', 'assignment_2303',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2304',
]; 'assignment_2302',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2301',
}); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
})
QUnit.module('when sorting by points', function () { QUnit.module('when sorting by points', function() {
test('sorts assignment columns by assignment points possible', function () { test('sorts assignment columns by assignment points possible', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('points', 'ascending'); arrangeColumnsBy('points', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2301', 'assignment_2302', 'assignment_2303', 'assignment_2304', 'assignment_2301',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2303',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2304',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('optionally sorts in descending order', function () { test('optionally sorts in descending order', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('points', 'descending'); arrangeColumnsBy('points', 'descending')
const expectedOrder = [ const expectedOrder = [
'assignment_2304', 'assignment_2303', 'assignment_2302', 'assignment_2301', 'assignment_2304',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2303',
]; 'assignment_2302',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2301',
}); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
})
QUnit.module('when sorting by module position', function () { QUnit.module('when sorting by module position', function() {
test('sorts assignment columns by module position', function () { test('sorts assignment columns by module position', function() {
assignments.homework.splice(1, 1); assignments.homework.splice(1, 1)
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('module_position', 'ascending'); arrangeColumnsBy('module_position', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2304', 'assignment_2302', 'assignment_2301', 'assignment_2304',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('additionally sorts assignment columns by position within modules', function () { test('additionally sorts assignment columns by position within modules', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('module_position', 'ascending'); arrangeColumnsBy('module_position', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2304', 'assignment_2302', 'assignment_2301', 'assignment_2303', 'assignment_2304',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2302',
]; 'assignment_2301',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2303',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('sorts assignments with modules before assignments without modules', function () { test('sorts assignments with modules before assignments without modules', function() {
assignments.quizzes[0].module_ids = []; assignments.quizzes[0].module_ids = []
assignments.quizzes[0].module_positions = []; assignments.quizzes[0].module_positions = []
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('module_position', 'ascending'); arrangeColumnsBy('module_position', 'ascending')
const expectedOrder = [ const expectedOrder = [
'assignment_2304', 'assignment_2301', 'assignment_2303', 'assignment_2302', 'assignment_2304',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2301',
]; 'assignment_2303',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2302',
}); 'assignment_group_2202',
'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('optionally sorts in descending order', function () { test('optionally sorts in descending order', function() {
createGradebookAndAddData(); createGradebookAndAddData()
arrangeColumnsBy('module_position', 'descending'); arrangeColumnsBy('module_position', 'descending')
const expectedOrder = [ const expectedOrder = [
'assignment_2303', 'assignment_2301', 'assignment_2302', 'assignment_2304', 'assignment_2303',
'assignment_group_2202', 'assignment_group_2201', 'total_grade' 'assignment_2301',
]; 'assignment_2302',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2304',
}); 'assignment_group_2202',
}); 'assignment_group_2201',
'total_grade'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
})
QUnit.module('when using a custom order', function () { QUnit.module('when using a custom order', function() {
test('sorts all saved columns in the saved order', function () { test('sorts all saved columns in the saved order', function() {
const customOrder = [ const customOrder = [
'total_grade', 'assignment_group_2201', 'assignment_2301', 'assignment_2303', 'total_grade',
'assignment_group_2202', 'assignment_2302', 'assignment_2304' 'assignment_group_2201',
]; 'assignment_2301',
'assignment_2303',
'assignment_group_2202',
'assignment_2302',
'assignment_2304'
]
createGradebookAndAddData({ createGradebookAndAddData({
gradebook_column_order_settings: { sortType: 'custom', customOrder } gradebook_column_order_settings: {sortType: 'custom', customOrder}
}); })
deepEqual(gridSpecHelper.listScrollableColumnIds(), customOrder); deepEqual(gridSpecHelper.listScrollableColumnIds(), customOrder)
}); })
test('sorts any unsaved columns after the saved order', function () { test('sorts any unsaved columns after the saved order', function() {
const customOrder = [ const customOrder = [
'total_grade', 'assignment_2301', 'assignment_group_2202', 'assignment_2302', 'assignment_2304' 'total_grade',
]; 'assignment_2301',
'assignment_group_2202',
'assignment_2302',
'assignment_2304'
]
createGradebookAndAddData({ createGradebookAndAddData({
gradebook_column_order_settings: { sortType: 'custom', customOrder } gradebook_column_order_settings: {sortType: 'custom', customOrder}
}); })
const expectedOrder = [ const expectedOrder = [
'total_grade', 'assignment_2301', 'assignment_group_2202', 'assignment_2302', 'total_grade',
'assignment_2304', 'assignment_2303', 'assignment_group_2201' 'assignment_2301',
]; 'assignment_group_2202',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_2302',
}); 'assignment_2304',
'assignment_2303',
'assignment_group_2201'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
test('sorts unsaved columns by assignment group position', function () { test('sorts unsaved columns by assignment group position', function() {
const customOrder = [ const customOrder = [
'total_grade', 'assignment_2301', 'assignment_group_2202', 'assignment_group_2201' 'total_grade',
]; 'assignment_2301',
'assignment_group_2202',
'assignment_group_2201'
]
createGradebookAndAddData({ createGradebookAndAddData({
gradebook_column_order_settings: { sortType: 'custom', customOrder } gradebook_column_order_settings: {sortType: 'custom', customOrder}
}); })
const expectedOrder = [ const expectedOrder = [
'total_grade', 'assignment_2301', 'assignment_group_2202', 'assignment_group_2201', 'total_grade',
'assignment_2302', 'assignment_2304', 'assignment_2303' 'assignment_2301',
]; 'assignment_group_2202',
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder); 'assignment_group_2201',
}); 'assignment_2302',
}); 'assignment_2304',
}); 'assignment_2303'
]
deepEqual(gridSpecHelper.listScrollableColumnIds(), expectedOrder)
})
})
})

View File

@ -16,8 +16,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import $ from 'jquery'; import $ from 'jquery'
import fakeENV from 'helpers/fakeENV'; import fakeENV from 'helpers/fakeENV'
import DataLoader from 'jsx/gradezilla/DataLoader' import DataLoader from 'jsx/gradezilla/DataLoader'
import { import {
createGradebook, createGradebook,
@ -25,35 +25,36 @@ import {
} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper' } from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper'
import SlickGridSpecHelper from '../../gradezilla/default_gradebook/GradebookGrid/GridSupport/SlickGridSpecHelper' import SlickGridSpecHelper from '../../gradezilla/default_gradebook/GradebookGrid/GridSupport/SlickGridSpecHelper'
QUnit.module('Gradebook Grid Column Widths', (suiteHooks) => { QUnit.module('Gradebook Grid Column Widths', suiteHooks => {
let $fixture; let $fixture
let gridSpecHelper; let gridSpecHelper
let gradebook; let gradebook
let dataLoader; let dataLoader
let assignmentGroups; let assignmentGroups
let assignments; let assignments
let contextModules; let contextModules
let customColumns; let customColumns
function createContextModules () { function createContextModules() {
contextModules = [ contextModules = [
{ id: '2601', position: 3, name: 'Final Module' }, {id: '2601', position: 3, name: 'Final Module'},
{ id: '2602', position: 2, name: 'Second Module' }, {id: '2602', position: 2, name: 'Second Module'},
{ id: '2603', position: 1, name: 'First Module' } {id: '2603', position: 1, name: 'First Module'}
]; ]
} }
function createCustomColumns () { function createCustomColumns() {
customColumns = [ customColumns = [
{ id: '2401', teacher_notes: true, title: 'Notes' }, {id: '2401', teacher_notes: true, title: 'Notes'},
{ id: '2402', teacher_notes: false, title: 'Other Notes' } {id: '2402', teacher_notes: false, title: 'Other Notes'}
]; ]
} }
function createAssignments () { function createAssignments() {
assignments = { assignments = {
homework: [{ homework: [
{
id: '2301', id: '2301',
assignment_group_id: '2201', assignment_group_id: '2201',
course_id: '1201', course_id: '1201',
@ -68,7 +69,8 @@ QUnit.module('Gradebook Grid Column Widths', (suiteHooks) => {
position: 1, position: 1,
published: true, published: true,
submission_types: ['online_text_entry'] submission_types: ['online_text_entry']
}, { },
{
id: '2303', id: '2303',
assignment_group_id: '2201', assignment_group_id: '2201',
course_id: '1201', course_id: '1201',
@ -83,9 +85,11 @@ QUnit.module('Gradebook Grid Column Widths', (suiteHooks) => {
position: 2, position: 2,
published: true, published: true,
submission_types: ['online_text_entry'] submission_types: ['online_text_entry']
}], }
],
quizzes: [{ quizzes: [
{
id: '2302', id: '2302',
assignment_group_id: '2202', assignment_group_id: '2202',
course_id: '1201', course_id: '1201',
@ -100,7 +104,8 @@ QUnit.module('Gradebook Grid Column Widths', (suiteHooks) => {
position: 1, position: 1,
published: true, published: true,
submission_types: ['online_quiz'] submission_types: ['online_quiz']
}, { },
{
id: '2304', id: '2304',
assignment_group_id: '2202', assignment_group_id: '2202',
course_id: '1201', course_id: '1201',
@ -115,64 +120,65 @@ QUnit.module('Gradebook Grid Column Widths', (suiteHooks) => {
position: 2, position: 2,
published: true, published: true,
submission_types: ['online_quiz'] submission_types: ['online_quiz']
}] }
}; ]
}
} }
function createAssignmentGroups () { function createAssignmentGroups() {
assignmentGroups = [ assignmentGroups = [
{ id: '2201', position: 2, name: 'Homework', assignments: assignments.homework }, {id: '2201', position: 2, name: 'Homework', assignments: assignments.homework},
{ id: '2202', position: 1, name: 'Quizzes', assignments: assignments.quizzes } {id: '2202', position: 1, name: 'Quizzes', assignments: assignments.quizzes}
]; ]
} }
function addStudentIds () { function addStudentIds() {
dataLoader.gotStudentIds.resolve({ dataLoader.gotStudentIds.resolve({
user_ids: ['1101'] user_ids: ['1101']
}); })
} }
function addGradingPeriodAssignments () { function addGradingPeriodAssignments() {
dataLoader.gotGradingPeriodAssignments.resolve({ dataLoader.gotGradingPeriodAssignments.resolve({
grading_period_assignments: { 1401: ['2301'], 1402: ['2302'] } grading_period_assignments: {1401: ['2301'], 1402: ['2302']}
}); })
} }
function addContextModules () { function addContextModules() {
dataLoader.gotContextModules.resolve(contextModules); dataLoader.gotContextModules.resolve(contextModules)
} }
function addCustomColumns () { function addCustomColumns() {
dataLoader.gotCustomColumns.resolve(customColumns); dataLoader.gotCustomColumns.resolve(customColumns)
} }
function addAssignmentGroups () { function addAssignmentGroups() {
dataLoader.gotAssignmentGroups.resolve(assignmentGroups); dataLoader.gotAssignmentGroups.resolve(assignmentGroups)
} }
function addGridData () { function addGridData() {
addStudentIds(); addStudentIds()
addContextModules(); addContextModules()
addCustomColumns(); addCustomColumns()
addAssignmentGroups(); addAssignmentGroups()
addGradingPeriodAssignments(); addGradingPeriodAssignments()
} }
function createGradebookAndAddData (options) { function createGradebookAndAddData(options) {
gradebook = createGradebook(options); gradebook = createGradebook(options)
gradebook.initialize(); gradebook.initialize()
addGridData(); addGridData()
gridSpecHelper = new SlickGridSpecHelper(gradebook.gradebookGrid); gridSpecHelper = new SlickGridSpecHelper(gradebook.gradebookGrid)
} }
suiteHooks.beforeEach(() => { suiteHooks.beforeEach(() => {
$fixture = document.createElement('div'); $fixture = document.createElement('div')
document.body.appendChild($fixture); document.body.appendChild($fixture)
setFixtureHtml($fixture); setFixtureHtml($fixture)
fakeENV.setup({ fakeENV.setup({
current_user_id: '1101' current_user_id: '1101'
}); })
dataLoader = { dataLoader = {
gotAssignmentGroups: $.Deferred(), gotAssignmentGroups: $.Deferred(),
@ -183,100 +189,103 @@ QUnit.module('Gradebook Grid Column Widths', (suiteHooks) => {
gotStudentIds: $.Deferred(), gotStudentIds: $.Deferred(),
gotStudents: $.Deferred(), gotStudents: $.Deferred(),
gotSubmissions: $.Deferred() gotSubmissions: $.Deferred()
}; }
sinon.stub(DataLoader, 'loadGradebookData').returns(dataLoader); sinon.stub(DataLoader, 'loadGradebookData').returns(dataLoader)
sinon.stub(DataLoader, 'getDataForColumn'); sinon.stub(DataLoader, 'getDataForColumn')
createAssignments(); createAssignments()
createAssignmentGroups(); createAssignmentGroups()
createContextModules(); createContextModules()
createCustomColumns(); createCustomColumns()
}); })
suiteHooks.afterEach(() => { suiteHooks.afterEach(() => {
gradebook.gradebookGrid.destroy(); gradebook.gradebookGrid.destroy()
$(document).unbind('gridready'); $(document).unbind('gridready')
DataLoader.loadGradebookData.restore(); DataLoader.loadGradebookData.restore()
DataLoader.getDataForColumn.restore(); DataLoader.getDataForColumn.restore()
fakeENV.teardown(); fakeENV.teardown()
$fixture.remove(); $fixture.remove()
}); })
QUnit.module('when initializing the grid', (hooks) => { QUnit.module('when initializing the grid', hooks => {
hooks.beforeEach(() => { hooks.beforeEach(() => {
gradebook = createGradebook(); gradebook = createGradebook()
gradebook.gradebookColumnSizeSettings = { assignment_2302: 10, assignment_2303: 54 }; gradebook.gradebookColumnSizeSettings = {assignment_2302: 10, assignment_2303: 54}
gradebook.initialize(); gradebook.initialize()
addGridData(); addGridData()
gridSpecHelper = new SlickGridSpecHelper(gradebook.gradebookGrid); gridSpecHelper = new SlickGridSpecHelper(gradebook.gradebookGrid)
}); })
test('defaults assignment column size to fit the assignment name', () => { test('defaults assignment column size to fit the assignment name', () => {
const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2301'); const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2301')
ok(columnNode.offsetWidth > 10, 'width is not the minimum'); ok(columnNode.offsetWidth > 10, 'width is not the minimum')
}); })
test('uses a stored width for assignment column headers', () => { test('uses a stored width for assignment column headers', () => {
const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2303'); const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2303')
strictEqual(columnNode.offsetWidth, 54); strictEqual(columnNode.offsetWidth, 54)
}); })
test('hides assignment column header content when the column is minimized', () => { test('hides assignment column header content when the column is minimized', () => {
const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2302'); const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2302')
ok(columnNode.classList.contains('minimized')); ok(columnNode.classList.contains('minimized'))
}); })
test('hides assignment column cell content when the column is minimized', () => { test('hides assignment column cell content when the column is minimized', () => {
const columnIndex = gridSpecHelper.listColumnIds().indexOf('assignment_2302'); const columnIndex = gridSpecHelper.listColumnIds().indexOf('assignment_2302')
const cellNode = gradebook.gradebookGrid.grid.getCellNode(0, columnIndex); const cellNode = gradebook.gradebookGrid.grid.getCellNode(0, columnIndex)
ok(cellNode.classList.contains('minimized')); ok(cellNode.classList.contains('minimized'))
}); })
}); })
QUnit.module('onColumnsResized', (hooks) => { QUnit.module('onColumnsResized', hooks => {
function resizeColumn (columnId, widthChange) { function resizeColumn(columnId, widthChange) {
const column = gridSpecHelper.getColumn(columnId); const column = gridSpecHelper.getColumn(columnId)
const updatedColumn = { ...column, width: column.width + widthChange }; const updatedColumn = {...column, width: column.width + widthChange}
gradebook.gradebookGrid.gridSupport.events.onColumnsResized.trigger(null, [updatedColumn]); gradebook.gradebookGrid.gridSupport.events.onColumnsResized.trigger(null, [updatedColumn])
} }
hooks.beforeEach(() => { hooks.beforeEach(() => {
createGradebookAndAddData(); createGradebookAndAddData()
sinon.stub(gradebook, 'saveColumnWidthPreference'); sinon.stub(gradebook, 'saveColumnWidthPreference')
}); })
test('updates the column definitions for resized columns', () => { test('updates the column definitions for resized columns', () => {
const originalWidth = gridSpecHelper.getColumn('assignment_2304').width; const originalWidth = gridSpecHelper.getColumn('assignment_2304').width
resizeColumn('assignment_2304', -20); resizeColumn('assignment_2304', -20)
strictEqual(gradebook.gradebookGrid.gridData.columns.definitions.assignment_2304.width, originalWidth - 20); strictEqual(
}); gradebook.gradebookGrid.gridData.columns.definitions.assignment_2304.width,
originalWidth - 20
)
})
test('hides assignment column header content when the column is minimized', () => { test('hides assignment column header content when the column is minimized', () => {
resizeColumn('assignment_2304', -100); resizeColumn('assignment_2304', -100)
const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2304'); const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2304')
ok(columnNode.classList.contains('minimized')); ok(columnNode.classList.contains('minimized'))
}); })
test('hides assignment column cell content when the column is minimized', () => { test('hides assignment column cell content when the column is minimized', () => {
resizeColumn('assignment_2304', -100); resizeColumn('assignment_2304', -100)
const columnIndex = gridSpecHelper.listColumnIds().indexOf('assignment_2304'); const columnIndex = gridSpecHelper.listColumnIds().indexOf('assignment_2304')
const cellNode = gradebook.gradebookGrid.grid.getCellNode(0, columnIndex); const cellNode = gradebook.gradebookGrid.grid.getCellNode(0, columnIndex)
ok(cellNode.classList.contains('minimized')); ok(cellNode.classList.contains('minimized'))
}); })
test('unhides assignment column header content when the column is unminimized', () => { test('unhides assignment column header content when the column is unminimized', () => {
resizeColumn('assignment_2304', -100); resizeColumn('assignment_2304', -100)
resizeColumn('assignment_2304', 1); resizeColumn('assignment_2304', 1)
const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2304'); const columnNode = gridSpecHelper.getColumnHeaderNode('assignment_2304')
notOk(columnNode.classList.contains('minimized')); notOk(columnNode.classList.contains('minimized'))
}); })
test('unhides assignment column cell content when the column is unminimized', () => { test('unhides assignment column cell content when the column is unminimized', () => {
resizeColumn('assignment_2304', -100); resizeColumn('assignment_2304', -100)
resizeColumn('assignment_2304', 1); resizeColumn('assignment_2304', 1)
const columnIndex = gridSpecHelper.listColumnIds().indexOf('assignment_2304'); const columnIndex = gridSpecHelper.listColumnIds().indexOf('assignment_2304')
const cellNode = gradebook.gradebookGrid.grid.getCellNode(0, columnIndex); const cellNode = gradebook.gradebookGrid.grid.getCellNode(0, columnIndex)
notOk(cellNode.classList.contains('minimized')); notOk(cellNode.classList.contains('minimized'))
}); })
}); })
}); })

View File

@ -16,172 +16,199 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import _ from 'underscore'; import _ from 'underscore'
import GradebookApi from 'jsx/gradezilla/default_gradebook/apis/GradebookApi'; import GradebookApi from 'jsx/gradezilla/default_gradebook/apis/GradebookApi'
QUnit.module('GradebookApi.createTeacherNotesColumn', { QUnit.module('GradebookApi.createTeacherNotesColumn', {
setup () { setup() {
this.customColumn = { id: '2401', hidden: false, position: 1, teacher_notes: true, title: 'Notes' }; this.customColumn = {
this.createTeacherNotesColumnUrl = '/api/v1/courses/1201/custom_gradebook_columns'; id: '2401',
this.server = sinon.fakeServer.create({ respondImmediately: true }); hidden: false,
const responseBody = JSON.stringify(this.customColumn); position: 1,
this.server.respondWith('POST', this.createTeacherNotesColumnUrl, [200, { 'Content-Type': 'application/json' }, responseBody]); teacher_notes: true,
}, title: 'Notes'
getRequest () {
// filter requests to eliminate spec pollution from unrelated specs
return _.find(this.server.requests, request => request.url.includes(this.createTeacherNotesColumnUrl));
},
teardown () {
this.server.restore();
} }
}); this.createTeacherNotesColumnUrl = '/api/v1/courses/1201/custom_gradebook_columns'
this.server = sinon.fakeServer.create({respondImmediately: true})
const responseBody = JSON.stringify(this.customColumn)
this.server.respondWith('POST', this.createTeacherNotesColumnUrl, [
200,
{'Content-Type': 'application/json'},
responseBody
])
},
test('sends a post request to the "create teacher notes column" url', function () { getRequest() {
return GradebookApi.createTeacherNotesColumn('1201') // filter requests to eliminate spec pollution from unrelated specs
.then(() => { return _.find(this.server.requests, request =>
const request = this.getRequest(); request.url.includes(this.createTeacherNotesColumnUrl)
equal(request.method, 'POST'); )
equal(request.url, this.createTeacherNotesColumnUrl); },
});
});
test('includes data to create a teacher notes column', function () { teardown() {
return GradebookApi.createTeacherNotesColumn('1201') this.server.restore()
.then(() => { }
const bodyData = JSON.parse(this.getRequest().requestBody); })
equal(bodyData.column.title, 'Notes');
strictEqual(bodyData.column.position, 1);
equal(bodyData.column.teacher_notes, true);
});
});
test('includes required request headers', function () { test('sends a post request to the "create teacher notes column" url', function() {
return GradebookApi.createTeacherNotesColumn('1201') return GradebookApi.createTeacherNotesColumn('1201').then(() => {
.then(() => { const request = this.getRequest()
const { requestHeaders } = this.getRequest(); equal(request.method, 'POST')
ok(requestHeaders.Accept.includes('application/json+canvas-string-ids'), 'includes header for Canvas string ids'); equal(request.url, this.createTeacherNotesColumnUrl)
ok(requestHeaders['Content-Type'].includes('application/json'), })
'includes "application/json" content type'); })
equal(requestHeaders['X-Requested-With'], 'XMLHttpRequest');
});
});
test('sends the column data to the success handler', function () { test('includes data to create a teacher notes column', function() {
return GradebookApi.createTeacherNotesColumn('1201') return GradebookApi.createTeacherNotesColumn('1201').then(() => {
.then(({ data }) => { const bodyData = JSON.parse(this.getRequest().requestBody)
deepEqual(data, this.customColumn); equal(bodyData.column.title, 'Notes')
}); strictEqual(bodyData.column.position, 1)
}); equal(bodyData.column.teacher_notes, true)
})
})
test('includes required request headers', function() {
return GradebookApi.createTeacherNotesColumn('1201').then(() => {
const {requestHeaders} = this.getRequest()
ok(
requestHeaders.Accept.includes('application/json+canvas-string-ids'),
'includes header for Canvas string ids'
)
ok(
requestHeaders['Content-Type'].includes('application/json'),
'includes "application/json" content type'
)
equal(requestHeaders['X-Requested-With'], 'XMLHttpRequest')
})
})
test('sends the column data to the success handler', function() {
return GradebookApi.createTeacherNotesColumn('1201').then(({data}) => {
deepEqual(data, this.customColumn)
})
})
QUnit.module('GradebookApi.updateTeacherNotesColumn', { QUnit.module('GradebookApi.updateTeacherNotesColumn', {
setup () { setup() {
this.customColumn = { id: '2401', hidden: true, position: 1, teacher_notes: true, title: 'Notes' }; this.customColumn = {id: '2401', hidden: true, position: 1, teacher_notes: true, title: 'Notes'}
this.updateTeacherNotesColumnUrl = '/api/v1/courses/1201/custom_gradebook_columns/2401'; this.updateTeacherNotesColumnUrl = '/api/v1/courses/1201/custom_gradebook_columns/2401'
this.server = sinon.fakeServer.create({ respondImmediately: true }); this.server = sinon.fakeServer.create({respondImmediately: true})
const responseBody = JSON.stringify(this.customColumn); const responseBody = JSON.stringify(this.customColumn)
this.server.respondWith('PUT', this.updateTeacherNotesColumnUrl, [200, { 'Content-Type': 'application/json' }, responseBody]); this.server.respondWith('PUT', this.updateTeacherNotesColumnUrl, [
200,
{'Content-Type': 'application/json'},
responseBody
])
}, },
getRequest () { getRequest() {
// filter requests to eliminate spec pollution from unrelated specs // filter requests to eliminate spec pollution from unrelated specs
return _.find(this.server.requests, request => request.url.includes(this.updateTeacherNotesColumnUrl)); return _.find(this.server.requests, request =>
request.url.includes(this.updateTeacherNotesColumnUrl)
)
}, },
teardown () { teardown() {
this.server.restore(); this.server.restore()
} }
}); })
test('sends a post request to the "create teacher notes column" url', function () { test('sends a post request to the "create teacher notes column" url', function() {
return GradebookApi.updateTeacherNotesColumn('1201', '2401', { hidden: true }) return GradebookApi.updateTeacherNotesColumn('1201', '2401', {hidden: true}).then(() => {
.then(() => { const request = this.getRequest()
const request = this.getRequest(); equal(request.method, 'PUT')
equal(request.method, 'PUT'); equal(request.url, this.updateTeacherNotesColumnUrl)
equal(request.url, this.updateTeacherNotesColumnUrl); })
}); })
});
test('includes params for updating a teacher notes column', function () { test('includes params for updating a teacher notes column', function() {
return GradebookApi.updateTeacherNotesColumn('1201', '2401', { hidden: true }) return GradebookApi.updateTeacherNotesColumn('1201', '2401', {hidden: true}).then(() => {
.then(() => { const bodyData = JSON.parse(this.getRequest().requestBody)
const bodyData = JSON.parse(this.getRequest().requestBody); equal(bodyData.column.hidden, true)
equal(bodyData.column.hidden, true); })
}); })
});
test('includes required request headers', function () { test('includes required request headers', function() {
return GradebookApi.updateTeacherNotesColumn('1201', '2401', { hidden: true }) return GradebookApi.updateTeacherNotesColumn('1201', '2401', {hidden: true}).then(() => {
.then(() => { const {requestHeaders} = this.getRequest()
const { requestHeaders } = this.getRequest(); ok(
ok(requestHeaders.Accept.includes('application/json+canvas-string-ids'), 'includes header for Canvas string ids'); requestHeaders.Accept.includes('application/json+canvas-string-ids'),
ok(requestHeaders['Content-Type'].includes('application/json'), 'includes header for Canvas string ids'
'includes "application/json" content type'); )
equal(requestHeaders['X-Requested-With'], 'XMLHttpRequest'); ok(
}); requestHeaders['Content-Type'].includes('application/json'),
}); 'includes "application/json" content type'
)
equal(requestHeaders['X-Requested-With'], 'XMLHttpRequest')
})
})
test('sends the column data to the success handler', function () { test('sends the column data to the success handler', function() {
return GradebookApi.updateTeacherNotesColumn('1201', '2401', { hidden: true }) return GradebookApi.updateTeacherNotesColumn('1201', '2401', {hidden: true}).then(({data}) => {
.then(({ data }) => { deepEqual(data, this.customColumn)
deepEqual(data, this.customColumn); })
}); })
});
QUnit.module('GradebookApi.updateSubmission', function (hooks) { QUnit.module('GradebookApi.updateSubmission', function(hooks) {
const courseId = '1201'; const courseId = '1201'
const assignmentId = '303'; const assignmentId = '303'
const userId = '201'; const userId = '201'
const updateSubmissionUrl = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`; const updateSubmissionUrl = `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`
const submissionData = { all_submissions: [{ id: 301, late_policy_status: 'none' }] }; const submissionData = {all_submissions: [{id: 301, late_policy_status: 'none'}]}
let server; let server
hooks.beforeEach(function () { hooks.beforeEach(function() {
server = sinon.fakeServer.create({ respondImmediately: true }); server = sinon.fakeServer.create({respondImmediately: true})
const responseBody = JSON.stringify(submissionData); const responseBody = JSON.stringify(submissionData)
server.respondWith('PUT', updateSubmissionUrl, [200, { 'Content-Type': 'application/json' }, responseBody]); server.respondWith('PUT', updateSubmissionUrl, [
}); 200,
{'Content-Type': 'application/json'},
responseBody
])
})
hooks.afterEach(function () { hooks.afterEach(function() {
server.restore(); server.restore()
}); })
function getRequest () { function getRequest() {
// filter requests to eliminate spec pollution from unrelated specs // filter requests to eliminate spec pollution from unrelated specs
return _.find(server.requests, request => request.url.includes(updateSubmissionUrl)); return _.find(server.requests, request => request.url.includes(updateSubmissionUrl))
} }
test('sends a put request to the "update submission" url', function () { test('sends a put request to the "update submission" url', function() {
return GradebookApi.updateSubmission(courseId, assignmentId, userId, { latePolicyStatus: 'none' }) return GradebookApi.updateSubmission(courseId, assignmentId, userId, {
.then(() => { latePolicyStatus: 'none'
const request = getRequest(); }).then(() => {
strictEqual(request.method, 'PUT'); const request = getRequest()
strictEqual(request.url, updateSubmissionUrl); strictEqual(request.method, 'PUT')
}); strictEqual(request.url, updateSubmissionUrl)
}); })
})
test('includes params for updating a submission', function () { test('includes params for updating a submission', function() {
return GradebookApi.updateSubmission(courseId, assignmentId, userId, { latePolicyStatus: 'none' }) return GradebookApi.updateSubmission(courseId, assignmentId, userId, {
.then(() => { latePolicyStatus: 'none'
const bodyData = JSON.parse(getRequest().requestBody); }).then(() => {
deepEqual(bodyData.submission.late_policy_status, 'none'); const bodyData = JSON.parse(getRequest().requestBody)
}); deepEqual(bodyData.submission.late_policy_status, 'none')
}); })
})
test('includes params to request visibility for the submission', function () { test('includes params to request visibility for the submission', function() {
return GradebookApi.updateSubmission(courseId, assignmentId, userId, { latePolicyStatus: 'none' }) return GradebookApi.updateSubmission(courseId, assignmentId, userId, {
.then(() => { latePolicyStatus: 'none'
const bodyData = JSON.parse(getRequest().requestBody); }).then(() => {
strictEqual(bodyData.include.includes('visibility'), true); const bodyData = JSON.parse(getRequest().requestBody)
}); strictEqual(bodyData.include.includes('visibility'), true)
}); })
})
test('sends the column data to the success handler', function () {
return GradebookApi.updateSubmission(courseId, assignmentId, userId, { latePolicyStatus: 'none' })
.then(({ data }) => {
deepEqual(data, submissionData);
});
});
});
test('sends the column data to the success handler', function() {
return GradebookApi.updateSubmission(courseId, assignmentId, userId, {
latePolicyStatus: 'none'
}).then(({data}) => {
deepEqual(data, submissionData)
})
})
})

View File

@ -16,14 +16,14 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import _ from 'underscore'; import _ from 'underscore'
import { import {
DEFAULT_LATE_POLICY_DATA, DEFAULT_LATE_POLICY_DATA,
fetchLatePolicy, fetchLatePolicy,
createLatePolicy, createLatePolicy,
updateLatePolicy updateLatePolicy
} from 'jsx/gradezilla/default_gradebook/apis/GradebookSettingsModalApi'; } from 'jsx/gradezilla/default_gradebook/apis/GradebookSettingsModalApi'
import { underscore } from 'convert_case'; import {underscore} from 'convert_case'
const latePolicyData = { const latePolicyData = {
id: '15', id: '15',
@ -34,128 +34,138 @@ const latePolicyData = {
lateSubmissionInterval: 'day', lateSubmissionInterval: 'day',
lateSubmissionMinimumPercentEnabled: true, lateSubmissionMinimumPercentEnabled: true,
lateSubmissionMinimumPercent: 40.0 lateSubmissionMinimumPercent: 40.0
}; }
function getRequestWithUrl (server, url) { function getRequestWithUrl(server, url) {
// filter requests to eliminate spec pollution from unrelated specs // filter requests to eliminate spec pollution from unrelated specs
return _.find(server.requests, request => request.url.includes(url)); return _.find(server.requests, request => request.url.includes(url))
} }
QUnit.module('GradebookSettingsModalApi.fetchLatePolicy success', { QUnit.module('GradebookSettingsModalApi.fetchLatePolicy success', {
setup () { setup() {
this.url = '/api/v1/courses/19/late_policy'; this.url = '/api/v1/courses/19/late_policy'
this.server = sinon.fakeServer.create({ respondImmediately: true }); this.server = sinon.fakeServer.create({respondImmediately: true})
const responseBody = JSON.stringify({ late_policy: underscore(latePolicyData) }); const responseBody = JSON.stringify({late_policy: underscore(latePolicyData)})
this.server.respondWith('GET', this.url, [200, { 'Content-Type': 'application/json' }, responseBody]); this.server.respondWith('GET', this.url, [
200,
{'Content-Type': 'application/json'},
responseBody
])
}, },
teardown () { teardown() {
this.server.restore(); this.server.restore()
} }
}); })
test('returns the late policy', function () { test('returns the late policy', function() {
return fetchLatePolicy('19') return fetchLatePolicy('19').then(({data}) => {
.then(({ data }) => { deepEqual(data, {latePolicy: latePolicyData})
deepEqual(data, { latePolicy: latePolicyData }); })
}); })
});
QUnit.module('GradebookSettingsModalApi.fetchLatePolicy when late policy does not exist', { QUnit.module('GradebookSettingsModalApi.fetchLatePolicy when late policy does not exist', {
setup () { setup() {
this.url = '/api/v1/courses/19/late_policy'; this.url = '/api/v1/courses/19/late_policy'
this.server = sinon.fakeServer.create({ respondImmediately: true }); this.server = sinon.fakeServer.create({respondImmediately: true})
const responseBody = JSON.stringify( const responseBody = JSON.stringify({
{ errors: [{ message: 'The specified resource does not exist.' }], error_report_id: '2199' } errors: [{message: 'The specified resource does not exist.'}],
); error_report_id: '2199'
this.server.respondWith('GET', this.url, [404, { 'Content-Type': 'application/json' }, responseBody]); })
this.server.respondWith('GET', this.url, [
404,
{'Content-Type': 'application/json'},
responseBody
])
}, },
teardown () { teardown() {
this.server.restore(); this.server.restore()
} }
}); })
test('returns default late policy data when the response is a 404', function () { test('returns default late policy data when the response is a 404', function() {
return fetchLatePolicy('19') return fetchLatePolicy('19').then(({data}) => {
.then(({ data }) => { deepEqual(data, {latePolicy: DEFAULT_LATE_POLICY_DATA})
deepEqual(data, { latePolicy: DEFAULT_LATE_POLICY_DATA }); })
}); })
});
QUnit.module('GradebookSettingsModalApi.fetchLatePolicy when the request fails', { QUnit.module('GradebookSettingsModalApi.fetchLatePolicy when the request fails', {
setup () { setup() {
this.url = '/api/v1/courses/19/late_policy'; this.url = '/api/v1/courses/19/late_policy'
this.server = sinon.fakeServer.create({ respondImmediately: true }); this.server = sinon.fakeServer.create({respondImmediately: true})
this.server.respondWith('GET', this.url, [500, { 'Content-Type': 'application/json' }, JSON.stringify({})]); this.server.respondWith('GET', this.url, [
500,
{'Content-Type': 'application/json'},
JSON.stringify({})
])
}, },
teardown () { teardown() {
this.server.restore(); this.server.restore()
} }
}); })
test('rejects the promise when the response is not a 200 or a 404', function () { test('rejects the promise when the response is not a 200 or a 404', function() {
return fetchLatePolicy('19') return fetchLatePolicy('19').catch(error => {
.catch((error) => { strictEqual(error.response.status, 500)
strictEqual(error.response.status, 500); })
}); })
});
QUnit.module('GradebookSettingsModalApi.createLatePolicy', { QUnit.module('GradebookSettingsModalApi.createLatePolicy', {
setup () { setup() {
this.latePolicyCreationData = { ...latePolicyData }; this.latePolicyCreationData = {...latePolicyData}
delete this.latePolicyCreationData.id; delete this.latePolicyCreationData.id
this.url = '/api/v1/courses/19/late_policy'; this.url = '/api/v1/courses/19/late_policy'
this.server = sinon.fakeServer.create({ respondImmediately: true }); this.server = sinon.fakeServer.create({respondImmediately: true})
const responseBody = JSON.stringify({ late_policy: underscore(latePolicyData) }); const responseBody = JSON.stringify({late_policy: underscore(latePolicyData)})
this.server.respondWith('POST', this.url, [200, { 'Content-Type': 'application/json' }, responseBody]); this.server.respondWith('POST', this.url, [
200,
{'Content-Type': 'application/json'},
responseBody
])
}, },
teardown () { teardown() {
this.server.restore(); this.server.restore()
} }
}); })
test('includes data to create a late_policy', function () { test('includes data to create a late_policy', function() {
return createLatePolicy('19', latePolicyData) return createLatePolicy('19', latePolicyData).then(() => {
.then(() => { const bodyData = JSON.parse(getRequestWithUrl(this.server, this.url).requestBody)
const bodyData = JSON.parse(getRequestWithUrl(this.server, this.url).requestBody); deepEqual(bodyData, {late_policy: underscore(latePolicyData)})
deepEqual(bodyData, { late_policy: underscore(latePolicyData) }); })
}); })
});
test('returns the late policy', function () { test('returns the late policy', function() {
return createLatePolicy('19', this.latePolicyCreationData) return createLatePolicy('19', this.latePolicyCreationData).then(({data}) => {
.then(({ data }) => { deepEqual(data, {latePolicy: latePolicyData})
deepEqual(data, { latePolicy: latePolicyData }); })
}); })
});
QUnit.module('GradebookSettingsModalApi.updateLatePolicy', { QUnit.module('GradebookSettingsModalApi.updateLatePolicy', {
setup () { setup() {
this.url = '/api/v1/courses/19/late_policy'; this.url = '/api/v1/courses/19/late_policy'
this.changes = { lateSubmissionInterval: 'hour' }; this.changes = {lateSubmissionInterval: 'hour'}
this.server = sinon.fakeServer.create({ respondImmediately: true }); this.server = sinon.fakeServer.create({respondImmediately: true})
this.server.respondWith('PATCH', this.url, [204, {}, '']); this.server.respondWith('PATCH', this.url, [204, {}, ''])
}, },
teardown () { teardown() {
this.server.restore(); this.server.restore()
} }
}); })
test('includes data to update a late_policy', function () { test('includes data to update a late_policy', function() {
return updateLatePolicy('19', this.changes) return updateLatePolicy('19', this.changes).then(() => {
.then(() => { const bodyData = JSON.parse(getRequestWithUrl(this.server, this.url).requestBody)
const bodyData = JSON.parse(getRequestWithUrl(this.server, this.url).requestBody); deepEqual(bodyData, {late_policy: underscore(this.changes)})
deepEqual(bodyData, { late_policy: underscore(this.changes) }); })
}); })
});
test('returns a 204 (successfully fulfilled request and no content)', function () { test('returns a 204 (successfully fulfilled request and no content)', function() {
return updateLatePolicy('19', this.changes) return updateLatePolicy('19', this.changes).then(({status}) => {
.then(({ status }) => { equal(status, 204)
equal(status, 204); })
}); })
});

View File

@ -16,49 +16,49 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { updateSubmissionComment } from 'jsx/gradezilla/default_gradebook/apis/SubmissionCommentApi'; import {updateSubmissionComment} from 'jsx/gradezilla/default_gradebook/apis/SubmissionCommentApi'
import { underscore } from 'convert_case'; import {underscore} from 'convert_case'
QUnit.module('SubmissionCommentApi.updateSubmissionComment', function (hooks) { QUnit.module('SubmissionCommentApi.updateSubmissionComment', function(hooks) {
let server; let server
const commentId = '12'; const commentId = '12'
const url = `/submission_comments/${commentId}`; const url = `/submission_comments/${commentId}`
const updatedComment = 'an updated comment!'; const updatedComment = 'an updated comment!'
const editedAt = '2015-10-12T19:25:41Z'; const editedAt = '2015-10-12T19:25:41Z'
const submissionComment = { const submissionComment = {
id: commentId, id: commentId,
created_at: '2015-10-09T19:25:41Z', created_at: '2015-10-09T19:25:41Z',
comment: updatedComment, comment: updatedComment,
edited_at: editedAt edited_at: editedAt
}; }
const responseBody = JSON.stringify({ submission_comment: underscore(submissionComment) }); const responseBody = JSON.stringify({submission_comment: underscore(submissionComment)})
hooks.beforeEach(function () { hooks.beforeEach(function() {
server = sinon.fakeServer.create({ respondImmediately: true }); server = sinon.fakeServer.create({respondImmediately: true})
}); })
hooks.afterEach(function () { hooks.afterEach(function() {
server.restore(); server.restore()
}); })
test('on success, returns the submission comment with the updated comment', function () { test('on success, returns the submission comment with the updated comment', function() {
server.respondWith('PUT', url, [200, { 'Content-Type': 'application/json' }, responseBody]); server.respondWith('PUT', url, [200, {'Content-Type': 'application/json'}, responseBody])
return updateSubmissionComment(commentId, updatedComment).then((response) => { return updateSubmissionComment(commentId, updatedComment).then(response => {
strictEqual(response.data.comment, updatedComment); strictEqual(response.data.comment, updatedComment)
}); })
}); })
test('on success, returns the submission comment with an updated editedAt', function () { test('on success, returns the submission comment with an updated editedAt', function() {
server.respondWith('PUT', url, [200, { 'Content-Type': 'application/json' }, responseBody]); server.respondWith('PUT', url, [200, {'Content-Type': 'application/json'}, responseBody])
return updateSubmissionComment(commentId, updatedComment).then((response) => { return updateSubmissionComment(commentId, updatedComment).then(response => {
strictEqual(response.data.editedAt.getTime(), new Date(editedAt).getTime()); strictEqual(response.data.editedAt.getTime(), new Date(editedAt).getTime())
}); })
}); })
test('on failure, returns a rejected promise with the error', function () { test('on failure, returns a rejected promise with the error', function() {
server.respondWith('PUT', url, [500, { 'Content-Type': 'application/json' }, JSON.stringify({})]); server.respondWith('PUT', url, [500, {'Content-Type': 'application/json'}, JSON.stringify({})])
return updateSubmissionComment(commentId, updatedComment).catch((error) => { return updateSubmissionComment(commentId, updatedComment).catch(error => {
strictEqual(error.response.status, 500); strictEqual(error.response.status, 500)
}); })
}); })
}); })

View File

@ -17,7 +17,10 @@
*/ */
import DataLoader from 'jsx/gradezilla/DataLoader' import DataLoader from 'jsx/gradezilla/DataLoader'
import {createGradebook, setFixtureHtml} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper' import {
createGradebook,
setFixtureHtml
} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper'
import {createExampleStudents} from './DataLoadingSpecHelpers' import {createExampleStudents} from './DataLoadingSpecHelpers'
import DataLoadingWrapper from './DataLoadingWrapper' import DataLoadingWrapper from './DataLoadingWrapper'

View File

@ -17,7 +17,10 @@
*/ */
import DataLoader from 'jsx/gradezilla/DataLoader' import DataLoader from 'jsx/gradezilla/DataLoader'
import {createGradebook, setFixtureHtml} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper' import {
createGradebook,
setFixtureHtml
} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper'
import {createExampleStudents} from './DataLoadingSpecHelpers' import {createExampleStudents} from './DataLoadingSpecHelpers'
import DataLoadingWrapper from './DataLoadingWrapper' import DataLoadingWrapper from './DataLoadingWrapper'

View File

@ -17,7 +17,10 @@
*/ */
import DataLoader from 'jsx/gradezilla/DataLoader' import DataLoader from 'jsx/gradezilla/DataLoader'
import {createGradebook, setFixtureHtml} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper' import {
createGradebook,
setFixtureHtml
} from 'jsx/gradezilla/default_gradebook/__tests__/GradebookSpecHelper'
import {createExampleStudents} from './DataLoadingSpecHelpers' import {createExampleStudents} from './DataLoadingSpecHelpers'
import DataLoadingWrapper from './DataLoadingWrapper' import DataLoadingWrapper from './DataLoadingWrapper'

View File

@ -16,94 +16,96 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import _ from 'lodash'; import _ from 'lodash'
import StudentDatastore from 'jsx/gradezilla/default_gradebook/stores/StudentDatastore'; import StudentDatastore from 'jsx/gradezilla/default_gradebook/stores/StudentDatastore'
QUnit.module('StudentDatastore', function (hooks) { QUnit.module('StudentDatastore', function(hooks) {
let studentDatastore; let studentDatastore
let userStudentMap; let userStudentMap
let testStudentMap; let testStudentMap
hooks.beforeEach(function () { hooks.beforeEach(function() {
userStudentMap = {}; userStudentMap = {}
testStudentMap = {}; testStudentMap = {}
studentDatastore = new StudentDatastore(userStudentMap, testStudentMap); studentDatastore = new StudentDatastore(userStudentMap, testStudentMap)
}); })
QUnit.module('#listStudentIds'); QUnit.module('#listStudentIds')
test('returns the definitive list of known students', function () { test('returns the definitive list of known students', function() {
const studentIds = ['1101', '1102', '1103']; const studentIds = ['1101', '1102', '1103']
studentDatastore.setStudentIds(studentIds); studentDatastore.setStudentIds(studentIds)
const storedStudentIds = studentDatastore.listStudentIds(); const storedStudentIds = studentDatastore.listStudentIds()
strictEqual(storedStudentIds.length, 3, 'datastore contains 3 students'); strictEqual(storedStudentIds.length, 3, 'datastore contains 3 students')
deepEqual(storedStudentIds, studentIds); deepEqual(storedStudentIds, studentIds)
}); })
QUnit.module('#setStudentIds'); QUnit.module('#setStudentIds')
test('removes stored user students not represented in the list of student ids', function () { test('removes stored user students not represented in the list of student ids', function() {
const students = [{ id: '1103' }, { id: '1101' }, { id: '1102' }]; const students = [{id: '1103'}, {id: '1101'}, {id: '1102'}]
studentDatastore.addUserStudents(students); studentDatastore.addUserStudents(students)
studentDatastore.setStudentIds(['1102']); studentDatastore.setStudentIds(['1102'])
const storedStudents = studentDatastore.listStudents(); const storedStudents = studentDatastore.listStudents()
strictEqual(storedStudents.length, 1, 'datastore contains 1 student'); strictEqual(storedStudents.length, 1, 'datastore contains 1 student')
equal(storedStudents[0].id, '1102'); equal(storedStudents[0].id, '1102')
}); })
test('removes stored test students not represented in the list of student ids', function () { test('removes stored test students not represented in the list of student ids', function() {
const students = [{ id: '1103' }, { id: '1101' }, { id: '1102' }]; const students = [{id: '1103'}, {id: '1101'}, {id: '1102'}]
studentDatastore.addTestStudents(students); studentDatastore.addTestStudents(students)
studentDatastore.setStudentIds(['1102']); studentDatastore.setStudentIds(['1102'])
const storedStudents = studentDatastore.listStudents(); const storedStudents = studentDatastore.listStudents()
strictEqual(storedStudents.length, 1, 'datastore contains 1 student'); strictEqual(storedStudents.length, 1, 'datastore contains 1 student')
equal(storedStudents[0].id, '1102'); equal(storedStudents[0].id, '1102')
}); })
QUnit.module('#listStudents'); QUnit.module('#listStudents')
test('returns the students stored in order of the saved student ids', function () { test('returns the students stored in order of the saved student ids', function() {
const students = [{ id: '1103' }, { id: '1101' }, { id: '1102' }]; const students = [{id: '1103'}, {id: '1101'}, {id: '1102'}]
studentDatastore.addUserStudents(students); studentDatastore.addUserStudents(students)
studentDatastore.setStudentIds(['1101', '1102', '1103']); studentDatastore.setStudentIds(['1101', '1102', '1103'])
const storedStudents = studentDatastore.listStudents(); const storedStudents = studentDatastore.listStudents()
strictEqual(storedStudents.length, 3, 'datastore contains 3 students'); strictEqual(storedStudents.length, 3, 'datastore contains 3 students')
deepEqual(storedStudents, _.sortBy(students, 'id')); deepEqual(storedStudents, _.sortBy(students, 'id'))
}); })
test('includes test students', function () { test('includes test students', function() {
const students = [{ id: '1103' }, { id: '1101' }, { id: '1102' }]; const students = [{id: '1103'}, {id: '1101'}, {id: '1102'}]
studentDatastore.addUserStudents(students.slice(0, 2)); studentDatastore.addUserStudents(students.slice(0, 2))
studentDatastore.addTestStudents(students.slice(2, 3)); studentDatastore.addTestStudents(students.slice(2, 3))
studentDatastore.setStudentIds(['1101', '1102', '1103']); studentDatastore.setStudentIds(['1101', '1102', '1103'])
const storedStudents = studentDatastore.listStudents(); const storedStudents = studentDatastore.listStudents()
strictEqual(storedStudents.length, 3, 'datastore contains 3 students'); strictEqual(storedStudents.length, 3, 'datastore contains 3 students')
deepEqual(storedStudents, _.sortBy(students, 'id')); deepEqual(storedStudents, _.sortBy(students, 'id'))
}); })
test('includes students stored directly into the original userStudentMap', function () { test('includes students stored directly into the original userStudentMap', function() {
studentDatastore.setStudentIds(['1101', '1102', '1103']); studentDatastore.setStudentIds(['1101', '1102', '1103'])
const students = [{ id: '1103' }, { id: '1101' }, { id: '1102' }]; const students = [{id: '1103'}, {id: '1101'}, {id: '1102'}]
Object.assign(userStudentMap, _.keyBy(students, 'id')); Object.assign(userStudentMap, _.keyBy(students, 'id'))
const storedStudents = studentDatastore.listStudents(); const storedStudents = studentDatastore.listStudents()
strictEqual(storedStudents.length, 3, 'datastore contains 3 students'); strictEqual(storedStudents.length, 3, 'datastore contains 3 students')
deepEqual(storedStudents, _.sortBy(students, 'id')); deepEqual(storedStudents, _.sortBy(students, 'id'))
}); })
test('includes students stored directly into the original testStudentMap', function () { test('includes students stored directly into the original testStudentMap', function() {
studentDatastore.setStudentIds(['1101', '1102', '1103']); studentDatastore.setStudentIds(['1101', '1102', '1103'])
const students = [{ id: '1103' }, { id: '1101' }, { id: '1102' }]; const students = [{id: '1103'}, {id: '1101'}, {id: '1102'}]
Object.assign(testStudentMap, _.keyBy(students, 'id')); Object.assign(testStudentMap, _.keyBy(students, 'id'))
const storedStudents = studentDatastore.listStudents(); const storedStudents = studentDatastore.listStudents()
strictEqual(storedStudents.length, 3, 'datastore contains 3 students'); strictEqual(storedStudents.length, 3, 'datastore contains 3 students')
deepEqual(storedStudents, _.sortBy(students, 'id')); deepEqual(storedStudents, _.sortBy(students, 'id'))
}); })
test('includes placeholder students for student ids not matching a stored student object', function () { test('includes placeholder students for student ids not matching a stored student object', function() {
const students = [{ id: '1103' }, { id: '1101' }]; const students = [{id: '1103'}, {id: '1101'}]
studentDatastore.addUserStudents(students); studentDatastore.addUserStudents(students)
studentDatastore.setStudentIds(['1101', '1102', '1103']); studentDatastore.setStudentIds(['1101', '1102', '1103'])
const placeholderStudent = studentDatastore.listStudents().find(student => student.id === '1102'); const placeholderStudent = studentDatastore
strictEqual(placeholderStudent.isPlaceholder, true); .listStudents()
}); .find(student => student.id === '1102')
}); strictEqual(placeholderStudent.isPlaceholder, true)
})
})

View File

@ -31,11 +31,7 @@ QUnit.module('AssignmentMuterDialogManager', suiteHooks => {
}) })
function createManager() { function createManager() {
return new AssignmentMuterDialogManager( return new AssignmentMuterDialogManager(assignment, url, submissionsLoaded)
assignment,
url,
submissionsLoaded
)
} }
QUnit.module('#assignment', () => { QUnit.module('#assignment', () => {

View File

@ -16,82 +16,82 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import * as EnterGradesAsSetting from 'jsx/gradezilla/shared/EnterGradesAsSetting'; import * as EnterGradesAsSetting from 'jsx/gradezilla/shared/EnterGradesAsSetting'
QUnit.module('EnterGradesAsSetting', () => { QUnit.module('EnterGradesAsSetting', () => {
QUnit.module('.defaultOptionForGradingType', () => { QUnit.module('.defaultOptionForGradingType', () => {
test('is "points" for the "points" grading type', () => { test('is "points" for the "points" grading type', () => {
equal(EnterGradesAsSetting.defaultOptionForGradingType('points'), 'points'); equal(EnterGradesAsSetting.defaultOptionForGradingType('points'), 'points')
}); })
test('is "percent" for the "percent" grading type', () => { test('is "percent" for the "percent" grading type', () => {
equal(EnterGradesAsSetting.defaultOptionForGradingType('percent'), 'percent'); equal(EnterGradesAsSetting.defaultOptionForGradingType('percent'), 'percent')
}); })
test('is "gradingScheme" for the "gpa_scale" grading type', () => { test('is "gradingScheme" for the "gpa_scale" grading type', () => {
equal(EnterGradesAsSetting.defaultOptionForGradingType('gpa_scale'), 'gradingScheme'); equal(EnterGradesAsSetting.defaultOptionForGradingType('gpa_scale'), 'gradingScheme')
}); })
test('is "gradingScheme" for the "letter_grade" grading type', () => { test('is "gradingScheme" for the "letter_grade" grading type', () => {
equal(EnterGradesAsSetting.defaultOptionForGradingType('letter_grade'), 'gradingScheme'); equal(EnterGradesAsSetting.defaultOptionForGradingType('letter_grade'), 'gradingScheme')
}); })
test('is "passFail" for the "pass_fail" grading type', () => { test('is "passFail" for the "pass_fail" grading type', () => {
equal(EnterGradesAsSetting.defaultOptionForGradingType('pass_fail'), 'passFail'); equal(EnterGradesAsSetting.defaultOptionForGradingType('pass_fail'), 'passFail')
}); })
test('does not exist for the "not_graded" grading type', () => { test('does not exist for the "not_graded" grading type', () => {
strictEqual(EnterGradesAsSetting.defaultOptionForGradingType('not_graded'), null); strictEqual(EnterGradesAsSetting.defaultOptionForGradingType('not_graded'), null)
}); })
}); })
QUnit.module('.optionsForGradingType', () => { QUnit.module('.optionsForGradingType', () => {
test('includes "points" for the "points" grading type', () => { test('includes "points" for the "points" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('points').includes('points')); ok(EnterGradesAsSetting.optionsForGradingType('points').includes('points'))
}); })
test('includes "percent" for the "points" grading type', () => { test('includes "percent" for the "points" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('points').includes('percent')); ok(EnterGradesAsSetting.optionsForGradingType('points').includes('percent'))
}); })
test('includes "points" for the "percent" grading type', () => { test('includes "points" for the "percent" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('percent').includes('points')); ok(EnterGradesAsSetting.optionsForGradingType('percent').includes('points'))
}); })
test('includes "percent" for the "percent" grading type', () => { test('includes "percent" for the "percent" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('percent').includes('percent')); ok(EnterGradesAsSetting.optionsForGradingType('percent').includes('percent'))
}); })
test('includes "points" for the "gpa_scale" grading type', () => { test('includes "points" for the "gpa_scale" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('gpa_scale').includes('points')); ok(EnterGradesAsSetting.optionsForGradingType('gpa_scale').includes('points'))
}); })
test('includes "percent" for the "gpa_scale" grading type', () => { test('includes "percent" for the "gpa_scale" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('gpa_scale').includes('percent')); ok(EnterGradesAsSetting.optionsForGradingType('gpa_scale').includes('percent'))
}); })
test('includes "gradingScheme" for the "gpa_scale" grading type', () => { test('includes "gradingScheme" for the "gpa_scale" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('gpa_scale').includes('gradingScheme')); ok(EnterGradesAsSetting.optionsForGradingType('gpa_scale').includes('gradingScheme'))
}); })
test('includes "points" for the "letter_grade" grading type', () => { test('includes "points" for the "letter_grade" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('letter_grade').includes('points')); ok(EnterGradesAsSetting.optionsForGradingType('letter_grade').includes('points'))
}); })
test('includes "percent" for the "letter_grade" grading type', () => { test('includes "percent" for the "letter_grade" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('letter_grade').includes('percent')); ok(EnterGradesAsSetting.optionsForGradingType('letter_grade').includes('percent'))
}); })
test('includes "gradingScheme" for the "letter_grade" grading type', () => { test('includes "gradingScheme" for the "letter_grade" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('letter_grade').includes('gradingScheme')); ok(EnterGradesAsSetting.optionsForGradingType('letter_grade').includes('gradingScheme'))
}); })
test('includes "passFail" for the "pass_fail" grading type', () => { test('includes "passFail" for the "pass_fail" grading type', () => {
ok(EnterGradesAsSetting.optionsForGradingType('pass_fail').includes('passFail')); ok(EnterGradesAsSetting.optionsForGradingType('pass_fail').includes('passFail'))
}); })
test('do not exist for the "not_graded" grading type', () => { test('do not exist for the "not_graded" grading type', () => {
deepEqual(EnterGradesAsSetting.optionsForGradingType('not_graded'), []); deepEqual(EnterGradesAsSetting.optionsForGradingType('not_graded'), [])
}); })
}); })
}); })

View File

@ -16,49 +16,49 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import TextMeasure from 'jsx/gradezilla/shared/helpers/TextMeasure'; import TextMeasure from 'jsx/gradezilla/shared/helpers/TextMeasure'
QUnit.module('TextMeasure', function (hooks) { QUnit.module('TextMeasure', function(hooks) {
let $fixture; let $fixture
hooks.beforeEach(function () { hooks.beforeEach(function() {
$fixture = document.createElement('div'); $fixture = document.createElement('div')
document.body.appendChild($fixture); document.body.appendChild($fixture)
$fixture.innerHTML = '<div id="content"></div>'; $fixture.innerHTML = '<div id="content"></div>'
}); })
hooks.afterEach(function () { hooks.afterEach(function() {
$fixture.remove(); $fixture.remove()
}); })
QUnit.module('getWidth', function () { QUnit.module('getWidth', function() {
test('returns a numerical width for the given text', function () { test('returns a numerical width for the given text', function() {
const width = TextMeasure.getWidth('example'); const width = TextMeasure.getWidth('example')
equal(typeof width, 'number'); equal(typeof width, 'number')
}); })
test('returns integers', function () { test('returns integers', function() {
const width = TextMeasure.getWidth('example'); const width = TextMeasure.getWidth('example')
strictEqual(width, Math.floor(width)); strictEqual(width, Math.floor(width))
}); })
test('returns larger numbers for wider text', function () { test('returns larger numbers for wider text', function() {
const orderedWords = ['a', 'aa', 'aaa']; const orderedWords = ['a', 'aa', 'aaa']
const orderedWidths = ['aaa', 'a', 'aa'].map(TextMeasure.getWidth).sort(); const orderedWidths = ['aaa', 'a', 'aa'].map(TextMeasure.getWidth).sort()
deepEqual(orderedWidths, orderedWords.map(TextMeasure.getWidth)); deepEqual(orderedWidths, orderedWords.map(TextMeasure.getWidth))
}); })
test('creates a "text-measure" element attached to the "content" element', function () { test('creates a "text-measure" element attached to the "content" element', function() {
TextMeasure.getWidth('example'); TextMeasure.getWidth('example')
const $textMeasure = document.getElementById('text-measure'); const $textMeasure = document.getElementById('text-measure')
equal($textMeasure.parentElement, document.getElementById('content')); equal($textMeasure.parentElement, document.getElementById('content'))
}); })
test('creates only one "text-measure" element', function () { test('creates only one "text-measure" element', function() {
TextMeasure.getWidth('example'); TextMeasure.getWidth('example')
TextMeasure.getWidth('sample'); TextMeasure.getWidth('sample')
strictEqual(document.getElementById('content').children.length, 1); strictEqual(document.getElementById('content').children.length, 1)
}); })
}); })
}); })