From 0673d67b620f59c502aa3dbbf232c18274bcc199 Mon Sep 17 00:00:00 2001 From: Walter Cruz Date: Tue, 23 Sep 2008 11:53:11 -0300 Subject: [PATCH 01/23] Adding sort by number of open tasks on each project --- app/views/projects/actionize.js.rjs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 app/views/projects/actionize.js.rjs diff --git a/app/views/projects/actionize.js.rjs b/app/views/projects/actionize.js.rjs new file mode 100644 index 00000000..ccb10451 --- /dev/null +++ b/app/views/projects/actionize.js.rjs @@ -0,0 +1,6 @@ +list_id = "list-#{@state}-projects" +page.replace_html list_id, + :partial => 'project_listing', + :collection => @projects +page.sortable list_id, get_listing_sortable_options(list_id) + \ No newline at end of file From ee5c730f44bc12e1d25ccd7b786e3018b84dee63 Mon Sep 17 00:00:00 2001 From: Walter Cruz Date: Tue, 23 Sep 2008 17:06:14 -0300 Subject: [PATCH 02/23] Ading a sort by number of tasks option --- app/controllers/projects_controller.rb | 7 +++++++ app/models/user.rb | 6 ++++++ app/views/projects/_project_state_group.rhtml | 14 +++++++++++++- config/routes.rb | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index bf84e44d..ed2128de 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -186,6 +186,13 @@ class ProjectsController < ApplicationController init_not_done_counts(['project']) end + def actionize + @state = params['state'] + @projects = current_user.projects.actionize(:state => @state) if @state + @contexts = current_user.contexts + init_not_done_counts(['project']) + end + protected def render_projects_html diff --git a/app/models/user.rb b/app/models/user.rb index bf84bbd3..4b1f41e7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -51,6 +51,12 @@ class User < ActiveRecord::Base self.update_positions(projects.map{ |p| p.id }) return projects end + def actionize(scope_conditions = {}) + projects = find(:all, :conditions => scope_conditions) + projects.sort!{ |x,y| y.todos.count(:state == 'active') <=> x.todos.count(:state == 'active') } + self.update_positions(projects.map{ |p| p.id }) + return projects + end end has_many :active_projects, :class_name => 'Project', diff --git a/app/views/projects/_project_state_group.rhtml b/app/views/projects/_project_state_group.rhtml index 6fe6ef21..c4a0f0b1 100644 --- a/app/views/projects/_project_state_group.rhtml +++ b/app/views/projects/_project_state_group.rhtml @@ -11,9 +11,21 @@ end end %> + +
+ <%= link_to("Sort By number of tasks", actionize_projects_path(:state => state), + :class => "actionize_link", :title => "Sort these projects by number of tasks") %> + <% apply_behavior '.actionize_link:click', :prevent_default => true do |page, element| + page.confirming 'Are you sure that you want to sort these projects by the number of tasks? This will replace the existing sort order.' do + page << "tasksSort = this.up('.tasks_sort'); + tasksSort.startWaiting();" + page << remote_to_href(:complete => "tasksSort.stopWaiting()") + end + end + %>
<%= render :partial => 'project_listing', :collection => project_state_group %>
<%= sortable_element "list-#{state}-projects", get_listing_sortable_options("list-#{state}-projects") %> - \ No newline at end of file + diff --git a/config/routes.rb b/config/routes.rb index a4eb29e3..4c1e69a6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,6 +28,10 @@ ActionController::Routing::Routes.draw do |map| projects.resources :todos, :name_prefix => "project_" end + map.resources :projects, :collection => {:order => :post, :actionize => :post} do |projects| + projects.resources :todos, :name_prefix => "project_" + end + map.resources :todos, :member => {:toggle_check => :put, :toggle_star => :put}, :collection => {:check_deferred => :post, :filter_to_context => :post, :filter_to_project => :post} From 1118a582d7bd18c471821126c49bf8b6e000c76d Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Tue, 7 Oct 2008 20:20:47 +0200 Subject: [PATCH 03/23] add check for actions that should move from tickler to home page to the index page for todos --- app/controllers/todos_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index ada43168..2bf99d63 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -10,6 +10,7 @@ class TodosController < ApplicationController session :off, :only => :index, :if => Proc.new { |req| is_feed_request(req) } def index + current_user.deferred_todos.find_and_activate_ready @projects = current_user.projects.find(:all, :include => [:default_context]) @contexts = current_user.contexts.find(:all) From 24c2b57b4af66176b218f24394421cf4bf8b73f7 Mon Sep 17 00:00:00 2001 From: Eric Allen Date: Sat, 4 Oct 2008 13:41:37 -0400 Subject: [PATCH 04/23] Make migration 42 more resilient: don't freak out when user has no prefs object --- db/migrate/042_change_dates_to_datetimes.rb | 31 +++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/db/migrate/042_change_dates_to_datetimes.rb b/db/migrate/042_change_dates_to_datetimes.rb index 99e75179..57b62ed5 100644 --- a/db/migrate/042_change_dates_to_datetimes.rb +++ b/db/migrate/042_change_dates_to_datetimes.rb @@ -6,18 +6,33 @@ class ChangeDatesToDatetimes < ActiveRecord::Migration change_column :recurring_todos, :end_date, :datetime User.all(:include => [:todos, :recurring_todos]).each do |user| - user.todos.each do |todo| - todo.update_attribute(:show_from, user.at_midnight(todo.show_from)) unless todo.show_from.nil? - todo.update_attribute(:due, user.at_midnight(todo.due)) unless todo.due.nil? - end + if user.prefs + user.todos.each do |todo| + todo.update_attribute(:show_from, user.at_midnight(todo.show_from)) unless todo.show_from.nil? + todo.update_attribute(:due, user.at_midnight(todo.due)) unless todo.due.nil? + end - user.recurring_todos.each do |todo| - todo.update_attribute(:start_from, user.at_midnight(todo.start_from)) unless todo.start_from.nil? - todo.update_attribute(:end_date, user.at_midnight(todo.end_date)) unless todo.end_date.nil? - end + user.recurring_todos.each do |todo| + todo.update_attribute(:start_from, user.at_midnight(todo.start_from)) unless todo.start_from.nil? + todo.update_attribute(:end_date, user.at_midnight(todo.end_date)) unless todo.end_date.nil? + end + else # weird...no preferences for this user + user.todos.each do |todo| + todo.update_attribute(:show_from, at_midnight(todo.show_from)) unless todo.show_from.nil? + todo.update_attribute(:due, at_midnight(todo.due)) unless todo.due.nil? + end + + user.recurring_todos.each do |todo| + todo.update_attribute(:start_from, at_midnight(todo.start_from)) unless todo.start_from.nil? + todo.update_attribute(:end_date, at_midnight(todo.end_date)) unless todo.end_date.nil? + end end end + def at_midnight(date) + return Time.zone.local(date.year, date.month, date.day, 0, 0, 0) + end + def self.down change_column :todos, :show_from, :date change_column :todos, :due, :date From 51b0a2bd9bc3dd310e39ea3e7265850dc83b9570 Mon Sep 17 00:00:00 2001 From: Eric Allen Date: Sat, 4 Oct 2008 13:44:38 -0400 Subject: [PATCH 05/23] Informative alt text for defer buttons --- app/helpers/todos_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index b989b432..aee59c90 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -281,7 +281,7 @@ module TodosHelper end def defer_link(days) - link_to_remote image_tag("defer_#{days}.png"), :url => {:controller => 'todos', :action => 'defer', :id => @todo.id, :days => days, :_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")} + link_to_remote image_tag("defer_#{days}.png", :alt => "Defer #{pluralize(days, 'day')}"), :url => {:controller => 'todos', :action => 'defer', :id => @todo.id, :days => days, :_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")} end end From b36ed968137183bb24b62c23236be9119da17547 Mon Sep 17 00:00:00 2001 From: Eric Allen Date: Wed, 8 Oct 2008 23:47:59 -0400 Subject: [PATCH 06/23] Wow I broke the migration. Oops. --- db/migrate/042_change_dates_to_datetimes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/migrate/042_change_dates_to_datetimes.rb b/db/migrate/042_change_dates_to_datetimes.rb index 57b62ed5..1ea940b0 100644 --- a/db/migrate/042_change_dates_to_datetimes.rb +++ b/db/migrate/042_change_dates_to_datetimes.rb @@ -26,6 +26,7 @@ class ChangeDatesToDatetimes < ActiveRecord::Migration todo.update_attribute(:start_from, at_midnight(todo.start_from)) unless todo.start_from.nil? todo.update_attribute(:end_date, at_midnight(todo.end_date)) unless todo.end_date.nil? end + end end end From a9a02896a021b652904990db3a60156193031e64 Mon Sep 17 00:00:00 2001 From: Eric Allen Date: Thu, 9 Oct 2008 10:14:26 -0400 Subject: [PATCH 07/23] Another shot at fixing migration 42. Actually tested to work. --- db/migrate/042_change_dates_to_datetimes.rb | 42 ++++++++++----------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/db/migrate/042_change_dates_to_datetimes.rb b/db/migrate/042_change_dates_to_datetimes.rb index 1ea940b0..d534c642 100644 --- a/db/migrate/042_change_dates_to_datetimes.rb +++ b/db/migrate/042_change_dates_to_datetimes.rb @@ -6,32 +6,28 @@ class ChangeDatesToDatetimes < ActiveRecord::Migration change_column :recurring_todos, :end_date, :datetime User.all(:include => [:todos, :recurring_todos]).each do |user| - if user.prefs - user.todos.each do |todo| - todo.update_attribute(:show_from, user.at_midnight(todo.show_from)) unless todo.show_from.nil? - todo.update_attribute(:due, user.at_midnight(todo.due)) unless todo.due.nil? - end - - user.recurring_todos.each do |todo| - todo.update_attribute(:start_from, user.at_midnight(todo.start_from)) unless todo.start_from.nil? - todo.update_attribute(:end_date, user.at_midnight(todo.end_date)) unless todo.end_date.nil? - end - else # weird...no preferences for this user - user.todos.each do |todo| - todo.update_attribute(:show_from, at_midnight(todo.show_from)) unless todo.show_from.nil? - todo.update_attribute(:due, at_midnight(todo.due)) unless todo.due.nil? - end - - user.recurring_todos.each do |todo| - todo.update_attribute(:start_from, at_midnight(todo.start_from)) unless todo.start_from.nil? - todo.update_attribute(:end_date, at_midnight(todo.end_date)) unless todo.end_date.nil? + if !user.prefs ## ugly hack for strange edge-case of not having preferences object + user.instance_eval do + def at_midnight(date) + return Time.zone.local(date.year, date.month, date.day, 0, 0, 0) + end + def time + Time.zone.now + end end end - end - end + user.todos.each do |todo| + todo[:show_from] = user.at_midnight(todo.show_from) unless todo.show_from.nil? + todo[:due] = user.at_midnight(todo.due) unless todo.due.nil? + todo.save_with_validation(false) + end - def at_midnight(date) - return Time.zone.local(date.year, date.month, date.day, 0, 0, 0) + user.recurring_todos.each do |todo| + todo[:start_from] = user.at_midnight(todo.start_from) unless todo.start_from.nil? + todo[:end_date] = user.at_midnight(todo.end_date) unless todo.end_date.nil? + todo.save_with_validation(false) + end + end end def self.down From c64bbb9e60defa4a96b33f1d8dc230870805efe8 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Tue, 14 Oct 2008 10:57:12 +0200 Subject: [PATCH 08/23] fix corner case for yearly repeating todos also adds test for this case --- app/models/recurring_todo.rb | 11 ++++++----- test/unit/recurring_todo_test.rb | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/models/recurring_todo.rb b/app/models/recurring_todo.rb index 86b24dae..0b103b03 100644 --- a/app/models/recurring_todo.rb +++ b/app/models/recurring_todo.rb @@ -517,17 +517,18 @@ class RecurringTodo < ActiveRecord::Base 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 there is no next month n in this year, search in next year - if start.month >= month - start = Time.zone.local(start.year+1, month, 1) if start.day >= day - start = Time.zone.local(start.year, month, 1) if start.day <= day + 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 return Time.zone.local(start.year, month, day) diff --git a/test/unit/recurring_todo_test.rb b/test/unit/recurring_todo_test.rb index bd256eba..82ba1cb5 100644 --- a/test/unit/recurring_todo_test.rb +++ b/test/unit/recurring_todo_test.rb @@ -174,6 +174,9 @@ class RecurringTodoTest < Test::Rails::TestCase # same month, day after due_date = @yearly.get_due_date(@monday) # june 9th assert_equal Time.zone.local(2009,6,8), due_date # june 8th next year + # very overdue + due_date = @yearly.get_due_date(@monday+5.months-2.days) # november 7 + assert_equal Time.zone.local(2009,6,8), due_date # june 8th next year @yearly.recurrence_selector = 1 @yearly.every_other3 = 2 # second From ce671f23f481093566dec6fa4ea430f6a47943ce Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Tue, 14 Oct 2008 22:49:17 +0200 Subject: [PATCH 09/23] fix bug introduced by last commit and add test for it also refactor check_for_next_todo a bit to depend less on globals --- app/controllers/todos_controller.rb | 23 +++--- app/models/recurring_todo.rb | 2 +- test/fixtures/todos.yml | 14 ++++ .../recurring_todos_controller_test.rb | 7 +- test/functional/stats_controller_test.rb | 2 +- test/functional/todos_controller_test.rb | 77 +++++++++++++++---- test/unit/context_test.rb | 6 +- test/unit/project_test.rb | 4 +- 8 files changed, 99 insertions(+), 36 deletions(-) diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 2bf99d63..b4cd1935 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -134,7 +134,7 @@ class TodosController < ApplicationController @saved = @todo.toggle_completion! # check if this todo has a related recurring_todo. If so, create next todo - check_for_next_todo if @saved + @new_recurring_todo = check_for_next_todo(@todo) if @saved respond_to do |format| format.js do @@ -279,7 +279,7 @@ class TodosController < ApplicationController @project_id = @todo.project_id # check if this todo has a related recurring_todo. If so, create next todo - check_for_next_todo + @new_recurring_todo = check_for_next_todo(@todo) @saved = @todo.destroy @@ -771,22 +771,22 @@ class TodosController < ApplicationController ['rss','atom','txt','ics'].include?(req.parameters[:format]) end - def check_for_next_todo + def check_for_next_todo(todo) # check if this todo has a related recurring_todo. If so, create next todo - @new_recurring_todo = nil - @recurring_todo = nil - if @todo.from_recurring_todo? - @recurring_todo = current_user.recurring_todos.find(@todo.recurring_todo_id) + new_recurring_todo = nil + recurring_todo = nil + if todo.from_recurring_todo? + recurring_todo = current_user.recurring_todos.find(todo.recurring_todo_id) # check for next todo either from the due date or the show_from date - date_to_check = @todo.due.nil? ? @todo.show_from : @todo.due + date_to_check = todo.due.nil? ? todo.show_from : todo.due # if both due and show_from are nil, check for a next todo with yesterday # as reference point. We pick yesterday so that new todos for today will # be created instead of new todos for tomorrow. date_to_check = Time.zone.now-1.day if date_to_check.nil? - if @recurring_todo.active? && @recurring_todo.has_next_todo(date_to_check) + if recurring_todo.active? && recurring_todo.has_next_todo(date_to_check) # shift the reference date to yesterday if date_to_check is furher in # the past. This is to make sure we do not get older todos for overdue @@ -795,9 +795,10 @@ class TodosController < ApplicationController # date. Discard the time part in the compare date = date_to_check.at_midnight >= Time.zone.now.at_midnight ? date_to_check : Time.zone.now-1.day - @new_recurring_todo = create_todo_from_recurring_todo(@recurring_todo, date) + new_recurring_todo = create_todo_from_recurring_todo(recurring_todo, date) end - end + end + return new_recurring_todo end def get_due_id_for_calendar(due) diff --git a/app/models/recurring_todo.rb b/app/models/recurring_todo.rb index 0b103b03..fbe2f07a 100644 --- a/app/models/recurring_todo.rb +++ b/app/models/recurring_todo.rb @@ -523,7 +523,7 @@ class RecurringTodo < ActiveRecord::Base case self.recurrence_selector when 0 # specific day of a specific month - if start.month > month || (start.month == month && start.day > day) + 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 diff --git a/test/fixtures/todos.yml b/test/fixtures/todos.yml index 52db71ea..f4941c0c 100644 --- a/test/fixtures/todos.yml +++ b/test/fixtures/todos.yml @@ -230,3 +230,17 @@ end completed_at: ~ show_from: <%= next_week %> user_id: 2 + +18: + id: 18 + user_id: 1 + context_id: 1 + project_id: 2 + description: Call Bill Gates every day + notes: ~ + state: active + created_at: <%= last_week %> + due: <%= last_week %> + completed_at: ~ + show_from: ~ + recurring_todo_id: 1 \ No newline at end of file diff --git a/test/functional/recurring_todos_controller_test.rb b/test/functional/recurring_todos_controller_test.rb index e9485c5b..150bce59 100644 --- a/test/functional/recurring_todos_controller_test.rb +++ b/test/functional/recurring_todos_controller_test.rb @@ -79,6 +79,11 @@ class RecurringTodosControllerTest < ActionController::TestCase recurring_todo_1 = RecurringTodo.find(1) assert recurring_todo_1.completed? + # remove remaining todo + todo = Todo.find_by_recurring_todo_id(1) + todo.recurring_todo_id = 2 + todo.save + todo_count = Todo.count # mark as active @@ -119,5 +124,5 @@ class RecurringTodosControllerTest < ActionController::TestCase # show_from should be nil since now+4.days-10.days is in the past assert_equal nil, new_todo.show_from end - + end diff --git a/test/functional/stats_controller_test.rb b/test/functional/stats_controller_test.rb index a3cf9645..f3f2c64a 100755 --- a/test/functional/stats_controller_test.rb +++ b/test/functional/stats_controller_test.rb @@ -56,7 +56,7 @@ class StatsControllerTest < Test::Unit::TestCase assert_equal 3, assigns['projects'].count assert_equal 3, assigns['projects'].count(:conditions => "state = 'active'") assert_equal 10, assigns['contexts'].count - assert_equal 15, assigns['actions'].count + assert_equal 16, assigns['actions'].count assert_equal 4, assigns['tags'].count assert_equal 2, assigns['unique_tags'].size assert_equal 2.week.ago.utc.beginning_of_day, assigns['first_action'].created_at diff --git a/test/functional/todos_controller_test.rb b/test/functional/todos_controller_test.rb index cd4fba01..0b5d8b1a 100644 --- a/test/functional/todos_controller_test.rb +++ b/test/functional/todos_controller_test.rb @@ -124,9 +124,9 @@ class TodosControllerTest < Test::Rails::TestCase def test_update_todo_to_deferred_is_reflected_in_badge_count login_as(:admin_user) get :index - assert_equal 10, assigns['count'] + assert_equal 11, assigns['count'] xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Make more money than Billy Gates", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006", "show_from"=>"30/11/2030"}, "tag_list"=>"foo bar" - assert_equal 9, assigns['down_count'] + assert_equal 10, assigns['down_count'] end def test_update_todo @@ -188,7 +188,7 @@ class TodosControllerTest < Test::Rails::TestCase assert_select '>description', "Actions for #{users(:admin_user).display_name}" assert_select 'language', 'en-us' assert_select 'ttl', '40' - assert_select 'item', 10 do + assert_select 'item', 11 do assert_select 'title', /.+/ assert_select 'description', /.*/ assert_select 'link', %r{http://test.host/contexts/.+} @@ -242,7 +242,7 @@ class TodosControllerTest < Test::Rails::TestCase assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do assert_xml_select '>title', 'Tracks Actions' assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}" - assert_xml_select 'entry', 10 do + assert_xml_select 'entry', 11 do assert_xml_select 'title', /.+/ assert_xml_select 'content[type="html"]', /.*/ assert_xml_select 'published', /(#{Regexp.escape(projects(:timemachine).updated_at.xmlschema)}|#{Regexp.escape(projects(:moremoney).updated_at.xmlschema)})/ @@ -311,7 +311,7 @@ class TodosControllerTest < Test::Rails::TestCase def test_mobile_index_assigns_down_count login_as(:admin_user) get :index, { :format => "m" } - assert_equal 10, assigns['down_count'] + assert_equal 11, assigns['down_count'] end def test_mobile_create_action_creates_a_new_todo @@ -362,38 +362,81 @@ class TodosControllerTest < Test::Rails::TestCase # link todo_1 and recurring_todo_1 recurring_todo_1 = RecurringTodo.find(1) - todo_1 = Todo.find(1) - todo_1.recurring_todo_id = recurring_todo_1.id - - # update todo_1 - assert todo_1.save + todo_1 = Todo.find_by_recurring_todo_id(1) # mark todo_1 as complete by toggle_check - xhr :post, :toggle_check, :id => 1, :_source_view => 'todo' + xhr :post, :toggle_check, :id => todo_1.id, :_source_view => 'todo' todo_1.reload assert todo_1.completed? + # check that there is only one active todo belonging to recurring_todo + count = Todo.count(:all, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'}) + assert_equal 1, count + # check there is a new todo linked to the recurring pattern next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'}) assert_equal "Call Bill Gates every day", next_todo.description + # check that the new todo is not the same as todo_1 + assert_not_equal todo_1.id, next_todo.id - # change recurrence pattern to weekly and set show_from 2 days befor due date - # this forces the next todo to be put in the tickler + # change recurrence pattern to monthly and set show_from 2 days before due + # date this forces the next todo to be put in the tickler recurring_todo_1.show_from_delta = 2 - recurring_todo_1.recurring_period = 'weekly' - recurring_todo_1.every_day = 'smtwtfs' + recurring_todo_1.recurring_period = 'monthly' + recurring_todo_1.recurrence_selector = 0 + recurring_todo_1.every_other1 = 1 + recurring_todo_1.every_other2 = 2 + recurring_todo_1.every_other3 = 5 recurring_todo_1.save - + # mark next_todo as complete by toggle_check xhr :post, :toggle_check, :id => next_todo.id, :_source_view => 'todo' next_todo.reload assert next_todo.completed? + # check that there are three todos belonging to recurring_todo: two + # completed and one deferred + count = Todo.count(:all, :conditions => {:recurring_todo_id => recurring_todo_1.id}) + assert_equal 3, count + # check there is a new todo linked to the recurring pattern in the tickler next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'}) + assert !next_todo.nil? assert_equal "Call Bill Gates every day", next_todo.description # check that the todo is in the tickler assert !next_todo.show_from.nil? end - + + def test_check_for_next_todo + login_as :admin_user + + recurring_todo_1 = RecurringTodo.find(5) + @todo = Todo.find_by_recurring_todo_id(1) + assert @todo.from_recurring_todo? + # rewire @todo to yearly recurring todo + @todo.recurring_todo_id = 5 + + # make todo due tomorrow and change recurring date also to tomorrow + @todo.due = Time.zone.now + 1.day + @todo.save + recurring_todo_1.every_other1 = @todo.due.day + recurring_todo_1.every_other2 = @todo.due.month + recurring_todo_1.save + + # mark todo complete + xhr :post, :toggle_check, :id => @todo.id, :_source_view => 'todo' + @todo.reload + assert @todo.completed? + + # check that there is no active todo + next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'}) + assert next_todo.nil? + + # check for new deferred todo + next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'}) + assert !next_todo.nil? + # check that the due date of the new todo is later than tomorrow + assert next_todo.due > @todo.due + end + end diff --git a/test/unit/context_test.rb b/test/unit/context_test.rb index 4952d8c6..121a7c29 100644 --- a/test/unit/context_test.rb +++ b/test/unit/context_test.rb @@ -53,7 +53,7 @@ class ContextTest < Test::Rails::TestCase end def test_delete_context_deletes_todos_within_it - assert_equal 6, @agenda.todos.count + assert_equal 7, @agenda.todos.count agenda_todo_ids = @agenda.todos.collect{|t| t.id } @agenda.destroy agenda_todo_ids.each do |todo_id| @@ -62,11 +62,11 @@ class ContextTest < Test::Rails::TestCase end def test_not_done_todos - assert_equal 5, @agenda.not_done_todos.size + assert_equal 6, @agenda.not_done_todos.size t = @agenda.not_done_todos[0] t.complete! t.save! - assert_equal 4, Context.find(@agenda.id).not_done_todos.size + assert_equal 5, Context.find(@agenda.id).not_done_todos.size end def test_done_todos diff --git a/test/unit/project_test.rb b/test/unit/project_test.rb index 966c8c6d..58f4f794 100644 --- a/test/unit/project_test.rb +++ b/test/unit/project_test.rb @@ -172,9 +172,9 @@ class ProjectTest < Test::Rails::TestCase def test_not_done_todo_count assert_equal 2, @timemachine.not_done_todo_count - assert_equal 3, @moremoney.not_done_todo_count + assert_equal 4, @moremoney.not_done_todo_count @moremoney.todos[0].complete! - assert_equal 2, @moremoney.not_done_todo_count + assert_equal 3, @moremoney.not_done_todo_count end def test_default_context_name From c5cff97f3f280cb9b1d01566ab47d44f5f01d0d7 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Tue, 21 Oct 2008 09:11:21 +0200 Subject: [PATCH 10/23] fixes #785 --- app/views/integrations/_applescript2.rhtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/integrations/_applescript2.rhtml b/app/views/integrations/_applescript2.rhtml index d63ee561..e883f998 100644 --- a/app/views/integrations/_applescript2.rhtml +++ b/app/views/integrations/_applescript2.rhtml @@ -8,9 +8,9 @@ the newly created action. *) (* Edit appropriately for your setup *) -property myUsername to "<%= current_user.login %>" -property myToken to "<%= current_user.token %>" -property myContextID to <%= context.id %> (* <%= context.name %> *) +property myUsername : "<%= current_user.login %>" +property myToken : "<%= current_user.token %>" +property myContextID : <%= context.id %> (* <%= context.name %> *) -- this string is used when the message subject is empty property emptySubject : "No Subject Specified" From a3f23c60e49c2c4bb50938209fe88c5b78918f1f Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Wed, 22 Oct 2008 10:16:31 +0200 Subject: [PATCH 11/23] fixes #786 where timezone handling of RoR screwed the algorithm to find the last sunday of march in 2009 Fix is a bit of a hack, see #786 for futher explanation. --- app/controllers/application.rb | 3 +- app/models/recurring_todo.rb | 11 ++-- .../recurring_todos_controller_test.rb | 57 ++++++++++++++++++- test/unit/recurring_todo_test.rb | 9 +++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/app/controllers/application.rb b/app/controllers/application.rb index b08dc0be..31a370e0 100644 --- a/app/controllers/application.rb +++ b/app/controllers/application.rb @@ -170,11 +170,12 @@ class ApplicationController < ActionController::Base # set dates todo.recurring_todo_id = rt.id todo.due = rt.get_due_date(date) - # make sure that show_from is not in the past + show_from_date = rt.get_show_from_date(date) if show_from_date.nil? todo.show_from=nil else + # make sure that show_from is not in the past todo.show_from = show_from_date < Time.zone.now ? nil : show_from_date end diff --git a/app/models/recurring_todo.rb b/app/models/recurring_todo.rb index fbe2f07a..fdd230e7 100644 --- a/app/models/recurring_todo.rb +++ b/app/models/recurring_todo.rb @@ -495,12 +495,14 @@ class RecurringTodo < ActiveRecord::Base def get_xth_day_of_month(x, weekday, month, year) if x == 5 - # last -> count backwards - last_day = Time.zone.local(year, month, Time.days_in_month(month)) + # 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 - return last_day + # convert back to local timezone + return Time.zone.local(last_day.year, last_day.month, last_day.day) else # 1-4th -> count upwards start = Time.zone.local(year,month,1) @@ -524,7 +526,8 @@ class RecurringTodo < ActiveRecord::Base 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 + # 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 diff --git a/test/functional/recurring_todos_controller_test.rb b/test/functional/recurring_todos_controller_test.rb index 150bce59..2bd67e67 100644 --- a/test/functional/recurring_todos_controller_test.rb +++ b/test/functional/recurring_todos_controller_test.rb @@ -79,7 +79,7 @@ class RecurringTodosControllerTest < ActionController::TestCase recurring_todo_1 = RecurringTodo.find(1) assert recurring_todo_1.completed? - # remove remaining todo + # remove remaining todo todo = Todo.find_by_recurring_todo_id(1) todo.recurring_todo_id = 2 todo.save @@ -124,5 +124,60 @@ class RecurringTodosControllerTest < ActionController::TestCase # show_from should be nil since now+4.days-10.days is in the past assert_equal nil, new_todo.show_from end + + def test_last_sunday_of_march + # this test is a duplicate of the unit test. Only this test covers the + # codepath in the controllers + + login_as(:admin_user) + + orig_rt_count = RecurringTodo.count + orig_todo_count = Todo.count + + put :create, + "context_name"=>"library", + "project_name"=>"Build a working time machine", + "recurring_todo" => + { + "daily_every_x_days"=>"1", + "daily_selector"=>"daily_every_x_day", + "description"=>"new recurring pattern", + "end_date" => "", + "ends_on" => "no_end_date", + "monthly_day_of_week" => "1", + "monthly_every_x_day" => "22", + "monthly_every_x_month2" => "1", + "monthly_every_x_month" => "1", + "monthly_every_xth_day"=>"1", + "monthly_selector"=>"monthly_every_x_day", + "notes"=>"with some notes", + "number_of_occurences" => "", + "recurring_period"=>"yearly", + "recurring_show_days_before"=>"0", + "recurring_target"=>"due_date", + "start_from"=>"", + "weekly_every_x_week"=>"1", + "weekly_return_monday"=>"w", + "yearly_day_of_week"=>"0", + "yearly_every_x_day"=>"22", + "yearly_every_xth_day"=>"5", + "yearly_month_of_year2"=>"3", + "yearly_month_of_year"=>"10", + "yearly_selector"=>"yearly_every_xth_day" + }, + "tag_list"=>"one, two, three, four" + + # check new recurring todo added + assert_equal orig_rt_count+1, RecurringTodo.count + # check new todo added + assert_equal orig_todo_count+1, Todo.count + + # find the newly created todo + new_todo = Todo.find_by_description("new recurring pattern") + assert !new_todo.nil? + + # the date should be 29 march 2009 + assert_equal Time.zone.local(2009,3,29), new_todo.due + end end diff --git a/test/unit/recurring_todo_test.rb b/test/unit/recurring_todo_test.rb index 82ba1cb5..80106554 100644 --- a/test/unit/recurring_todo_test.rb +++ b/test/unit/recurring_todo_test.rb @@ -197,6 +197,15 @@ class RecurringTodoTest < Test::Rails::TestCase assert_equal due_date1, due_date2 end + def test_last_sunday_of_march + @yearly.recurrence_selector = 1 + @yearly.every_other2 = 3 # march + @yearly.every_other3 = 5 # last + @yearly.every_count = 0 # sunday + due_date = @yearly.get_due_date(Time.zone.local(2008,10,1)) # oct 1st + assert_equal Time.zone.local(2009,3,29), due_date # march 29th + end + def test_start_from_in_future # every_day should return start_day if it is in the future @every_day.start_from = @in_three_days From e82be2e52214c90762f5d702f9361694afdc1262 Mon Sep 17 00:00:00 2001 From: waltercruz Date: Fri, 24 Oct 2008 10:25:04 -0200 Subject: [PATCH 12/23] Adding test to the sort by number of todos --- test/functional/projects_controller_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index f97a892a..1b3fe5db 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -218,6 +218,15 @@ class ProjectsControllerTest < TodoContainerControllerTestBase get :index, { :format => "txt", :token => users(:admin_user).token } assert_response :ok end + + def test_actionize_sorts_active_projects_by_number_of_tasks + login_as :admin_user + u = users(:admin_user) + post :actionize, :state => "active", :format => 'js' + assert_equal 1, projects(:gardenclean).position + assert_equal 2, projects(:moremoney).position + assert_equal 3, projects(:timemachine).position + end def test_alphabetize_sorts_active_projects_alphabetically login_as :admin_user From 385f34d07110ca0876fa22b6650b5449bfd695ae Mon Sep 17 00:00:00 2001 From: waltercruz Date: Sat, 25 Oct 2008 17:43:52 -0200 Subject: [PATCH 13/23] Taking care of css --- app/views/projects/_project_state_group.rhtml | 8 ++++---- public/stylesheets/standard.css | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/views/projects/_project_state_group.rhtml b/app/views/projects/_project_state_group.rhtml index c4a0f0b1..e5393399 100644 --- a/app/views/projects/_project_state_group.rhtml +++ b/app/views/projects/_project_state_group.rhtml @@ -1,5 +1,6 @@
>

<%= project_state_group.length %><%= state.titlecase %> Projects

+
<%= render :partial => 'project_listing', :collection => project_state_group %> diff --git a/public/stylesheets/standard.css b/public/stylesheets/standard.css index 3b31fcd7..ea8f0dc5 100644 --- a/public/stylesheets/standard.css +++ b/public/stylesheets/standard.css @@ -692,11 +692,14 @@ div#list-active-projects, div#list-hidden-projects, div#list-completed-projects, margin:20px 0px 8px 13px } -div.alpha_sort { +div.menu_sort { margin-top:-20px; float:right; } +div.alpha_sort, div.tasks_sort,span.sort_separator { + float:left; +} .container td { border: none; From 0b57b23b2d1aec73003773152f38725d23fdb743 Mon Sep 17 00:00:00 2001 From: waltercruz Date: Tue, 28 Oct 2008 16:40:28 -0200 Subject: [PATCH 14/23] html fixes and sort by number of todos logic --- app/controllers/projects_controller.rb | 11 ++++++++++- app/models/user.rb | 5 ++--- app/views/projects/_project_state_group.rhtml | 6 +++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index ed2128de..cab0bd10 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -188,7 +188,16 @@ class ProjectsController < ApplicationController def actionize @state = params['state'] - @projects = current_user.projects.actionize(:state => @state) if @state + query_state = '' + query_state = 'AND project.state = "' + params['state'] +'" 'if @state + projects = Project.find_by_sql([ + "SELECT project.id, count(todo.id) as p_count " + + "FROM projects as project " + + "LEFT OUTER JOIN todos as todo ON todo.project_id = project.id "+ + "WHERE project.user_id = ? " + + query_state + + " GROUP BY project.id ORDER by p_count DESC",current_user.id]) + @projects = current_user.projects.actionize(projects,:state => @state) if @state @contexts = current_user.contexts init_not_done_counts(['project']) end diff --git a/app/models/user.rb b/app/models/user.rb index 4b1f41e7..59e3cdbc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -51,10 +51,9 @@ class User < ActiveRecord::Base self.update_positions(projects.map{ |p| p.id }) return projects end - def actionize(scope_conditions = {}) + def actionize(proj,scope_conditions = {}) + self.update_positions(proj.map{ |p| p.id }) projects = find(:all, :conditions => scope_conditions) - projects.sort!{ |x,y| y.todos.count(:state == 'active') <=> x.todos.count(:state == 'active') } - self.update_positions(projects.map{ |p| p.id }) return projects end end diff --git a/app/views/projects/_project_state_group.rhtml b/app/views/projects/_project_state_group.rhtml index e5393399..32693f0f 100644 --- a/app/views/projects/_project_state_group.rhtml +++ b/app/views/projects/_project_state_group.rhtml @@ -1,8 +1,8 @@
>

<%= project_state_group.length %><%= state.titlecase %> Projects

-