Merge "Merge branch 'dev/mgp-v2'"

This commit is contained in:
Gerrit Code Review 2016-09-23 17:08:38 +00:00
commit d9751edf53
72 changed files with 4631 additions and 1720 deletions

View File

@ -23,12 +23,9 @@ define [
else
null
isInPastGradingPeriodAndNotAdmin = ((assignment) ->
GradebookHelpers.gradeIsLocked(assignment, ENV)
)(@assignment)
@url = @options.change_grade_url.replace(":assignment", @assignment.id).replace(":submission", @student.id)
@submission = $.extend {}, @student["assignment_#{@assignment.id}"],
submission = @student["assignment_#{@assignment.id}"]
@submission = $.extend {}, submission,
label: "student_grading_#{@assignment.id}"
inputName: 'submission[posted_grade]'
assignment: @assignment
@ -36,7 +33,7 @@ define [
loading: true
showPointsPossible: (@assignment.points_possible || @assignment.points_possible == '0') && @assignment.grading_type != "gpa_scale"
shouldShowExcusedOption: true
isInPastGradingPeriodAndNotAdmin: isInPastGradingPeriodAndNotAdmin
isInPastGradingPeriodAndNotAdmin: submission.gradeLocked
@submission["assignment_grading_type_is_#{@assignment.grading_type}"] = true
@submission.grade = "EX" if @submission.excused
@$el = $('<div class="use-css-transitions-for-show-hide" style="padding:0;"/>')

View File

@ -27,6 +27,9 @@ define [
title: period.title
startDate: new Date(period.start_date)
endDate: new Date(period.end_date)
# TODO: After the close_date data fixup has run, this can become:
# `closeDate: new Date(period.close_date)`
closeDate: new Date(period.close_date || period.end_date)
}
baseDeserializeSet = (set) ->

View File

@ -14,6 +14,7 @@ define [
title: period.title
start_date: period.startDate
end_date: period.endDate
close_date: period.closeDate
}
grading_periods: serialized
@ -24,6 +25,9 @@ define [
title: period.title
startDate: new Date(period.start_date)
endDate: new Date(period.end_date)
# TODO: After the close_date data fixup has run, this can become:
# `closeDate: new Date(period.close_date)`
closeDate: new Date(period.close_date || period.end_date)
}
batchUpdate: (setId, periods) ->

View File

@ -19,9 +19,9 @@ define [
isPercent: Ember.computed.equal('assignment.grading_type', 'percent')
isLetterGrade: Ember.computed.equal('assignment.grading_type', 'letter_grade')
isPassFail: Ember.computed.equal('assignment.grading_type', 'pass_fail')
isInPastGradingPeriodAndNotAdmin: ( ->
GradebookHelpers.gradeIsLocked(@assignment, ENV)
).property('assignment')
isInPastGradingPeriodAndNotAdmin: (->
@submission?.gradeLocked
).property('submission')
nilPointsPossible: Ember.computed.none('assignment.points_possible')
isGpaScale: Ember.computed.equal('assignment.grading_type', 'gpa_scale')

View File

@ -15,8 +15,9 @@ define [
'../../shared/components/ic_submission_download_dialog_component'
'str/htmlEscape'
'compiled/models/grade_summary/CalculationMethodContent'
'jsx/gradebook/SubmissionStateMap'
'jquery.instructure_date_and_time'
], (ajax, round, userSettings, fetchAllPages, parseLinkHeader, I18n, Ember, _, tz, AssignmentDetailsDialog, AssignmentMuter, GradeCalculator, outcomeGrid, ic_submission_download_dialog, htmlEscape, CalculationMethodContent) ->
], (ajax, round, userSettings, fetchAllPages, parseLinkHeader, I18n, Ember, _, tz, AssignmentDetailsDialog, AssignmentMuter, GradeCalculator, outcomeGrid, ic_submission_download_dialog, htmlEscape, CalculationMethodContent, SubmissionStateMap) ->
{get, set, setProperties} = Ember
@ -25,6 +26,9 @@ define [
# http://emberjs.com/api/classes/Ember.ArrayController.html
# http://emberjs.com/api/classes/Ember.ObjectController.html
gradingPeriodIsClosed = (gradingPeriod) ->
new Date(gradingPeriod.close_date) < new Date()
studentsUniqByEnrollments = (args...)->
hiddenNameCounter = 1
options =
@ -234,23 +238,14 @@ define [
Ember.$.subscribe 'submissions_updated', _.bind(@updateSubmissionsFromExternal, this)
).on('init')
setupAssignmentGroupsChange: (->
Ember.$.subscribe 'assignment_group_weights_changed', _.bind(@checkWeightingScheme, this)
setupAssignmentWeightingScheme: (->
@set 'weightingScheme', ENV.GRADEBOOK_OPTIONS.group_weighting_scheme
).on('init')
willDestroy: ->
Ember.$.unsubscribe 'submissions_updated'
Ember.$.unsubscribe 'assignment_group_weights_changed'
@_super()
checkWeightingScheme: ({assignmentGroups})->
ags = @get('assignment_groups')
ags.clear()
ags.pushObjects assignmentGroups
@set 'weightingScheme', ENV.GRADEBOOK_OPTIONS.group_weighting_scheme
updateSubmissionsFromExternal: (submissions) ->
subs_proxy = @get('submissions')
selected = @get('selectedSubmission')
@ -308,6 +303,17 @@ define [
assignmentSelectDefaultLabel: I18n.t "no_assignment", "No Assignment Selected"
outcomeSelectDefaultLabel: I18n.t "no_outcome", "No Outcome Selected"
submissionStateMap: (
periods = _.map get(window, 'ENV.GRADEBOOK_OPTIONS.active_grading_periods'), (gradingPeriod) =>
_.extend({}, gradingPeriod, closed: gradingPeriodIsClosed(gradingPeriod))
new SubmissionStateMap(
gradingPeriodsEnabled: !!get(window, 'ENV.GRADEBOOK_OPTIONS.multiple_grading_periods_enabled')
selectedGradingPeriodID: '0'
gradingPeriods: periods
isAdmin: ENV.current_user_roles && _.contains(ENV.current_user_roles, "admin")
)
)
assignment_groups: []
fetchAssignmentGroups: (->
@ -607,6 +613,11 @@ define [
assignmentsProxy.addObject as
).observes('assignment_groups', 'assignment_groups.@each')
populateSubmissionStateMap: (->
return unless @get('enrollments.isLoaded') && @get('assignment_groups.isLoaded')
@submissionStateMap.setup(@get('students').toArray(), @get('assignments').toArray())
).observes('enrollments.isLoaded', 'assignment_groups.isLoaded')
includeUngradedAssignments: (->
userSettings.contextGet('include_ungraded_assignments') or false
).property().volatile()
@ -672,18 +683,20 @@ define [
if arguments.length > 1
@set 'selectedStudent', @get('students').findBy('id', selectedSubmission.user_id)
@set 'selectedAssignment', @get('assignments').findBy('id', selectedSubmission.assignment_id)
selectedSubmission
else
return null unless @get('selectedStudent')? and @get('selectedAssignment')?
student = @get 'selectedStudent'
assignment = @get 'selectedAssignment'
sub = get student, "assignment_#{assignment.id}"
sub or {
selectedSubmission = sub or {
user_id: student.id
assignment_id: assignment.id
hidden: !@differentiatedAssignmentVisibleToStudent(assignment, student.id)
grade_matches_current_submission: true
}
submissionState = @submissionStateMap.getSubmissionState(selectedSubmission) || {}
selectedSubmission.gradeLocked = submissionState.locked
selectedSubmission
).property('selectedStudent', 'selectedAssignment')
selectedSubmissionHidden: (->

View File

@ -73,17 +73,6 @@
{{!-- Intentionally left empty so this scales to smaller screens --}}
</div>
<div class="span8">
<div class="pad-box bottom-only">
<button
id="ag_weights"
class="btn"
{{ action 'openDialog' 'ag_weights' target=view }}
{{ bind-attr disabled=assignment_groups.isLoading }}
>
{{#t 'set_group_weights'}}Set Group Weights{{/t}}
</button>
</div>
<div class="pad-box bottom-only">
<button class="btn" {{action "exportGradebookCsv"}} id="gradebook-export">
<i class="icon-download"></i> {{#t 'download_current_scores'}}Download Current Scores (.csv){{/t}}

View File

@ -29,6 +29,7 @@ define [
run =>
@submission = Ember.Object.create
grade: 'A'
gradeLocked: false
assignment_id: 1
user_id: 1
@assignment = Ember.Object.create
@ -48,10 +49,8 @@ define [
test "setting value on init", ->
component = App.GradingCellComponent.create()
equal(component.get('value'), '-')
equal(@component.get('value'), 'A')
test "saveURL", ->
equal(@component.get('saveURL'), "/api/v1/assignment/1/1")
@ -67,36 +66,12 @@ define [
setType 'letter_grade'
ok @component.get('isLetterGrade')
test "isInPastGradingPeriodAndNotAdmin is false when multiple grading periods are not enabled", ->
ENV.GRADEBOOK_OPTIONS.multiple_grading_periods_enabled = false
equal @component.get('isInPastGradingPeriodAndNotAdmin'), false
test "isInPastGradingPeriodAndNotAdmin is false when no grading periods are in the past", ->
ENV.GRADEBOOK_OPTIONS.latest_end_date_of_admin_created_grading_periods_in_the_past = null
equal @component.get('isInPastGradingPeriodAndNotAdmin'), false
test "isInPastGradingPeriodAndNotAdmin is false when current user roles are undefined", ->
ENV.current_user_roles = null
equal @component.get('isInPastGradingPeriodAndNotAdmin'), false
test "isInPastGradingPeriodAndNotAdmin is false when the current user is an admin", ->
ENV.current_user_roles = ['admin']
equal @component.get('isInPastGradingPeriodAndNotAdmin'), false
test "isInPastGradingPeriodAndNotAdmin is true for assignments in the previous grading period", ->
run => @assignment.set('due_at', tz.parse("2013-10-01T09:59:00Z"))
test "isInPastGradingPeriodAndNotAdmin is true when the submission is gradeLocked", ->
run => @submission.set('gradeLocked', true)
equal @component.get('isInPastGradingPeriodAndNotAdmin'), true
test "isInPastGradingPeriodAndNotAdmin is true for assignments due exactly at the end of the previous grading period", ->
run => @assignment.set('due_at', tz.parse("2013-10-01T10:00:00Z"))
equal @component.get('isInPastGradingPeriodAndNotAdmin'), true
test "isInPastGradingPeriodAndNotAdmin is false for assignments after the previous grading period", ->
run => @assignment.set('due_at', tz.parse("2013-10-01T10:01:00Z"))
equal @component.get('isInPastGradingPeriodAndNotAdmin'), false
test "isInPastGradingPeriodAndNotAdmin is false for assignments without a due date", ->
run => @assignment.set('due_at', null)
test "isInPastGradingPeriodAndNotAdmin is false when the submission is not gradeLocked", ->
run => @submission.set('gradeLocked', false)
equal @component.get('isInPastGradingPeriodAndNotAdmin'), false
test "nilPointsPossible", ->

View File

@ -11,14 +11,12 @@ define [
App = null
originalIsDraft = null
originalWeightingScheme = null
clone = (obj) ->
Ember.copy obj, true
setup = (isDraftState=false, sortOrder='assignment_group') ->
fixtures.create()
originalWeightingScheme = window.ENV.GRADEBOOK_OPTIONS.group_weighting_scheme
@contextGetStub = sinon.stub(userSettings, 'contextGet')
@contextSetStub = sinon.stub(userSettings, 'contextSet')
@contextGetStub.withArgs('sort_grade_columns_by').returns({sortType: sortOrder})
@ -36,7 +34,6 @@ define [
})
teardown = ->
window.ENV.GRADEBOOK_OPTIONS.group_weighting_scheme = originalWeightingScheme
@contextGetStub.restore()
@contextSetStub.restore()
Ember.run App, 'destroy'
@ -116,14 +113,6 @@ define [
equal @srgb.get('assignments.firstObject.name'), 'Z Eats Soup'
equal @srgb.get('assignments.lastObject.name'), 'Da Fish and Chips!'
test 'updates assignment groups and weightingScheme when event is triggered', ->
window.ENV.GRADEBOOK_OPTIONS.group_weighting_scheme = 'whoa'
Ember.run =>
$.publish('assignment_group_weights_changed', assignmentGroups: Ember.copy(fixtures.assignment_groups, true).slice(0,1))
equal @srgb.get('weightingScheme'), 'whoa', 'weightingScheme was updated'
equal @srgb.get('assignment_groups.length'), 1, 'assignment_groups was updated'
test 'updates assignment_visibility on an assignment', ->
assignments = @srgb.get('assignments')
assgn = assignments.objectAt(2)
@ -194,6 +183,17 @@ define [
_.each submission, (val, key) =>
equal selectedSubmission[key], val, "#{key} is the expected value on selectedSubmission"
test 'selectedSubmission sets gradeLocked', ->
selectedSubmission = @srgb.get('selectedSubmission')
equal selectedSubmission.gradeLocked, false
test 'selectedSubmission sets gradeLocked for unassigned students', ->
@student = @srgb.get('students')[1]
Ember.run =>
@srgb.set('selectedStudent', @student)
selectedSubmission = @srgb.get('selectedSubmission')
equal selectedSubmission.gradeLocked, true
module 'screenreader_gradebook_controller: with selected assignment',
setup: ->
setup.call this

View File

@ -247,69 +247,69 @@ define [
students = [
{
user_id: '1'
user: { id: '1', name: 'Bob' }
user: { id: '1', name: 'Bob', group_ids: [], sections: [] }
course_section_id: '1'
}
{
user_id: '2'
user: { id: '2', name: 'Fred' }
user: { id: '2', name: 'Fred', group_ids: [], sections: [] }
course_section_id: '1'
}
{
user_id: '3'
user: { id: '3', name: 'Suzy' }
user: { id: '3', name: 'Suzy', group_ids: [], sections: [] }
course_section_id: '1'
}
{
user_id: '4'
user: { id: '4', name: 'Buffy' }
user: { id: '4', name: 'Buffy', group_ids: [], sections: [] }
course_section_id: '2'
}
{
user_id: '5'
user: { id: '5', name: 'Willow' }
user: { id: '5', name: 'Willow', group_ids: [], sections: [] }
course_section_id: '2'
}
{
user_id: '5'
user: { id: '5', name: 'Willow' }
user: { id: '5', name: 'Willow', group_ids: [], sections: [] }
course_section_id: '1'
}
{
user_id: '6'
user: { id: '6', name: 'Giles' }
user: { id: '6', name: 'Giles', group_ids: [], sections: [] }
course_section_id: '2'
}
{
user_id: '7'
user: { id: '7', name: 'Xander' }
user: { id: '7', name: 'Xander', group_ids: [], sections: [] }
course_section_id: '2'
}
{
user_id: '8'
user: { id: '8', name: 'Cordelia' }
user: { id: '8', name: 'Cordelia', group_ids: [], sections: [] }
course_section_id: '2'
}
{
user_id: '9'
user: { id: '9', name: 'Drusilla' }
user: { id: '9', name: 'Drusilla', group_ids: [], sections: [] }
course_section_id: '1'
}
{
user_id: '10'
user: { id: '10', name: 'Spike' }
user: { id: '10', name: 'Spike', group_ids: [], sections: [] }
course_section_id: '2'
}
{
user_id: '10'
user: { id: '10', name: 'Spike' }
user: { id: '10', name: 'Spike', group_ids: [], sections: [] }
course_section_id: '1'
}
]
concludedStudents = [
{
user: { id: '105', name: 'Lyra' }
user: { id: '105', name: 'Lyra', group_ids: [], sections: [] }
course_section_id: '1'
user_id: '105'
workflow_state: 'completed'

View File

@ -1,21 +1,12 @@
define [
'ember'
'compiled/gradebook2/GradebookHeaderMenu'
'compiled/gradebook2/AssignmentGroupWeightsDialog'
'compiled/SubmissionDetailsDialog'
], (Ember, GradebookHeaderMenu, AssignmentGroupWeightsDialog, SubmissionDetailsDialog) ->
], (Ember, GradebookHeaderMenu, SubmissionDetailsDialog) ->
AssignmentsView = Ember.View.extend
templateName: 'assignments'
setupDialog: (->
@agDialog = new AssignmentGroupWeightsDialog({context: ENV.GRADEBOOK_OPTIONS, assignmentGroups:[], mergeFunction: @mergeObjects})
).on('didInsertElement')
removeDialog: (->
@agDialog.$dialog.dialog('destroy')
).on('willDestroyElement')
mergeObjects: (old_ag, new_ag) ->
Ember.setProperties(old_ag, new_ag)
@ -40,12 +31,6 @@ define [
'submission': SubmissionDetailsDialog.open
switch dialogType
when 'ag_weights'
options =
context: ENV.GRADEBOOK_OPTIONS
assignmentGroups: con.get('assignment_groups').toArray()
@agDialog.update(options)
@agDialog.$dialog.dialog('open')
when 'submission'
dialogs[dialogType]?.call(this, con.get('selectedAssignment'), con.get('selectedStudent'), options)
else

View File

@ -28,6 +28,7 @@ define [
'str/htmlEscape'
'jsx/gradebook/SISGradePassback/PostGradesStore'
'jsx/gradebook/SISGradePassback/PostGradesApp'
'jsx/gradebook/SubmissionStateMap'
'jst/gradebook2/column_header'
'jst/gradebook2/group_total_cell'
'jst/gradebook2/row_student_name'
@ -56,7 +57,7 @@ InputFilterView, I18n, GRADEBOOK_TRANSLATIONS, GradeCalculator, UserSettings,
Spinner, SubmissionDetailsDialog, AssignmentGroupWeightsDialog,
GradeDisplayWarningDialog, PostGradesFrameDialog, SubmissionCell,
GradebookHeaderMenu, NumberCompare, htmlEscape, PostGradesStore, PostGradesApp,
ColumnHeaderTemplate, GroupTotalCellTemplate, RowStudentNameTemplate,
SubmissionStateMap, ColumnHeaderTemplate, GroupTotalCellTemplate, RowStudentNameTemplate,
SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
class Gradebook
@ -86,7 +87,7 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
@show_attendance = !!UserSettings.contextGet 'show_attendance'
@include_ungraded_assignments = UserSettings.contextGet 'include_ungraded_assignments'
@userFilterRemovedRows = []
# preferenecs serialization causes these to always come
# preferences serialization causes these to always come
# from the database as strings
@showConcludedEnrollments = @options.course_is_concluded ||
@options.settings['show_concluded_enrollments'] == "true"
@ -94,10 +95,15 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
@options.settings['show_inactive_enrollments'] == "true"
@totalColumnInFront = UserSettings.contextGet 'total_column_in_front'
@numberOfFrozenCols = if @totalColumnInFront then 3 else 2
@mgpEnabled = @options.multiple_grading_periods_enabled
@gradingPeriods = @options.active_grading_periods
@indexedGradingPeriods = _.indexBy @gradingPeriods, 'id'
@gradingPeriodsEnabled = @options.multiple_grading_periods_enabled
@gradingPeriods = _.map @options.active_grading_periods, (gradingPeriod) =>
_.extend({}, gradingPeriod, closed: @gradingPeriodIsClosed(gradingPeriod))
@gradingPeriodToShow = @getGradingPeriodToShow()
@submissionStateMap = new SubmissionStateMap
gradingPeriodsEnabled: @gradingPeriodsEnabled
selectedGradingPeriodID: @gradingPeriodToShow
gradingPeriods: @gradingPeriods
isAdmin: _.contains(ENV.current_user_roles, "admin")
@gradebookColumnSizeSettings = @options.gradebook_column_size_settings
@gradebookColumnOrderSettings = @options.gradebook_column_order_settings
@teacherNotesNotYetLoaded = !@options.teacher_notes? || @options.teacher_notes.hidden
@ -109,7 +115,7 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
$.subscribe 'currentGradingPeriod/change', @updateCurrentGradingPeriod
assignmentGroupsParams = { exclude_response_fields: @fieldsToExcludeFromAssignments }
if @mgpEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
if @gradingPeriodsEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
$.extend(assignmentGroupsParams, {grading_period_id: @gradingPeriodToShow})
$('li.external-tools-dialog > a[data-url], button.external-tools-dialog').on 'click keyclick', (event) ->
@ -122,7 +128,7 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
submissionParams =
response_fields: ['id', 'user_id', 'url', 'score', 'grade', 'submission_type', 'submitted_at', 'assignment_id', 'grade_matches_current_submission', 'attachments', 'late', 'workflow_state', 'excused']
exclude_response_fields: ['preview_url']
submissionParams['grading_period_id'] = @gradingPeriodToShow if @mgpEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
submissionParams['grading_period_id'] = @gradingPeriodToShow if @gradingPeriodsEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
dataLoader = DataLoader.loadGradebookData(
assignmentGroupsURL: @options.assignment_groups_url
assignmentGroupsParams: assignmentGroupsParams
@ -188,23 +194,8 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
filteredVisibility = assignment.assignment_visibility.filter (id) -> id != hiddenSub.user_id
assignment.assignment_visibility = filteredVisibility
# dependencies - assignmentGroupsLoaded
disableAssignmentsInClosedGradingPeriods: () ->
closedAdminGradingPeriods = @getClosedAdminGradingPeriods()
if closedAdminGradingPeriods.length > 0
assignments = @getAssignmentsInClosedGradingPeriods()
@disabledAssignments = assignments.map (a) -> a.id
getClosedAdminGradingPeriods: () ->
_.select @gradingPeriods, (gradingPeriod) =>
@gradingPeriodIsAdmin(gradingPeriod) && @gradingPeriodIsClosed(gradingPeriod)
gradingPeriodIsAdmin: (gradingPeriod) ->
!gradingPeriod.permissions.update
gradingPeriodIsClosed: (gradingPeriod) ->
new Date(gradingPeriod.end_date) < new Date()
new Date(gradingPeriod.close_date) < new Date()
gradingPeriodIsActive: (gradingPeriodId) ->
activePeriodIds = _.pluck(@gradingPeriods, 'id')
@ -217,18 +208,6 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
else
@options.current_grading_period_id
getAssignmentsInClosedGradingPeriods: () ->
latestEndDate = new Date(@options.latest_end_date_of_admin_created_grading_periods_in_the_past)
#return assignments whose end date is within the latest closed's end date
_.select @assignments, (a) =>
@assignmentIsDueBeforeEndDate(a, latestEndDate)
assignmentIsDueBeforeEndDate: (assignment, gradingPeriodEndDate) ->
if assignment.due_at
new Date(assignment.due_at) <= gradingPeriodEndDate
else
false
onShow: ->
$(".post-grades-button-placeholder").show()
return if @startedInitializing
@ -275,21 +254,6 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
@assignments[assignment.id] = assignment
@postGradesStore.setGradeBookAssignments @assignments
@disableAssignmentsInClosedGradingPeriods() if @mgpEnabled
initializeSubmissionsForStudent: (student) =>
for assignment_id, assignment of @assignments
student["assignment_#{assignment_id}"] ?= { assignment_id: assignment_id, user_id: student.id }
submission = student["assignment_#{assignment_id}"]
if @submissionOutsideOfGradingPeriod(submission, student)
submission.hidden = true
submission.outsideOfGradingPeriod = true
student.initialized = true
@calculateStudentGrade(student)
@grid?.invalidateRow(student.row)
gotSections: (sections) =>
@sections = {}
for section in sections
@ -322,10 +286,17 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
e.type == "StudentEnrollment" || e.type == "StudentViewEnrollment"
setupGrading: (students) =>
# fill in dummy submissions, so there's something there even if the
# student didn't submit anything for that assignment
@submissionStateMap.setup(students, @assignments)
for student in students
@initializeSubmissionsForStudent(student)
for assignment_id of @assignments
student["assignment_#{assignment_id}"] ?=
@submissionStateMap.getSubmission student.id, assignment_id
submissionState = @submissionStateMap.getSubmissionState(student["assignment_#{assignment_id}"])
student["assignment_#{assignment_id}"].gradeLocked = submissionState.locked
student.initialized = true
@calculateStudentGrade(student)
@grid?.invalidateRow(student.row)
@setAssignmentVisibility(_.pluck(students, 'id'))
@ -634,17 +605,11 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
activeCell.row is student.row and
activeCell.cell is cell
#check for DA visible
if submission.assignment_visible?
submission.hidden = !submission.assignment_visible
if @submissionOutsideOfGradingPeriod(submission, student)
submission.hidden = true
submission.outsideOfGradingPeriod = true
if submission.hidden
@updateAssignmentVisibilities(submission)
@updateAssignmentVisibilities(submission) unless submission.assignment_visible
@updateSubmission(submission)
@submissionStateMap.setSubmissionCellState(student, @assignments[submission.assignment_id], submission)
submissionState = @submissionStateMap.getSubmissionState(submission)
student["assignment_#{submission.assignment_id}"].gradeLocked = submissionState.locked
@calculateStudentGrade(student)
@grid.updateCell student.row, cell unless thisCellIsActive
@updateRowTotals student.row
@ -658,113 +623,39 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
if !@rows[row].loaded or !@rows[row].initialized
@staticCellFormatter(row, col, '')
else
if submission.outsideOfGradingPeriod
@uneditableCellOutsideOfGradingPeriodFormatter(row, col)
else if submission.hidden
@uneditableCellFormatter(row, col)
else if !submission?
@staticCellFormatter(row, col, '-')
cellAttributes = @submissionStateMap.getSubmissionState(submission)
if cellAttributes.hideGrade
@lockedAndHiddenGradeCellFormatter(row, col, cellAttributes.tooltip)
else
assignment = @assignments[submission.assignment_id]
student = @students[submission.user_id]
formatterOpts =
isLocked: cellAttributes.locked
tooltip: cellAttributes.tooltip
if !assignment?
@staticCellFormatter(row, col, '')
else if submission.workflow_state == 'pending_review'
(SubmissionCell[assignment.grading_type] || SubmissionCell).formatter(row, col, submission, assignment, student)
(SubmissionCell[assignment.grading_type] || SubmissionCell).formatter(row, col, submission, assignment, student, formatterOpts)
else if assignment.grading_type == 'points' && assignment.points_possible
SubmissionCell.out_of.formatter(row, col, submission, assignment, student)
SubmissionCell.out_of.formatter(row, col, submission, assignment, student, formatterOpts)
else
(SubmissionCell[assignment.grading_type] || SubmissionCell).formatter(row, col, submission, assignment, student)
indexedOverrides: =>
@_indexedOverrides ||= (=>
indexed = {
studentOverrides: {},
groupOverrides: {},
sectionOverrides: {}
}
_.each @assignments, (assignment) ->
if assignment.has_overrides && assignment.overrides
_.each assignment.overrides, (override) ->
if override.student_ids
indexed.studentOverrides[assignment.id] ?= {}
_.each override.student_ids, (studentId) ->
indexed.studentOverrides[assignment.id][studentId] = override
else if sectionId = override.course_section_id
indexed.sectionOverrides[assignment.id] ?= {}
indexed.sectionOverrides[assignment.id][sectionId] = override
else if groupId = override.group_id
indexed.groupOverrides[assignment.id] ?= {}
indexed.groupOverrides[assignment.id][groupId] = override
indexed
)()
# depedencies: assignmentGroupsLoaded
submissionOutsideOfGradingPeriod: (submission, student) ->
return false unless @mgpEnabled
selectedPeriodId = @gradingPeriodToShow
return false if @isAllGradingPeriods(selectedPeriodId)
assignment = @assignments[submission.assignment_id]
gradingPeriod = @indexedGradingPeriods[selectedPeriodId]
effectiveDueAt = assignment.due_at
if assignment.has_overrides && assignment.overrides
IDsByOverrideType = {
"sectionOverrides": student.sections
"groupOverrides": student.group_ids
"studentOverrides": [student.id]
}
getOverridesForType = ((typeIds, overrideType) =>
_.map typeIds, (typeId) =>
@indexedOverrides()[overrideType]?[assignment.id]?[typeId]).bind(this)
allOverridesForSubmission = _.chain(IDsByOverrideType)
.map(getOverridesForType)
.flatten()
.compact()
.value()
overrideDates = _.chain(allOverridesForSubmission)
.pluck('due_at')
.map((dateString) -> tz.parse(dateString))
.value()
if overrideDates.length > 0
nullDueAtsExist = _.any(overrideDates, (date) -> _.isNull(date))
effectiveDueAt = if nullDueAtsExist then null else _.max(overrideDates)
else
return true if assignment.only_visible_to_overrides
showSubmission = @lastGradingPeriodAndDueAtNull(gradingPeriod, effectiveDueAt) || @dateIsInGradingPeriod(gradingPeriod, effectiveDueAt)
!showSubmission
lastGradingPeriodAndDueAtNull: (gradingPeriod, dueAt) ->
gradingPeriod.is_last && _.isNull(dueAt)
dateIsInGradingPeriod: (gradingPeriod, date) ->
return false if _.isNull(date)
startDate = tz.parse(gradingPeriod.start_date)
endDate = tz.parse(gradingPeriod.end_date)
startDate < date && date <= endDate
(SubmissionCell[assignment.grading_type] || SubmissionCell).formatter(row, col, submission, assignment, student, formatterOpts)
staticCellFormatter: (row, col, val) ->
"<div class='cell-content gradebook-cell'>#{htmlEscape(val)}</div>"
uneditableCellOutsideOfGradingPeriodFormatter: (row, col) ->
"""
<div class='gradebook-tooltip'>
#{htmlEscape(I18n.t("Submission in another grading period"))}
</div>
<div class='cell-content gradebook-cell grayed-out cannot_edit'></div>
"""
uneditableCellFormatter: (row, col) ->
"<div class='cell-content gradebook-cell grayed-out cannot_edit'></div>"
lockedAndHiddenGradeCellFormatter: (row, col, tooltipKey) ->
if tooltipKey
tooltip = GRADEBOOK_TRANSLATIONS["submission_tooltip_#{tooltipKey}"]
"""
<div class='gradebook-tooltip'>
#{htmlEscape(tooltip)}
</div>
<div class='cell-content gradebook-cell grayed-out cannot_edit'></div>
"""
else
"<div class='cell-content gradebook-cell grayed-out cannot_edit'></div>"
groupTotalFormatter: (row, col, val, columnDef, student) =>
return '' unless val?
@ -1084,7 +975,7 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
initHeader: =>
@drawSectionSelectButton() if @sections_enabled
@drawGradingPeriodSelectButton() if @mgpEnabled
@drawGradingPeriodSelectButton() if @gradingPeriodsEnabled
$settingsMenu = $('.gradebook_dropdown')
showConcludedEnrollmentsEl = $settingsMenu.find("#show_concluded_enrollments")
@ -1708,7 +1599,7 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger) ->
currentPeriodId == "0"
hideAggregateColumns: ->
return false unless @mgpEnabled
return false unless @gradingPeriodsEnabled
return false if @options.all_grading_periods_totals
selectedPeriodId = @getGradingPeriodToShow()
@isAllGradingPeriods(selectedPeriodId)

View File

@ -1,9 +1,8 @@
define [
'jquery'
'underscore'
'i18n!gradebook2'
'jsx/gradebook/grid/constants'
], ($, _, I18n, GradebookConstants) ->
], ($, I18n, GradebookConstants) ->
FLASH_ERROR_CLASS: '.ic-flash-error'
flashMaxLengthError: () ->
@ -23,11 +22,3 @@ define [
textareaIsLessThanOrEqualToMaxLength: (textareaLength) ->
textareaLength <= GradebookConstants.MAX_NOTE_LENGTH
gradeIsLocked: (assignment, env) ->
return false unless env.GRADEBOOK_OPTIONS.multiple_grading_periods_enabled
return false unless env.GRADEBOOK_OPTIONS.latest_end_date_of_admin_created_grading_periods_in_the_past
return false unless env.current_user_roles
return false if _.contains(env.current_user_roles, "admin")
latest_end_date = new Date(env.GRADEBOOK_OPTIONS.latest_end_date_of_admin_created_grading_periods_in_the_past)
assignment.due_at != null && assignment.due_at <= latest_end_date

View File

@ -14,6 +14,9 @@ define ['i18n!gradebook2'], (I18n) ->
submission_tooltip_media_recording: I18n.t("Media Recording Submission")
submission_tooltip_online_quiz: I18n.t("Quiz Submission")
submission_tooltip_turnitin: I18n.t('Has Turnitin score')
submission_tooltip_not_in_any_grading_period: I18n.t("This submission is not in any grading period"),
submission_tooltip_in_another_grading_period: I18n.t("This submission is in another grading period"),
submission_tooltip_in_closed_grading_period: I18n.t("This submission is in a closed grading period"),
submission_update_error: I18n.t('There was an error updating this assignment. Please refresh the page and try again.')
submission_too_many_points_warning: I18n.t("This student was just awarded an unusually high grade.")
submission_negative_points_warning: I18n.t("This student was just awarded negative points.")

View File

@ -72,7 +72,7 @@ define [
validate: () ->
{ valid: true, msg: null }
@formatter: (row, col, submission, assignment, student) ->
@formatter: (row, col, submission, assignment, student, opts = {}) ->
if submission.excused
grade = "EX"
else
@ -85,7 +85,14 @@ define [
if grade && assignment?.grading_type == "percent"
grade = grade.toString() + "%"
this.prototype.cellWrapper(grade, {submission: submission, assignment: assignment, editable: false, student: student})
this.prototype.cellWrapper(grade, {
submission: submission,
assignment: assignment,
editable: false,
student: student,
isLocked: !!opts.isLocked,
tooltip: opts.tooltip
})
cellWrapper: (innerContents, options = {}) ->
opts = $.extend({}, {
@ -94,14 +101,16 @@ define [
student: {
isInactive: false,
isConcluded: false,
}
},
isLocked: false
}, options)
opts.submission ||= @opts.item[@opts.column.field]
opts.assignment ||= @opts.column.object
submission_type = opts.submission.submission_type if opts.submission?.submission_type || null
specialClasses = SubmissionCell.classesBasedOnSubmission(opts.submission, opts.assignment)
specialClasses.push("grayed-out") if opts.student.isInactive || opts.student.isConcluded
specialClasses.push("cannot_edit") if opts.student.isConcluded
specialClasses.push("grayed-out") if opts.student.isInactive || opts.student.isConcluded || opts.isLocked
specialClasses.push("cannot_edit") if opts.student.isConcluded || opts.isLocked
specialClasses.push(opts.tooltip) if opts.tooltip
opts.editable = false if opts.student.isConcluded
opts.classes += ' no_grade_yet ' unless opts.submission.grade && opts.submission.workflow_state != 'pending_review'
@ -114,7 +123,7 @@ define [
tooltipText = $.map(specialClasses, (c)-> GRADEBOOK_TRANSLATIONS["submission_tooltip_#{c}"]).join ', '
cellCommentHTML = if !opts.student.isConcluded
cellCommentHTML = if !opts.student.isConcluded && !opts.isLocked
"""
<a href="#" data-user-id=#{opts.submission.user_id} data-assignment-id=#{opts.assignment.id} class="gradebook-cell-comment"><span class="gradebook-cell-comment-label">submission comments</span></a>
"""
@ -179,7 +188,7 @@ define [
@$input = @$wrapper.find('input').focus().select()
class SubmissionCell.letter_grade extends SubmissionCell
@formatter: (row, col, submission, assignment, student) ->
@formatter: (row, col, submission, assignment, student, opts={}) ->
innerContents = if submission.excused
"EX"
else if submission.score?
@ -187,16 +196,16 @@ define [
else
submission.grade
SubmissionCell.prototype.cellWrapper(innerContents, {submission: submission, assignment: assignment, editable: false, student: student})
SubmissionCell.prototype.cellWrapper(innerContents, {submission: submission, assignment: assignment, editable: false, student: student, isLocked: !!opts.isLocked, tooltip: opts.tooltip})
class SubmissionCell.gpa_scale extends SubmissionCell
@formatter: (row, col, submission, assignment, student) ->
@formatter: (row, col, submission, assignment, student, opts={}) ->
innerContents = if submission.excused
"EX"
else
submission.grade
SubmissionCell.prototype.cellWrapper(innerContents, {submission: submission, assignment: assignment, editable: false, student: student, classes: "gpa_scale_cell"})
SubmissionCell.prototype.cellWrapper(innerContents, {submission: submission, assignment: assignment, editable: false, student: student, classes: "gpa_scale_cell", isLocked: !!opts.isLocked, tooltip: opts.tooltip})
class SubmissionCell.pass_fail extends SubmissionCell
@ -233,9 +242,9 @@ define [
aria-label="#{htmlEscape cssClass}"><span class="screenreader-only">#{htmlEscape cssClass}</span>#{checkboxButtonTemplate(iconClass)}</button>
""", options)
@formatter: (row, col, submission, assignment, student) ->
@formatter: (row, col, submission, assignment, student, opts={}) ->
return SubmissionCell.formatter.apply(this, arguments) unless submission.grade?
pass_fail::htmlFromSubmission({ submission, assignment, editable: false})
pass_fail::htmlFromSubmission({ submission, assignment, editable: false, isLocked: opts.isLocked, tooltip: opts.tooltip })
init: () ->
@$wrapper = $(@cellWrapper())

View File

@ -566,6 +566,8 @@ class GradebooksController < ApplicationController
:force_anonymous_grading => force_anonymous_grading?(@assignment),
:grading_role => grading_role,
:lti_retrieve_url => retrieve_course_external_tools_url(@context.id, assignment_id: @assignment.id, display: 'borderless'),
:course_id => @context.id,
:assignment_id => @assignment.id,
}
if [:moderator, :provisional_grader].include?(grading_role)
env[:provisional_status_url] = api_v1_course_assignment_provisional_status_path(@context.id, @assignment.id)

View File

@ -1,7 +1,8 @@
define([
"react",
"./createStore",
"underscore"
], function(createStore, _) {
], function(React, createStore, _) {
var { string, shape, arrayOf } = React.PropTypes;

View File

@ -0,0 +1,184 @@
define([
'underscore',
'timezone',
'i18n!gradebook2'
], function(_, tz, I18n) {
const TOOLTIP_KEYS = {
NOT_IN_ANY_GP: "not_in_any_grading_period",
IN_ANOTHER_GP: "in_another_grading_period",
IN_CLOSED_GP: "in_closed_grading_period",
NONE: null
};
function visibleToStudent(assignment, student) {
if (!assignment.only_visible_to_overrides) return true;
return _.contains(assignment.assignment_visibility, student.id);
}
function assignedToStudent(assignment, overriddenDate) {
if (!assignment.only_visible_to_overrides) return true;
return overriddenDate !== undefined;
}
function isAllGradingPeriods(periodId) {
return periodId === "0";
}
function lastGradingPeriodAndDueAtNull(gradingPeriod, dueAt) {
return gradingPeriod.is_last && dueAt === null;
}
function dateIsInGradingPeriod(gradingPeriod, date) {
if (date === null) return false;
return tz.parse(gradingPeriod.start_date) < date && date <= tz.parse(gradingPeriod.end_date);
}
function addStudentID(student, collection = []) {
return collection.concat([student.id]);
}
function studentIDCollections(students) {
const sections = {};
const groups = {};
students.forEach(function(student) {
student.sections.forEach(sectionID => sections[sectionID] = addStudentID(student, sections[sectionID]));
student.group_ids.forEach(groupID => groups[groupID] = addStudentID(student, groups[groupID]));
});
return { studentIDsInSections: sections, studentIDsInGroups: groups };
}
function studentIDsOnOverride(override, sections, groups) {
if (override.student_ids) {
return override.student_ids;
} else if (override.course_section_id && sections[override.course_section_id]) {
return sections[override.course_section_id];
} else if (override.group_id && groups[override.group_id]) {
return groups[override.group_id];
} else {
return [];
}
}
function getLatestDefinedDate(newDate, existingDate) {
if (existingDate === undefined || newDate === null) {
return newDate;
} else if (existingDate !== null && newDate > existingDate) {
return newDate;
} else {
return existingDate;
}
}
function indexOverrides(assignments, students) {
const { studentIDsInSections, studentIDsInGroups } = studentIDCollections(students);
const overrides = students.reduce(function(obj, student) {
obj[student.id] = {};
return obj;
}, {});
_.each(assignments, function(assignment) {
if (!assignment.has_overrides || !assignment.overrides) return;
assignment.overrides.forEach(function(override) {
const studentIDs = studentIDsOnOverride(override, studentIDsInSections, studentIDsInGroups);
studentIDs.forEach(function(studentID) {
overrides[studentID] = overrides[studentID] || {};
const existingDate = overrides[studentID][assignment.id];
const newDate = tz.parse(override.due_at);
overrides[studentID][assignment.id] = getLatestDefinedDate(newDate, existingDate);
});
});
});
return overrides;
}
function getGradingPeriodForDueAt(gradingPeriods, dueAt) {
return _.find(gradingPeriods, function(period) {
return lastGradingPeriodAndDueAtNull(period, dueAt) ||
dateIsInGradingPeriod(period, dueAt);
});
}
function cellMapForSubmission(assignment, student, overriddenDate, gradingPeriodsEnabled, selectedGradingPeriodID, gradingPeriods, isAdmin) {
if (!visibleToStudent(assignment, student)) {
return { locked: true, hideGrade: true, tooltip: TOOLTIP_KEYS.NONE };
} else if (gradingPeriodsEnabled) {
return cellMappingsForMultipleGradingPeriods(assignment, student, overriddenDate, selectedGradingPeriodID, gradingPeriods, isAdmin);
} else {
return { locked: false, hideGrade: false, tooltip: TOOLTIP_KEYS.NONE };
}
}
function cellMappingsForMultipleGradingPeriods(assignment, student, overriddenDate, selectedGradingPeriodID, gradingPeriods, isAdmin) {
const specificPeriodSelected = !isAllGradingPeriods(selectedGradingPeriodID);
const effectiveDueAt = overriddenDate === undefined ? assignment.due_at : overriddenDate;
const gradingPeriodForDueAt = getGradingPeriodForDueAt(gradingPeriods, effectiveDueAt);
if (specificPeriodSelected && visibleToStudent(assignment, student) && !assignedToStudent(assignment, overriddenDate)) {
return { locked: true, hideGrade: true, tooltip: TOOLTIP_KEYS.NOT_IN_ANY_GP };
} else if (specificPeriodSelected && !gradingPeriodForDueAt) {
return { locked: true, hideGrade: true, tooltip: TOOLTIP_KEYS.NOT_IN_ANY_GP };
} else if (specificPeriodSelected && selectedGradingPeriodID !== gradingPeriodForDueAt.id) {
return { locked: true, hideGrade: true, tooltip: TOOLTIP_KEYS.IN_ANOTHER_GP };
} else if (!isAdmin && (gradingPeriodForDueAt || {}).closed) {
return { locked: true, hideGrade: false, tooltip: TOOLTIP_KEYS.IN_CLOSED_GP };
} else {
return { locked: false, hideGrade: false, tooltip: TOOLTIP_KEYS.NONE };
}
}
class SubmissionState {
constructor({ gradingPeriodsEnabled, selectedGradingPeriodID, gradingPeriods, isAdmin }) {
this.gradingPeriodsEnabled = gradingPeriodsEnabled;
this.selectedGradingPeriodID = selectedGradingPeriodID;
this.gradingPeriods = gradingPeriods;
this.isAdmin = isAdmin;
this.overrides = {};
this.submissionCellMap = {};
this.submissionMap = {};
}
setup(students, assignments) {
const newOverrides = indexOverrides(assignments, students);
this.overrides = Object.assign(this.overrides, newOverrides);
students.forEach((student) => {
this.submissionCellMap[student.id] = {};
this.submissionMap[student.id] = {};
_.each(assignments, (assignment) => {
this.setSubmissionCellState(student, assignment, student[`assignment_${assignment.id}`]);
});
});
}
setSubmissionCellState(student, assignment, submission = { assignment_id: assignment.id, user_id: student.id }) {
this.submissionMap[student.id][assignment.id] = submission;
const params = [
assignment,
student,
this.overrides[student.id][assignment.id],
this.gradingPeriodsEnabled,
this.selectedGradingPeriodID,
this.gradingPeriods,
this.isAdmin
];
this.submissionCellMap[student.id][assignment.id] = cellMapForSubmission(...params);
}
getSubmission(user_id, assignment_id) {
return (this.submissionMap[user_id] || {})[assignment_id];
}
getSubmissionState({ user_id, assignment_id }) {
return (this.submissionCellMap[user_id] || {})[assignment_id];
}
};
return SubmissionState;
})

View File

@ -11,7 +11,7 @@ define(["jquery", "i18n!gradebook_uploads", "vendor/jquery.spin"],
dfd.resolve(uploadedGradebook)
});
} else if (progress.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 {
setTimeout(function() {
$.ajaxJSON(`/api/v1/progress/${progress.id}`, "GET")

View File

@ -6,27 +6,28 @@ define([
'jsx/shared/helpers/dateHelper',
'jquery.instructure_misc_helpers'
], function(React, $, axios, I18n, DateHelper) {
const types = React.PropTypes;
const Types = React.PropTypes;
let AccountGradingPeriod = React.createClass({
propTypes: {
period: types.shape({
id: types.string.isRequired,
title: types.string.isRequired,
startDate: types.instanceOf(Date).isRequired,
endDate: types.instanceOf(Date).isRequired
period: Types.shape({
id: Types.string.isRequired,
title: Types.string.isRequired,
startDate: Types.instanceOf(Date).isRequired,
endDate: Types.instanceOf(Date).isRequired,
closeDate: Types.instanceOf(Date).isRequired
}).isRequired,
onEdit: types.func.isRequired,
actionsDisabled: types.bool,
readOnly: types.bool.isRequired,
permissions: types.shape({
read: types.bool.isRequired,
create: types.bool.isRequired,
update: types.bool.isRequired,
delete: types.bool.isRequired
onEdit: Types.func.isRequired,
actionsDisabled: Types.bool,
readOnly: Types.bool.isRequired,
permissions: Types.shape({
read: Types.bool.isRequired,
create: Types.bool.isRequired,
update: Types.bool.isRequired,
delete: Types.bool.isRequired
}).isRequired,
onDelete: types.func.isRequired,
deleteGradingPeriodURL: types.string.isRequired
onDelete: Types.func.isRequired,
deleteGradingPeriodURL: Types.string.isRequired
},
promptDeleteGradingPeriod(event) {
@ -85,15 +86,18 @@ define([
return (
<div className="GradingPeriodList__period">
<div className="GradingPeriodList__period__attributes grid-row">
<div className="GradingPeriodList__period__attribute col-xs-12 col-md-8 col-lg-4">
<div className="GradingPeriodList__period__attribute col-xs-12 col-md-8 col-lg-3">
<span tabIndex="0" ref="title">{this.props.period.title}</span>
</div>
<div className="GradingPeriodList__period__attribute col-xs-12 col-md-8 col-lg-4">
<div className="GradingPeriodList__period__attribute col-xs-12 col-md-8 col-lg-3">
<span tabIndex="0" ref="startDate">{I18n.t("Start Date:")} {DateHelper.formatDatetimeForDisplay(this.props.period.startDate)}</span>
</div>
<div className="GradingPeriodList__period__attribute col-xs-12 col-md-8 col-lg-4">
<div className="GradingPeriodList__period__attribute col-xs-12 col-md-8 col-lg-3">
<span tabIndex="0" ref="endDate">{I18n.t("End Date:")} {DateHelper.formatDatetimeForDisplay(this.props.period.endDate)}</span>
</div>
<div className="GradingPeriodList__period__attribute col-xs-12 col-md-8 col-lg-3">
<span tabIndex="0" ref="closeDate">{I18n.t("Close Date:")} {DateHelper.formatDatetimeForDisplay(this.props.period.closeDate)}</span>
</div>
</div>
<div className="GradingPeriodList__period__actions">
{this.renderEditButton()}

View File

@ -6,33 +6,70 @@ define([
'jsx/due_dates/DueDateCalendarPicker',
'jsx/shared/helpers/accessibleDateFormat'
], function(React, ReactDOM, _, I18n, DueDateCalendarPicker, accessibleDateFormat) {
const types = React.PropTypes;
const update = React.addons.update;
const Types = React.PropTypes;
const buildPeriod = function(attr) {
return {
id: attr.id,
title: attr.title,
startDate: attr.startDate || new Date(''),
endDate: attr.endDate || new Date('')
startDate: attr.startDate,
endDate: attr.endDate,
closeDate: attr.closeDate
};
};
const hasDistinctCloseDate = ({ endDate, closeDate }) => {
return closeDate && !_.isEqual(endDate, closeDate);
};
const mergePeriod = (form, attr) => {
return update(form.state.period, {$merge: attr});
}
const changeTitle = function(e) {
let period = mergePeriod(this, {title: e.target.value});
this.setState({period: period});
};
const changeStartDate = function(date) {
let period = mergePeriod(this, {startDate: date});
this.setState({period: period});
};
const changeEndDate = function(date) {
let attr = {endDate: date};
if (!this.state.preserveCloseDate && !hasDistinctCloseDate(this.state.period)) {
attr.closeDate = date;
}
let period = mergePeriod(this, attr);
this.setState({period: period});
};
const changeCloseDate = function(date) {
let period = mergePeriod(this, {closeDate: date});
this.setState({period: period, preserveCloseDate: !!date});
};
let GradingPeriodForm = React.createClass({
propTypes: {
period: types.shape({
id: types.string.isRequired,
title: types.string.isRequired,
startDate: types.instanceOf(Date).isRequired,
endDate: types.instanceOf(Date).isRequired
period: Types.shape({
id: Types.string.isRequired,
title: Types.string.isRequired,
startDate: Types.instanceOf(Date).isRequired,
endDate: Types.instanceOf(Date).isRequired,
closeDate: Types.instanceOf(Date).isRequired
}),
disabled: types.bool.isRequired,
onSave: types.func.isRequired,
onCancel: types.func.isRequired
disabled: Types.bool.isRequired,
onSave: Types.func.isRequired,
onCancel: Types.func.isRequired
},
getInitialState: function() {
let period = buildPeriod(this.props.period || {});
return {
period: buildPeriod(this.props.period || {})
period: period,
preserveCloseDate: hasDistinctCloseDate(period)
};
},
@ -56,7 +93,7 @@ define([
className='ic-Input'
title={I18n.t('Grading Period Title')}
defaultValue={this.state.period.title}
onChange={this.changeTitle}
onChange={changeTitle.bind(this)}
type='text'/>
</div>
@ -73,7 +110,7 @@ define([
<DueDateCalendarPicker dateValue = {this.state.period.startDate}
ref = "startDate"
dateType = "due_at"
handleUpdate = {this.changeStartDate}
handleUpdate = {changeStartDate.bind(this)}
rowKey = "start-date"
labelledBy = "start-date" />
</div>
@ -87,12 +124,24 @@ define([
<DueDateCalendarPicker dateValue = {this.state.period.endDate}
ref = "endDate"
dateType = "due_at"
handleUpdate = {this.changeEndDate}
handleUpdate = {changeEndDate.bind(this)}
rowKey = "end-date"
labelledBy = "end-date" />
</div>
</div>
</div>
<div className="ic-Form-control">
<label className="ic-Label" htmlFor="close-date">
{I18n.t("Close Date")}
</label>
<DueDateCalendarPicker dateValue = {this.state.period.closeDate}
ref = "closeDate"
dateType = "due_at"
handleUpdate = {changeCloseDate.bind(this)}
rowKey = "close-date"
labelledBy = "close-date" />
</div>
</div>
</div>
</div>
@ -124,24 +173,6 @@ define([
);
},
changeTitle: function(e) {
let period = _.clone(this.state.period);
period.title = e.target.value;
this.setState({period: period});
},
changeStartDate: function(date) {
let period = _.clone(this.state.period);
period.startDate = date;
this.setState({period: period});
},
changeEndDate: function(date) {
let period = _.clone(this.state.period);
period.endDate = date;
this.setState({period: period});
},
triggerSave: function() {
if (this.props.onSave) {
this.props.onSave(this.state.period);

View File

@ -38,21 +38,31 @@ define([
}
let validDates = _.all(periods, (period) => {
return isValidDate(period.startDate) && isValidDate(period.endDate);
return isValidDate(period.startDate) &&
isValidDate(period.endDate) &&
isValidDate(period.closeDate);
});
if (!validDates) {
return [I18n.t('All dates fields must be present and formatted correctly')];
}
let orderedDates = _.all(periods, (period) => {
let orderedStartAndEndDates = _.all(periods, (period) => {
return period.startDate < period.endDate;
});
if (!orderedDates) {
if (!orderedStartAndEndDates) {
return [I18n.t('All start dates must be before the end date')];
}
let orderedEndAndCloseDates = _.all(periods, (period) => {
return period.endDate <= period.closeDate;
});
if (!orderedEndAndCloseDates) {
return [I18n.t('All close dates must be on or after the end date')];
}
if (anyPeriodsOverlap(periods)) {
return [I18n.t('Grading periods must not overlap')];
}

View File

@ -7,21 +7,22 @@ define([
'jsx/grading/gradingPeriodTemplate',
'jsx/shared/helpers/dateHelper'
], function(tz, React, $, I18n, _, GradingPeriodTemplate, DateHelper) {
var types = React.PropTypes;
var Types = React.PropTypes;
var GradingPeriod = React.createClass({
propTypes: {
title: types.string.isRequired,
startDate: types.instanceOf(Date).isRequired,
endDate: types.instanceOf(Date).isRequired,
id: types.string.isRequired,
updateGradingPeriodCollection: types.func.isRequired,
onDeleteGradingPeriod: types.func.isRequired,
disabled: types.bool.isRequired,
readOnly: types.bool.isRequired,
permissions: types.shape({
update: types.bool.isRequired,
delete: types.bool.isRequired,
title: Types.string.isRequired,
startDate: Types.instanceOf(Date).isRequired,
endDate: Types.instanceOf(Date).isRequired,
closeDate: Types.instanceOf(Date).isRequired,
id: Types.string.isRequired,
updateGradingPeriodCollection: Types.func.isRequired,
onDeleteGradingPeriod: Types.func.isRequired,
disabled: Types.bool.isRequired,
readOnly: Types.bool.isRequired,
permissions: Types.shape({
update: Types.bool.isRequired,
delete: Types.bool.isRequired,
}).isRequired
},
@ -82,6 +83,7 @@ define([
title={this.props.title}
startDate={this.props.startDate}
endDate={this.props.endDate}
closeDate={this.props.closeDate || this.props.endDate}
permissions={this.props.permissions}
disabled={this.props.disabled}
readOnly={this.props.readOnly}

View File

@ -14,10 +14,6 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
return state.periods !== null;
};
const canAddPeriods = (state) => {
return !state.readOnly && state.canAddNewPeriods;
};
let GradingPeriodCollection = React.createClass({
propTypes: {
@ -30,7 +26,6 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
readOnly: false,
disabled: false,
saveDisabled: true,
canAddNewPeriods: false,
canChangeGradingPeriodsSetting: false
};
},
@ -46,7 +41,6 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
self.setState({
periods: self.deserializePeriods(periods),
readOnly: periods.grading_periods_read_only,
canAddNewPeriods: periods.can_create_grading_periods,
canChangeGradingPeriodsSetting: periods.can_toggle_grading_periods,
disabled: false,
saveDisabled: _.isEmpty(periods.grading_periods)
@ -62,17 +56,11 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
let newPeriod = ConvertCase.camelize(period);
newPeriod.startDate = new Date(period.start_date);
newPeriod.endDate = new Date(period.end_date);
newPeriod.closeDate = new Date(period.close_date || period.end_date);
return newPeriod;
});
},
componentDidUpdate: function(prevProps, prevState) {
if (prevState.periods) {
let removedAGradingPeriod = this.state.periods.length < prevState.periods.length;
if (removedAGradingPeriod && this.refs.addPeriodButton) this.refs.addPeriodButton.focus();
}
},
deleteGradingPeriod: function(id) {
if (id.indexOf('new') > -1) {
this.removeDeletedGradingPeriod(id);
@ -105,29 +93,6 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
this.setState({periods: newPeriods});
},
getCreateGradingPeriodCSS: function() {
let cssClasses = 'center-md new-grading-period pad-box border border-round';
if (!this.state.periods || this.state.periods.length === 0) {
cssClasses += ' no-active-grading-periods';
}
return cssClasses;
},
createNewGradingPeriod: function() {
if (!this.state.readOnly && this.state.canAddNewPeriods) {
let newPeriod = {
title: '',
startDate: new Date(''),
endDate: new Date(''),
id: _.uniqueId('new'),
permissions: { read: true, update: true, delete: true}
};
let periods = update(this.state.periods, {$push: [newPeriod]});
this.setState({periods: periods, saveDisabled: false});
}
},
getPeriodById: function(id) {
return _.find(this.state.periods, period => period.id === id);
},
@ -246,7 +211,7 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
},
renderSaveButton: function() {
if (periodsAreLoaded(this.state) && !this.state.readOnly && _.all(this.state.periods, period => period.permissions.update || period.permissions.create)) {
if (periodsAreLoaded(this.state) && !this.state.readOnly && _.all(this.state.periods, period => period.permissions.update)) {
let buttonText = this.state.disabled ? I18n.t('Updating') : I18n.t('Save');
return (
<div className='form-actions'>
@ -271,6 +236,7 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
title={period.title}
startDate={period.startDate}
endDate={period.endDate}
closeDate={period.closeDate}
permissions={period.permissions}
readOnly={this.state.readOnly}
disabled={this.state.disabled}
@ -281,20 +247,6 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
});
},
renderAddPeriodButton: function() {
if (periodsAreLoaded(this.state) && canAddPeriods(this.state)) {
return (
<div className={this.getCreateGradingPeriodCSS()}>
<button id='add-period-button' className='Button--link' ref='addPeriodButton'
onClick={this.createNewGradingPeriod} disabled={this.state.disabled}>
<i className='icon-plus grading-period-add-icon'/>
{I18n.t('Add Grading Period')}
</button>
</div>
);
}
},
render: function () {
return (
<div>
@ -304,7 +256,6 @@ function(React, GradingPeriod, $, I18n, _, ConvertCase) {
<div id='grading_periods' className='content-box'>
{this.renderGradingPeriods()}
</div>
{this.renderAddPeriodButton()}
{this.renderSaveButton()}
</div>
);

View File

@ -8,23 +8,52 @@ define([
'jsx/shared/helpers/dateHelper',
'jquery.instructure_date_and_time'
], function(tz, React, ReactDOM, $, I18n, _, DateHelper) {
const types = React.PropTypes;
const Types = React.PropTypes;
const postfixId = (text, { props }) => {
return text + props.id;
};
const isEditable = ({ props }) => {
return props.permissions.update && !props.readOnly;
};
const tabbableDate = (ref, date) => {
let formattedDate = DateHelper.formatDatetimeForDisplay(date);
return <span ref={ref} className="GradingPeriod__Action" tabIndex="0">{ formattedDate }</span>;
};
const renderActions = ({ props, onDeleteGradingPeriod }) => {
if (props.permissions.delete && !props.readOnly) {
let cssClasses = "Button Button--icon-action icon-delete-grading-period";
if (props.disabled) cssClasses += " disabled";
return (
<div className="GradingPeriod__Actions content-box">
<button ref="deleteButton"
role="button"
className={cssClasses}
aria-disabled={props.disabled}
onClick={onDeleteGradingPeriod}>
<i className="icon-x icon-delete-grading-period"/>
<span className="screenreader-only">{I18n.t("Delete grading period")}</span>
</button>
</div>
);
}
};
let GradingPeriodTemplate = React.createClass({
propTypes: {
title: types.string.isRequired,
startDate: types.instanceOf(Date).isRequired,
endDate: types.instanceOf(Date).isRequired,
id: types.string.isRequired,
permissions: types.shape({
update: types.bool.isRequired,
delete: types.bool.isRequired,
title: Types.string.isRequired,
startDate: Types.instanceOf(Date).isRequired,
endDate: Types.instanceOf(Date).isRequired,
closeDate: Types.instanceOf(Date).isRequired,
id: Types.string.isRequired,
permissions: Types.shape({
update: Types.bool.isRequired,
delete: Types.bool.isRequired,
}).isRequired,
readOnly: types.bool.isRequired,
readOnly: Types.bool.isRequired,
requiredPropsIfEditable: function(props) {
if (!props.permissions.update && !props.permissions.delete) return;
@ -68,34 +97,17 @@ define([
},
onDeleteGradingPeriod: function() {
this.props.onDeleteGradingPeriod(this.props.id);
},
renderDeleteButton: function() {
if (!this.props.permissions.delete || this.props.readOnly) return null;
let cssClasses = "Button Button--icon-action icon-delete-grading-period";
if (this.props.disabled) cssClasses += " disabled";
return (
<div className="col-xs-12 col-sm-6 col-lg-3 manage-buttons-container">
<div className="content-box">
<div className="buttons-grid-row grid-row">
<div className="col-xs">
<button ref="deleteButton" role="button" className={cssClasses} onClick={this.onDeleteGradingPeriod}>
<i className="icon-x icon-delete-grading-period"/>
<span className="screenreader-only">{I18n.t("Delete grading period")}</span>
</button>
</div>
</div>
</div>
</div>
);
if (!this.props.disabled) {
this.props.onDeleteGradingPeriod(this.props.id);
}
},
renderTitle: function() {
if (this.props.permissions.update && !this.props.readOnly) {
if (isEditable(this)) {
return (
<input id={postfixId("period_title_", this)}
type="text"
className="GradingPeriod__Detail ic-Input"
onChange={this.props.onTitleChange}
value={this.props.title}
disabled={this.props.disabled}
@ -103,38 +115,40 @@ define([
);
} else {
return (
<div id={postfixId("period_title_", this)} ref="title">
{this.props.title}
<div>
<span className="screenreader-only">{I18n.t("Grading Period Name")}</span>
<span ref="title" tabIndex="0">{this.props.title}</span>
</div>
);
}
},
renderStartDate: function() {
if (this.props.permissions.update && !this.props.readOnly) {
if (isEditable(this)) {
return (
<input id={postfixId("period_start_date_", this)}
type="text"
ref="startDate"
name="startDate"
className="input-grading-period-date date_field"
className="GradingPeriod__Detail ic-Input input-grading-period-date date_field"
defaultValue={DateHelper.formatDatetimeForDisplay(this.props.startDate)}
disabled={this.props.disabled}/>
);
} else {
return (
<div id={postfixId("period_start_date_", this)} ref="startDate">
{DateHelper.formatDatetimeForDisplay(this.props.startDate)}
<div>
<span className="screenreader-only">{I18n.t("Start Date")}</span>
{ tabbableDate("startDate", this.props.startDate) }
</div>
);
}
},
renderEndDate: function() {
if (this.props.permissions.update && !this.props.readOnly) {
if (isEditable(this)) {
return (
<input id={postfixId("period_end_date_", this)} type="text"
className="input-grading-period-date date_field"
className="GradingPeriod__Detail ic-Input input-grading-period-date date_field"
ref="endDate"
name="endDate"
defaultValue={DateHelper.formatDatetimeForDisplay(this.props.endDate)}
@ -142,37 +156,57 @@ define([
);
} else {
return (
<div id={postfixId("period_end_date_", this)} ref="endDate">
{DateHelper.formatDatetimeForDisplay(this.props.endDate)}
<div>
<span className="screenreader-only">{I18n.t("End Date")}</span>
{ tabbableDate("endDate", this.props.endDate) }
</div>
);
}
},
renderCloseDate: function() {
let closeDate = isEditable(this) ? this.props.endDate : this.props.closeDate;
return (
<div>
<span className="screenreader-only">{I18n.t("Close Date")}</span>
{ tabbableDate("closeDate", closeDate || this.props.endDate) }
</div>
);
},
render: function () {
return (
<div id={postfixId("grading-period-", this)} className="grading-period pad-box-mini border border-trbl border-round">
<div className="grid-row pad-box-micro">
<div className="col-xs-12 col-sm-6 col-lg-3">
<label htmlFor={postfixId("period_title_", this)}>
{I18n.t("Grading Period Name")}
</label>
{this.renderTitle()}
<div className="GradingPeriod__Details pad-box-micro">
<div className="grid-row">
<div className="col-xs-12 col-sm-6 col-lg-3">
<label className="ic-Label" htmlFor={postfixId("period_title_", this)}>
{I18n.t("Grading Period Name")}
</label>
{this.renderTitle()}
</div>
<div className="col-xs-12 col-sm-6 col-lg-3">
<label className="ic-Label" htmlFor={postfixId("period_start_date_", this)}>
{I18n.t("Start Date")}
</label>
{this.renderStartDate()}
</div>
<div className="col-xs-12 col-sm-6 col-lg-3">
<label className="ic-Label" htmlFor={postfixId("period_end_date_", this)}>
{I18n.t("End Date")}
</label>
{this.renderEndDate()}
</div>
<div className="col-xs-12 col-sm-6 col-lg-3">
<label className="ic-Label" id={postfixId("period_close_date_", this)}>
{I18n.t("Close Date")}
</label>
{this.renderCloseDate()}
</div>
</div>
<div className="col-xs-12 col-sm-6 col-lg-3">
<label htmlFor={postfixId("period_start_date_", this)}>
{I18n.t("Start Date")}
</label>
{this.renderStartDate()}
</div>
<div className="col-xs-12 col-sm-6 col-lg-3">
<label htmlFor={postfixId("period_end_date_", this)}>
{I18n.t("End Date")}
</label>
{this.renderEndDate()}
</div>
{this.renderDeleteButton()}
</div>
{renderActions(this)}
</div>
);
}

View File

@ -0,0 +1,39 @@
define(["underscore"], (_) => {
const assignmentClosedForStudent = (student, {due_at, only_visible_to_overrides, gradingPeriods, assignmentOverrides}) => {
const potentialDueDates = only_visible_to_overrides ?
assignmentOverrides :
[{default: true, due_at: due_at}, ...assignmentOverrides];
const dueDate = dueDateForStudent(student, potentialDueDates);
const gp = gradingPeriodForDate(dueDate, gradingPeriods);
return gp && new Date(gp.close_date) < new Date();
}
const gradingPeriodForDate = (date, gradingPeriods) => {
if (date == null) {
return _.last(gradingPeriods);
} else {
return gradingPeriods.find((gp) => {
return date >= gp.start_date && date < gp.end_date;
});
}
}
const dueDateForStudent = (student, dueDates) => {
const applicableDueDates = dueDates.filter(function(o) {
if (o.default) {
return true;
}
if (o.student_ids && _.include(o.student_ids, student.id)) {
return true;
}
if (o.course_section_id != null &&
_.include(student.section_ids, o.course_section_id)) {
return true;
}
}).map(o => o.due_at);
return _.last(applicableDueDates.sort());
};
return {assignmentClosedForStudent, gradingPeriodForDate, dueDateForStudent};
});

View File

@ -24,11 +24,12 @@ class GradingPeriod < ActiveRecord::Base
belongs_to :grading_period_group, inverse_of: :grading_periods
has_many :grading_period_grades, dependent: :destroy
validates :title, :start_date, :end_date, :grading_period_group_id, presence: true
validates :title, :start_date, :end_date, :close_date, :grading_period_group_id, presence: true
validate :start_date_is_before_end_date
validate :close_date_is_not_before_end_date
validate :close_date_is_on_or_after_end_date
validate :not_overlapping, unless: :skip_not_overlapping_validator?
before_validation :adjust_close_date_for_course_period
before_validation :ensure_close_date
scope :current, -> do
@ -104,6 +105,10 @@ class GradingPeriod < ActiveRecord::Base
end
alias_method :is_last, :last?
def closed?
Time.zone.now > close_date
end
def overlapping?
overlaps.active.exists?
end
@ -125,7 +130,7 @@ class GradingPeriod < ActiveRecord::Base
def as_json_with_user_permissions(user)
as_json(
only: [:id, :title, :start_date, :end_date],
only: [:id, :title, :start_date, :end_date, :close_date],
permissions: { user: user },
methods: :is_last
).fetch(:grading_period)
@ -166,18 +171,21 @@ class GradingPeriod < ActiveRecord::Base
def start_date_is_before_end_date
if start_date && end_date && end_date < start_date
errors.add(:end_date, t('errors.invalid_grading_period_end_date',
'Grading period end date precedes start date'))
errors.add(:end_date, t('must be after start date'))
end
end
def adjust_close_date_for_course_period
self.close_date = self.end_date if grading_period_group.present? && course_group?
end
def ensure_close_date
self.close_date = self.end_date
self.close_date ||= self.end_date
end
def close_date_is_not_before_end_date
if close_date && end_date && close_date < end_date
errors.add(:close_date, t('Grading period close date precedes end date'))
def close_date_is_on_or_after_end_date
if close_date.present? && end_date.present? && close_date < end_date
errors.add(:close_date, t('must be on or after end date'))
end
end
end

View File

@ -217,9 +217,28 @@ class Submission < ActiveRecord::Base
given { |user| user && user.id == self.user_id && !self.assignment.muted? }
can :read_grade
given {|user, session| self.assignment.published? && self.assignment.context.grants_right?(user, session, :manage_grades) }
given do |user, session|
!context.feature_enabled?(:multiple_grading_periods) &&
assignment.published? &&
context.grants_right?(user, session, :manage_grades)
end
can :read and can :comment and can :make_group_comment and can :read_grade and can :grade
given do |user, session|
context.feature_enabled?(:multiple_grading_periods) &&
assignment.published? &&
context.grants_right?(user, session, :manage_grades)
end
can :read and can :comment and can :make_group_comment and can :read_grade
given do |user, session|
context.feature_enabled?(:multiple_grading_periods) &&
assignment.published? &&
context.grants_right?(user, session, :manage_grades) &&
(user.admin_of_root_account?(assignment.root_account) || !in_closed_grading_period?)
end
can :grade
given {|user, session| self.assignment.user_can_read_grades?(user, session) }
can :read and can :read_grade
@ -249,6 +268,17 @@ class Submission < ActiveRecord::Base
can :view_turnitin_report
end
def in_closed_grading_period?
return false unless self.assignment.context.feature_enabled?(:multiple_grading_periods)
grading_period = GradingPeriod.
for(self.assignment.context).
where(":due_at >= start_date AND :due_at <= end_date", due_at: self.cached_due_date).
first
return false unless grading_period.present?
grading_period.closed?
end
def user_can_read_grade?(user, session=nil)
# improves performance by checking permissions on the assignment before the submission
self.assignment.user_can_read_grades?(user, session) || self.grants_right?(user, session, :read_grade)

View File

@ -2277,7 +2277,6 @@ class User < ActiveRecord::Base
roles << 'student' unless (enrollment_types & %w[StudentEnrollment StudentViewEnrollment]).empty?
roles << 'teacher' unless (enrollment_types & %w[TeacherEnrollment TaEnrollment DesignerEnrollment]).empty?
roles << 'observer' unless (enrollment_types & %w[ObserverEnrollment]).empty?
account_users = root_account.all_account_users_for(self)
if account_users.any?
roles << 'admin'
@ -2288,6 +2287,10 @@ class User < ActiveRecord::Base
end
end
def admin_of_root_account?(root_account)
root_account.all_account_users_for(self).any?
end
def eportfolios_enabled?
accounts = associated_root_accounts.reject(&:site_admin?)
accounts.size == 0 || accounts.any?{ |a| a.settings[:enable_eportfolios] != false }

View File

@ -14,9 +14,7 @@
.grading-period:nth-child(n+2) {
border-top: none;
}
.input-grading-period-date {
width: 160px;
}
.icon-delete-grading-period::before {
padding: 9px 0px;
cursor: pointer;
@ -26,34 +24,30 @@
.icon-delete-grading-period {
width: 20px;
}
.buttons-grid-row {
width: 250px;
}
label {
font-weight: bold;
}
}
.new-grading-period {
border-right-style: dashed;
border-bottom-style: dashed;
border-left-style: dashed;
border-top-style: none;
border-width: 2px;
@include fontSize(16px);
.grading-period {
display: flex;
}
#add-period-button {
@include fontSize(16px);
&:hover {
text-decoration: none;
}
}
.grading-period-add-icon {
margin-right: 4px;
.GradingPeriod__Details {
flex: 1 1;
}
.GradingPeriod__Detail {
width: 100%;
&.ic-Input.hasDatepicker {
display: inline-block;
width: calc(100% - 46px);
}
}
.no-active-grading-periods {
border-top-style: dashed;
.GradingPeriod__Actions {
flex: 0 0;
margin-right: 12px;
}

View File

@ -244,14 +244,7 @@ $icon-size: 1.4rem;
}
}
#enrollment_inactive_notice {
width: 100%;
border-bottom: 1px solid $ic-border-light;
border-radius: 0;
text-align: center;
}
#enrollment_concluded_notice {
#enrollment_inactive_notice, #enrollment_concluded_notice, #closed_gp_notice {
width: 100%;
border-bottom: 1px solid $ic-border-light;
border-radius: 0;

View File

@ -86,8 +86,12 @@
</div>
<% end -%>
<div id="assignments_outside_current_periods" class="hidden">
<h4><%= t('headers.past_grading_periods_prevent_editing', "The following assignments cannot be edited because they are not in a current grading period:") %></h4>
<div id="prevented-new-assignment-in-closed-period" style="display:none;">
<h4><%= t("Some assignments could not be created because they would fall in a closed grading period.") %></h4>
</div>
<div id="prevented-grading-ungradeable-submission" style="display:none;">
<h4><%= t("Some submissions are not gradeable; grade changes for those submissions have been ignored.") %></h4>
</div>
<style>

View File

@ -109,9 +109,6 @@
<%= t('Publish grades to SIS') %>
</a></li>
<% end %>
<% if @gradebook_is_editable %>
<li><a class="dialog_opener" role="button" aria-controls="assignment_group_weights_dialog" href="<%= context_url(@context, :context_assignments_url) %>"><%= t('Set Group Weights') %></a></li>
<% end %>
<li><a class="student_names_toggle" href="#" role="button"><%= t('Hide Student Names') %></a></li>
<li><a data-arrange-columns-by="due_date" href="#"><label><%= t('Arrange columns by due date') %><input type="radio" name="arrange-columns-by" /></label></a></li>
<li><a data-arrange-columns-by="assignment_group" href="#"><label><%= t('Arrange columns by assignment group') %><input type="radio" name="arrange-columns-by" /></label></a></li>

View File

@ -140,6 +140,9 @@
<div id="enrollment_concluded_notice" class="alert alert-info" style="display: none;">
<span><%= t "Notice: Concluded Student" %></span>
</div>
<div id="closed_gp_notice" class="alert alert-info" style="display:none">
<span><%= t "Notice: The grading period is closed for this student" %></span>
</div>
<div id="full_width_container" style="display:none">
<div id="left_side" class="full_height">
<div id="left_side_inner">

View File

@ -4,7 +4,9 @@
<form class="submission_details_grade_form form-inline">
<strong><label for="{{label}}">{{#t 'grade_form_label'}}Grade:{{/t}}</label></strong>
{{> grading_box}}
<button style="float: right;" class="btn" type="submit">{{#t "update_grade"}}Update Grade{{/t}}</button>
{{#unless isInPastGradingPeriodAndNotAdmin}}
<button style="float: right;" class="btn" type="submit">{{#t "update_grade"}}Update Grade{{/t}}</button>
{{/unless}}
</form>
{{#if speedGraderUrl }}
<a class="more-details-link" target="_blank" href="{{speedGraderUrl}}">{{#t "more_details_in_the_speedgrader"}}More details in the SpeedGrader{{/t}}</a>

View File

@ -0,0 +1,8 @@
class PopulateGradingPeriodCloseDates < ActiveRecord::Migration
tag :postdeploy
def up
DataFixup::PopulateGradingPeriodCloseDates.run
end
end

View File

@ -0,0 +1,8 @@
module DataFixup::PopulateGradingPeriodCloseDates
def self.run
GradingPeriod.
where(close_date: nil).
where.not(end_date: nil).
update_all("close_date=end_date")
end
end

View File

@ -73,10 +73,10 @@ class GradebookImporter
@all_assignments = @context.assignments
.published
.gradeable
.select([:id, :title, :points_possible, :grading_type, :due_at])
.select([:id, :title, :points_possible, :grading_type, :updated_at, :context_id, :context_type, :group_category_id, :created_at, :due_at])
.index_by(&:id)
@all_students = @context.all_students
.select(['users.id', :name, :sortable_name])
.select(['users.id', :name, :sortable_name, 'users.updated_at'])
.index_by(&:id)
@assignments = nil
@ -85,6 +85,10 @@ class GradebookImporter
@pseudonyms_by_login_id = {}
@students = []
@pp_row = []
@warning_messages = {
prevented_new_assignment_creation_in_closed_period: false,
prevented_grading_ungradeable_submission: false
}
csv_stream do |row|
already_processed = check_for_non_student_row(row)
@ -94,47 +98,54 @@ class GradebookImporter
end
end
@assignments_outside_current_periods = []
if @context.feature_enabled? :multiple_grading_periods
current_period = GradingPeriod.for(@context).current.first
if current_period.present?
with_due_at = @assignments.select(&:due_at)
in_current_period = select_in_grading_period(with_due_at, @context, current_period)
@assignments_outside_current_periods = with_due_at - in_current_period
@assignments = @assignments - @assignments_outside_current_periods
end
end
@missing_assignments = []
@missing_assignments = @all_assignments.values - @assignments if @missing_assignment
@missing_students = []
@missing_students = @all_students.values - @students if @missing_student
# look up existing score for everything that was provided
assignment_ids = @missing_assignment ? @all_assignments.values : @assignments
user_ids = @missing_student ? @all_students.values : @students
@original_submissions = @context.submissions
.select([:assignment_id, :user_id, :score, :excused])
.where(:assignment_id => (@missing_assignment ? @all_assignments.values : @assignments),
:user_id => (@missing_student ? @all_students.values : @students))
.preload(:assignment)
.select(['submissions.id', :assignment_id, :user_id, :score, :excused, :cached_due_date, 'submissions.updated_at'])
.where(assignment_id: assignment_ids, user_id: user_ids)
.map do |submission|
{
:user_id => submission.user_id,
:assignment_id => submission.assignment_id,
:score => submission.excused? ? "EX" : submission.score.to_s
user_id: submission.user_id,
assignment_id: submission.assignment_id,
score: submission.excused? ? "EX" : submission.score.to_s,
gradeable: submission.grants_right?(@user, :grade)
}
end
# cache the score on the existing object
original_submissions_by_student = @original_submissions.inject({}) do |r, s|
r[s[:user_id]] ||= {}
r[s[:user_id]][s[:assignment_id]] = s[:score]
r[s[:user_id]][s[:assignment_id]] ||= {}
r[s[:user_id]][s[:assignment_id]][:score] = s[:score]
r[s[:user_id]][s[:assignment_id]][:gradeable] = s[:gradeable]
r
end
@students.each do |student|
student.gradebook_importer_submissions.each do |submission|
submission['original_grade'] = original_submissions_by_student[student.id]
.try(:[], submission['assignment_id'].to_i)
submission_assignment_id = submission.fetch('assignment_id').to_i
assignment = original_submissions_by_student
.fetch(student.id, {})
.fetch(submission_assignment_id, {})
submission['original_grade'] = assignment.fetch(:score, nil)
submission['gradeable'] = assignment.fetch(:gradable, nil)
if submission.fetch('gradeable').nil?
assignment = @all_assignments[submission['assignment_id']] || @context.assignments.build
new_submission = Submission.new
new_submission.user = student
new_submission.assignment = assignment
new_submission.cache_due_date
submission['gradeable'] = new_submission.grants_right?(@user, :grade)
end
end
end
@ -151,19 +162,29 @@ class GradebookImporter
# Have potentially mixed case excused in grade match case
# expectations for the compare so it doesn't look changed
submission['grade'] = 'EX' if submission['grade'].to_s.upcase == 'EX'
submission['grade'] == submission['original_grade'] ||
no_change = submission['grade'] == submission['original_grade'] ||
(submission['original_grade'].present? && submission['grade'].present? && submission['original_grade'].to_f == submission['grade'].to_f) ||
(submission['original_grade'].blank? && submission['grade'].blank?)
if !submission['gradeable'] && !no_change
@warning_messages[:prevented_grading_ungradeable_submission] = true
end
no_change || !submission['gradeable']
end
end
indexes_to_delete.reverse_each do |idx|
@assignments.delete_at(idx)
@students.each do |student|
student.gradebook_importer_submissions.delete_at(idx)
end
end
@students.each do |student|
student.gradebook_importer_submissions.select! { |sub| sub['gradeable'] }
end
@unchanged_assignments = !indexes_to_delete.empty?
@students = [] if @assignments.empty?
end
@ -175,8 +196,18 @@ class GradebookImporter
@students.delete_if { |s| prior_enrollment_ids.include? s.id }
@original_submissions = [] unless @missing_student || @missing_assignment
@upload.gradebook = self.as_json
if prevent_new_assignment_creation?
@assignments.delete_if do |assignment|
new_assignment = assignment.new_record?
if new_assignment
@warning_messages[:prevented_new_assignment_creation_in_closed_period] = true
end
new_assignment
end
end
@upload.gradebook = self.as_json
@upload.save!
end
@ -243,8 +274,7 @@ class GradebookImporter
def strip_non_assignment_columns(row)
drop_student_information_columns(row)
while row.last =~ /Current Score|Current Points|Final Score|Final Points|Final Grade/
while row.last =~ /Current Score|Current Points|Current Grade|Final Score|Final Points|Final Grade/
row.pop
end
@ -266,9 +296,18 @@ class GradebookImporter
assignment ||= Assignment.new(:title => title || name_and_id)
assignment.previous_id = assignment.id
assignment.id ||= NegativeId.generate
@missing_assignment ||= assignment.new_record?
assignment
end
end.compact
end
def prevent_new_assignment_creation?
return false unless context.feature_enabled?(:multiple_grading_periods)
return false if @user.admin_of_root_account?(@context.root_account)
last_period = GradingPeriod.for(@context).sort_by(&:end_date).last
last_period.present? && last_period.closed?
end
def process_pp(row)
@ -312,20 +351,20 @@ class GradebookImporter
end
def process_submissions(row, student)
l = []
importer_submissions = []
@assignments.each_with_index do |assignment, idx|
assignment_id = assignment.new_record? ? assignment.id : assignment.previous_id
grade = row[idx + @student_columns]
unless assignment_visible_to_student(student, assignment, assignment_id, @visible_assignments)
if !assignment_visible_to_student(student, assignment, assignment_id, @visible_assignments)
grade = ''
end
new_submission = {
'grade' => grade,
'assignment_id' => assignment_id
}
l << new_submission
importer_submissions << new_submission
end
student.gradebook_importer_submissions = l
student.gradebook_importer_submissions = importer_submissions
end
def assignment_visible_to_student(student, assignment, assignment_id, visible_assignments)
@ -346,8 +385,7 @@ class GradebookImporter
},
:original_submissions => @original_submissions,
:unchanged_assignments => @unchanged_assignments,
:assignments_outside_current_periods =>
@assignments_outside_current_periods.map { |a| assignment_to_hash(a) }
:warning_messages => @warning_messages
}
end
@ -420,8 +458,7 @@ class GradebookImporter
:previous_id => assignment.previous_id,
:title => assignment.title,
:points_possible => assignment.points_possible,
:grading_type => assignment.grading_type,
:due_at => assignment.due_at
:grading_type => assignment.grading_type
}
end

View File

@ -184,17 +184,12 @@ define([
$("#no_changes_detected").show();
}
if ( uploadedGradebook.assignments_outside_current_periods.length > 0 ) {
var $assignment_list = $("<ul></ul>");
var assignments = uploadedGradebook.assignments_outside_current_periods;
if (uploadedGradebook.warning_messages.prevented_new_assignment_creation_in_closed_period) {
$("#prevented-new-assignment-in-closed-period").show();
}
_.each(assignments, function(assignment) {
var $li = $("<li></li>");
$li.text(assignment.title);
$assignment_list.append($li);
});
$("#assignments_outside_current_periods").removeClass("hidden");
$("#assignments_outside_current_periods").append($assignment_list);
if (uploadedGradebook.warning_messages.prevented_grading_ungradeable_submission) {
$("#prevented-grading-ungradeable-submission").show();
}
},

View File

@ -17,6 +17,7 @@
*/
define([
'jsx/speed_grader/mgp',
'jsx/grading/helpers/OutlierScoreHelper',
'jst/speed_grader/student_viewed_at',
'jst/speed_grader/submissions_dropdown',
@ -53,7 +54,7 @@ define([
'vendor/jquery.getScrollbarWidth' /* getScrollbarWidth */,
'vendor/jquery.scrollTo' /* /\.scrollTo/ */,
'vendor/ui.selectmenu' /* /\.selectmenu/ */
], function(OutlierScoreHelper, studentViewedAtTemplate, submissionsDropdownTemplate, speechRecognitionTemplate, round, _, INST, I18n, $, tz, userSettings, htmlEscape, rubricAssessment, SpeedgraderSelectMenu, SpeedgraderHelpers, turnitinInfoTemplate, turnitinScoreTemplate) {
], function(MGP, OutlierScoreHelper, studentViewedAtTemplate, submissionsDropdownTemplate, speechRecognitionTemplate, round, _, INST, I18n, $, tz, userSettings, htmlEscape, rubricAssessment, SpeedgraderSelectMenu, SpeedgraderHelpers, turnitinInfoTemplate, turnitinScoreTemplate) {
// PRIVATE VARIABLES AND FUNCTIONS
// all of the $ variables here are to speed up access to dom nodes,
@ -119,6 +120,7 @@ define([
$submission_not_newest_notice = $("#submission_not_newest_notice"),
$enrollment_inactive_notice = $("#enrollment_inactive_notice"),
$enrollment_concluded_notice = $("#enrollment_concluded_notice"),
$closed_gp_notice = $("#closed_gp_notice"),
$submission_files_container = $("#submission_files_container"),
$submission_files_list = $("#submission_files_list"),
$submission_attachment_viewed_at = $("#submission_attachment_viewed_at_container"),
@ -146,7 +148,8 @@ define([
groupLabel = I18n.t("group", "Group"),
gradeeLabel = studentLabel,
utils,
crocodocSessionTimer;
crocodocSessionTimer,
isAdmin = _.include(ENV.current_user_roles, "admin");
utils = {
getParam: function(name){
@ -1149,6 +1152,7 @@ define([
$full_width_container.removeClass("with_enrollment_notice");
$enrollment_inactive_notice.hide();
$enrollment_concluded_notice.hide();
$closed_gp_notice.hide();
EG.setGradeReadOnly(true); // disabling now will keep it from getting undisabled unintentionally by disableWhileLoading
if (ENV.grading_role == 'moderator' && this.currentStudent.submission_state == 'not_graded') {
@ -1506,15 +1510,14 @@ define([
var $submission_to_view = $("#submission_to_view"),
submissionToViewVal = $submission_to_view.val(),
currentSelectedIndex = currentIndex(this, submissionToViewVal),
isMostRecent = this.currentStudent &&
this.currentStudent.submission &&
this.currentStudent.submission.submission_history &&
this.currentStudent.submission.submission_history.length - 1 === currentSelectedIndex,
submission = this.currentStudent &&
this.currentStudent.submission &&
this.currentStudent.submission.submission_history &&
this.currentStudent.submission.submission_history[currentSelectedIndex] &&
this.currentStudent.submission.submission_history[currentSelectedIndex].submission
submissionHolder = this.currentStudent &&
this.currentStudent.submission,
submissionHistory = submissionHolder && submissionHolder.submission_history,
isMostRecent = submissionHistory &&
submissionHistory.length - 1 === currentSelectedIndex,
submission = submissionHistory &&
submissionHistory[currentSelectedIndex] &&
submissionHistory[currentSelectedIndex].submission
|| {},
inlineableAttachments = [],
browserableAttachments = [];
@ -1611,9 +1614,16 @@ define([
);
var isConcluded = EG.isStudentConcluded(this.currentStudent.id);
var isClosedForStudent = MGP.assignmentClosedForStudent(this.currentStudent, jsonData);
$enrollment_concluded_notice.showIf(isConcluded);
$closed_gp_notice.showIf(isClosedForStudent);
SpeedgraderHelpers.setRightBarDisabled(isConcluded);
if (isConcluded) {
EG.setGradeReadOnly((typeof submissionHolder != "undefined" &&
submissionHolder.submission_type === "online_quiz") ||
isConcluded ||
(isClosedForStudent && !isAdmin));
if (isConcluded || isClosedForStudent) {
$full_width_container.addClass("with_enrollment_notice");
}
},
@ -2204,9 +2214,6 @@ define([
var grade = EG.getGradeToShow(submission, ENV.grading_role);
$grade.val(grade);
EG.setGradeReadOnly((typeof submission != "undefined" &&
submission.submission_type === 'online_quiz') ||
EG.isStudentConcluded(EG.currentStudent.id));
$('#submit_same_score').hide();
if (typeof submission != "undefined" && submission.score !== null) {
@ -2323,14 +2330,43 @@ define([
}
};
function getAssignmentOverrides() {
return $.getJSON("/api/v1/courses/" + ENV.course_id +
"/assignments/" + ENV.assignment_id + "/overrides");
}
function getGradingPeriods() {
var dfd = $.Deferred();
// treating failure as a success here since grading periods 404 when not
// enabled
$.ajaxJSON("/api/v1/courses/" + ENV.course_id + "/grading_periods",
"GET", {},
function(response) { dfd.resolve(response.grading_periods); },
function() { dfd.resolve([]); },
{skipDefaultError: true}
);
return dfd;
}
function gotData(assignmentOverridesResponse, gradingPeriods, speedGraderJsonResponse) {
var speedGraderJSON = speedGraderJsonResponse[0];
var assignmentOverrides = assignmentOverridesResponse[0];
speedGraderJSON.gradingPeriods = gradingPeriods;
speedGraderJSON.assignmentOverrides = assignmentOverrides;
window.jsonData = speedGraderJSON;
EG.jsonReady();
}
return {
setup: function() {
// fire off the request to get the jsonData
window.jsonData = {};
$.ajaxJSON(window.location.pathname+ '.json' + window.location.search, 'GET', {}, function(json) {
jsonData = json;
$(EG.jsonReady);
});
var speedGraderJsonDfd = $.getJSON(window.location.pathname+ '.json' + window.location.search);
$.when(getAssignmentOverrides(), getGradingPeriods(), speedGraderJsonDfd).then(gotData);
//run the stuff that just attaches event handlers and dom stuff, but does not need the jsonData
$(document).ready(function() {

View File

@ -12,12 +12,14 @@ define [
id: "1",
title: "Q1",
startDate: new Date("2015-09-01T12:00:00Z"),
endDate: new Date("2015-10-31T12:00:00Z")
endDate: new Date("2015-10-31T12:00:00Z"),
closeDate: new Date("2015-11-07T12:00:00Z")
},{
id: "2",
title: "Q2",
startDate: new Date("2015-11-01T12:00:00Z"),
endDate: new Date("2015-12-31T12:00:00Z")
endDate: new Date("2015-12-31T12:00:00Z"),
closeDate: new Date("2016-01-07T12:00:00Z")
}
],
permissions: { read: true, create: true, update: true, delete: true },
@ -41,12 +43,14 @@ define [
id: "1",
title: "Q1",
start_date: new Date("2015-09-01T12:00:00Z"),
end_date: new Date("2015-10-31T12:00:00Z")
end_date: new Date("2015-10-31T12:00:00Z"),
close_date: new Date("2015-11-07T12:00:00Z")
},{
id: "2",
title: "Q2",
start_date: new Date("2015-11-01T12:00:00Z"),
end_date: new Date("2015-12-31T12:00:00Z")
end_date: new Date("2015-12-31T12:00:00Z"),
close_date: new Date("2016-01-07T12:00:00Z")
}
],
permissions: { read: true, create: true, update: true, delete: true },
@ -86,7 +90,7 @@ define [
start()
@server.respond()
asyncTest "uses the creation date as the title if the grading period set does not have a title", ->
asyncTest "creates a title from the creation date when the set has no title", ->
untitledSets =
grading_period_sets: [
id: "1"
@ -95,21 +99,44 @@ define [
permissions: { read: true, create: true, update: true, delete: true }
created_at: "2015-11-29T12:00:00Z"
]
@server.respondWith "GET", /grading_period_sets/, [200, { "Content-Type":"application/json", "Link": @fakeHeaders }, JSON.stringify untitledSets]
jsonString = JSON.stringify(untitledSets)
@server.respondWith(
"GET",
/grading_period_sets/,
[200, { "Content-Type":"application/json", "Link": @fakeHeaders }, jsonString]
)
api.list()
.then (sets) =>
equal sets[0].title, "Set created Nov 29, 2015"
start()
@server.respond()
# no fail for CheatDepaginator
# asyncTest "SKIPPED: rejects the promise upon errors", ->
# @server.respondWith "GET", /grading_period_sets/, [500, {"Content-Type":"application/json"}, "FAIL"]
# api.list().catch (error) =>
# equal error, "FAIL"
# start()
# @server.respond()
asyncTest "uses the endDate as the closeDate when a period has no closeDate", ->
setsWithoutPeriodCloseDate =
grading_period_sets: [
id: "1"
title: "Fall 2015"
grading_periods: [{
id: "1",
title: "Q1",
start_date: new Date("2015-09-01T12:00:00Z"),
end_date: new Date("2015-10-31T12:00:00Z"),
close_date: null
}]
permissions: { read: true, create: true, update: true, delete: true }
created_at: "2015-11-29T12:00:00Z"
]
jsonString = JSON.stringify(setsWithoutPeriodCloseDate)
@server.respondWith(
"GET",
/grading_period_sets/,
[200, { "Content-Type":"application/json", "Link": @fakeHeaders }, jsonString]
)
api.list()
.then (sets) =>
deepEqual sets[0].gradingPeriods[0].closeDate, new Date("2015-10-31T12:00:00Z")
start()
@server.respond()
deserializedSetCreating = {
title: "Fall 2015",
@ -190,12 +217,14 @@ define [
id: "1",
title: "Q1",
start_date: new Date("2015-09-01T12:00:00Z"),
end_date: new Date("2015-10-31T12:00:00Z")
end_date: new Date("2015-10-31T12:00:00Z"),
close_date: new Date("2015-11-07T12:00:00Z")
},{
id: "2",
title: "Q2",
start_date: new Date("2015-11-01T12:00:00Z"),
end_date: new Date("2015-12-31T12:00:00Z")
end_date: new Date("2015-12-31T12:00:00Z"),
close_date: null
}
],
permissions: { read: true, create: true, update: true, delete: true }

View File

@ -9,12 +9,14 @@ define [
id: "1",
title: "Q1",
startDate: new Date("2015-09-01T12:00:00Z"),
endDate: new Date("2015-10-31T12:00:00Z")
endDate: new Date("2015-10-31T12:00:00Z"),
closeDate: new Date("2015-11-07T12:00:00Z")
},{
id: "2",
title: "Q2",
startDate: new Date("2015-11-01T12:00:00Z"),
endDate: new Date("2015-12-31T12:00:00Z")
endDate: new Date("2015-12-31T12:00:00Z"),
closeDate: new Date("2016-01-07T12:00:00Z")
}
]
@ -24,12 +26,14 @@ define [
id: "1",
title: "Q1",
start_date: new Date("2015-09-01T12:00:00Z"),
end_date: new Date("2015-10-31T12:00:00Z")
end_date: new Date("2015-10-31T12:00:00Z"),
close_date: new Date("2015-11-07T12:00:00Z")
},{
id: "2",
title: "Q2",
start_date: new Date("2015-11-01T12:00:00Z"),
end_date: new Date("2015-12-31T12:00:00Z")
end_date: new Date("2015-12-31T12:00:00Z"),
close_date: new Date("2016-01-07T12:00:00Z")
}
]
}
@ -54,6 +58,25 @@ define [
deepEqual periods, deserializedPeriods
start()
asyncTest "uses the endDate as the closeDate when a period has no closeDate", ->
periodsWithoutCloseDate = {
grading_periods: [
{
id: "1",
title: "Q1",
start_date: new Date("2015-09-01T12:00:00Z"),
end_date: new Date("2015-10-31T12:00:00Z"),
close_date: null
}
]
}
successPromise = new Promise (resolve) => resolve({ data: periodsWithoutCloseDate })
@stub(axios, "patch").returns(successPromise)
api.batchUpdate(123, deserializedPeriods)
.then (periods) =>
deepEqual periods[0].closeDate, new Date("2015-10-31T12:00:00Z")
start()
asyncTest "rejects the promise upon errors", ->
failurePromise = new Promise (_, reject) => reject("FAIL")
@stub(axios, "patch").returns(failurePromise)

View File

@ -112,42 +112,12 @@ define [
excusedOptionText = $('.grading_value option')[3].text
deepEqual excusedOptionText, 'Excused'
test "is enabled when multiple grading periods are not enabled", ->
ENV.GRADEBOOK_OPTIONS.multiple_grading_periods_enabled = false
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), false
test "is enabled when no grading periods are in the past", ->
ENV.GRADEBOOK_OPTIONS.latest_end_date_of_admin_created_grading_periods_in_the_past = null
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), false
test "is enabled when current user roles are undefined", ->
ENV.current_user_roles = null
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), false
test "is enabled when the current user is an admin", ->
ENV.current_user_roles = ['admin']
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), false
test "is disabled for assignments in the previous grading period", ->
@assignment.due_at = tz.parse("2013-10-01T09:59:00Z")
test "is disabled for assignments locked for the given student", ->
@user.assignment_1.gradeLocked = true
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), true
test "is disabled for assignments due exactly at the end of the previous grading period", ->
@assignment.due_at = tz.parse("2013-10-01T10:00:00Z")
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), true
test "is enabled for assignments after the previous grading period", ->
@assignment.due_at = tz.parse("2013-10-01T10:01:00Z")
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), false
test "is enabled for assignments without a due date", ->
@assignment.due_at = null
test "is enabled for assignments not locked for the given student", ->
@user.assignment_1.gradeLocked = false
new SubmissionDetailsDialog(@assignment, @user, @options).open()
equal $('#student_grading_1').prop('disabled'), false

View File

@ -1,10 +1,7 @@
define [
'compiled/gradebook2/GradebookHelpers'
'jsx/gradebook/grid/constants'
'timezone'
'compiled/models/Assignment'
], (GradebookHelpers, GradebookConstants, tz, Assignment) ->
], (GradebookHelpers, GradebookConstants) ->
module "GradebookHelpers#noErrorsOnPage",
setup: ->
@mockFind = @mock($, "find")
@ -42,48 +39,3 @@ define [
"the max allowed length AND there are no DOM errors", ->
@mockFind.expects("find").once().returns([])
ok GradebookHelpers.maxLengthErrorShouldBeShown(GradebookConstants.MAX_NOTE_LENGTH + 1)
module "GradebookHelpers#gradeIsLocked",
setup: ->
@env =
current_user_roles: []
GRADEBOOK_OPTIONS:
multiple_grading_periods_enabled: true
latest_end_date_of_admin_created_grading_periods_in_the_past: "2013-10-01T10:00:00Z"
@assignment = new Assignment(id: 1)
@assignment.due_at = tz.parse("2013-10-01T10:00:00Z")
@assignment.grading_type = 'points'
test "gradeIsLocked is false when multiple grading periods are not enabled", ->
@env.GRADEBOOK_OPTIONS.multiple_grading_periods_enabled = false
equal GradebookHelpers.gradeIsLocked(@assignment, @env), false
test "gradeIsLocked is false when no grading periods are in the past", ->
@env.GRADEBOOK_OPTIONS.latest_end_date_of_admin_created_grading_periods_in_the_past = null
equal GradebookHelpers.gradeIsLocked(@assignment, @env), false
test "gradeIsLocked is false when current user roles are undefined", ->
@env.current_user_roles = null
equal GradebookHelpers.gradeIsLocked(@assignment, @env), false
@env.current_user_roles = undefined
equal GradebookHelpers.gradeIsLocked(@assignment, @env), false
test "gradeIsLocked is false when the current user is an admin", ->
@env.current_user_roles = ['admin']
equal GradebookHelpers.gradeIsLocked(@assignment, @env), false
test "gradeIsLocked is true for assignments in the previous grading period", ->
@assignment.due_at = tz.parse("2013-10-01T09:59:00Z")
equal GradebookHelpers.gradeIsLocked(@assignment, @env), true
test "gradeIsLocked is true for assignments due exactly at the end of the previous grading period", ->
@assignment.due_at = tz.parse("2013-10-01T10:00:00Z")
equal GradebookHelpers.gradeIsLocked(@assignment, @env), true
test "gradeIsLocked is false for assignments after the previous grading period", ->
@assignment.due_at = tz.parse("2013-10-01T10:01:00Z")
equal GradebookHelpers.gradeIsLocked(@assignment, @env), false
test "gradeIsLocked is false for assignments without a due date", ->
@assignment.due_at = null
equal GradebookHelpers.gradeIsLocked(@assignment, @env), false

View File

@ -43,514 +43,11 @@ define [
indexedOverrides: Gradebook.prototype.indexedOverrides
indexedGradingPeriods: _.indexBy(@gradingPeriods, 'id')
module "Gradebook2#submissionOutsideOfGradingPeriod - assignment with no overrides",
setupThis: (options) ->
customOptions = options || {}
defaults =
mgpEnabled: true
isAllGradingPeriods: -> false
gradingPeriodToShow: '8'
lastGradingPeriodAndDueAtNull: -> false
dateIsInGradingPeriod: -> false
_.defaults customOptions, defaults, gradebookStubs()
setup: ->
@subOutsideOfPeriod = Gradebook.prototype.submissionOutsideOfGradingPeriod
@submission = { assignment_id: '1' }
@student = { id: '5', sections: ['101','102','103'] }
@gradingPeriods = {
'8': { id: '8', start_date: '2015-04-01T06:00:00Z', end_date: '2015-05-01T05:59:59Z', is_last: false }
'10': { id: '10', start_date: '2015-05-05T06:00:00Z', end_date: '2015-06-01T05:59:59Z', is_last: true }
}
@overrides = { studentOverrides: {}, sectionOverrides: {} }
teardown: ->
test 'returns false if multiple grading periods is not enabled', ->
self = @setupThis(mgpEnabled: false)
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
deepEqual result, false
test 'returns false if "All Grading Periods" is selected', ->
self = @setupThis(mgpEnabled: true, isAllGradingPeriods: -> true)
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
deepEqual result, false
test 'returns false if the assignment has a null due_at and the last grading period is selected', ->
assignments = { '1': { id: '1', has_overrides: false, due_at: null } }
self = @setupThis(assignments: assignments, gradingPeriodToShow: '10', lastGradingPeriodAndDueAtNull: -> true)
lastGradingPeriodAndDueAtNullSpy = @spy(self, 'lastGradingPeriodAndDueAtNull')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok lastGradingPeriodAndDueAtNullSpy.called
ok _.isNull(lastGradingPeriodAndDueAtNullSpy.args[0][1])
deepEqual result, false
test 'returns true if the assignment has a null due_at and the last grading period is not selected', ->
assignments = { '1': { id: '1', has_overrides: false, due_at: null } }
self = @setupThis(assignments: assignments)
lastGradingPeriodAndDueAtNullSpy = @spy(self, 'lastGradingPeriodAndDueAtNull')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok lastGradingPeriodAndDueAtNullSpy.called
ok _.isNull(lastGradingPeriodAndDueAtNullSpy.args[0][1])
deepEqual result, true
test 'returns false if the assignment due_at falls in the selected grading period', ->
assignments = { '1': { id: '1', has_overrides: false, due_at: tz.parse('2015-04-15T06:00:00Z') } }
self = @setupThis(assignments: assignments, dateIsInGradingPeriod: -> true)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-04-15T06:00:00Z')
deepEqual result, false
test 'returns true if the assignment due_at falls outside of the selected grading period', ->
assignments = { '1': { id: '1', has_overrides: false, due_at: tz.parse('2015-05-15T06:00:00Z') } }
self = @setupThis(assignments: assignments, dateIsInGradingPeriod: -> false)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-05-15T06:00:00Z')
deepEqual result, true
module "Gradebook2#submissionOutsideOfGradingPeriod - assignment with one student override that applies to the student",
setupThis: (options, overrides) ->
customOptions = options || {}
assignments = { '1': { id: '1', has_overrides: true, due_at: tz.parse('2015-05-15T06:00:00Z'), overrides: overrides } }
defaults =
mgpEnabled: true
assignments: assignments
isAllGradingPeriods: -> false
gradingPeriodToShow: '8'
lastGradingPeriodAndDueAtNull: -> false
dateIsInGradingPeriod: -> false
_.defaults customOptions, defaults, gradebookStubs()
generateOverrides: (dueAt) ->
[
{ student_ids: ['5'], due_at: dueAt }
]
setup: ->
@subOutsideOfPeriod = Gradebook.prototype.submissionOutsideOfGradingPeriod
@submission = { assignment_id: '1' }
@student = { id: '5', sections: ['101','102','103'] }
@gradingPeriods = {
'8': { id: '8', start_date: '2015-04-01T06:00:00Z', end_date: '2015-05-01T05:59:59Z', is_last: false }
'10': { id: '10', start_date: '2015-05-05T06:00:00Z', end_date: '2015-06-01T05:59:59Z', is_last: true }
}
teardown: ->
test 'returns false if the due_at on the override falls within the grading period', ->
overrides = @generateOverrides('2015-04-15T06:00:00Z')
self = @setupThis({ dateIsInGradingPeriod: -> true }, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-04-15T06:00:00Z')
deepEqual result, false
test 'returns true if the due_at on the override falls outside of the grading period', ->
overrides = @generateOverrides('2015-06-15T06:00:00Z')
self = @setupThis({}, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-06-15T06:00:00Z')
deepEqual result, true
test 'returns true if the due_at on the override is null and the grading period is not the last', ->
overrides = @generateOverrides(null)
self = @setupThis({}, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok _.isNull(dateIsInGradingPeriodSpy.args[0][1])
deepEqual result, true
test 'returns false if the due_at on the override is null and the grading period is the last', ->
overrides = @generateOverrides(null)
self = @setupThis({ gradingPeriodToShow: '10', lastGradingPeriodAndDueAtNull: -> true }, overrides)
lastGradingPeriodAndDueAtNullSpy = @spy(self, 'lastGradingPeriodAndDueAtNull')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok lastGradingPeriodAndDueAtNullSpy.called
ok _.isNull(lastGradingPeriodAndDueAtNullSpy.args[0][1])
deepEqual result, false
module "Gradebook2#submissionOutsideOfGradingPeriod - assignment with one section override that applies to the student",
setupThis: (options, overrides) ->
customOptions = options || {}
assignments = { '1': { id: '1', has_overrides: true, due_at: tz.parse('2015-05-15T06:00:00Z'), overrides: overrides } }
defaults =
mgpEnabled: true
assignments: assignments
isAllGradingPeriods: -> false
gradingPeriodToShow: '8'
lastGradingPeriodAndDueAtNull: -> false
dateIsInGradingPeriod: -> false
_.defaults customOptions, defaults, gradebookStubs()
generateOverrides: (dueAt) ->
[
{ student_ids: ['5'], due_at: dueAt }
]
setup: ->
@subOutsideOfPeriod = Gradebook.prototype.submissionOutsideOfGradingPeriod
@submission = { assignment_id: '1' }
@student = { id: '5', sections: ['101','102','103'] }
@gradingPeriods = {
'8': { id: '8', start_date: '2015-04-01T06:00:00Z', end_date: '2015-05-01T05:59:59Z', is_last: false }
'10': { id: '10', start_date: '2015-05-05T06:00:00Z', end_date: '2015-06-01T05:59:59Z', is_last: true }
}
teardown: ->
test 'returns false if the due_at on the override falls within the grading period', ->
overrides = @generateOverrides('2015-04-15T06:00:00Z')
self = @setupThis({ dateIsInGradingPeriod: -> true }, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-04-15T06:00:00Z')
deepEqual result, false
test 'returns true if the due_at on the override falls outside of the grading period', ->
overrides = @generateOverrides('2015-06-15T06:00:00Z')
self = @setupThis({}, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-06-15T06:00:00Z')
deepEqual result, true
test 'returns true if the due_at on the override is null and the grading period is not the last', ->
overrides = @generateOverrides(null)
self = @setupThis({}, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok _.isNull(dateIsInGradingPeriodSpy.args[0][1])
deepEqual result, true
test 'returns false if the due_at on the override is null and the grading period is the last', ->
overrides = @generateOverrides(null)
self = @setupThis({ gradingPeriodToShow: '10', lastGradingPeriodAndDueAtNull: -> true }, overrides)
lastGradingPeriodAndDueAtNullSpy = @spy(self, 'lastGradingPeriodAndDueAtNull')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok lastGradingPeriodAndDueAtNullSpy.called
ok _.isNull(lastGradingPeriodAndDueAtNullSpy.args[0][1])
deepEqual result, false
module "Gradebook2#submissionOutsideOfGradingPeriod - assignment with one group that applies to the student",
setupThis: (options, overrides) ->
customOptions = options || {}
assignments = { '1': { id: '1', has_overrides: true, due_at: tz.parse('2015-05-15T06:00:00Z'), overrides: overrides } }
defaults =
mgpEnabled: true
assignments: assignments
isAllGradingPeriods: -> false
gradingPeriodToShow: '8'
lastGradingPeriodAndDueAtNull: -> false
dateIsInGradingPeriod: -> false
_.defaults customOptions, defaults, gradebookStubs()
generateOverrides: (dueAt) ->
[
{ group_id: '202', due_at: dueAt }
]
setup: ->
@subOutsideOfPeriod = Gradebook.prototype.submissionOutsideOfGradingPeriod
@submission = { assignment_id: '1' }
@student = { id: '5', sections: ['101','102','103'], group_ids: ['202'] }
@gradingPeriods = {
'8': { id: '8', start_date: '2015-04-01T06:00:00Z', end_date: '2015-05-01T05:59:59Z', is_last: false }
'10': { id: '10', start_date: '2015-05-05T06:00:00Z', end_date: '2015-06-01T05:59:59Z', is_last: true }
}
teardown: ->
test 'returns false if the due_at on the override falls within the grading period', ->
overrides = @generateOverrides('2015-04-15T06:00:00Z')
self = @setupThis({ dateIsInGradingPeriod: -> true }, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-04-15T06:00:00Z')
deepEqual result, false
test 'returns true if the due_at on the override falls outside of the grading period', ->
overrides = @generateOverrides('2015-06-15T06:00:00Z')
self = @setupThis({}, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-06-15T06:00:00Z')
deepEqual result, true
test 'returns true if the due_at on the override is null and the grading period is not the last', ->
overrides = @generateOverrides(null)
self = @setupThis({}, overrides)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok _.isNull(dateIsInGradingPeriodSpy.args[0][1])
deepEqual result, true
test 'returns false if the due_at on the override is null and the grading period is the last', ->
overrides = @generateOverrides(null)
self = @setupThis({ gradingPeriodToShow: '10', lastGradingPeriodAndDueAtNull: -> true }, overrides)
lastGradingPeriodAndDueAtNullSpy = @spy(self, 'lastGradingPeriodAndDueAtNull')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok lastGradingPeriodAndDueAtNullSpy.called
ok _.isNull(lastGradingPeriodAndDueAtNullSpy.args[0][1])
deepEqual result, false
module "Gradebook2#submissionOutsideOfGradingPeriod - assignment with one override that does not apply to the student",
setupThis: (options) ->
customOptions = options || {}
defaults =
mgpEnabled: true
isAllGradingPeriods: -> false
gradingPeriodToShow: '8'
lastGradingPeriodAndDueAtNull: -> false
dateIsInGradingPeriod: -> false
_.defaults customOptions, defaults, gradebookStubs()
setup: ->
@subOutsideOfPeriod = Gradebook.prototype.submissionOutsideOfGradingPeriod
@submission = { assignment_id: '1' }
@student = { id: '5', sections: ['101','102','103'] }
@gradingPeriods = {
'8': { id: '8', start_date: '2015-04-01T06:00:00Z', end_date: '2015-05-01T05:59:59Z', is_last: false }
'10': { id: '10', start_date: '2015-05-05T06:00:00Z', end_date: '2015-06-01T05:59:59Z', is_last: true }
}
@overrides = {
studentOverrides: { '1': { '18': { student_ids: ['18'], due_at: '2015-04-15T06:00:00Z' } } }
sectionOverrides: {}
}
teardown: ->
test 'returns true if the assignment is only visible to overrides', ->
assignments = { '1': { id: '1', has_overrides: true, due_at: null, only_visible_to_overrides: true } }
self = @setupThis( assignments: assignments)
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
deepEqual result, true
test 'returns true if the assignment due_at is outside of the grading period', ->
assignments = { '1': { id: '1', has_overrides: true, due_at: tz.parse('2015-05-15T06:00:00Z') } }
self = @setupThis(assignments: assignments)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-05-15T06:00:00Z')
deepEqual result, true
test 'returns false if the assignment due_at is within the grading period', ->
assignments = { '1': { id: '1', has_overrides: true, due_at: tz.parse('2015-04-15T06:00:00Z') } }
self = @setupThis(assignments: assignments, dateIsInGradingPeriod: -> true)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-04-15T06:00:00Z')
deepEqual result, false
test 'returns true if the assignment due_at is null and the grading period is not the last', ->
assignments = { '1': { id: '1', has_overrides: true, due_at: null } }
self = @setupThis(assignments: assignments)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok dateIsInGradingPeriodSpy.called
ok _.isNull(dateIsInGradingPeriodSpy.args[0][1])
deepEqual result, true
test 'returns false if the assignment due_at is null and the grading period is the last', ->
assignments = { '1': { id: '1', has_overrides: true, due_at: null } }
self = @setupThis(assignments: assignments, gradingPeriodToShow: '10', lastGradingPeriodAndDueAtNull: -> true)
lastGradingPeriodAndDueAtNullSpy = @spy(self, 'lastGradingPeriodAndDueAtNull')
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
result = isOutsidePeriod(@submission, @student, @gradingPeriods, @overrides)
ok lastGradingPeriodAndDueAtNullSpy.called
ok _.isNull(lastGradingPeriodAndDueAtNullSpy.args[0][1])
deepEqual result, false
module "Gradebook2#submissionOutsideOfGradingPeriod - assignment with two overrides that apply to the student",
setupThis: (options, overrides) ->
customOptions = options || {}
assignments = { '1': { id: '1', has_overrides: true, due_at: tz.parse('2015-05-15T06:00:00Z'), overrides: overrides } }
defaults =
mgpEnabled: true
assignments: assignments
isAllGradingPeriods: -> false
gradingPeriodToShow: '8'
lastGradingPeriodAndDueAtNull: -> false
dateIsInGradingPeriod: -> false
_.defaults customOptions, defaults, gradebookStubs()
generateOverrides: (date1, date2) ->
[
{ student_ids: ['5'], due_at: date1 }
{ course_section_id: '101', assignment_id: '1', due_at: date2 }
]
setup: ->
@subOutsideOfPeriod = Gradebook.prototype.submissionOutsideOfGradingPeriod
@submission = { assignment_id: '1' }
@student = { id: '5', sections: ['101','102','103'] }
@gradingPeriods = {
'8': { id: '8', start_date: '2015-04-01T06:00:00Z', end_date: '2015-05-01T05:59:59Z', is_last: false }
'10': { id: '10', start_date: '2015-05-05T06:00:00Z', end_date: '2015-06-01T05:59:59Z', is_last: true }
}
teardown: ->
test 'returns false if the latest date of the two overrides falls within the grading period', ->
overrides = @generateOverrides('2015-03-01T06:00:00Z', '2015-04-15T06:00:00Z')
self = @setupThis({ dateIsInGradingPeriod: -> true }, overrides)
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-04-15T06:00:00Z')
deepEqual result, false
test 'returns true if the latest date of the two overrides falls outside the grading period' +
'(even if the earlier date falls within the grading period)', ->
overrides = @generateOverrides('2015-04-15T06:00:00Z', '2015-05-15T06:00:00Z')
self = @setupThis({}, overrides)
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok +dateIsInGradingPeriodSpy.args[0][1] == +tz.parse('2015-05-15T06:00:00Z')
deepEqual result, true
test 'returns false if either date is null and the last grading period is selected', ->
overrides = @generateOverrides(null, '2015-05-15T06:00:00Z')
self = @setupThis({ gradingPeriodToShow: '10', lastGradingPeriodAndDueAtNull: -> true }, overrides)
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
lastGradingPeriodAndDueAtNullSpy = @spy(self, 'lastGradingPeriodAndDueAtNull')
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok lastGradingPeriodAndDueAtNullSpy.called
ok _.isNull(lastGradingPeriodAndDueAtNullSpy.args[0][1])
deepEqual result, false
test 'returns true if either date is null and the last grading period is not selected', ->
overrides = @generateOverrides(null, '2015-05-15T06:00:00Z')
self = @setupThis({}, overrides)
isOutsidePeriod = @subOutsideOfPeriod.bind(self)
dateIsInGradingPeriodSpy = @spy(self, 'dateIsInGradingPeriod')
result = isOutsidePeriod(@submission, @student, @gradingPeriods, overrides)
ok dateIsInGradingPeriodSpy.called
ok _.isNull(dateIsInGradingPeriodSpy.args[0][1])
deepEqual result, true
module "Gradebook2#lastGradingPeriodAndDueAtNull",
setup: ->
@lastGradingPeriodAndDueAtNull = Gradebook.prototype.lastGradingPeriodAndDueAtNull
teardown: ->
test 'returns true if it is the last grading period and the due at is null', ->
gradingPeriod = { is_last: true }
dueAt = null
ok @lastGradingPeriodAndDueAtNull(gradingPeriod, dueAt)
test 'returns false if it is not the last grading period', ->
gradingPeriod = { is_last: false }
dueAt = null
notOk @lastGradingPeriodAndDueAtNull(gradingPeriod, dueAt)
test 'returns false if the dueAt is something other than null', ->
gradingPeriod = { is_last: true }
dueAt = tz.parse('2015-05-15T06:00:00Z')
notOk @lastGradingPeriodAndDueAtNull(gradingPeriod, dueAt)
module "Gradebook2#dateIsInGradingPeriod",
setup: ->
@dateIsInGradingPeriod = Gradebook.prototype.dateIsInGradingPeriod
@gradingPeriod = { start_date: tz.parse('2015-04-01T06:00:00Z'), end_date: tz.parse('2015-05-01T06:00:00Z') }
teardown: ->
test 'returns false if the date is null', ->
date = null
notOk @dateIsInGradingPeriod(@gradingPeriod, date)
test 'returns true if the date falls between the grading period start date and end date', ->
date = tz.parse('2015-04-15T06:00:00Z')
ok @dateIsInGradingPeriod(@gradingPeriod, date)
test 'returns false if the date is before the grading period start date', ->
date = tz.parse('2015-03-15T06:00:00Z')
notOk @dateIsInGradingPeriod(@gradingPeriod, date)
test 'returns false if the date is after the grading period end date', ->
date = tz.parse('2015-05-15T06:00:00Z')
notOk @dateIsInGradingPeriod(@gradingPeriod, date)
test 'returns false if the date is the same as the grading period start date', ->
date = tz.parse('2015-04-01T06:00:00Z')
notOk @dateIsInGradingPeriod(@gradingPeriod, date)
test 'returns true if the date is the same as the grading period end date', ->
date = tz.parse('2015-05-01T06:00:00Z')
ok @dateIsInGradingPeriod(@gradingPeriod, date)
module "Gradebook2#hideAggregateColumns",
setupThis: (options) ->
customOptions = options || {}
defaults =
mgpEnabled: true
gradingPeriodsEnabled: true
getGradingPeriodToShow: -> '1'
options:
all_grading_periods_totals: false
@ -562,12 +59,12 @@ define [
teardown: ->
test 'returns false if multiple grading periods is disabled', ->
self = @setupThis(mgpEnabled: false, isAllGradingPeriods: -> false)
self = @setupThis(gradingPeriodsEnabled: false, isAllGradingPeriods: -> false)
notOk @hideAggregateColumns.call(self)
test 'returns false if multiple grading periods is disabled, even if isAllGradingPeriods is true', ->
self = @setupThis
mgpEnabled: false
gradingPeriodsEnabled: false
getGradingPeriodToShow: -> '0'
isAllGradingPeriods: -> true

View File

@ -59,19 +59,47 @@ define [
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 'happy'}, { }, student)
notEqual submissionCellResponse.indexOf("grayed-out"), -1
test "#class.formatter, isLocked: true adds grayed-out", ->
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 73 }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("grayed-out") > -1
test "#class.formatter, isLocked: true adds cannot_edit", ->
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 73 }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("cannot_edit") > -1
test "#class.formatter, isLocked: true does not include the cell comment bubble", ->
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 73 }, {}, {}, { isLocked: true })
equal submissionCellResponse.indexOf("gradebook-cell-comment"), -1
test "#class.formatter, isLocked: false doesn't add grayed-out", ->
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 73 }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("grayed-out"), -1
test "#class.formatter, isLocked: false doesn't add cannot_edit", ->
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 73 }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("cannot_edit"), -1
test "#class.formatter, isLocked: false includes the cell comment bubble", ->
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 73 }, {}, {}, { isLocked: false })
ok submissionCellResponse.indexOf("gradebook-cell-comment") > -1
test "#class.formatter, tooltip adds your text to the special classes", ->
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 73 }, {}, {}, { tooltip: "dora_the_explorer" })
ok submissionCellResponse.indexOf("dora_the_explorer") > -1
test "#class.formatter, isInactive: false doesn't add grayed-out", ->
student = { isInactive: false }
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 10}, { }, student)
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 10 }, { }, student)
equal submissionCellResponse.indexOf("grayed-out"), -1
test "#class.formatter, isConcluded adds grayed-out", ->
student = { isConcluded: true }
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 10}, { }, student)
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 10 }, { }, student)
notEqual submissionCellResponse.indexOf("grayed-out"), -1
test "#class.formatter, isConcluded doesn't have grayed-out", ->
student = { isConcluded: false }
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 10}, { }, student)
submissionCellResponse = SubmissionCell.formatter(0, 0, { grade: 10 }, { }, student)
equal submissionCellResponse.indexOf("grayed-out"), -1
test "#letter_grade.formatter, shows EX when submission is excused", ->
@ -88,3 +116,63 @@ define [
@stub(SubmissionCell.prototype, 'cellWrapper').withArgs('B').returns('ok')
formattedResponse = SubmissionCell.letter_grade.formatter(0, 0, {grade: 'B'}, {}, {})
equal formattedResponse, 'ok'
test "#letter_grade.formatter, isLocked: true adds grayed-out", ->
submissionCellResponse = SubmissionCell.letter_grade.formatter(0, 0, { grade: "A" }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("grayed-out") > -1
test "#letter_grade.formatter, isLocked: true adds cannot_edit", ->
submissionCellResponse = SubmissionCell.letter_grade.formatter(0, 0, { grade: "A" }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("cannot_edit") > -1
test "#letter_grade.formatter, isLocked: false doesn't add grayed-out", ->
submissionCellResponse = SubmissionCell.letter_grade.formatter(0, 0, { grade: "A" }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("grayed-out"), -1
test "#letter_grade.formatter, isLocked: false doesn't add cannot_edit", ->
submissionCellResponse = SubmissionCell.letter_grade.formatter(0, 0, { grade: "A" }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("cannot_edit"), -1
test "#letter_grade.formatter, tooltip adds your text to the special classes", ->
submissionCellResponse = SubmissionCell.letter_grade.formatter(0, 0, { grade: "A" }, {}, {}, { tooltip: "dora_the_explorer" })
ok submissionCellResponse.indexOf("dora_the_explorer") > -1
test "#gpa_scale.formatter, isLocked: true adds grayed-out", ->
submissionCellResponse = SubmissionCell.gpa_scale.formatter(0, 0, { grade: 3.2 }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("grayed-out") > -1
test "#gpa_scale.formatter, isLocked: true adds cannot_edit", ->
submissionCellResponse = SubmissionCell.gpa_scale.formatter(0, 0, { grade: 3.2 }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("cannot_edit") > -1
test "#gpa_scale.formatter, isLocked: false doesn't add grayed-out", ->
submissionCellResponse = SubmissionCell.gpa_scale.formatter(0, 0, { grade: 3.2 }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("grayed-out"), -1
test "#gpa_scale.formatter, isLocked: false doesn't add cannot_edit", ->
submissionCellResponse = SubmissionCell.gpa_scale.formatter(0, 0, { grade: 3.2 }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("cannot_edit"), -1
test "#gpa_scale.formatter, tooltip adds your text to the special classes", ->
submissionCellResponse = SubmissionCell.gpa_scale.formatter(0, 0, { grade: 3.2 }, {}, {}, { tooltip: "dora_the_explorer" })
ok submissionCellResponse.indexOf("dora_the_explorer") > -1
test "#pass_fail.formatter, isLocked: true adds grayed-out", ->
submissionCellResponse = SubmissionCell.pass_fail.formatter(0, 0, { grade: "complete" }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("grayed-out") > -1
test "#pass_fail.formatter, isLocked: true adds cannot_edit", ->
submissionCellResponse = SubmissionCell.pass_fail.formatter(0, 0, { grade: "complete" }, {}, {}, { isLocked: true })
ok submissionCellResponse.indexOf("cannot_edit") > -1
test "#pass_fail.formatter, isLocked: false doesn't add grayed-out", ->
submissionCellResponse = SubmissionCell.pass_fail.formatter(0, 0, { grade: "complete" }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("grayed-out"), -1
test "#pass_fail.formatter, isLocked: false doesn't add cannot_edit", ->
submissionCellResponse = SubmissionCell.pass_fail.formatter(0, 0, { grade: "complete" }, {}, {}, { isLocked: false })
equal submissionCellResponse.indexOf("cannot_edit"), -1
test "#pass_fail.formatter, tooltip adds your text to the special classes", ->
submissionCellResponse = SubmissionCell.pass_fail.formatter(0, 0, { grade: "complete" }, {}, {}, { tooltip: "dora_the_explorer" })
ok submissionCellResponse.indexOf("dora_the_explorer") > -1

View File

@ -9,7 +9,6 @@ define [
], (React, $, _, GradingPeriodCollection, fakeENV) ->
TestUtils = React.addons.TestUtils
Simulate = TestUtils.Simulate
module 'GradingPeriodCollection',
setup: ->
@ -23,12 +22,22 @@ define [
@indexData =
"grading_periods":[
{
"id":"1", "start_date":"2015-03-01T06:00:00Z", "end_date":"2015-05-31T05:00:00Z",
"weight":null, "title":"Spring", "permissions": { "update":true, "delete":true }
"id":"1",
"title":"Spring",
"start_date":"2015-03-01T06:00:00Z",
"end_date":"2015-05-31T05:00:00Z",
"close_date":"2015-06-07T05:00:00Z",
"weight":null,
"permissions": { "update":true, "delete":true }
},
{
"id":"2", "start_date":"2015-06-01T05:00:00Z", "end_date":"2015-08-31T05:00:00Z",
"weight":null, "title":"Summer", "permissions": { "update":true, "delete":true }
"id":"2",
"title":"Summer",
"start_date":"2015-06-01T05:00:00Z",
"end_date":"2015-08-31T05:00:00Z",
"close_date":"2015-09-07T05:00:00Z",
"weight":null,
"permissions": { "update":true, "delete":true }
}
]
"grading_periods_read_only": false,
@ -38,12 +47,22 @@ define [
@formattedIndexData =
"grading_periods":[
{
"id":"1", "startDate": new Date("2015-03-01T06:00:00Z"), "endDate": new Date("2015-05-31T05:00:00Z"),
"weight":null, "title":"Spring", "permissions": { "update":true, "delete":true }
"id":"1",
"title":"Spring",
"startDate": new Date("2015-03-01T06:00:00Z"),
"endDate": new Date("2015-05-31T05:00:00Z"),
"closeDate": new Date("2015-06-07T05:00:00Z"),
"weight":null,
"permissions": { "update":true, "delete":true }
},
{
"id":"2", "startDate": new Date("2015-06-01T05:00:00Z"), "endDate": new Date("2015-08-31T05:00:00Z"),
"weight":null, "title":"Summer", "permissions": { "update":true, "delete":true }
"id":"2",
"title":"Summer",
"startDate": new Date("2015-06-01T05:00:00Z"),
"endDate": new Date("2015-08-31T05:00:00Z"),
"closeDate": new Date("2015-09-07T05:00:00Z"),
"weight":null,
"permissions": { "update":true, "delete":true }
}
]
"grading_periods_read_only": false,
@ -52,8 +71,13 @@ define [
@createdPeriodData = "grading_periods":[
{
"id":"3", "start_date":"2015-04-20T05:00:00Z", "end_date":"2015-04-21T05:00:00Z",
"weight":null, "title":"New Period!", "permissions": { "update":true, "delete":true }
"id":"3",
"title":"New Period!",
"start_date":"2015-04-20T05:00:00Z",
"end_date":"2015-04-21T05:00:00Z",
"close_date":"2015-04-28T05:00:00Z",
"weight":null,
"permissions": { "update":true, "delete":true }
}
]
@server.respondWith "GET", ENV.GRADING_PERIODS_URL, [200, {"Content-Type":"application/json"}, JSON.stringify @indexData]
@ -80,30 +104,9 @@ define [
equal @gradingPeriodCollection.refs.grading_period_1.props.readOnly, false
equal @gradingPeriodCollection.refs.grading_period_2.props.readOnly, false
test 'createNewGradingPeriod adds a new period', ->
deepEqual @gradingPeriodCollection.state.periods.length, 2
@gradingPeriodCollection.createNewGradingPeriod()
deepEqual @gradingPeriodCollection.state.periods.length, 3
test 'createNewGradingPeriod adds the new period with a blank title, start date, and end date', ->
@gradingPeriodCollection.createNewGradingPeriod()
newPeriod = _.find(@gradingPeriodCollection.state.periods, (p) => p.id.indexOf('new') > -1)
deepEqual newPeriod.title, ''
deepEqual newPeriod.startDate.getTime(), new Date('').getTime()
deepEqual newPeriod.endDate.getTime(), new Date('').getTime()
test 'deleteGradingPeriod does not call confirmDelete if the grading period is not saved', ->
unsavedPeriod = [
{
"id":"new1", "startDate": new Date("2029-03-01T06:00:00Z"), "endDate": new Date("2030-05-31T05:00:00Z"),
"weight":null, "title":"New Period. I'm not saved yet!",
"permissions": { "update":true, "delete":true }
}
]
@gradingPeriodCollection.setState({periods: unsavedPeriod})
confirmDelete = @stub($.fn, 'confirmDelete')
@gradingPeriodCollection.deleteGradingPeriod('new1')
ok confirmDelete.notCalled
test "renders grading periods with their individual 'closeDate'", ->
deepEqual @gradingPeriodCollection.refs.grading_period_1.props.closeDate, new Date("2015-06-07T05:00:00Z")
deepEqual @gradingPeriodCollection.refs.grading_period_2.props.closeDate, new Date("2015-09-07T05:00:00Z")
test 'deleteGradingPeriod calls confirmDelete if the period being deleted is not new (it is saved server side)', ->
confirmDelete = @stub($.fn, 'confirmDelete')
@ -238,13 +241,6 @@ define [
ok @gradingPeriodCollection.areDatesOverlapping(periodOne)
ok @gradingPeriodCollection.areDatesOverlapping(periodTwo)
test 'renderAddPeriodButton does not render a button if canAddNewPeriods is false (based on permissions)', ->
@gradingPeriodCollection.setState({ canAddNewPeriods: false })
notOk @gradingPeriodCollection.renderAddPeriodButton()
test 'renderAddPeriodButton renders a button if canAddNewPeriods is true (based on permissions)', ->
ok @gradingPeriodCollection.renderAddPeriodButton()
test 'renderSaveButton does not render a button if the user cannot update any of the periods on the page', ->
uneditable = [{
"id":"12", "startDate": new Date("2015-03-01T06:00:00Z"), "endDate": new Date("2015-05-31T05:00:00Z"),

View File

@ -11,6 +11,7 @@ define [
], (React, ReactDOM, $, _, GradingPeriod, fakeENV, DateHelper) ->
TestUtils = React.addons.TestUtils
wrapper = document.getElementById('fixtures')
module 'GradingPeriod',
setup: ->
@ -20,25 +21,27 @@ define [
fakeENV.setup()
ENV.GRADING_PERIODS_URL = "api/v1/courses/1/grading_periods"
@createdPeriodData = "grading_periods":[
{
"id":"3", "start_date":"2015-04-20T05:00:00Z", "end_date":"2015-04-21T05:00:00Z",
"weight":null, "title":"New Period!", "permissions": { "update":true, "delete":true }
}
]
@updatedPeriodData = "grading_periods":[
{
"id":"1", "startDate":"2015-03-01T06:00:00Z", "endDate":"2015-05-31T05:00:00Z",
"weight":null, "title":"Updated Grading Period!", "permissions": { "update":true, "delete":true }
"id":"1",
"title":"Updated Grading Period!",
"startDate":"2015-03-01T06:00:00Z",
"endDate":"2015-05-31T05:00:00Z",
"closeDate":"2015-06-07T05:00:00Z",
"weight":null,
"permissions": { "update":true, "delete":true }
}
]
@server.respondWith "POST", ENV.GRADING_PERIODS_URL, [200, {"Content-Type":"application/json"}, JSON.stringify @createdPeriodData]
@server.respondWith "PUT", ENV.GRADING_PERIODS_URL + "/1", [200, {"Content-Type":"application/json"}, JSON.stringify @updatedPeriodData]
@props =
@server.respond()
renderComponent: (opts = {}) ->
exampleProps =
id: "1"
title: "Spring"
startDate: new Date("2015-03-01T00:00:00Z")
endDate: new Date("2015-05-31T00:00:00Z")
closeDate: new Date("2015-06-07T00:00:00Z")
weight: null
disabled: false
readOnly: false
@ -46,57 +49,64 @@ define [
onDeleteGradingPeriod: ->
updateGradingPeriodCollection: sinon.spy()
@server.respond()
renderComponent: ->
GradingPeriodElement = React.createElement(GradingPeriod, @props)
@gradingPeriod = TestUtils.renderIntoDocument(GradingPeriodElement)
props = _.defaults(opts, exampleProps)
GradingPeriodElement = React.createElement(GradingPeriod, props)
ReactDOM.render(GradingPeriodElement, wrapper)
teardown: ->
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(@gradingPeriod).parentNode)
ReactDOM.unmountComponentAtNode(wrapper)
ENV.GRADING_PERIODS_URL = null
@server.restore()
test 'sets initial state properly', ->
@renderComponent()
equal @gradingPeriod.state.title, @props.title
equal @gradingPeriod.state.startDate, @props.startDate
equal @gradingPeriod.state.endDate, @props.endDate
equal @gradingPeriod.state.weight, @props.weight
gradingPeriod = @renderComponent()
equal gradingPeriod.state.title, "Spring"
deepEqual gradingPeriod.state.startDate, new Date("2015-03-01T00:00:00Z")
deepEqual gradingPeriod.state.endDate, new Date("2015-05-31T00:00:00Z")
equal gradingPeriod.state.weight, null
test 'onDateChange calls replaceInputWithDate', ->
@renderComponent()
replaceInputWithDate = @stub(@gradingPeriod, 'replaceInputWithDate')
@gradingPeriod.onDateChange("startDate", "period_start_date_1")
gradingPeriod = @renderComponent()
replaceInputWithDate = @stub(gradingPeriod, 'replaceInputWithDate')
gradingPeriod.onDateChange("startDate", "period_start_date_1")
ok replaceInputWithDate.calledOnce
test 'onDateChange calls updateGradingPeriodCollection', ->
@renderComponent()
@gradingPeriod.onDateChange("startDate", "period_start_date_1")
ok @gradingPeriod.props.updateGradingPeriodCollection.calledOnce
gradingPeriod = @renderComponent()
gradingPeriod.onDateChange("startDate", "period_start_date_1")
ok gradingPeriod.props.updateGradingPeriodCollection.calledOnce
test 'onTitleChange changes the title state', ->
@renderComponent()
gradingPeriod = @renderComponent()
fakeEvent = { target: { name: "title", value: "MXP: Most Xtreme Primate" } }
@gradingPeriod.onTitleChange(fakeEvent)
deepEqual @gradingPeriod.state.title, "MXP: Most Xtreme Primate"
gradingPeriod.onTitleChange(fakeEvent)
equal gradingPeriod.state.title, "MXP: Most Xtreme Primate"
test 'onTitleChange calls updateGradingPeriodCollection', ->
@renderComponent()
gradingPeriod = @renderComponent()
fakeEvent = { target: { name: "title", value: "MXP: Most Xtreme Primate" } }
@gradingPeriod.onTitleChange(fakeEvent)
ok @gradingPeriod.props.updateGradingPeriodCollection.calledOnce
gradingPeriod.onTitleChange(fakeEvent)
ok gradingPeriod.props.updateGradingPeriodCollection.calledOnce
test 'replaceInputWithDate calls formatDatetimeForDisplay', ->
@renderComponent()
gradingPeriod = @renderComponent()
formatDatetime = @stub(DateHelper, 'formatDatetimeForDisplay')
fakeDateElement = { val: -> }
@gradingPeriod.replaceInputWithDate("startDate", fakeDateElement)
gradingPeriod.replaceInputWithDate("startDate", fakeDateElement)
ok formatDatetime.calledOnce
test "assigns the 'readOnly' property on the template when false", ->
@renderComponent()
equal @gradingPeriod.refs.template.props.readOnly, false
gradingPeriod = @renderComponent()
equal gradingPeriod.refs.template.props.readOnly, false
test "assigns the 'readOnly' property on the template when true", ->
@props.readOnly = true
@renderComponent()
equal @gradingPeriod.refs.template.props.readOnly, true
gradingPeriod = @renderComponent(readOnly: true)
equal gradingPeriod.refs.template.props.readOnly, true
test "assigns the 'closeDate' property", ->
gradingPeriod = @renderComponent()
deepEqual gradingPeriod.refs.template.props.closeDate, new Date("2015-06-07T00:00:00Z")
test "assigns 'endDate' as 'closeDate' when 'closeDate' is not defined", ->
gradingPeriod = @renderComponent(closeDate: null)
deepEqual gradingPeriod.refs.template.props.closeDate, new Date("2015-05-31T00:00:00Z")

View File

@ -5,25 +5,35 @@ define [
'jsx/grading/gradingPeriodTemplate'
], (React, ReactDOM, _, GradingPeriod) ->
TestUtils = React.addons.TestUtils
defaultProps =
title: "Spring"
startDate: new Date("2015-03-01T00:00:00Z")
endDate: new Date("2015-05-31T00:00:00Z")
closeDate: new Date("2015-06-07T00:00:00Z")
id: "1"
permissions: {
update: true
delete: true
}
disabled: false
readOnly: false
onDeleteGradingPeriod: ->
onDateChange: ->
onTitleChange: ->
Simulate = React.addons.TestUtils.Simulate
wrapper = document.getElementById('fixtures')
module 'GradingPeriod with read-only permissions',
renderComponent: (opts) ->
defaultProps =
title: "Spring"
startDate: new Date("2015-03-01T00:00:00Z")
endDate: new Date("2015-05-31T00:00:00Z")
id: "1"
readOnly: false
renderComponent: (opts = {}) ->
readOnlyProps =
permissions: {
update: false
delete: false
}
onDeleteGradingPeriod: ->
@props = _.defaults(opts || {}, defaultProps)
GradingPeriodElement = React.createElement(GradingPeriod, @props)
props = _.defaults(opts, readOnlyProps, defaultProps)
GradingPeriodElement = React.createElement(GradingPeriod, props)
ReactDOM.render(GradingPeriodElement, wrapper)
teardown: ->
@ -41,52 +51,33 @@ define [
gradingPeriod = @renderComponent()
notOk gradingPeriod.refs.deleteButton
test 'renderTitle returns a non-input element (since the grading period is readonly)', ->
test 'renders attributes as read-only', ->
gradingPeriod = @renderComponent()
notEqual gradingPeriod.renderTitle().type, "input"
notEqual gradingPeriod.refs.title.type, "INPUT"
notEqual gradingPeriod.refs.startDate.type, "INPUT"
notEqual gradingPeriod.refs.endDate.type, "INPUT"
test 'renderStartDate returns a non-input element (since the grading period is readonly)', ->
test 'displays the correct attributes', ->
gradingPeriod = @renderComponent()
notEqual gradingPeriod.renderStartDate().type, "input"
equal gradingPeriod.refs.title.textContent, "Spring"
equal gradingPeriod.refs.startDate.textContent, "Mar 1, 2015 at 12am"
equal gradingPeriod.refs.endDate.textContent, "May 31, 2015 at 12am"
test 'renderEndDate returns a non-input element (since the grading period is readonly)', ->
test 'displays the assigned close date', ->
gradingPeriod = @renderComponent()
notEqual gradingPeriod.renderEndDate().type, "input"
equal gradingPeriod.refs.closeDate.textContent, "Jun 7, 2015 at 12am"
test 'displays the correct title', ->
gradingPeriod = @renderComponent()
titleNode = gradingPeriod.refs.title
equal titleNode.textContent, "Spring"
test 'displays the correct start date', ->
gradingPeriod = @renderComponent()
startDateNode = gradingPeriod.refs.startDate
equal startDateNode.textContent, "Mar 1, 2015 at 12am"
test 'displays the correct end date', ->
gradingPeriod = @renderComponent()
endDateNode = gradingPeriod.refs.endDate
equal endDateNode.textContent, "May 31, 2015 at 12am"
test 'uses the end date when close date is not defined', ->
gradingPeriod = @renderComponent(closeDate: null)
equal gradingPeriod.refs.closeDate.textContent, "May 31, 2015 at 12am"
module "GradingPeriod with 'readOnly' set to true",
renderComponent: (opts) ->
defaultProps =
title: "Spring"
startDate: new Date("2015-03-01T00:00:00Z")
endDate: new Date("2015-05-31T00:00:00Z")
id: "1"
renderComponent: (opts = {}) ->
readOnlyProps =
readOnly: true
permissions: {
update: true
delete: true
}
disabled: false
onDeleteGradingPeriod: ->
onDateChange: ->
onTitleChange: ->
@props = _.defaults(opts || {}, defaultProps)
GradingPeriodElement = React.createElement(GradingPeriod, @props)
props = _.defaults(opts, readOnlyProps, defaultProps)
GradingPeriodElement = React.createElement(GradingPeriod, props)
ReactDOM.render(GradingPeriodElement, wrapper)
teardown: ->
@ -104,108 +95,87 @@ define [
gradingPeriod = @renderComponent()
notOk gradingPeriod.refs.deleteButton
test 'renderTitle returns a non-input element (since the grading period is readonly)', ->
test 'renders attributes as read-only', ->
gradingPeriod = @renderComponent()
notEqual gradingPeriod.renderTitle().type, "input"
notEqual gradingPeriod.refs.title.type, "INPUT"
notEqual gradingPeriod.refs.startDate.type, "INPUT"
notEqual gradingPeriod.refs.endDate.type, "INPUT"
test 'renderStartDate returns a non-input element (since the grading period is readonly)', ->
test 'displays the correct attributes', ->
gradingPeriod = @renderComponent()
notEqual gradingPeriod.renderStartDate().type, "input"
equal gradingPeriod.refs.title.textContent, "Spring"
equal gradingPeriod.refs.startDate.textContent, "Mar 1, 2015 at 12am"
equal gradingPeriod.refs.endDate.textContent, "May 31, 2015 at 12am"
test 'renderEndDate returns a non-input element (since the grading period is readonly)', ->
test 'displays the assigned close date', ->
gradingPeriod = @renderComponent()
notEqual gradingPeriod.renderEndDate().type, "input"
equal gradingPeriod.refs.closeDate.textContent, "Jun 7, 2015 at 12am"
test 'displays the correct title', ->
gradingPeriod = @renderComponent()
titleNode = gradingPeriod.refs.title
equal titleNode.textContent, "Spring"
test 'displays the correct start date', ->
gradingPeriod = @renderComponent()
startDateNode = gradingPeriod.refs.startDate
equal startDateNode.textContent, "Mar 1, 2015 at 12am"
test 'displays the correct end date', ->
gradingPeriod = @renderComponent()
endDateNode = gradingPeriod.refs.endDate
equal endDateNode.textContent, "May 31, 2015 at 12am"
test 'uses the end date when close date is not defined', ->
gradingPeriod = @renderComponent(closeDate: null)
equal gradingPeriod.refs.closeDate.textContent, "May 31, 2015 at 12am"
module 'editable GradingPeriod',
setup: ->
@props =
title: "Spring"
startDate: new Date("2015-03-01T00:00:00Z")
endDate: new Date("2015-05-31T00:00:00Z")
id: "1"
permissions: {
update: true
delete: true
}
disabled: false
readOnly: false
onDeleteGradingPeriod: ->
onDateChange: ->
onTitleChange: ->
GradingPeriodElement = React.createElement(GradingPeriod, @props)
@gradingPeriod = ReactDOM.render(GradingPeriodElement, wrapper)
renderComponent: (opts = {}) ->
props = _.defaults(opts, defaultProps)
GradingPeriodElement = React.createElement(GradingPeriod, props)
ReactDOM.render(GradingPeriodElement, wrapper)
teardown: ->
ReactDOM.unmountComponentAtNode(wrapper)
test 'renders a delete button', ->
ok @gradingPeriod.renderDeleteButton()
gradingPeriod = @renderComponent()
ok gradingPeriod.refs.deleteButton
test 'renderTitle returns an input element (since the grading period is editable)', ->
equal @gradingPeriod.renderTitle().type, "input"
test 'renders with input fields', ->
gradingPeriod = @renderComponent()
equal gradingPeriod.refs.title.tagName, "INPUT"
equal gradingPeriod.refs.startDate.tagName, "INPUT"
equal gradingPeriod.refs.endDate.tagName, "INPUT"
test 'renderStartDate returns an input element (since the grading period is editable)', ->
equal @gradingPeriod.renderStartDate().type, "input"
test 'displays the correct attributes', ->
gradingPeriod = @renderComponent()
equal gradingPeriod.refs.title.value, "Spring"
equal gradingPeriod.refs.startDate.value, "Mar 1, 2015 at 12am"
equal gradingPeriod.refs.endDate.value, "May 31, 2015 at 12am"
test 'renderEndDate returns an input element (since the grading period is editable)', ->
equal @gradingPeriod.renderEndDate().type, "input"
test 'uses the end date for close date', ->
gradingPeriod = @renderComponent()
equal gradingPeriod.refs.closeDate.textContent, "May 31, 2015 at 12am"
test 'displays the correct title', ->
titleNode = @gradingPeriod.refs.title
equal titleNode.value, "Spring"
test "calls onClick handler for clicks on 'delete grading period'", ->
deleteSpy = sinon.spy()
gradingPeriod = @renderComponent(onDeleteGradingPeriod: deleteSpy)
Simulate.click(gradingPeriod.refs.deleteButton)
ok deleteSpy.calledOnce
test 'displays the correct start date', ->
startDateNode = @gradingPeriod.refs.startDate
equal startDateNode.value, "Mar 1, 2015 at 12am"
test 'displays the correct end date', ->
endDateNode = @gradingPeriod.refs.endDate
equal endDateNode.value, "May 31, 2015 at 12am"
test "ignores clicks on 'delete grading period' when disabled", ->
deleteSpy = sinon.spy()
gradingPeriod = @renderComponent(onDeleteGradingPeriod: deleteSpy, disabled: true)
Simulate.click(gradingPeriod.refs.deleteButton)
notOk deleteSpy.called
module 'custom prop validation for editable periods',
renderComponent: (opts = {}) ->
props = _.defaults(opts, defaultProps)
GradingPeriodElement = React.createElement(GradingPeriod, props)
ReactDOM.render(GradingPeriodElement, wrapper)
setup: ->
@consoleError = @stub(console, 'error')
@props =
title: "Spring"
startDate: new Date("2015-03-01T00:00:00Z")
endDate: new Date("2015-05-31T00:00:00Z")
id: "1"
permissions: {
update: true
delete: true
}
disabled: false
readOnly: false
onDeleteGradingPeriod: ->
onDateChange: ->
onTitleChange: ->
teardown: ->
ReactDOM.unmountComponentAtNode(wrapper)
test 'does not warn of invalid props if all required props are present and of the correct type', ->
React.createElement(GradingPeriod, @props)
@renderComponent()
ok @consoleError.notCalled
test 'warns if required props are missing', ->
delete @props.disabled
React.createElement(GradingPeriod, @props)
@renderComponent(disabled: null)
ok @consoleError.calledOnce
test 'warns if required props are of the wrong type', ->
@props.onDeleteGradingPeriod = "a/s/l?"
React.createElement(GradingPeriod, @props)
@renderComponent(onDeleteGradingPeriod: "invalid-type")
ok @consoleError.calledOnce

View File

@ -0,0 +1,679 @@
define([
'underscore',
'timezone',
'jsx/gradebook/SubmissionStateMap'
], (_, tz, SubmissionStateMap) => {
const student = {
id: '1',
group_ids: ['1'],
sections: ['1']
};
const tooltipKeys = {
NOT_IN_ANY_GP: "not_in_any_grading_period",
IN_ANOTHER_GP: "in_another_grading_period",
IN_CLOSED_GP: "in_closed_grading_period",
NONE: null
};
function createMap(opts={}) {
const defaults = {
gradingPeriodsEnabled: false,
selectedGradingPeriodID: '0',
isAdmin: false,
gradingPeriods: []
};
const params = Object.assign(defaults, opts);
return new SubmissionStateMap(params);
}
function createAndSetupMap(assignment, opts={}) {
const map = createMap(opts);
const assignments = {};
assignments[assignment.id] = assignment;
map.setup([student], assignments);
return map;
}
function createGradingPeriod(opts={}) {
const defaults = {
id: '1',
is_last: false,
closed: false
};
return Object.assign(defaults, opts);
}
function createOverride({ type, id, dueAt }={}) {
const override = {
assignment_id: '1',
due_at: dueAt,
};
if (type === 'student') {
override.student_ids = [id];
} else if (type === 'section') {
override.course_section_id = id;
} else {
override.group_id = id;
}
return override;
}
function createAssignment({ dueAt, overrides, gradedButNotAssigned }={}) {
const assignment = {
id: '1',
only_visible_to_overrides: false,
assignment_visibility: [],
due_at: null,
has_overrides: false
};
if (dueAt === undefined) {
assignment.only_visible_to_overrides = true;
} else {
assignment.due_at = tz.parse(dueAt);
}
if (overrides) {
assignment.has_overrides = true;
assignment.overrides = overrides;
const overrideForStudent = _.any(overrides, function(override) {
const includesStudent = override.student_ids && _.contains(override.student_ids, student.id);
const includesSection = _.contains(student.sections, override.course_section_id);
const includesGroup = _.contains(student.group_ids, override.group_id);
return includesStudent || includesSection || includesGroup;
});
const studentGradedButNotAssigned = gradedButNotAssigned && _.contains(gradedButNotAssigned, student.id);
if (overrideForStudent || studentGradedButNotAssigned) assignment.assignment_visibility.push(student.id);
}
return assignment;
}
// 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
// to be able to require javascript files that live in the spec directory
const OTHER_STUDENT_ID = '2';
module('SubmissionStateMap with MGP disabled');
test('submission has grade hidden for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, { gradingPeriodsEnabled: false });
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, true);
});
test('submission has grade visible for an unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: null });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
const map = createAndSetupMap(assignment, { gradingPeriodsEnabled: false });
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
module('SubmissionStateMap with MGP enabled and all grading periods selected', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02' });
const lastPeriod = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: '0', gradingPeriods };
}
});
test('submission has grade hidden for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with assignment due in a closed grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with assignment due in a non-closed grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_OPEN_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with assignment due outside of any grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_AFTER_LAST_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment due in a closed grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment due in a non-closed grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment due outside of any grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in a closed grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in a non-closed grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const mapOptions = Object.assign(this.mapOptions);
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with multiple applicable overrides with the latest due date due outside of any grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const mapOptions = Object.assign(this.mapOptions);
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: null });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const mapOptions = Object.assign(this.mapOptions);
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
module('SubmissionStateMap with MGP enabled and a non-closed grading period selected that is not the last grading period', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02' });
const lastPeriod = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_SELECTED_PERIOD = '2015-08-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: openPeriod.id, gradingPeriods };
}
});
test('submission has grade hidden for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 hidden for an unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 hidden for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
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 the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
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 hidden for an assigned student with overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
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 hidden for an assigned student with overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: null });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, true);
});
module('SubmissionStateMap with MGP enabled and a closed grading period selected that is not the last grading period', {
setup() {
const firstClosedPeriod = createGradingPeriod({ id: '1', start_date: '2015-06-01', end_date: '2015-06-30', close_date: '2015-07-02', closed: true });
const secondClosedPeriod = createGradingPeriod({ id: '2', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '3', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02'});
const lastPeriod = createGradingPeriod({ id: '4', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-05-15';
this.DATE_IN_SELECTED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [firstClosedPeriod, secondClosedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: secondClosedPeriod.id, gradingPeriods };
}
});
test('submission has grade hidden for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 hidden for an unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 hidden for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_AFTER_LAST_PERIOD });
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 the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
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 hidden for an assigned student with overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
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 hidden for an assigned student with overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, true);
});
module('SubmissionStateMap with MGP enabled and the last grading period selected which is not closed', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02' });
const lastPeriod = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_IN_SELECTED_PERIOD = '2015-09-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: lastPeriod.id, gradingPeriods };
}
});
test('submission has grade hidden for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 hidden for an unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 hidden for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
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 the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
module('SubmissionStateMap with MGP enabled and the last grading period selected which is closed', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-12-25' });
const lastPeriodAndClosed = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true, closed: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_IN_SELECTED_PERIOD = '2015-09-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriodAndClosed];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: lastPeriodAndClosed.id, gradingPeriods };
}
});
test('submission has grade hidden for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 hidden for an unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 hidden for an assigned student with assignment due outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
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 the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade hidden for an assigned student with overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
test('submission has grade visible for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.hideGrade, false);
});
});

View File

@ -0,0 +1,919 @@
define([
'underscore',
'timezone',
'jsx/gradebook/SubmissionStateMap'
], (_, tz, SubmissionStateMap) => {
const student = {
id: '1',
group_ids: ['1'],
sections: ['1']
};
const tooltipKeys = {
NOT_IN_ANY_GP: "not_in_any_grading_period",
IN_ANOTHER_GP: "in_another_grading_period",
IN_CLOSED_GP: "in_closed_grading_period",
NONE: null
};
function createMap(opts={}) {
const defaults = {
gradingPeriodsEnabled: false,
selectedGradingPeriodID: '0',
isAdmin: false,
gradingPeriods: []
};
const params = Object.assign(defaults, opts);
return new SubmissionStateMap(params);
}
function createAndSetupMap(assignment, opts={}) {
const map = createMap(opts);
const assignments = {};
assignments[assignment.id] = assignment;
map.setup([student], assignments);
return map;
}
function createGradingPeriod(opts={}) {
const defaults = {
id: '1',
is_last: false,
closed: false
};
return Object.assign(defaults, opts);
}
function createOverride({ type, id, dueAt }={}) {
const override = {
assignment_id: '1',
due_at: dueAt,
};
if (type === 'student') {
override.student_ids = [id];
} else if (type === 'section') {
override.course_section_id = id;
} else {
override.group_id = id;
}
return override;
}
function createAssignment({ dueAt, overrides, gradedButNotAssigned }={}) {
const assignment = {
id: '1',
only_visible_to_overrides: false,
assignment_visibility: [],
due_at: null,
has_overrides: false
};
if (dueAt === undefined) {
assignment.only_visible_to_overrides = true;
} else {
assignment.due_at = tz.parse(dueAt);
}
if (overrides) {
assignment.has_overrides = true;
assignment.overrides = overrides;
const overrideForStudent = _.any(overrides, function(override) {
const includesStudent = override.student_ids && _.contains(override.student_ids, student.id);
const includesSection = _.contains(student.sections, override.course_section_id);
const includesGroup = _.contains(student.group_ids, override.group_id);
return includesStudent || includesSection || includesGroup;
});
const studentGradedButNotAssigned = gradedButNotAssigned && _.contains(gradedButNotAssigned, student.id);
if (overrideForStudent || studentGradedButNotAssigned) assignment.assignment_visibility.push(student.id);
}
return assignment;
}
// 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
// to be able to require javascript files that live in the spec directory
const OTHER_STUDENT_ID = '2';
module('SubmissionStateMap with MGP disabled');
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, { gradingPeriodsEnabled: false });
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
test('submission is unlocked for an unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: null });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
const map = createAndSetupMap(assignment, { gradingPeriodsEnabled: false });
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
module('SubmissionStateMap with MGP enabled and all grading periods selected', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02' });
const lastPeriod = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: '0', gradingPeriods };
}
});
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unlocked for an unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with assignment due in a closed grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
test('user is admin: submission is unlocked for an assigned student with assignment due in a closed grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
const mapOptions = Object.assign(this.mapOptions, { isAdmin: true });
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with assignment due in a non-closed grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_OPEN_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with assignment due outside of any grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_AFTER_LAST_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment due in a closed grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
test('user is admin: submission is unlocked for an assigned student with overridden assignment due in a closed grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const mapOptions = Object.assign(this.mapOptions, { isAdmin: true })
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment due in a non-closed grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment due outside of any grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in a closed grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
test('user is admin: submission is unlocked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in a closed grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const mapOptions = Object.assign(this.mapOptions, { isAdmin: true });
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in a non-closed grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const mapOptions = Object.assign(this.mapOptions);
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date due outside of any grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const mapOptions = Object.assign(this.mapOptions);
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: null });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const mapOptions = Object.assign(this.mapOptions);
const map = createAndSetupMap(assignment, mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
module('SubmissionStateMap with MGP enabled and a non-closed grading period selected that is not the last grading period', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02' });
const lastPeriod = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_SELECTED_PERIOD = '2015-08-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: openPeriod.id, gradingPeriods };
}
});
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
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 unlocked for an assigned student with assignment due in the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
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 overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unlocked for an assigned student with overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 unlocked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: null });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
module('SubmissionStateMap with MGP enabled and a closed grading period selected that is not the last grading period', {
setup() {
const firstClosedPeriod = createGradingPeriod({ id: '1', start_date: '2015-06-01', end_date: '2015-06-30', close_date: '2015-07-02', closed: true });
const secondClosedPeriod = createGradingPeriod({ id: '2', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '3', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02'});
const lastPeriod = createGradingPeriod({ id: '4', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-05-15';
this.DATE_IN_SELECTED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [firstClosedPeriod, secondClosedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: secondClosedPeriod.id, gradingPeriods };
}
});
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_AFTER_LAST_PERIOD });
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 the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
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 with no due date', function() {
const assignment = createAssignment({ dueAt: null });
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 overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
module('SubmissionStateMap -- user is admin with MGP enabled and a closed grading period selected that is not the last grading period', {
setup() {
const firstClosedPeriod = createGradingPeriod({ id: '1', start_date: '2015-06-01', end_date: '2015-06-30', close_date: '2015-07-02', closed: true });
const secondClosedPeriod = createGradingPeriod({ id: '2', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '3', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02'});
const lastPeriod = createGradingPeriod({ id: '4', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-05-15';
this.DATE_IN_SELECTED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [firstClosedPeriod, secondClosedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: secondClosedPeriod.id, gradingPeriods, isAdmin: true };
}
});
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_AFTER_LAST_PERIOD });
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 unlocked for an assigned student with assignment due in the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
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 overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unlocked for an assigned student with overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 unlocked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
module('SubmissionStateMap with MGP enabled and the last grading period selected which is not closed', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-09-02' });
const lastPeriod = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_IN_SELECTED_PERIOD = '2015-09-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriod];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: lastPeriod.id, gradingPeriods };
}
});
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
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 unlocked for an assigned student with assignment due in the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unlocked for an assigned student with overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 unlocked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
module('SubmissionStateMap with MGP enabled and the last grading period selected which is closed', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-12-25' });
const lastPeriodAndClosed = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true, closed: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_IN_SELECTED_PERIOD = '2015-09-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriodAndClosed];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: lastPeriodAndClosed.id, gradingPeriods };
}
});
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
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 the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
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 with no due date', function() {
const assignment = createAssignment({ dueAt: null });
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 overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
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 overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, true);
});
module('SubmissionStateMap -- user is admin with MGP enabled and the last grading period selected which is closed', {
setup() {
const closedPeriod = createGradingPeriod({ id: '1', start_date: '2015-07-01', end_date: '2015-07-31', close_date: '2015-08-02', closed: true });
const openPeriod = createGradingPeriod({ id: '2', start_date: '2015-08-01', end_date: '2015-08-31', close_date: '2015-12-25' });
const lastPeriodAndClosed = createGradingPeriod({ id: '3', start_date: '2015-09-01', end_date: '2015-09-30', close_date: '2015-10-02', is_last: true, closed: true });
this.DATE_BEFORE_FIRST_PERIOD = '2015-06-15';
this.DATE_IN_CLOSED_PERIOD = '2015-07-15';
this.DATE_IN_OPEN_PERIOD = '2015-08-15';
this.DATE_IN_SELECTED_PERIOD = '2015-09-15';
this.DATE_AFTER_LAST_PERIOD = '2015-10-15';
const gradingPeriods = [closedPeriod, openPeriod, lastPeriodAndClosed];
this.mapOptions = { gradingPeriodsEnabled: true, selectedGradingPeriodID: lastPeriodAndClosed.id, gradingPeriods, isAdmin: true };
}
});
test('submission is locked for an unassigned student with no submission or ungraded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unassigned student with a graded submission', function() {
const override = createOverride({ type: 'student', id: OTHER_STUDENT_ID, dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [override], gradedButNotAssigned: [student.id] });
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 outside of the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_CLOSED_PERIOD });
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 unlocked for an assigned student with assignment due in the selected grading period', function() {
const assignment = createAssignment({ dueAt: this.DATE_IN_SELECTED_PERIOD });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with assignment with no due date', function() {
const assignment = createAssignment({ dueAt: null });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment due outside of the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
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 unlocked for an assigned student with overridden assignment due in the selected grading period', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with no due date', function() {
const override = createOverride({ type: 'student', id: student.id, dueAt: null });
const assignment = createAssignment({ overrides: [override] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is locked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date outside of the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_OPEN_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
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 unlocked for an assigned student with overridden assignment with multiple applicable overrides with the latest due date in the selected grading period', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: this.DATE_IN_CLOSED_PERIOD });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_BEFORE_FIRST_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_IN_SELECTED_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
test('submission is unlocked for an assigned student with overridden assignment with with multiple applicable overrides with at least one override with no due date', function() {
const studentOverride = createOverride({ type: 'student', id: student.id, dueAt: null });
const sectionOverride = createOverride({ type: 'section', id: student.sections[0], dueAt: this.DATE_IN_CLOSED_PERIOD });
const groupOverride = createOverride({ type: 'group', id: student.group_ids[0], dueAt: this.DATE_AFTER_LAST_PERIOD });
const assignment = createAssignment({ overrides: [studentOverride, sectionOverride, groupOverride] });
const map = createAndSetupMap(assignment, this.mapOptions);
const state = map.getSubmissionState({ user_id: student.id, assignment_id: assignment.id });
equal(state.locked, false);
});
});

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,8 @@ define([
id: "1",
title: "We did it! We did it! We did it! #dora #boots",
startDate: new Date("2015-01-01T20:11:00+00:00"),
endDate: new Date("2015-03-01T00:00:00+00:00")
endDate: new Date("2015-03-01T00:00:00+00:00"),
closeDate: new Date("2015-03-08T00:00:00+00:00")
},
readOnly: false,
onEdit: () => {},
@ -75,6 +76,12 @@ define([
equal(endDate, "End Date: Mar 1, 2015 at 12am");
});
test("displays the close date in a friendly format", function() {
let period = this.renderComponent();
const closeDate = ReactDOM.findDOMNode(period.refs.closeDate).textContent;
equal(closeDate, "Close Date: Mar 8, 2015 at 12am");
});
test("calls the 'onEdit' callback when the edit button is clicked", function() {
let spy = sinon.spy();
let period = this.renderComponent({onEdit: spy});

View File

@ -7,9 +7,18 @@ define([
const wrapper = document.getElementById('fixtures');
const Simulate = React.addons.TestUtils.Simulate;
const examplePeriod = {
id: '1',
title: 'Q1',
startDate: new Date("2015-11-01T12:00:00Z"),
endDate: new Date("2015-12-31T12:00:00Z"),
closeDate: new Date("2016-01-07T12:00:00Z")
};
module('GradingPeriodForm', {
renderComponent: function(opts={}) {
const defaults = {
period: examplePeriod,
disabled: false,
onSave: () => {},
onCancel: () => {}
@ -23,43 +32,112 @@ define([
}
});
test('mounts', function() {
test("sets form values from 'period' props", function() {
let form = this.renderComponent();
ok(form.isMounted());
equal(form.refs.title.value, 'Q1');
equal(form.refs.startDate.refs.dateInput.value, 'Nov 1, 2015 at 12pm');
equal(form.refs.endDate.refs.dateInput.value, 'Dec 31, 2015 at 12pm');
equal(form.refs.closeDate.refs.dateInput.value, 'Jan 7 at 12pm');
});
test('renders with the save button enabled', function() {
let form = this.renderComponent();
let saveButton = React.findDOMNode(form.refs.saveButton);
let saveButton = ReactDOM.findDOMNode(form.refs.saveButton);
equal(saveButton.disabled, false);
});
test('renders with the cancel button enabled', function() {
let form = this.renderComponent();
let cancelButton = React.findDOMNode(form.refs.saveButton);
let cancelButton = ReactDOM.findDOMNode(form.refs.saveButton);
equal(cancelButton.disabled, false);
});
test('optionally renders with the save and cancel buttons disabled', function() {
let form = this.renderComponent({disabled: true});
let saveButton = React.findDOMNode(form.refs.saveButton);
let cancelButton = React.findDOMNode(form.refs.cancelButton);
let saveButton = ReactDOM.findDOMNode(form.refs.saveButton);
let cancelButton = ReactDOM.findDOMNode(form.refs.cancelButton);
equal(saveButton.disabled, true);
equal(cancelButton.disabled, true);
});
test("auto-updates 'closeDate' when not already set and 'endDate' changes", function() {
let incompletePeriod = _.extend({}, examplePeriod, { closeDate: null });
let form = this.renderComponent({period: incompletePeriod});
let endDateInput = ReactDOM.findDOMNode(form.refs.endDate.refs.dateInput);
endDateInput.value = 'Dec 31, 2015 at 12pm';
endDateInput.dispatchEvent(new Event("change"));
equal(form.refs.endDate.refs.dateInput.value, 'Dec 31, 2015 at 12pm');
equal(form.refs.closeDate.refs.dateInput.value, 'Dec 31, 2015 at 12pm');
});
test("auto-updates 'closeDate' when set equal to 'endDate' and 'endDate' changes", function() {
let consistentPeriod = _.extend({}, examplePeriod, { closeDate: examplePeriod.endDate });
let form = this.renderComponent({period: consistentPeriod});
let endDateInput = ReactDOM.findDOMNode(form.refs.endDate.refs.dateInput);
endDateInput.value = 'Dec 30, 2015 at 12pm';
endDateInput.dispatchEvent(new Event("change"));
equal(form.refs.endDate.refs.dateInput.value, 'Dec 30, 2015 at 12pm');
equal(form.refs.closeDate.refs.dateInput.value, 'Dec 30, 2015 at 12pm');
});
test("preserves 'closeDate' when not set equal to 'endDate' and 'endDate' changes", function() {
let form = this.renderComponent();
let endDateInput = ReactDOM.findDOMNode(form.refs.endDate.refs.dateInput);
endDateInput.value = 'Dec 30, 2015 at 12pm';
endDateInput.dispatchEvent(new Event("change"));
equal(form.refs.endDate.refs.dateInput.value, 'Dec 30, 2015 at 12pm');
equal(form.refs.closeDate.refs.dateInput.value, 'Jan 7 at 12pm');
});
test("preserves 'closeDate' when already set and 'endDate' changes to match, then changes again", function() {
let form = this.renderComponent();
let endDateInput = ReactDOM.findDOMNode(form.refs.endDate.refs.dateInput);
endDateInput.value = 'Jan 7 at 12pm';
endDateInput.dispatchEvent(new Event("change"));
endDateInput.value = 'Dec 30, 2015 at 12pm';
endDateInput.dispatchEvent(new Event("change"));
equal(form.refs.endDate.refs.dateInput.value, 'Dec 30, 2015 at 12pm');
equal(form.refs.closeDate.refs.dateInput.value, 'Jan 7 at 12pm');
});
test("auto-updates 'closeDate' when cleared and 'endDate' changes", function() {
let form = this.renderComponent();
let closeDateInput = ReactDOM.findDOMNode(form.refs.closeDate.refs.dateInput);
closeDateInput.value = '';
closeDateInput.dispatchEvent(new Event("change"));
let endDateInput = ReactDOM.findDOMNode(form.refs.endDate.refs.dateInput);
endDateInput.value = 'Jan 7 at 12pm';
endDateInput.dispatchEvent(new Event("change"));
equal(form.refs.endDate.refs.dateInput.value, 'Jan 7 at 12pm');
equal(form.refs.closeDate.refs.dateInput.value, 'Jan 7 at 12pm');
});
test("calls the 'onSave' callback when the save button is clicked", function() {
let spy = sinon.spy();
let form = this.renderComponent({onSave: spy});
let saveButton = React.findDOMNode(form.refs.saveButton);
let saveButton = ReactDOM.findDOMNode(form.refs.saveButton);
Simulate.click(saveButton);
ok(spy.calledOnce);
});
test("sends form values in 'onSave'", function() {
let spy = sinon.spy();
let form = this.renderComponent({onSave: spy});
let saveButton = ReactDOM.findDOMNode(form.refs.saveButton);
Simulate.click(saveButton);
deepEqual(spy.args[0][0], {
id: '1',
title: 'Q1',
startDate: new Date("2015-11-01T12:00:00Z"),
endDate: new Date("2015-12-31T12:00:00Z"),
closeDate: new Date("2016-01-07T12:00:00Z")
});
});
test("calls the 'onCancel' callback when the cancel button is clicked", function() {
let spy = sinon.spy();
let form = this.renderComponent({onCancel: spy});
let cancelButton = React.findDOMNode(form.refs.cancelButton);
let cancelButton = ReactDOM.findDOMNode(form.refs.cancelButton);
Simulate.click(cancelButton);
ok(spy.calledOnce);
});

View File

@ -41,19 +41,20 @@ define([
id: "1",
title: "We did it! We did it! We did it! #dora #boots",
startDate: new Date("2015-01-01T20:11:00+00:00"),
endDate: new Date("2015-03-01T00:00:00+00:00")
},
{
endDate: new Date("2015-03-01T00:00:00+00:00"),
closeDate: new Date("2015-03-01T00:00:00+00:00")
},{
id: "3",
title: "Como estas?",
startDate: new Date("2014-11-01T20:11:00+00:00"),
endDate: new Date("2014-11-11T00:00:00+00:00")
},
{
endDate: new Date("2014-11-11T00:00:00+00:00"),
closeDate: new Date("2014-11-11T00:00:00+00:00")
},{
id: "2",
title: "Swiper no swiping!",
startDate: new Date("2015-04-01T20:11:00+00:00"),
endDate: new Date("2015-05-01T00:00:00+00:00")
endDate: new Date("2015-05-01T00:00:00+00:00"),
closeDate: new Date("2015-05-01T00:00:00+00:00")
}
];
@ -61,7 +62,8 @@ define([
id: "4",
title: "Example Period",
startDate: new Date("2015-03-02T20:11:00+00:00"),
endDate: new Date("2015-03-03T00:00:00+00:00")
endDate: new Date("2015-03-03T00:00:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
const props = {
@ -383,7 +385,8 @@ define([
id: "1",
title: "",
startDate: new Date("2015-03-02T20:11:00+00:00"),
endDate: new Date("2015-03-03T00:00:00+00:00")
endDate: new Date("2015-03-03T00:00:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -397,7 +400,8 @@ define([
id: "1",
title: " ",
startDate: new Date("2015-03-02T20:11:00+00:00"),
endDate: new Date("2015-03-03T00:00:00+00:00")
endDate: new Date("2015-03-03T00:00:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -410,7 +414,8 @@ define([
let period = {
title: "Period without Start Date",
startDate: undefined,
endDate: new Date("2015-03-03T00:00:00+00:00")
endDate: new Date("2015-03-03T00:00:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -423,7 +428,22 @@ define([
let period = {
title: "Period without End Date",
startDate: new Date("2015-03-02T20:11:00+00:00"),
endDate: null
endDate: null,
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
this.callOnSave(set, period);
notOk(gradingPeriodsApi.batchUpdate.called, "does not call update");
ok(set.refs.editPeriodForm, "form is still visible");
});
test('does not save a grading period without a valid closeDate', function() {
let period = {
title: "Period without End Date",
startDate: new Date("2015-03-02T20:11:00+00:00"),
endDate: new Date("2015-03-03T00:00:00+00:00"),
closeDate: null
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -436,7 +456,8 @@ define([
let period = {
title: "Period with Overlapping Start Date",
startDate: new Date("2015-04-30T20:11:00+00:00"),
endDate: new Date("2015-05-30T00:00:00+00:00")
endDate: new Date("2015-05-30T00:00:00+00:00"),
closeDate: new Date("2015-05-30T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -449,7 +470,8 @@ define([
let period = {
title: "Period with Overlapping End Date",
startDate: new Date("2014-12-30T20:11:00+00:00"),
endDate: new Date("2015-01-30T00:00:00+00:00")
endDate: new Date("2015-01-30T00:00:00+00:00"),
closeDate: new Date("2015-01-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -462,7 +484,22 @@ define([
let period = {
title: "Overlapping Period",
startDate: new Date("2015-03-03T00:00:00+00:00"),
endDate: new Date("2015-03-02T20:11:00+00:00")
endDate: new Date("2015-03-02T20:11:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
this.callOnSave(set, period);
notOk(gradingPeriodsApi.batchUpdate.called, "does not call update");
ok(set.refs.editPeriodForm, "form is still visible");
});
test('does not save a grading period with closeDate before endDate', function() {
let period = {
title: "Overlapping Period",
startDate: new Date("2015-03-01T00:00:00+00:00"),
endDate: new Date("2015-03-02T20:11:00+00:00"),
closeDate: new Date("2015-03-02T20:10:59+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -677,7 +714,8 @@ define([
let period = {
title: "",
startDate: new Date("2015-03-02T20:11:00+00:00"),
endDate: new Date("2015-03-03T00:00:00+00:00")
endDate: new Date("2015-03-03T00:00:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -690,7 +728,8 @@ define([
let period = {
title: "Period without Start Date",
startDate: undefined,
endDate: new Date("2015-03-03T00:00:00+00:00")
endDate: new Date("2015-03-03T00:00:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -703,7 +742,8 @@ define([
let period = {
title: "Period without End Date",
startDate: new Date("2015-03-02T20:11:00+00:00"),
endDate: null
endDate: null,
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -716,7 +756,8 @@ define([
let period = {
title: "Period with Overlapping Start Date",
startDate: new Date("2015-04-30T20:11:00+00:00"),
endDate: new Date("2015-05-30T00:00:00+00:00")
endDate: new Date("2015-05-30T00:00:00+00:00"),
closeDate: new Date("2015-05-30T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -729,7 +770,8 @@ define([
let period = {
title: "Period with Overlapping End Date",
startDate: new Date("2014-12-30T20:11:00+00:00"),
endDate: new Date("2015-01-30T00:00:00+00:00")
endDate: new Date("2015-01-30T00:00:00+00:00"),
closeDate: new Date("2015-01-30T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();
@ -742,7 +784,8 @@ define([
let period = {
title: "Overlapping Period",
startDate: new Date("2015-03-03T00:00:00+00:00"),
endDate: new Date("2015-03-02T20:11:00+00:00")
endDate: new Date("2015-03-02T20:11:00+00:00"),
closeDate: new Date("2015-03-03T00:00:00+00:00")
};
let update = this.stubUpdate();
let set = this.renderComponent();

View File

@ -0,0 +1,100 @@
require(["jsx/speed_grader/mgp"], ({
gradingPeriodForDate,
dueDateForStudent,
assignmentClosedForStudent,
}) => {
module("Speedgrader MGP Spec");
const MS_IN_DAY = 24 * 60 * 60 * 1000;
const NOW = new Date().getTime();
const closedGradingPeriod = {
start_date: new Date(1999, 0, 1).toISOString(),
end_date: new Date(1999, 5, 1).toISOString(),
close_date: new Date(1999, 6, 1).toISOString(),
};
// this one is over but not closed
const pastGradingPeriod = {
start_date: new Date(NOW - MS_IN_DAY * 8).toISOString(),
end_date: new Date(NOW - MS_IN_DAY * 2).toISOString(),
close_date: new Date(NOW + MS_IN_DAY * 1).toISOString(),
};
const currentGradingPeriod = {
start_date: new Date(NOW - MS_IN_DAY * 1).toISOString(),
end_date: new Date(NOW + MS_IN_DAY * 7).toISOString(),
close_date: new Date(NOW + MS_IN_DAY * 14).toISOString(),
};
const gradingPeriods = [
closedGradingPeriod,
pastGradingPeriod,
currentGradingPeriod
];
test("gradingPeriodForDate", () => {
const pastGPDate = new Date(NOW - MS_IN_DAY * 3).toISOString();
ok(gradingPeriodForDate(pastGPDate, gradingPeriods) === pastGradingPeriod);
const currentGPDate = new Date(NOW + MS_IN_DAY * 5).toISOString();
ok(gradingPeriodForDate(currentGPDate, gradingPeriods) ===
currentGradingPeriod);
const invalidDate = new Date(NOW + MS_IN_DAY * 10).toISOString();
ok(gradingPeriodForDate(invalidDate, gradingPeriods) ===
undefined);
ok(gradingPeriodForDate(null, gradingPeriods) === currentGradingPeriod);
});
const student = {id: 1};
const studentOverride = {student_ids: [1], due_at: currentGradingPeriod.start_date};
const sectionOverride = {course_section_id: 2, due_at: currentGradingPeriod.end_date};
const overrides = [studentOverride,sectionOverride];
test("dueDateForStudent", () => {
ok(dueDateForStudent(student, [{default: true, due_at: NOW}]) === NOW,
"no assignment overrides returns default due date");
ok(dueDateForStudent(student, [
{default: true, due_at: currentGradingPeriod.start_date},
...overrides
]) == studentOverride.due_at,
"returns overrides for matching student_ids");
ok(dueDateForStudent({...student, section_ids: [2]}, [
{default: true, due_at: closedGradingPeriod.end_date},
...overrides
]) == sectionOverride.due_at,
"returns section due dates");
ok(dueDateForStudent(student,
[{default: true, due_at: null}, studentOverride]
) == null,
"returns most lenient date");
});
test("assignmentClosedForStudent", () => {
const dueAt = closedGradingPeriod.start_date
ok(!assignmentClosedForStudent(student, {
due_at: dueAt,
gradingPeriods: gradingPeriods,
assignmentOverrides: overrides
}));
ok(assignmentClosedForStudent(student, {
due_at: null,
only_visible_to_overrides: true,
gradingPeriods: gradingPeriods,
assignmentOverrides: [{
student_ids: [student.id],
due_at: dueAt
}],
}), "respects only_visible_to_overrides");
const closedStudent = {id: 99};
ok(assignmentClosedForStudent(closedStudent, {
due_at: dueAt,
gradingPeriods: gradingPeriods,
assignmentOverrides: overrides
}));
});
});

View File

@ -0,0 +1,29 @@
require 'spec_helper'
describe DataFixup::PopulateGradingPeriodCloseDates do
before(:each) do
root_account = Account.create(name: 'new account')
group = Factories::GradingPeriodGroupHelper.new.create_for_account(root_account)
period_helper = Factories::GradingPeriodHelper.new
@first_period = period_helper.create_presets_for_group(group, :past).first
@first_period.close_date = nil
@first_period.save!
@second_period = period_helper.create_presets_for_group(group, :current).first
@second_period.close_date = 3.days.from_now(@second_period.end_date)
@second_period.save!
end
before(:each) do
DataFixup::PopulateGradingPeriodCloseDates.run
end
it "does not alter already-set close dates" do
@second_period.reload
expect(@second_period.close_date).to eq 3.days.from_now(@second_period.end_date)
end
it "sets the close date to the end date for periods with nil close dates" do
@first_period.reload
expect(@first_period.close_date).to eq @first_period.end_date
end
end

View File

@ -21,7 +21,11 @@ require_relative '../spec_helper'
require 'csv'
describe GradebookImporter do
let(:gradebook_user){ user_model }
let(:gradebook_user) do
teacher = User.create!
course_with_teacher(user: teacher, course: @course)
teacher
end
context "construction" do
let!(:gradebook_course){ course_model }
@ -386,9 +390,10 @@ describe GradebookImporter do
describe "simplified json output" do
it "has only the specified keys" do
keys = [:assignments,:assignments_outside_current_periods,
:missing_objects, :original_submissions, :students,
:unchanged_assignments]
keys = [:assignments, :missing_objects,
:original_submissions, :students,
:unchanged_assignments,
:warning_messages]
expect(hash.keys.sort).to eql(keys)
end
@ -398,12 +403,12 @@ describe GradebookImporter do
end
it "a submission only has specified keys" do
keys = ["assignment_id", "grade", "original_grade"]
keys = ["assignment_id", "grade", "gradeable", "original_grade"]
expect(submission.keys.sort).to eql(keys)
end
it "an assignment only has specified keys" do
keys = [:due_at, :grading_type, :id, :points_possible, :previous_id,
keys = [:grading_type, :id, :points_possible, :previous_id,
:title]
expect(assignment.keys.sort).to eql(keys)
end
@ -453,120 +458,235 @@ describe GradebookImporter do
expect(@gi.assignments.last.title).to eq 'a3'
expect(@gi.assignments.last).to be_new_record
expect(@gi.assignments.last.id).to be < 0
json = @gi.as_json
expect(json[:students][0][:submissions].first["grade"]).to eq "1"
expect(json[:students][0][:submissions].last["grade"]).to eq "3"
submissions = @gi.as_json[:students][0][:submissions]
expect(submissions.length).to eq(2)
expect(submissions.first["grade"]).to eq "1"
expect(submissions.last["grade"]).to eq "3"
end
end
context "multiple grading periods" do
let(:group) { Factories::GradingPeriodGroupHelper.new.legacy_create_for_course(course) }
let!(:old_period) do
old_period_params = { title: "Course Period 2: old period",
start_date: 2.months.ago,
end_date: 1.month.ago }
group.grading_periods.create old_period_params
before(:once) do
account = Account.default
@course = account.courses.create!
@teacher = User.create!
course_with_teacher(course: @course, user: @teacher, active_enrollment: true)
account.enable_feature!(:multiple_grading_periods)
group = account.grading_period_groups.create!
group.enrollment_terms << @course.enrollment_term
@now = Time.zone.now
group.grading_periods.create!(
title: "Closed Period",
start_date: 3.months.ago(@now),
end_date: 1.month.ago(@now),
close_date: 1.month.ago(@now)
)
@active_period = group.grading_periods.create!(
title: "Active Period",
start_date: 1.month.ago(@now),
end_date: 2.months.from_now(@now),
close_date: 2.months.from_now(@now)
)
@closed_assignment = @course.assignments.create!(
name: "Assignment in closed period",
points_possible: 10,
due_at: date_in_closed_period
)
@open_assignment = @course.assignments.create!(
name: "Assignment in open period",
points_possible: 10,
due_at: date_in_open_period
)
end
let!(:current_period) do
current_period_params = { title: "Course Period 1: current period",
start_date: 1.month.ago,
end_date: 1.month.from_now }
group.grading_periods.create current_period_params
end
let(:assignments) { @gi.as_json[:assignments] }
let(:date_in_open_period) { 1.month.from_now(@now) }
let(:date_in_closed_period) { 2.months.ago(@now) }
let(:student_submissions) { @gi.as_json[:students][0][:submissions] }
let(:future_period) do
future_period_params = { title: "Course Period 3: future period",
start_date: 1.month.from_now,
end_date: 2.months.from_now }
group.grading_periods.create future_period_params
end
let(:account) { Account.default }
let(:course) { Course.create account: account }
let(:student) { User.create }
let(:progress) { Progress.create tag: "test", context: student }
let(:importer_json) do
lambda do |hashes|
hashes.each { |hash| course.assignments.create hash }
contents = <<CSV
Student,ID,Section,#{course.assignments.map(&:name).join(',')}
,#{student.id},#{',9' * course.assignments.length}
CSV
upload = GradebookUpload.create!(course: course, user: student, progress: progress)
attachment = attachment_with_rows(contents)
importer = GradebookImporter.new(upload, attachment, student, progress)
importer.parse!
importer.as_json
end
end
describe "assignments_outside_current_periods" do
describe "when multiple grading periods is on" do
before do
course.root_account.enable_feature! :multiple_grading_periods
context "uploading submissions for existing assignments" do
context "assignments without overrides" do
before(:once) do
@student = User.create!
course_with_student(course: @course, user: @student, active_enrollment: true)
end
describe "empty assignments_outside_current_periods" do
it "when assignments are in a current grading period" do
assignment_hashes = [ { name: 'Assignment 1',
points_possible: 10,
due_at: Time.zone.now } ]
json = importer_json.call(assignment_hashes)
expect(json[:assignments_outside_current_periods]).to be_empty
it "excludes entire assignments if no submissions for the assignment are being uploaded" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5",
)
assignment_ids = assignments.map { |a| a[:id] }
expect(assignment_ids).to_not include @closed_assignment.id
end
it "includes assignments if there is at least one submission in the assignment being uploaded" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5",
)
assignment_ids = assignments.map { |a| a[:id] }
expect(assignment_ids).to include @open_assignment.id
end
context "submissions already exist" do
before(:once) do
@closed_assignment.grade_student(@student, grade: 8)
@open_assignment.grade_student(@student, grade: 8)
end
it "when all assignments have no due_ats" do
assignment_hashes = [ { points_possible: 10,
name: 'Assignment 2' } ]
json = importer_json.call(assignment_hashes)
expect(json[:assignments_outside_current_periods]).to be_empty
it "does not include submissions that fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5",
)
assignment_ids = student_submissions.map { |s| s['assignment_id'] }
expect(assignment_ids).to_not include @closed_assignment.id
end
it "when assignment due_ats are nil and there is a future period" do
future_period
assignment_hashes = [ { points_possible: 10,
name: 'Assignment 2.five' } ]
json = importer_json.call(assignment_hashes)
expect(json[:assignments_outside_current_periods]).to be_empty
it "includes submissions that do not fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5",
)
assignment_ids = student_submissions.map { |s| s['assignment_id'] }
expect(assignment_ids).to include @open_assignment.id
end
end
describe "when all assignments are in past grading periods" do
it "indicates assignments not in a current grading period" do
assignment_hashes = [ { points_possible: 10,
name: 'Assignment 3',
due_at: 6.weeks.ago } ]
json = importer_json.call(assignment_hashes)
past_assignment = json[:assignments_outside_current_periods].first
expect(past_assignment[:title]).to eq 'Assignment 3'
end
end
describe "when some assignments are in past grading periods" do
it "indicates assignments not in a current grading period" do
assignment_hashes = [ { points_possible: 10,
name: 'Assignment 4',
due_at: 6.weeks.ago},
{ points_possible: 10,
name: 'Assignment 5',
due_at: 1.day.from_now } ]
json = importer_json.call(assignment_hashes)
past_assignment = json[:assignments_outside_current_periods].first
expect(past_assignment[:title]).to eq 'Assignment 4'
context "submissions do not already exist" do
it "does not include submissions that will fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5",
)
expect(student_submissions.map {|s| s['assignment_id']}).to_not include @closed_assignment.id
end
it "includes submissions that will not fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5",
)
expect(student_submissions.map {|s| s['assignment_id']}).to include @open_assignment.id
end
end
end
it "should be empty when multiple grading periods is off" do
assignment_hashes = [ { points_possible: 10,
name: 'Assignment 6',
due_at: 6.weeks.ago } ]
json = importer_json.call(assignment_hashes)
course.root_account.disable_feature! :multiple_grading_periods
expect(json[:assignments_outside_current_periods]).to be_empty
context "assignments with overrides" do
before(:once) do
section_one = @course.course_sections.create!(name: 'Section One')
@student = student_in_section(section_one)
# set up overrides such that the student has a due date in an open grading period
# for @closed_assignment and a due date in a closed grading period for @open_assignment
@override_in_open_grading_period = @closed_assignment.assignment_overrides.create! do |override|
override.set = section_one
override.due_at_overridden = true
override.due_at = date_in_open_period
end
@open_assignment.assignment_overrides.create! do |override|
override.set = section_one
override.due_at_overridden = true
override.due_at = date_in_closed_period
end
end
it "excludes entire assignments if there are no submissions in the assignment" \
"being uploaded that are gradeable" do
@override_in_open_grading_period.update_attribute(:due_at, date_in_closed_period)
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5"
)
assignment_ids = assignments.map { |a| a[:id] }
expect(assignment_ids).not_to include @closed_assignment.id
end
it "includes assignments if there is at least one submission in the assignment" \
"being uploaded that is gradeable (it does not fall in a closed grading period)" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5"
)
assignment_ids = assignments.map { |a| a[:id] }
expect(assignment_ids).to include @closed_assignment.id
end
context "submissions already exist" do
before(:once) do
@closed_assignment.grade_student(@student, grade: 8)
@open_assignment.grade_student(@student, grade: 8)
end
it "does not include submissions that fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5"
)
assignment_ids = student_submissions.map { |s| s['assignment_id'] }
expect(assignment_ids).not_to include @open_assignment.id
end
it "includes submissions that do not fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5"
)
assignment_ids = student_submissions.map { |s| s['assignment_id'] }
expect(assignment_ids).to include @closed_assignment.id
end
end
context "submissions do not already exist" do
it "does not include submissions that will fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5"
)
assignment_ids = student_submissions.map {|s| s['assignment_id']}
expect(assignment_ids).to_not include @open_assignment.id
end
it "includes submissions that will not fall in closed grading periods" do
importer_with_rows(
"Student,ID,Section,Assignment in closed period,Assignment in open period",
",#{@student.id},,5,5"
)
assignment_ids = student_submissions.map {|s| s['assignment_id']}
expect(assignment_ids).to include @closed_assignment.id
end
end
end
end
context "uploading submissions for new assignments" do
before(:once) do
@student = User.create!
course_with_student(course: @course, user: @student, active_enrollment: true)
end
it "does not create a new assignment if the last grading period is closed" do
@active_period.destroy!
importer_with_rows(
"Student,ID,Section,Some new assignment",
",#{@student.id},,5",
)
expect(assignments.count).to eq(0)
end
it "creates a new assignment if the last grading period is not closed" do
importer_with_rows(
"Student,ID,Section,Some new assignment",
",#{@student.id},,5",
)
expect(assignments.count).to eq(1)
end
end
end

View File

@ -1,4 +1,3 @@
# coding: utf-8
#
# Copyright (C) 2011 Instructure, Inc.
#
@ -26,7 +25,8 @@ describe Assignment do
end
it "should create a new instance given valid attributes" do
@course.assignments.create!(assignment_valid_attributes)
course = @course.assignments.create!(assignment_valid_attributes)
expect(course).to be_valid
end
it "should have a useful state machine" do

View File

@ -59,29 +59,92 @@ describe GradingPeriod do
expect(grading_period).to_not be_valid
end
describe "#as_json_with_user_permissions" do
it "includes the close_date in the returned object" do
json = grading_period.as_json_with_user_permissions(User.new)
expect(json).to have_key("close_date")
end
end
describe "close_date" do
it "sets the close_date to the end_date if no close_date is provided" do
grading_period = grading_period_group.grading_periods.create!(params.except(:close_date))
expect(grading_period.close_date).to eq(grading_period.end_date)
context "grading period group belonging to an account" do
it "allows setting a close_date that is different from the end_date" do
grading_period = grading_period_group.grading_periods.create!(params)
expect(grading_period.close_date).not_to eq(grading_period.end_date)
end
it "sets the close_date to the end_date if no close_date is provided" do
grading_period = grading_period_group.grading_periods.create!(params.except(:close_date))
expect(grading_period.close_date).to eq(grading_period.end_date)
end
it "considers the grading period invalid if the close date is before the end date" do
period_params = params.merge(close_date: 1.day.ago(params[:end_date]))
grading_period = grading_period_group.grading_periods.build(period_params)
expect(grading_period).to be_invalid
end
it "considers the grading period valid if the close date is equal to the end date" do
period_params = params.merge(close_date: params[:end_date])
grading_period = grading_period_group.grading_periods.build(period_params)
expect(grading_period).to be_valid
end
end
it "allows setting a close_date that is different from the end_date" do
skip
grading_period = grading_period_group.grading_periods.create!(params)
expect(grading_period.close_date).not_to eq(grading_period.end_date)
context "grading period group belonging to a course" do
let(:course_grading_period_group) { group_helper.legacy_create_for_course(course) }
it "does not allow setting a close_date that is different from the end_date" do
grading_period = course_grading_period_group.grading_periods.create!(params)
expect(grading_period.close_date).to eq(params[:end_date])
end
it "sets the close_date to the end_date if no close_date is provided" do
grading_period = course_grading_period_group.grading_periods.create!(params.except(:close_date))
expect(grading_period.close_date).to eq(grading_period.end_date)
end
it "sets the close_date to the end_date when the grading period is updated" do
grading_period = course_grading_period_group.grading_periods.create!(params.except(:close_date))
new_end_date = 5.weeks.from_now(now)
grading_period.end_date = new_end_date
grading_period.save!
expect(grading_period.close_date).to eq(new_end_date)
end
end
end
describe "#closed?" do
around { |example| Timecop.freeze(now, &example) }
it "returns true if the current date is past the close date" do
period = grading_period_group.grading_periods.build(
title: "Closed Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 3.days.ago(now)
)
expect(period).to be_closed
end
it "considers the grading period invalid if the close date is before the end date" do
skip
period_params = params.merge(close_date: 1.day.ago(params[:end_date]))
grading_period = grading_period_group.grading_periods.build(period_params)
expect(grading_period).to be_invalid
it "returns false if the current date is before the close date" do
period = grading_period_group.grading_periods.build(
title: "Open Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 2.days.from_now(now)
)
expect(period).not_to be_closed
end
it "considers the grading period valid if the close date is equal to the end date" do
period_params = params.merge(close_date: params[:end_date])
grading_period = grading_period_group.grading_periods.build(period_params)
expect(grading_period).to be_valid
it "returns false if the current date matches the close date" do
period = grading_period_group.grading_periods.build(
title: "Open Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: now
)
expect(period).not_to be_closed
end
end
@ -178,7 +241,7 @@ describe GradingPeriod do
it "does not include grading periods from the course enrollment term group if inherit is false" do
group = group_helper.create_for_account(@root_account)
term.update_attribute(:grading_period_group_id, group)
period_1 = period_helper.create_with_weeks_for_group(group, 5, 3)
period_helper.create_with_weeks_for_group(group, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group, 3, 1)
period_2.workflow_state = :deleted
period_2.save
@ -212,8 +275,8 @@ describe GradingPeriod do
it "does not return grading periods on the course directly" do
group = group_helper.legacy_create_for_course(@course)
period_1 = period_helper.create_with_weeks_for_group(group, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group, 3, 1)
period_helper.create_with_weeks_for_group(group, 5, 3)
period_helper.create_with_weeks_for_group(group, 3, 1)
expect(GradingPeriod.for(@root_account)).to match_array([])
end

View File

@ -35,6 +35,133 @@ describe Submission do
}
end
describe "Multiple Grading Periods" do
let(:in_closed_grading_period) { 9.days.ago }
let(:in_open_grading_period) { 1.day.from_now }
let(:outside_of_any_grading_period) { 10.days.from_now }
before(:once) do
@root_account = @context.root_account
@root_account.enable_feature!(:multiple_grading_periods)
group = @root_account.grading_period_groups.create!
@closed_period = group.grading_periods.create!(
title: "Closed!",
start_date: 2.weeks.ago,
end_date: 1.week.ago,
close_date: 3.days.ago
)
@open_period = group.grading_periods.create!(
title: "Open!",
start_date: 3.days.ago,
end_date: 3.days.from_now,
close_date: 5.days.from_now
)
group.enrollment_terms << @context.enrollment_term
end
describe "#in_closed_grading_period?" do
it "returns true if the submission is due in a closed grading period" do
@assignment.due_at = in_closed_grading_period
@assignment.save!
submission = Submission.create!(@valid_attributes)
expect(submission).to be_in_closed_grading_period
end
it "returns false if the submission is due in an open grading period" do
@assignment.due_at = in_open_grading_period
@assignment.save!
submission = Submission.create!(@valid_attributes)
expect(submission).not_to be_in_closed_grading_period
end
it "returns false if the submission is due outside of any grading period" do
@assignment.due_at = outside_of_any_grading_period
@assignment.save!
submission = Submission.create!(@valid_attributes)
expect(submission).not_to be_in_closed_grading_period
end
end
describe "permissions" do
before(:once) do
@admin = user(active_all: true)
@root_account.account_users.create!(user: @admin)
@teacher = user(active_all: true)
@context.enroll_teacher(@teacher)
end
describe "grade" do
context "the submission is due in a closed grading period" do
before(:once) do
@assignment.due_at = in_closed_grading_period
@assignment.save!
@submission = Submission.create!(@valid_attributes)
end
it "has grade permissions if the user is a root account admin" do
expect(@submission.grants_right?(@admin, :grade)).to eq(true)
end
it "does not have grade permissions if the user is not a root account admin" do
expect(@submission.grants_right?(@teacher, :grade)).to eq(false)
end
end
context "the submission is due in an open grading period" do
before(:once) do
@assignment.due_at = in_open_grading_period
@assignment.save!
@submission = Submission.create!(@valid_attributes)
end
it "has grade permissions if the user is a root account admin" do
expect(@submission.grants_right?(@admin, :grade)).to eq(true)
end
it "has grade permissions if the user is non-root account admin with manage_grades permissions" do
expect(@submission.grants_right?(@teacher, :grade)).to eq(true)
end
it "has not have grade permissions if the user is non-root account admin without manage_grades permissions" do
@student = user(active_all: true)
@context.enroll_student(@student)
expect(@submission.grants_right?(@student, :grade)).to eq(false)
end
end
context "the submission is due outside of any grading period" do
before(:once) do
@assignment.due_at = outside_of_any_grading_period
@assignment.save!
@submission = Submission.create!(@valid_attributes)
end
it "has grade permissions if the user is a root account admin" do
expect(@submission.grants_right?(@admin, :grade)).to eq(true)
end
it "has grade permissions if the user is non-root account admin with manage_grades permissions" do
expect(@submission.grants_right?(@teacher, :grade)).to eq(true)
end
it "has not have grade permissions if the user is non-root account admin without manage_grades permissions" do
@student = user(active_all: true)
@context.enroll_student(@student)
expect(@submission.grants_right?(@student, :grade)).to eq(false)
end
end
end
end
end
it "#in_closed_grading_period? returns false if the course does not have Multiple Grading Periods enabled" do
@context.root_account.disable_feature!(:multiple_grading_periods)
@assignment.due_at = 9.days.ago
@assignment.save!
submission = Submission.create!(@valid_attributes)
expect(submission).not_to be_in_closed_grading_period
end
it "should create a new instance given valid attributes" do
Submission.create!(@valid_attributes)
end

View File

@ -3018,6 +3018,27 @@ describe User do
end
end
describe "#admin_of_root_account?" do
before(:once) do
@user = user(active_all: true)
@root_account = Account.default
end
it "returns false if the user is not an admin of the root account" do
expect(@user).not_to be_admin_of_root_account(@root_account)
end
it "returns true if the user is an admin of the root account" do
@root_account.account_users.create!(user: @user)
expect(@user).to be_admin_of_root_account(@root_account)
end
it "raises an error if the given account is not a root account" do
sub_account = @root_account.sub_accounts.create!
expect { @user.admin_of_root_account?(sub_account) }.to raise_error("must be a root account")
end
end
it "should not grant user_notes rights to restricted users" do
course_with_ta(:active_all => true)
student_in_course(:course => @course, :active_all => true)

View File

@ -47,13 +47,6 @@ describe "gradebook2" do
context "return focus to settings menu when it closes" do
let!(:open_gradebook_settings_menu) { f('#gradebook_settings').click }
it "after set group weights closes", priority: "2", test_id: 720464 do
f("[aria-controls='assignment_group_weights_dialog']").click
f('.ui-dialog-titlebar-close').click
expect(active_element).to have_attribute('id', 'gradebook_settings')
end
it "after hide/show student names is clicked", priority: "2", test_id: 720461 do
f(".student_names_toggle").click

View File

@ -48,10 +48,6 @@ describe "group weights" do
end
it 'should show total column as points' do
points_array = ["25"]
unweighted_array = ["41.67%"]
weighted_array = ["45%"]
@assignment1.grade_student @student, :grade => 20
@assignment2.grade_student @student, :grade => 5
@ -60,37 +56,19 @@ describe "group weights" do
# Displays total column as points
get "/courses/#{@course.id}/gradebook2"
expect(student_totals).to eq(points_array)
wait_for_ajax_requests
# Display weighted totals
toggle_group_weight
expect(student_totals).to eq(weighted_array)
# Display unweighted totals again
toggle_group_weight
expect(student_totals).to eq(unweighted_array)
expect(student_totals).to eq(["25"])
end
it "should validate setting group weights", priority: "1", test_id: 164007 do
weight_numbers = [26.1, 73.5]
it 'should show total column as percent' do
@assignment1.grade_student @student, :grade => 20
@assignment2.grade_student @student, :grade => 5
@course.show_total_grade_as_points = false
@course.update_attributes(:group_weighting_scheme => 'percent')
# Displays total column as points
get "/courses/#{@course.id}/gradebook2"
wait_for_ajaximations
group_1 = AssignmentGroup.where(name: @group1.name).first
group_2 = AssignmentGroup.where(name: @group2.name).first
#set and check the group weight of the first assignment group
set_group_weight(group_1, weight_numbers[0])
#set and check the group weight of the second assignment group
set_group_weight(group_2, weight_numbers[1])
validate_group_weight(group_2, weight_numbers[1])
# check display of group weights in column heading
# TODO: make the header cell in the UI update to reflect new value
# validate_group_weight_text(AssignmentGroup.all, weight_numbers)
expect(student_totals).to eq(["45%"])
end
context "warning message" do
@ -124,13 +102,10 @@ describe "group weights" do
expect(ff('.icon-warning').count).to eq(2)
end
it 'should remove triangle warnings if group weights are turned off in gradebook', priority: "1", test_id: 305579 do
it 'should not display triangle warnings if group weights are turned off in gradebook', priority: "1", test_id: 305579 do
@course.apply_assignment_group_weights = false
@course.save!
get "/courses/#{@course.id}/gradebook"
f('#gradebook_settings').click
f("[aria-controls='assignment_group_weights_dialog']").click
f('#group_weighting_scheme').click
submit_dialog('.ui-dialog-buttonset', '.ui-button')
refresh_page
expect(f("body")).not_to contain_css('.icon-warning')
end

View File

@ -280,32 +280,6 @@ describe "Screenreader Gradebook" do
expect(ff('#assignment_information table td').map(&:text)).to eq ['20', '10', '15', '5']
end
context 'Group Weights' do
before(:each) do
enroll_teacher_and_students
assignment_1
assignment_5
user_session(teacher)
get "/courses/#{test_course.id}/gradebook/change_gradebook_version?version=srgb"
end
it 'should display the group weighting dialog with group weights disabled', priority: '1', test_id: 163995 do
group_weights_button.click
expect(f("#assignment_group_weights_dialog table[style='opacity: 0.5;']")).to be_truthy
end
it 'should correctly sync group weight settings between srgb and gb2', priority: '1', test_id: 588913 do
turn_on_group_weights
# go back to gb2 to verify settings stuck
get "/courses/#{test_course.id}/gradebook/change_gradebook_version?version=2"
gradebook_settings_cog.click
group_weights_menu.click
expect(f("#assignment_group_weights_dialog table[style='opacity: 1;']")).to be_truthy
end
end
context "as a teacher" do
before(:each) do
gradebook_data_setup

View File

@ -21,11 +21,11 @@ describe 'Screenreader Gradebook Student Information' do
before(:each) do
course_setup
login_to_srgb
end
context 'in Student Information section' do
it 'allows comments in Notes field', priority: "2", test_id: 615709 do
login_to_srgb
skip_if_chrome('fails in chrome - due to replace content')
select_student(student)
show_notes_option.click
@ -37,7 +37,9 @@ describe 'Screenreader Gradebook Student Information' do
context 'displays no points possible warning' do
before(:each) do
turn_on_group_weights
@course.apply_assignment_group_weights = true
@course.save!
login_to_srgb
end
it "with only a student selected", priority: "2", test_id: 615711 do
@ -55,4 +57,4 @@ describe 'Screenreader Gradebook Student Information' do
end
end
end
end
end

View File

@ -39,16 +39,22 @@ describe "gradebook2 - total points toggle" do
submit_dialog(dialog, '.ui-button')
end
it "setting group weights should switch to percentage", test_id: 164231, priority: "2" do
it "shows points when group weights are not set" do
@course.show_total_grade_as_points = true
@course.save!
get "/courses/#{@course.id}/gradebook2"
toggle_grade_display
should_show_points
end
it "shows percentages when group weights are set", test_id: 164231, priority: "2" do
@course.show_total_grade_as_points = false
@course.save!
group = AssignmentGroup.where(name: @group.name).first
set_group_weight(group, 50, enable_scheme: true)
group.group_weight = 50
group.save!
disable_group_weight
get "/courses/#{@course.id}/gradebook2"
should_show_percentages
end

View File

@ -50,11 +50,6 @@ describe 'Course Grading Periods' do
expect(ff(grading_period_selector).length).to be 1
end
it 'does not allow adding grading periods', priority: "1", test_id: 239999 do
get "/courses/#{@course.id}/grading_standards"
expect(f('#grading_periods')).to_not contain_css(('#add-period-button'))
end
it 'allows updating grading periods', priority: "1", test_id: 202317 do
period_helper.create_with_group_for_course(@course)
get "/courses/#{@course.id}/grading_standards"

View File

@ -318,55 +318,4 @@ module Gradebook2Common
def get_group_points
ff('div.assignment-points-possible')
end
def check_group_points(expected_weight_text)
2..3.each do |i|
expect(get_group_points[i].text).to eq expected_weight_text[i-2] + ' of grade'
end
end
def set_group_weight(assignment_group, weight_number, enable_scheme: false)
f('#gradebook_settings').click
f('[aria-controls="assignment_group_weights_dialog"]').click
dialog = f('#assignment_group_weights_dialog')
expect(dialog).to be_displayed
if enable_scheme
group_check = dialog.find_element(:id, 'group_weighting_scheme')
group_check.click
end
expect(is_checked('#group_weighting_scheme')).to be_truthy
group_weight_input = f("#assignment_group_#{assignment_group.id}_weight")
set_value(group_weight_input, "")
set_value(group_weight_input, weight_number)
fj('.ui-button:contains("Save")').click
wait_for_ajaximations
expect(@course.reload.group_weighting_scheme).to eq 'percent'
end
def disable_group_weight
f('#gradebook_settings').click
f('[aria-controls="assignment_group_weights_dialog"]').click
dialog = f('#assignment_group_weights_dialog')
expect(dialog).to be_displayed
group_check = dialog.find_element(:id, 'group_weighting_scheme')
group_check.click
expect(is_checked('#group_weighting_scheme')).to be_falsey
fj('.ui-button:contains("Save")').click
refresh_page
end
def validate_group_weight_text(assignment_groups, weight_numbers)
assignment_groups.each_with_index do |ag, i|
heading = fj(".slick-column-name:contains('#{ag.name}') .assignment-points-possible")
expect(heading).to include_text("#{weight_numbers[i]}% of grade")
end
end
def validate_group_weight(assignment_group, weight_number)
expect(assignment_group.reload.group_weight).to eq weight_number
end
end

View File

@ -6,7 +6,6 @@ module Gradebook2SRGBCommon
let(:grade_for_label) { f("label[for='student_and_assignment_grade']") }
let(:next_assignment_button) { fj("button:contains('Next Assignment')") }
let(:submission_details_button) { f('#submission_details') }
let(:group_weights_button) { f('#ag_weights') }
let(:notes_field) { f('#student_information textarea') }
let(:final_grade) { f('#student_information .total-grade') }
let(:secondary_id_label) { f('#student_information .secondary_id') }
@ -51,12 +50,6 @@ module Gradebook2SRGBCommon
replace_content(input, grade)
end
def turn_on_group_weights
f('#ag_weights').click
f('#group_weighting_scheme').click
f('button .ui-button-text').click
end
def tab_out_of_input(input_selector)
# This is a hack for a timing issue with SRGB
2.times { input_selector.send_keys(:tab) }

View File

@ -131,40 +131,6 @@ describe "interaction with multiple grading periods" do
end
end
context 'sub-accounts' do
# top-level account & grading periods setup
let(:parent_account) { Account.default }
let!(:enable_mgp_flag) { parent_account.enable_feature!(:multiple_grading_periods) }
# sub-account & grading periods setup
let(:sub_account) { Account.create(name: 'Sub Account', parent_account: parent_account) }
# sub-account course setup
let(:sub_account_course) do
sub_account.courses.create(
name: 'Sub-Account Course',
workflow_state: 'active'
)
end
let(:sub_account_teacher) { user(active_all: true) }
let(:enroll_teacher) do
sub_account_course.enroll_user(
sub_account_teacher,
'TeacherEnrollment',
enrollment_state: 'active'
)
end
let(:view_sub_course_grading_period) do
sub_account_course
enroll_teacher
user_session(sub_account_teacher)
get "/courses/#{sub_account_course.id}/grading_standards"
end
it 'does not allow creation of a GP in sub-account course', priority: "1", test_id: 587759 do
view_sub_course_grading_period
expect(f('#grading_periods')).not_to contain_css('#add-period-button')
end
end
context 'student view' do
let(:account) { Account.default }
let(:test_course) { account.courses.create!(name: 'New Course') }

View File

@ -130,12 +130,15 @@ describe "speed grader - quiz submissions" do
Quizzes::SubmissionGrader.new(qs).grade_submission
get "/courses/#{@course.id}/gradebook/speed_grader?assignment_id=#{@assignment.id}"
input = f('#grade_container input')
expect(input["readonly"]).to eq "true"
in_frame('speedgrader_iframe') do
question_inputs = ff('.header .question_input')
question_inputs.each { |qi| replace_content(qi, 3) }
submit_form('#update_history_form')
end
input = f('#grade_container input')
expect(input).to have_attribute('value', expected_points)
end

View File

@ -508,4 +508,33 @@ describe 'Speedgrader' do
expect(keyboard_modal).not_to be_displayed
end
end
context "closed grading periods" do
before(:once) do
course_with_teacher(active_all: true)
student_in_course(active_all: true)
account = @course.root_account
account.enable_feature! :multiple_grading_periods
gpg = GradingPeriodGroup.new
gpg.account_id = account
gpg.save!
gpg.grading_periods.create! start_date: 3.years.ago,
end_date: 1.year.ago,
close_date: 1.week.ago,
title: "closed grading period"
term = @course.enrollment_term
term.update_attribute :grading_period_group, gpg
@assignment = @course.assignments.create! name: "aaa", due_at: 2.years.ago
user_session(@teacher)
end
it "disables grading" do
get "/courses/#{@course.id}/gradebook/speed_grader?assignment_id=#{@assignment.id}"
expect(f("#grade_container input")["readonly"]).to eq "true"
expect(f("#closed_gp_notice")).to be_displayed
end
end
end