back-end grade calculator supports grading period weighting
closes CNVS-34105 test plan: - Setup - 1. At the account-level, create a new grading period set that uses weighted grading periods. Attach the default enrollment term to this set. 2. Create a grading period (GP1) that runs from January 1, 2017 to June 30, 2017. Give it a weight of 75%. 3. Create a grading period (GP2) that runs from July 1, 2017 to December 31, 2017. Give it a weight of 25%. 4. Create a course that belongs to the default enrollment term from step 1. Enroll one student in the course. Make sure that student is enrolled in at least one other course. 5. In the course settings, enable Multiple Grading Periods and enable Display ‘All Grading Period’ Totals. 6. In the course, create an assignment (Assignment in GP1) that is worth 10 points and due for everyone on March 15, 2017. 7. In the course, create an assignment (Assignment in GP2) that is worth 10 points and due for everyone on September 15, 2017. 8. Go to the gradebook and give the student 5/10 on Assignment in GP1 and 10/10 on Assignment in GP2. Ignore the totals that show up in the gradebook. - Grade Verification - 1. Sign in as the student and go to the /grades page. 2. Verify the grade for GP1 is 50%. 3. Verify the grade for GP2 is 100%. 4. Verify the grade for ‘All Grading Periods’ is 62.5%. 5. Sign in as the account admin. Change the weight for GP1 to 20% and change the weight for GP2 to 40%. 6. Sign in as the student and go to the /grades page. 7. Verify the grade for GP1 is 50%. 8. Verify the grade for GP2 is 100%. 9. Verify the grade for ‘All Grading Periods’ is 83.33%. 10. Sign in as the account admin. Change the weight for GP1 to 100% and change the weight for GP2 to 100%. 11. Sign in as the student and go to the /grades page. 12. Verify the grade for GP1 is 50%. 13. Verify the grade for GP2 is 100%. 14. Verify the grade for ‘All Grading Periods’ is 150%. 15. Sign in as the account admin. Change the weight for GP1 to 100% and change the weight for GP2 to 0%. 16. Sign in as the student and go to the /grades page. 17. Verify the grade for GP1 is 50%. 18. Verify the grade for GP2 is 100%. 19. Verify the grade for ‘All Grading Periods’ is 50%. 20. Sign in as the account admin. Change the weight for GP1 to 0% and change the weight for GP2 to 0%. 21. Sign in as the student and go to the /grades page. 22. Verify the grade for GP1 is 50%. 23. Verify the grade for GP2 is 100%. 24. Verify the grade for ‘All Grading Periods’ is 0%. 25. Sign in as the account admin. Uncheck the box on the grading period set for ‘weighting’. 26. Sign in as the student and go to the /grades page. 27. Verify the grade for GP1 is 50%. 28. Verify the grade for GP2 is 100%. 29. Verify the grade for ‘All Grading Periods’ is 75%. Change-Id: Iac23e92986f371b13de2d5e0524d6bed8a277bc4 Reviewed-on: https://gerrit.instructure.com/99040 Reviewed-by: Derek Bender <djbender@instructure.com> Tested-by: Jenkins Reviewed-by: Jeremy Neander <jneander@instructure.com> QA-Review: KC Naegle <knaegle@instructure.com> Product-Review: Keith T. Garner <kgarner@instructure.com>
This commit is contained in:
parent
fe7b0b2b0a
commit
74d948ff3b
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (C) 2011 - 2014 Instructure, Inc.
|
||||
# Copyright (C) 2011 - 2017 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
|
@ -65,33 +65,18 @@ class GradeCalculator
|
|||
|
||||
def compute_scores
|
||||
@submissions = @course.submissions.
|
||||
except(:order, :select).
|
||||
for_user(@user_ids).
|
||||
where(assignment_id: @assignments).
|
||||
select("submissions.id, user_id, assignment_id, score, excused, submissions.workflow_state")
|
||||
submissions_by_user = @submissions.group_by(&:user_id)
|
||||
|
||||
result = []
|
||||
except(:order, :select).
|
||||
for_user(@user_ids).
|
||||
where(assignment_id: @assignments).
|
||||
select("submissions.id, user_id, assignment_id, score, excused, submissions.workflow_state")
|
||||
scores_and_group_sums = []
|
||||
@user_ids.each_slice(100) do |batched_ids|
|
||||
load_assignment_visibilities_for_users(batched_ids)
|
||||
batched_ids.each do |user_id|
|
||||
user_submissions = submissions_by_user[user_id] || []
|
||||
user_submissions.select!{|s| assignment_ids_visible_to_user(user_id).include?(s.assignment_id)}
|
||||
current, current_groups = calculate_current_score(user_id, user_submissions)
|
||||
final, final_groups = calculate_final_score(user_id, user_submissions)
|
||||
|
||||
scores = {
|
||||
current: current,
|
||||
current_groups: current_groups,
|
||||
final: final,
|
||||
final_groups: final_groups
|
||||
}
|
||||
|
||||
result << scores
|
||||
end
|
||||
scores_and_group_sums_batch = compute_scores_and_group_sums_for_batch(batched_ids)
|
||||
scores_and_group_sums.concat(scores_and_group_sums_batch)
|
||||
clear_assignment_visibilities_cache
|
||||
end
|
||||
result
|
||||
scores_and_group_sums
|
||||
end
|
||||
|
||||
def compute_and_save_scores
|
||||
|
@ -103,8 +88,114 @@ class GradeCalculator
|
|||
|
||||
private
|
||||
|
||||
def compute_scores_and_group_sums_for_batch(user_ids)
|
||||
user_ids.map do |user_id|
|
||||
group_sums = compute_group_sums_for_user(user_id)
|
||||
scores = compute_scores_for_user(user_id, group_sums)
|
||||
update_changes_hash_for_user(user_id, scores)
|
||||
{
|
||||
current: scores[:current],
|
||||
current_groups: group_sums[:current].index_by { |group| group[:id] },
|
||||
final: scores[:final],
|
||||
final_groups: group_sums[:final].index_by { |group| group[:id] }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def compute_group_sums_for_user(user_id)
|
||||
user_submissions = submissions_by_user.fetch(user_id, []).select do |submission|
|
||||
assignment_ids_visible_to_user(user_id).include?(submission.assignment_id)
|
||||
end
|
||||
{
|
||||
current: create_group_sums(user_submissions, user_id, ignore_ungraded: true),
|
||||
final: create_group_sums(user_submissions, user_id, ignore_ungraded: false)
|
||||
}
|
||||
end
|
||||
|
||||
def compute_scores_for_user(user_id, group_sums)
|
||||
if compute_course_scores_from_weighted_grading_periods?
|
||||
scores = calculate_total_from_weighted_grading_periods(user_id)
|
||||
else
|
||||
scores = {
|
||||
current: calculate_total_from_group_scores(group_sums[:current]),
|
||||
final: calculate_total_from_group_scores(group_sums[:final])
|
||||
}
|
||||
end
|
||||
Rails.logger.info "GRADES: calculated: #{scores.inspect}"
|
||||
scores
|
||||
end
|
||||
|
||||
def update_changes_hash_for_user(user_id, scores)
|
||||
@current_updates[user_id] = scores[:current][:grade]
|
||||
@final_updates[user_id] = scores[:final][:grade]
|
||||
end
|
||||
|
||||
def calculate_total_from_weighted_grading_periods(user_id)
|
||||
enrollment = enrollments_by_user[user_id].first
|
||||
grading_period_ids = grading_periods_for_course.map(&:id)
|
||||
# using Enumberable#select because the scores are preloaded
|
||||
grading_period_scores = enrollment.scores.select do |score|
|
||||
grading_period_ids.include?(score.grading_period_id)
|
||||
end
|
||||
scores = apply_grading_period_weights_to_scores(grading_period_scores)
|
||||
scale_and_round_scores(scores, grading_period_scores)
|
||||
end
|
||||
|
||||
def apply_grading_period_weights_to_scores(grading_period_scores)
|
||||
grading_period_scores.each_with_object(
|
||||
{ current: { full_weight: 0.0, grade: 0.0 }, final: { full_weight: 0.0, grade: 0.0 } }
|
||||
) do |score, scores|
|
||||
weight = grading_period_weights[score.grading_period_id] || 0.0
|
||||
scores[:final][:full_weight] += weight
|
||||
scores[:current][:full_weight] += weight if score.current_score
|
||||
scores[:current][:grade] += (score.current_score || 0.0) * (weight / 100.0)
|
||||
scores[:final][:grade] += (score.final_score || 0.0) * (weight / 100.0)
|
||||
end
|
||||
end
|
||||
|
||||
def scale_and_round_scores(scores, grading_period_scores)
|
||||
[:current, :final].each_with_object({ current: {}, final: {} }) do |score_type, adjusted_scores|
|
||||
score = scores[score_type][:grade]
|
||||
full_weight = scores[score_type][:full_weight]
|
||||
score = scale_score_up(score, full_weight) if full_weight < 100
|
||||
if score == 0.0 && score_type == :current && grading_period_scores.none?(&:current_score)
|
||||
score = nil
|
||||
end
|
||||
adjusted_scores[score_type][:grade] = score ? score.round(2) : score
|
||||
end
|
||||
end
|
||||
|
||||
def scale_score_up(score, weight)
|
||||
return 0.0 if weight.zero?
|
||||
(score * 100.0) / weight
|
||||
end
|
||||
|
||||
def compute_course_scores_from_weighted_grading_periods?
|
||||
return @compute_from_weighted_periods if @compute_from_weighted_periods.present?
|
||||
|
||||
if @grading_period || !@course.feature_enabled?(:multiple_grading_periods) || grading_periods_for_course.empty?
|
||||
@compute_from_weighted_periods = false
|
||||
else
|
||||
@compute_from_weighted_periods = grading_periods_for_course.first.grading_period_group.weighted?
|
||||
end
|
||||
end
|
||||
|
||||
def grading_periods_for_course
|
||||
@periods ||= GradingPeriod.for(@course)
|
||||
end
|
||||
|
||||
def grading_period_weights
|
||||
@grading_period_weights ||= grading_periods_for_course.each_with_object({}) do |period, weights|
|
||||
weights[period.id] = period.weight
|
||||
end
|
||||
end
|
||||
|
||||
def submissions_by_user
|
||||
@submissions_by_user ||= @submissions.group_by(&:user_id)
|
||||
end
|
||||
|
||||
def calculate_grading_period_scores
|
||||
GradingPeriod.for(@course).each do |grading_period|
|
||||
grading_periods_for_course.each do |grading_period|
|
||||
# update this grading period score, and do not
|
||||
# update any other scores (grading period or course)
|
||||
# after this one
|
||||
|
@ -132,7 +223,7 @@ class GradeCalculator
|
|||
def enrollments
|
||||
@enrollments ||= Enrollment.shard(@course).active.
|
||||
where(user_id: @user_ids, course_id: @course.id).
|
||||
select(:id, :user_id)
|
||||
select(:id, :user_id).preload(:scores)
|
||||
end
|
||||
|
||||
def joined_enrollment_ids
|
||||
|
@ -225,27 +316,6 @@ class GradeCalculator
|
|||
end
|
||||
end
|
||||
|
||||
# The score ignoring unsubmitted assignments
|
||||
def calculate_current_score(user_id, submissions)
|
||||
calculate_score(submissions, user_id, true)
|
||||
end
|
||||
|
||||
# The final score for the class, so unsubmitted assignments count as zeros
|
||||
def calculate_final_score(user_id, submissions)
|
||||
calculate_score(submissions, user_id, false)
|
||||
end
|
||||
|
||||
def calculate_score(submissions, user_id, ignore_ungraded)
|
||||
group_sums = create_group_sums(submissions, user_id, ignore_ungraded)
|
||||
info = calculate_total_from_group_scores(group_sums)
|
||||
Rails.logger.info "GRADES: calculated: #{info.inspect}"
|
||||
|
||||
updates_hash = ignore_ungraded ? @current_updates : @final_updates
|
||||
updates_hash[user_id] = info[:grade]
|
||||
|
||||
[info, group_sums.index_by { |s| s[:id] }]
|
||||
end
|
||||
|
||||
# returns information about assignments groups in the form:
|
||||
# [
|
||||
# {
|
||||
|
@ -256,7 +326,7 @@ class GradeCalculator
|
|||
# :weight => 50},
|
||||
# ...]
|
||||
# each group
|
||||
def create_group_sums(submissions, user_id, ignore_ungraded=true)
|
||||
def create_group_sums(submissions, user_id, ignore_ungraded: true)
|
||||
visible_assignments = @assignments
|
||||
visible_assignments = visible_assignments.select{|a| assignment_ids_visible_to_user(user_id).include?(a.id)}
|
||||
|
||||
|
|
|
@ -460,6 +460,7 @@ describe AssignmentsController do
|
|||
|
||||
it "to wiki page" do
|
||||
Course.any_instance.stubs(:feature_enabled?).with(:conditional_release).returns(true)
|
||||
Course.any_instance.stubs(:feature_enabled?).with(:multiple_grading_periods).returns(false)
|
||||
wiki_page_assignment_model course: @course
|
||||
get 'edit', :course_id => @course.id, :id => @page.assignment.id
|
||||
expect(response).to redirect_to controller.edit_course_wiki_page_path(@course, @page)
|
||||
|
|
|
@ -479,39 +479,42 @@ describe GradeCalculator do
|
|||
describe '#compute_and_save_scores' do
|
||||
before(:once) do
|
||||
@first_period, @second_period = grading_periods(count: 2)
|
||||
first_assignment = @course.assignments.create!(
|
||||
@first_assignment = @course.assignments.create!(
|
||||
due_at: 1.day.from_now(@first_period.start_date),
|
||||
points_possible: 100
|
||||
)
|
||||
second_assignment = @course.assignments.create!(
|
||||
@second_assignment = @course.assignments.create!(
|
||||
due_at: 1.day.from_now(@second_period.start_date),
|
||||
points_possible: 100
|
||||
)
|
||||
first_assignment.grade_student(@student, grade: 25, grader: @teacher)
|
||||
second_assignment.grade_student(@student, grade: 75, grader: @teacher)
|
||||
# update_column to avoid callbacks on submission that would trigger score updates
|
||||
Submission.where(user: @student, assignment: first_assignment).first.update_column(:score, 100.0)
|
||||
Submission.where(user: @student, assignment: second_assignment).first.update_column(:score, 95.0)
|
||||
|
||||
@first_assignment.grade_student(@student, grade: 25, grader: @teacher)
|
||||
@second_assignment.grade_student(@student, grade: 75, grader: @teacher)
|
||||
# update_column to avoid callbacks on submission that would trigger the grade calculator
|
||||
submission_for_first_assignment.update_column(:score, 99.6)
|
||||
submission_for_second_assignment.update_column(:score, 95.0)
|
||||
end
|
||||
|
||||
let(:scores) { @student.enrollments.first.scores }
|
||||
let(:overall_course_score) { scores.where(grading_period_id: nil).first }
|
||||
let(:scores) { @student.enrollments.first.scores.index_by(&:grading_period_id) }
|
||||
let(:overall_course_score) { scores[nil] }
|
||||
let(:submission_for_first_assignment) { Submission.find_by(user: @student, assignment: @first_assignment) }
|
||||
let(:submission_for_second_assignment) { Submission.find_by(user: @student, assignment: @second_assignment) }
|
||||
|
||||
it 'updates the overall course score' do
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(97.5)
|
||||
expect(overall_course_score.current_score).to eq(97.3)
|
||||
end
|
||||
|
||||
it 'updates all grading period scores' do
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
grading_period_scores = scores.where.not(grading_period_id: nil).order(:current_score).pluck(:current_score)
|
||||
expect(grading_period_scores).to match_array([100.0, 95.0])
|
||||
expect(scores[@first_period.id].current_score).to eq(99.6)
|
||||
expect(scores[@second_period.id].current_score).to eq(95.0)
|
||||
end
|
||||
|
||||
it 'does not update grading period scores if update_all_grading_period_scores is false' do
|
||||
GradeCalculator.new(@student.id, @course, update_all_grading_period_scores: false).compute_and_save_scores
|
||||
grading_period_scores = scores.where.not(grading_period_id: nil).order(:current_score).pluck(:current_score)
|
||||
expect(grading_period_scores).to match_array([25.0, 75.0])
|
||||
expect(scores[@first_period.id].current_score).to eq(25.0)
|
||||
expect(scores[@second_period.id].current_score).to eq(75.0)
|
||||
end
|
||||
|
||||
it 'restores and updates previously deleted scores' do
|
||||
|
@ -523,19 +526,17 @@ describe GradeCalculator do
|
|||
context 'grading period is provided' do
|
||||
it 'updates the grading period score' do
|
||||
GradeCalculator.new(@student.id, @course, grading_period: @first_period).compute_and_save_scores
|
||||
score = scores.where(grading_period_id: @first_period).first
|
||||
expect(score.current_score).to eq(100.0)
|
||||
expect(scores[@first_period.id].current_score).to eq(99.6)
|
||||
end
|
||||
|
||||
it 'updates the overall course score' do
|
||||
GradeCalculator.new(@student.id, @course, grading_period: @first_period).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(97.5)
|
||||
expect(overall_course_score.current_score).to eq(97.3)
|
||||
end
|
||||
|
||||
it 'does not update scores for other grading periods' do
|
||||
GradeCalculator.new(@student.id, @course, grading_period: @first_period).compute_and_save_scores
|
||||
score = scores.where(grading_period_id: @second_period).first
|
||||
expect(score.current_score).to eq(75.0)
|
||||
expect(scores[@second_period.id].current_score).to eq(75.0)
|
||||
end
|
||||
|
||||
it 'does not update the overall course score if update_course_score is false' do
|
||||
|
@ -544,14 +545,252 @@ describe GradeCalculator do
|
|||
).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(50.0)
|
||||
end
|
||||
|
||||
|
||||
it 'does not restore previously deleted score if grading period is deleted too' do
|
||||
score = scores.where(grading_period_id: @first_period).first
|
||||
score = scores[@first_period.id]
|
||||
@first_period.destroy
|
||||
GradeCalculator.new(@student.id, @course, grading_period: @first_period).compute_and_save_scores
|
||||
expect(score.reload).to be_deleted
|
||||
end
|
||||
end
|
||||
|
||||
context 'weighted grading periods' do
|
||||
before(:once) do
|
||||
group = @first_period.grading_period_group
|
||||
group.update!(weighted: true)
|
||||
@ungraded_assignment = @course.assignments.create!(
|
||||
due_at: 1.day.from_now(@second_period.start_date),
|
||||
points_possible: 100
|
||||
)
|
||||
end
|
||||
|
||||
it 'calculates the course score from weighted grading period scores' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 75.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
# (99.6 * 0.25) + (95.0 * 0.75) = 96.15
|
||||
expect(overall_course_score.current_score).to eq(96.15)
|
||||
# (99.6 * 0.25) + (47.5 * 0.75) = 60.525 rounds to 60.53
|
||||
expect(overall_course_score.final_score).to eq(60.53)
|
||||
end
|
||||
|
||||
it 'up-scales grading period weights which add up to less than 100 percent' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 50.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
# (99.6 * 0.25) + (95.0 * 0.50) = 72.4
|
||||
# 72.4 / (0.25 + 0.50) = 96.5333 rounded to 96.53
|
||||
expect(overall_course_score.current_score).to eq(96.53)
|
||||
# (99.6 * 0.25) + (47.5 * 0.50) = 48.65
|
||||
# 48.65 / (0.25 + 0.50) = 64.8666 rounded to 64.87
|
||||
expect(overall_course_score.final_score).to eq(64.87)
|
||||
end
|
||||
|
||||
it 'does not down-scale grading period weights which add up to greater than 100 percent' do
|
||||
@first_period.update!(weight: 100.0)
|
||||
@second_period.update!(weight: 50.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
# (99.6 * 1.0) + (95.0 * 0.5) = 147.1
|
||||
expect(overall_course_score.current_score).to eq(147.1)
|
||||
# (99.6 * 1.0) + (47.5 * 0.5) = 123.35
|
||||
expect(overall_course_score.final_score).to eq(123.35)
|
||||
end
|
||||
|
||||
it 'sets current course score to zero when all grading period weights are zero' do
|
||||
@first_period.update!(weight: 0)
|
||||
@second_period.update!(weight: 0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets final course score to zero when all grading period weights are zero' do
|
||||
@first_period.update!(weight: 0)
|
||||
@second_period.update!(weight: 0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets current course score to zero when all grading period weights are nil' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets current course score to zero when all grading period weights are nil or zero' do
|
||||
@first_period.update!(weight: 0.0)
|
||||
@second_period.update!(weight: nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets final course score to zero when all grading period weights are nil' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets final course score to zero when all grading period weights are nil or zero' do
|
||||
@first_period.update!(weight: 0.0)
|
||||
@second_period.update!(weight: nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'treats grading periods with nil weights as zero when some grading period ' \
|
||||
'weights are nil and computing current score' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: 50.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(95.0)
|
||||
end
|
||||
|
||||
it 'treats grading periods with nil weights as zero when some grading period ' \
|
||||
'weights are nil and computing final score' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: 50.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(47.50)
|
||||
end
|
||||
|
||||
it 'sets current course score to nil when all grading period current scores are nil' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 75.0)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to be_nil
|
||||
end
|
||||
|
||||
it 'sets final course score to zero when all grading period final scores are nil' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 75.0)
|
||||
# update_all to avoid callbacks on assignment that would trigger the grade calculator
|
||||
@course.assignments.update_all(omit_from_final_grade: true)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'does not consider grading periods with nil current score when computing course current score' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 75.0)
|
||||
# update_column to avoid callbacks on submission that would trigger the grade calculator
|
||||
submission_for_first_assignment.update_column(:score, nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
# (0.0 * 0.0) + (95.0 * 0.75) = 71.25
|
||||
# 71.25 / (0.0 + 0.75) = 95.0
|
||||
expect(overall_course_score.current_score).to eq(95.0)
|
||||
end
|
||||
|
||||
it 'considers grading periods with nil final score as having zero score when computing course final score' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 75.0)
|
||||
# update_column to avoid callbacks on assignment that would trigger the grade calculator
|
||||
@first_assignment.update_column(:omit_from_final_grade, true)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
# (0.0 * 0.25) + (47.5 * 0.75) = 35.625 rounded to 35.63
|
||||
expect(overall_course_score.final_score).to eq(35.63)
|
||||
end
|
||||
|
||||
it 'sets course current score to zero when all grading period current scores are zero' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 75.0)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: 0.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets course final score to zero when all grading period final scores are zero' do
|
||||
@first_period.update!(weight: 25.0)
|
||||
@second_period.update!(weight: 75.0)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: 0.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets course current score to nil when all grading period current scores are nil ' \
|
||||
'and all grading period weights are nil' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: nil)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to be_nil
|
||||
end
|
||||
|
||||
it 'sets course final score to zero when all grading period final scores are nil and all ' \
|
||||
'grading period weights are nil' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: nil)
|
||||
# update_all to avoid callbacks on assignment that would trigger the grade calculator
|
||||
@course.assignments.update_all(omit_from_final_grade: true)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets course current score to zero when all grading period current scores are zero ' \
|
||||
'and all grading period weights are zero' do
|
||||
@first_period.update!(weight: 0.0)
|
||||
@second_period.update!(weight: 0.0)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: 0.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets course final score to zero when all grading period final scores are zero and ' \
|
||||
'all grading period weights are zero' do
|
||||
@first_period.update!(weight: 0.0)
|
||||
@second_period.update!(weight: 0.0)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: 0.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets course current score to nil when all grading period current scores are nil and ' \
|
||||
'all grading period weights are zero' do
|
||||
@first_period.update!(weight: 0.0)
|
||||
@second_period.update!(weight: 0.0)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: nil)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to be_nil
|
||||
end
|
||||
|
||||
it 'sets course final score to zero when all grading period final scores are nil and all ' \
|
||||
'grading period weights are zero' do
|
||||
@first_period.update!(weight: 0.0)
|
||||
@second_period.update!(weight: 0.0)
|
||||
# update_all to avoid callbacks on assignment that would trigger the grade calculator
|
||||
@course.assignments.update_all(omit_from_final_grade: true)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets course current score to zero when all grading period current scores are zero and ' \
|
||||
'all grading period weights are nil' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: nil)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: 0.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.current_score).to eq(0.0)
|
||||
end
|
||||
|
||||
it 'sets course final score to zero when all grading period final scores are zero and all ' \
|
||||
'grading period weights are nil' do
|
||||
@first_period.update!(weight: nil)
|
||||
@second_period.update!(weight: nil)
|
||||
# update_all to avoid callbacks on submission that would trigger the grade calculator
|
||||
@student.submissions.update_all(score: 0.0)
|
||||
GradeCalculator.new(@student.id, @course).compute_and_save_scores
|
||||
expect(overall_course_score.final_score).to eq(0.0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "should return grades in the order they are requested" do
|
||||
|
|
|
@ -160,7 +160,7 @@ describe GradingPeriod do
|
|||
|
||||
context "where end_date equals Time.now" do
|
||||
it "does not include period" do
|
||||
Timecop.freeze(Time.zone.now.change(usec: 0)) do
|
||||
Timecop.freeze(now.change(usec: 0)) do
|
||||
period.update!(end_date: now)
|
||||
is_expected.to be_present
|
||||
end
|
||||
|
@ -169,7 +169,7 @@ describe GradingPeriod do
|
|||
|
||||
context "where end_date equals Time.now + 1" do
|
||||
it "does not include period" do
|
||||
Timecop.freeze(Time.zone.now.change(usec: 0) + 1) do
|
||||
Timecop.freeze(now.change(usec: 0) + 1) do
|
||||
period.update!(end_date: now)
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
@ -178,7 +178,7 @@ describe GradingPeriod do
|
|||
|
||||
context "where start_date equals Time.now" do
|
||||
it "does not include period" do
|
||||
Timecop.freeze(Time.zone.now.change(usec: 0)) do
|
||||
Timecop.freeze(now.change(usec: 0)) do
|
||||
period.update!(start_date: now)
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
@ -187,7 +187,7 @@ describe GradingPeriod do
|
|||
|
||||
context "where start_date equals Time.now + 1" do
|
||||
it "does not include period" do
|
||||
Timecop.freeze(Time.zone.now.change(usec: 0) + 1) do
|
||||
Timecop.freeze(now.change(usec: 0) + 1) do
|
||||
period.update!(start_date: now)
|
||||
is_expected.to be_present
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue