- 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
for 'due', 'wait' and 'until' (thanks to T. Charles Yun).
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 'blocked' report for showing blocked tasks.
+ 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:3wks
- 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,
instead of the day after the due date.
- Import and export of YAML 1.1, including round-trip capability.
- 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
list here.

View file

@ -140,6 +140,8 @@ int handleAdd (std::string &outs)
out << "Created task " << context.tdb.nextId () << "." << std::endl;
#endif
out << onProjectChange (context.task);
context.tdb.commit ();
context.tdb.unlock ();
@ -206,6 +208,8 @@ int handleLog (std::string &outs)
if (context.config.getBoolean ("echo.command"))
out << "Logged task." << std::endl;
out << onProjectChange (context.task);
outs = out.str ();
context.hooks.trigger ("post-log-command");
}
@ -720,7 +724,8 @@ int handleShow (std::string &outs)
// Complain about configuration variables that are not recognized.
// 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 =
" annotations blanklines bulk calendar.details calendar.details.report "
"calendar.holidays calendar.legend color color.active color.due color.due.today "
@ -1183,6 +1188,8 @@ int handleDelete (std::string &outs)
<< task->get ("description")
<< "'."
<< std::endl;
out << onProjectChange (*task);
}
}
else
@ -1203,6 +1210,8 @@ int handleDelete (std::string &outs)
<< task->get ("description")
<< "'."
<< std::endl;
out << onProjectChange (*task);
}
}
else {
@ -1413,6 +1422,8 @@ int handleDone (std::string &outs)
<< "'."
<< std::endl;
out << onProjectChange (*task, false);
++count;
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?"))
{
context.tdb.update (*other);
if (before.get ("project") != other->get ("project"))
out << onProjectChange (before, *other);
++count;
}
}
@ -1626,6 +1641,9 @@ int handleAppend (std::string &outs)
<< "."
<< std::endl;
if (before.get ("project") != other->get ("project"))
out << onProjectChange (before, *other);
++count;
}
}
@ -1703,6 +1721,9 @@ int handlePrepend (std::string &outs)
<< "."
<< std::endl;
if (before.get ("project") != other->get ("project"))
out << onProjectChange (before, *other);
++count;
}
}
@ -1784,6 +1805,9 @@ int handleDuplicate (std::string &outs)
<< task->get ("description")
<< "'."
<< std::endl;
out << onProjectChange (dup);
++count;
}

View file

@ -42,10 +42,6 @@
#include "Color.h"
#include "../auto.h"
// task.cpp
void gatherNextTasks (std::vector <Task>&);
void onChangeCallback ();
// recur.cpp
void handleRecurrence ();
Date getNextRecurrence (Date&, std::string&);
@ -107,8 +103,11 @@ int handleReportGHistoryAnnual (std::string &);
int handleReportCalendar (std::string &);
int handleReportStats (std::string &);
int handleReportTimesheet (std::string &);
void gatherNextTasks (std::vector <Task>&);
std::string getFullDescription (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
int handleCustomReport (const std::string&, std::string &);

View file

@ -51,6 +51,8 @@
#include <ncurses.h>
#endif
static void countTasks (const std::vector <Task>&, const std::string&, const std::string&, int&, int&);
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;