canvas-lms/lib/late_policy_applicator.rb

117 lines
3.8 KiB
Ruby

# frozen_string_literal: true
#
# Copyright (C) 2017 - 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 LatePolicyApplicator
def self.for_course(course)
return unless course.published?
return unless course.assignments.published.exists?
new(course).delay_if_production(
singleton: "late_policy_applicator:calculator:Course:#{course.global_id}",
n_strand: ["LatePolicyApplicator", course.root_account.global_id]
).process
end
def self.for_assignment(assignment)
return if assignment.has_sub_assignments?
return unless assignment.published? && assignment.points_possible&.positive?
return unless assignment.course
new(assignment.course, [assignment]).delay_if_production(
singleton: "late_policy_applicator:calculator:Assignment:#{assignment.global_id}",
n_strand: ["LatePolicyApplicator", assignment.root_account.global_id]
).process
end
def initialize(course, assignments = [])
@course = course
@assignments = if assignments.present?
assignments.select { |a| a.published? && !a.has_sub_assignments? }
else
AbstractAssignment.where(context_id: course.id, context_type: "Course").published.has_no_sub_assignments
end
@relevant_submissions = {}
end
def process
return unless needs_processing?
late_policy = @course.late_policy
user_ids = []
@assignments.each do |assignment|
relevant_submissions(assignment).find_each do |submission|
submission.assignment = assignment
user_ids << submission.user_id if process_submission(late_policy, assignment, submission)
end
end
@course.recompute_student_scores(user_ids.uniq) if user_ids.present?
end
private
def process_submission(late_policy, assignment, submission)
submission.apply_late_policy(late_policy, assignment)
if submission.changed?
submission.skip_grade_calc = true
return submission.save!
end
false
end
def relevant_submissions(assignment)
@relevant_submissions[assignment.id] ||= begin
if @course.late_policy.late_submission_deduction_enabled
query = late_submissions_for(assignment).union(no_longer_late_submissions_for(assignment))
if @course.late_policy.missing_submission_deduction_enabled
query = query.union(missing_submissions_for(assignment))
end
else
query = missing_submissions_for(assignment)
end
query.eager_load(:grading_period).merge(GradingPeriod.open).preload(:stream_item, :user)
end
end
def submissions(assignment)
enrollments = assignment.course.admin_visible_student_enrollments
assignment.submissions.for_enrollments(enrollments)
end
def late_submissions_for(assignment)
submissions(assignment).late.where.not(score: nil)
end
def no_longer_late_submissions_for(assignment)
submissions(assignment).not_late.where("submissions.points_deducted > 0")
end
def missing_submissions_for(assignment)
submissions(assignment).missing.where(score: nil, grade: nil)
end
def needs_processing?
(@course.late_policy&.missing_submission_deduction_enabled ||
@course.late_policy&.late_submission_deduction_enabled) && @assignments.present?
end
end