mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Enhancement - history report
- Implemented history report. - Improved (not to the point of them working) filters. - Improved automatic filter construction. - Removed obsolete members from Task. - Added tag support to the "add" command.
This commit is contained in:
parent
85e38e67d3
commit
9f6b112003
7 changed files with 89 additions and 111 deletions
|
@ -173,8 +173,8 @@ std::string Context::dispatch ()
|
||||||
else if (cmd.command == "help") { out = longUsage (); }
|
else if (cmd.command == "help") { out = longUsage (); }
|
||||||
else if (cmd.command == "stats") { out = handleReportStats (); }
|
else if (cmd.command == "stats") { out = handleReportStats (); }
|
||||||
else if (cmd.command == "info") { out = handleInfo (); }
|
else if (cmd.command == "info") { out = handleInfo (); }
|
||||||
/*
|
|
||||||
else if (cmd.command == "history") { out = handleReportHistory (); }
|
else if (cmd.command == "history") { out = handleReportHistory (); }
|
||||||
|
/*
|
||||||
else if (cmd.command == "ghistory") { out = handleReportGHistory (); }
|
else if (cmd.command == "ghistory") { out = handleReportGHistory (); }
|
||||||
else if (cmd.command == "calendar") { out = handleReportCalendar (); }
|
else if (cmd.command == "calendar") { out = handleReportCalendar (); }
|
||||||
else if (cmd.command == "summary") { out = handleReportSummary (); }
|
else if (cmd.command == "summary") { out = handleReportSummary (); }
|
||||||
|
@ -365,6 +365,7 @@ std::cout << "[1;31m# parse tag addition '" << *arg << "'[0m" << std::endl;
|
||||||
foundSomethingAfterSequence = true;
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
tagAdditions.push_back (arg->substr (1, std::string::npos));
|
tagAdditions.push_back (arg->substr (1, std::string::npos));
|
||||||
|
task.addTag (arg->substr (1, std::string::npos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tags to remove begin with '-'.
|
// Tags to remove begin with '-'.
|
||||||
|
@ -451,7 +452,7 @@ std::cout << "[1;31m# parse post-termination description '" << *arg << "'[0m"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noVerticalSpace (descCandidate))
|
if (descCandidate != "" && noVerticalSpace (descCandidate))
|
||||||
task.set ("description", descCandidate);
|
task.set ("description", descCandidate);
|
||||||
|
|
||||||
// TODO task.validate ()
|
// TODO task.validate ()
|
||||||
|
@ -484,16 +485,46 @@ std::cout << "[1;31m# parse post-termination description '" << *arg << "'[0m"
|
||||||
void Context::constructFilter ()
|
void Context::constructFilter ()
|
||||||
{
|
{
|
||||||
foreach (att, task)
|
foreach (att, task)
|
||||||
if (att->first != "uuid")
|
{
|
||||||
filter.push_back (att->second);
|
|
||||||
|
|
||||||
// TODO this doesn't work.
|
// TODO this doesn't work.
|
||||||
if (task.has ("description"))
|
if (att->first == "description")
|
||||||
{
|
{
|
||||||
std::vector <std::string> words;
|
std::vector <std::string> words;
|
||||||
split (words, task.get ("description"), ' ');
|
split (words, att->second.value (), ' ');
|
||||||
foreach (word, words)
|
foreach (word, words)
|
||||||
filter.push_back (Att ("description", "contains", *word));
|
{
|
||||||
|
filter.push_back (Att ("description", "has", *word));
|
||||||
|
std::cout << "Context::constructFilter " << att->first << "=" << att->second.value () << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Don't create a uuid for every task?
|
||||||
|
// Every task has a unique uuid by default, and it shouldn't be included.
|
||||||
|
// The mechanism for filtering on tags is +/-<tag>, not tags:foo which
|
||||||
|
// means that there can only be one tag, "foo".
|
||||||
|
else if (att->first != "uuid" &&
|
||||||
|
att->first != "tags")
|
||||||
|
{
|
||||||
|
filter.push_back (att->second);
|
||||||
|
std::cout << "Context::constructFilter " << att->first << "=" << att->second.value () << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Include Annotations as part of the description?
|
||||||
|
|
||||||
|
// Include tagAdditions.
|
||||||
|
foreach (tag, tagAdditions)
|
||||||
|
{
|
||||||
|
filter.push_back (Att ("tags", "has", *tag));
|
||||||
|
std::cout << "Context::constructFilter tags=+" << *tag << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Include tagRemovals.
|
||||||
|
foreach (tag, tagRemovals)
|
||||||
|
{
|
||||||
|
filter.push_back (Att ("tags", "hasnt", *tag));
|
||||||
|
std::cout << "Context::constructFilter tags=-" << *tag << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ public:
|
||||||
std::string program;
|
std::string program;
|
||||||
std::vector <std::string> args;
|
std::vector <std::string> args;
|
||||||
Cmd cmd;
|
Cmd cmd;
|
||||||
std::vector <std::string> tagAdditions;
|
std::vector <std::string> tagAdditions; // TODO This is redundant, remove.
|
||||||
std::vector <std::string> tagRemovals;
|
std::vector <std::string> tagRemovals;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <iostream> // TODO Remove
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "Filter.h"
|
#include "Filter.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -41,6 +42,8 @@ bool Filter::pass (const Record& record) const
|
||||||
// but it doesn't match, fail.
|
// but it doesn't match, fail.
|
||||||
foreach (att, (*this))
|
foreach (att, (*this))
|
||||||
{
|
{
|
||||||
|
// TODO std::cout << "Filter::pass " << att->name () << "=" << att->value () << std::endl;
|
||||||
|
|
||||||
// If the record doesn't have the attribute, match against a default one.
|
// If the record doesn't have the attribute, match against a default one.
|
||||||
// This is because "att" may contain a modifier like "name.not:X".
|
// This is because "att" may contain a modifier like "name.not:X".
|
||||||
if ((r = record.find (att->name ())) == record.end ())
|
if ((r = record.find (att->name ())) == record.end ())
|
||||||
|
|
|
@ -74,12 +74,6 @@ public:
|
||||||
private:
|
private:
|
||||||
int determineVersion (const std::string&);
|
int determineVersion (const std::string&);
|
||||||
void legacyParse (const std::string&);
|
void legacyParse (const std::string&);
|
||||||
|
|
||||||
private:
|
|
||||||
/*
|
|
||||||
std::vector<std::string> mTags;
|
|
||||||
std::vector<std::string> mRemoveTags;
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -73,6 +73,10 @@ std::string handleAdd ()
|
||||||
context.task.set ("priority", defaultPriority);
|
context.task.set ("priority", defaultPriority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include tags.
|
||||||
|
foreach (tag, context.tagAdditions)
|
||||||
|
context.task.addTag (*tag);
|
||||||
|
|
||||||
// Only valid tasks can be added.
|
// Only valid tasks can be added.
|
||||||
context.task.validate ();
|
context.task.validate ();
|
||||||
|
|
||||||
|
|
|
@ -88,22 +88,7 @@ std::string handleCustomReport (const std::string& report)
|
||||||
context.tdb.load (tasks, context.filter);
|
context.tdb.load (tasks, context.filter);
|
||||||
context.tdb.unlock ();
|
context.tdb.unlock ();
|
||||||
|
|
||||||
/*
|
// TODO handleRecurrence (tdb, tasks);
|
||||||
// Load all pending tasks.
|
|
||||||
std::vector <T> tasks;
|
|
||||||
tdb.allPendingT (tasks);
|
|
||||||
handleRecurrence (tdb, tasks);
|
|
||||||
|
|
||||||
// Apply filters.
|
|
||||||
{
|
|
||||||
std::string ignore;
|
|
||||||
T filterTask;
|
|
||||||
parse (filterArgs, ignore, filterTask);
|
|
||||||
|
|
||||||
filter (tasks, filterTask); // Filter from custom report
|
|
||||||
filter (tasks, task); // Filter from command line
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Initialize colorization for subsequent auto colorization.
|
// Initialize colorization for subsequent auto colorization.
|
||||||
initializeColorRules ();
|
initializeColorRules ();
|
||||||
|
|
|
@ -818,7 +818,8 @@ std::string handleReportNext ()
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Year Month Added Completed Deleted
|
// Year Month Added Completed Deleted
|
||||||
// 2006 November 87 63 14
|
// 2006 November 87 63 14
|
||||||
// 2006 December 21 6 1
|
// December 21 6 1
|
||||||
|
// 2007 January 3 12 0
|
||||||
time_t monthlyEpoch (const std::string& date)
|
time_t monthlyEpoch (const std::string& date)
|
||||||
{
|
{
|
||||||
// Convert any date in epoch form to m/d/y, then convert back
|
// Convert any date in epoch form to m/d/y, then convert back
|
||||||
|
@ -839,34 +840,33 @@ time_t monthlyEpoch (const std::string& date)
|
||||||
|
|
||||||
std::string handleReportHistory ()
|
std::string handleReportHistory ()
|
||||||
{
|
{
|
||||||
std::stringstream out;
|
std::map <time_t, int> groups; // Represents any month with data
|
||||||
/*
|
std::map <time_t, int> addedGroup; // Additions by month
|
||||||
std::map <time_t, int> groups;
|
std::map <time_t, int> completedGroup; // Completions by month
|
||||||
std::map <time_t, int> addedGroup;
|
std::map <time_t, int> deletedGroup; // Deletions by month
|
||||||
std::map <time_t, int> completedGroup;
|
|
||||||
std::map <time_t, int> deletedGroup;
|
|
||||||
|
|
||||||
// Scan the pending tasks.
|
// Scan the pending tasks.
|
||||||
std::vector <T> pending;
|
std::vector <Task> tasks;
|
||||||
tdb.allPendingT (pending);
|
context.tdb.lock (context.config.get ("locking", true));
|
||||||
handleRecurrence (tdb, pending);
|
context.tdb.load (tasks, context.filter);
|
||||||
filter (pending, task);
|
context.tdb.unlock ();
|
||||||
for (unsigned int i = 0; i < pending.size (); ++i)
|
// TODO handleRecurrence (tdb, tasks);
|
||||||
{
|
|
||||||
T task (pending[i]);
|
foreach (task, tasks)
|
||||||
time_t epoch = monthlyEpoch (task.getAttribute ("entry"));
|
|
||||||
if (epoch)
|
|
||||||
{
|
{
|
||||||
|
time_t epoch = monthlyEpoch (task->get ("entry"));
|
||||||
groups[epoch] = 0;
|
groups[epoch] = 0;
|
||||||
|
|
||||||
|
// Every task has an entry date.
|
||||||
if (addedGroup.find (epoch) != addedGroup.end ())
|
if (addedGroup.find (epoch) != addedGroup.end ())
|
||||||
addedGroup[epoch] = addedGroup[epoch] + 1;
|
addedGroup[epoch] = addedGroup[epoch] + 1;
|
||||||
else
|
else
|
||||||
addedGroup[epoch] = 1;
|
addedGroup[epoch] = 1;
|
||||||
|
|
||||||
if (task.getStatus () == T::deleted)
|
// All deleted tasks have an end date.
|
||||||
|
if (task->getStatus () == Task::deleted)
|
||||||
{
|
{
|
||||||
epoch = monthlyEpoch (task.getAttribute ("end"));
|
epoch = monthlyEpoch (task->get ("end"));
|
||||||
groups[epoch] = 0;
|
groups[epoch] = 0;
|
||||||
|
|
||||||
if (deletedGroup.find (epoch) != deletedGroup.end ())
|
if (deletedGroup.find (epoch) != deletedGroup.end ())
|
||||||
|
@ -874,9 +874,11 @@ std::string handleReportHistory ()
|
||||||
else
|
else
|
||||||
deletedGroup[epoch] = 1;
|
deletedGroup[epoch] = 1;
|
||||||
}
|
}
|
||||||
else if (task.getStatus () == T::completed)
|
|
||||||
|
// All completed tasks have an end date.
|
||||||
|
else if (task->getStatus () == Task::completed)
|
||||||
{
|
{
|
||||||
epoch = monthlyEpoch (task.getAttribute ("end"));
|
epoch = monthlyEpoch (task->get ("end"));
|
||||||
groups[epoch] = 0;
|
groups[epoch] = 0;
|
||||||
|
|
||||||
if (completedGroup.find (epoch) != completedGroup.end ())
|
if (completedGroup.find (epoch) != completedGroup.end ())
|
||||||
|
@ -885,48 +887,6 @@ std::string handleReportHistory ()
|
||||||
completedGroup[epoch] = 1;
|
completedGroup[epoch] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Scan the completed tasks.
|
|
||||||
std::vector <T> completed;
|
|
||||||
tdb.allCompletedT (completed);
|
|
||||||
filter (completed, task);
|
|
||||||
for (unsigned int i = 0; i < completed.size (); ++i)
|
|
||||||
{
|
|
||||||
T task (completed[i]);
|
|
||||||
time_t epoch = monthlyEpoch (task.getAttribute ("entry"));
|
|
||||||
if (epoch)
|
|
||||||
{
|
|
||||||
groups[epoch] = 0;
|
|
||||||
|
|
||||||
if (addedGroup.find (epoch) != addedGroup.end ())
|
|
||||||
addedGroup[epoch] = addedGroup[epoch] + 1;
|
|
||||||
else
|
|
||||||
addedGroup[epoch] = 1;
|
|
||||||
|
|
||||||
epoch = monthlyEpoch (task.getAttribute ("end"));
|
|
||||||
if (task.getStatus () == T::deleted)
|
|
||||||
{
|
|
||||||
epoch = monthlyEpoch (task.getAttribute ("end"));
|
|
||||||
groups[epoch] = 0;
|
|
||||||
|
|
||||||
if (deletedGroup.find (epoch) != deletedGroup.end ())
|
|
||||||
deletedGroup[epoch] = deletedGroup[epoch] + 1;
|
|
||||||
else
|
|
||||||
deletedGroup[epoch] = 1;
|
|
||||||
}
|
|
||||||
else if (task.getStatus () == T::completed)
|
|
||||||
{
|
|
||||||
epoch = monthlyEpoch (task.getAttribute ("end"));
|
|
||||||
groups[epoch] = 0;
|
|
||||||
|
|
||||||
if (completedGroup.find (epoch) != completedGroup.end ())
|
|
||||||
completedGroup[epoch] = completedGroup[epoch] + 1;
|
|
||||||
else
|
|
||||||
completedGroup[epoch] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now build the table.
|
// Now build the table.
|
||||||
Table table;
|
Table table;
|
||||||
|
@ -1019,13 +979,14 @@ std::string handleReportHistory ()
|
||||||
table.addCell (row, 5, (totalAdded - totalCompleted - totalDeleted) / (table.rowCount () - 2));
|
table.addCell (row, 5, (totalAdded - totalCompleted - totalDeleted) / (table.rowCount () - 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::stringstream out;
|
||||||
if (table.rowCount ())
|
if (table.rowCount ())
|
||||||
out << optionalBlankLine ()
|
out << optionalBlankLine ()
|
||||||
<< table.render ()
|
<< table.render ()
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
else
|
else
|
||||||
out << "No tasks." << std::endl;
|
out << "No tasks." << std::endl;
|
||||||
*/
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue