- Added feature #471, which makes greater use of projects by reporting
  changes to the completion percentage when it changes.
- Added unit tests.
This commit is contained in:
Paul Beckingham 2010-08-20 23:53:57 -04:00
parent d460e604ff
commit 5c235ce1ef
6 changed files with 193 additions and 6 deletions

View file

@ -24,6 +24,8 @@
+ Added feature #446, task supports now 'sow', 'som' and 'soy' as dates + Added feature #446, task supports now 'sow', 'som' and 'soy' as dates
for 'due', 'wait' and 'until' (thanks to T. Charles Yun). for 'due', 'wait' and 'until' (thanks to T. Charles Yun).
Added as well synonyms soww/eoww plus new socw/eocw for calendar weeks. Added as well synonyms soww/eoww plus new socw/eocw for calendar weeks.
+ Added feature #471, which makes greater use of projects by reporting
changes to the completion percentage when it changes.
+ New 'depends' column for custom reports. + New 'depends' column for custom reports.
+ New 'blocked' report for showing blocked tasks. + New 'blocked' report for showing blocked tasks.
+ Improved man pages (thanks to Andy Lester). + Improved man pages (thanks to Andy Lester).

3
NEWS
View file

@ -7,11 +7,12 @@ New Features in task 1.9.3
$ task ... due:4d $ task ... due:4d
$ task ... due:3wks $ task ... due:3wks
- Now supports the beginning of the week, month and year in dates. - Now supports the beginning of the week, month and year in dates.
- Now supports 'now' as a date. - Now supports 'now' as a date/time.
- Now defines an overdue task as being one second after the due date, - Now defines an overdue task as being one second after the due date,
instead of the day after the due date. instead of the day after the due date.
- Import and export of YAML 1.1, including round-trip capability. - Import and export of YAML 1.1, including round-trip capability.
- New merge capability for syncing task data files. - New merge capability for syncing task data files.
- When completing or modifying a task, the project status is displayed.
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.

View file

@ -140,6 +140,8 @@ int handleAdd (std::string &outs)
out << "Created task " << context.tdb.nextId () << "." << std::endl; out << "Created task " << context.tdb.nextId () << "." << std::endl;
#endif #endif
out << onProjectChange (context.task);
context.tdb.commit (); context.tdb.commit ();
context.tdb.unlock (); context.tdb.unlock ();
@ -206,6 +208,8 @@ int handleLog (std::string &outs)
if (context.config.getBoolean ("echo.command")) if (context.config.getBoolean ("echo.command"))
out << "Logged task." << std::endl; out << "Logged task." << std::endl;
out << onProjectChange (context.task);
outs = out.str (); outs = out.str ();
context.hooks.trigger ("post-log-command"); context.hooks.trigger ("post-log-command");
} }
@ -720,7 +724,8 @@ int handleShow (std::string &outs)
// Complain about configuration variables that are not recognized. // Complain about configuration variables that are not recognized.
// These are the regular configuration variables. // These are the regular configuration variables.
// Note that there is a leading and trailing space, to make searching easier. // Note that there is a leading and trailing space, to make it easier to
// search for whole words.
std::string recognized = std::string recognized =
" annotations blanklines bulk calendar.details calendar.details.report " " annotations blanklines bulk calendar.details calendar.details.report "
"calendar.holidays calendar.legend color color.active color.due color.due.today " "calendar.holidays calendar.legend color color.active color.due color.due.today "
@ -1183,6 +1188,8 @@ int handleDelete (std::string &outs)
<< task->get ("description") << task->get ("description")
<< "'." << "'."
<< std::endl; << std::endl;
out << onProjectChange (*task);
} }
} }
else else
@ -1203,6 +1210,8 @@ int handleDelete (std::string &outs)
<< task->get ("description") << task->get ("description")
<< "'." << "'."
<< std::endl; << std::endl;
out << onProjectChange (*task);
} }
} }
else { else {
@ -1413,6 +1422,8 @@ int handleDone (std::string &outs)
<< "'." << "'."
<< std::endl; << std::endl;
out << onProjectChange (*task, false);
++count; ++count;
context.hooks.trigger ("post-completed", *task); context.hooks.trigger ("post-completed", *task);
} }
@ -1551,6 +1562,10 @@ int handleModify (std::string &outs)
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Proceed with change?")) if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Proceed with change?"))
{ {
context.tdb.update (*other); context.tdb.update (*other);
if (before.get ("project") != other->get ("project"))
out << onProjectChange (before, *other);
++count; ++count;
} }
} }
@ -1626,6 +1641,9 @@ int handleAppend (std::string &outs)
<< "." << "."
<< std::endl; << std::endl;
if (before.get ("project") != other->get ("project"))
out << onProjectChange (before, *other);
++count; ++count;
} }
} }
@ -1703,6 +1721,9 @@ int handlePrepend (std::string &outs)
<< "." << "."
<< std::endl; << std::endl;
if (before.get ("project") != other->get ("project"))
out << onProjectChange (before, *other);
++count; ++count;
} }
} }
@ -1784,6 +1805,9 @@ int handleDuplicate (std::string &outs)
<< task->get ("description") << task->get ("description")
<< "'." << "'."
<< std::endl; << std::endl;
out << onProjectChange (dup);
++count; ++count;
} }

View file

@ -42,10 +42,6 @@
#include "Color.h" #include "Color.h"
#include "../auto.h" #include "../auto.h"
// task.cpp
void gatherNextTasks (std::vector <Task>&);
void onChangeCallback ();
// recur.cpp // recur.cpp
void handleRecurrence (); void handleRecurrence ();
Date getNextRecurrence (Date&, std::string&); Date getNextRecurrence (Date&, std::string&);
@ -107,8 +103,11 @@ int handleReportGHistoryAnnual (std::string &);
int handleReportCalendar (std::string &); int handleReportCalendar (std::string &);
int handleReportStats (std::string &); int handleReportStats (std::string &);
int handleReportTimesheet (std::string &); int handleReportTimesheet (std::string &);
void gatherNextTasks (std::vector <Task>&);
std::string getFullDescription (Task&, const std::string&); std::string getFullDescription (Task&, const std::string&);
std::string getDueDate (Task&, const std::string&); std::string getDueDate (Task&, const std::string&);
std::string onProjectChange (Task&, bool scope = true);
std::string onProjectChange (Task&, Task&);
// custom.cpp // custom.cpp
int handleCustomReport (const std::string&, std::string &); int handleCustomReport (const std::string&, std::string &);

View file

@ -51,6 +51,8 @@
#include <ncurses.h> #include <ncurses.h>
#endif #endif
static void countTasks (const std::vector <Task>&, const std::string&, const std::string&, int&, int&);
extern Context context; extern Context context;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2828,3 +2830,83 @@ std::string getDueDate (Task& task, const std::string& format)
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
std::string onProjectChange (Task& task, bool scope /* = true */)
{
std::stringstream msg;
std::string project = task.get ("project");
if (project != "")
{
if (scope)
msg << "The scope of project '"
<< project
<< "' has changed. ";
// Count pending and done tasks, for this project.
int count_pending = 0;
int count_done = 0;
std::vector <Task> all;
Filter filter;
context.tdb.load (all, filter);
countTasks (all, project, task.get ("uuid"), count_pending, count_done);
countTasks (context.tdb.getAllNew (), project, "nope", count_pending, count_done);
countTasks (context.tdb.getAllModified (), project, "nope", count_pending, count_done);
msg << "Project '"
<< project
<< "' is "
<< (count_done * 100 / (count_done + count_pending))
<< "% complete ("
<< count_pending
<< " of "
<< (count_pending + count_done)
<< " tasks remaining).\n";
}
return msg.str ();
}
///////////////////////////////////////////////////////////////////////////////
std::string onProjectChange (Task& task1, Task& task2)
{
std::string messages = onProjectChange (task1);
messages += onProjectChange (task2);
return messages;
}
///////////////////////////////////////////////////////////////////////////////
static void countTasks (
const std::vector <Task>& all,
const std::string& project,
const std::string& skip,
int& count_pending,
int& count_done)
{
std::vector <Task>::const_iterator it;
for (it = all.begin (); it != all.end (); ++it)
{
if (it->get ("project") == project &&
it->get ("uuid") != skip)
{
switch (it->getStatus ())
{
case Task::pending:
case Task::waiting:
++count_pending;
break;
case Task::completed:
++count_done;
break;
default:
break;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////

79
src/tests/project.t Executable file
View file

@ -0,0 +1,79 @@
#! /usr/bin/perl
################################################################################
## task - a command line task list manager.
##
## Copyright 2006 - 2010, Paul Beckingham.
## All rights reserved.
##
## This program is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the
##
## Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor,
## Boston, MA
## 02110-1301
## USA
##
################################################################################
use strict;
use warnings;
use Test::More tests => 13;
# Create the rc file.
if (open my $fh, '>', 'pro.rc')
{
print $fh "data.location=.\n",
"confirmation=off\n";
close $fh;
ok (-r 'pro.rc', 'Created pro.rc');
}
# Test the project status numbers.
my $output = qx{../task rc:pro.rc add one pro:foo};
like ($output, qr/The scope of project 'foo' has changed\. Project 'foo' is 0% complete \(1 of 1 tasks remaining\)\./, 'add one');
$output = qx{../task rc:pro.rc add two pro:'foo'};
like ($output, qr/The scope of project 'foo' has changed\. Project 'foo' is 0% complete \(2 of 2 tasks remaining\)\./, 'add two');
$output = qx{../task rc:pro.rc add three pro:'foo'};
like ($output, qr/The scope of project 'foo' has changed\. Project 'foo' is 0% complete \(3 of 3 tasks remaining\)\./, 'add three');
$output = qx{../task rc:pro.rc add four pro:'foo'};
like ($output, qr/The scope of project 'foo' has changed\. Project 'foo' is 0% complete \(4 of 4 tasks remaining\)\./, 'add four');
$output = qx{../task rc:pro.rc 1 done};
like ($output, qr/Project 'foo' is 25% complete \(3 of 4 tasks remaining\)\./, 'done one');
$output = qx{../task rc:pro.rc 2 delete};
like ($output, qr/The scope of project 'foo' has changed\. Project 'foo' is 33% complete \(2 of 3 tasks remaining\)\./, 'delete two');
$output = qx{../task rc:pro.rc 3 pro:bar};
like ($output, qr/The scope of project 'foo' has changed\. Project 'foo' is 50% complete \(1 of 2 tasks remaining\)\./, 'change project');
like ($output, qr/The scope of project 'bar' has changed\. Project 'bar' is 0% complete \(1 of 1 tasks remaining\)\./, 'change project');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'completed.data';
ok (!-r 'completed.data', 'Removed completed.data');
unlink 'undo.data';
ok (!-r 'undo.data', 'Removed undo.data');
unlink 'pro.rc';
ok (!-r 'pro.rc', 'Removed pro.rc');
exit 0;