Coursemology/coursemology2

View on GitHub
app/services/course/assessment/reminder_service.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true
class Course::Assessment::ReminderService
  include Course::ReminderServiceConcern

  class << self
    delegate :closing_reminder, to: :new
    delegate :send_closing_reminder, to: :new
  end

  def closing_reminder(assessment, token)
    email_enabled = assessment.course.email_enabled(:assessments, :closing_reminder, assessment.tab.category.id)
    return unless assessment.closing_reminder_token == token && assessment.published?
    return unless email_enabled.phantom || email_enabled.regular

    send_closing_reminder(assessment)
  end

  def send_closing_reminder(assessment, course_user_ids = [], include_unsubscribed: false)
    students = uncompleted_subscribed_students(assessment, course_user_ids, include_unsubscribed)
    # Exclude students with personal times
    # TODO(#3240): Send closing reminder emails based on personal times
    students -=
      Set.new(CourseUser.joins(:personal_times).where(course_personal_times: { lesson_plan_item_id: assessment }))
    return if students.empty?

    closing_reminder_students(assessment, students)
    closing_reminder_staff(assessment, students)
  end

  private

  # Send reminder emails to each student who hasn't submitted.
  #
  # @param [Course::Assessment] assessment The assessment to query.
  def closing_reminder_students(assessment, recipients)
    recipients.each do |recipient|
      # Need to get the User model from the Course User because we need the email address.
      Course::Mailer.assessment_closing_reminder_email(assessment, recipient.user).deliver_later
    end
  end

  # Send an email to each instructor with a list of students who haven't submitted.
  #
  # @param [Course::Assessment] assessment The assessment to query.
  def closing_reminder_staff(assessment, students)
    course_instructors = assessment.course.instructors.includes(:user)
    student_list = name_list(students)
    email_enabled = assessment.course.email_enabled(:assessments, :closing_reminder_summary, assessment.tab.category.id)
    course_instructors.each do |instructor|
      is_disabled_as_phantom = instructor.phantom? && !email_enabled.phantom
      is_disabled_as_regular = !instructor.phantom? && !email_enabled.regular
      next if is_disabled_as_phantom || is_disabled_as_regular
      next if instructor.email_unsubscriptions.where(course_settings_email_id: email_enabled.id).exists?

      Course::Mailer.assessment_closing_summary_email(instructor.user, assessment, student_list).deliver_later
    end
  end

  # Returns a Set of students who have not completed the given assessment.
  #
  # @param [Course::Assessment] assessment The assessment to query.
  # @param [Array<Integer>] course_user_ids Course user ids of intended recipients (if specified).
  #   If empty, all students will be selected.
  # @param [Boolean] include_unsubscribed Whether to include unsubscribed students in the reminder (forced reminder).
  # @return [Set<CourseUser>] Set of CourseUsers who have not finished the assessment.
  def uncompleted_subscribed_students(assessment, course_user_ids, include_unsubscribed)
    course_users = assessment.course.course_users
    course_users = course_users.where(id: course_user_ids) unless course_user_ids.empty?
    email_enabled = assessment.course.email_enabled(:assessments, :closing_reminder, assessment.tab.category.id)
    # Eager load :user as it's needed for the recipient email.
    if email_enabled.regular && email_enabled.phantom
      students = course_users.student.includes(:user)
    elsif email_enabled.regular
      students = course_users.student.without_phantom_users.includes(:user)
    elsif email_enabled.phantom
      students = course_users.student.phantom.includes(:user)
    end
    submitted =
      assessment.submissions.confirmed.includes(experience_points_record: { course_user: :user }).
      map(&:course_user)
    return Set.new(students) - Set.new(submitted) if include_unsubscribed

    unsubscribed = students.joins(:email_unsubscriptions).
                   where('course_user_email_unsubscriptions.course_settings_email_id = ?', email_enabled.id)
    Set.new(students) - Set.new(unsubscribed) - Set.new(submitted)
  end
end