canvas-lms/app/models/rubric_assessment.rb

204 lines
7.3 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/>.
#
# Associates an artifact with a rubric while offering an assessment and
# scoring using the rubric. Assessments are grouped together in one
# RubricAssociation, which may or may not have an association model.
class RubricAssessment < ActiveRecord::Base
include TextHelper
attr_accessible :rubric, :rubric_association, :user, :score, :data, :comments, :assessor, :artifact, :assessment_type
belongs_to :rubric
belongs_to :rubric_association
belongs_to :user
belongs_to :assessor, :class_name => 'User'
belongs_to :artifact, :polymorphic => true, :touch => true
has_many :assessment_requests, :dependent => :destroy
serialize :data
simply_versioned
validates_presence_of :assessment_type, :rubric_id, :artifact_id, :artifact_type, :assessor_id
validates_length_of :comments, :maximum => maximum_text_length, :allow_nil => true, :allow_blank => true
before_save :update_artifact_parameters
before_save :htmlify_rating_comments
after_save :update_assessment_requests, :update_artifact
after_save :track_outcomes
def track_outcomes
outcome_ids = (self.data || []).map{|r| r[:learning_outcome_id] }.compact.uniq
send_later_if_production(:update_outcomes_for_assessment, outcome_ids) unless outcome_ids.empty?
end
def update_outcomes_for_assessment(outcome_ids=[])
return if outcome_ids.empty?
alignments = self.rubric_association.association.learning_outcome_alignments.find_all_by_learning_outcome_id(outcome_ids)
(self.data || []).each do |rating|
if rating[:learning_outcome_id]
alignments.each do |alignment|
if alignment.learning_outcome_id == rating[:learning_outcome_id]
create_outcome_result(alignment)
end
end
end
end
end
def create_outcome_result(alignment)
# find or create the user's unique LearningOutcomeResult for this alignment
# of the assessment's associated object.
result = alignment.learning_outcome_results.
for_association(rubric_association).
find_or_initialize_by_user_id(user.id)
# force the context and artifact
result.artifact = self
result.context = alignment.context
# mastery
criterion = rubric_association.rubric.data.find{|c| c[:learning_outcome_id] == alignment.learning_outcome_id }
criterion_result = self.data.find{|c| c[:criterion_id] == criterion[:id] }
if criterion
result.possible = criterion[:points]
result.score = criterion_result && criterion_result[:points]
result.mastery = result.score && (criterion[:mastery_points] || result.possible) && result.score >= (criterion[:mastery_points] || result.possible)
else
result.possible = nil
result.score = nil
result.mastery = nil
end
# attempt
if self.artifact && self.artifact.is_a?(Submission)
result.attempt = self.artifact.attempt || 1
else
result.attempt = self.version_number
end
# title
result.title = "#{user.name}, #{rubric_association.title}"
result.assessed_at = Time.now
result.save_to_version(result.attempt)
result
end
def update_artifact_parameters
if self.artifact_type == 'Submission' && self.artifact
self.artifact_attempt = self.artifact.attempt
end
end
def htmlify_rating_comments
if self.data_changed? && self.data.present?
self.data.each do |rating|
if rating.is_a?(Hash) && rating[:comments].present?
rating[:comments_html] = format_message(rating[:comments]).first
end
end
end
true
end
def update_assessment_requests
requests = self.assessment_requests
requests += self.rubric_association.assessment_requests.find_all_by_assessor_id_and_asset_id_and_asset_type(self.assessor_id, self.artifact_id, self.artifact_type)
requests.each { |a|
a.attributes = {:rubric_assessment => self, :assessor => self.assessor}
a.complete
}
end
protected :update_assessment_requests
def attempt
self.artifact_type == 'Submission' ? self.artifact.attempt : nil
end
def update_artifact
if self.artifact_type == 'Submission' && self.artifact
Submission.where(:id => self.artifact).update_all(:has_rubric_assessment => true)
if self.rubric_association && self.rubric_association.use_for_grading && self.artifact.score != self.score
if self.rubric_association.association.grants_right?(self.assessor, nil, :grade)
# TODO: this should go through assignment.grade_student to
# handle group assignments.
self.artifact.workflow_state = 'graded'
self.artifact.update_attributes(:score => self.score, :graded_at => Time.now, :grade_matches_current_submission => true, :grader => self.assessor)
end
end
end
end
protected :update_artifact
set_policy do
given {|user, session| session && session[:rubric_assessment_ids] && session[:rubric_assessment_ids].include?(self.id) }
can :create and can :read and can :update
given {|user, session| user && self.assessor_id == user.id }
can :create and can :read and can :update
given {|user, session| user && self.user_id == user.id }
can :read
given {|user, session| self.rubric_association && self.rubric_association.grants_rights?(user, session, :manage)[:manage] }
can :create and can :read and can :delete
given {|user, session|
self.rubric_association &&
self.rubric_association.grants_rights?(user, session, :manage)[:manage] &&
(self.rubric_association.association.context.grants_right?(self.assessor, nil, :manage_rubrics) rescue false)
}
can :update
end
scope :of_type, lambda { |type| where(:assessment_type => type.to_s) }
def methods_for_serialization(*methods)
@serialization_methods = methods
end
def serialization_methods
@serialization_methods || []
end
def assessor_name
self.assessor.name rescue t('unknown_user', "Unknown User")
end
def assessment_url
self.artifact.url rescue nil
end
def ratings
self.data
end
def related_group_submissions_and_assessments
if self.rubric_association && self.rubric_association.association.is_a?(Assignment) && !self.rubric_association.association.grade_group_students_individually
students = self.rubric_association.association.group_students(self.user).last
submissions = students.map do |student|
submission = self.rubric_association.association.find_asset_for_assessment(self.rubric_association, student.id).first
{:submission => submission, :rubric_assessments => submission.rubric_assessments.map{|ra| ra.as_json(:methods => :assessor_name)}}
end
else
[]
end
end
end