0

I have an ice_cube recurrence rule, for example, which occurs daily. The objective of this rule is to rotate users on a daily basis.

For example, if I have 4 users: John, Peter, Mark, Matthew, and a daily recurrence rule. I also am using delayed jobs to create a single job to run on the next_occurence which will rotate the users in the schedule_users table (by changing a priority value). I am only creating one job at a time, for example when the first rotation is performed a new job will be created for the next occurrence.

I would like to see which of the users is scheduled for a particular day. How would I look ahead to see which users are scheduled for the next 3, 5, 7 etc or any number of days in the future.

class Schedule < ApplicationRecord    
  belongs_to :project
  belongs_to :user

  has_many :schedule_users, -> { order priority: :desc }, dependent: :destroy
  has_many :users, through: :schedule_users

  enum frequency: { daily: 0, weekly: 1, biweekly: 2, monthly: 3 }

  validates_presence_of :name, :start, :frequency, :project
    
  def next_occurrence
    ice_cube_schedule.next_occurrence.start_time
  end

  def jobs
    Delayed::Job.where('handler LIKE ?', "%Schedule/#{id}\%")
  end

  def on_call_user
    schedule_users.max_by(&:priority).user if users.present?
  end

  def priority(user)
    schedule_users.where(user: user).first.try(:priority)
  end

  def ice_cube_schedule
    schedule = IceCube::Schedule.new(start, end_time: self.end)

    case frequency
    when 'daily'
      schedule.add_recurrence_rule IceCube::Rule.daily(1)
    when 'weekly'
      schedule.add_recurrence_rule IceCube::Rule.weekly(1)
    when 'biweekly'
      schedule.add_recurrence_rule IceCube::Rule.weekly(2)
    when 'monthly'
      schedule.add_recurrence_rule IceCube::Rule.monthly(1)
    end
    schedule
  end
end

class ScheduleUser < ApplicationRecord
  belongs_to :schedule
  belongs_to :user

  validates_presence_of :priority, :schedule, :user
end

class ReprioritizeScheduleUsersJob < ApplicationJob
  queue_as :default

  after_perform do |job|
    schedule = job.arguments.first
    ReprioritizeScheduleUsersJob.set(wait_until: schedule.next_occurrence).perform_later(schedule)
  end

  def perform(schedule)
    ActiveRecord::Base.transaction do
      schedule_users = schedule.schedule_users.reload
      num_users = schedule_users.count

      schedule.schedule_users.rotate.each_with_index do |schedule_user, index|
        schedule_user.update(priority: num_users - index)
      end
    end
  end
end

1 Answers1

0

You are auto-incrementing the priority value of the users, right?

schedule.schedule_users.rotate.each_with_index do |schedule_user, index|
  schedule_user.update(priority: num_users - index)
end

So with 4 users, you should have something like this on day 1:

ScheduleUser.all.pluck(:name, :priority)
[["Matthew", 1], ["Mark", 2], ["Luke", 3], ["John", 4]]

And on day 2:

ScheduleUser.all.pluck(:name, :priority)
[["Matthew", 2], ["Mark", 3], ["Luke", 4], ["John", 1]]

And on day 3:

ScheduleUser.all.pluck(:name, :priority)
[["Matthew", 3], ["Mark", 4], ["Luke", 1], ["John", 2]]

So another way to think about your problem is like an array wheel. If I move through this array X times, where will I land?

I know Matthew is #1 today, where will he be in 2 days? #3.

Here are our variables:

  • x = number of days in the future you want to predict
  • priority_array_size = the number of possible priorities (4 in this example), and equal to schedule_user.size
  • current_priority = the schedule_user's current priority
  • priority = the priority for the schedule_user x days in the future

An equation to calculate this could be:

priority = x.remainder(priority_array_size) + current_priority

I would suggest this is a method on ScheduleUser:

class ScheduleUser < ApplicationRecord
  belongs_to :schedule
  belongs_to :user

  validates_presence_of :priority, :schedule, :user

  def priority_in_x_days(days_in_future)
    days_in_future.remainder(ScheduleUser.all.size) + priority
  end
end
Dharman
  • 30,962
  • 25
  • 85
  • 135
Chiperific
  • 4,428
  • 3
  • 21
  • 41