mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Custom Reports - basic implementation
- Custom reports can be defined and run. - Custom columns included. - Custom filter applied. - Custom sorting applied.
This commit is contained in:
parent
dae268a836
commit
6764a6a7ec
1 changed files with 268 additions and 11 deletions
279
src/report.cpp
279
src/report.cpp
|
@ -2673,6 +2673,8 @@ void gatherNextTasks (
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This report will eventually become the one report that many others morph into
|
||||
// via the .taskrc file.
|
||||
std::string handleCustomReport (
|
||||
TDB& tdb,
|
||||
T& task,
|
||||
|
@ -2699,30 +2701,285 @@ std::string handleCustomReport (
|
|||
std::vector <std::string> sortOrder;
|
||||
split (sortOrder, sortList, ',');
|
||||
|
||||
std::string filter = conf.get ("report." + report + ".filter");
|
||||
std::string filterList = conf.get ("report." + report + ".filter");
|
||||
|
||||
std::cout << "# columns " << columnList << std::endl
|
||||
<< "# sort " << sortList << std::endl
|
||||
<< "# filter " << filter << std::endl;
|
||||
<< "# filter " << filterList << std::endl;
|
||||
|
||||
// TODO Load pending tasks.
|
||||
// Load all pending tasks.
|
||||
std::vector <T> tasks;
|
||||
tdb.allT (tasks);
|
||||
// filter (tasks, task);
|
||||
tdb.allPendingT (tasks);
|
||||
|
||||
// TODO Apply filters.
|
||||
// Apply filters.
|
||||
{
|
||||
std::vector <std::string> args;
|
||||
split (args, filterList, ' ');
|
||||
|
||||
std::string ignore;
|
||||
T filterTask;
|
||||
parse (args, ignore, filterTask, conf);
|
||||
|
||||
filter (tasks, filterTask);
|
||||
}
|
||||
|
||||
// Initialize colorization for subsequent auto colorization.
|
||||
initializeColorRules (conf);
|
||||
|
||||
Table table;
|
||||
table.setTableWidth (width);
|
||||
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
|
||||
|
||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
||||
table.addRow ();
|
||||
|
||||
int columnCount = 0;
|
||||
int dueColumn = -1;
|
||||
foreach (col, columns)
|
||||
{
|
||||
// TODO Add column.
|
||||
// TODO Add underline.
|
||||
|
||||
// TODO Add data.
|
||||
foreach (t, tasks)
|
||||
// Add each column individually.
|
||||
if (*col == "id")
|
||||
{
|
||||
table.addColumn ("ID");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::right);
|
||||
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
table.addCell (row, columnCount, tasks[row].getId ());
|
||||
}
|
||||
|
||||
else if (*col == "uuid")
|
||||
{
|
||||
table.addColumn ("UUID");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::left);
|
||||
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
table.addCell (row, columnCount, tasks[row].getUUID ());
|
||||
}
|
||||
|
||||
else if (*col == "project")
|
||||
{
|
||||
table.addColumn ("Project");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::left);
|
||||
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
table.addCell (row, columnCount, tasks[row].getAttribute ("project"));
|
||||
}
|
||||
|
||||
else if (*col == "priority")
|
||||
{
|
||||
table.addColumn ("Pri");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::left);
|
||||
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
table.addCell (row, columnCount, tasks[row].getAttribute ("priority"));
|
||||
}
|
||||
|
||||
else if (*col == "entry")
|
||||
{
|
||||
table.addColumn ("Added");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::right);
|
||||
|
||||
std::string entered;
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
{
|
||||
entered = tasks[row].getAttribute ("entry");
|
||||
if (entered.length ())
|
||||
{
|
||||
Date dt (::atoi (entered.c_str ()));
|
||||
entered = dt.toString (conf.get ("dateformat", "m/d/Y"));
|
||||
table.addCell (row, columnCount, entered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (*col == "start")
|
||||
{
|
||||
table.addColumn ("Started");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::right);
|
||||
|
||||
std::string started;
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
{
|
||||
started = tasks[row].getAttribute ("start");
|
||||
if (started.length ())
|
||||
{
|
||||
Date dt (::atoi (started.c_str ()));
|
||||
started = dt.toString (conf.get ("dateformat", "m/d/Y"));
|
||||
table.addCell (row, columnCount, started);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (*col == "due")
|
||||
{
|
||||
table.addColumn ("Due");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::right);
|
||||
|
||||
std::string due;
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
{
|
||||
due = tasks[row].getAttribute ("due");
|
||||
if (due.length ())
|
||||
{
|
||||
Date dt (::atoi (due.c_str ()));
|
||||
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
|
||||
table.addCell (row, columnCount, due);
|
||||
}
|
||||
}
|
||||
|
||||
dueColumn = columnCount;
|
||||
}
|
||||
|
||||
else if (*col == "age")
|
||||
{
|
||||
table.addColumn ("Age");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::right);
|
||||
|
||||
std::string created;
|
||||
std::string age;
|
||||
Date now;
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
{
|
||||
created = tasks[row].getAttribute ("entry");
|
||||
if (created.length ())
|
||||
{
|
||||
Date dt (::atoi (created.c_str ()));
|
||||
formatTimeDeltaDays (age, (time_t) (now - dt));
|
||||
table.addCell (row, columnCount, age);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (*col == "active")
|
||||
{
|
||||
table.addColumn ("Active");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::left);
|
||||
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
if (tasks[row].getAttribute ("start") != "")
|
||||
table.addCell (row, columnCount, "*");
|
||||
}
|
||||
|
||||
else if (*col == "tags")
|
||||
{
|
||||
table.addColumn ("Tags");
|
||||
table.setColumnWidth (columnCount, Table::minimum);
|
||||
table.setColumnJustification (columnCount, Table::left);
|
||||
|
||||
std::vector <std::string> all;
|
||||
std::string tags;
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
{
|
||||
tasks[row].getTags (all);
|
||||
join (tags, " ", all);
|
||||
table.addCell (row, columnCount, tags);
|
||||
}
|
||||
}
|
||||
|
||||
else if (*col == "description")
|
||||
{
|
||||
table.addColumn ("Description");
|
||||
table.setColumnWidth (columnCount, Table::flexible);
|
||||
table.setColumnJustification (columnCount, Table::left);
|
||||
|
||||
for (unsigned int row = 0; row < tasks.size(); ++row)
|
||||
table.addCell (row, columnCount, tasks[row].getDescription ());
|
||||
}
|
||||
|
||||
// Common to all columns.
|
||||
// Add underline.
|
||||
if (conf.get (std::string ("color"), true))
|
||||
table.setColumnUnderline (columnCount);
|
||||
else
|
||||
table.setTableDashedUnderline ();
|
||||
|
||||
++columnCount;
|
||||
}
|
||||
|
||||
// Dynamically add sort criteria.
|
||||
// Build a map of column names -> index.
|
||||
std::map <std::string, unsigned int> columnIndex;
|
||||
for (unsigned int c = 0; c < columns.size (); ++c)
|
||||
columnIndex[columns[c]] = c;
|
||||
|
||||
foreach (sortColumn, sortOrder)
|
||||
{
|
||||
// Separate column and direction.
|
||||
std::string column = sortColumn->substr (0, sortColumn->length () - 1);
|
||||
char direction = (*sortColumn)[sortColumn->length () - 1];
|
||||
|
||||
if (column == "id")
|
||||
table.sortOn (columnIndex[column],
|
||||
(direction == '+' ?
|
||||
Table::ascendingNumeric :
|
||||
Table::descendingNumeric));
|
||||
|
||||
else if (column == "priority")
|
||||
table.sortOn (columnIndex[column],
|
||||
(direction == '+' ?
|
||||
Table::ascendingPriority :
|
||||
Table::descendingPriority));
|
||||
|
||||
else if (column == "entry" || column == "start" || column == "due")
|
||||
table.sortOn (columnIndex[column],
|
||||
(direction == '+' ?
|
||||
Table::ascendingDate :
|
||||
Table::descendingDate));
|
||||
|
||||
else
|
||||
table.sortOn (columnIndex[column],
|
||||
(direction == '+' ?
|
||||
Table::ascendingCharacter :
|
||||
Table::descendingCharacter));
|
||||
}
|
||||
|
||||
// Now auto colorize all rows.
|
||||
std::string due;
|
||||
bool imminent;
|
||||
bool overdue;
|
||||
for (unsigned int row = 0; row < tasks.size (); ++row)
|
||||
{
|
||||
imminent = false;
|
||||
overdue = false;
|
||||
due = tasks[row].getAttribute ("due");
|
||||
if (due.length ())
|
||||
{
|
||||
switch (getDueState (due))
|
||||
{
|
||||
case 2: overdue = true; break;
|
||||
case 1: imminent = true; break;
|
||||
case 0:
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.get ("color", true))
|
||||
{
|
||||
Text::color fg = Text::colorCode (tasks[row].getAttribute ("fg"));
|
||||
Text::color bg = Text::colorCode (tasks[row].getAttribute ("bg"));
|
||||
autoColorize (tasks[row], fg, bg, conf);
|
||||
table.setRowFg (row, fg);
|
||||
table.setRowBg (row, bg);
|
||||
|
||||
if (fg == Text::nocolor)
|
||||
{
|
||||
if (dueColumn != -1)
|
||||
{
|
||||
if (overdue)
|
||||
table.setCellFg (row, columnCount, Text::colorCode (conf.get ("color.overdue", "red")));
|
||||
else if (imminent)
|
||||
table.setCellFg (row, columnCount, Text::colorCode (conf.get ("color.due", "yellow")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue