Coursemology/coursemology2

View on GitHub
app/controllers/concerns/course/lesson_plan/personalization_concern.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true
module Course::LessonPlan::PersonalizationConcern
  extend ActiveSupport::Concern

  # Dispatches the call to the correct personalization algorithm strategy.
  # If the algorithm takes too long (e.g. voodoo AI magic), it is responsible for scheduling an async job.
  #
  # Some properties for the algorithms:
  # - We don't shift personal dates that have already passed. This is to prevent items becoming locked
  #   when students are switched between different algos. There are thus quite a few checks for
  #   > Time.zone.now. The only exception is the backwards-shifting of already-past deadlines, which
  #   allows students to slow down their learning more effectively.
  # - We don't shift closing dates forward when the item has already opened for the student. This is to
  #   prevent students from being shocked that their deadlines have shifted forward suddenly.
  #
  # @param [CourseUser] course_user The user to update the personalized timeline for.
  # @param [String|nil] timeline_algorithm The timeline algorithm to run. If not provided, the user's timeline algorithm
  #   is used.
  # @param [Set<Number>|nil] items_to_shift A set of lesson plan item IDs to shift. If not in this set, the item won't
  #   be shifted.
  def update_personalized_timeline_for_user(course_user, timeline_algorithm = nil, items_to_shift = nil)
    timeline_algorithm ||= course_user.timeline_algorithm

    strategy = case timeline_algorithm
               when 'otot'
                 Course::LessonPlan::Strategies::OtotPersonalizationStrategy.new
               when 'fomo'
                 Course::LessonPlan::Strategies::FomoPersonalizationStrategy.new
               when 'stragglers'
                 Course::LessonPlan::Strategies::StragglersPersonalizationStrategy.new
               else
                 # Default to fixed.
                 Course::LessonPlan::Strategies::FixedPersonalizationStrategy.new
               end

    precomputed_data = strategy.precompute_data(course_user)
    strategy.execute(course_user, precomputed_data, items_to_shift)
    return if precomputed_data[:learning_rate_ema].nil?

    # Log the information for future usages
    learning_rate_record = Course::LearningRateRecord.new(course_user: course_user,
                                                          learning_rate: precomputed_data[:learning_rate_ema],
                                                          effective_min: precomputed_data[:effective_min],
                                                          effective_max: precomputed_data[:effective_max])
    learning_rate_record.save!
  end

  # Updates the personalized timeline for all course users in the course of the given lesson plan item.
  # Only the timing for the lesson plan item will be shifted. Generally, you should only call this if the timing of the
  # lesson plan item has shifted, or other personalized timeline related changes have been made for a specific item.
  def update_personalized_timeline_for_item(lesson_plan_item)
    course = Course.includes(:course_users).find(lesson_plan_item.course_id)
    course.course_users.each do |course_user|
      update_personalized_timeline_for_user(course_user, nil, Set[lesson_plan_item.id])
    end
  end
end