mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Merge branch '1.8.0' of git@github.com:pbeckingham/task into cal
This commit is contained in:
commit
8bcf459a52
15 changed files with 286 additions and 252 deletions
|
@ -571,7 +571,7 @@ bool Att::match (const Att& other) const
|
||||||
}
|
}
|
||||||
else if (which == "text")
|
else if (which == "text")
|
||||||
{
|
{
|
||||||
if (::strcmp (mValue.c_str (), other.mValue.c_str ()) <= 0)
|
if (mValue <= other.mValue)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -601,7 +601,7 @@ bool Att::match (const Att& other) const
|
||||||
}
|
}
|
||||||
else if (which == "text")
|
else if (which == "text")
|
||||||
{
|
{
|
||||||
if (::strcmp (mValue.c_str (), other.mValue.c_str ()) >= 0)
|
if (mValue >= other.mValue)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/Cmd.cpp
33
src/Cmd.cpp
|
@ -147,12 +147,19 @@ void Cmd::load ()
|
||||||
if (i->substr (0, 7) == "report.")
|
if (i->substr (0, 7) == "report.")
|
||||||
{
|
{
|
||||||
std::string report = i->substr (7, std::string::npos);
|
std::string report = i->substr (7, std::string::npos);
|
||||||
|
|
||||||
|
// Oh, what a massive hack. Shame. Shame.
|
||||||
|
// The "next" report is in limbo between being a built-in report and
|
||||||
|
// a custom report. The projection is defined as a custom report, but
|
||||||
|
// the restriction is different.
|
||||||
|
if (report.substr (0, 4) == "next")
|
||||||
|
continue;
|
||||||
|
|
||||||
std::string::size_type columns = report.find (".columns");
|
std::string::size_type columns = report.find (".columns");
|
||||||
if (columns != std::string::npos)
|
if (columns != std::string::npos)
|
||||||
{
|
{
|
||||||
report = report.substr (0, columns);
|
report = report.substr (0, columns);
|
||||||
|
|
||||||
|
|
||||||
// Make sure a custom report does not clash with a built-in
|
// Make sure a custom report does not clash with a built-in
|
||||||
// command.
|
// command.
|
||||||
if (std::find (commands.begin (), commands.end (), report) != commands.end ())
|
if (std::find (commands.begin (), commands.end (), report) != commands.end ())
|
||||||
|
@ -165,30 +172,6 @@ void Cmd::load ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Now load the aliases.
|
|
||||||
foreach (i, all)
|
|
||||||
{
|
|
||||||
if (i->substr (0, 6) == "alias.")
|
|
||||||
{
|
|
||||||
std::string name = i->substr (6, std::string::npos);
|
|
||||||
std::string alias = context.config.get (name);
|
|
||||||
|
|
||||||
// Make sure a custom report does not clash with a built-in
|
|
||||||
// command.
|
|
||||||
if (std::find (commands.begin (), commands.end (), report) != commands.end ())
|
|
||||||
throw std::string ("Alias '") + name +
|
|
||||||
"' conflicts with built-in task command.";
|
|
||||||
|
|
||||||
if (std::find (customReports.begin (), customReports.end (), report) != customReports.end ())
|
|
||||||
throw std::string ("Alias '") + name +
|
|
||||||
"' conflicts with custom report.";
|
|
||||||
|
|
||||||
aliases[name] = alias;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,8 @@
|
||||||
#ifndef INCLUDED_CMD
|
#ifndef INCLUDED_CMD
|
||||||
#define INCLUDED_CMD
|
#define INCLUDED_CMD
|
||||||
|
|
||||||
//#include <map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "Cmd.h"
|
|
||||||
|
|
||||||
class Cmd
|
class Cmd
|
||||||
{
|
{
|
||||||
|
@ -59,7 +57,6 @@ private:
|
||||||
private:
|
private:
|
||||||
std::vector <std::string> commands;
|
std::vector <std::string> commands;
|
||||||
std::vector <std::string> customReports;
|
std::vector <std::string> customReports;
|
||||||
// std::map <std::string, std::string> aliases;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -224,6 +224,17 @@ void Config::createDefault (const std::string& home)
|
||||||
fprintf (out, "report.waiting.sort=wait+,priority-,project+\n"); // TODO i18n
|
fprintf (out, "report.waiting.sort=wait+,priority-,project+\n"); // TODO i18n
|
||||||
fprintf (out, "report.waiting.filter=status:waiting\n"); // TODO i18n
|
fprintf (out, "report.waiting.filter=status:waiting\n"); // TODO i18n
|
||||||
|
|
||||||
|
fprintf (out, "report.all.description=Lists all tasks matching the specified criteria\n"); // TODO i18n
|
||||||
|
fprintf (out, "report.all.columns=id,project,priority,due,active,age,description\n"); // TODO i18n
|
||||||
|
fprintf (out, "report.all.labels=ID,Project,Pri,Due,Active,Age,Description\n"); // TODO i18n
|
||||||
|
fprintf (out, "report.all.sort=due+,priority-,project+\n"); // TODO i18n
|
||||||
|
|
||||||
|
fprintf (out, "report.next.description=Lists all tasks matching the specified criteria\n"); // TODO i18n
|
||||||
|
fprintf (out, "report.next.columns=id,project,priority,due,active,age,description\n"); // TODO i18n
|
||||||
|
fprintf (out, "report.next.labels=ID,Project,Pri,Due,Active,Age,Description\n"); // TODO i18n
|
||||||
|
fprintf (out, "report.next.sort=due+,priority-,project+\n"); // TODO i18n
|
||||||
|
fprintf (out, "report.next.filter=status:pending\n"); // TODO i18n
|
||||||
|
|
||||||
fclose (out);
|
fclose (out);
|
||||||
|
|
||||||
std::cout << "Done." << std::endl; // TODO i18n
|
std::cout << "Done." << std::endl; // TODO i18n
|
||||||
|
@ -302,6 +313,17 @@ void Config::setDefaults ()
|
||||||
set ("report.waiting.labels", "ID,Project,Pri,Wait,Age,Description"); // TODO i18n
|
set ("report.waiting.labels", "ID,Project,Pri,Wait,Age,Description"); // TODO i18n
|
||||||
set ("report.waiting.sort", "wait+,priority-,project+"); // TODO i18n
|
set ("report.waiting.sort", "wait+,priority-,project+"); // TODO i18n
|
||||||
set ("report.waiting.filter", "status:waiting"); // TODO i18n
|
set ("report.waiting.filter", "status:waiting"); // TODO i18n
|
||||||
|
|
||||||
|
set ("report.all.description", "Lists all tasks matching the specified criteria"); // TODO i18n
|
||||||
|
set ("report.all.columns", "id,project,priority,due,active,age,description"); // TODO i18n
|
||||||
|
set ("report.all.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n
|
||||||
|
set ("report.all.sort", "due+,priority-,project+"); // TODO i18n
|
||||||
|
|
||||||
|
set ("report.next.description", "Lists all tasks matching the specified criteria"); // TODO i18n
|
||||||
|
set ("report.next.columns", "id,project,priority,due,active,age,description"); // TODO i18n
|
||||||
|
set ("report.next.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n
|
||||||
|
set ("report.next.sort", "due+,priority-,project+"); // TODO i18n
|
||||||
|
set ("report.next.filter", "status:pending"); // TODO i18n
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -93,6 +93,7 @@ void Context::initialize ()
|
||||||
// Load the configuration file from the home directory. If the file cannot
|
// Load the configuration file from the home directory. If the file cannot
|
||||||
// be found, offer to create a sample one.
|
// be found, offer to create a sample one.
|
||||||
loadCorrectConfigFile ();
|
loadCorrectConfigFile ();
|
||||||
|
loadAliases ();
|
||||||
|
|
||||||
// When redirecting output to a file, do not use color, curses.
|
// When redirecting output to a file, do not use color, curses.
|
||||||
if (!isatty (fileno (stdout)))
|
if (!isatty (fileno (stdout)))
|
||||||
|
@ -289,6 +290,24 @@ void Context::shadow ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Only allows aliases 10 deep.
|
||||||
|
std::string Context::canonicalize (const std::string& input) const
|
||||||
|
{
|
||||||
|
std::string canonical = input;
|
||||||
|
|
||||||
|
// Follow the chain.
|
||||||
|
int i = 10; // Safety valve.
|
||||||
|
std::map <std::string, std::string>::const_iterator found;
|
||||||
|
while ((found = aliases.find (canonical)) != aliases.end () && i-- > 0)
|
||||||
|
canonical = found->second;
|
||||||
|
|
||||||
|
if (i < 1)
|
||||||
|
return input;
|
||||||
|
|
||||||
|
return canonical;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void Context::loadCorrectConfigFile ()
|
void Context::loadCorrectConfigFile ()
|
||||||
{
|
{
|
||||||
|
@ -352,6 +371,26 @@ void Context::loadCorrectConfigFile ()
|
||||||
args = filtered;
|
args = filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Context::loadAliases ()
|
||||||
|
{
|
||||||
|
aliases.clear ();
|
||||||
|
|
||||||
|
std::vector <std::string> vars;
|
||||||
|
config.all (vars);
|
||||||
|
foreach (var, vars)
|
||||||
|
{
|
||||||
|
if (var->substr (0, 6) == "alias.")
|
||||||
|
{
|
||||||
|
std::string alias = var->substr (6, std::string::npos);
|
||||||
|
std::string canonical = config.get (*var);
|
||||||
|
|
||||||
|
aliases[alias] = canonical;
|
||||||
|
debug (std::string ("Alias ") + alias + " -> " + canonical);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void Context::parse ()
|
void Context::parse ()
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,8 +65,11 @@ public:
|
||||||
void parse (std::vector <std::string>&, Cmd&, Task&, Sequence&, Subst&, Filter&);
|
void parse (std::vector <std::string>&, Cmd&, Task&, Sequence&, Subst&, Filter&);
|
||||||
void clear ();
|
void clear ();
|
||||||
|
|
||||||
|
std::string canonicalize (const std::string&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadCorrectConfigFile ();
|
void loadCorrectConfigFile ();
|
||||||
|
void loadAliases ();
|
||||||
void autoFilter (Task&, Filter&);
|
void autoFilter (Task&, Filter&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -81,7 +84,8 @@ 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; // TODO This is redundant, remove.
|
std::map <std::string, std::string> aliases;
|
||||||
|
std::vector <std::string> tagAdditions;
|
||||||
std::vector <std::string> tagRemovals;
|
std::vector <std::string> tagRemovals;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
16
src/TDB.cpp
16
src/TDB.cpp
|
@ -382,19 +382,6 @@ int TDB::commit ()
|
||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TODO -> FF4
|
|
||||||
void TDB::upgrade ()
|
|
||||||
{
|
|
||||||
// TODO Read all pending
|
|
||||||
// TODO Write out all pending
|
|
||||||
|
|
||||||
// TODO Read all completed
|
|
||||||
// TODO Write out all completed
|
|
||||||
|
|
||||||
throw std::string ("unimplemented TDB::upgrade");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Scans the pending tasks for any that are completed or deleted, and if so,
|
// Scans the pending tasks for any that are completed or deleted, and if so,
|
||||||
// moves them to the completed.data file. Returns a count of tasks moved.
|
// moves them to the completed.data file. Returns a count of tasks moved.
|
||||||
|
@ -435,7 +422,10 @@ int TDB::gc ()
|
||||||
// Wake up tasks that are waiting.
|
// Wake up tasks that are waiting.
|
||||||
Date wait_date (::atoi (task->get ("wait").c_str ()));
|
Date wait_date (::atoi (task->get ("wait").c_str ()));
|
||||||
if (now > wait_date)
|
if (now > wait_date)
|
||||||
|
{
|
||||||
task->setStatus (Task::pending);
|
task->setStatus (Task::pending);
|
||||||
|
task->remove ("wait");
|
||||||
|
}
|
||||||
|
|
||||||
still_pending.push_back (*task);
|
still_pending.push_back (*task);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ public:
|
||||||
void add (const Task&); // Single task add to pending
|
void add (const Task&); // Single task add to pending
|
||||||
void update (const Task&); // Single task update to pending
|
void update (const Task&); // Single task update to pending
|
||||||
int commit (); // Write out all tasks
|
int commit (); // Write out all tasks
|
||||||
void upgrade (); // Convert both files to FF4
|
|
||||||
int gc (); // Clean up pending
|
int gc (); // Clean up pending
|
||||||
int nextId ();
|
int nextId ();
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,10 @@ Timer::~Timer ()
|
||||||
<< mDescription
|
<< mDescription
|
||||||
<< " "
|
<< " "
|
||||||
<< std::setprecision (6)
|
<< std::setprecision (6)
|
||||||
|
<< std::fixed
|
||||||
<< ((end.tv_sec - mStart.tv_sec) + ((end.tv_usec - mStart.tv_usec )
|
<< ((end.tv_sec - mStart.tv_sec) + ((end.tv_usec - mStart.tv_usec )
|
||||||
/ 1000000.0));
|
/ 1000000.0))
|
||||||
|
<< " sec";
|
||||||
|
|
||||||
context.debug (s.str ());
|
context.debug (s.str ());
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,29 +55,10 @@ std::string handleCustomReport (const std::string& report)
|
||||||
{
|
{
|
||||||
// Load report configuration.
|
// Load report configuration.
|
||||||
std::string columnList = context.config.get ("report." + report + ".columns");
|
std::string columnList = context.config.get ("report." + report + ".columns");
|
||||||
std::vector <std::string> columns;
|
std::string labelList = context.config.get ("report." + report + ".labels");
|
||||||
split (columns, columnList, ',');
|
std::string sortList = context.config.get ("report." + report + ".sort");
|
||||||
validReportColumns (columns);
|
|
||||||
|
|
||||||
std::string labelList = context.config.get ("report." + report + ".labels");
|
|
||||||
std::vector <std::string> labels;
|
|
||||||
split (labels, labelList, ',');
|
|
||||||
|
|
||||||
if (columns.size () != labels.size () && labels.size () != 0)
|
|
||||||
throw std::string ("There are a different number of columns than labels ") +
|
|
||||||
"for report '" + report + "'.";
|
|
||||||
|
|
||||||
std::map <std::string, std::string> columnLabels;
|
|
||||||
if (labels.size ())
|
|
||||||
for (unsigned int i = 0; i < columns.size (); ++i)
|
|
||||||
columnLabels[columns[i]] = labels[i];
|
|
||||||
|
|
||||||
std::string sortList = context.config.get ("report." + report + ".sort");
|
|
||||||
std::vector <std::string> sortOrder;
|
|
||||||
split (sortOrder, sortList, ',');
|
|
||||||
validSortColumns (columns, sortOrder);
|
|
||||||
|
|
||||||
std::string filterList = context.config.get ("report." + report + ".filter");
|
std::string filterList = context.config.get ("report." + report + ".filter");
|
||||||
|
|
||||||
std::vector <std::string> filterArgs;
|
std::vector <std::string> filterArgs;
|
||||||
split (filterArgs, filterList, ' ');
|
split (filterArgs, filterList, ' ');
|
||||||
{
|
{
|
||||||
|
@ -106,6 +87,68 @@ std::string handleCustomReport (const std::string& report)
|
||||||
context.tdb.commit ();
|
context.tdb.commit ();
|
||||||
context.tdb.unlock ();
|
context.tdb.unlock ();
|
||||||
|
|
||||||
|
return runCustomReport (
|
||||||
|
report,
|
||||||
|
columnList,
|
||||||
|
labelList,
|
||||||
|
sortList,
|
||||||
|
filterList,
|
||||||
|
tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This report will eventually become the one report that many others morph into
|
||||||
|
// via the .taskrc file.
|
||||||
|
|
||||||
|
std::string runCustomReport (
|
||||||
|
const std::string& report,
|
||||||
|
const std::string& columnList,
|
||||||
|
const std::string& labelList,
|
||||||
|
const std::string& sortList,
|
||||||
|
const std::string& filterList,
|
||||||
|
std::vector <Task>& tasks)
|
||||||
|
{
|
||||||
|
// Load report configuration.
|
||||||
|
std::vector <std::string> columns;
|
||||||
|
split (columns, columnList, ',');
|
||||||
|
validReportColumns (columns);
|
||||||
|
|
||||||
|
std::vector <std::string> labels;
|
||||||
|
split (labels, labelList, ',');
|
||||||
|
|
||||||
|
if (columns.size () != labels.size () && labels.size () != 0)
|
||||||
|
throw std::string ("There are a different number of columns than labels ") +
|
||||||
|
"for report '" + report + "'.";
|
||||||
|
|
||||||
|
std::map <std::string, std::string> columnLabels;
|
||||||
|
if (labels.size ())
|
||||||
|
for (unsigned int i = 0; i < columns.size (); ++i)
|
||||||
|
columnLabels[columns[i]] = labels[i];
|
||||||
|
|
||||||
|
std::vector <std::string> sortOrder;
|
||||||
|
split (sortOrder, sortList, ',');
|
||||||
|
validSortColumns (columns, sortOrder);
|
||||||
|
|
||||||
|
std::vector <std::string> filterArgs;
|
||||||
|
split (filterArgs, filterList, ' ');
|
||||||
|
{
|
||||||
|
Cmd cmd (report);
|
||||||
|
Task task;
|
||||||
|
Sequence sequence;
|
||||||
|
Subst subst;
|
||||||
|
Filter filter;
|
||||||
|
context.parse (filterArgs, cmd, task, sequence, subst, filter);
|
||||||
|
|
||||||
|
context.sequence.combine (sequence);
|
||||||
|
|
||||||
|
// Allow limit to be overridden by the command line.
|
||||||
|
if (!context.task.has ("limit") && task.has ("limit"))
|
||||||
|
context.task.set ("limit", task.get ("limit"));
|
||||||
|
|
||||||
|
foreach (att, filter)
|
||||||
|
context.filter.push_back (*att);
|
||||||
|
}
|
||||||
|
|
||||||
// Filter sequence.
|
// Filter sequence.
|
||||||
if (context.sequence.size ())
|
if (context.sequence.size ())
|
||||||
context.filter.applySequence (tasks, context.sequence);
|
context.filter.applySequence (tasks, context.sequence);
|
||||||
|
|
|
@ -45,7 +45,7 @@ void validSortColumns (const std::vector <std::string>&, const std::vector <std:
|
||||||
bool validTag (const std::string&);
|
bool validTag (const std::string&);
|
||||||
|
|
||||||
// task.cpp
|
// task.cpp
|
||||||
void gatherNextTasks (std::vector <Task>&, std::vector <int>&);
|
void gatherNextTasks (std::vector <Task>&);
|
||||||
void onChangeCallback ();
|
void onChangeCallback ();
|
||||||
|
|
||||||
// recur.cpp
|
// recur.cpp
|
||||||
|
@ -101,6 +101,9 @@ std::string getDueDate (Task&);
|
||||||
|
|
||||||
// custom.cpp
|
// custom.cpp
|
||||||
std::string handleCustomReport (const std::string&);
|
std::string handleCustomReport (const std::string&);
|
||||||
|
std::string runCustomReport (const std::string&, const std::string&,
|
||||||
|
const std::string&, const std::string&,
|
||||||
|
const std::string&, std::vector <Task>&);
|
||||||
|
|
||||||
// rules.cpp
|
// rules.cpp
|
||||||
void initializeColorRules ();
|
void initializeColorRules ();
|
||||||
|
|
282
src/report.cpp
282
src/report.cpp
|
@ -679,138 +679,50 @@ std::string handleReportSummary ()
|
||||||
//
|
//
|
||||||
std::string handleReportNext ()
|
std::string handleReportNext ()
|
||||||
{
|
{
|
||||||
|
// Load report configuration.
|
||||||
|
std::string columnList = context.config.get ("report.next.columns");
|
||||||
|
std::string labelList = context.config.get ("report.next.labels");
|
||||||
|
std::string sortList = context.config.get ("report.next.sort");
|
||||||
|
std::string filterList = context.config.get ("report.next.filter");
|
||||||
|
|
||||||
|
std::vector <std::string> filterArgs;
|
||||||
|
split (filterArgs, filterList, ' ');
|
||||||
|
{
|
||||||
|
Cmd cmd ("next");
|
||||||
|
Task task;
|
||||||
|
Sequence sequence;
|
||||||
|
Subst subst;
|
||||||
|
Filter filter;
|
||||||
|
context.parse (filterArgs, cmd, task, sequence, subst, filter);
|
||||||
|
|
||||||
|
context.sequence.combine (sequence);
|
||||||
|
|
||||||
|
// Allow limit to be overridden by the command line.
|
||||||
|
if (!context.task.has ("limit") && task.has ("limit"))
|
||||||
|
context.task.set ("limit", task.get ("limit"));
|
||||||
|
|
||||||
|
foreach (att, filter)
|
||||||
|
context.filter.push_back (*att);
|
||||||
|
}
|
||||||
|
|
||||||
// Get all the tasks.
|
// Get all the tasks.
|
||||||
std::vector <Task> tasks;
|
std::vector <Task> tasks;
|
||||||
context.tdb.lock (context.config.get ("locking", true));
|
context.tdb.lock (context.config.get ("locking", true));
|
||||||
handleRecurrence ();
|
handleRecurrence ();
|
||||||
context.tdb.loadPending (tasks, context.filter);
|
context.tdb.load (tasks, context.filter);
|
||||||
context.tdb.commit ();
|
context.tdb.commit ();
|
||||||
context.tdb.unlock ();
|
context.tdb.unlock ();
|
||||||
|
|
||||||
// Restrict to matching subset.
|
// Restrict to matching subset.
|
||||||
std::vector <int> matching;
|
gatherNextTasks (tasks);
|
||||||
gatherNextTasks (tasks, matching);
|
|
||||||
|
|
||||||
initializeColorRules ();
|
return runCustomReport (
|
||||||
|
"next",
|
||||||
// Create a table for output.
|
columnList,
|
||||||
Table table;
|
labelList,
|
||||||
table.setTableWidth (context.getWidth ());
|
sortList,
|
||||||
table.setDateFormat (context.config.get ("dateformat", "m/d/Y"));
|
filterList,
|
||||||
table.addColumn ("ID");
|
tasks);
|
||||||
table.addColumn ("Project");
|
|
||||||
table.addColumn ("Pri");
|
|
||||||
table.addColumn ("Due");
|
|
||||||
table.addColumn ("Active");
|
|
||||||
table.addColumn ("Age");
|
|
||||||
table.addColumn ("Description");
|
|
||||||
|
|
||||||
if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) &&
|
|
||||||
context.config.get (std::string ("fontunderline"), "true"))
|
|
||||||
{
|
|
||||||
table.setColumnUnderline (0);
|
|
||||||
table.setColumnUnderline (1);
|
|
||||||
table.setColumnUnderline (2);
|
|
||||||
table.setColumnUnderline (3);
|
|
||||||
table.setColumnUnderline (4);
|
|
||||||
table.setColumnUnderline (5);
|
|
||||||
table.setColumnUnderline (6);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
table.setTableDashedUnderline ();
|
|
||||||
|
|
||||||
table.setColumnWidth (0, Table::minimum);
|
|
||||||
table.setColumnWidth (1, Table::minimum);
|
|
||||||
table.setColumnWidth (2, Table::minimum);
|
|
||||||
table.setColumnWidth (3, Table::minimum);
|
|
||||||
table.setColumnWidth (4, Table::minimum);
|
|
||||||
table.setColumnWidth (5, Table::minimum);
|
|
||||||
table.setColumnWidth (6, Table::flexible);
|
|
||||||
|
|
||||||
table.setColumnJustification (0, Table::right);
|
|
||||||
table.setColumnJustification (3, Table::right);
|
|
||||||
table.setColumnJustification (5, Table::right);
|
|
||||||
|
|
||||||
table.sortOn (3, Table::ascendingDate);
|
|
||||||
table.sortOn (2, Table::descendingPriority);
|
|
||||||
table.sortOn (1, Table::ascendingCharacter);
|
|
||||||
|
|
||||||
// Iterate over each task, and apply selection criteria.
|
|
||||||
foreach (i, matching)
|
|
||||||
{
|
|
||||||
Date now;
|
|
||||||
|
|
||||||
// Now format the matching task.
|
|
||||||
bool imminent = false;
|
|
||||||
bool overdue = false;
|
|
||||||
std::string due = tasks[*i].get ("due");
|
|
||||||
if (due.length ())
|
|
||||||
{
|
|
||||||
switch (getDueState (due))
|
|
||||||
{
|
|
||||||
case 2: overdue = true; break;
|
|
||||||
case 1: imminent = true; break;
|
|
||||||
case 0:
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Date dt (::atoi (due.c_str ()));
|
|
||||||
due = dt.toString (context.config.get ("dateformat", "m/d/Y"));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string active;
|
|
||||||
if (tasks[*i].has ("start"))
|
|
||||||
active = "*";
|
|
||||||
|
|
||||||
std::string age;
|
|
||||||
std::string created = tasks[*i].get ("entry");
|
|
||||||
if (created.length ())
|
|
||||||
{
|
|
||||||
Date dt (::atoi (created.c_str ()));
|
|
||||||
age = formatSeconds ((time_t) (now - dt));
|
|
||||||
}
|
|
||||||
|
|
||||||
// All criteria match, so add tasks[*i] to the output table.
|
|
||||||
int row = table.addRow ();
|
|
||||||
table.addCell (row, 0, tasks[*i].id);
|
|
||||||
table.addCell (row, 1, tasks[*i].get ("project"));
|
|
||||||
table.addCell (row, 2, tasks[*i].get ("priority"));
|
|
||||||
table.addCell (row, 3, due);
|
|
||||||
table.addCell (row, 4, active);
|
|
||||||
table.addCell (row, 5, age);
|
|
||||||
table.addCell (row, 6, getFullDescription (tasks[*i]));
|
|
||||||
|
|
||||||
if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false))
|
|
||||||
{
|
|
||||||
Text::color fg = Text::colorCode (tasks[*i].get ("fg"));
|
|
||||||
Text::color bg = Text::colorCode (tasks[*i].get ("bg"));
|
|
||||||
autoColorize (tasks[*i], fg, bg);
|
|
||||||
table.setRowFg (row, fg);
|
|
||||||
table.setRowBg (row, bg);
|
|
||||||
|
|
||||||
if (fg == Text::nocolor)
|
|
||||||
{
|
|
||||||
if (overdue)
|
|
||||||
table.setCellFg (row, 3, Text::colorCode (context.config.get ("color.overdue", "red")));
|
|
||||||
else if (imminent)
|
|
||||||
table.setCellFg (row, 3, Text::colorCode (context.config.get ("color.due", "yellow")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream out;
|
|
||||||
if (table.rowCount ())
|
|
||||||
out << optionalBlankLine ()
|
|
||||||
<< table.render ()
|
|
||||||
<< optionalBlankLine ()
|
|
||||||
<< table.rowCount ()
|
|
||||||
<< (table.rowCount () == 1 ? " task" : " tasks")
|
|
||||||
<< std::endl;
|
|
||||||
else
|
|
||||||
out << "No matches."
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
return out.str ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1919,34 +1831,33 @@ std::string handleReportStats ()
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void gatherNextTasks (
|
void gatherNextTasks (std::vector <Task>& tasks)
|
||||||
std::vector <Task>& tasks,
|
|
||||||
std::vector <int>& all)
|
|
||||||
{
|
{
|
||||||
// For counting tasks by project.
|
// For counting tasks by project.
|
||||||
std::map <std::string, int> countByProject;
|
std::map <std::string, int> countByProject;
|
||||||
std::map <int, bool> matching;
|
std::map <int, bool> matching;
|
||||||
|
std::vector <Task> filtered;
|
||||||
Date now;
|
Date now;
|
||||||
|
|
||||||
// How many items per project? Default 3.
|
// How many items per project? Default 3.
|
||||||
int limit = context.config.get ("next", 3);
|
int limit = context.config.get ("next", 3);
|
||||||
|
|
||||||
// due:< 1wk, pri:*
|
// due:< 1wk, pri:*
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
if (tasks[i].has ("due"))
|
if (task->has ("due"))
|
||||||
{
|
{
|
||||||
Date d (::atoi (tasks[i].get ("due").c_str ()));
|
Date d (::atoi (task->get ("due").c_str ()));
|
||||||
if (d < now + (7 * 24 * 60 * 60)) // if due:< 1wk
|
if (d < now + (7 * 24 * 60 * 60)) // if due:< 1wk
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1954,20 +1865,21 @@ void gatherNextTasks (
|
||||||
}
|
}
|
||||||
|
|
||||||
// due:*, pri:H
|
// due:*, pri:H
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
if (tasks[i].has ("due"))
|
if (task->has ("due"))
|
||||||
{
|
{
|
||||||
std::string priority = tasks[i].get ("priority");
|
std::string priority = task->get ("priority");
|
||||||
if (priority == "H")
|
if (priority == "H")
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1975,38 +1887,40 @@ void gatherNextTasks (
|
||||||
}
|
}
|
||||||
|
|
||||||
// pri:H
|
// pri:H
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
std::string priority = tasks[i].get ("priority");
|
std::string priority = task->get ("priority");
|
||||||
if (priority == "H")
|
if (priority == "H")
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// due:*, pri:M
|
// due:*, pri:M
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
if (tasks[i].has ("due"))
|
if (task->has ("due"))
|
||||||
{
|
{
|
||||||
std::string priority = tasks[i].get ("priority");
|
std::string priority = task->get ("priority");
|
||||||
if (priority == "M")
|
if (priority == "M")
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2014,38 +1928,40 @@ void gatherNextTasks (
|
||||||
}
|
}
|
||||||
|
|
||||||
// pri:M
|
// pri:M
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
std::string priority = tasks[i].get ("priority");
|
std::string priority = task->get ("priority");
|
||||||
if (priority == "M")
|
if (priority == "M")
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// due:*, pri:L
|
// due:*, pri:L
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
if (tasks[i].has ("due"))
|
if (task->has ("due"))
|
||||||
{
|
{
|
||||||
std::string priority = tasks[i].get ("priority");
|
std::string priority = task->get ("priority");
|
||||||
if (priority == "L")
|
if (priority == "L")
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2053,47 +1969,47 @@ void gatherNextTasks (
|
||||||
}
|
}
|
||||||
|
|
||||||
// pri:L
|
// pri:L
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
std::string priority = tasks[i].get ("priority");
|
std::string priority = task->get ("priority");
|
||||||
if (priority == "L")
|
if (priority == "L")
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// due:, pri:
|
// due:, pri:
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
if (tasks[i].getStatus () == Task::pending)
|
if (task->getStatus () == Task::pending)
|
||||||
{
|
{
|
||||||
if (tasks[i].has ("due"))
|
if (task->has ("due"))
|
||||||
{
|
{
|
||||||
std::string priority = tasks[i].get ("priority");
|
std::string priority = task->get ("priority");
|
||||||
if (priority == "")
|
if (priority == "")
|
||||||
{
|
{
|
||||||
std::string project = tasks[i].get ("project");
|
std::string project = task->get ("project");
|
||||||
if (countByProject[project] < limit && matching.find (i) == matching.end ())
|
if (countByProject[project] < limit && matching.find (task->id) == matching.end ())
|
||||||
{
|
{
|
||||||
++countByProject[project];
|
++countByProject[project];
|
||||||
matching[i] = true;
|
matching[task->id] = true;
|
||||||
|
filtered.push_back (*task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert map to vector.
|
tasks = filtered;
|
||||||
foreach (i, matching)
|
|
||||||
all.push_back (i->first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
1
src/tests/.gitignore
vendored
1
src/tests/.gitignore
vendored
|
@ -15,3 +15,4 @@ subst.t
|
||||||
filt.t
|
filt.t
|
||||||
cmd.t
|
cmd.t
|
||||||
config.t
|
config.t
|
||||||
|
*.log
|
||||||
|
|
34
src/util.cpp
34
src/util.cpp
|
@ -74,6 +74,40 @@ bool confirm (const std::string& question)
|
||||||
return (answer == "y" || answer == "ye" || answer == "yes") ? true : false; // TODO i18n
|
return (answer == "y" || answer == "ye" || answer == "yes") ? true : false; // TODO i18n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 0 = no
|
||||||
|
// 1 = yes
|
||||||
|
// 2 = all
|
||||||
|
int confirm3 (const std::string& question)
|
||||||
|
{
|
||||||
|
std::vector <std::string> options;
|
||||||
|
options.push_back ("yes");
|
||||||
|
options.push_back ("no");
|
||||||
|
options.push_back ("all");
|
||||||
|
|
||||||
|
std::string answer;
|
||||||
|
std::vector <std::string> matches;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
std::cout << question
|
||||||
|
<< " ("
|
||||||
|
<< options[0] << "/"
|
||||||
|
<< options[1] << "/"
|
||||||
|
<< options[2]
|
||||||
|
<< ") ";
|
||||||
|
|
||||||
|
std::getline (std::cin, answer);
|
||||||
|
answer = trim (answer);
|
||||||
|
autoComplete (answer, options, matches);
|
||||||
|
}
|
||||||
|
while (matches.size () != 1);
|
||||||
|
|
||||||
|
if (matches[0] == "yes") return 1;
|
||||||
|
else if (matches[0] == "all") return 2;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void delay (float f)
|
void delay (float f)
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,6 +51,7 @@ for (typeof (c) *foreach_p = & (c); \
|
||||||
|
|
||||||
// util.cpp
|
// util.cpp
|
||||||
bool confirm (const std::string&);
|
bool confirm (const std::string&);
|
||||||
|
int confirm3 (const std::string&);
|
||||||
void delay (float);
|
void delay (float);
|
||||||
std::string formatSeconds (time_t);
|
std::string formatSeconds (time_t);
|
||||||
std::string formatSecondsCompact (time_t);
|
std::string formatSecondsCompact (time_t);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue