mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 15:20:13 +01:00
move recurrence text helpers into patterns. move next_date calculation into
respective models
This commit is contained in:
parent
d8507bf8b7
commit
b84adfc172
13 changed files with 291 additions and 289 deletions
|
|
@ -40,14 +40,6 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pattern
|
|
||||||
if valid_period?
|
|
||||||
@pattern = eval("RecurringTodos::#{recurring_period.capitalize}RepeatPattern.new(user)")
|
|
||||||
@pattern.build_from_recurring_todo(self)
|
|
||||||
end
|
|
||||||
@pattern
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid_period?
|
def valid_period?
|
||||||
%W[daily weekly monthly yearly].include?(recurring_period)
|
%W[daily weekly monthly yearly].include?(recurring_period)
|
||||||
end
|
end
|
||||||
|
|
@ -78,16 +70,12 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
# choosing between both options is done on recurrence_selector where 0 is
|
# choosing between both options is done on recurrence_selector where 0 is
|
||||||
# for first type and 1 for second type
|
# for first type and 1 for second type
|
||||||
|
|
||||||
|
def pattern
|
||||||
def switch_week_day(day, position)
|
if valid_period?
|
||||||
self.every_day = ' ' if self.every_day.nil?
|
@pattern = eval("RecurringTodos::#{recurring_period.capitalize}RepeatPattern.new(user)")
|
||||||
self.every_day = every_day[0, position] + day + every_day[position+1, every_day.length]
|
@pattern.build_from_recurring_todo(self)
|
||||||
end
|
|
||||||
|
|
||||||
{ monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }.each do |day, number|
|
|
||||||
define_method("weekly_return_#{day}=") do |selector|
|
|
||||||
switch_week_day(selector, number) if recurring_period=='weekly'
|
|
||||||
end
|
end
|
||||||
|
@pattern
|
||||||
end
|
end
|
||||||
|
|
||||||
def recurrence_pattern
|
def recurrence_pattern
|
||||||
|
|
@ -103,214 +91,11 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_due_date(previous)
|
def get_due_date(previous)
|
||||||
case self.target
|
pattern.get_due_date(previous)
|
||||||
when 'due_date'
|
|
||||||
get_next_date(previous)
|
|
||||||
when 'show_from_date'
|
|
||||||
# so leave due date empty
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_show_from_date(previous)
|
def get_show_from_date(previous)
|
||||||
case self.target
|
pattern.get_show_from_date(previous)
|
||||||
when 'due_date'
|
|
||||||
# so set show from date relative to due date unless show_always is true or show_from_delta is nil
|
|
||||||
(self.show_always? || self.show_from_delta.nil?) ? nil : get_due_date(previous) - self.show_from_delta.days
|
|
||||||
when 'show_from_date'
|
|
||||||
# Leave due date empty
|
|
||||||
get_next_date(previous)
|
|
||||||
else
|
|
||||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_next_date(previous)
|
|
||||||
case self.recurring_period
|
|
||||||
when 'daily' then get_daily_date(previous)
|
|
||||||
when 'weekly' then get_weekly_date(previous)
|
|
||||||
when 'monthly' then get_monthly_date(previous)
|
|
||||||
when 'yearly' then get_yearly_date(previous)
|
|
||||||
else
|
|
||||||
raise Exception.new, "unknown recurrence pattern: '#{self.recurring_period}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_daily_date(previous)
|
|
||||||
# previous is the due date of the previous todo or it is the completed_at
|
|
||||||
# date when the completed_at date is after due_date (i.e. you did not make
|
|
||||||
# the due date in time)
|
|
||||||
#
|
|
||||||
# assumes self.recurring_period == 'daily'
|
|
||||||
|
|
||||||
start = determine_start(previous, 1.day)
|
|
||||||
|
|
||||||
if self.only_work_days
|
|
||||||
return start + 2.day if start.wday() == 6 # saturday
|
|
||||||
return start + 1.day if start.wday() == 0 # sunday
|
|
||||||
return start
|
|
||||||
else # every nth day; n = every_other1
|
|
||||||
# if there was no previous todo, do not add n: the first todo starts on
|
|
||||||
# today or on start_from
|
|
||||||
return previous == nil ? start : start+every_other1.day-1.day
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_weekly_date(previous)
|
|
||||||
# determine start
|
|
||||||
if previous == nil
|
|
||||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
|
||||||
else
|
|
||||||
start = previous + 1.day
|
|
||||||
if start.wday() == 0
|
|
||||||
# we went to a new week , go to the nth next week and find first match
|
|
||||||
# that week. Note that we already went into the next week, so -1
|
|
||||||
start += (self.every_other1-1).week
|
|
||||||
end
|
|
||||||
unless self.start_from.nil?
|
|
||||||
# check if the start_from date is later than previous. If so, use
|
|
||||||
# start_from as start to search for next date
|
|
||||||
start = self.start_from if self.start_from > previous
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
day = find_first_day_in_this_week(start)
|
|
||||||
return day unless day == -1
|
|
||||||
|
|
||||||
# we did not find anything this week, so check the nth next, starting from
|
|
||||||
# sunday
|
|
||||||
start = start + self.every_other1.week - (start.wday()).days
|
|
||||||
|
|
||||||
start = find_first_day_in_this_week(start)
|
|
||||||
return start unless start == -1
|
|
||||||
|
|
||||||
raise Exception.new, "unable to find next weekly date (#{self.every_day})"
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_monthly_date(previous)
|
|
||||||
start = determine_start(previous)
|
|
||||||
day = self.every_other1
|
|
||||||
n = self.every_other2
|
|
||||||
|
|
||||||
case self.recurrence_selector
|
|
||||||
when 0 # specific day of the month
|
|
||||||
if (previous && start.mday >= day) || (previous.nil? && start.mday > day)
|
|
||||||
# there is no next day n in this month, search in next month
|
|
||||||
#
|
|
||||||
# start += n.months
|
|
||||||
#
|
|
||||||
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
|
||||||
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
|
||||||
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
|
||||||
# local timezone.
|
|
||||||
#
|
|
||||||
# TODO: recheck if future rails versions have this problem too
|
|
||||||
start = Time.utc(start.year, start.month, start.day)+n.months
|
|
||||||
start = Time.zone.local(start.year, start.month, start.day)
|
|
||||||
|
|
||||||
# go back to day
|
|
||||||
end
|
|
||||||
Time.zone.local(start.year, start.month, day)
|
|
||||||
|
|
||||||
when 1 # relative weekday of a month
|
|
||||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, start.month, start.year)
|
|
||||||
if the_next.nil? || the_next <= start
|
|
||||||
# the nth day is already passed in this month, go to next month and try
|
|
||||||
# again
|
|
||||||
|
|
||||||
# fiddle with timezone. Looks like we hit a bug in rails here where
|
|
||||||
# 2008-12-01 +0100 plus 1.month becomes 2008-12-31 +0100. For now, just
|
|
||||||
# calculate in UTC and convert back to local timezone.
|
|
||||||
# TODO: recheck if future rails versions have this problem too
|
|
||||||
the_next = Time.utc(the_next.year, the_next.month, the_next.day)+n.months
|
|
||||||
the_next = Time.zone.local(the_next.year, the_next.month, the_next.day)
|
|
||||||
|
|
||||||
# TODO: if there is still no match, start will be set to nil. if we ever
|
|
||||||
# support 5th day of the month, we need to handle this case
|
|
||||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, the_next.month, the_next.year)
|
|
||||||
end
|
|
||||||
the_next
|
|
||||||
else
|
|
||||||
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_xth_day_of_month(x, weekday, month, year)
|
|
||||||
if x == 5
|
|
||||||
# last -> count backwards. use UTC to avoid strange timezone oddities
|
|
||||||
# where last_day -= 1.day seems to shift tz+0100 to tz+0000
|
|
||||||
last_day = Time.utc(year, month, Time.days_in_month(month))
|
|
||||||
while last_day.wday != weekday
|
|
||||||
last_day -= 1.day
|
|
||||||
end
|
|
||||||
# convert back to local timezone
|
|
||||||
Time.zone.local(last_day.year, last_day.month, last_day.day)
|
|
||||||
else
|
|
||||||
# 1-4th -> count upwards last -> count backwards. use UTC to avoid strange
|
|
||||||
# timezone oddities where last_day -= 1.day seems to shift tz+0100 to
|
|
||||||
# tz+0000
|
|
||||||
start = Time.utc(year,month,1)
|
|
||||||
n = x
|
|
||||||
while n > 0
|
|
||||||
while start.wday() != weekday
|
|
||||||
start+= 1.day
|
|
||||||
end
|
|
||||||
n -= 1
|
|
||||||
start += 1.day unless n==0
|
|
||||||
end
|
|
||||||
# convert back to local timezone
|
|
||||||
Time.zone.local(start.year, start.month, start.day)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_yearly_date(previous)
|
|
||||||
start = determine_start(previous)
|
|
||||||
day = self.every_other1
|
|
||||||
month = self.every_other2
|
|
||||||
|
|
||||||
case self.recurrence_selector
|
|
||||||
when 0 # specific day of a specific month
|
|
||||||
if start.month > month || (start.month == month && start.day >= day)
|
|
||||||
# if there is no next month n and day m in this year, search in next
|
|
||||||
# year
|
|
||||||
start = Time.zone.local(start.year+1, month, 1)
|
|
||||||
else
|
|
||||||
# if there is a next month n, stay in this year
|
|
||||||
start = Time.zone.local(start.year, month, 1)
|
|
||||||
end
|
|
||||||
Time.zone.local(start.year, month, day)
|
|
||||||
|
|
||||||
when 1 # relative weekday of a specific month
|
|
||||||
# if there is no next month n in this year, search in next year
|
|
||||||
the_next = start.month > month ? Time.zone.local(start.year+1, month, 1) : start
|
|
||||||
|
|
||||||
# get the xth day of the month
|
|
||||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, month, the_next.year)
|
|
||||||
|
|
||||||
# if the_next is before previous, we went back into the past, so try next
|
|
||||||
# year
|
|
||||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, month, start.year+1) if the_next <= start
|
|
||||||
|
|
||||||
the_next
|
|
||||||
else
|
|
||||||
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def continues_recurring?(previous)
|
|
||||||
return self.occurences_count < self.number_of_occurences unless self.number_of_occurences.nil?
|
|
||||||
return true if self.end_date.nil? || self.ends_on == 'no_end_date'
|
|
||||||
|
|
||||||
case self.target
|
|
||||||
when 'due_date'
|
|
||||||
get_due_date(previous) <= self.end_date
|
|
||||||
when 'show_from_date'
|
|
||||||
get_show_from_date(previous) <= self.end_date
|
|
||||||
else
|
|
||||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def done?(end_date)
|
def done?(end_date)
|
||||||
|
|
@ -350,29 +135,8 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
self.save
|
self.save
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
def continues_recurring?(previous)
|
||||||
|
pattern.continues_recurring?(previous)
|
||||||
# Determine start date to calculate next date for recurring todo
|
|
||||||
# offset needs to be 1.day for daily patterns
|
|
||||||
def determine_start(previous, offset=0.day)
|
|
||||||
start = self.start_from || NullTime.new
|
|
||||||
now = Time.zone.now
|
|
||||||
if previous
|
|
||||||
# check if the start_from date is later than previous. If so, use
|
|
||||||
# start_from as start to search for next date
|
|
||||||
start > previous ? start : previous + offset
|
|
||||||
else
|
|
||||||
# skip to present
|
|
||||||
start > now ? start : now
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_first_day_in_this_week(start)
|
end
|
||||||
# check if there are any days left this week for the next todo
|
|
||||||
start.wday().upto 6 do |i|
|
|
||||||
return start + (i-start.wday()).days unless self.every_day[i,1] == ' '
|
|
||||||
end
|
|
||||||
-1
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -24,7 +24,7 @@ module RecurringTodos
|
||||||
get :target
|
get :target
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_always
|
def show_always?
|
||||||
get :show_always
|
get :show_always
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -104,8 +104,8 @@ module RecurringTodos
|
||||||
when 'show_from_date'
|
when 'show_from_date'
|
||||||
# no validations
|
# no validations
|
||||||
when 'due_date'
|
when 'due_date'
|
||||||
validate_not_nil(show_always, "Please select when to show the action")
|
validate_not_nil(show_always?, "Please select when to show the action")
|
||||||
validate_not_blank(show_from_delta, "Please fill in the number of days to show the todo before the due date") unless show_always
|
validate_not_blank(show_from_delta, "Please fill in the number of days to show the todo before the due date") unless show_always?
|
||||||
else
|
else
|
||||||
errors[:base] << "Unexpected value of recurrence target selector '#{target}'"
|
errors[:base] << "Unexpected value of recurrence target selector '#{target}'"
|
||||||
end
|
end
|
||||||
|
|
@ -119,6 +119,93 @@ module RecurringTodos
|
||||||
@attributes[attribute]
|
@attributes[attribute]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# gets the next due date. returns nil if recurrence_target is not 'due_date'
|
||||||
|
def get_due_date(previous)
|
||||||
|
case target
|
||||||
|
when 'due_date'
|
||||||
|
get_next_date(previous)
|
||||||
|
when 'show_from_date'
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_show_from_date(previous)
|
||||||
|
case target
|
||||||
|
when 'due_date'
|
||||||
|
# so set show from date relative to due date unless show_always is true or show_from_delta is nil
|
||||||
|
return nil unless put_in_tickler?
|
||||||
|
get_due_date(previous) - show_from_delta.days
|
||||||
|
when 'show_from_date'
|
||||||
|
# Leave due date empty
|
||||||
|
get_next_date(previous)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# checks if the next todos should be put in the tickler for recurrence_target == 'due_date'
|
||||||
|
def put_in_tickler?
|
||||||
|
!( show_always? || show_from_delta.nil?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_next_date(previous)
|
||||||
|
raise "Should not call AbstractRepeatPattern.get_next_date directly. Overwrite in subclass"
|
||||||
|
end
|
||||||
|
|
||||||
|
def continues_recurring?(previous)
|
||||||
|
return @recurring_todo.occurences_count < @recurring_todo.number_of_occurences unless @recurring_todo.number_of_occurences.nil?
|
||||||
|
return true if self.end_date.nil? || self.ends_on == 'no_end_date'
|
||||||
|
|
||||||
|
case self.target
|
||||||
|
when 'due_date'
|
||||||
|
get_due_date(previous) <= self.end_date
|
||||||
|
when 'show_from_date'
|
||||||
|
get_show_from_date(previous) <= self.end_date
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Determine start date to calculate next date for recurring todo
|
||||||
|
# offset needs to be 1.day for daily patterns
|
||||||
|
def determine_start(previous, offset=0.day)
|
||||||
|
start = self.start_from || NullTime.new
|
||||||
|
now = Time.zone.now
|
||||||
|
if previous
|
||||||
|
# check if the start_from date is later than previous. If so, use
|
||||||
|
# start_from as start to search for next date
|
||||||
|
start > previous ? start : previous + offset
|
||||||
|
else
|
||||||
|
# skip to present
|
||||||
|
start > now ? start : now
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_xth_day_of_month(x, weekday, month, year)
|
||||||
|
if x == 5
|
||||||
|
# last -> count backwards. use UTC to avoid strange timezone oddities
|
||||||
|
# where last_day -= 1.day seems to shift tz+0100 to tz+0000
|
||||||
|
last_day = Time.utc(year, month, Time.days_in_month(month))
|
||||||
|
while last_day.wday != weekday
|
||||||
|
last_day -= 1.day
|
||||||
|
end
|
||||||
|
# convert back to local timezone
|
||||||
|
Time.zone.local(last_day.year, last_day.month, last_day.day)
|
||||||
|
else
|
||||||
|
# 1-4th -> count upwards last -> count backwards. use UTC to avoid strange
|
||||||
|
# timezone oddities where last_day -= 1.day seems to shift tz+0100 to
|
||||||
|
# tz+0000
|
||||||
|
start = Time.utc(year,month,1)
|
||||||
|
n = x
|
||||||
|
while n > 0
|
||||||
|
while start.wday() != weekday
|
||||||
|
start+= 1.day
|
||||||
|
end
|
||||||
|
n -= 1
|
||||||
|
start += 1.day unless n==0
|
||||||
|
end
|
||||||
|
# convert back to local timezone
|
||||||
|
Time.zone.local(start.year, start.month, start.day)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -29,7 +29,23 @@ module RecurringTodos
|
||||||
errors[:base] << "Every other nth day may not be empty for this daily recurrence setting" if (!only_work_days?) && every_x_days.blank?
|
errors[:base] << "Every other nth day may not be empty for this daily recurrence setting" if (!only_work_days?) && every_x_days.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_next_date(previous)
|
||||||
|
# previous is the due date of the previous todo or it is the completed_at
|
||||||
|
# date when the completed_at date is after due_date (i.e. you did not make
|
||||||
|
# the due date in time)
|
||||||
|
|
||||||
|
start = determine_start(previous, 1.day)
|
||||||
|
|
||||||
|
if only_work_days?
|
||||||
|
return start + 2.day if start.wday() == 6 # saturday
|
||||||
|
return start + 1.day if start.wday() == 0 # sunday
|
||||||
|
return start
|
||||||
|
else # every nth day; n = every_other1
|
||||||
|
# if there was no previous todo, do not add n: the first todo starts on
|
||||||
|
# today or on start_from
|
||||||
|
return previous == nil ? start : start+every_x_days.day-1.day
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -75,6 +75,54 @@ module RecurringTodos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_next_date(previous)
|
||||||
|
start = determine_start(previous)
|
||||||
|
day = every_x_day
|
||||||
|
n = get(:every_other2)
|
||||||
|
|
||||||
|
case recurrence_selector
|
||||||
|
when 0 # specific day of the month
|
||||||
|
if (previous && start.mday >= day) || (previous.nil? && start.mday > day)
|
||||||
|
# there is no next day n in this month, search in next month
|
||||||
|
#
|
||||||
|
# start += n.months
|
||||||
|
#
|
||||||
|
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
||||||
|
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
||||||
|
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
||||||
|
# local timezone.
|
||||||
|
#
|
||||||
|
# TODO: recheck if future rails versions have this problem too
|
||||||
|
start = Time.utc(start.year, start.month, start.day)+n.months
|
||||||
|
start = Time.zone.local(start.year, start.month, start.day)
|
||||||
|
|
||||||
|
# go back to day
|
||||||
|
end
|
||||||
|
Time.zone.local(start.year, start.month, day)
|
||||||
|
|
||||||
|
when 1 # relative weekday of a month
|
||||||
|
the_next = get_xth_day_of_month(every_xth_day, day_of_week, start.month, start.year)
|
||||||
|
if the_next.nil? || the_next <= start
|
||||||
|
# the nth day is already passed in this month, go to next month and try
|
||||||
|
# again
|
||||||
|
|
||||||
|
# fiddle with timezone. Looks like we hit a bug in rails here where
|
||||||
|
# 2008-12-01 +0100 plus 1.month becomes 2008-12-31 +0100. For now, just
|
||||||
|
# calculate in UTC and convert back to local timezone.
|
||||||
|
# TODO: recheck if future rails versions have this problem too
|
||||||
|
the_next = Time.utc(the_next.year, the_next.month, the_next.day)+n.months
|
||||||
|
the_next = Time.zone.local(the_next.year, the_next.month, the_next.day)
|
||||||
|
|
||||||
|
# TODO: if there is still no match, start will be set to nil. if we ever
|
||||||
|
# support 5th day of the month, we need to handle this case
|
||||||
|
the_next = get_xth_day_of_month(every_xth_day, day_of_week, the_next.month, the_next.year)
|
||||||
|
end
|
||||||
|
the_next
|
||||||
|
else
|
||||||
|
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -13,7 +13,10 @@ module RecurringTodos
|
||||||
def map_attributes(mapping)
|
def map_attributes(mapping)
|
||||||
mapping = map(mapping, :every_other1, 'weekly_every_x_week')
|
mapping = map(mapping, :every_other1, 'weekly_every_x_week')
|
||||||
|
|
||||||
{ monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }.each{|day, index| mapping = map_day(mapping, :every_day, "weekly_return_#{day}", index)}
|
{ monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }
|
||||||
|
.each do |day, index|
|
||||||
|
mapping = map_day(mapping, :every_day, "weekly_return_#{day}", index)
|
||||||
|
end
|
||||||
|
|
||||||
mapping
|
mapping
|
||||||
end
|
end
|
||||||
|
|
@ -23,7 +26,7 @@ module RecurringTodos
|
||||||
mapping.set_if_nil(source_key, ' ') # avoid nil
|
mapping.set_if_nil(source_key, ' ') # avoid nil
|
||||||
|
|
||||||
mapping.set(key, mapping.get(key)[0, index] + mapping.get(source_key) + mapping.get(key)[index+1, mapping.get(key).length])
|
mapping.set(key, mapping.get(key)[0, index] + mapping.get(source_key) + mapping.get(key)[index+1, mapping.get(key).length])
|
||||||
mapping
|
mapping.except(source_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def selector_key
|
def selector_key
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,48 @@ module RecurringTodos
|
||||||
errors[:base] << "You must specify at least one day on which the todo recurs" unless something_set
|
errors[:base] << "You must specify at least one day on which the todo recurs" unless something_set
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_next_date(previous)
|
||||||
|
# determine start
|
||||||
|
if previous.nil?
|
||||||
|
start = start_from.nil? ? Time.zone.now : self.start_from
|
||||||
|
else
|
||||||
|
start = previous + 1.day
|
||||||
|
if start.wday() == 0
|
||||||
|
# we went to a new week , go to the nth next week and find first match
|
||||||
|
# that week. Note that we already went into the next week, so -1
|
||||||
|
start += (every_x_week-1).week
|
||||||
|
end
|
||||||
|
unless self.start_from.nil?
|
||||||
|
# check if the start_from date is later than previous. If so, use
|
||||||
|
# start_from as start to search for next date
|
||||||
|
start = self.start_from if self.start_from > previous
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
day = find_first_day_in_this_week(start)
|
||||||
|
return day unless day == -1
|
||||||
|
|
||||||
|
# we did not find anything this week, so check the nth next, starting from
|
||||||
|
# sunday
|
||||||
|
start = start + self.every_x_week.week - (start.wday()).days
|
||||||
|
|
||||||
|
start = find_first_day_in_this_week(start)
|
||||||
|
return start unless start == -1
|
||||||
|
|
||||||
|
raise Exception.new, "unable to find next weekly date (#{self.every_day})"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_first_day_in_this_week(start)
|
||||||
|
# check if there are any days left this week for the next todo
|
||||||
|
start.wday().upto 6 do |i|
|
||||||
|
return start + (i-start.wday()).days if on_xday(i)
|
||||||
|
end
|
||||||
|
-1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -61,6 +61,37 @@ module RecurringTodos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
def get_next_date(previous)
|
||||||
|
start = determine_start(previous)
|
||||||
|
day = every_x_day
|
||||||
|
month = get(:every_other2)
|
||||||
|
|
||||||
|
case recurrence_selector
|
||||||
|
when 0 # specific day of a specific month
|
||||||
|
if start.month > month || (start.month == month && start.day >= day)
|
||||||
|
# if there is no next month n and day m in this year, search in next
|
||||||
|
# year
|
||||||
|
start = Time.zone.local(start.year+1, month, 1)
|
||||||
|
else
|
||||||
|
# if there is a next month n, stay in this year
|
||||||
|
start = Time.zone.local(start.year, month, 1)
|
||||||
|
end
|
||||||
|
Time.zone.local(start.year, month, day)
|
||||||
|
|
||||||
|
when 1 # relative weekday of a specific month
|
||||||
|
# if there is no next month n in this year, search in next year
|
||||||
|
the_next = start.month > month ? Time.zone.local(start.year+1, month, 1) : start
|
||||||
|
|
||||||
|
# get the xth day of the month
|
||||||
|
the_next = get_xth_day_of_month(self.every_xth_day, day_of_week, month, the_next.year)
|
||||||
|
|
||||||
|
# if the_next is before previous, we went back into the past, so try next
|
||||||
|
# year
|
||||||
|
the_next = get_xth_day_of_month(self.every_xth_day, day_of_week, month, start.year+1) if the_next <= start
|
||||||
|
|
||||||
|
the_next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -623,7 +623,7 @@ en:
|
||||||
- December
|
- December
|
||||||
third: third
|
third: third
|
||||||
every_n: every %{n}
|
every_n: every %{n}
|
||||||
on_day_n: on day %{n}
|
on_day_n: on day %{n}
|
||||||
second: second
|
second: second
|
||||||
every_xth_day_of_every_n_months: every %{x} %{day} of every %{n_months}
|
every_xth_day_of_every_n_months: every %{x} %{day} of every %{n_months}
|
||||||
from: from
|
from: from
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,6 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
||||||
@thursday = Time.zone.local(2008,6,12)
|
@thursday = Time.zone.local(2008,6,12)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_pattern_text
|
|
||||||
assert_equal "every day", @every_day.recurrence_pattern
|
|
||||||
assert_equal "on work days", @every_workday.recurrence_pattern
|
|
||||||
assert_equal "every last friday of every 2 months", @monthly_every_last_friday.recurrence_pattern
|
|
||||||
assert_equal "every year on June 08", @yearly.recurrence_pattern
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_daily_every_day
|
def test_daily_every_day
|
||||||
# every_day should return todays date if there was no previous date
|
# every_day should return todays date if there was no previous date
|
||||||
due_date = @every_day.get_due_date(nil)
|
due_date = @every_day.get_due_date(nil)
|
||||||
|
|
@ -96,29 +89,6 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
||||||
assert_equal false, @every_day.continues_recurring?(@in_four_days)
|
assert_equal false, @every_day.continues_recurring?(@in_four_days)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_weekly_every_day_setters
|
|
||||||
@weekly_every_day.every_day = ' '
|
|
||||||
|
|
||||||
@weekly_every_day.weekly_return_sunday=('s')
|
|
||||||
assert_equal 's ', @weekly_every_day.every_day
|
|
||||||
@weekly_every_day.weekly_return_monday=('m')
|
|
||||||
assert_equal 'sm ', @weekly_every_day.every_day
|
|
||||||
@weekly_every_day.weekly_return_tuesday=('t')
|
|
||||||
assert_equal 'smt ', @weekly_every_day.every_day
|
|
||||||
@weekly_every_day.weekly_return_wednesday=('w')
|
|
||||||
assert_equal 'smtw ', @weekly_every_day.every_day
|
|
||||||
@weekly_every_day.weekly_return_thursday=('t')
|
|
||||||
assert_equal 'smtwt ', @weekly_every_day.every_day
|
|
||||||
@weekly_every_day.weekly_return_friday=('f')
|
|
||||||
assert_equal 'smtwtf ', @weekly_every_day.every_day
|
|
||||||
@weekly_every_day.weekly_return_saturday=('s')
|
|
||||||
assert_equal 'smtwtfs', @weekly_every_day.every_day
|
|
||||||
|
|
||||||
# test remove
|
|
||||||
@weekly_every_day.weekly_return_wednesday=(' ')
|
|
||||||
assert_equal 'smt tfs', @weekly_every_day.every_day
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_weekly_pattern
|
def test_weekly_pattern
|
||||||
assert_equal true, @weekly_every_day.continues_recurring?(nil)
|
assert_equal true, @weekly_every_day.continues_recurring?(nil)
|
||||||
|
|
||||||
|
|
@ -132,9 +102,7 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
||||||
assert_equal @sunday + 2.weeks, due_date
|
assert_equal @sunday + 2.weeks, due_date
|
||||||
|
|
||||||
# remove tuesday and wednesday
|
# remove tuesday and wednesday
|
||||||
@weekly_every_day.weekly_return_tuesday=(' ')
|
@weekly_every_day.every_day = 'sm tfs'
|
||||||
@weekly_every_day.weekly_return_wednesday=(' ')
|
|
||||||
assert_equal 'sm tfs', @weekly_every_day.every_day
|
|
||||||
due_date = @weekly_every_day.get_due_date(@monday)
|
due_date = @weekly_every_day.get_due_date(@monday)
|
||||||
assert_equal @thursday, due_date
|
assert_equal @thursday, due_date
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,17 @@ module RecurringTodos
|
||||||
assert rt.valid?, "should be valid again"
|
assert rt.valid?, "should be valid again"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_pattern_text
|
||||||
|
@every_day = recurring_todos(:call_bill_gates_every_day)
|
||||||
|
@every_workday = recurring_todos(:call_bill_gates_every_workday)
|
||||||
|
|
||||||
|
assert_equal "every day", @every_day.recurrence_pattern
|
||||||
|
assert_equal "on work days", @every_workday.recurrence_pattern
|
||||||
|
|
||||||
|
@every_day.every_other1 = 2
|
||||||
|
assert_equal "every 2 days", @every_day.recurrence_pattern
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -98,6 +98,20 @@ module RecurringTodos
|
||||||
assert !rt.valid?, "should not be valid since day_of_week is empty"
|
assert !rt.valid?, "should not be valid since day_of_week is empty"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_pattern_text
|
||||||
|
rt = recurring_todos(:check_with_bill_every_last_friday_of_month)
|
||||||
|
assert_equal "every last friday of every 2 months", rt.recurrence_pattern
|
||||||
|
|
||||||
|
rt.every_other2 = 1
|
||||||
|
assert_equal "every last friday of every month", rt.recurrence_pattern
|
||||||
|
|
||||||
|
rt.recurrence_selector = 0
|
||||||
|
assert_equal "every 5 months on day 1", rt.recurrence_pattern
|
||||||
|
|
||||||
|
rt.every_other3 = 1
|
||||||
|
assert_equal "every month on day 1", rt.recurrence_pattern
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -40,6 +40,14 @@ module RecurringTodos
|
||||||
assert !rt.valid?, "missing selected days in every_day"
|
assert !rt.valid?, "missing selected days in every_day"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_pattern_text
|
||||||
|
rt = @admin.recurring_todos.where(recurring_period: 'weekly').first
|
||||||
|
assert_equal "every 2 weeks", rt.recurrence_pattern
|
||||||
|
|
||||||
|
rt.every_other1 = 1
|
||||||
|
assert_equal "weekly", rt.recurrence_pattern
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -64,6 +64,16 @@ module RecurringTodos
|
||||||
assert !rt.valid?, "should not be valid since day_of_week is empty"
|
assert !rt.valid?, "should not be valid since day_of_week is empty"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_pattern_text
|
||||||
|
rt = recurring_todos(:birthday_reinier)
|
||||||
|
assert_equal "every year on June 08", rt.recurrence_pattern
|
||||||
|
|
||||||
|
rt.recurrence_selector = 1
|
||||||
|
rt.every_count = 3
|
||||||
|
rt.every_other3 = 3
|
||||||
|
assert_equal "every year on the third wednesday of June", rt.recurrence_pattern
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue