canvas-lms/app/models/score.rb

119 lines
3.3 KiB
Ruby

#
# Copyright (C) 2011 - present 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/>.
#
class Score < ActiveRecord::Base
include Canvas::SoftDeletable
belongs_to :enrollment, inverse_of: :scores
belongs_to :grading_period, optional: true
belongs_to :assignment_group, optional: true
has_one :course, through: :enrollment
has_one :score_metadata
validates :enrollment, presence: true
validates :current_score, :unposted_current_score, :final_score,
:unposted_final_score, numericality: true, allow_nil: true
validate :scorable_association_check
before_validation :set_course_score, unless: :course_score_changed?
set_policy do
given do |user, _session|
(user&.id == enrollment.user_id && !course.hide_final_grades?) ||
course.grants_any_right?(user, :manage_grades, :view_all_grades) ||
enrollment.user.grants_right?(user, :read_as_parent)
end
can :read
end
alias original_destroy destroy
private :original_destroy
def destroy
score_metadata.destroy if score_metadata.present?
original_destroy
end
alias original_destroy_permanently! destroy_permanently!
private :original_destroy_permanently!
def destroy_permanently!
ScoreMetadata.where(score: self).delete_all
original_destroy_permanently!
end
alias original_undestroy undestroy
private :original_undestroy
def undestroy
score_metadata.undestroy if score_metadata.present?
original_undestroy
end
def current_grade
score_to_grade(current_score)
end
def unposted_current_grade
score_to_grade(unposted_current_score)
end
def final_grade
score_to_grade(final_score)
end
def unposted_final_grade
score_to_grade(unposted_final_score)
end
def scorable
# if you're calling this method, you might want to preload objects to avoid N+1
grading_period || assignment_group || enrollment.course
end
def self.params_for_course
{ course_score: true }
end
delegate :score_to_grade, to: :course
private
def set_course_score
gpid = read_attribute(:grading_period_id)
agid = read_attribute(:assignment_group_id)
write_attribute(:course_score, (gpid || agid).nil?)
true
end
def scorable_association_check
scc = scorable_association_count
if scc == 0 && !course_score
errors.add(:course_score, "should be true when there are no scorable associations")
elsif scc == 1 && course_score
errors.add(:course_score, "should be false when there is a scorable association")
elsif scc > 1
errors.add(:base, "may not have multiple scorable associations")
else
return true
end
end
def scorable_association_count
[grading_period_id, assignment_group_id].compact.length
end
end