diff --git a/ChangeLog b/ChangeLog index 1633bb281..201510051 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/NEWS b/NEWS index b93c838ed..d5796568b 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/doc/man/task.1.in b/doc/man/task.1.in index e95e28d65..e402bac6b 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -546,8 +546,8 @@ Specifies the due-date of a task. Specifies the frequency of a recurrence of a task. .TP -.B until: -Specifies the Recurrence end-date of a task. +.B until: +Specifies the expiration date of a task, after which it will be deleted. .TP .B fg: diff --git a/src/Task.cpp b/src/Task.cpp index fb62338dd..952758f1d 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -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")) { diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index a17b0cafa..6a970e0e1 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -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)); } diff --git a/src/en-US.h b/src/en-US.h index 1e8c17461..b0950ab38 100644 --- a/src/en-US.h +++ b/src/en-US.h @@ -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." diff --git a/src/feedback.cpp b/src/feedback.cpp index 9e72c36c0..84ce3ce89 100644 --- a/src/feedback.cpp +++ b/src/feedback.cpp @@ -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 & all, diff --git a/src/main.h b/src/main.h index 333f37de8..5da13375b 100644 --- a/src/main.h +++ b/src/main.h @@ -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 &, std::vector &, const std::string&); diff --git a/src/recur.cpp b/src/recur.cpp index e70c5dbca..fd1d0cac3 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -56,6 +56,7 @@ extern Context context; void handleRecurrence () { std::vector tasks = context.tdb2.pending.get_tasks (); + Date now; // Look at all tasks and find any recurring ones. std::vector ::iterator t; @@ -68,12 +69,10 @@ void handleRecurrence () std::vector 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)); + } + } } } diff --git a/test/bug.368.t b/test/bug.368.t index ce893df20..ddbf2aa49 100755 --- a/test/bug.368.t +++ b/test/bug.368.t @@ -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); diff --git a/test/recur.until.t b/test/recur.until.t index c3861927b..d1e84b282 100755 --- a/test/recur.until.t +++ b/test/recur.until.t @@ -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');