mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Feature #471
- 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:
parent
d460e604ff
commit
5c235ce1ef
6 changed files with 193 additions and 6 deletions
|
@ -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
3
NEWS
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 &);
|
||||
|
|
|
@ -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
79
src/tests/project.t
Executable 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;
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue