Merge branch 'master' into new-gui

This commit is contained in:
Reinier Balt 2013-07-19 18:29:50 +02:00
commit ff1fa62d4d
17 changed files with 299 additions and 254 deletions

View file

@ -14,64 +14,74 @@ class StatsController < ApplicationController
def actions_done_last12months_data
# get actions created and completed in the past 12+3 months. +3 for running
# average
@actions_done_last12months = current_user.todos.completed_after(@cut_off_year).select("completed_at" )
@actions_created_last12months = current_user.todos.created_after(@cut_off_year).select("created_at")
@actions_done_last12monthsPlus3 = current_user.todos.completed_after(@cut_off_year_plus3).select("completed_at" )
@actions_created_last12monthsPlus3 = current_user.todos.created_after(@cut_off_year_plus3).select("created_at")
actions_done_last12months = current_user.todos.completed_after(@cut_off_year).select("completed_at" )
actions_created_last12months = current_user.todos.created_after(@cut_off_year).select("created_at")
# convert to array and fill in non-existing months
@actions_done_last12months_array = convert_to_months_from_today_array(@actions_done_last12months, 13, :completed_at)
@actions_created_last12months_array = convert_to_months_from_today_array(@actions_created_last12months, 13, :created_at)
@actions_done_last12monthsPlus3_array = convert_to_months_from_today_array(@actions_done_last12monthsPlus3, 16, :completed_at)
@actions_created_last12monthsPlus3_array = convert_to_months_from_today_array(@actions_created_last12monthsPlus3, 16, :created_at)
@actions_done_last12months_array = convert_to_months_from_today_array(actions_done_last12months, 13, :completed_at)
@actions_created_last12months_array = convert_to_months_from_today_array(actions_created_last12months, 13, :created_at)
# find max for graph in both arrays
@max = [@actions_done_last12months_array.max, @actions_created_last12months_array.max].max
# find running avg
actions_done_last12monthsPlus3 = current_user.todos.completed_after(@cut_off_year_plus3).select("completed_at" )
actions_created_last12monthsPlus3 = current_user.todos.created_after(@cut_off_year_plus3).select("created_at")
actions_done_last12monthsPlus3_array = convert_to_months_from_today_array(actions_done_last12monthsPlus3, 16, :completed_at)
actions_created_last12monthsPlus3_array = convert_to_months_from_today_array(actions_created_last12monthsPlus3, 16, :created_at)
@actions_done_avg_last12months_array, @actions_created_avg_last12months_array =
find_running_avg_array(@actions_done_last12monthsPlus3_array, @actions_created_last12monthsPlus3_array, 13)
find_running_avg_array(actions_done_last12monthsPlus3_array, actions_created_last12monthsPlus3_array, 13)
# interpolate avg for current month.
percent_of_month = Time.zone.now.day.to_f / Time.zone.now.end_of_month.day.to_f
@interpolated_actions_created_this_month = interpolate_avg(@actions_created_last12months_array, percent_of_month)
@interpolated_actions_done_this_month = interpolate_avg(@actions_done_last12months_array, percent_of_month)
interpolate_avg_for_current_month(@actions_created_last12months_array, @actions_done_last12months_array)
@created_count_array = Array.new(13, actions_created_last12months.size/12.0)
@done_count_array = Array.new(13, actions_done_last12months.size/12.0)
@month_names = Array.new(13){ |i| t('date.month_names')[ (Time.now.mon - i -1 ) % 12 + 1 ]}
render :layout => false
end
def interpolate_avg_for_current_month(created_set, done_set)
percent_of_month = Time.zone.now.day.to_f / Time.zone.now.end_of_month.day.to_f
@interpolated_actions_created_this_month = interpolate_avg(created_set, percent_of_month)
@interpolated_actions_done_this_month = interpolate_avg(done_set, percent_of_month)
end
def actions_done_last_years
@page_title = t('stats.index_title')
@chart = Stats::Chart.new('actions_done_lastyears_data', :height => 400, :width => 900)
end
def actions_done_lastyears_data
@actions_done_last_months = current_user.todos.completed.select("completed_at").reorder("completed_at DESC")
@actions_created_last_months = current_user.todos.select("created_at").reorder("created_at DESC" )
actions_done_last_months = current_user.todos.completed.select("completed_at").reorder("completed_at DESC")
actions_created_last_months = current_user.todos.select("created_at").reorder("created_at DESC" )
# query is sorted, so use last todo to calculate number of months
@month_count = [difference_in_months(@today, @actions_created_last_months.last.created_at),
difference_in_months(@today, @actions_done_last_months.last.completed_at)].max
month_count = [difference_in_months(@today, actions_created_last_months.last.created_at),
difference_in_months(@today, actions_done_last_months.last.completed_at)].max
# convert to array and fill in non-existing months
@actions_done_last_months_array = convert_to_months_from_today_array(@actions_done_last_months, @month_count+1, :completed_at)
@actions_created_last_months_array = convert_to_months_from_today_array(@actions_created_last_months, @month_count+1, :created_at)
@actions_done_last_months_array = convert_to_months_from_today_array(actions_done_last_months, month_count+1, :completed_at)
@actions_created_last_months_array = convert_to_months_from_today_array(actions_created_last_months, month_count+1, :created_at)
# find max for graph in both hashes
@max = [@actions_done_last_months_array.max, @actions_created_last_months_array.max].max
# find running avg
@actions_done_avg_last_months_array, @actions_created_avg_last_months_array =
find_running_avg_array(@actions_done_last_months_array, @actions_created_last_months_array, @month_count+1)
find_running_avg_array(@actions_done_last_months_array, @actions_created_last_months_array, month_count+1)
# correct last two months since the data of last+1 and last+2 are not available for avg
correct_last_two_months(@actions_done_avg_last_months_array, @month_count)
correct_last_two_months(@actions_created_avg_last_months_array, @month_count)
correct_last_two_months(@actions_done_avg_last_months_array, month_count)
correct_last_two_months(@actions_created_avg_last_months_array, month_count)
# interpolate avg for this month.
percent_of_month = Time.zone.now.day.to_f / Time.zone.now.end_of_month.day.to_f
@interpolated_actions_created_this_month = interpolate_avg(@actions_created_last_months_array, percent_of_month)
@interpolated_actions_done_this_month = interpolate_avg(@actions_done_last_months_array, percent_of_month)
interpolate_avg_for_current_month(@actions_created_last_months_array, @actions_done_last_months_array)
@created_count_array = Array.new(month_count+1, actions_created_last_months.size/month_count)
@done_count_array = Array.new(month_count+1, actions_done_last_months.size/month_count)
@month_names = Array.new(month_count+1){ |i| t('date.month_names')[ (Time.now.mon - i -1 ) % 12 + 1 ]+ " " + (Time.now - i.months).year.to_s}
render :layout => false
end
@ -449,7 +459,7 @@ class StatsController < ApplicationController
end
def interpolate_avg(set, percent)
return (set[0]*(1/percent) + set[1] + set[2]) / 3.0
(set[0]*(1/percent) + set[1] + set[2]) / 3.0
end
def correct_last_two_months(month_data, count)

View file

@ -118,7 +118,7 @@ module TodosHelper
:collapsible => false,
:show_empty_containers => true,
:container_name => "#{period}",
:title =>t("todos.calendar.#{period}", :month => l(Time.zone.now, :format => "%B"))
:title =>t("todos.calendar.#{period}", :month => l(Time.zone.now, :format => "%B"), :next_month => l(1.month.from_now, :format => "%B"))
}
}
end

View file

@ -23,7 +23,8 @@ class MessageGateway < ActionMailer::Base
end
end
todo = Todo.from_rich_message(user, context.id, description, notes)
todo_builder = TodoFromRichMessage.new(user, context.id, description, notes)
todo = todo_builder.construct
todo.save!
Rails.logger.info "Saved email as todo for user #{user.login} in context #{context.name}"
end

7
app/models/null_time.rb Normal file
View file

@ -0,0 +1,7 @@
class NullTime
include Comparable
def <=>(another)
-1 # any other Time object is always greater
end
end

View file

@ -24,11 +24,7 @@ class RecurringTodo < ActiveRecord::Base
end
end
validates_presence_of :description
validates_presence_of :recurring_period
validates_presence_of :target
validates_presence_of :ends_on
validates_presence_of :context
validates_presence_of :description, :recurring_period, :target, :ends_on, :context
validates_length_of :description, :maximum => 100
validates_length_of :notes, :maximum => 60000, :allow_nil => true
@ -41,7 +37,7 @@ class RecurringTodo < ActiveRecord::Base
if %W[daily weekly monthly yearly].include?(recurring_period)
self.send("validate_#{recurring_period}")
else
errors.add(:recurring_period, "is an unknown recurrence pattern: '#{self.recurring_period}'")
errors.add(:recurring_period, "is an unknown recurrence pattern: '#{recurring_period}'")
end
end
@ -70,7 +66,7 @@ class RecurringTodo < ActiveRecord::Base
errors[:base] <<"The nth day of the month may not be empty for recurrence setting" if monthly_every_xth_day.blank?
errors[:base] <<"The day of the month may not be empty for recurrence setting" if monthly_day_of_week.blank?
else
raise Exception.new, "unexpected value of recurrence selector '#{self.recurrence_selector}'"
raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'"
end
end
@ -84,13 +80,13 @@ class RecurringTodo < ActiveRecord::Base
errors[:base] << "The nth day of the month may not be empty for recurrence setting" if yearly_every_xth_day.blank?
errors[:base] << "The day of the week may not be empty for recurrence setting" if yearly_day_of_week.blank?
else
raise Exception.new, "unexpected value of recurrence selector '#{self.recurrence_selector}'"
raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'"
end
end
def starts_and_ends_on_validations
errors[:base] << "The start date needs to be filled in" if start_from.blank?
case self.ends_on
case ends_on
when 'ends_on_number_of_times'
errors[:base] << "The number of recurrences needs to be filled in for 'Ends on'" if number_of_occurences.blank?
when "ends_on_end_date"
@ -102,7 +98,7 @@ class RecurringTodo < ActiveRecord::Base
def set_recurrence_on_validations
# show always or x days before due date. x not null
case self.target
case target
when 'show_from_date'
# no validations
when 'due_date'
@ -111,7 +107,7 @@ class RecurringTodo < ActiveRecord::Base
errors[:base] << "Please fill in the number of days to show the todo before the due date" if show_from_delta.blank?
end
else
raise Exception.new, "unexpected value of recurrence target selector '#{self.recurrence_target}'"
raise Exception.new, "unexpected value of recurrence target selector '#{recurrence_target}'"
end
end
@ -142,99 +138,49 @@ class RecurringTodo < ActiveRecord::Base
def daily_selector=(selector)
case selector
when 'daily_every_x_day'
self.only_work_days = false
only_work_days = false
when 'daily_every_work_day'
self.only_work_days = true
only_work_days = true
else
raise Exception.new, "unknown daily recurrence pattern: '#{selector}'"
end
end
def daily_every_x_days=(x)
self.every_other1 = x if recurring_period=='daily'
every_other1 = x if recurring_period=='daily'
end
def daily_every_x_days
return self.every_other1
every_other1
end
# WEEKLY
def weekly_every_x_week=(x)
self.every_other1 = x if recurring_period=='weekly'
every_other1 = x if recurring_period=='weekly'
end
def weekly_every_x_week
return self.every_other1
every_other1
end
def switch_week_day (day, position)
def switch_week_day(day, position)
self.every_day = ' ' if self.every_day.nil?
self.every_day = self.every_day[0,position] + day + self.every_day[position+1,self.every_day.length]
self.every_day = every_day[0, position] + day + every_day[position+1, every_day.length]
end
def weekly_return_monday=(selector)
switch_week_day(selector,1) if recurring_period=='weekly'
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
def weekly_return_tuesday=(selector)
switch_week_day(selector,2) if recurring_period=='weekly'
end
def weekly_return_wednesday=(selector)
switch_week_day(selector,3) if recurring_period=='weekly'
end
def weekly_return_thursday=(selector)
switch_week_day(selector,4) if recurring_period=='weekly'
end
def weekly_return_friday=(selector)
switch_week_day(selector,5) if recurring_period=='weekly'
end
def weekly_return_saturday=(selector)
switch_week_day(selector,6) if recurring_period=='weekly'
end
def weekly_return_sunday=(selector)
switch_week_day(selector,0) if recurring_period=='weekly'
end
def on_xday(n)
unless self.every_day.nil?
return self.every_day[n,1] == ' ' ? false : true
else
return false
define_method("on_#{day}") do
on_xday number
end
end
def on_monday
return on_xday(1)
end
def on_tuesday
return on_xday(2)
end
def on_wednesday
return on_xday(3)
end
def on_thursday
return on_xday(4)
end
def on_friday
return on_xday(5)
end
def on_saturday
return on_xday(6)
end
def on_sunday
return on_xday(0)
def on_xday(n)
every_day && every_day[n, 1] != ' '
end
# MONTHLY
@ -248,17 +194,15 @@ class RecurringTodo < ActiveRecord::Base
end
def monthly_every_x_day
return self.every_other1
self.every_other1
end
def is_monthly_every_x_day
return self.recurrence_selector == 0 if recurring_period == 'monthly'
return false
recurring_period == 'monthly' && self.recurrence_selector == 0
end
def is_monthly_every_xth_day
return self.recurrence_selector == 1 if recurring_period == 'monthly'
return false
recurring_period == 'monthly' && self.recurrence_selector == 1
end
def monthly_every_x_month=(x)
@ -268,7 +212,7 @@ class RecurringTodo < ActiveRecord::Base
def monthly_every_x_month
# in case monthly pattern is every day x, return every_other2 otherwise
# return a default value
return self.recurrence_selector == 0 ? self.every_other2 : 1
self.recurrence_selector == 0 ? self.every_other2 : 1
end
def monthly_every_x_month2=(x)
@ -278,7 +222,7 @@ class RecurringTodo < ActiveRecord::Base
def monthly_every_x_month2
# in case monthly pattern is every xth day, return every_other2 otherwise
# return a default value
return self.recurrence_selector == 1 ? self.every_other2 : 1
self.recurrence_selector == 1 ? self.every_other2 : 1
end
def monthly_every_xth_day=(x)
@ -286,8 +230,7 @@ class RecurringTodo < ActiveRecord::Base
end
def monthly_every_xth_day(default=nil)
return self.every_other3 unless self.every_other3.nil?
return default
self.every_other3 || default
end
def monthly_day_of_week=(dow)
@ -295,7 +238,7 @@ class RecurringTodo < ActiveRecord::Base
end
def monthly_day_of_week
return self.every_count
self.every_count
end
# YEARLY
@ -311,7 +254,7 @@ class RecurringTodo < ActiveRecord::Base
def yearly_month_of_year
# if recurrence pattern is every x day in a month, return month otherwise
# return a default value
return self.recurrence_selector == 0 ? self.every_other2 : Time.zone.now.month
self.recurrence_selector == 0 ? self.every_other2 : Time.zone.now.month
end
def yearly_month_of_year2=(moy)
@ -321,7 +264,7 @@ class RecurringTodo < ActiveRecord::Base
def yearly_month_of_year2
# if recurrence pattern is every xth day in a month, return month otherwise
# return a default value
return self.recurrence_selector == 1 ? self.every_other2 : Time.zone.now.month
self.recurrence_selector == 1 ? self.every_other2 : Time.zone.now.month
end
def yearly_every_x_day=(x)
@ -329,7 +272,7 @@ class RecurringTodo < ActiveRecord::Base
end
def yearly_every_x_day
return self.every_other1
self.every_other1
end
def yearly_every_xth_day=(x)
@ -337,7 +280,7 @@ class RecurringTodo < ActiveRecord::Base
end
def yearly_every_xth_day
return self.every_other3
self.every_other3
end
def yearly_day_of_week=(dow)
@ -345,7 +288,7 @@ class RecurringTodo < ActiveRecord::Base
end
def yearly_day_of_week
return self.every_count
self.every_count
end
# target
@ -357,9 +300,9 @@ class RecurringTodo < ActiveRecord::Base
def recurring_target_as_text
case self.target
when 'due_date'
return I18n.t("todos.recurrence.pattern.due")
I18n.t("todos.recurrence.pattern.due")
when 'show_from_date'
return I18n.t("todos.recurrence.pattern.show")
I18n.t("todos.recurrence.pattern.show")
else
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
end
@ -374,19 +317,20 @@ class RecurringTodo < ActiveRecord::Base
end
def daily_recurrence_pattern
return I18n.t("todos.recurrence.pattern.on_work_days") if only_work_days
if every_other1 > 1
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.days_midsentence.other")
if only_work_days
I18n.t("todos.recurrence.pattern.on_work_days")
elsif every_other1 > 1
I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.days_midsentence.other")
else
return I18n.t("todos.recurrence.pattern.every_day")
I18n.t("todos.recurrence.pattern.every_day")
end
end
def weekly_recurrence_pattern
if every_other1 > 1
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.weeks")
I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.weeks")
else
return I18n.t('todos.recurrence.pattern.weekly')
I18n.t('todos.recurrence.pattern.weekly')
end
end
@ -395,27 +339,27 @@ class RecurringTodo < ActiveRecord::Base
if self.recurrence_selector == 0
on_day = " #{I18n.t('todos.recurrence.pattern.on_day_n', :n => self.every_other1)}"
if self.every_other2>1
return I18n.t("todos.recurrence.pattern.every_n", :n => self.every_other2) + " " + I18n.t('common.months') + on_day
I18n.t("todos.recurrence.pattern.every_n", :n => self.every_other2) + " " + I18n.t('common.months') + on_day
else
return I18n.t("todos.recurrence.pattern.every_month") + on_day
I18n.t("todos.recurrence.pattern.every_month") + on_day
end
else
if self.every_other2>1
n_months = "#{self.every_other2} #{I18n.t('common.months')}"
else
n_months = I18n.t('common.month')
end
return I18n.t('todos.recurrence.pattern.every_xth_day_of_every_n_months',
n_months = if self.every_other2 > 1
"#{self.every_other2} #{I18n.t('common.months')}"
else
I18n.t('common.month')
end
I18n.t('todos.recurrence.pattern.every_xth_day_of_every_n_months',
:x => self.xth, :day => self.day_of_week, :n_months => n_months)
end
end
def yearly_recurrence_pattern
if self.recurrence_selector == 0
return I18n.t("todos.recurrence.pattern.every_year_on",
I18n.t("todos.recurrence.pattern.every_year_on",
:date => I18n.l(DateTime.new(Time.zone.now.year, self.every_other2, self.every_other1), :format => :month_day))
else
return I18n.t("todos.recurrence.pattern.every_year_on",
I18n.t("todos.recurrence.pattern.every_year_on",
:date => I18n.t("todos.recurrence.pattern.the_xth_day_of_month", :x => self.xth, :day => self.day_of_week, :month => self.month_of_year))
end
end
@ -428,7 +372,7 @@ class RecurringTodo < ActiveRecord::Base
when 'monthly' then monthly_recurrence_pattern
when 'yearly' then yearly_recurrence_pattern
else
return 'unknown recurrence pattern: period unknown'
'unknown recurrence pattern: period unknown'
end
end
@ -436,28 +380,28 @@ class RecurringTodo < ActiveRecord::Base
xth_day = [
I18n.t('todos.recurrence.pattern.first'),I18n.t('todos.recurrence.pattern.second'),I18n.t('todos.recurrence.pattern.third'),
I18n.t('todos.recurrence.pattern.fourth'),I18n.t('todos.recurrence.pattern.last')]
return self.every_other3.nil? ? '??' : xth_day[self.every_other3-1]
self.every_other3.nil? ? '??' : xth_day[self.every_other3-1]
end
def day_of_week
return self.every_count.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[self.every_count]
self.every_count.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[self.every_count]
end
def month_of_year
return self.every_other2.nil? ? '??' : I18n.t('todos.recurrence.pattern.month_names')[self.every_other2]
self.every_other2.nil? ? '??' : I18n.t('todos.recurrence.pattern.month_names')[self.every_other2]
end
def starred?
return has_tag?(Todo::STARRED_TAG_NAME)
has_tag?(Todo::STARRED_TAG_NAME)
end
def get_due_date(previous)
case self.target
when 'due_date'
return get_next_date(previous)
get_next_date(previous)
when 'show_from_date'
# so leave due date empty
return nil
nil
else
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
end
@ -561,7 +505,7 @@ class RecurringTodo < ActiveRecord::Base
# go back to day
end
return Time.zone.local(start.year, start.month, day)
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)
@ -580,11 +524,10 @@ class RecurringTodo < ActiveRecord::Base
# 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
return the_next
the_next
else
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
end
return nil
end
def get_xth_day_of_month(x, weekday, month, year)
@ -596,7 +539,7 @@ class RecurringTodo < ActiveRecord::Base
last_day -= 1.day
end
# convert back to local timezone
return Time.zone.local(last_day.year, last_day.month, last_day.day)
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
@ -611,7 +554,7 @@ class RecurringTodo < ActiveRecord::Base
start += 1.day unless n==0
end
# convert back to local timezone
return Time.zone.local(start.year, start.month, start.day)
Time.zone.local(start.year, start.month, start.day)
end
end
@ -630,7 +573,7 @@ class RecurringTodo < ActiveRecord::Base
# if there is a next month n, stay in this year
start = Time.zone.local(start.year, month, 1)
end
return Time.zone.local(start.year, month, day)
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
@ -643,11 +586,10 @@ class RecurringTodo < ActiveRecord::Base
# year
the_next = get_xth_day_of_month(self.every_other3, self.every_count, month, start.year+1) if the_next <= start
return the_next
the_next
else
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
end
return nil
end
def continues_recurring?(previous)
@ -656,9 +598,9 @@ class RecurringTodo < ActiveRecord::Base
case self.target
when 'due_date'
return get_due_date(previous) <= self.end_date
get_due_date(previous) <= self.end_date
when 'show_from_date'
return get_show_from_date(previous) <= self.end_date
get_show_from_date(previous) <= self.end_date
else
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
end
@ -669,7 +611,7 @@ class RecurringTodo < ActiveRecord::Base
end
def toggle_completion!
return completed? ? activate! : complete!
completed? ? activate! : complete!
end
def toggle_star!
@ -706,20 +648,16 @@ class RecurringTodo < ActiveRecord::Base
# 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)
if previous.nil?
start = self.start_from.nil? ? Time.zone.now : self.start_from
# skip to present
start = Time.zone.now if Time.zone.now > start
else
start = previous + offset
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 = self.start_from if ( self.start_from && self.start_from > previous )
start > previous ? start : previous + offset
else
# skip to present
start > now ? start : now
end
return start
end
def find_first_day_in_this_week(start)
@ -727,7 +665,7 @@ class RecurringTodo < ActiveRecord::Base
start.wday().upto 6 do |i|
return start + (i-start.wday()).days unless self.every_day[i,1] == ' '
end
return -1
-1
end
end

View file

@ -381,48 +381,6 @@ class Todo < ActiveRecord::Base
end
end
# Rich Todo API
def self.from_rich_message(user, default_context_id, description, notes)
fields = description.match(/([^>@]*)@?([^>]*)>?(.*)/)
description = fields[1].strip
context = fields[2].strip
project = fields[3].strip
context = nil if context == ""
project = nil if project == ""
context_id = default_context_id
unless(context.nil?)
found_context = user.contexts.active.where("name like ?", "%#{context}%").first
found_context = user.contexts.where("name like ?", "%#{context}%").first if !found_context
context_id = found_context.id if found_context
end
unless user.contexts.exists? context_id
raise(CannotAccessContext, "Cannot access a context that does not belong to this user.")
end
project_id = nil
unless(project.blank?)
if(project[0..3].downcase == "new:")
found_project = user.projects.build
found_project.name = project[4..255+4].strip
found_project.save!
else
found_project = user.projects.active.find_by_namepart(project)
found_project = user.projects.find_by_namepart(project) if found_project.nil?
end
project_id = found_project.id unless found_project.nil?
end
todo = user.todos.build
todo.description = description
todo.raw_notes = notes
todo.context_id = context_id
todo.project_id = project_id unless project_id.nil?
return todo
end
def render_note
unless self.notes.nil?
self.rendered_notes = Tracks::Utils.render_text(self.notes)

View file

@ -0,0 +1,28 @@
class RichMessageExtractor
RICH_MESSAGE_FIELDS_REGEX = /([^>@]*)@?([^>]*)>?(.*)/
def initialize(message)
@message = message
end
def description
fields[1].strip
end
def context
fields[2].strip
end
def project
stripped = fields[3].strip
stripped.blank? ? nil : stripped
end
private
def fields
@message.match(RICH_MESSAGE_FIELDS_REGEX)
end
end

View file

@ -0,0 +1,49 @@
class TodoFromRichMessage
attr_reader :user, :default_context_id, :description, :notes
def initialize(user, default_context_id, description, notes)
@user = user
@default_context_id = default_context_id
@description = description
@notes = notes
end
def construct
extractor = RichMessageExtractor.new(description)
description = extractor.description
context = extractor.context
project = extractor.project
context_id = default_context_id
unless context.blank?
found_context = user.contexts.active.where("name like ?", "%#{context}%").first
found_context = user.contexts.where("name like ?", "%#{context}%").first if !found_context
context_id = found_context.id if found_context
end
unless user.context_ids.include? context_id
raise(CannotAccessContext, "Cannot access a context that does not belong to this user.")
end
project_id = nil
unless project.blank?
if project[0..3].downcase == "new:"
found_project = user.projects.build
found_project.name = project[4..259].strip
found_project.save!
else
found_project = user.projects.active.find_by_namepart(project)
found_project = user.projects.find_by_namepart(project) if found_project.nil?
end
project_id = found_project.id unless found_project.nil?
end
todo = user.todos.build
todo.description = description
todo.raw_notes = notes
todo.context_id = context_id
todo.project_id = project_id unless project_id.nil?
todo
end
end

View file

@ -1,8 +1,5 @@
<%-
url_array = Array.new(13){ |i| url_for :controller => 'stats', :action => 'actions_done_last_years'}
created_count_array = Array.new(13){ |i| @actions_created_last12months.size/12.0 }
done_count_array = Array.new(13){ |i| @actions_done_last12months.size/12.0 }
month_names = Array.new(13){ |i| t('date.month_names')[ (Time.now.mon - i -1 ) % 12 + 1 ]}
<%-
url = url_for :controller => 'stats', :action => 'actions_done_last_years'
-%>
&title=<%= t('stats.actions_lastyear_title') %>,{font-size:16},&
&y_legend=<%= t('stats.legend.number_of_actions') %>,12,0x736AFF&
@ -17,18 +14,18 @@ month_names = Array.new(13){ |i| t('date.month_names')[ (Time.now.mon -
&line_7=1,0xAA0000&
&line_8=1,0x007700&
&values=<%= @actions_created_last12months_array.join(",")%>&
&links=<%= url_array.join(",")%>&
&links_2=<%= url_array.join(",")%>&
&links=<%= Array.new(13,url).join(",") %>&
&links_2=<%= Array.new(13,url).join(",") %>&
&values_2=<%= @actions_done_last12months_array.join(",")%>&
&values_3=<%= created_count_array.join(",")%>&
&values_4=<%= done_count_array.join(",")%>&
&values_3=<%= @created_count_array.join(",")%>&
&values_4=<%= @done_count_array.join(",")%>&
&values_5=<%= @actions_created_avg_last12months_array.join(",")%>&
&values_6=<%= @actions_done_avg_last12months_array.join(",")%>&
&values_7=<%= @interpolated_actions_created_this_month%>,<%=@actions_done_avg_last12months_array[1]%>&
&values_8=<%= @interpolated_actions_done_this_month%>,<%=@actions_created_avg_last12months_array[1]%>&
&x_labels=<%= month_names.join(",")%>&
&x_labels=<%= @month_names.join(",")%>&
&y_min=0&
<% # add one to @max for people who have no actions completed yet.
# OpenFlashChart cannot handle y_max=0 -%>
&y_max=<%=@max+@max/10+1-%>&
&x_label_style=9,,2,&
&x_label_style=9,,2,&

View file

@ -1,8 +1,3 @@
<%-
created_count_array = Array.new(@month_count+1){ |i| @actions_created_last_months.size/@month_count }
done_count_array = Array.new(@month_count+1){ |i| @actions_done_last_months.size/@month_count }
month_names = Array.new(@month_count+1){ |i| t('date.month_names')[ (Time.now.mon - i -1 ) % 12 + 1 ]+ " " + (Time.now - i.months).year.to_s}
-%>
&title=<%= t('stats.actions_last_year') %>,{font-size:16},&
&y_legend=<%= t('stats.actions_last_year_legend.number_of_actions') %>,12,0x736AFF&
&x_legend=<%= t('stats.actions_last_year_legend.months_ago') %>,12,0x736AFF&
@ -17,15 +12,15 @@ month_names = Array.new(@month_count+1){ |i| t('date.month_names')[ (Tim
&line_8=1,0x007700&
&values=<%= @actions_created_last_months_array.join(",")%>&
&values_2=<%= @actions_done_last_months_array.join(",")%>&
&values_3=<%= created_count_array.join(",")%>&
&values_4=<%= done_count_array.join(",")%>&
&values_3=<%= @created_count_array.join(",")%>&
&values_4=<%= @done_count_array.join(",")%>&
&values_5=<%= @actions_created_avg_last_months_array.join(",")%>&
&values_6=<%= @actions_done_avg_last_months_array.join(",")%>&
&values_7=<%= @interpolated_actions_created_this_month%>,<%=@actions_done_avg_last_months_array[1]%>&
&values_8=<%= @interpolated_actions_done_this_month%>,<%=@actions_created_avg_last_months_array[1]%>&
&x_labels=<%= month_names.join(",")%>&
&x_labels=<%= @month_names.join(",")%>&
&y_min=0&
<% # add one to @max for people who have no actions completed yet.
# OpenFlashChart cannot handle y_max=0 -%>
&y_max=<%=@max+@max/10+1-%>&
&x_label_style=9,,2,&
&x_label_style=9,,2,&

View file

@ -59,7 +59,7 @@ module Tracksapp
config.assets.paths << Rails.root.join("app", "assets", "swfs")
# add print and login css to assets
config.assets.precompile += %w(login.css print.css)
config.assets.precompile += %w(login.css print.css mobile.css)
# configure Tracks to handle deployment in a subdir
config.action_controller.relative_url_root = SITE_CONFIG['subdir'] if SITE_CONFIG['subdir']

View file

@ -593,7 +593,7 @@ en:
due_this_week: Due in rest of this week
due_next_week: Due next week
due_this_month: Due in rest of %{month}
due_after_this_month: Due in %{month} and later
due_after_this_month: Due in %{next_month} and later
show_tomorrow: Show Tomorrow
tagged_page_title: "TRACKS::Tagged with '%{tag_name}'"
action_deferred: "The action '%{description}' was deferred"
@ -653,7 +653,7 @@ en:
daily: Daily
yearly_every_x_day: Every %{month} %{day}
recurrence_on:
options: Use the calculated date to
options: Use the calculated date to
due_date: set the actions due date
show_options: Show the action
show_always: always
@ -1022,4 +1022,4 @@ en:
notes_matching_query: Notes matching query
no_results: Your search yielded no results.
todos_matching_query: Todos matching query
projects_matching_query: Projects matching query
projects_matching_query: Projects matching query

View file

@ -194,7 +194,7 @@ nl:
- Vr
- Za
abbr_month_names:
-
-
- Jan
- Feb
- Maa
@ -223,7 +223,7 @@ nl:
only_day: '%e'
short: '%e %b'
month_names:
-
-
- Januari
- Februari
- Maart
@ -819,7 +819,7 @@ nl:
due_this_week: Deadline in rest van deze week
due_today: Deadline vandaag
get_in_ical_format: Ontvang deze agenda in iCal-formaat
due_after_this_month: Deadline in %{month} en later
due_after_this_month: Deadline in %{next_month} en later
calendar_page_title: TRACKS::Agenda
cannot_add_dependency_to_completed_todo: Kan deze actie niet als een afhankelijkheid
van een voltooide actie toevoegen!
@ -957,7 +957,7 @@ nl:
from: vanaf
last: laatste
month_names:
-
-
- januari
- februari
- maart

View file

@ -109,9 +109,9 @@ class StatsControllerTest < ActionController::TestCase
assert_response :success
# Then the todos for the chart should be retrieved
assert_not_nil assigns['actions_done_last12months']
assert_not_nil assigns['actions_created_last12months']
assert_equal 7, assigns['actions_created_last12months'].count, "very old todo should not be retrieved"
#assert_not_nil assigns['actions_done_last12months']
#assert_not_nil assigns['actions_created_last12months']
#assert_equal 7, assigns['actions_created_last12months'].count, "very old todo should not be retrieved"
# And they should be totalled in a hash
assert_equal 2, assigns['actions_created_last12months_array'][0], "there should be two todos in current month"
@ -172,9 +172,6 @@ class StatsControllerTest < ActionController::TestCase
# only tests difference with actions_done_last_12months_data
# Then the count of months should be calculated
assert_equal 27, assigns['month_count'], "two years and three months of last todo"
# And the last two months are corrected
assert_equal 2/3.0, assigns['actions_done_avg_last_months_array'][23]
assert_equal 2/3.0, assigns['actions_done_avg_last_months_array'][24]

View file

@ -0,0 +1,55 @@
require 'test/unit'
require 'active_support/core_ext/object/blank'
require_relative '../../app/services/rich_message_extractor.rb'
class RichMessageExtractorTest < Test::Unit::TestCase
def test_message_with_all_options
message = "ohai@some-context>in-this-project"
extractor = RichMessageExtractor.new(message)
assert_equal "ohai", extractor.description
assert_equal "some-context", extractor.context
assert_equal "in-this-project", extractor.project
end
def test_message_without_project
message = "ohai @ some-context"
extractor = RichMessageExtractor.new(message)
assert_equal "ohai", extractor.description
assert_equal "some-context", extractor.context
assert_equal nil, extractor.project
end
def test_message_without_project
message = " ohai @ some-context"
extractor = RichMessageExtractor.new(message)
assert_equal "ohai", extractor.description
assert_equal "some-context", extractor.context
assert_equal nil, extractor.project
end
def test_message_without_project_or_context
message = "ohai"
extractor = RichMessageExtractor.new(message)
assert_equal "ohai", extractor.description
assert_equal "", extractor.context
assert_equal nil, extractor.project
end
def test_message_without_anything
message = ""
extractor = RichMessageExtractor.new(message)
assert_equal "", extractor.description
assert_equal "", extractor.context
assert_equal nil, extractor.project
end
def test_message_with_just_a_context
message = "@some-context"
extractor = RichMessageExtractor.new(message)
assert_equal "", extractor.description
assert_equal "some-context", extractor.context
assert_equal nil, extractor.project
end
end

View file

@ -0,0 +1,21 @@
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
class TodoFromRichMessageTest < ActiveSupport::TestCase
def setup
@completed = Todo.find(8)
end
def test_from_rich_message_adds_to_default_context
user = @completed.user
default_context_id = @completed.context_id
builder = TodoFromRichMessage.new(user, default_context_id, "new todo", "notes")
new_todo = builder.construct
assert_not_nil new_todo
assert_equal "new todo", new_todo.description
assert_equal "notes", new_todo.notes
assert_equal default_context_id, new_todo.context_id
end
end

View file

@ -477,15 +477,4 @@ class TodoTest < ActiveSupport::TestCase
assert_equal "<p><strong>test</strong></p>", todo.rendered_notes
end
def test_from_rich_message_adds_to_default_context
user = @completed.user
default_context_id = @completed.context_id
new_todo = Todo::from_rich_message(user, default_context_id, "new todo", "notes")
assert_not_nil new_todo
assert_equal "new todo", new_todo.description
assert_equal "notes", new_todo.notes
assert_equal default_context_id, new_todo.context_id
end
end