diff --git a/app/coffeescripts/api/gradingPeriodSetsApi.coffee b/app/coffeescripts/api/gradingPeriodSetsApi.coffee
index d2a41e563db..9eb98eb9424 100644
--- a/app/coffeescripts/api/gradingPeriodSetsApi.coffee
+++ b/app/coffeescripts/api/gradingPeriodSetsApi.coffee
@@ -3,10 +3,11 @@ define [
'underscore'
'i18n!grading_periods'
'jsx/shared/helpers/dateHelper'
+ 'compiled/api/gradingPeriodsApi'
'axios'
'jsx/shared/CheatDepaginator'
'jquery.instructure_misc_helpers'
-], ($, _, I18n, DateHelper, axios, Depaginate) ->
+], ($, _, I18n, DateHelper, gradingPeriodsApi, axios, Depaginate) ->
listUrl = () =>
ENV.GRADING_PERIOD_SETS_URL
@@ -17,26 +18,15 @@ define [
$.replaceTags(ENV.GRADING_PERIOD_SET_UPDATE_URL, 'id', id)
serializeSet = (set) =>
- grading_period_set: { title: set.title },
+ grading_period_set: { title: set.title, weighted: set.weighted },
enrollment_term_ids: set.enrollmentTermIDs
- deserializePeriods = (periods) =>
- _.map periods, (period) ->
- {
- id: period.id.toString()
- 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) ->
{
id: set.id.toString()
title: gradingPeriodSetTitle(set)
- gradingPeriods: deserializePeriods(set.grading_periods)
+ weighted: set.weighted || false
+ gradingPeriods: gradingPeriodsApi.deserializePeriods(set.grading_periods)
permissions: set.permissions
createdAt: new Date(set.created_at)
}
@@ -46,7 +36,7 @@ define [
set.title.trim()
else
createdAt = DateHelper.formatDateForDisplay(new Date(set.created_at))
- I18n.t("Set created %{createdAt}", { createdAt: createdAt });
+ I18n.t('Set created %{createdAt}', { createdAt })
deserializeSet = (set) ->
newSet = baseDeserializeSet(set)
@@ -57,29 +47,23 @@ define [
_.flatten _.map setGroups, (group) ->
_.map group.grading_period_sets, (set) -> baseDeserializeSet(set)
+ deserializeSet: deserializeSet
+
list: () ->
promise = new Promise (resolve, reject) =>
Depaginate(listUrl())
- .then (response) ->
- resolve(deserializeSets(response))
- .fail (error) ->
- reject(error)
+ .then (response) ->
+ resolve(deserializeSets(response))
+ .fail (error) ->
+ reject(error)
promise
create: (set) ->
- promise = new Promise (resolve, reject) =>
- axios.post(createUrl(), serializeSet(set))
- .then (response) ->
- resolve(deserializeSet(response.data.grading_period_set))
- .catch (error) ->
- reject(error)
- promise
+ axios.post(createUrl(), serializeSet(set))
+ .then (response) ->
+ deserializeSet(response.data.grading_period_set)
update: (set) ->
- promise = new Promise (resolve, reject) =>
- axios.patch(updateUrl(set.id), serializeSet(set))
- .then (response) ->
- resolve(set)
- .catch (error) ->
- reject(error)
- promise
+ axios.patch(updateUrl(set.id), serializeSet(set))
+ .then (response) ->
+ set
diff --git a/app/coffeescripts/api/gradingPeriodsApi.coffee b/app/coffeescripts/api/gradingPeriodsApi.coffee
index 0a43b52f8a3..664286ddf85 100644
--- a/app/coffeescripts/api/gradingPeriodsApi.coffee
+++ b/app/coffeescripts/api/gradingPeriodsApi.coffee
@@ -15,6 +15,7 @@ define [
start_date: period.startDate
end_date: period.endDate
close_date: period.closeDate
+ weight: period.weight
}
grading_periods: serialized
@@ -25,11 +26,10 @@ 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)
+ closeDate: new Date(period.close_date)
isLast: period.is_last
isClosed: period.is_closed
+ weight: period.weight
}
batchUpdate: (setId, periods) ->
diff --git a/app/coffeescripts/bundles/account_grading_standards.coffee b/app/coffeescripts/bundles/account_grading_standards.coffee
index e2b51a66960..f6de6c87b4c 100644
--- a/app/coffeescripts/bundles/account_grading_standards.coffee
+++ b/app/coffeescripts/bundles/account_grading_standards.coffee
@@ -7,7 +7,6 @@ require [
ReactDOM.render(
TabContainerFactory(
- multipleGradingPeriodsEnabled: ENV.MULTIPLE_GRADING_PERIODS
readOnly: ENV.GRADING_PERIODS_READ_ONLY
urls:
enrollmentTermsURL: ENV.ENROLLMENT_TERMS_URL
diff --git a/app/coffeescripts/bundles/assignment_index.coffee b/app/coffeescripts/bundles/assignment_index.coffee
index 4ff766a9923..74173e47743 100644
--- a/app/coffeescripts/bundles/assignment_index.coffee
+++ b/app/coffeescripts/bundles/assignment_index.coffee
@@ -95,7 +95,7 @@ require [
# kick it all off
assignmentGroups.fetch(reset: true).then ->
- app.filterResults() if ENV.MULTIPLE_GRADING_PERIODS_ENABLED
+ app.filterResults() if ENV.HAS_GRADING_PERIODS
if ENV.PERMISSIONS.manage
assignmentGroups.loadModuleNames()
else
diff --git a/app/coffeescripts/bundles/course_grading_standards.coffee b/app/coffeescripts/bundles/course_grading_standards.coffee
index b99f4bf789e..195a8612c06 100644
--- a/app/coffeescripts/bundles/course_grading_standards.coffee
+++ b/app/coffeescripts/bundles/course_grading_standards.coffee
@@ -4,8 +4,7 @@ require [
'jsx/grading/CourseTabContainer'
], (React, ReactDOM, CourseTabContainer) ->
CourseTabContainerFactory = React.createFactory CourseTabContainer
- mgpEnabled = ENV.MULTIPLE_GRADING_PERIODS
ReactDOM.render(
- CourseTabContainerFactory(multipleGradingPeriodsEnabled: mgpEnabled),
+ CourseTabContainerFactory(hasGradingPeriods: ENV.HAS_GRADING_PERIODS),
document.getElementById("react_grading_tabs")
)
diff --git a/app/coffeescripts/ember/screenreader_gradebook/components/final_grade_component.coffee b/app/coffeescripts/ember/screenreader_gradebook/components/final_grade_component.coffee
index ef9f43e183e..d7be1d02cdf 100644
--- a/app/coffeescripts/ember/screenreader_gradebook/components/final_grade_component.coffee
+++ b/app/coffeescripts/ember/screenreader_gradebook/components/final_grade_component.coffee
@@ -17,7 +17,7 @@ define [
pointRatio: ( ->
"#{I18n.n @get('student.total_grade.score')} / #{I18n.n @get('student.total_grade.possible')}"
- ).property("weighted_groups", "student.total_grade.score", "student.total_grade.possible")
+ ).property("weighted_grades", "student.total_grade.score", "student.total_grade.possible")
letterGrade:(->
GradingSchemeHelper.scoreToGrade(@get('percent'), @get('gradingStandard'))
@@ -26,7 +26,7 @@ define [
showGrade: Ember.computed.bool('student.total_grade.possible')
showPoints:(->
- !!(!@get("weighted_groups") && @get("student.total_grade"))
- ).property("weighted_groups","student.total_grade")
+ !!(!@get("weighted_grades") && @get("student.total_grade"))
+ ).property("weighted_grades","student.total_grade")
showLetterGrade: Ember.computed.bool("gradingStandard")
diff --git a/app/coffeescripts/ember/screenreader_gradebook/controllers/screenreader_gradebook_controller.coffee b/app/coffeescripts/ember/screenreader_gradebook/controllers/screenreader_gradebook_controller.coffee
index 7ee69ee3b4a..cc79e24b8d7 100644
--- a/app/coffeescripts/ember/screenreader_gradebook/controllers/screenreader_gradebook_controller.coffee
+++ b/app/coffeescripts/ember/screenreader_gradebook/controllers/screenreader_gradebook_controller.coffee
@@ -32,19 +32,23 @@ define [
'compiled/AssignmentDetailsDialog'
'compiled/AssignmentMuter'
'jsx/gradebook/CourseGradeCalculator'
+ 'jsx/gradebook/EffectiveDueDates'
'compiled/gradebook/OutcomeGradebookGrid'
'../../shared/components/ic_submission_download_dialog_component'
'str/htmlEscape'
'compiled/models/grade_summary/CalculationMethodContent'
'jsx/gradebook/SubmissionStateMap'
'compiled/api/gradingPeriodsApi'
+ 'compiled/api/gradingPeriodSetsApi'
'jsx/gradezilla/individual-gradebook/components/GradebookSelector'
'jquery.instructure_date_and_time'
-], ($, React, ReactDOM, ajax, round, userSettings, fetchAllPages, parseLinkHeader,
- I18n, Ember, _, tz, AssignmentDetailsDialog, AssignmentMuter,
- CourseGradeCalculator, outcomeGrid, ic_submission_download_dialog,
- htmlEscape, CalculationMethodContent, SubmissionStateMap, GradingPeriodsAPI,
- GradebookSelector) ->
+], (
+ $, React, ReactDOM,
+ ajax, round, userSettings, fetchAllPages, parseLinkHeader, I18n, Ember, _, tz, AssignmentDetailsDialog,
+ AssignmentMuter, CourseGradeCalculator, EffectiveDueDates, outcomeGrid, ic_submission_download_dialog,
+ htmlEscape, CalculationMethodContent, SubmissionStateMap, GradingPeriodsApi, GradingPeriodSetsApi,
+ GradebookSelector
+) ->
{ get, set, setProperties } = Ember
@@ -117,17 +121,33 @@ define [
submissionsUrl: get(window, 'ENV.GRADEBOOK_OPTIONS.submissions_url')
- mgpEnabled: get(window, 'ENV.GRADEBOOK_OPTIONS.multiple_grading_periods_enabled')
+ has_grading_periods: get(window, 'ENV.GRADEBOOK_OPTIONS.has_grading_periods')
+
+ gradingPeriods:
+ (->
+ periods = get(window, 'ENV.GRADEBOOK_OPTIONS.active_grading_periods')
+ deserializedPeriods = GradingPeriodsApi.deserializePeriods(periods)
+ optionForAllPeriods =
+ id: '0', title: I18n.t("all_grading_periods", "All Grading Periods")
+ _.compact([optionForAllPeriods].concat(deserializedPeriods))
+ )()
+
+ getGradingPeriodSet: ->
+ grading_period_set = get(window, 'ENV.GRADEBOOK_OPTIONS.grading_period_set')
+ if grading_period_set
+ GradingPeriodSetsApi.deserializeSet(grading_period_set)
+ else
+ null
gradingPeriods: (->
periods = get(window, 'ENV.GRADEBOOK_OPTIONS.active_grading_periods')
- deserializedPeriods = GradingPeriodsAPI.deserializePeriods(periods)
+ deserializedPeriods = GradingPeriodsApi.deserializePeriods(periods)
optionForAllPeriods =
id: '0', title: I18n.t("all_grading_periods", "All Grading Periods")
_.compact([optionForAllPeriods].concat(deserializedPeriods))
)()
- lastGeneratedCsvLabel: do () =>
+ lastGeneratedCsvLabel: do () =>
if get(window, 'ENV.GRADEBOOK_OPTIONS.gradebook_csv_progress')
gradebook_csv_export_date = get(window, 'ENV.GRADEBOOK_OPTIONS.gradebook_csv_progress.progress.updated_at')
I18n.t('Download Scores Generated on %{date}',
@@ -333,12 +353,26 @@ define [
id != userId
set(assignment, 'assignment_visibility', filteredVisibilities)
- calculate: (submissionsArray) ->
- CourseGradeCalculator.calculate submissionsArray, @assignmentGroupsHash(), @get('weightingScheme')
+ calculate: (student) ->
+ submissions = @submissionsForStudent(student)
+ assignmentGroups = @assignmentGroupsHash()
+ weightingScheme = @get('weightingScheme')
+ gradingPeriodSet = @getGradingPeriodSet()
+ effectiveDueDates = @get('effectiveDueDates.content')
+
+ hasGradingPeriods = gradingPeriodSet and effectiveDueDates
+
+ CourseGradeCalculator.calculate(
+ submissions,
+ assignmentGroups,
+ weightingScheme,
+ gradingPeriodSet if hasGradingPeriods,
+ EffectiveDueDates.scopeToUser(effectiveDueDates, student.id) if hasGradingPeriods
+ )
submissionsForStudent: (student) ->
allSubmissions = (value for key, value of student when key.match /^assignment_(?!group)/)
- return allSubmissions unless @get('mgpEnabled')
+ return allSubmissions unless @get('has_grading_periods')
selectedPeriodID = @get('selectedGradingPeriod.id')
return allSubmissions if !selectedPeriodID or selectedPeriodID == '0'
@@ -348,23 +382,32 @@ define [
calculateStudentGrade: (student) ->
if student.isLoaded
- finalOrCurrent = if @get('includeUngradedAssignments') then 'final' else 'current'
- result = @calculate(@submissionsForStudent(student))
- for group in result.group_sums
- set(student, "assignment_group_#{group.group.id}", group[finalOrCurrent])
- for submissionData in group[finalOrCurrent].submissions
- set(submissionData.submission, 'drop', submissionData.drop)
- result = result[finalOrCurrent]
+ grades = @calculate(student)
- percent = round (result.score / result.possible * 100), 2
+ selectedPeriodID = @get('selectedGradingPeriod.id')
+ if selectedPeriodID && selectedPeriodID != '0'
+ grades = grades.gradingPeriods[selectedPeriodID]
+
+ finalOrCurrent = if @get('includeUngradedAssignments') then 'final' else 'current'
+
+ for assignmentGroupId, grade of grades.assignmentGroups
+ set(student, "assignment_group_#{assignmentGroupId}", grade[finalOrCurrent])
+ for submissionData in grade[finalOrCurrent].submissions
+ set(submissionData.submission, 'drop', submissionData.drop)
+ grades = grades[finalOrCurrent]
+
+ percent = round (grades.score / grades.possible * 100), 2
percent = 0 if isNaN(percent)
setProperties student,
- total_grade: result
+ total_grade: grades
total_percent: I18n.n(percent, percentage: true)
calculateAllGrades: (->
@get('students').forEach (student) => @calculateStudentGrade student
- ).observes('includeUngradedAssignments','groupsAreWeighted', 'assignment_groups.@each.group_weight')
+ ).observes(
+ 'includeUngradedAssignments','groupsAreWeighted', 'assignment_groups.@each.group_weight',
+ 'effectiveDueDates.isLoaded'
+ )
sectionSelectDefaultLabel: I18n.t "all_sections", "All Sections"
studentSelectDefaultLabel: I18n.t "no_student", "No Student Selected"
@@ -378,7 +421,7 @@ define [
fetchAssignmentGroups: (->
params = { exclude_response_fields: ['in_closed_grading_period'] }
gpId = @get('selectedGradingPeriod.id')
- if @get('mgpEnabled') && gpId != '0'
+ if @get('has_grading_periods') && gpId != '0'
params.grading_period_id = gpId
@set('assignment_groups', [])
@set('assignmentsFromGroups', [])
@@ -475,16 +518,17 @@ define [
)
displayPointTotals: (->
- if @get("groupsAreWeighted")
- false
- else
- @get("showTotalAsPoints")
- ).property('groupsAreWeighted', 'showTotalAsPoints')
+ @get('showTotalAsPoints') and not @get('gradesAreWeighted')
+ ).property('gradesAreWeighted', 'showTotalAsPoints')
groupsAreWeighted: (->
@get("weightingScheme") == "percent"
).property("weightingScheme")
+ gradesAreWeighted: (->
+ @get('groupsAreWeighted') or !!@getGradingPeriodSet()?.weighted
+ ).property('weightingScheme')
+
updateShowTotalAs: (->
@set "showTotalAsPoints", @get("displayPointTotals")
ajax.request(
@@ -493,7 +537,7 @@ define [
url: ENV.GRADEBOOK_OPTIONS.setting_update_url
data:
show_total_grade_as_points: @get("displayPointTotals"))
- ).observes('showTotalAsPoints', 'groupsAreWeighted')
+ ).observes('showTotalAsPoints', 'gradesAreWeighted')
studentColumnData: {}
@@ -705,7 +749,7 @@ define [
populateSubmissionStateMap: (->
map = new SubmissionStateMap(
- gradingPeriodsEnabled: !!@mgpEnabled
+ hasGradingPeriods: !!@has_grading_periods
selectedGradingPeriodID: @get('selectedGradingPeriod.id') || '0'
isAdmin: ENV.current_user_roles && _.contains(ENV.current_user_roles, "admin")
)
diff --git a/app/coffeescripts/ember/screenreader_gradebook/templates/settings/grading_period_select.hbs b/app/coffeescripts/ember/screenreader_gradebook/templates/settings/grading_period_select.hbs
index 4fe7f4e1fd2..4a7a551e9f8 100644
--- a/app/coffeescripts/ember/screenreader_gradebook/templates/settings/grading_period_select.hbs
+++ b/app/coffeescripts/ember/screenreader_gradebook/templates/settings/grading_period_select.hbs
@@ -1,4 +1,4 @@
-{{#if mgpEnabled}}
+{{#if has_grading_periods}}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/app/coffeescripts/ember/screenreader_gradebook/templates/student_information/index.hbs b/app/coffeescripts/ember/screenreader_gradebook/templates/student_information/index.hbs
index 9d478418522..89658f09086 100644
--- a/app/coffeescripts/ember/screenreader_gradebook/templates/student_information/index.hbs
+++ b/app/coffeescripts/ember/screenreader_gradebook/templates/student_information/index.hbs
@@ -35,7 +35,7 @@
{{
final-grade
student=selectedStudent
- weighted_groups=groupsAreWeighted
+ weighted_grades=gradesAreWeighted
gradingStandard=ENV.GRADEBOOK_OPTIONS.grading_standard
}}
@@ -55,4 +55,4 @@
{{/if}}
-
\ No newline at end of file
+
diff --git a/app/coffeescripts/ember/screenreader_gradebook/tests/components/grading_cell.spec.coffee b/app/coffeescripts/ember/screenreader_gradebook/tests/components/grading_cell.spec.coffee
index dce1283a92f..2fa2d52af76 100644
--- a/app/coffeescripts/ember/screenreader_gradebook/tests/components/grading_cell.spec.coffee
+++ b/app/coffeescripts/ember/screenreader_gradebook/tests/components/grading_cell.spec.coffee
@@ -17,8 +17,7 @@ define [
App = startApp()
@component = App.GradingCellComponent.create()
- ENV.GRADEBOOK_OPTIONS.multiple_grading_periods_enabled = true
- ENV.GRADEBOOK_OPTIONS.latest_end_date_of_admin_created_grading_periods_in_the_past = "2013-10-01T10:00:00Z"
+ ENV.GRADEBOOK_OPTIONS.has_grading_periods = true
ENV.current_user_roles = []
setType = (type) =>
diff --git a/app/coffeescripts/ember/screenreader_gradebook/tests/controllers/screenreader_gradebook.spec.coffee b/app/coffeescripts/ember/screenreader_gradebook/tests/controllers/screenreader_gradebook.spec.coffee
index fe573cc4dfe..3d14a1cf1d2 100644
--- a/app/coffeescripts/ember/screenreader_gradebook/tests/controllers/screenreader_gradebook.spec.coffee
+++ b/app/coffeescripts/ember/screenreader_gradebook/tests/controllers/screenreader_gradebook.spec.coffee
@@ -5,10 +5,15 @@ define [
'../start_app'
'ember'
'../shared_ajax_fixtures'
+ 'spec/jsx/gradebook/GradeCalculatorSpecHelper'
'../../controllers/screenreader_gradebook_controller'
'compiled/userSettings'
+ 'jsx/gradebook/CourseGradeCalculator'
'vendor/jquery.ba-tinypubsub'
-], ($, _, ajax, startApp, Ember, fixtures, SRGBController, userSettings) ->
+], (
+ $, _, ajax, startApp, Ember, fixtures, GradeCalculatorSpecHelper, SRGBController, userSettings,
+ CourseGradeCalculator
+) ->
workAroundRaceCondition = ->
ajax.request()
@@ -19,6 +24,13 @@ define [
clone = (obj) ->
Ember.copy obj, true
+ createExampleGrades = GradeCalculatorSpecHelper.createCourseGradesWithGradingPeriods
+
+ createExampleGradingPeriodSet = ->
+ id: '1501'
+ gradingPeriods: [{ id: '701', weight: 50 }, { id: '702', weight: 50 }]
+ weighted: true
+
setup = (isDraftState=false, sortOrder='assignment_group') ->
fixtures.create()
@contextGetStub = sinon.stub(userSettings, 'contextGet')
@@ -51,7 +63,6 @@ define [
teardown: ->
teardown.call this
-
test 'calculates students properly', ->
equal @srgb.get('students.length'), 10
equal @srgb.get('students.firstObject').name, fixtures.students[0].user.name
@@ -85,19 +96,19 @@ define [
test 'displayName is hiddenName when hideStudentNames is true', ->
@srgb.set('hideStudentNames', true)
- equal @srgb.get('displayName'), "hiddenName"
+ equal @srgb.get('displayName'), 'hiddenName'
@srgb.set('hideStudentNames', false)
- equal @srgb.get('displayName'), "name"
+ equal @srgb.get('displayName'), 'name'
- test 'displayPointTotals is false when groups are weighted even if showTotalAsPoints is true', ->
+ test 'displayPointTotals is false when grades are weighted even if showTotalAsPoints is true', ->
Ember.run =>
@srgb.set('showTotalAsPoints', true)
- @srgb.set('groupsAreWeighted', true)
+ @srgb.set('gradesAreWeighted', true)
equal @srgb.get('displayPointTotals'), false
- test 'displayPointTotals is toggled by showTotalAsPoints when groups are unweighted', ->
+ test 'displayPointTotals is toggled by showTotalAsPoints when grades are unweighted', ->
Ember.run =>
- @srgb.set('groupsAreWeighted', false)
+ @srgb.set('gradesAreWeighted', false)
@srgb.set('showTotalAsPoints', true)
equal @srgb.get('displayPointTotals'), true
@srgb.set('showTotalAsPoints', false)
@@ -161,18 +172,75 @@ define [
equal @srgb.get('assignments.lastObject.name'), 'Drink Water'
start()
+ QUnit.module 'screenreader_gradebook_controller#gradesAreWeighted',
+ setup: ->
+ setup.call this
+ teardown: ->
+ teardown.call this
+
+ test 'is true when the grading period set is weighted', ->
+ gradingPeriodSet = createExampleGradingPeriodSet()
+ gradingPeriodSet.weighted = true
+ @stub(@srgb, 'getGradingPeriodSet').returns(gradingPeriodSet)
+ Ember.run =>
+ @srgb.set('groupsAreWeighted', false)
+ equal @srgb.get('gradesAreWeighted'), true
+
+ test 'is true when groupsAreWeighted is true', ->
+ gradingPeriodSet = createExampleGradingPeriodSet()
+ gradingPeriodSet.weighted = false
+ @stub(@srgb, 'getGradingPeriodSet').returns(gradingPeriodSet)
+ Ember.run =>
+ @srgb.set('groupsAreWeighted', true)
+ equal @srgb.get('gradesAreWeighted'), true
+
+ test 'is false when assignment groups are not weighted and the grading period set is not weighted', ->
+ gradingPeriodSet = createExampleGradingPeriodSet()
+ gradingPeriodSet.weighted = false
+ @stub(@srgb, 'getGradingPeriodSet').returns(gradingPeriodSet)
+ Ember.run =>
+ @srgb.set('groupsAreWeighted', false)
+ equal @srgb.get('gradesAreWeighted'), false
+
+ test 'is false when assignment groups are not weighted and the grading period set is not defined', ->
+ @stub(@srgb, 'getGradingPeriodSet').returns(null)
+ Ember.run =>
+ @srgb.set('groupsAreWeighted', false)
+ equal @srgb.get('gradesAreWeighted'), false
+
+ QUnit.module '#getGradingPeriodSet',
+ setup: ->
+ setup.call this
+
+ teardown: ->
+ teardown.call this
+
+ test 'normalizes the grading period set from the env', ->
+ ENV.GRADEBOOK_OPTIONS.grading_period_set =
+ id: '1501'
+ grading_periods: [{ id: '701', weight: 50 }, { id: '702', weight: 50 }]
+ weighted: true
+ gradingPeriodSet = @srgb.getGradingPeriodSet()
+ deepEqual(gradingPeriodSet.id, '1501')
+ equal(gradingPeriodSet.gradingPeriods.length, 2)
+ deepEqual(_.map(gradingPeriodSet.gradingPeriods, 'id'), ['701', '702'])
+
+ test 'sets grading period set to null when not defined in the env', ->
+ gradingPeriodSet = @srgb.getGradingPeriodSet()
+ deepEqual(gradingPeriodSet, null)
+
QUnit.module '#submissionsForStudent',
setupThis: (options = {}) ->
effectiveDueDates = Ember.ObjectProxy.create(
content: {
- 1: { 1: { grading_period_id: "1" } },
- 2: { 1: { grading_period_id: "2" } }
+ 1: { 1: { grading_period_id: '1' } },
+ 2: { 1: { grading_period_id: '2' } }
}
)
defaults = {
- mgpEnabled: false,
- "selectedGradingPeriod.id": null,
+ has_grading_periods: false,
+ 'selectedGradingPeriod.id': null,
effectiveDueDates
}
self = _.defaults options, defaults
@@ -181,40 +249,41 @@ define [
setup: ->
@student =
- id: "1"
- assignment_1: { assignment_id: "1", user_id: "1", name: "yolo" }
- assignment_2: { assignment_id: "2", user_id: "1", name: "froyo" }
+ id: '1'
+ assignment_1: { assignment_id: '1', user_id: '1', name: 'yolo' }
+ assignment_2: { assignment_id: '2', user_id: '1', name: 'froyo' }
setup.call this
teardown: ->
teardown.call this
- test "returns all submissions for the student (multiple grading periods disabled)", ->
+ test 'returns all submissions for the student when there are no grading periods', ->
self = @setupThis()
submissions = @srgb.submissionsForStudent.call(self, @student)
- propEqual _.pluck(submissions, "assignment_id"), ["1", "2"]
+ propEqual _.pluck(submissions, 'assignment_id'), ['1', '2']
- test "returns all submissions if 'All Grading Periods' is selected", ->
+ test 'returns all submissions if "All Grading Periods" is selected', ->
self = @setupThis(
- mgpEnabled: true,
- "selectedGradingPeriod.id": "0",
+ has_grading_periods: true,
+ 'selectedGradingPeriod.id': '0'
)
submissions = @srgb.submissionsForStudent.call(self, @student)
- propEqual _.pluck(submissions, "assignment_id"), ["1", "2"]
+ propEqual _.pluck(submissions, 'assignment_id'), ['1', '2']
- test "only returns submissions due for the student in the selected grading period", ->
+ test 'only returns submissions due for the student in the selected grading period', ->
self = @setupThis(
- mgpEnabled: true,
- "selectedGradingPeriod.id": "2"
+ has_grading_periods: true,
+ 'selectedGradingPeriod.id': '2'
)
submissions = @srgb.submissionsForStudent.call(self, @student)
- propEqual _.pluck(submissions, "assignment_id"), ["2"]
+ propEqual _.pluck(submissions, 'assignment_id'), ['2']
QUnit.module 'screenreader_gradebook_controller: with selected student',
setup: ->
setup.call this
+ @stub(@srgb, 'calculateStudentGrade')
@completeSetup = =>
workAroundRaceCondition().then =>
Ember.run =>
@@ -232,7 +301,7 @@ define [
start()
asyncTest 'assignments excludes any due for the selected student in a different grading period', ->
- @srgb.mgpEnabled = true
+ @srgb.has_grading_periods = true
@completeSetup().then =>
deepEqual(@srgb.get('assignments').mapBy('id'), ['3'])
start()
@@ -350,8 +419,8 @@ define [
id: '21'
name: 'Unpublished Assignment'
points_possible: 10
- grading_type: "percent"
- submission_types: ["none"]
+ grading_type: 'percent'
+ submission_types: ['none']
due_at: null
position: 6
assignment_group_id:'4'
@@ -370,24 +439,7 @@ define [
calc_stub = {
- group_sums: [
- {
- final:
- possible: 100
- score: 50
- submission_count: 10
- weight: 50
- submissions: []
- current:
- possible: 100
- score: 20
- submission_count: 5
- weight: 50
- submissions:[]
- group:
- id: "1"
- }
- ]
+ assignmentGroups: {}
final:
possible: 100
score: 90
@@ -398,24 +450,7 @@ define [
calc_stub_with_0_possible = {
- group_sums: [
- {
- final:
- possible: 0
- score: 50
- submission_count: 10
- weight: 50
- submissions: []
- current:
- possible: 0
- score: 20
- submission_count: 5
- weight: 50
- submissions:[]
- group:
- id: "1"
- }
- ]
+ assignmentGroups: {}
final:
possible: 0
score: 0
@@ -457,6 +492,125 @@ define [
equal @srgb.get('students.firstObject.total_percent'), '0%'
start()
+ QUnit.module 'screenreader_gradebook_controller: calculate',
+ setupThis:(options = {}) ->
+ assignments = [{ id: 201, points_possible: 10, omit_from_final_grade: false }]
+ submissions = [{ assignment_id: 201, score: 10 }]
+ assignmentGroupsHash = { 301: { id: 301, group_weight: 60, rules: {}, assignments } }
+ gradingPeriodSet =
+ id: '1501'
+ gradingPeriods: [{ id: '701', weight: 50 }, { id: '702', weight: 50 }]
+ weighted: true
+ props = _.defaults options,
+ weightingScheme: 'points'
+ getGradingPeriodSet: () -> gradingPeriodSet
+ 'effectiveDueDates.content': { 201: { 101: { grading_period_id: '701' } } }
+ _.extend {}, props,
+ get: (attr) -> props[attr]
+ submissionsForStudent: () -> submissions
+ assignmentGroupsHash: () -> assignmentGroupsHash
+
+ setup: ->
+ @calculate = SRGBController.prototype.calculate
+
+ test 'calculates grades using properties from the gradebook', ->
+ self = @setupThis()
+ @stub(CourseGradeCalculator, 'calculate').returns('expected')
+ grades = @calculate.call(self, id: '101', loaded: true)
+ equal(grades, 'expected')
+ args = CourseGradeCalculator.calculate.getCall(0).args
+ equal(args[0], self.submissionsForStudent())
+ equal(args[1], self.assignmentGroupsHash())
+ equal(args[2], self.get('weightingScheme'))
+ equal(args[3], self.getGradingPeriodSet())
+
+ test 'scopes effective due dates to the user', ->
+ self = @setupThis()
+ @stub(CourseGradeCalculator, 'calculate')
+ @calculate.call(self, id: '101', loaded: true)
+ dueDates = CourseGradeCalculator.calculate.getCall(0).args[4]
+ deepEqual(dueDates, 201: { grading_period_id: '701' })
+
+ test 'calculates grades without grading period data when grading period set is null', ->
+ self = @setupThis(getGradingPeriodSet: -> null)
+ @stub(CourseGradeCalculator, 'calculate')
+ @calculate.call(self, id: '101', loaded: true)
+ args = CourseGradeCalculator.calculate.getCall(0).args
+ equal(args[0], self.submissionsForStudent())
+ equal(args[1], self.assignmentGroupsHash())
+ equal(args[2], self.get('weightingScheme'))
+ equal(typeof args[3], 'undefined')
+ equal(typeof args[4], 'undefined')
+
+ test 'calculates grades without grading period data when effective due dates are not defined', ->
+ self = @setupThis('effectiveDueDates.content': null)
+ @stub(CourseGradeCalculator, 'calculate')
+ @calculate.call(self, id: '101', loaded: true)
+ args = CourseGradeCalculator.calculate.getCall(0).args
+ equal(args[0], self.submissionsForStudent())
+ equal(args[1], self.assignmentGroupsHash())
+ equal(args[2], self.get('weightingScheme'))
+ equal(typeof args[3], 'undefined')
+ equal(typeof args[4], 'undefined')
+
+ QUnit.module 'screenreader_gradebook_controller: calculateStudentGrade',
+ setupThis:(options = {}) ->
+ assignments = [{ id: 201, points_possible: 10, omit_from_final_grade: false }]
+ submissions = [{ assignment_id: 201, score: 10 }]
+ assignmentGroupsHash = { 301: { id: 301, group_weight: 60, rules: {}, assignments } }
+ gradingPeriodSet =
+ id: '1501'
+ gradingPeriods: [{ id: '701', weight: 50 }, { id: '702', weight: 50 }]
+ weighted: true
+ props = _.defaults options,
+ weightingScheme: 'points'
+ getGradingPeriodSet: () -> gradingPeriodSet
+ calculate: () -> CourseGradeCalculator.calculate()
+ 'effectiveDueDates.content': { 201: { 101: { grading_period_id: '701' } } }
+ 'selectedGradingPeriod.id': '0'
+ _.extend {}, props,
+ get: (attr) -> props[attr]
+ submissionsForStudent: () -> submissions
+ assignmentGroupsHash: () -> assignmentGroupsHash
+
+ setup: ->
+ @calculateStudentGrade = SRGBController.prototype.calculateStudentGrade
+
+ test 'stores the current grade on the student when not including ungraded assignments', ->
+ exampleGrades = createExampleGrades()
+ self = @setupThis(includeUngradedAssignments: false)
+ @stub(CourseGradeCalculator, 'calculate').returns(exampleGrades)
+ student = Ember.Object.create(id: '101', loaded: true)
+ student.set('isLoaded', true)
+ @calculateStudentGrade.call(self, student)
+ equal(student.total_grade, exampleGrades.current)
+
+ test 'stores the final grade on the student when including ungraded assignments', ->
+ exampleGrades = createExampleGrades()
+ self = @setupThis(includeUngradedAssignments: true)
+ @stub(CourseGradeCalculator, 'calculate').returns(exampleGrades)
+ student = Ember.Object.create(id: '101', loaded: true)
+ student.set('isLoaded', true)
+ @calculateStudentGrade.call(self, student)
+ equal(student.total_grade, exampleGrades.final)
+
+ test 'stores the current grade from the selected grading period when not including ungraded assignments', ->
+ exampleGrades = createExampleGrades()
+ self = @setupThis('selectedGradingPeriod.id': 701, includeUngradedAssignments: false)
+ @stub(CourseGradeCalculator, 'calculate').returns(exampleGrades)
+ student = Ember.Object.create(id: '101', loaded: true)
+ student.set('isLoaded', true)
+ @calculateStudentGrade.call(self, student)
+ equal(student.total_grade, exampleGrades.gradingPeriods[701].current)
+
+ test 'stores the final grade from the selected grading period when including ungraded assignments', ->
+ exampleGrades = createExampleGrades()
+ self = @setupThis('selectedGradingPeriod.id': 701, includeUngradedAssignments: true)
+ @stub(CourseGradeCalculator, 'calculate').returns(exampleGrades)
+ student = Ember.Object.create(id: '101', loaded: true)
+ student.set('isLoaded', true)
+ @calculateStudentGrade.call(self, student)
+ equal(student.total_grade, exampleGrades.gradingPeriods[701].final)
QUnit.module 'screenreader_gradebook_controller: notes computed props',
setup: ->
@@ -519,20 +673,20 @@ define [
Ember.run =>
@srgb.set('showNotesColumn', true)
@srgb.set('shouldCreateNotes', false)
- deepEqual @srgb.get('notesParams'), "column[hidden]": false
+ deepEqual @srgb.get('notesParams'), 'column[hidden]': false
Ember.run =>
@srgb.set('showNotesColumn', false)
@srgb.set('shouldCreateNotes', false)
- deepEqual @srgb.get('notesParams'), "column[hidden]": true
+ deepEqual @srgb.get('notesParams'), 'column[hidden]': true
Ember.run =>
@srgb.set('showNotesColumn', true)
@srgb.set('shouldCreateNotes', true)
deepEqual @srgb.get('notesParams'),
- "column[title]": "Notes"
- "column[position]": 1
- "column[teacher_notes]": true
+ 'column[title]': 'Notes'
+ 'column[position]': 1
+ 'column[teacher_notes]': true
test 'notesVerb', ->
Ember.run =>
@@ -552,13 +706,14 @@ define [
teardown.call this
test 'calculates invalidGroupsWarningPhrases properly', ->
- equal @srgb.get('invalidGroupsWarningPhrases'), "Note: Score does not include assignments from the group Invalid AG because it has no points possible."
+ equal @srgb.get('invalidGroupsWarningPhrases'),
+ 'Note: Score does not include assignments from the group Invalid AG because it has no points possible.'
test 'sets showInvalidGroupWarning to false if groups are not weighted', ->
Ember.run =>
- @srgb.set('weightingScheme', "equal")
+ @srgb.set('weightingScheme', 'equal')
equal @srgb.get('showInvalidGroupWarning'), false
- @srgb.set('weightingScheme', "percent")
+ @srgb.set('weightingScheme', 'percent')
equal @srgb.get('showInvalidGroupWarning'), true
diff --git a/app/coffeescripts/ember/screenreader_gradebook/tests/start_app.coffee b/app/coffeescripts/ember/screenreader_gradebook/tests/start_app.coffee
index c9e837003fb..1e47ad8dc37 100644
--- a/app/coffeescripts/ember/screenreader_gradebook/tests/start_app.coffee
+++ b/app/coffeescripts/ember/screenreader_gradebook/tests/start_app.coffee
@@ -3,11 +3,6 @@ define ['../main', 'ember'], (Application, Ember) ->
App = null
Ember.run.join ->
App = Application.create
- LOG_ACTIVE_GENERATION: yes
- LOG_MODULE_RESOLVER: yes
- LOG_TRANSITIONS: yes
- LOG_TRANSITIONS_INTERNAL: yes
- LOG_VIEW_LOOKUPS: yes
rootElement: '#fixtures'
App.Router.reopen history: 'none'
App.setupForTesting()
diff --git a/app/coffeescripts/gradebook/Gradebook.coffee b/app/coffeescripts/gradebook/Gradebook.coffee
index 68b5e11cf72..a0142ff6db2 100644
--- a/app/coffeescripts/gradebook/Gradebook.coffee
+++ b/app/coffeescripts/gradebook/Gradebook.coffee
@@ -1,4 +1,21 @@
-# This class both creates the slickgrid instance, and acts as the data source for that instance.
+#
+# Copyright (C) 2011 - 2017 Instructure, Inc.
+#
+# This file is part of Canvas.
+#
+# Canvas is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, version 3 of the License.
+#
+# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program. If not, see .
+#
+
define [
'jquery'
'underscore'
@@ -18,6 +35,7 @@ define [
'i18n!gradebook'
'compiled/gradebook/GradebookTranslations'
'jsx/gradebook/CourseGradeCalculator'
+ 'jsx/gradebook/EffectiveDueDates'
'jsx/gradebook/GradingSchemeHelper'
'compiled/userSettings'
'spin.js'
@@ -41,6 +59,7 @@ define [
'compiled/gradebook/GradebookKeyboardNav'
'jsx/gradebook/shared/helpers/assignmentHelper'
'compiled/api/gradingPeriodsApi'
+ 'compiled/api/gradingPeriodSetsApi'
'jst/_avatar' #needed by row_student_name
'jquery.ajaxJSON'
'jquery.instructure_date_and_time'
@@ -60,13 +79,13 @@ define [
], (
$, _, Backbone, tz, DataLoader, React, ReactDOM, LongTextEditor, KeyboardNavDialog, KeyboardNavTemplate, Slick,
TotalColumnHeaderView, round, InputFilterView, i18nObj, I18n, GRADEBOOK_TRANSLATIONS, CourseGradeCalculator,
- GradingSchemeHelper, UserSettings, Spinner, SubmissionDetailsDialog, AssignmentGroupWeightsDialog,
- GradeDisplayWarningDialog, PostGradesFrameDialog, SubmissionCell, GradebookHeaderMenu, NumberCompare, natcompare,
- htmlEscape, PostGradesStore, PostGradesApp, SubmissionStateMap, ColumnHeaderTemplate, GroupTotalCellTemplate,
- RowStudentNameTemplate, SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, assignmentHelper,
- GradingPeriodsAPI
+ EffectiveDueDates, GradingSchemeHelper, UserSettings, Spinner, SubmissionDetailsDialog,
+ AssignmentGroupWeightsDialog, GradeDisplayWarningDialog, PostGradesFrameDialog, SubmissionCell,
+ GradebookHeaderMenu, NumberCompare, natcompare, htmlEscape, PostGradesStore, PostGradesApp, SubmissionStateMap,
+ ColumnHeaderTemplate, GroupTotalCellTemplate, RowStudentNameTemplate, SectionMenuView, GradingPeriodMenuView,
+ GradebookKeyboardNav, assignmentHelper, GradingPeriodsApi, GradingPeriodSetsApi
) ->
-
+ # This class both creates the slickgrid instance, and acts as the data source for that instance.
class Gradebook
columnWidths =
assignment:
@@ -102,11 +121,15 @@ define [
@options.settings['show_inactive_enrollments'] == "true"
@totalColumnInFront = UserSettings.contextGet 'total_column_in_front'
@numberOfFrozenCols = if @totalColumnInFront then 3 else 2
- @gradingPeriodsEnabled = @options.multiple_grading_periods_enabled
- @gradingPeriods = GradingPeriodsAPI.deserializePeriods(@options.active_grading_periods)
+ @hasGradingPeriods = @options.has_grading_periods
+ @gradingPeriods = GradingPeriodsApi.deserializePeriods(@options.active_grading_periods)
+ if @options.grading_period_set
+ @gradingPeriodSet = GradingPeriodSetsApi.deserializeSet(@options.grading_period_set)
+ else
+ @gradingPeriodSet = null
@gradingPeriodToShow = @getGradingPeriodToShow()
@submissionStateMap = new SubmissionStateMap
- gradingPeriodsEnabled: @gradingPeriodsEnabled
+ hasGradingPeriods: @hasGradingPeriods
selectedGradingPeriodID: @gradingPeriodToShow
isAdmin: _.contains(ENV.current_user_roles, "admin")
@gradebookColumnSizeSettings = @options.gradebook_column_size_settings
@@ -120,7 +143,7 @@ define [
$.subscribe 'currentGradingPeriod/change', @updateCurrentGradingPeriod
assignmentGroupsParams = { exclude_response_fields: @fieldsToExcludeFromAssignments }
- if @gradingPeriodsEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
+ if @hasGradingPeriods && @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) ->
@@ -135,7 +158,7 @@ define [
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 @gradingPeriodsEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
+ submissionParams['grading_period_id'] = @gradingPeriodToShow if @hasGradingPeriods && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
dataLoader = DataLoader.loadGradebookData(
assignmentGroupsURL: @options.assignment_groups_url
assignmentGroupsParams: assignmentGroupsParams
@@ -235,7 +258,7 @@ define [
_.contains(activePeriodIds, gradingPeriodId)
getGradingPeriodToShow: () =>
- return null unless @gradingPeriodsEnabled
+ return null unless @hasGradingPeriods
currentPeriodId = UserSettings.contextGet('gradebook_current_grading_period')
if currentPeriodId && (@isAllGradingPeriods(currentPeriodId) || @gradingPeriodIsActive(currentPeriodId))
currentPeriodId
@@ -719,7 +742,7 @@ define [
templateOpts.warning = @totalGradeWarning
templateOpts.lastColumn = true
templateOpts.showPointsNotPercent = @displayPointTotals()
- templateOpts.hideTooltip = @weightedGroups() and not @totalGradeWarning
+ templateOpts.hideTooltip = @weightedGrades() and not @totalGradeWarning
GroupTotalCellTemplate templateOpts
htmlContentFormatter: (row, col, val, columnDef, student) ->
@@ -732,7 +755,7 @@ define [
submissionsForStudent: (student) =>
allSubmissions = (value for key, value of student when key.match /^assignment_(?!group)/)
- return allSubmissions unless @gradingPeriodsEnabled
+ return allSubmissions unless @hasGradingPeriods
return allSubmissions if !@gradingPeriodToShow or @isAllGradingPeriods(@gradingPeriodToShow)
_.filter allSubmissions, (submission) =>
@@ -741,17 +764,26 @@ define [
calculateStudentGrade: (student) =>
if student.loaded and student.initialized
- finalOrCurrent = if @include_ungraded_assignments then 'final' else 'current'
- result = CourseGradeCalculator.calculate(
+ hasGradingPeriods = @gradingPeriodSet and @effectiveDueDates
+
+ grades = CourseGradeCalculator.calculate(
@submissionsForStudent(student),
@assignmentGroups,
- @options.group_weighting_scheme
+ @options.group_weighting_scheme,
+ @gradingPeriodSet if hasGradingPeriods,
+ EffectiveDueDates.scopeToUser(@effectiveDueDates, student.id) if hasGradingPeriods
)
- for group in result.group_sums
- student["assignment_group_#{group.group.id}"] = group[finalOrCurrent]
- for submissionData in group[finalOrCurrent].submissions
+
+ if @gradingPeriodToShow && !@isAllGradingPeriods(@gradingPeriodToShow)
+ grades = grades.gradingPeriods[@gradingPeriodToShow]
+
+ finalOrCurrent = if @include_ungraded_assignments then 'final' else 'current'
+
+ for assignmentGroupId, grade of grades.assignmentGroups
+ student["assignment_group_#{assignmentGroupId}"] = grade[finalOrCurrent]
+ for submissionData in grade[finalOrCurrent].submissions
submissionData.submission.drop = submissionData.drop
- student["total_grade"] = result[finalOrCurrent]
+ student["total_grade"] = grades[finalOrCurrent]
@addDroppedClass(student)
@@ -1044,7 +1076,7 @@ define [
initHeader: =>
@drawSectionSelectButton() if @sections_enabled
- @drawGradingPeriodSelectButton() if @gradingPeriodsEnabled
+ @drawGradingPeriodSelectButton() if @hasGradingPeriods
$settingsMenu = $('.gradebook_dropdown')
showConcludedEnrollmentsEl = $settingsMenu.find("#show_concluded_enrollments")
@@ -1217,11 +1249,11 @@ define [
weightedGroups: =>
@options.group_weighting_scheme == "percent"
+ weightedGrades: =>
+ @options.group_weighting_scheme == "percent" || @gradingPeriodSet?.weighted || false
+
displayPointTotals: =>
- if @weightedGroups()
- false
- else
- @options.show_total_grade_as_points
+ @options.show_total_grade_as_points and not @weightedGrades()
switchTotalDisplay: =>
@options.show_total_grade_as_points = not @options.show_total_grade_as_points
@@ -1667,7 +1699,7 @@ define [
currentPeriodId == "0"
hideAggregateColumns: ->
- return false unless @gradingPeriodsEnabled
+ return false unless @hasGradingPeriods
return false if @options.all_grading_periods_totals
selectedPeriodId = @getGradingPeriodToShow()
@isAllGradingPeriods(selectedPeriodId)
diff --git a/app/coffeescripts/gradezilla/Gradebook.coffee b/app/coffeescripts/gradezilla/Gradebook.coffee
index 14b7cd277d2..1de06039033 100644
--- a/app/coffeescripts/gradezilla/Gradebook.coffee
+++ b/app/coffeescripts/gradezilla/Gradebook.coffee
@@ -29,12 +29,14 @@ define [
'jst/KeyboardNavDialog'
'vendor/slickgrid'
'compiled/api/gradingPeriodsApi'
+ 'compiled/api/gradingPeriodSetsApi'
'compiled/util/round'
'compiled/views/InputFilterView'
'i18nObj'
'i18n!gradezilla'
'compiled/gradezilla/GradebookTranslations'
'jsx/gradebook/CourseGradeCalculator'
+ 'jsx/gradebook/EffectiveDueDates'
'jsx/gradebook/GradingSchemeHelper'
'compiled/userSettings'
'spin.js'
@@ -79,15 +81,16 @@ define [
'compiled/jquery.kylemenu'
'compiled/jquery/fixDialogButtons'
'jsx/context_cards/StudentContextCardTrigger'
-], ($, _, Backbone, tz, DataLoader, React, ReactDOM, LongTextEditor, KeyboardNavDialog, KeyboardNavTemplate, Slick,
- GradingPeriodsAPI, round, InputFilterView, i18nObj, I18n, GRADEBOOK_TRANSLATIONS, CourseGradeCalculator,
- GradingSchemeHelper, UserSettings, Spinner, SubmissionDetailsDialog, AssignmentGroupWeightsDialog,
- GradeDisplayWarningDialog, PostGradesFrameDialog, SubmissionCell, GradebookHeaderMenu, NumberCompare, natcompare,
- htmlEscape, AssignmentColumnHeader, AssignmentGroupColumnHeader, StudentColumnHeader, TotalGradeColumnHeader,
- GradebookMenu, ViewOptionsMenu, ActionMenu, PostGradesStore, PostGradesApp, SubmissionStateMap,
- GroupTotalCellTemplate, RowStudentNameTemplate, SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav,
- assignmentHelper ) ->
-
+], (
+ $, _, Backbone, tz, DataLoader, React, ReactDOM, LongTextEditor, KeyboardNavDialog, KeyboardNavTemplate, Slick,
+ GradingPeriodsApi, GradingPeriodSetsApi, round, InputFilterView, i18nObj, I18n, GRADEBOOK_TRANSLATIONS,
+ CourseGradeCalculator, EffectiveDueDates, GradingSchemeHelper, UserSettings, Spinner, SubmissionDetailsDialog,
+ AssignmentGroupWeightsDialog, GradeDisplayWarningDialog, PostGradesFrameDialog, SubmissionCell,
+ GradebookHeaderMenu, NumberCompare, natcompare, htmlEscape, AssignmentColumnHeader, AssignmentGroupColumnHeader,
+ StudentColumnHeader, TotalGradeColumnHeader, GradebookMenu, ViewOptionsMenu, ActionMenu, PostGradesStore,
+ PostGradesApp, SubmissionStateMap, GroupTotalCellTemplate, RowStudentNameTemplate, SectionMenuView,
+ GradingPeriodMenuView, GradebookKeyboardNav, assignmentHelper
+) ->
renderComponent = (reactClass, mountPoint, props = {}, children = null) ->
component = React.createElement(reactClass, props, children)
ReactDOM.render(component, mountPoint)
@@ -127,11 +130,15 @@ define [
@options.settings['show_inactive_enrollments'] == "true"
@totalColumnInFront = UserSettings.contextGet 'total_column_in_front'
@numberOfFrozenCols = if @totalColumnInFront then 3 else 2
- @gradingPeriodsEnabled = @options.multiple_grading_periods_enabled
- @gradingPeriods = GradingPeriodsAPI.deserializePeriods(@options.active_grading_periods)
+ @hasGradingPeriods = @options.has_grading_periods
+ @gradingPeriods = GradingPeriodsApi.deserializePeriods(@options.active_grading_periods)
+ if @options.grading_period_set
+ @gradingPeriodSet = GradingPeriodSetsApi.deserializeSet(@options.grading_period_set)
+ else
+ @gradingPeriodSet = null
@gradingPeriodToShow = @getGradingPeriodToShow()
@submissionStateMap = new SubmissionStateMap
- gradingPeriodsEnabled: @gradingPeriodsEnabled
+ hasGradingPeriods: @hasGradingPeriods
selectedGradingPeriodID: @gradingPeriodToShow
isAdmin: _.contains(ENV.current_user_roles, "admin")
@gradebookColumnSizeSettings = @options.gradebook_column_size_settings
@@ -145,7 +152,7 @@ define [
$.subscribe 'currentGradingPeriod/change', @updateCurrentGradingPeriod
assignmentGroupsParams = { exclude_response_fields: @fieldsToExcludeFromAssignments }
- if @gradingPeriodsEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
+ if @hasGradingPeriods && @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) ->
@@ -158,7 +165,7 @@ define [
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 @gradingPeriodsEnabled && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
+ submissionParams['grading_period_id'] = @gradingPeriodToShow if @hasGradingPeriods && @gradingPeriodToShow && @gradingPeriodToShow != '0' && @gradingPeriodToShow != ''
dataLoader = DataLoader.loadGradebookData(
assignmentGroupsURL: @options.assignment_groups_url
assignmentGroupsParams: assignmentGroupsParams
@@ -270,7 +277,7 @@ define [
_.contains(activePeriodIds, gradingPeriodId)
getGradingPeriodToShow: () =>
- return null unless @gradingPeriodsEnabled
+ return null unless @hasGradingPeriods
currentPeriodId = UserSettings.contextGet('gradebook_current_grading_period')
if currentPeriodId && (@isAllGradingPeriods(currentPeriodId) || @gradingPeriodIsActive(currentPeriodId))
currentPeriodId
@@ -737,7 +744,7 @@ define [
templateOpts.warning = @totalGradeWarning
templateOpts.lastColumn = true
templateOpts.showPointsNotPercent = @displayPointTotals()
- templateOpts.hideTooltip = @weightedGroups() and not @totalGradeWarning
+ templateOpts.hideTooltip = @weightedGrades() and not @totalGradeWarning
GroupTotalCellTemplate templateOpts
htmlContentFormatter: (row, col, val, columnDef, student) ->
@@ -750,7 +757,7 @@ define [
submissionsForStudent: (student) =>
allSubmissions = (value for key, value of student when key.match /^assignment_(?!group)/)
- return allSubmissions unless @gradingPeriodsEnabled
+ return allSubmissions unless @hasGradingPeriods
return allSubmissions if !@gradingPeriodToShow or @isAllGradingPeriods(@gradingPeriodToShow)
_.filter allSubmissions, (submission) =>
@@ -759,17 +766,26 @@ define [
calculateStudentGrade: (student) =>
if student.loaded and student.initialized
- finalOrCurrent = if @include_ungraded_assignments then 'final' else 'current'
- result = CourseGradeCalculator.calculate(
+ hasGradingPeriods = @gradingPeriodSet and @effectiveDueDates
+
+ grades = CourseGradeCalculator.calculate(
@submissionsForStudent(student),
@assignmentGroups,
- @options.group_weighting_scheme
+ @options.group_weighting_scheme,
+ @gradingPeriodSet if hasGradingPeriods,
+ EffectiveDueDates.scopeToUser(@effectiveDueDates, student.id) if hasGradingPeriods
)
- for group in result.group_sums
- student["assignment_group_#{group.group.id}"] = group[finalOrCurrent]
- for submissionData in group[finalOrCurrent].submissions
+
+ if @gradingPeriodToShow && !@isAllGradingPeriods(@gradingPeriodToShow)
+ grades = grades.gradingPeriods[@gradingPeriodToShow]
+
+ finalOrCurrent = if @include_ungraded_assignments then 'final' else 'current'
+
+ for assignmentGroupId, grade of grades.assignmentGroups
+ student["assignment_group_#{assignmentGroupId}"] = grade[finalOrCurrent]
+ for submissionData in grade[finalOrCurrent].submissions
submissionData.submission.drop = submissionData.drop
- student["total_grade"] = result[finalOrCurrent]
+ student["total_grade"] = grades[finalOrCurrent]
@addDroppedClass(student)
@@ -1062,7 +1078,7 @@ define [
initHeader: =>
@renderGradebookMenus()
@drawSectionSelectButton() if @sections_enabled
- @drawGradingPeriodSelectButton() if @gradingPeriodsEnabled
+ @drawGradingPeriodSelectButton() if @hasGradingPeriods
$settingsMenu = $('.gradebook_dropdown')
showConcludedEnrollmentsEl = $settingsMenu.find("#show_concluded_enrollments")
@@ -1185,11 +1201,11 @@ define [
weightedGroups: =>
@options.group_weighting_scheme == "percent"
+ weightedGrades: =>
+ @options.group_weighting_scheme == "percent" || @gradingPeriodSet?.weighted || false
+
displayPointTotals: =>
- if @weightedGroups()
- false
- else
- @options.show_total_grade_as_points
+ @options.show_total_grade_as_points and not @weightedGrades()
switchTotalDisplay: =>
@options.show_total_grade_as_points = not @options.show_total_grade_as_points
@@ -1648,7 +1664,7 @@ define [
currentPeriodId == "0"
hideAggregateColumns: ->
- return false unless @gradingPeriodsEnabled
+ return false unless @hasGradingPeriods
return false if @options.all_grading_periods_totals
selectedPeriodId = @getGradingPeriodToShow()
@isAllGradingPeriods(selectedPeriodId)
diff --git a/app/coffeescripts/util/DateValidator.coffee b/app/coffeescripts/util/DateValidator.coffee
index e6f508a773f..39f8fcd3374 100644
--- a/app/coffeescripts/util/DateValidator.coffee
+++ b/app/coffeescripts/util/DateValidator.coffee
@@ -30,7 +30,7 @@ define [
constructor: (params) ->
@dateRange = params['date_range']
@data = params['data']
- @multipleGradingPeriodsEnabled = params.multipleGradingPeriodsEnabled
+ @hasGradingPeriods = params.hasGradingPeriods
@gradingPeriods = params.gradingPeriods
@userIsAdmin = params.userIsAdmin
@dueDateRequired = params.postToSIS && ENV.DUE_DATE_REQUIRED_FOR_ACCOUNT
@@ -79,7 +79,7 @@ define [
dueDateRequired: @dueDateRequired,
}
- if @multipleGradingPeriodsEnabled && !@userIsAdmin && @data.persisted == false
+ if @hasGradingPeriods && !@userIsAdmin && @data.persisted == false
datetimesToValidate.push {
date: dueAt,
range: "grading_period_range",
diff --git a/app/coffeescripts/views/assignments/CreateAssignmentView.coffee b/app/coffeescripts/views/assignments/CreateAssignmentView.coffee
index 748845d6aba..557174b6667 100644
--- a/app/coffeescripts/views/assignments/CreateAssignmentView.coffee
+++ b/app/coffeescripts/views/assignments/CreateAssignmentView.coffee
@@ -159,7 +159,7 @@ define [
dateValidator = new DateValidator(
date_range: _.extend({}, validRange)
data: data
- multipleGradingPeriodsEnabled: !!ENV.MULTIPLE_GRADING_PERIODS_ENABLED
+ hasGradingPeriods: !!ENV.HAS_GRADING_PERIODS
gradingPeriods: GradingPeriodsAPI.deserializePeriods(ENV.active_grading_periods)
userIsAdmin: @currentUserIsAdmin(),
data
diff --git a/app/coffeescripts/views/assignments/DueDateOverride.coffee b/app/coffeescripts/views/assignments/DueDateOverride.coffee
index a17666fadf5..84e6101e93d 100644
--- a/app/coffeescripts/views/assignments/DueDateOverride.coffee
+++ b/app/coffeescripts/views/assignments/DueDateOverride.coffee
@@ -44,7 +44,7 @@ define [
defaultSectionId: @model.defaultDueDateSectionId,
selectedGroupSetId: @model.assignment.get("group_category_id"),
gradingPeriods: @gradingPeriods,
- multipleGradingPeriodsEnabled: @multipleGradingPeriodsEnabled,
+ hasGradingPeriods: @hasGradingPeriods,
isOnlyVisibleToOverrides: @model.assignment.isOnlyVisibleToOverrides(),
dueAt: tz.parse(@model.assignment.get("due_at"))
})
@@ -53,7 +53,7 @@ define [
gradingPeriods: GradingPeriodsAPI.deserializePeriods(ENV.active_grading_periods)
- multipleGradingPeriodsEnabled: !!ENV.MULTIPLE_GRADING_PERIODS_ENABLED
+ hasGradingPeriods: !!ENV.HAS_GRADING_PERIODS
validateBeforeSave: (data, errors) =>
return errors unless data
@@ -69,7 +69,7 @@ define [
dateValidator = new DateValidator({
date_range: _.extend({}, ENV.VALID_DATE_RANGE)
data: override
- multipleGradingPeriodsEnabled: @multipleGradingPeriodsEnabled
+ hasGradingPeriods: @hasGradingPeriods
gradingPeriods: @gradingPeriods
userIsAdmin: _.contains(ENV.current_user_roles, "admin"),
postToSIS: @options.postToSIS || data.postToSIS == '1'
diff --git a/app/coffeescripts/views/assignments/IndexView.coffee b/app/coffeescripts/views/assignments/IndexView.coffee
index ab952eddbdf..fb150687a44 100644
--- a/app/coffeescripts/views/assignments/IndexView.coffee
+++ b/app/coffeescripts/views/assignments/IndexView.coffee
@@ -104,7 +104,7 @@ define [
filterResults: =>
term = $('#search_term').val()
gradingPeriod = null
- if ENV.MULTIPLE_GRADING_PERIODS_ENABLED
+ if ENV.HAS_GRADING_PERIODS
gradingPeriodIndex = $("#grading_period_selector").val()
gradingPeriod = @gradingPeriods[parseInt(gradingPeriodIndex)] if gradingPeriodIndex != "all"
@saveSelectedGradingPeriod(gradingPeriod)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f1ce7a60070..f6a42f916f8 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -235,11 +235,10 @@ class ApplicationController < ActionController::Base
end
helper_method :k12?
- def multiple_grading_periods?
- account_and_grading_periods_allowed? ||
- context_grading_periods_enabled?
+ def grading_periods?
+ !!@context.try(:grading_periods?)
end
- helper_method :multiple_grading_periods?
+ helper_method :grading_periods?
def master_courses?
@domain_root_account && @domain_root_account.feature_enabled?(:master_courses)
@@ -277,18 +276,6 @@ class ApplicationController < ActionController::Base
end
private :tool_dimensions
- def account_and_grading_periods_allowed?
- @context.is_a?(Account) &&
- @context.feature_allowed?(:multiple_grading_periods)
- end
- private :account_and_grading_periods_allowed?
-
- def context_grading_periods_enabled?
- @context.present? &&
- @context.feature_enabled?(:multiple_grading_periods)
- end
- private :context_grading_periods_enabled?
-
# Reject the request by halting the execution of the current handler
# and returning a helpful error message (and HTTP status code).
#
@@ -2050,7 +2037,7 @@ class ApplicationController < ActionController::Base
},
:POST_TO_SIS => Assignment.sis_grade_export_enabled?(@context),
:PERMISSIONS => permissions,
- :MULTIPLE_GRADING_PERIODS_ENABLED => @context.feature_enabled?(:multiple_grading_periods),
+ :HAS_GRADING_PERIODS => @context.grading_periods?,
:VALID_DATE_RANGE => CourseDateRange.new(@context),
:assignment_menu_tools => external_tools_display_hashes(:assignment_menu),
:discussion_topic_menu_tools => external_tools_display_hashes(:discussion_topic_menu),
@@ -2061,7 +2048,7 @@ class ApplicationController < ActionController::Base
conditional_release_js_env(includes: :active_rules)
- if @context.feature_enabled?(:multiple_grading_periods)
+ if @context.grading_periods?
js_env(:active_grading_periods => GradingPeriod.json_for(@context, @current_user))
end
end
diff --git a/app/controllers/assignment_groups_api_controller.rb b/app/controllers/assignment_groups_api_controller.rb
index a2602479631..581cea7b196 100644
--- a/app/controllers/assignment_groups_api_controller.rb
+++ b/app/controllers/assignment_groups_api_controller.rb
@@ -37,7 +37,7 @@ class AssignmentGroupsApiController < ApplicationController
#
# @argument grading_period_id [Integer]
# The id of the grading period in which assignment groups are being requested
- # (Requires the Multiple Grading Periods account feature turned on)
+ # (Requires grading periods to exist on the account)
#
# @returns AssignmentGroup
def show
@@ -45,7 +45,7 @@ class AssignmentGroupsApiController < ApplicationController
includes = Array(params[:include])
override_dates = value_to_boolean(params[:override_assignment_dates] || true)
assignments = @assignment_group.visible_assignments(@current_user)
- if params[:grading_period_id].present? && multiple_grading_periods?
+ if params[:grading_period_id].present?
assignments = GradingPeriod.for(@context).find_by(id: params[:grading_period_id]).assignments(assignments)
end
if assignments.any? && includes.include?('submission')
diff --git a/app/controllers/assignment_groups_controller.rb b/app/controllers/assignment_groups_controller.rb
index 1c98e90f9b7..dfb109b0fca 100644
--- a/app/controllers/assignment_groups_controller.rb
+++ b/app/controllers/assignment_groups_controller.rb
@@ -115,14 +115,14 @@ class AssignmentGroupsController < ApplicationController
#
# @argument grading_period_id [Integer]
# The id of the grading period in which assignment groups are being requested
- # (Requires the Multiple Grading Periods feature turned on.)
+ # (Requires grading periods to exist.)
#
# @argument scope_assignments_to_student [Boolean]
# If true, all assignments returned will apply to the current user in the
# specified grading period. If assignments apply to other students in the
# specified grading period, but not the current user, they will not be
- # returned. (Requires the grading_period_id argument and the Multiple Grading
- # Periods feature turned on. In addition, the current user must be a student.)
+ # returned. (Requires the grading_period_id argument and grading periods to
+ # exist. In addition, the current user must be a student.)
#
# @returns [AssignmentGroup]
def index
@@ -295,8 +295,7 @@ class AssignmentGroupsController < ApplicationController
end
def filter_by_grading_period?
- return false if all_grading_periods_selected?
- params[:grading_period_id].present? && multiple_grading_periods?
+ params[:grading_period_id].present? && !all_grading_periods_selected?
end
def all_grading_periods_selected?
@@ -390,7 +389,7 @@ class AssignmentGroupsController < ApplicationController
assignments = assignments.with_student_submission_count.all
- if params[:grading_period_id].present? && multiple_grading_periods?
+ if filter_by_grading_period?
assignments = filter_assignments_by_grading_period(assignments, context)
end
@@ -427,7 +426,7 @@ class AssignmentGroupsController < ApplicationController
end
def can_reorder_assignments?(assignments, group)
- return true unless @context.feature_enabled?(:multiple_grading_periods)
+ return true unless @context.grading_periods?
return true if @context.account_membership_allows(@current_user)
effective_due_dates = EffectiveDueDates.for_course(@context, assignments)
diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb
index 45706999d92..8d77d009634 100644
--- a/app/controllers/assignments_controller.rb
+++ b/app/controllers/assignments_controller.rb
@@ -443,7 +443,7 @@ class AssignmentsController < ApplicationController
GROUP_CATEGORIES: group_categories,
HAS_GRADED_SUBMISSIONS: @assignment.graded_submissions_exist?,
KALTURA_ENABLED: !!feature_enabled?(:kaltura),
- MULTIPLE_GRADING_PERIODS_ENABLED: @context.feature_enabled?(:multiple_grading_periods),
+ HAS_GRADING_PERIODS: @context.grading_periods?,
PLAGIARISM_DETECTION_PLATFORM: @context.root_account.feature_enabled?(:plagiarism_detection_platform),
POST_TO_SIS: post_to_sis,
SIS_NAME: AssignmentUtil.post_to_sis_friendly_name(@assignment),
@@ -475,7 +475,7 @@ class AssignmentsController < ApplicationController
hash[:SELECTED_CONFIG_TOOL_ID] = selected_tool ? selected_tool.id : nil
hash[:SELECTED_CONFIG_TOOL_TYPE] = selected_tool ? selected_tool.class.to_s : nil
- if @context.feature_enabled?(:multiple_grading_periods)
+ if @context.grading_periods?
hash[:active_grading_periods] = GradingPeriod.json_for(@context, @current_user)
end
append_sis_data(hash)
diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb
index 28b5f1193a1..2bdc4bcea83 100644
--- a/app/controllers/courses_controller.rb
+++ b/app/controllers/courses_controller.rb
@@ -359,14 +359,14 @@ class CoursesController < ApplicationController
# - "current_grading_period_scores": Optional information to include with
# each Course. When current_grading_period_scores is given and total_scores
# is given, any student enrollments will also include the fields
- # 'multiple_grading_periods_enabled',
+ # 'has_grading_periods',
# 'totals_for_all_grading_periods_option', 'current_grading_period_title',
# 'current_grading_period_id', current_period_computed_current_score',
# 'current_period_computed_final_score',
# 'current_period_computed_current_grade', and
# 'current_period_computed_final_grade' (see Enrollment documentation for
# more information on these fields). In addition, when this argument is
- # passed, the course will have a 'multiple_grading_periods_enabled' attribute
+ # passed, the course will have a 'has_grading_periods' attribute
# on it. This argument is ignored if the course is configured to hide final
# grades or if the total_scores argument is not included.
# - "term": Optional information to include with each Course. When
@@ -474,14 +474,14 @@ class CoursesController < ApplicationController
# - "current_grading_period_scores": Optional information to include with
# each Course. When current_grading_period_scores is given and total_scores
# is given, any student enrollments will also include the fields
- # 'multiple_grading_periods_enabled',
+ # 'has_grading_periods',
# 'totals_for_all_grading_periods_option', 'current_grading_period_title',
# 'current_grading_period_id', current_period_computed_current_score',
# 'current_period_computed_final_score',
# 'current_period_computed_current_grade', and
# 'current_period_computed_final_grade' (see Enrollment documentation for
# more information on these fields). In addition, when this argument is
- # passed, the course will have a 'multiple_grading_periods_enabled' attribute
+ # passed, the course will have a 'has_grading_periods' attribute
# on it. This argument is ignored if the course is configured to hide final
# grades or if the total_scores argument is not included.
# - "term": Optional information to include with each Course. When
@@ -2474,9 +2474,9 @@ class CoursesController < ApplicationController
# @API Get effective due dates
# For each assignment in the course, returns each assigned student's ID
- # and their corresponding due date along with some Multiple Grading Periods
- # data. Returns a collection with keys representing assignment IDs and values
- # as a collection containing keys representing student IDs and values representing
+ # and their corresponding due date along with some grading period data.
+ # Returns a collection with keys representing assignment IDs and values as a
+ # collection containing keys representing student IDs and values representing
# the student's effective due_at, the grading_period_id of which the due_at falls
# in, and whether or not the grading period is closed (in_closed_grading_period)
#
@@ -2805,7 +2805,7 @@ class CoursesController < ApplicationController
end
def can_change_group_weighting_scheme?
- return true unless @course.feature_enabled?(:multiple_grading_periods)
+ return true unless @course.grading_periods?
return true if @course.account_membership_allows(@current_user)
!@course.any_assignment_in_closed_grading_period?
end
diff --git a/app/controllers/discussion_topics_controller.rb b/app/controllers/discussion_topics_controller.rb
index 6e2ffcfe658..15dfd827d29 100644
--- a/app/controllers/discussion_topics_controller.rb
+++ b/app/controllers/discussion_topics_controller.rb
@@ -448,7 +448,7 @@ class DiscussionTopicsController < ApplicationController
GROUP_CATEGORIES: categories.
reject(&:student_organized?).
map { |category| { id: category.id, name: category.name } },
- MULTIPLE_GRADING_PERIODS_ENABLED: @context.feature_enabled?(:multiple_grading_periods),
+ HAS_GRADING_PERIODS: @context.grading_periods?,
SECTION_LIST: sections.map { |section| { id: section.id, name: section.name } }
}
@@ -479,7 +479,7 @@ class DiscussionTopicsController < ApplicationController
js_hash[:CANCEL_TO] = cancel_redirect_url
append_sis_data(js_hash)
- if @context.feature_enabled?(:multiple_grading_periods)
+ if @context.grading_periods?
gp_context = @context.is_a?(Group) ? @context.context : @context
js_hash[:active_grading_periods] = GradingPeriod.json_for(gp_context, @current_user)
end
diff --git a/app/controllers/enrollments_api_controller.rb b/app/controllers/enrollments_api_controller.rb
index 41960d98cd2..77f9ef58296 100644
--- a/app/controllers/enrollments_api_controller.rb
+++ b/app/controllers/enrollments_api_controller.rb
@@ -207,8 +207,8 @@
# "example": "B-",
# "type": "string"
# },
-# "multiple_grading_periods_enabled": {
-# "description": "optional: Indicates whether the course the enrollment belongs to has the Multiple Grading Periods feature enabled. (applies only to student enrollments, and only available in course endpoints)",
+# "has_grading_periods": {
+# "description": "optional: Indicates whether the course the enrollment belongs to has grading periods set up. (applies only to student enrollments, and only available in course endpoints)",
# "example": true,
# "type": "boolean"
# },
@@ -218,32 +218,32 @@
# "type": "boolean"
# },
# "current_grading_period_title": {
-# "description": "optional: The name of the currently active grading period, if one exists. If the course the enrollment belongs to does not have Multiple Grading Periods enabled, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
+# "description": "optional: The name of the currently active grading period, if one exists. If the course the enrollment belongs to does not have grading periods, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
# "example": "Fall Grading Period",
# "type": "string"
# },
# "current_grading_period_id": {
-# "description": "optional: The id of the currently active grading period, if one exists. If the course the enrollment belongs to does not have Multiple Grading Periods enabled, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
+# "description": "optional: The id of the currently active grading period, if one exists. If the course the enrollment belongs to does not have grading periods, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
# "example": 5,
# "type": "integer"
# },
# "current_period_computed_current_score": {
-# "description": "optional: The student's score in the course for the current grading period, ignoring ungraded assignments. If the course the enrollment belongs to does not have Multiple Grading Periods enabled, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
+# "description": "optional: The student's score in the course for the current grading period, ignoring ungraded assignments. If the course the enrollment belongs to does not have grading periods, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
# "example": 95.80,
# "type": "number"
# },
# "current_period_computed_final_score": {
-# "description": "optional: The student's score in the course for the current grading period, including ungraded assignments with a score of 0. If the course the enrollment belongs to does not have Multiple Grading Periods enabled, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
+# "description": "optional: The student's score in the course for the current grading period, including ungraded assignments with a score of 0. If the course the enrollment belongs to does not have grading periods, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
# "example": 85.25,
# "type": "number"
# },
# "current_period_computed_current_grade": {
-# "description": "optional: The letter grade equivalent of current_period_computed_current_score, if available. If the course the enrollment belongs to does not have Multiple Grading Periods enabled, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
+# "description": "optional: The letter grade equivalent of current_period_computed_current_score, if available. If the course the enrollment belongs to does not have grading periods, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
# "example": "A",
# "type": "string"
# },
# "current_period_computed_final_grade": {
-# "description": "optional: The letter grade equivalent of current_period_computed_final_score, if available. If the course the enrollment belongs to does not have Multiple Grading Periods enabled, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
+# "description": "optional: The letter grade equivalent of current_period_computed_final_score, if available. If the course the enrollment belongs to does not have grading periods, or if no currently active grading period exists, the value will be null. (applies only to student enrollments, and only available in course endpoints)",
# "example": "B",
# "type": "string"
# }
@@ -262,7 +262,6 @@ class EnrollmentsApiController < ApplicationController
:inactive_role => 'Cannot create an enrollment with this role because it is inactive.',
:base_type_mismatch => 'The specified type must match the base type for the role',
:concluded_course => 'Can\'t add an enrollment to a concluded course.',
- :multiple_grading_periods_disabled => 'Multiple Grading Periods feature is disabled. Cannot filter by grading_period_id with this feature disabled',
:insufficient_sis_permissions => 'Insufficient permissions to filter by SIS fields'
}
@@ -371,20 +370,10 @@ class EnrollmentsApiController < ApplicationController
if params[:grading_period_id].present?
if @context.is_a? User
- unless @context.account.feature_enabled?(:multiple_grading_periods)
- render_create_errors([@@errors[:multiple_grading_periods_disabled]])
- return false
- end
-
grading_period = @context.courses.lazy.map do |course|
GradingPeriod.for(course).find_by(id: params[:grading_period_id])
end.detect(&:present?)
else
- unless multiple_grading_periods?
- render_create_errors([@@errors[:multiple_grading_periods_disabled]])
- return false
- end
-
grading_period = GradingPeriod.for(@context).find_by(id: params[:grading_period_id])
end
diff --git a/app/controllers/filters/grading_periods.rb b/app/controllers/filters/grading_periods.rb
deleted file mode 100644
index 669590aceaa..00000000000
--- a/app/controllers/filters/grading_periods.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Filters::GradingPeriods
- def check_feature_flag
- unless multiple_grading_periods?
- if api_request?
- render json: {message: t('Page not found')}, status: :not_found
- else
- render status: 404, template: "shared/errors/404_message"
- end
- end
- end
-end
diff --git a/app/controllers/gradebooks_controller.rb b/app/controllers/gradebooks_controller.rb
index bc6d6e683d8..9c80ebc7296 100644
--- a/app/controllers/gradebooks_controller.rb
+++ b/app/controllers/gradebooks_controller.rb
@@ -36,7 +36,7 @@ class GradebooksController < ApplicationController
MAX_POST_GRADES_TOOLS = 10
def grade_summary
- set_current_grading_period if multiple_grading_periods?
+ set_current_grading_period if grading_periods?
@presenter = grade_summary_presenter
# do this as the very first thing, if the current user is a
# teacher in the course and they are not trying to view another
@@ -59,9 +59,10 @@ class GradebooksController < ApplicationController
add_crumb(@presenter.student_name, named_context_url(@context, :context_student_grades_url,
@presenter.student_id))
gp_id = nil
- if multiple_grading_periods?
+ if grading_periods?
@grading_periods = active_grading_periods_json
gp_id = @current_grading_period_id unless view_all_grading_periods?
+ effective_due_dates = EffectiveDueDates.new(@context).to_hash
end
@exclude_total = exclude_total?(@context)
@@ -89,7 +90,6 @@ class GradebooksController < ApplicationController
grading_period = @grading_periods && @grading_periods.find { |period| period[:id] == gp_id }
-
ags_json = light_weight_ags_json(@presenter.groups, {student: @presenter.student})
grading_scheme = @context.grading_standard.try(:data) ||
@@ -100,7 +100,10 @@ class GradebooksController < ApplicationController
group_weighting_scheme: @context.group_weighting_scheme,
show_total_grade_as_points: @context.settings[:show_total_grade_as_points],
grading_scheme: grading_scheme,
+ grading_period_set: grading_period_group_json,
grading_period: grading_period,
+ grading_periods: @grading_periods,
+ effective_due_dates: effective_due_dates,
exclude_total: @exclude_total,
student_outcome_gradebook_enabled: @context.feature_enabled?(:student_outcome_gradebook),
student_id: @presenter.student_id)
@@ -124,7 +127,7 @@ class GradebooksController < ApplicationController
assignment_groups.map do |ag|
visible_assignments = ag.visible_assignments(opts[:student] || @current_user).to_a
- if multiple_grading_periods? && @current_grading_period_id && !view_all_grading_periods?
+ if grading_periods? && @current_grading_period_id && !view_all_grading_periods?
current_period = GradingPeriod.for(@context).find_by(id: @current_grading_period_id)
visible_assignments = current_period.assignments_for_student(visible_assignments, opts[:student])
end
@@ -193,7 +196,7 @@ class GradebooksController < ApplicationController
def show
if authorized_action(@context, @current_user, [:manage_grades, :view_all_grades])
@last_exported_gradebook_csv = GradebookCsv.last_successful_export(course: @context, user: @current_user)
- set_current_grading_period if multiple_grading_periods?
+ set_current_grading_period if grading_periods?
set_js_env
@course_is_concluded = @context.completed?
@post_grades_tools = post_grades_tools
@@ -289,27 +292,34 @@ class GradebooksController < ApplicationController
@current_grading_period_id == 0
end
+ def grading_period_group
+ return @grading_period_group if defined? @grading_period_group
+
+ @grading_period_group = active_grading_periods.first&.grading_period_group
+ end
+
def active_grading_periods
@active_grading_periods ||= GradingPeriod.for(@context).sort_by(&:start_date)
end
+ def grading_period_group_json
+ return @grading_period_group_json if defined? @grading_period_group_json
+ return @grading_period_group_json = nil unless grading_period_group.present?
+
+ @grading_period_group_json = grading_period_group
+ .as_json
+ .fetch(:grading_period_group)
+ .merge(grading_periods: active_grading_periods_json)
+ end
+
def active_grading_periods_json
@agp_json ||= GradingPeriod.periods_json(active_grading_periods, @current_user)
end
- def latest_end_date_of_admin_created_grading_periods_in_the_past
- periods = active_grading_periods.select do |period|
- admin_created = period.account_group?
- admin_created && period.end_date.past?
- end
- periods.map(&:end_date).compact.sort.last
- end
- private :latest_end_date_of_admin_created_grading_periods_in_the_past
-
def set_js_env
@gradebook_is_editable = @context.grants_right?(@current_user, session, :manage_grades)
per_page = Setting.get('api_max_per_page', '50').to_i
- teacher_notes = @context.custom_gradebook_columns.not_deleted.where(:teacher_notes=> true).first
+ teacher_notes = @context.custom_gradebook_columns.not_deleted.where(teacher_notes: true).first
ag_includes = [:assignments, :assignment_visibility]
chunk_size = if @context.assignments.published.count < Setting.get('gradebook2.assignments_threshold', '20').to_i
Setting.get('gradebook2.submissions_chunk_size', '35').to_i
@@ -317,82 +327,88 @@ class GradebooksController < ApplicationController
Setting.get('gradebook2.many_submissions_chunk_size', '10').to_i
end
js_env STUDENT_CONTEXT_CARDS_ENABLED: @domain_root_account.feature_enabled?(:student_context_cards)
- js_env :GRADEBOOK_OPTIONS => {
- :gradezilla => @context.root_account.feature_enabled?(:gradezilla),
- :chunk_size => chunk_size,
- :assignment_groups_url => api_v1_course_assignment_groups_url(
+ js_env GRADEBOOK_OPTIONS: {
+ gradezilla: @context.root_account.feature_enabled?(:gradezilla),
+ chunk_size: chunk_size,
+ assignment_groups_url: api_v1_course_assignment_groups_url(
@context,
include: ag_includes,
override_assignment_dates: "false",
exclude_assignment_submission_types: ['wiki_page']
),
- :sections_url => api_v1_course_sections_url(@context),
- :course_url => api_v1_course_url(@context),
- :effective_due_dates_url => api_v1_course_effective_due_dates_url(@context),
- :enrollments_url => custom_course_enrollments_api_url(per_page: per_page),
- :enrollments_with_concluded_url =>
+ sections_url: api_v1_course_sections_url(@context),
+ course_url: api_v1_course_url(@context),
+ effective_due_dates_url: api_v1_course_effective_due_dates_url(@context),
+ enrollments_url: custom_course_enrollments_api_url(per_page: per_page),
+ enrollments_with_concluded_url:
custom_course_enrollments_api_url(include_concluded: true, per_page: per_page),
- :enrollments_with_inactive_url =>
+ enrollments_with_inactive_url:
custom_course_enrollments_api_url(include_inactive: true, per_page: per_page),
- :enrollments_with_concluded_and_inactive_url =>
+ enrollments_with_concluded_and_inactive_url:
custom_course_enrollments_api_url(include_concluded: true, include_inactive: true, per_page: per_page),
- :students_url => custom_course_users_api_url(per_page: per_page),
- :students_with_concluded_enrollments_url =>
+ students_url: custom_course_users_api_url(per_page: per_page),
+ students_with_concluded_enrollments_url:
custom_course_users_api_url(include_concluded: true, per_page: per_page),
- :students_with_inactive_enrollments_url =>
+ students_with_inactive_enrollments_url:
custom_course_users_api_url(include_inactive: true, per_page: per_page),
- :students_with_concluded_and_inactive_enrollments_url =>
+ students_with_concluded_and_inactive_enrollments_url:
custom_course_users_api_url(include_concluded: true, include_inactive: true, per_page: per_page),
- :submissions_url => api_v1_course_student_submissions_url(@context, :grouped => '1'),
- :outcome_links_url => api_v1_course_outcome_group_links_url(@context, :outcome_style => :full),
- :outcome_rollups_url => api_v1_course_outcome_rollups_url(@context, :per_page => 100),
- :change_grade_url => api_v1_course_assignment_submission_url(@context, ":assignment", ":submission", :include =>[:visibility]),
- :context_url => named_context_url(@context, :context_url),
- :download_assignment_submissions_url => named_context_url(@context, :context_assignment_submissions_url, "{{ assignment_id }}", :zip => 1),
- :re_upload_submissions_url => named_context_url(@context, :submissions_upload_context_gradebook_url, "{{ assignment_id }}"),
- :context_id => @context.id.to_s,
- :context_code => @context.asset_string,
- :context_sis_id => @context.sis_source_id,
- :group_weighting_scheme => @context.group_weighting_scheme,
- :grading_standard => @context.grading_standard_enabled? && (@context.grading_standard.try(:data) || GradingStandard.default_grading_standard),
- :course_is_concluded => @context.completed?,
- :course_name => @context.name,
- :gradebook_is_editable => @gradebook_is_editable,
- :context_allows_gradebook_uploads => @context.allows_gradebook_uploads?,
- :gradebook_import_url => new_course_gradebook_upload_path(@context),
- :setting_update_url => api_v1_course_settings_url(@context),
- :show_total_grade_as_points => @context.settings[:show_total_grade_as_points],
- :publish_to_sis_enabled => @context.allows_grade_publishing_by(@current_user) && @gradebook_is_editable,
- :publish_to_sis_url => context_url(@context, :context_details_url, :anchor => 'tab-grade-publishing'),
- :speed_grader_enabled => @context.allows_speed_grader?,
- :multiple_grading_periods_enabled => multiple_grading_periods?,
- :active_grading_periods => active_grading_periods_json,
- :latest_end_date_of_admin_created_grading_periods_in_the_past => latest_end_date_of_admin_created_grading_periods_in_the_past,
- :current_grading_period_id => @current_grading_period_id,
- :outcome_gradebook_enabled => @context.feature_enabled?(:outcome_gradebook),
- :custom_columns_url => api_v1_course_custom_gradebook_columns_url(@context),
- :custom_column_url => api_v1_course_custom_gradebook_column_url(@context, ":id"),
- :custom_column_data_url => api_v1_course_custom_gradebook_column_data_url(@context, ":id", per_page: per_page),
- :custom_column_datum_url => api_v1_course_custom_gradebook_column_datum_url(@context, ":id", ":user_id"),
- :reorder_custom_columns_url => api_v1_custom_gradebook_columns_reorder_url(@context),
- :teacher_notes => teacher_notes && custom_gradebook_column_json(teacher_notes, @current_user, session),
- :change_gradebook_version_url => context_url(@context, :change_gradebook_version_context_gradebook_url, :version => 2),
- :export_gradebook_csv_url => course_gradebook_csv_url,
- :gradebook_csv_progress => @last_exported_gradebook_csv.try(:progress),
- :attachment_url => @last_exported_gradebook_csv.try(:attachment).try(:download_url),
- :attachment => @last_exported_gradebook_csv.try(:attachment),
- :sis_app_url => Setting.get('sis_app_url', nil),
- :sis_app_token => Setting.get('sis_app_token', nil),
- :list_students_by_sortable_name_enabled => @context.list_students_by_sortable_name?,
- :gradebook_column_size_settings => @current_user.preferences[:gradebook_column_size],
- :gradebook_column_size_settings_url => change_gradebook_column_size_course_gradebook_url,
- :gradebook_column_order_settings => @current_user.preferences[:gradebook_column_order].try(:[], @context.id),
- :gradebook_column_order_settings_url => save_gradebook_column_order_course_gradebook_url,
- :all_grading_periods_totals => @context.feature_enabled?(:all_grading_periods_totals),
- :sections => sections_json(@context.active_course_sections, @current_user, session),
- :settings_update_url => api_v1_course_gradebook_settings_update_url(@context),
- :settings => @current_user.preferences.fetch(:gradebook_settings, {}).fetch(@context.id, {}),
- :version => params.fetch(:version, nil)
+ submissions_url: api_v1_course_student_submissions_url(@context, grouped: '1'),
+ outcome_links_url: api_v1_course_outcome_group_links_url(@context, outcome_style: :full),
+ outcome_rollups_url: api_v1_course_outcome_rollups_url(@context, per_page: 100),
+ change_grade_url:
+ api_v1_course_assignment_submission_url(@context, ":assignment", ":submission", include: [:visibility]),
+ context_url: named_context_url(@context, :context_url),
+ download_assignment_submissions_url:
+ named_context_url(@context, :context_assignment_submissions_url, "{{ assignment_id }}", zip: 1),
+ re_upload_submissions_url:
+ named_context_url(@context, :submissions_upload_context_gradebook_url, "{{ assignment_id }}"),
+ context_id: @context.id.to_s,
+ context_code: @context.asset_string,
+ context_sis_id: @context.sis_source_id,
+ group_weighting_scheme: @context.group_weighting_scheme,
+ grading_standard: (
+ @context.grading_standard_enabled? &&
+ (@context.grading_standard.try(:data) || GradingStandard.default_grading_standard)
+ ),
+ course_is_concluded: @context.completed?,
+ course_name: @context.name,
+ gradebook_is_editable: @gradebook_is_editable,
+ context_allows_gradebook_uploads: @context.allows_gradebook_uploads?,
+ gradebook_import_url: new_course_gradebook_upload_path(@context),
+ setting_update_url: api_v1_course_settings_url(@context),
+ show_total_grade_as_points: @context.settings[:show_total_grade_as_points],
+ publish_to_sis_enabled: @context.allows_grade_publishing_by(@current_user) && @gradebook_is_editable,
+ publish_to_sis_url: context_url(@context, :context_details_url, anchor: 'tab-grade-publishing'),
+ speed_grader_enabled: @context.allows_speed_grader?,
+ has_grading_periods: grading_periods?,
+ active_grading_periods: active_grading_periods_json,
+ grading_period_set: grading_period_group_json,
+ current_grading_period_id: @current_grading_period_id,
+ outcome_gradebook_enabled: @context.feature_enabled?(:outcome_gradebook),
+ custom_columns_url: api_v1_course_custom_gradebook_columns_url(@context),
+ custom_column_url: api_v1_course_custom_gradebook_column_url(@context, ":id"),
+ custom_column_data_url: api_v1_course_custom_gradebook_column_data_url(@context, ":id", per_page: per_page),
+ custom_column_datum_url: api_v1_course_custom_gradebook_column_datum_url(@context, ":id", ":user_id"),
+ reorder_custom_columns_url: api_v1_custom_gradebook_columns_reorder_url(@context),
+ teacher_notes: teacher_notes && custom_gradebook_column_json(teacher_notes, @current_user, session),
+ change_gradebook_version_url: context_url(@context, :change_gradebook_version_context_gradebook_url, version: 2),
+ export_gradebook_csv_url: course_gradebook_csv_url,
+ gradebook_csv_progress: @last_exported_gradebook_csv.try(:progress),
+ attachment_url: @last_exported_gradebook_csv.try(:attachment).try(:download_url),
+ attachment: @last_exported_gradebook_csv.try(:attachment),
+ sis_app_url: Setting.get('sis_app_url', nil),
+ sis_app_token: Setting.get('sis_app_token', nil),
+ list_students_by_sortable_name_enabled: @context.list_students_by_sortable_name?,
+ gradebook_column_size_settings: @current_user.preferences[:gradebook_column_size],
+ gradebook_column_size_settings_url: change_gradebook_column_size_course_gradebook_url,
+ gradebook_column_order_settings: @current_user.preferences[:gradebook_column_order].try(:[], @context.id),
+ gradebook_column_order_settings_url: save_gradebook_column_order_course_gradebook_url,
+ all_grading_periods_totals: @context.feature_enabled?(:all_grading_periods_totals),
+ sections: sections_json(@context.active_course_sections, @current_user, session),
+ settings_update_url: api_v1_course_gradebook_settings_update_url(@context),
+ settings: @current_user.preferences.fetch(:gradebook_settings, {}).fetch(@context.id, {}),
+ version: params.fetch(:version, nil)
}
end
@@ -752,7 +768,7 @@ class GradebooksController < ApplicationController
return true if context.hide_final_grades
all_grading_periods_selected =
- multiple_grading_periods? && view_all_grading_periods?
+ grading_periods? && view_all_grading_periods?
hide_all_grading_periods_totals = !context.feature_enabled?(:all_grading_periods_totals)
all_grading_periods_selected && hide_all_grading_periods_totals
end
@@ -787,7 +803,7 @@ class GradebooksController < ApplicationController
options = {}
return options unless @context.present?
- if @current_grading_period_id.present? && !view_all_grading_periods? && multiple_grading_periods?
+ if @current_grading_period_id.present? && !view_all_grading_periods? && grading_periods?
options[:grading_period_id] = @current_grading_period_id
end
diff --git a/app/controllers/grading_period_sets_controller.rb b/app/controllers/grading_period_sets_controller.rb
index a5d00c4867d..73296ea9dd3 100644
--- a/app/controllers/grading_period_sets_controller.rb
+++ b/app/controllers/grading_period_sets_controller.rb
@@ -1,9 +1,6 @@
class GradingPeriodSetsController < ApplicationController
- include ::Filters::GradingPeriods
-
before_action :require_user
before_action :get_context
- before_action :check_feature_flag
before_action :check_manage_rights, except: [:index]
before_action :check_read_rights, except: [:update, :create, :destroy]
@@ -42,7 +39,11 @@ class GradingPeriodSetsController < ApplicationController
end
def update
+ old_term_ids = grading_period_set.enrollment_terms.pluck(:id)
grading_period_set.enrollment_terms = enrollment_terms
+ # we need to recompute scores for enrollment terms that were removed since the line above
+ # will not run callbacks for the removed enrollment terms
+ EnrollmentTerm.where(id: old_term_ids - enrollment_terms.map(&:id)).each(&:recompute_course_scores)
respond_to do |format|
if grading_period_set.update(set_params)
@@ -74,7 +75,7 @@ class GradingPeriodSetsController < ApplicationController
end
def set_params
- params.require(:grading_period_set).permit(:title)
+ params.require(:grading_period_set).permit(:title, :weighted)
end
def check_read_rights
diff --git a/app/controllers/grading_periods_controller.rb b/app/controllers/grading_periods_controller.rb
index b57f3bcaacb..9834cb8ea69 100644
--- a/app/controllers/grading_periods_controller.rb
+++ b/app/controllers/grading_periods_controller.rb
@@ -61,11 +61,8 @@
# }
#
class GradingPeriodsController < ApplicationController
- include ::Filters::GradingPeriods
-
before_action :require_user
before_action :get_context
- before_action :check_feature_flag
# @API List grading periods
# @beta
@@ -240,6 +237,7 @@ class GradingPeriodsController < ApplicationController
set_subquery = GradingPeriodGroup.active.select(:account_id).where(id: params[:set_id])
@context = Account.active.where(id: set_subquery).take
+ render json: {message: t('Page not found')}, status: :not_found unless @context
end
# model level validations
@@ -325,11 +323,6 @@ class GradingPeriodsController < ApplicationController
def index_permissions
can_create_grading_periods = @context.is_a?(Account) &&
@context.root_account? && @context.grants_right?(@current_user, :manage)
- can_toggle_grading_periods = @domain_root_account.grants_right?(@current_user, :manage) ||
- @context.feature_allowed?(:multiple_grading_periods, exclude_enabled: true)
- {
- can_create_grading_periods: can_create_grading_periods,
- can_toggle_grading_periods: can_toggle_grading_periods
- }.as_json
+ {can_create_grading_periods: can_create_grading_periods}.as_json
end
end
diff --git a/app/controllers/grading_standards_controller.rb b/app/controllers/grading_standards_controller.rb
index 5b377eb628c..b611077f258 100644
--- a/app/controllers/grading_standards_controller.rb
+++ b/app/controllers/grading_standards_controller.rb
@@ -30,7 +30,7 @@ class GradingStandardsController < ApplicationController
GRADING_STANDARDS_URL: context_url(@context, :context_grading_standards_url),
GRADING_PERIOD_SETS_URL: api_v1_account_grading_period_sets_url(@context),
ENROLLMENT_TERMS_URL: api_v1_enrollment_terms_url(@context),
- MULTIPLE_GRADING_PERIODS: multiple_grading_periods?,
+ HAS_GRADING_PERIODS: grading_periods?,
DEFAULT_GRADING_STANDARD_DATA: GradingStandard.default_grading_standard,
CONTEXT_SETTINGS_URL: context_url(@context, :context_settings_url)
}
diff --git a/app/controllers/quizzes/quizzes_controller.rb b/app/controllers/quizzes/quizzes_controller.rb
index a89eb12f7cc..7385d9efe9e 100644
--- a/app/controllers/quizzes/quizzes_controller.rb
+++ b/app/controllers/quizzes/quizzes_controller.rb
@@ -320,12 +320,12 @@ class Quizzes::QuizzesController < ApplicationController
:quiz_max_combination_count => QUIZ_MAX_COMBINATION_COUNT,
:SHOW_QUIZ_ALT_TEXT_WARNING => true,
:VALID_DATE_RANGE => CourseDateRange.new(@context),
- :MULTIPLE_GRADING_PERIODS_ENABLED => @context.feature_enabled?(:multiple_grading_periods),
+ :HAS_GRADING_PERIODS => @context.grading_periods?,
:MAX_NAME_LENGTH_REQUIRED_FOR_ACCOUNT => max_name_length_required_for_account,
:MAX_NAME_LENGTH => max_name_length
}
- if @context.feature_enabled?(:multiple_grading_periods)
+ if @context.grading_periods?
hash[:active_grading_periods] = GradingPeriod.json_for(@context, @current_user)
end
diff --git a/app/controllers/submissions_api_controller.rb b/app/controllers/submissions_api_controller.rb
index 419d1f2e972..89c82ee417d 100644
--- a/app/controllers/submissions_api_controller.rb
+++ b/app/controllers/submissions_api_controller.rb
@@ -245,7 +245,7 @@ class SubmissionsApiController < ApplicationController
#
# @argument grading_period_id [Integer]
# The id of the grading period in which submissions are being requested
- # (Requires the Multiple Grading Periods account feature turned on)
+ # (Requires grading periods to exist on the account)
#
# @argument order [String, "id"|"graded_at"]
# The order submissions will be returned in. Defaults to "id". Doesn't
@@ -330,7 +330,7 @@ class SubmissionsApiController < ApplicationController
assignment_scope = assignment_scope.where(:id => requested_assignment_ids)
end
- if params[:grading_period_id].present? && multiple_grading_periods?
+ if params[:grading_period_id].present?
assignments = GradingPeriod.active.find(params[:grading_period_id]).assignments(assignment_scope)
else
assignments = assignment_scope.to_a
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index e70f3754ca0..53871b699bc 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -175,7 +175,12 @@ class UsersController < ApplicationController
add_crumb(@current_user.short_name, crumb_url)
add_crumb(t('crumbs.grades', 'Grades'), grades_path)
- current_active_enrollments = @user.enrollments.current.preload(:course, :enrollment_state).shard(@user).to_a
+ current_active_enrollments = @user.
+ enrollments.
+ current.
+ preload(:course, :enrollment_state, :scores).
+ shard(@user).
+ to_a
@presenter = GradesPresenter.new(current_active_enrollments)
@@ -197,19 +202,11 @@ class UsersController < ApplicationController
enrollment = Enrollment.active.find(params[:enrollment_id])
return render_unauthorized_action unless enrollment.grants_right?(@current_user, session, :read_grades)
- course = enrollment.course
- grading_period_id = params[:grading_period_id].to_i
- grading_period = GradingPeriod.for(course).find_by(id: grading_period_id)
- grading_periods = {
- course.id => {
- periods: [grading_period],
- selected_period_id: grading_period_id
- }
+ grading_period_id = generate_grading_period_id(params[:grading_period_id])
+ render json: {
+ grade: enrollment.computed_current_score(grading_period_id: grading_period_id),
+ hide_final_grades: enrollment.course.hide_final_grades?
}
- calculator = grade_calculator([enrollment.user_id], course, grading_periods)
- totals = calculator.compute_scores.first[:current]
- totals[:hide_final_grades] = course.hide_final_grades?
- render json: totals
end
def oauth
@@ -2119,6 +2116,12 @@ class UsersController < ApplicationController
private
+ def generate_grading_period_id(period_id)
+ # nil and '' will get converted to 0 in the .to_i call
+ id = period_id.to_i
+ id == 0 ? nil : id
+ end
+
def render_new_user_tutorial_statuses(user)
render(json: { new_user_tutorial_statuses: { collapsed: user.new_user_tutorial_statuses }})
end
@@ -2137,47 +2140,30 @@ class UsersController < ApplicationController
presenter.observed_enrollments.group_by { |enrollment| enrollment[:course_id] }
grouped_observed_enrollments.each do |course_id, enrollments|
+ grading_period_id = generate_grading_period_id(
+ grading_periods[course_id].try(:selected_period_id)
+ )
grades[:observed_enrollments][course_id] = {}
-
- if grading_periods[course_id].present?
- user_ids = enrollments.map(&:user_id)
- course = enrollments.first.course
- grades[:observed_enrollments][course_id] = grades_from_grade_calculator(user_ids, course, grading_periods)
- else
- grades[:observed_enrollments][course_id] = grades_from_enrollments(enrollments)
- end
+ grades[:observed_enrollments][course_id] = grades_from_enrollments(
+ enrollments,
+ grading_period_id: grading_period_id
+ )
end
- presenter.student_enrollments.each do |enrollment_course_pair|
- course = enrollment_course_pair.first
- enrollment = enrollment_course_pair.second
-
- if grading_periods[course.id].present?
- computed_score = grades_from_grade_calculator([enrollment.user_id], course, grading_periods)[enrollment.user_id]
- grades[:student_enrollments][course.id] = computed_score
- else
- computed_score = enrollment.computed_current_score
- grades[:student_enrollments][course.id] = computed_score
- end
+ presenter.student_enrollments.each do |course, enrollment|
+ grading_period_id = generate_grading_period_id(
+ grading_periods[course.id].try(:[], :selected_period_id)
+ )
+ computed_score = enrollment.computed_current_score(grading_period_id: grading_period_id)
+ grades[:student_enrollments][course.id] = computed_score
end
grades
end
- def grades_from_grade_calculator(user_ids, course, grading_periods)
- calculator = grade_calculator(user_ids, course, grading_periods)
- grades = {}
- calculator.compute_scores.each_with_index do |score, index|
- computed_score = score[:current][:grade]
- user_id = user_ids[index]
- grades[user_id] = computed_score
- end
- grades
- end
-
- def grades_from_enrollments(enrollments)
+ def grades_from_enrollments(enrollments, grading_period_id: nil)
grades = {}
enrollments.each do |enrollment|
- computed_score = enrollment.computed_current_score
+ computed_score = enrollment.computed_current_score(grading_period_id: grading_period_id)
grades[enrollment.user_id] = computed_score
end
grades
@@ -2191,7 +2177,7 @@ class UsersController < ApplicationController
grading_periods = {}
courses.each do |course|
- next unless course.feature_enabled?(:multiple_grading_periods)
+ next unless course.grading_periods?
course_periods = GradingPeriod.for(course)
grading_period_specified = grading_period_id &&
@@ -2212,19 +2198,6 @@ class UsersController < ApplicationController
grading_periods
end
- def grade_calculator(user_ids, course, grading_periods)
- if course.feature_enabled?(:multiple_grading_periods) &&
- grading_periods[course.id][:selected_period_id] != 0
-
- grading_period = grading_periods[course.id][:periods].find do |period|
- period.id == grading_periods[course.id][:selected_period_id]
- end
- GradeCalculator.new(user_ids, course, grading_period: grading_period)
- else
- GradeCalculator.new(user_ids, course)
- end
- end
-
def create_user
run_login_hooks
# Look for an incomplete registration with this pseudonym
diff --git a/app/jsx/due_dates/DueDates.jsx b/app/jsx/due_dates/DueDates.jsx
index c2735d43672..c47c5be88f3 100644
--- a/app/jsx/due_dates/DueDates.jsx
+++ b/app/jsx/due_dates/DueDates.jsx
@@ -26,7 +26,7 @@ define([
syncWithBackbone: React.PropTypes.func.isRequired,
sections: React.PropTypes.array.isRequired,
defaultSectionId: React.PropTypes.string.isRequired,
- multipleGradingPeriodsEnabled: React.PropTypes.bool.isRequired,
+ hasGradingPeriods: React.PropTypes.bool.isRequired,
gradingPeriods: React.PropTypes.array.isRequired,
isOnlyVisibleToOverrides: React.PropTypes.bool.isRequired,
dueAt: function(props) {
@@ -350,7 +350,7 @@ define([
let validGroups = this.valuesWithOmission({object: this.groupsForSelectedSet(), keysToOmit: this.chosenGroupIds()})
let validSections = this.valuesWithOmission({object: this.state.sections, keysToOmit: this.chosenSectionIds()})
let validNoops = this.valuesWithOmission({object: this.state.noops, keysToOmit: this.chosenNoops()})
- if (this.props.multipleGradingPeriodsEnabled && !_.contains(ENV.current_user_roles, "admin")) {
+ if (this.props.hasGradingPeriods && !_.contains(ENV.current_user_roles, "admin")) {
({validStudents, validGroups, validSections} =
this.filterDropdownOptionsForMultipleGradingPeriods(validStudents, validGroups, validSections))
}
@@ -431,7 +431,7 @@ define([
disableInputs(row) {
const rowIsNewOrUserIsAdmin = !row.persisted || _.contains(ENV.current_user_roles, "admin")
- if (!this.props.multipleGradingPeriodsEnabled || rowIsNewOrUserIsAdmin) {
+ if (!this.props.hasGradingPeriods || rowIsNewOrUserIsAdmin) {
return false
}
diff --git a/app/jsx/gradebook/AssignmentGroupGradeCalculator.jsx b/app/jsx/gradebook/AssignmentGroupGradeCalculator.jsx
index 77f5cf16fee..19efd3b5ee7 100644
--- a/app/jsx/gradebook/AssignmentGroupGradeCalculator.jsx
+++ b/app/jsx/gradebook/AssignmentGroupGradeCalculator.jsx
@@ -18,109 +18,105 @@
define([
'underscore'
-], function (_) {
- const sum = function (collection) {
- return _.reduce(collection, function (sum, value) {
- return sum + value;
- }, 0);
- };
+], (_) => {
+ function sum (collection) {
+ return _.reduce(collection, (total, value) => total + value, 0);
+ }
- const sumBy = function (collection, attr) {
+ function sumBy (collection, attr) {
const values = _.map(collection, attr);
return sum(values);
- };
+ }
- const partition = function (collection, partitionFn) {
+ function partition (collection, partitionFn) {
const grouped = _.groupBy(collection, partitionFn);
- return [grouped[true] || [], grouped[false] || []];
- };
+ return [grouped.true || [], grouped.false || []];
+ }
- const parseScore = function (score) {
+ function parseScore (score) {
const result = parseFloat(score);
return (result && isFinite(result)) ? result : 0;
- };
+ }
+ function sortPairsDescending ([scoreA, submissionA], [scoreB, submissionB]) {
+ const scoreDiff = scoreB - scoreA;
+ if (scoreDiff !== 0) {
+ return scoreDiff;
+ }
+ // To ensure stable sorting, use the assignment id as a secondary sort.
+ return submissionA.assignment_id - submissionB.assignment_id;
+ }
- // Some browser sorting functions (such as in V8) are not stable.
- // This function ensures that the same submission will be dropped regardless
- // of browser.
- const stableSubmissionSort = function (sortFn, getAssignmentIdFn) {
- return function (a, b) {
- const ret = sortFn(a, b);
- if (ret === 0) {
- return getAssignmentIdFn(a) - getAssignmentIdFn(b);
- } else {
- return ret;
- }
- };
- };
+ function sortPairsAscending ([scoreA, submissionA], [scoreB, submissionB]) {
+ const scoreDiff = scoreA - scoreB;
+ if (scoreDiff !== 0) {
+ return scoreDiff;
+ }
+ // To ensure stable sorting, use the assignment id as a secondary sort.
+ return submissionA.assignment_id - submissionB.assignment_id;
+ }
- const sortDescending = function ([a, xx], [b, yy]) {
- return b - a;
- };
- const sortAscending = function ([a, xx], [b, yy]) {
- return a - b;
- };
- const getAssignmentIdFn = function ([score, submission]) {
- return submission.submission.assignment_id;
- };
+ function sortSubmissionsAscending (submissionA, submissionB) {
+ const scoreDiff = submissionA.score - submissionB.score;
+ if (scoreDiff !== 0) {
+ return scoreDiff;
+ }
+ // To ensure stable sorting, use the assignment id as a secondary sort.
+ return submissionA.assignment_id - submissionB.assignment_id;
+ }
- const getSubmissionGrade = function ({ score, total }) {
+ function getSubmissionGrade ({ score, total }) {
return score / total;
- };
+ }
- const estimateQHigh = function (pointed, unpointed, grades) {
+ function estimateQHigh (pointed, unpointed, grades) {
if (unpointed.length > 0) {
const pointsPossible = sumBy(pointed, 'total');
const bestPointedScore = Math.max(pointsPossible, sumBy(pointed, 'score'));
const unpointedScore = sumBy(unpointed, 'score');
return (bestPointedScore + unpointedScore) / pointsPossible;
- } else {
- return grades[grades.length - 1];
}
- };
- const dropPointed = function (submissions, cannotDrop, keepHighest, keepLowest) {
- const totals = _.map(submissions, 'total');
- const maxTotal = Math.max.apply(Math, totals);
+ return grades[grades.length - 1];
+ }
- const keepHelper = function (submissions, keep, bigFSort) {
- keep = Math.max(1, keep);
+ function buildBigF (keepCount, cannotDrop, sortFn) {
+ return function bigF (q, submissions) {
+ const ratedScores = _.map(submissions, submission => (
+ [submission.score - (q * submission.total), submission]
+ ));
+ const rankedScores = ratedScores.sort(sortFn);
+ const keptScores = rankedScores.slice(0, keepCount);
+ const qKept = sumBy(keptScores, ([score]) => score);
+ const keptSubmissions = _.map(keptScores, ([_score, submission]) => submission);
+ const qCannotDrop = sumBy(cannotDrop, submission => submission.score - (q * submission.total));
+ return [qKept + qCannotDrop, keptSubmissions];
+ }
+ }
- if (submissions.length <= keep) {
+ function dropPointed (droppableSubmissionData, cannotDrop, keepHighest, keepLowest) {
+ const totals = _.map(droppableSubmissionData, 'total');
+ const maxTotal = Math.max(...totals);
+
+ function keepHelper (submissions, initialKeepCount, bigFSort) {
+ const keepCount = Math.max(1, initialKeepCount);
+
+ if (submissions.length <= keepCount) {
return submissions;
}
- const allSubmissions = [...submissions, ...cannotDrop];
- const [unpointed, pointed] = partition(allSubmissions, function (submission) {
- return submission.total == 0;
- });
+ const allSubmissionData = [...submissions, ...cannotDrop];
+ const [unpointed, pointed] = partition(allSubmissionData, submissionDatum => submissionDatum.total === 0);
const grades = _.map(pointed, getSubmissionGrade).sort();
let qHigh = estimateQHigh(pointed, unpointed, grades);
let qLow = grades[0];
let qMid = (qLow + qHigh) / 2;
- const bigF = function (q, submissions) {
- const ratedScores = _.map(submissions, function (submission) {
- return [submission.score - (q * submission.total), submission];
- });
- const rankedScores = ratedScores.sort(bigFSort);
- const keptScores = rankedScores.slice(0, keep);
- const qKept = sumBy(keptScores, function ([score]) {
- return score;
- });
- const keptSubmissions = _.map(keptScores, function ([score, submission]) {
- return submission;
- });
- const qCantDrop = sumBy(cannotDrop, function (submission) {
- return submission.score - q * submission.total;
- });
- return [qKept + qCantDrop, keptSubmissions];
- };
+ const bigF = buildBigF(keepCount, cannotDrop, bigFSort);
- let [x, kept] = bigF(qMid, submissions);
- const threshold = 1 / (2 * keep * Math.pow(maxTotal, 2));
+ let [x, submissionsToKeep] = bigF(qMid, submissions);
+ const threshold = 1 / (2 * keepCount * (maxTotal ** 2));
while (qHigh - qLow >= threshold) {
if (x < 0) {
qHigh = qMid;
@@ -132,22 +128,24 @@ define([
break;
}
- [x, kept] = bigF(qMid, submissions);
+ [x, submissionsToKeep] = bigF(qMid, submissions);
}
- return kept;
- };
+ return submissionsToKeep;
+ }
- const kept = keepHelper(submissions, keepHighest, stableSubmissionSort(sortDescending, getAssignmentIdFn));
- return keepHelper(kept, keepLowest, stableSubmissionSort(sortAscending, getAssignmentIdFn));
- };
+ const submissionsWithLowestDropped = keepHelper(
+ droppableSubmissionData, keepHighest, sortPairsDescending
+ );
+ return keepHelper(
+ submissionsWithLowestDropped, keepLowest, sortPairsAscending
+ );
+ }
- const dropUnpointed = function (submissions, keepHighest, keepLowest) {
- const sortAscending = function (a, b) { return a.score - b.score };
- const getAssignmentIdFn = function ({ submission }) { return submission.assignment_id };
- const sortedSubmissions = submissions.sort(stableSubmissionSort(sortAscending, getAssignmentIdFn));
+ function dropUnpointed (submissions, keepHighest, keepLowest) {
+ const sortedSubmissions = submissions.sort(sortSubmissionsAscending);
return _.chain(sortedSubmissions).last(keepHighest).first(keepLowest).value();
- };
+ }
// I am not going to pretend that this code is understandable.
//
@@ -159,130 +157,121 @@ define([
// Grades" by Daniel Kane and Jonathan Kane. Please see that paper for
// a full explanation of the math.
// (http://cseweb.ucsd.edu/~dakane/droplowest.pdf)
- const dropAssignments = function (submissions, rules) {
- rules = rules || {};
+ function dropAssignments (allSubmissionData, rules = {}) {
let dropLowest = rules.drop_lowest || 0;
let dropHighest = rules.drop_highest || 0;
const neverDropIds = rules.never_drop || [];
if (!(dropLowest || dropHighest)) {
- return submissions;
+ return allSubmissionData;
}
- let cannot_drop = [];
+ let cannotDrop = [];
+ let droppableSubmissionData = allSubmissionData;
if (neverDropIds.length > 0) {
- [cannot_drop, submissions] = partition(submissions, function (submission) {
- return _.contains(neverDropIds, submission.submission.assignment_id);
- });
+ [cannotDrop, droppableSubmissionData] = partition(allSubmissionData, submission => (
+ _.contains(neverDropIds, submission.submission.assignment_id)
+ ));
}
- if (submissions.length === 0) {
- return cannot_drop;
+ if (droppableSubmissionData.length === 0) {
+ return cannotDrop;
}
- dropLowest = Math.min(dropLowest, submissions.length - 1);
- dropHighest = (dropLowest + dropHighest) >= submissions.length ? 0 : dropHighest;
+ dropLowest = Math.min(dropLowest, droppableSubmissionData.length - 1);
+ dropHighest = (dropLowest + dropHighest) >= droppableSubmissionData.length ? 0 : dropHighest;
- const keepHighest = submissions.length - dropLowest;
+ const keepHighest = droppableSubmissionData.length - dropLowest;
const keepLowest = keepHighest - dropHighest;
- const hasPointed = _.some(submissions, function (submission) { return submission.total > 0 });
+ const hasPointed = _.some(droppableSubmissionData, submission => submission.total > 0);
- let kept;
+ let submissionsToKeep;
if (hasPointed) {
- kept = dropPointed(submissions, cannot_drop, keepHighest, keepLowest);
+ submissionsToKeep = dropPointed(droppableSubmissionData, cannotDrop, keepHighest, keepLowest);
} else {
- kept = dropUnpointed(submissions, keepHighest, keepLowest);
+ submissionsToKeep = dropUnpointed(droppableSubmissionData, keepHighest, keepLowest);
}
- kept = [ ...kept, ...cannot_drop];
+ submissionsToKeep = [...submissionsToKeep, ...cannotDrop];
- _.difference(submissions, kept).forEach(function (submission) {
- submission.drop = true;
- });
+ _.difference(droppableSubmissionData, submissionsToKeep).forEach((submission) => { submission.drop = true });
- return kept;
- };
+ return submissionsToKeep;
+ }
- const calculateGroupSum = function (group, submissions, includeUngraded) {
- // remove assignments without visibility from gradeableAssignments
- const hiddenAssignments = _.chain(submissions).filter('hidden').indexBy('assignment_id').value();
- const gradeableAssignments = _.reject(group.assignments, function (assignment) {
- return assignment.omit_from_final_grade ||
- hiddenAssignments[assignment.id] ||
- _.isEqual(assignment.submission_types, ['not_graded']);
- });
+ function calculateGroupGrade (group, allSubmissions, includeUngraded) {
+ // Remove assignments without visibility from gradeableAssignments.
+ const hiddenAssignmentsById = _.chain(allSubmissions).filter('hidden').indexBy('assignment_id').value();
+ const gradeableAssignments = _.reject(group.assignments, assignment => (
+ assignment.omit_from_final_grade ||
+ hiddenAssignmentsById[assignment.id] ||
+ _.isEqual(assignment.submission_types, ['not_graded'])
+ ));
const assignments = _.indexBy(gradeableAssignments, 'id');
- // filter out submissions from other assignment groups
- submissions = _.filter(submissions, function (submission) {
- return assignments[submission.assignment_id];
- });
+ // Remove submissions from other assignment groups.
+ let submissions = _.filter(allSubmissions, submission => assignments[submission.assignment_id]);
- // fill in any missing submissions
+ // To calculate grades for assignments to which the student has not yet
+ // submitted, create a submission stub with a score of `null`.
if (includeUngraded) {
- const submissionAssignmentIds = _.map(submissions, function ({ assignment_id }) {
- return assignment_id.toString();
- });
- const missingSubmissions = _.difference(_.keys(assignments), submissionAssignmentIds);
- const submissionStubs = _.map(missingSubmissions, (assignment_id) => {
- return { assignment_id, score: null };
- });
- submissions = [ ...submissions, ...submissionStubs ];
+ const submissionAssignmentIds = _.map(submissions, ({ assignment_id }) => assignment_id.toString());
+ const missingAssignmentIds = _.difference(_.keys(assignments), submissionAssignmentIds);
+ const submissionStubs = _.map(missingAssignmentIds, assignmentId => (
+ { assignment_id: assignmentId, score: null }
+ ));
+ submissions = [...submissions, ...submissionStubs];
}
- // filter out excused assignments
+ // Remove excused submissions.
submissions = _.reject(submissions, 'excused');
- const submissionsByAssignment = _.indexBy(submissions, 'assignment_id');
-
- const submissionData = _.map(submissions, function (submission) {
- return {
+ const submissionData = _.map(submissions, submission => (
+ {
total: parseScore(assignments[submission.assignment_id].points_possible),
score: parseScore(submission.score),
submitted: submission.score != null && submission.score !== '',
pending_review: submission.workflow_state === 'pending_review',
submission
- };
- });
+ }
+ ));
let relevantSubmissionData = submissionData;
if (!includeUngraded) {
- relevantSubmissionData = _.filter(submissionData, function (submission) {
- return submission.submitted && !submission.pending_review;
- });
+ relevantSubmissionData = _.filter(submissionData, submission => (
+ submission.submitted && !submission.pending_review
+ ));
}
- const kept = dropAssignments(relevantSubmissionData, group.rules);
- const score = sum(_.chain(kept).map('score').map(parseScore).value());
- const possible = sumBy(kept, 'total');
+ const submissionsToKeep = dropAssignments(relevantSubmissionData, group.rules);
+ const score = sum(_.chain(submissionsToKeep).map('score').map(parseScore).value());
+ const possible = sumBy(submissionsToKeep, 'total');
return {
- possible,
score,
- weight: group.group_weight,
+ possible,
submission_count: _.filter(submissionData, 'submitted').length,
- submissions: _.map(submissionData, function (submission) {
- return {
- drop: submission.drop,
- percent: parseScore(submission.score / submission.total),
- possible: submission.total,
- score: parseScore(submission.score),
- submission: submission.submission,
- submitted: submission.submitted
- };
- })
+ submissions: _.map(submissionData, submissionDatum => (
+ {
+ drop: submissionDatum.drop,
+ percent: parseScore(submissionDatum.score / submissionDatum.total),
+ score: parseScore(submissionDatum.score),
+ possible: submissionDatum.total,
+ submission: submissionDatum.submission,
+ submitted: submissionDatum.submitted
+ }
+ ))
};
- };
+ }
// Each submission requires the following properties:
// * score: number
// * points_possible: non-negative integer
- // * assignment_id: Canvas id
- // * assignment_group_id: Canvas id
+ // * assignment_id: