From 03ff56d70320a67c4b1c087ba6698b76602394b1 Mon Sep 17 00:00:00 2001 From: bsag Date: Tue, 2 May 2006 17:11:46 +0000 Subject: [PATCH] Added the beginnings of a tickler to Tracks. It's fairly rudimentary at the moment, but it's designed to set the foundations for more kinds of deferred tasks.The current system works, but isn't very DRY: it will need refactoring for speed. It has these features: * The todos table and model has been altered (run rake migrate to update) to create two sub-classes of the todo model: Immediate and Deferred. Fairly obviously, Immediate actions are those shown immediately, and Deferred are those shown when certain conditions are fulfilled. At the moment, this is when the 'show_from' date arrives. * Deferred actions are created on a separate page: /todo/tickler. You can view the show_from date here and delete or edit the actions. Deferred actions don't show on the home page (their handling on project and context pages is still to be fixed). * A periodically called method (every 10 minutes) checks whether any of the deferred actions is due to be show, and if so, a warning message is shown on the home page to tell you how many deferred actions are to be shown. You need to refresh the page to see them (again, this is to be fixed). * When deferred actions become due, their type is changed from "Deferred" to "Immediate". The handling of their staleness is still to be fixed. There's a way to go before it's really smooth, but it's a start. At least partially fixes #270 and #78, but will be improved with time too. git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@232 a4c988fc-2ded-0310-b66e-134b36920a42 --- tracks/app/controllers/todo_controller.rb | 91 ++++++++++++++++++- tracks/app/helpers/todo_helper.rb | 47 +++++++++- tracks/app/models/context.rb | 4 +- tracks/app/models/deferred.rb | 2 + tracks/app/models/immediate.rb | 2 + tracks/app/views/context/error.rjs | 2 +- tracks/app/views/layouts/standard.rhtml | 5 +- .../app/views/shared/_add_new_item_form.rhtml | 18 +++- .../todo/_action_edit_deferred_form.rhtml | 62 +++++++++++++ .../app/views/todo/_add_new_tickle_form.rhtml | 46 ++++++++++ tracks/app/views/todo/_item.rhtml | 4 +- tracks/app/views/todo/_tickle.rhtml | 26 ++++++ tracks/app/views/todo/_tickler_items.rhtml | 12 +++ tracks/app/views/todo/add_deferred_item.rjs | 13 +++ tracks/app/views/todo/check_tickler.rjs | 3 + .../app/views/todo/deferred_update_action.rjs | 14 +++ tracks/app/views/todo/tickler.rhtml | 16 ++++ .../migrate/008_add_subclass_attr_to_todos.rb | 12 +++ tracks/db/schema.rb | 4 +- tracks/public/stylesheets/standard.css | 2 +- tracks/test/fixtures/deferreds.yml | 5 + tracks/test/fixtures/immediates.yml | 5 + tracks/test/unit/deferred_test.rb | 10 ++ tracks/test/unit/immediate_test.rb | 10 ++ 24 files changed, 399 insertions(+), 16 deletions(-) create mode 100644 tracks/app/models/deferred.rb create mode 100644 tracks/app/models/immediate.rb create mode 100644 tracks/app/views/todo/_action_edit_deferred_form.rhtml create mode 100644 tracks/app/views/todo/_add_new_tickle_form.rhtml create mode 100644 tracks/app/views/todo/_tickle.rhtml create mode 100644 tracks/app/views/todo/_tickler_items.rhtml create mode 100644 tracks/app/views/todo/add_deferred_item.rjs create mode 100644 tracks/app/views/todo/check_tickler.rjs create mode 100644 tracks/app/views/todo/deferred_update_action.rjs create mode 100644 tracks/app/views/todo/tickler.rhtml create mode 100644 tracks/db/migrate/008_add_subclass_attr_to_todos.rb create mode 100644 tracks/test/fixtures/deferreds.yml create mode 100644 tracks/test/fixtures/immediates.yml create mode 100644 tracks/test/unit/deferred_test.rb create mode 100644 tracks/test/unit/immediate_test.rb diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb index 29d26d9a..f55b5c23 100644 --- a/tracks/app/controllers/todo_controller.rb +++ b/tracks/app/controllers/todo_controller.rb @@ -56,7 +56,7 @@ class TodoController < ApplicationController else @item.due = "" end - + @saved = @item.save @on_page = "home" @@ -82,6 +82,44 @@ class TodoController < ApplicationController render :action => 'list' end end + + # Adding deferred actions from form on todo/tickler + # + def add_deferred_item + self.init + @tickle = Deferred.create(@params["todo"]) + + if @tickle.due? + @tickle.due = Date.strptime(@params["todo"]["due"], @user.preferences["date_format"]) + else + @tickle.due = "" + end + + @saved = @tickle.save + + @on_page = "home" + if @saved + @up_count = @todos.collect { |x| ( !x.done? and !x.context.hide? ) ? x:nil }.compact.size.to_s + end + return if request.xhr? + + # fallback for standard requests + if @saved + flash["notice"] = 'Added new next action.' + redirect_to :action => 'tickler' + else + flash["warning"] = 'The next action was not added. Please try again.' + redirect_to :action => 'tickler' + end + + rescue + if request.xhr? # be sure to include an error.rjs + render :action => 'error' + else + flash["warning"] = 'An error occurred on the server.' + render :action => 'tickler' + end + end def edit_action self.init @@ -90,6 +128,13 @@ class TodoController < ApplicationController render :partial => 'action_edit_form', :object => item end + def edit_deferred_action + self.init + item = check_user_return_item + + render :partial => 'action_edit_deferred_form', :object => item + end + # Toggles the 'done' status of the action # def toggle_check @@ -133,6 +178,21 @@ class TodoController < ApplicationController @saved = @item.save end + def deferred_update_action + #self.init + @tickle = check_user_return_item + @original_item_context_id = @tickle.context_id + @tickle.attributes = @params["item"] + + if @tickle.due? + @tickle.due = Date.strptime(@params["item"]["due"], @user.preferences["date_format"]) + else + @tickle.due = "" + end + + @saved = @tickle.save + end + # Delete a next action # def destroy_action @@ -191,7 +251,31 @@ class TodoController < ApplicationController self.init @page_title = "TRACKS::Feeds" end + + def tickler + self.init + @page_title = "TRACKS::Tickler" + @tickles = @user.todos.find(:all, :conditions => ['type = ?', "Deferred"], :order => "show_from ASC") + @count = @tickles.size + end + # Called by periodically_call_remote + # Check for any due tickler items, change them to type Immediate and show + # on the page + # + def check_tickler + self.init + now = Date.today() + @due_tickles = @user.todos.find(:all, :conditions => ['type = ? AND (show_from < ? OR show_from = ?)', "Deferred", now, now ], :order => "show_from ASC") + unless @due_tickles.empty? + # Change the due tickles to type "Immediate" + @due_tickles.each do |t| + t[:type] = "Immediate" + t.show_from = nil + t.save + end + end + end protected @@ -208,8 +292,9 @@ class TodoController < ApplicationController def init @projects = @user.projects @contexts = @user.contexts - @todos = @user.todos - @done = @todos.find(:all, :conditions => ["done = ?", true]) + @todos = Todo.find(:all, :conditions => ['user_id = ? and type = ?', @user.id, "Immediate"]) + @done = Todo.find(:all, :conditions => ['user_id = ? and done = ?', @user.id, true]) + # @todos = @todos.collect { |x| (x.class == Immediate) ? x : nil }.compact end end diff --git a/tracks/app/helpers/todo_helper.rb b/tracks/app/helpers/todo_helper.rb index 535032e4..0d982911 100644 --- a/tracks/app/helpers/todo_helper.rb +++ b/tracks/app/helpers/todo_helper.rb @@ -7,13 +7,20 @@ module TodoHelper count = Todo.find_all("done=0 AND context_id=#{context.id}").length end - def form_remote_tag_edit_todo( item ) - form_remote_tag( :url => { :controller => 'todo', :action => 'update_action', :id => item.id }, + def form_remote_tag_edit_todo( item, type ) + (type == "deferred") ? act = 'deferred_update_action' : act = 'update_action' + form_remote_tag( :url => { :controller => 'todo', :action => act, :id => item.id }, :html => { :id => "form-action-#{item.id}", :class => "inline-form" } ) end - def link_to_remote_todo( item, handled_by) + def link_to_remote_todo( item, handled_by, type) + if type == "deferred" + act = "edit_deferred_action" + else + act = "edit_action" + end + str = link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"), {:url => { :controller => handled_by, :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action, \'#{item.description}\'?"}, @@ -23,7 +30,7 @@ module TodoHelper { :update => "form-action-#{item.id}", :loading => visual_effect(:pulsate, "action-#{item.id}-edit-icon"), - :url => { :controller => "todo", :action => "edit_action", :id => item.id }, + :url => { :controller => "todo", :action => act, :id => item.id }, :success => "Element.toggle('item-#{item.id}','action-#{item.id}-edit-form'); new Effect.Appear('action-#{item.id}-edit-form', { duration: .2 }); Form.focusFirstElement('form-action-#{item.id}')" }, { @@ -88,6 +95,38 @@ module TodoHelper end end + # Check show_from date in comparison to today's date + # Flag up date appropriately with a 'traffic light' colour code + # + def show_date(due) + if due == nil + return "" + end + + @now = Date.today + @days = due-@now + + case @days + # overdue or due very soon! sound the alarm! + when -1000..-1 + "Shown on " + (@days * -1).to_s + " days " + when 0 + "Show Today " + when 1 + "Show Tomorrow " + # due 2-7 days away + when 2..7 + if @user.preferences["due_style"] == "1" + "Show on " + due.strftime("%A") + " " + else + "Show in " + @days.to_s + " days " + end + # more than a week away - relax + else + "Show in " + @days.to_s + " days " + end + end + def toggle_show_notes( item ) str = " ["todos.context_id = #{id} AND todos.done = ?", false], + todos = Todo.find :all, :conditions => ["todos.context_id = #{id} AND todos.done = ? AND type = ?", false, "Immediate"], :include => [:context, :project], :order => "due IS NULL, due ASC, created_at ASC" end def find_done_todos - todos = Todo.find :all, :conditions => ["todos.context_id = #{id} AND todos.done = ?", true], + todos = Todo.find :all, :conditions => ["todos.context_id = #{id} AND todos.done = ? AND type = ?", true, "Immediate"], :include => [:context, :project], :order => "due IS NULL, due ASC, created_at ASC", :limit => @user.preferences["no_completed"].to_i diff --git a/tracks/app/models/deferred.rb b/tracks/app/models/deferred.rb new file mode 100644 index 00000000..1b817239 --- /dev/null +++ b/tracks/app/models/deferred.rb @@ -0,0 +1,2 @@ +class Deferred < Todo +end diff --git a/tracks/app/models/immediate.rb b/tracks/app/models/immediate.rb new file mode 100644 index 00000000..bcd26694 --- /dev/null +++ b/tracks/app/models/immediate.rb @@ -0,0 +1,2 @@ +class Immediate < Todo +end diff --git a/tracks/app/views/context/error.rjs b/tracks/app/views/context/error.rjs index f4f6b35c..8bbd1558 100644 --- a/tracks/app/views/context/error.rjs +++ b/tracks/app/views/context/error.rjs @@ -1 +1 @@ -page.replace_html "status", "A server error has occurred" \ No newline at end of file +page.replace_html "status", content_tag("div", "A server error has occurred", "class" => "warning") \ No newline at end of file diff --git a/tracks/app/views/layouts/standard.rhtml b/tracks/app/views/layouts/standard.rhtml index 6eb8cc0f..cc326a12 100644 --- a/tracks/app/views/layouts/standard.rhtml +++ b/tracks/app/views/layouts/standard.rhtml @@ -31,6 +31,7 @@
  • <%= navigation_link("Home", {:controller => "todo", :action => "list"}, {:accesskey => "t", :title => "Home"} ) %>
  • <%= navigation_link( "Contexts", {:controller => "context", :action => "list"}, {:accesskey=>"c", :title=>"Contexts"} ) %>
  • <%= navigation_link( "Projects", {:controller => "project", :action => "list"}, {:accesskey=>"p", :title=>"Projects"} ) %>
  • +
  • <%= navigation_link( "Tickler", {:controller => "todo", :action => "tickler"}, :title => "Ticker" ) %>
  • <%= navigation_link( "Done", {:controller => "todo", :action => "completed"}, {:accesskey=>"d", :title=>"Completed"} ) %>
  • <%= navigation_link( "Notes", {:controller => "note", :action => "index"}, {:accesskey => "o", :title => "Show all notes"} ) %>
  • <%= navigation_link( "Preferences", {:controller => "user", :action => "preferences"}, {:accesskey => "u", :title => "Show my preferences"} ) %>
  • @@ -47,7 +48,9 @@ <% unless @controller_name == 'feed' or session['noexpiry'] == "on" -%> <%= periodically_call_remote( :url => {:controller => "login", :action => "check_expiry"}, :frequency => (5*60)) %> -<% end -%> +<% end -%> +<%= periodically_call_remote( :url => {:controller => "todo", :action => "check_tickler"}, + :frequency => (10*60)) %> <%= @content_for_layout %>