canvas-lms/spec/models/grading_standard_spec.rb

399 lines
14 KiB
Ruby

#
# Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
describe GradingStandard do
before :once do
course_with_teacher
@default_standard_v1 = {
"A" => 1.0,
"A-" => 0.93,
"B+" => 0.89,
"B" => 0.86,
"B-" => 0.83,
"C+" => 0.79,
"C" => 0.76,
"C-" => 0.73,
"D+" => 0.69,
"D" => 0.66,
"D-" => 0.63,
"F" => 0.6
}.to_a.sort_by { |i| i[1] }.reverse
end
def compare_schemes(subject, expected)
expect(subject.size).to eq expected.size
subject.each_with_index do |row, i|
expect(row[0]).to eq expected[i][0]
expect(row[1]).to be_within(0.001).of(expected[i][1])
end
end
it "should upgrade the standard scheme from v1 to v2" do
converted = GradingStandard.upgrade_data(@default_standard_v1, 1)
default = GradingStandard.default_grading_standard
compare_schemes(converted, default)
end
it "should not the argument to data=" do
input = [['A', 0.9999]]
standard = GradingStandard.new
standard.data = input
expect(standard.data[0][1]).to be_within(0.00001).of(0.9999)
expect(input[0][1]).to be_within(0.00001).of(0.9999)
end
it "should upgrade in memory when accessing data" do
standard = GradingStandard.new
standard.write_attribute(:data, @default_standard_v1)
standard.write_attribute(:version, 1)
compare_schemes(standard.data, GradingStandard.default_grading_standard)
expect(standard.version).to eq GradingStandard::VERSION
end
it "should not upgrade repeatedly when accessing data repeatedly" do
standard = GradingStandard.new
standard.write_attribute(:data, @default_standard_v1)
standard.write_attribute(:version, 1)
compare_schemes(standard.data, GradingStandard.default_grading_standard)
compare_schemes(standard.data, GradingStandard.default_grading_standard)
compare_schemes(standard.data, GradingStandard.default_grading_standard)
end
context "#for" do
it "should return standards that match the context" do
grading_standard_for @course
standards = GradingStandard.for(@course)
expect(standards.length).to eq 1
expect(standards[0].id).to eq @standard.id
end
it "should include standards made in the parent account" do
grading_standard_for @course.root_account
standards = GradingStandard.for(@course)
expect(standards.length).to eq 1
expect(standards[0].id).to eq @standard.id
end
end
context "sorted" do
it "should return used grading standards before unused ones" do
gs = grading_standard_for(@course.root_account, :title => "zzz")
gs2 = grading_standard_for(@course.root_account, :title => "aaa")
# Add this grading standard to 3 assignments, triggring the "used" condition
3.times do
@course.assignments.create!(:title => "hi", :grading_standard_id => gs.id)
end
standards = GradingStandard.for(@course).sorted
expect(standards.length).to eq 2
expect(standards.map(&:id)).to eq [gs.id, gs2.id]
end
it "it should return standards with a title first" do
gs = grading_standard_for(@course.root_account, :title => "zzz")
gs2 = grading_standard_for(@course.root_account, :title => "aaa")
gs2.title = nil
gs2.save!
standards = GradingStandard.for(@course).sorted
expect(standards.length).to eq 2
expect(standards.map(&:id)).to eq [gs.id, gs2.id]
end
end
context "score_to_grade" do
it "should compute correct grades" do
input = [['A', 0.90], ['B+', 0.886], ['B', 0.80], ['C', 0.695], ['D', 0.555], ['E', 0.545], ['M', 0.00]]
standard = GradingStandard.new
standard.data = input
expect(standard.score_to_grade(1005)).to eql("A")
expect(standard.score_to_grade(105)).to eql("A")
expect(standard.score_to_grade(100)).to eql("A")
expect(standard.score_to_grade(99)).to eql("A")
expect(standard.score_to_grade(90)).to eql("A")
expect(standard.score_to_grade(89.999)).to eql("B+")
expect(standard.score_to_grade(88.601)).to eql("B+")
expect(standard.score_to_grade(88.6)).to eql("B+")
expect(standard.score_to_grade(88.599)).to eql("B")
expect(standard.score_to_grade(80)).to eql("B")
expect(standard.score_to_grade(79.999)).to eql("C")
expect(standard.score_to_grade(79)).to eql("C")
expect(standard.score_to_grade(69.501)).to eql("C")
expect(standard.score_to_grade(69.5)).to eql("C")
expect(standard.score_to_grade(69.499)).to eql("D")
expect(standard.score_to_grade(60)).to eql("D")
expect(standard.score_to_grade(55.5)).to eql("D")
expect(standard.score_to_grade(54.5)).to eql("E")
expect(standard.score_to_grade(50)).to eql("M")
expect(standard.score_to_grade(0)).to eql("M")
expect(standard.score_to_grade(-100)).to eql("M")
end
it "should assign the lowest grade to below-scale scores" do
input = [['A', 0.90], ['B', 0.80], ['C', 0.70], ['D', 0.60], ['E', 0.50]]
standard = GradingStandard.new
standard.data = input
expect(standard.score_to_grade(40)).to eql("E")
end
end
context "grade_to_score" do
before do
@gs = GradingStandard.default_instance
end
it "should return a score in the proper range for letter grades" do
score = @gs.grade_to_score('B')
expect(score).to eql(86.0)
end
it "should return nil when no grade matches" do
score = @gs.grade_to_score('Z')
expect(score).to eql(nil)
end
end
context "place in scheme" do
before do
@gs = GradingStandard.default_instance
@gs.data = {"4.0" => 0.94,
"3.7" => 0.90,
"3.3" => 0.87,
"3.0" => 0.84,
"2.7" => 0.80,
"2.3" => 0.77,
"2.0" => 0.74,
"1.7" => 0.70,
"1.3" => 0.67,
"1.0" => 0.64,
"0" => 0.01,
"M" => 0.0 }
end
it "should match alphabetical keys regardless of case" do
idx = @gs.place_in_scheme('m')
expect(idx).to eql(11)
end
it "should match numeric keys" do
idx = @gs.place_in_scheme(4)
expect(idx).to eql(0)
end
it "should not confuse letters and zeros" do
@gs.data = {"4.0" => 0.9,
"M" => 0.8,
"0" => 0.7,
"C" => 0.6,
"1.4" => 0.5}
[[4,0],["m",1],[0,2],["C",3],["1.4",4]].each do |grade,exp_index|
idx = @gs.place_in_scheme(grade)
expect(idx).to eql(exp_index)
end
end
end
context "associations" do
it "should not count deleted standards in associations" do
grading_standard_for(@course)
grading_standard_for(@course).destroy
expect(@course.grading_standards.count).to eq 1
grading_standard_for(@course.root_account)
grading_standard_for(@course.root_account).destroy
expect(@course.root_account.grading_standards.count).to eq 1
end
end
describe "assessed_assignment?" do
before(:once) do
student_in_course active_all: true
@gs = grading_standard_for @course, title: "gs"
end
context "without assignment link" do
it "should be false" do
expect(@gs).not_to be_assessed_assignment
end
end
context "with assignment link" do
before(:once) do
@assignment = @course.assignments.create!(:title => "hi",
:grading_type => 'letter_grade', :grading_standard_id => @gs.id, :submission_types => ["online_text_entry"])
end
context "without submissions" do
it "should be false" do
expect(@gs).not_to be_assessed_assignment
end
end
context "with submissions" do
before(:once) do
@submission = @assignment.submit_homework(@student, :body => "done!")
end
it "should be false if no submissions are graded" do
expect(@gs).not_to be_assessed_assignment
end
it "should be true if a graded submission exists" do
@submission.grade_it!
expect(@gs).to be_assessed_assignment
end
end
end
end
describe "permissions:" do
context "course belonging to root account" do
before(:once) do
@root_account = Account.default
@sub_account = @root_account.sub_accounts.create!
course_with_teacher_logged_in(account: @root_account)
@enrollment.update_attributes(workflow_state: "active")
@root_account_standard = grading_standard_for(@root_account)
@sub_account_standard = grading_standard_for(@sub_account)
@course_standard = grading_standard_for(@course)
end
context "root-account admin" do
before(:once) do
account_admin_user(account: @root_account)
end
it "should be able to manage root-account level grading standards" do
expect(@root_account_standard.grants_right?(@admin, :manage)).to eq(true)
end
it "should be able to manage sub-account level grading standards" do
expect(@sub_account_standard.grants_right?(@admin, :manage)).to eq(true)
end
it "should be able to manage course level grading standards" do
expect(@course_standard.grants_right?(@admin, :manage)).to eq(true)
end
end
context "sub-account admin" do
before(:once) do
account_admin_user(account: @sub_account)
end
it "should NOT be able to manage root-account level grading standards" do
expect(@root_account_standard.grants_right?(@admin, :manage)).to eq(false)
end
it "should be able to manage sub-account level grading standards" do
expect(@sub_account_standard.grants_right?(@admin, :manage)).to eq(true)
end
it "should NOT be able to manage course level grading standards, when the course is under the root-account" do
expect(@course_standard.grants_right?(@admin, :manage)).to eq(false)
end
end
context "teacher" do
it "should NOT be able to manage root-account level grading standards" do
expect(@root_account_standard.grants_right?(@teacher, :manage)).to eq(false)
end
it "should NOT be able to manage sub-account level grading standards" do
expect(@sub_account_standard.grants_right?(@teacher, :manage)).to eq(false)
end
it "should be able to manage course level grading standards" do
expect(@course_standard.grants_right?(@teacher, :manage)).to eq(true)
end
end
end
context "course belonging to sub-account" do
before(:once) do
@root_account = Account.default
@sub_account = @root_account.sub_accounts.create!
course_with_teacher_logged_in(account: @sub_account)
@enrollment.update_attributes(workflow_state: "active")
@root_account_standard = grading_standard_for(@root_account)
@sub_account_standard = grading_standard_for(@sub_account)
@course_standard = grading_standard_for(@course)
end
context "root-account admin" do
before(:once) do
account_admin_user(account: @root_account)
end
it "should be able to manage root-account level grading standards" do
expect(@root_account_standard.grants_right?(@admin, :manage)).to eq(true)
end
it "should be able to manage sub-account level grading standards" do
expect(@sub_account_standard.grants_right?(@admin, :manage)).to eq(true)
end
it "should be able to manage course level grading standards" do
expect(@course_standard.grants_right?(@admin, :manage)).to eq(true)
end
end
context "sub-account admin" do
before(:once) do
account_admin_user(account: @sub_account)
end
it "should NOT be able to manage root-account level grading standards" do
expect(@root_account_standard.grants_right?(@admin, :manage)).to eq(false)
end
it "should be able to manage sub-account level grading standards" do
expect(@sub_account_standard.grants_right?(@admin, :manage)).to eq(true)
end
it "should be able to manage course level grading standards, when the course is under the sub-account" do
expect(@course_standard.grants_right?(@admin, :manage)).to eq(true)
end
end
context "teacher" do
it "should NOT be able to manage root-account level grading standards" do
expect(@root_account_standard.grants_right?(@teacher, :manage)).to eq(false)
end
it "should NOT be able to manage sub-account level grading standards" do
expect(@sub_account_standard.grants_right?(@teacher, :manage)).to eq(false)
end
it "should be able to manage course level grading standards" do
expect(@course_standard.grants_right?(@teacher, :manage)).to eq(true)
end
end
end
end
end