From 1a656f0f609979c282e43b4cb36be36613adf502 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Wed, 25 Mar 2009 01:08:13 -0400 Subject: [PATCH] Recurring Tasks - upgrades and bug fixes - Improved (fixed) logical consistency checks that prevent the addition of a recurrence frequency without a due date. - Improved (fixed) logical consistency checks that prevent the addition of an until date with a recurrence frequency. - When a recurring task is modified, all sibling instances, as well as the parent task now get modified. - When a recurring task is appended, all sibling instances, as well as the parent task now get modified. - Updated documentation. - It is now possible to upgrade a regular task to a recurring task, which is triggered by the "recur" attribute. --- ChangeLog | 18 +++++++----- html/recur.html | 7 +++++ html/task.html | 18 +++++++----- src/T.h | 2 +- src/command.cpp | 75 ++++++++++++++++++++++++++++++++++++++++--------- src/parse.cpp | 8 ------ src/task.h | 1 + 7 files changed, 92 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index 295c0fcc1..c3d281d33 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,13 +9,17 @@ + UTF8 text is now supported in task project names, tags and descriptions. + Fixed bug that caused the y/n confirmation on task deletion to ignore the Enter key and fail to re-prompt (thanks to Bruce Dillahunty). - + Added support for the "echo.command" configuration variable that displays - the task affected by the start, stop, do, undo, delete and undelete - commands (thanks to Bruce Dillahunty). - + Added support for task annotations, with each annotation comprising a - timestamp and a description. - + Added support for a 'description_only' column that can be used in custom - reports which excludes annotations. + + When the "echo.command" configuration variable is set to "yes", it causes + commands that modify tasks to display which task was affected (thanks to + Bruce Dillahunty). + + A task can now be annotated with the command "task annotate ...", and + a timestamped annotation will appear in reports. + + A 'description_only' column is now available for use in custom reports, + and it excludes annotations. + + A task can now be upgraded to a recurring task by adding a recurrence + frequency, a due date, and an optional until date. + + When a recurring task is modified, all other instances of the recurring + task are also modified. ------ old releases ------------------------------ diff --git a/html/recur.html b/html/recur.html index 250a3316d..1da5d7891 100644 --- a/html/recur.html +++ b/html/recur.html @@ -101,6 +101,13 @@ Permanently delete task? (y/n) y This is a recurring task. Do you want to delete all pending recurrences of this same task? (y/n) y +

Modification

+

+ When a recurring task is modified, all the other recurring task instances will + be modified. For example, if you raise the priority of one of the recurring + task instances, all will be modified. +

+

Recurrence Periods

In the above examples, the recurrence period was specified as "monthly" and diff --git a/html/task.html b/html/task.html index 787655ecb..0c471c85e 100644 --- a/html/task.html +++ b/html/task.html @@ -105,13 +105,17 @@

  • UTF8 text is now supported in task project names, tags and descriptions.
  • Fixed bug that caused the y/n confirmation on task deletion to ignore the Enter key and fail to re-prompt (thanks to Bruce Dillahunty). -
  • Added support for the "echo.command" configuration variable that displays - the task affected by the start, stop, do, undo, delete and undelete - commands (thanks to Bruce Dillahunty). -
  • Added support for task annotations, with each annotation comprising a - timestamp and a description. -
  • Added support for a 'description_only' column that can be used in custom - reports which excludes annotations. +
  • When the "echo.command" configuration variable is set to "yes", it causes + commands that modify tasks to display which task was affected (thanks to + Bruce Dillahunty). +
  • A task can now be annotated with the command "task annotate ...", and + a timestamped annotation will appear in reports. +
  • A 'description_only' column is now available for use in custom reports, + and it excludes annotations. +
  • A task can now be upgraded to a recurring task by adding a recurrence + frequency, a due date, and an optional until date. +
  • When a recurring task is modified, all other instances of the recurring + task are also modified.

    diff --git a/src/T.h b/src/T.h index 7f3898e2b..5060ab634 100644 --- a/src/T.h +++ b/src/T.h @@ -56,7 +56,7 @@ public: const std::string getDescription () const { return mDescription; } void setDescription (const std::string& description) { mDescription = description; } - int getAnnotationCount () const { return mAnnotations.size (); } + int getAnnotationCount () const { return mAnnotations.size (); } void getSubstitution (std::string&, std::string&) const; void setSubstitution (const std::string&, const std::string&); diff --git a/src/command.cpp b/src/command.cpp index e356ae6d8..fc74eef90 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -674,12 +674,30 @@ std::string handleModify (TDB& tdb, T& task, Config& conf) { std::stringstream out; std::vector all; - tdb.pendingT (all); + tdb.allPendingT (all); + // Lookup the complete task. + T complete = findT (task.getId (), all); + + // Perform some logical consistency checks. + if (task.getAttribute ("recur") != "" && + task.getAttribute ("due") == "" && + complete.getAttribute ("due") == "") + throw std::string ("You cannot specify a recurring task without a due date."); + + if (task.getAttribute ("until") != "" && + task.getAttribute ("recur") == "" && + complete.getAttribute ("recur") == "") + throw std::string ("You cannot specify an until date for a non-recurring task."); + + int count = 0; std::vector ::iterator it; for (it = all.begin (); it != all.end (); ++it) { - if (it->getId () == task.getId ()) + if (it->getId () == complete.getId () || // Self + (complete.getAttribute ("parent") != "" && + it->getAttribute ("parent") == complete.getAttribute ("parent")) || // Sibling + it->getUUID () == complete.getAttribute ("parent")) // Parent { T original (*it); @@ -725,8 +743,14 @@ std::string handleModify (TDB& tdb, T& task, Config& conf) if (i->second == "") original.removeAttribute (i->first); else + { original.setAttribute (i->first, i->second); + // If a "recur" attribute is added, upgrade to a recurring task. + if (i->first == "recur") + original.setStatus (T::recurring); + } + ++changes; } @@ -748,16 +772,18 @@ std::string handleModify (TDB& tdb, T& task, Config& conf) } if (changes) - { - original.setId (task.getId ()); tdb.modifyT (original); - } - return out.str (); + ++count; } } - throw std::string ("Task not found."); + if (count == 0) + throw std::string ("Task not found."); + + if (conf.get ("echo.command", true)) + out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl; + return out.str (); } @@ -766,12 +792,19 @@ std::string handleAppend (TDB& tdb, T& task, Config& conf) { std::stringstream out; std::vector all; - tdb.pendingT (all); + tdb.allPendingT (all); + // Lookup the complete task. + T complete = findT (task.getId (), all); + + int count = 0; std::vector ::iterator it; for (it = all.begin (); it != all.end (); ++it) { - if (it->getId () == task.getId ()) + if (it->getId () == complete.getId () || // Self + (complete.getAttribute ("parent") != "" && + it->getAttribute ("parent") == complete.getAttribute ("parent")) || // Sibling + it->getUUID () == complete.getAttribute ("parent")) // Parent { T original (*it); @@ -843,23 +876,26 @@ std::string handleAppend (TDB& tdb, T& task, Config& conf) if (changes) { - original.setId (task.getId ()); tdb.modifyT (original); if (conf.get ("echo.command", true)) out << "Appended '" << task.getDescription () << "' to task " - << task.getId () + << original.getId () << std::endl; - } - return out.str (); + ++count; } } - throw std::string ("Task not found."); + if (count == 0) + throw std::string ("Task not found."); + + if (conf.get ("echo.command", true)) + out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl; + return out.str (); } @@ -983,3 +1019,14 @@ std::string handleAnnotate (TDB& tdb, T& task, Config& conf) } //////////////////////////////////////////////////////////////////////////////// +T findT (int id, const std::vector & all) +{ + std::vector ::const_iterator it; + for (it = all.begin (); it != all.end (); ++it) + if (id == it->getId ()) + return *it; + + return T (); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/parse.cpp b/src/parse.cpp index 56b32a98a..4dacbc0d2 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -475,14 +475,6 @@ void parse ( } } - if (task.getAttribute ("recur") != "" && - task.getAttribute ("due") == "") - throw std::string ("You cannot specify a recurring task without a due date."); - - if (task.getAttribute ("until") != "" && - task.getAttribute ("recur") == "") - throw std::string ("You cannot specify an until date for a non-recurring task."); - if (validDescription (descCandidate)) task.setDescription (descCandidate); } diff --git a/src/task.h b/src/task.h index ab25f2d14..a2c2c75b7 100644 --- a/src/task.h +++ b/src/task.h @@ -89,6 +89,7 @@ std::string handleStop (TDB&, T&, Config&); std::string handleUndo (TDB&, T&, Config&); std::string handleColor (Config&); std::string handleAnnotate (TDB&, T&, Config&); +T findT (int, const std::vector &); // report.cpp void filter (std::vector&, T&);