- 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 ()
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
Peter De Poorter, Ethan Schoonover).
+ 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.
- Support for the 'scheduled' date for a task, which represent the earliest
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
list here.
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

View file

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

View file

@ -1154,10 +1154,6 @@ void Task::validate ()
if (! has ("due") && has ("recur"))
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.
if (has ("recur"))
{

View file

@ -203,7 +203,7 @@ int CmdInfo::execute (std::string& output)
if (task->has ("until"))
{
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 file

@ -225,7 +225,7 @@
#define STRING_CMD_INFO_USAGE "Shows all data and metadata"
#define STRING_CMD_INFO_BLOCKED "This task blocked by"
#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_TOTAL_ACTIVE "Total active time"
#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_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_EXPIRED "Task {1} '{2}' expired and was deleted."
// File
#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_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
#define STRING_CMD_SHOW "Shows all configuration variables or subset"
#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;
}
///////////////////////////////////////////////////////////////////////////////
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 (
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&);
std::string onProjectChange (Task&, bool scope = true);
std::string onProjectChange (Task&, Task&);
std::string onExpiration (Task&);
// sort.cpp
void sort_tasks (std::vector <Task>&, std::vector <int>&, const std::string&);

View file

@ -56,6 +56,7 @@ extern Context context;
void handleRecurrence ()
{
std::vector <Task> tasks = context.tdb2.pending.get_tasks ();
Date now;
// Look at all tasks and find any recurring ones.
std::vector <Task>::iterator t;
@ -68,12 +69,10 @@ void handleRecurrence ()
std::vector <Date> due;
if (!generateDueDates (*t, due))
{
std::cout << format (STRING_RECUR_PAST_UNTIL, trim (t->get ("description")))
<< "\n";
// Determine the end date.
t->setStatus (Task::deleted);
context.tdb2.modify (*t);
context.footnote (onExpiration (*t));
continue;
}
@ -137,6 +136,18 @@ void handleRecurrence ()
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};
# 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');
like ($output, qr/Recur until\s+\d+\/\d+\/\d{4}/, 'until is shown as a date');
unlike ($output, qr/Until\s+\d{10}/, 'until is not shown as an epoch');
like ($output, qr/Until\s+\d+\/\d+\/\d{4}/, 'until is shown as a date');
# Cleanup.
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 5 do};
$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};
like ($output, qr/No duplicates found/, 'No duplicate UUIDs detected');