- Tasks may now be given an 'until' date, after which they expire and are
  deleted.
This commit is contained in:
Paul Beckingham
2012-05-13 23:43:53 -04:00
parent d122173103
commit 665bc197dc
11 changed files with 40 additions and 18 deletions

View File

@@ -4,6 +4,8 @@
2.0.1 () 2.0.1 ()
Features Features
+ Feature #457, tasks may now be given an 'until' date, after which they expire
and are deleted.
+ Feature #516, which allows the duplication of completed tasks (thanks to + Feature #516, which allows the duplication of completed tasks (thanks to
Peter De Poorter, Ethan Schoonover). Peter De Poorter, Ethan Schoonover).
+ Applied patch for feature #1005, which prevents the update-holidays.pl script + Applied patch for feature #1005, which prevents the update-holidays.pl script

4
NEWS
View File

@@ -5,13 +5,15 @@ New Features in taskwarrior 2.0.1
and 'summary' commands. and 'summary' commands.
- Support for the 'scheduled' date for a task, which represent the earliest - Support for the 'scheduled' date for a task, which represent the earliest
opportunity to work on a task. opportunity to work on a task.
- All tasks may now be given an 'until' date, after which they will expire
and are deleted.
Please refer to the ChangeLog file for full details. There are too many to Please refer to the ChangeLog file for full details. There are too many to
list here. list here.
New commands in taskwarrior 2.0.1 New commands in taskwarrior 2.0.1
- None - New 'ready' report that lists tasks ready for work, sorted by urgency.
New configuration options in taskwarrior 2.0.1 New configuration options in taskwarrior 2.0.1

View File

@@ -546,8 +546,8 @@ Specifies the due-date of a task.
Specifies the frequency of a recurrence of a task. Specifies the frequency of a recurrence of a task.
.TP .TP
.B until:<end-date-of-recurrence> .B until:<expiration date of task>
Specifies the Recurrence end-date of a task. Specifies the expiration date of a task, after which it will be deleted.
.TP .TP
.B fg:<color-spec> .B fg:<color-spec>

View File

@@ -1154,10 +1154,6 @@ void Task::validate ()
if (! has ("due") && has ("recur")) if (! has ("due") && has ("recur"))
throw std::string (STRING_TASK_VALID_REC_DUE); throw std::string (STRING_TASK_VALID_REC_DUE);
// Cannot have an until date no recurrence frequency.
if (has ("until") && !has ("recur"))
throw std::string (STRING_TASK_VALID_UNTIL);
// Recur durations must be valid. // Recur durations must be valid.
if (has ("recur")) if (has ("recur"))
{ {

View File

@@ -203,7 +203,7 @@ int CmdInfo::execute (std::string& output)
if (task->has ("until")) if (task->has ("until"))
{ {
row = view.addRow (); row = view.addRow ();
view.set (row, 0, STRING_CMD_INFO_RECUR_UNTIL); view.set (row, 0, STRING_CMD_INFO_UNTIL);
view.set (row, 1, Date (task->get_date ("until")).toString (dateformat)); view.set (row, 1, Date (task->get_date ("until")).toString (dateformat));
} }

View File

@@ -225,7 +225,7 @@
#define STRING_CMD_INFO_USAGE "Shows all data and metadata" #define STRING_CMD_INFO_USAGE "Shows all data and metadata"
#define STRING_CMD_INFO_BLOCKED "This task blocked by" #define STRING_CMD_INFO_BLOCKED "This task blocked by"
#define STRING_CMD_INFO_BLOCKING "This task is blocking" #define STRING_CMD_INFO_BLOCKING "This task is blocking"
#define STRING_CMD_INFO_RECUR_UNTIL "Recur until" #define STRING_CMD_INFO_UNTIL "Until"
#define STRING_CMD_INFO_MODIFICATION "Modification" #define STRING_CMD_INFO_MODIFICATION "Modification"
#define STRING_CMD_INFO_TOTAL_ACTIVE "Total active time" #define STRING_CMD_INFO_TOTAL_ACTIVE "Total active time"
#define STRING_CMD_UNDO_USAGE "Reverts the most recent change to a task" #define STRING_CMD_UNDO_USAGE "Reverts the most recent change to a task"
@@ -698,6 +698,7 @@
#define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the calendar report." #define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the calendar report."
#define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report." #define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."
#define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'." #define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'."
#define STRING_FEEDBACK_EXPIRED "Task {1} '{2}' expired and was deleted."
// File // File
#define STRING_FILE_PERMS "Task does not have the correct permissions for '{1}'." #define STRING_FILE_PERMS "Task does not have the correct permissions for '{1}'."
@@ -732,9 +733,6 @@
#define STRING_RECORD_JUNK_AT_EOL "Unrecognized characters at end of line." #define STRING_RECORD_JUNK_AT_EOL "Unrecognized characters at end of line."
#define STRING_RECORD_NOT_FF4 "Record not recognized as format 4." #define STRING_RECORD_NOT_FF4 "Record not recognized as format 4."
// recur
#define STRING_RECUR_PAST_UNTIL "Task ({1}) has past its 'until' date, and has been deleted."
// 'show' command // 'show' command
#define STRING_CMD_SHOW "Shows all configuration variables or subset" #define STRING_CMD_SHOW "Shows all configuration variables or subset"
#define STRING_CMD_SHOW_ARGS "You can only specify 'all' or a search string." #define STRING_CMD_SHOW_ARGS "You can only specify 'all' or a search string."

View File

@@ -468,6 +468,18 @@ std::string onProjectChange (Task& task1, Task& task2)
return messages; return messages;
} }
///////////////////////////////////////////////////////////////////////////////
std::string onExpiration (Task& task)
{
std::stringstream msg;
if (context.verbose ("affected"))
msg << format (STRING_FEEDBACK_EXPIRED, task.id, task.get ("description"))
<< "\n";
return msg.str ();
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
static void countTasks ( static void countTasks (
const std::vector <Task>& all, const std::vector <Task>& all,

View File

@@ -79,6 +79,7 @@ void feedback_special_tags (const Task&, const std::string&);
void feedback_unblocked (const Task&); void feedback_unblocked (const Task&);
std::string onProjectChange (Task&, bool scope = true); std::string onProjectChange (Task&, bool scope = true);
std::string onProjectChange (Task&, Task&); std::string onProjectChange (Task&, Task&);
std::string onExpiration (Task&);
// sort.cpp // sort.cpp
void sort_tasks (std::vector <Task>&, std::vector <int>&, const std::string&); void sort_tasks (std::vector <Task>&, std::vector <int>&, const std::string&);

View File

@@ -56,6 +56,7 @@ extern Context context;
void handleRecurrence () void handleRecurrence ()
{ {
std::vector <Task> tasks = context.tdb2.pending.get_tasks (); std::vector <Task> tasks = context.tdb2.pending.get_tasks ();
Date now;
// Look at all tasks and find any recurring ones. // Look at all tasks and find any recurring ones.
std::vector <Task>::iterator t; std::vector <Task>::iterator t;
@@ -68,12 +69,10 @@ void handleRecurrence ()
std::vector <Date> due; std::vector <Date> due;
if (!generateDueDates (*t, due)) if (!generateDueDates (*t, due))
{ {
std::cout << format (STRING_RECUR_PAST_UNTIL, trim (t->get ("description")))
<< "\n";
// Determine the end date. // Determine the end date.
t->setStatus (Task::deleted); t->setStatus (Task::deleted);
context.tdb2.modify (*t); context.tdb2.modify (*t);
context.footnote (onExpiration (*t));
continue; continue;
} }
@@ -137,6 +136,18 @@ void handleRecurrence ()
context.tdb2.modify (*t); context.tdb2.modify (*t);
} }
} }
// Non-recurring tasks expire too.
else
{
if (t->has ("until") &&
Date (t->get_date ("until")) < now)
{
t->setStatus (Task::deleted);
context.tdb2.modify(*t);
context.footnote (onExpiration (*t));
}
}
} }
} }

View File

@@ -45,8 +45,8 @@ qx{../src/task rc:bug.rc add foo due:today recur:daily until:eom};
my $output = qx{../src/task rc:bug.rc info 1}; my $output = qx{../src/task rc:bug.rc info 1};
# Result: Make sure the 'until' date is rendered as a date, not an epoch. # Result: Make sure the 'until' date is rendered as a date, not an epoch.
unlike ($output, qr/Recur until\s+\d{10}/, 'until is not shown as an epoch'); unlike ($output, qr/Until\s+\d{10}/, 'until is not shown as an epoch');
like ($output, qr/Recur until\s+\d+\/\d+\/\d{4}/, 'until is shown as a date'); like ($output, qr/Until\s+\d+\/\d+\/\d{4}/, 'until is shown as a date');
# Cleanup. # Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data synch.key bug.rc); unlink qw(pending.data completed.data undo.data backlog.data synch.key bug.rc);

View File

@@ -53,7 +53,7 @@ qx{../src/task rc:recur.rc 3 do};
qx{../src/task rc:recur.rc 4 do}; qx{../src/task rc:recur.rc 4 do};
qx{../src/task rc:recur.rc 5 do}; qx{../src/task rc:recur.rc 5 do};
$output = qx{../src/task rc:recur.rc list}; $output = qx{../src/task rc:recur.rc list};
like ($output, qr/and has been deleted/, 'Parent task deleted'); like ($output, qr/and was deleted/, 'Parent task deleted');
$output = qx{../src/task rc:recur.rc diag}; $output = qx{../src/task rc:recur.rc diag};
like ($output, qr/No duplicates found/, 'No duplicate UUIDs detected'); like ($output, qr/No duplicates found/, 'No duplicate UUIDs detected');