mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-23 23:46:42 +02:00
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.
This commit is contained in:
parent
5ec0d569a9
commit
1a656f0f60
7 changed files with 92 additions and 37 deletions
18
ChangeLog
18
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 <id> 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 ------------------------------
|
||||
|
||||
|
|
|
@ -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</code></pre>
|
||||
|
||||
<h4>Modification</h4>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<h4>Recurrence Periods</h4>
|
||||
<p>
|
||||
In the above examples, the recurrence period was specified as "monthly" and
|
||||
|
|
|
@ -105,13 +105,17 @@
|
|||
<li>UTF8 text is now supported in task project names, tags and descriptions.
|
||||
<li>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).
|
||||
<li>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).
|
||||
<li>Added support for task annotations, with each annotation comprising a
|
||||
timestamp and a description.
|
||||
<li>Added support for a 'description_only' column that can be used in custom
|
||||
reports which excludes annotations.
|
||||
<li>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).
|
||||
<li>A task can now be annotated with the command "task <id> annotate ...", and
|
||||
a timestamped annotation will appear in reports.
|
||||
<li>A 'description_only' column is now available for use in custom reports,
|
||||
and it excludes annotations.
|
||||
<li>A task can now be upgraded to a recurring task by adding a recurrence
|
||||
frequency, a due date, and an optional until date.
|
||||
<li>When a recurring task is modified, all other instances of the recurring
|
||||
task are also modified.
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
|
|
|
@ -674,12 +674,30 @@ std::string handleModify (TDB& tdb, T& task, Config& conf)
|
|||
{
|
||||
std::stringstream out;
|
||||
std::vector <T> 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 <T>::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;
|
||||
}
|
||||
}
|
||||
|
||||
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 <T> all;
|
||||
tdb.pendingT (all);
|
||||
tdb.allPendingT (all);
|
||||
|
||||
// Lookup the complete task.
|
||||
T complete = findT (task.getId (), all);
|
||||
|
||||
int count = 0;
|
||||
std::vector <T>::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;
|
||||
}
|
||||
}
|
||||
|
||||
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 <T>& all)
|
||||
{
|
||||
std::vector <T>::const_iterator it;
|
||||
for (it = all.begin (); it != all.end (); ++it)
|
||||
if (id == it->getId ())
|
||||
return *it;
|
||||
|
||||
return T ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 <T>&);
|
||||
|
||||
// report.cpp
|
||||
void filter (std::vector<T>&, T&);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue