mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-26 06:37:20 +02:00
Commands - info
- Migrated 'info' command to CmdInfo. - Renamed 'info' command to 'information'.
This commit is contained in:
parent
306f10b420
commit
0ce198ab8c
10 changed files with 451 additions and 363 deletions
|
@ -329,6 +329,7 @@ void Arguments::extract_sequence (std::vector <int>& sequence)
|
||||||
if (low > high)
|
if (low > high)
|
||||||
throw std::string ("Inverted sequence range high-low.");
|
throw std::string ("Inverted sequence range high-low.");
|
||||||
|
|
||||||
|
// TODO Is this meaningful?
|
||||||
if (high - low >= ARGUMENTS_SEQUENCE_MAX_RANGE)
|
if (high - low >= ARGUMENTS_SEQUENCE_MAX_RANGE)
|
||||||
throw std::string ("ID Range too large.");
|
throw std::string ("ID Range too large.");
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,6 @@ void Cmd::load ()
|
||||||
commands.push_back ("edit");
|
commands.push_back ("edit");
|
||||||
commands.push_back ("help");
|
commands.push_back ("help");
|
||||||
commands.push_back ("import");
|
commands.push_back ("import");
|
||||||
commands.push_back ("info");
|
|
||||||
commands.push_back ("log");
|
commands.push_back ("log");
|
||||||
commands.push_back ("prepend");
|
commands.push_back ("prepend");
|
||||||
commands.push_back ("projects");
|
commands.push_back ("projects");
|
||||||
|
@ -260,7 +259,6 @@ bool Cmd::isReadOnlyCommand ()
|
||||||
command == "diagnostics" ||
|
command == "diagnostics" ||
|
||||||
command == "config" ||
|
command == "config" ||
|
||||||
command == "help" ||
|
command == "help" ||
|
||||||
command == "info" ||
|
|
||||||
command == "projects" ||
|
command == "projects" ||
|
||||||
command == "push" ||
|
command == "push" ||
|
||||||
command == "shell" ||
|
command == "shell" ||
|
||||||
|
|
|
@ -230,6 +230,9 @@ int Context::dispatch2 (std::string &out)
|
||||||
return c->execute (commandLine, out);
|
return c->execute (commandLine, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Need to invoke 'information' when a sequence/filter is present, but
|
||||||
|
// no command is specified.
|
||||||
|
|
||||||
// TODO When ::dispatch is eliminated, show usage on unrecognized command.
|
// TODO When ::dispatch is eliminated, show usage on unrecognized command.
|
||||||
// commands["help"]->execute (commandLine, out);
|
// commands["help"]->execute (commandLine, out);
|
||||||
|
|
||||||
|
@ -248,7 +251,6 @@ int Context::dispatch (std::string &out)
|
||||||
else if (cmd.command == "colors") { rc = handleColor (out); }
|
else if (cmd.command == "colors") { rc = handleColor (out); }
|
||||||
else if (cmd.command == "config") { rc = handleConfig (out); }
|
else if (cmd.command == "config") { rc = handleConfig (out); }
|
||||||
else if (cmd.command == "stats") { rc = handleReportStats (out); }
|
else if (cmd.command == "stats") { rc = handleReportStats (out); }
|
||||||
else if (cmd.command == "info") { rc = handleInfo (out); }
|
|
||||||
else if (cmd.command == "history.monthly") { rc = handleReportHistoryMonthly (out); }
|
else if (cmd.command == "history.monthly") { rc = handleReportHistoryMonthly (out); }
|
||||||
else if (cmd.command == "history.annual") { rc = handleReportHistoryAnnual (out); }
|
else if (cmd.command == "history.annual") { rc = handleReportHistoryAnnual (out); }
|
||||||
else if (cmd.command == "ghistory.monthly") { rc = handleReportGHistoryMonthly (out); }
|
else if (cmd.command == "ghistory.monthly") { rc = handleReportGHistoryMonthly (out); }
|
||||||
|
|
|
@ -9,6 +9,7 @@ set (commands_SRCS Command.cpp Command.h
|
||||||
CmdCustom.cpp CmdCustom.h
|
CmdCustom.cpp CmdCustom.h
|
||||||
CmdExec.cpp CmdExec.h
|
CmdExec.cpp CmdExec.h
|
||||||
CmdHelp.cpp CmdHelp.h
|
CmdHelp.cpp CmdHelp.h
|
||||||
|
CmdInfo.cpp CmdInfo.h
|
||||||
CmdInstall.cpp CmdInstall.h
|
CmdInstall.cpp CmdInstall.h
|
||||||
CmdLogo.cpp CmdLogo.h
|
CmdLogo.cpp CmdLogo.h
|
||||||
CmdShow.cpp CmdShow.h
|
CmdShow.cpp CmdShow.h
|
||||||
|
|
|
@ -134,10 +134,6 @@ int CmdHelp::execute (const std::string& command_line, std::string& output)
|
||||||
view.set (row, 1, "task delete ID");
|
view.set (row, 1, "task delete ID");
|
||||||
view.set (row, 2, "Deletes the specified task.");
|
view.set (row, 2, "Deletes the specified task.");
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 1, "task info ID");
|
|
||||||
view.set (row, 2, "Shows all data, metadata for specified task.");
|
|
||||||
|
|
||||||
row = view.addRow ();
|
row = view.addRow ();
|
||||||
view.set (row, 1, "task start ID");
|
view.set (row, 1, "task start ID");
|
||||||
view.set (row, 2, "Marks specified task as started.");
|
view.set (row, 2, "Marks specified task as started.");
|
||||||
|
|
402
src/commands/CmdInfo.cpp
Normal file
402
src/commands/CmdInfo.cpp
Normal file
|
@ -0,0 +1,402 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// taskwarrior - a command line task list manager.
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <Context.h>
|
||||||
|
#include <Date.h>
|
||||||
|
#include <Duration.h>
|
||||||
|
#include <ViewText.h>
|
||||||
|
#include <main.h>
|
||||||
|
#include <text.h>
|
||||||
|
#include <CmdInfo.h>
|
||||||
|
|
||||||
|
extern Context context;
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
CmdInfo::CmdInfo ()
|
||||||
|
{
|
||||||
|
_keyword = "information";
|
||||||
|
_usage = "task information <filter>";
|
||||||
|
_description = "Shows all data and metadata for specified tasks.";
|
||||||
|
_read_only = true;
|
||||||
|
_displays_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int CmdInfo::execute (const std::string& command_line, std::string& output)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
// Get all the tasks.
|
||||||
|
std::vector <Task> tasks;
|
||||||
|
context.tdb.lock (context.config.getBoolean ("locking"));
|
||||||
|
handleRecurrence ();
|
||||||
|
context.tdb.loadPending (tasks, context.filter);
|
||||||
|
context.tdb.commit ();
|
||||||
|
context.tdb.unlock ();
|
||||||
|
|
||||||
|
// Filter sequence.
|
||||||
|
context.filter.applySequence (tasks, context.sequence);
|
||||||
|
|
||||||
|
// Read the undo file.
|
||||||
|
std::vector <std::string> undo;
|
||||||
|
if (context.config.getBoolean ("journal.info"))
|
||||||
|
{
|
||||||
|
Directory location (context.config.get ("data.location"));
|
||||||
|
std::string undoFile = location.data + "/undo.data";
|
||||||
|
File::read (undoFile, undo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the task.
|
||||||
|
std::stringstream out;
|
||||||
|
std::vector <Task>::iterator task;
|
||||||
|
for (task = tasks.begin (); task != tasks.end (); ++task)
|
||||||
|
{
|
||||||
|
ViewText view;
|
||||||
|
view.width (context.getWidth ());
|
||||||
|
view.add (Column::factory ("string", "Name"));
|
||||||
|
view.add (Column::factory ("string", "Value"));
|
||||||
|
|
||||||
|
// If an alternating row color is specified, notify the table.
|
||||||
|
if (context.color ())
|
||||||
|
{
|
||||||
|
Color alternate (context.config.get ("color.alternate"));
|
||||||
|
view.colorOdd (alternate);
|
||||||
|
view.intraColorOdd (alternate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Date now;
|
||||||
|
|
||||||
|
// id
|
||||||
|
int row = view.addRow ();
|
||||||
|
view.set (row, 0, "ID");
|
||||||
|
view.set (row, 1, format (task->id));
|
||||||
|
|
||||||
|
std::string status = ucFirst (Task::statusToText (task->getStatus ()));
|
||||||
|
|
||||||
|
// description
|
||||||
|
Color c;
|
||||||
|
autoColorize (*task, c);
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Description");
|
||||||
|
view.set (row, 1, getFullDescription (*task, "info"), c);
|
||||||
|
|
||||||
|
// status
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Status");
|
||||||
|
view.set (row, 1, status);
|
||||||
|
|
||||||
|
// project
|
||||||
|
if (task->has ("project"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Project");
|
||||||
|
view.set (row, 1, task->get ("project"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// priority
|
||||||
|
if (task->has ("priority"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Priority");
|
||||||
|
view.set (row, 1, task->get ("priority"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// dependencies: blocked
|
||||||
|
{
|
||||||
|
std::vector <Task> blocked;
|
||||||
|
dependencyGetBlocking (*task, blocked);
|
||||||
|
if (blocked.size ())
|
||||||
|
{
|
||||||
|
std::stringstream message;
|
||||||
|
std::vector <Task>::const_iterator it;
|
||||||
|
for (it = blocked.begin (); it != blocked.end (); ++it)
|
||||||
|
message << it->id << " " << it->get ("description") << "\n";
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "This task blocked by");
|
||||||
|
view.set (row, 1, message.str ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dependencies: blocking
|
||||||
|
{
|
||||||
|
std::vector <Task> blocking;
|
||||||
|
dependencyGetBlocked (*task, blocking);
|
||||||
|
if (blocking.size ())
|
||||||
|
{
|
||||||
|
std::stringstream message;
|
||||||
|
std::vector <Task>::const_iterator it;
|
||||||
|
for (it = blocking.begin (); it != blocking.end (); ++it)
|
||||||
|
message << it->id << " " << it->get ("description") << "\n";
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "This task is blocking");
|
||||||
|
view.set (row, 1, message.str ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recur
|
||||||
|
if (task->has ("recur"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Recurrence");
|
||||||
|
view.set (row, 1, task->get ("recur"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// until
|
||||||
|
if (task->has ("until"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Recur until");
|
||||||
|
|
||||||
|
Date dt (atoi (task->get ("until").c_str ()));
|
||||||
|
std::string format = context.config.get ("reportdateformat");
|
||||||
|
if (format == "")
|
||||||
|
format = context.config.get ("dateformat");
|
||||||
|
|
||||||
|
std::string until = getDueDate (*task, format);
|
||||||
|
view.set (row, 1, until);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mask
|
||||||
|
if (task->getStatus () == Task::recurring)
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Mask");
|
||||||
|
view.set (row, 1, task->get ("mask"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->has ("parent"))
|
||||||
|
{
|
||||||
|
// parent
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Parent task");
|
||||||
|
view.set (row, 1, task->get ("parent"));
|
||||||
|
|
||||||
|
// imask
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Mask Index");
|
||||||
|
view.set (row, 1, task->get ("imask"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// due (colored)
|
||||||
|
if (task->has ("due"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Due");
|
||||||
|
|
||||||
|
std::string format = context.config.get ("reportdateformat");
|
||||||
|
if (format == "")
|
||||||
|
format = context.config.get ("dateformat");
|
||||||
|
|
||||||
|
view.set (row, 1, getDueDate (*task, format));
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait
|
||||||
|
if (task->has ("wait"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Waiting until");
|
||||||
|
Date dt (atoi (task->get ("wait").c_str ()));
|
||||||
|
view.set (row, 1, dt.toString (context.config.get ("dateformat")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// start
|
||||||
|
if (task->has ("start"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Start");
|
||||||
|
Date dt (atoi (task->get ("start").c_str ()));
|
||||||
|
view.set (row, 1, dt.toString (context.config.get ("dateformat")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// end
|
||||||
|
if (task->has ("end"))
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "End");
|
||||||
|
Date dt (atoi (task->get ("end").c_str ()));
|
||||||
|
view.set (row, 1, dt.toString (context.config.get ("dateformat")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// tags ...
|
||||||
|
std::vector <std::string> tags;
|
||||||
|
task->getTags (tags);
|
||||||
|
if (tags.size ())
|
||||||
|
{
|
||||||
|
std::string allTags;
|
||||||
|
join (allTags, " ", tags);
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Tags");
|
||||||
|
view.set (row, 1, allTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// uuid
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "UUID");
|
||||||
|
std::string uuid = task->get ("uuid");
|
||||||
|
view.set (row, 1, uuid);
|
||||||
|
|
||||||
|
// entry
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Entered");
|
||||||
|
Date dt (atoi (task->get ("entry").c_str ()));
|
||||||
|
std::string entry = dt.toString (context.config.get ("dateformat"));
|
||||||
|
|
||||||
|
std::string age;
|
||||||
|
std::string created = task->get ("entry");
|
||||||
|
if (created.length ())
|
||||||
|
{
|
||||||
|
Date dt (atoi (created.c_str ()));
|
||||||
|
age = Duration (now - dt).format ();
|
||||||
|
}
|
||||||
|
|
||||||
|
view.set (row, 1, entry + " (" + age + ")");
|
||||||
|
|
||||||
|
// fg
|
||||||
|
std::string color = task->get ("fg");
|
||||||
|
if (color != "")
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Foreground color");
|
||||||
|
view.set (row, 1, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bg
|
||||||
|
color = task->get ("bg");
|
||||||
|
if (color != "")
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Background color");
|
||||||
|
view.set (row, 1, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task::urgency
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "Urgency");
|
||||||
|
view.set (row, 1, trimLeft (format (task->urgency (), 4, 4)));
|
||||||
|
|
||||||
|
// Create a second table, containing undo log change details.
|
||||||
|
ViewText journal;
|
||||||
|
|
||||||
|
// If an alternating row color is specified, notify the table.
|
||||||
|
if (context.color ())
|
||||||
|
{
|
||||||
|
Color alternate (context.config.get ("color.alternate"));
|
||||||
|
journal.colorOdd (alternate);
|
||||||
|
journal.intraColorOdd (alternate);
|
||||||
|
}
|
||||||
|
|
||||||
|
journal.width (context.getWidth ());
|
||||||
|
journal.add (Column::factory ("string", "Date"));
|
||||||
|
journal.add (Column::factory ("string", "Modification"));
|
||||||
|
|
||||||
|
if (context.config.getBoolean ("journal.info") &&
|
||||||
|
undo.size () > 3)
|
||||||
|
{
|
||||||
|
// Scan the undo data for entries matching this task.
|
||||||
|
std::string when;
|
||||||
|
std::string previous;
|
||||||
|
std::string current;
|
||||||
|
unsigned int i = 0;
|
||||||
|
long total_time = 0;
|
||||||
|
while (i < undo.size ())
|
||||||
|
{
|
||||||
|
when = undo[i++];
|
||||||
|
previous = "";
|
||||||
|
if (undo[i].substr (0, 3) == "old")
|
||||||
|
previous = undo[i++];
|
||||||
|
|
||||||
|
current = undo[i++];
|
||||||
|
i++; // Separator
|
||||||
|
|
||||||
|
if (current.find ("uuid:\"" + uuid) != std::string::npos)
|
||||||
|
{
|
||||||
|
if (previous != "")
|
||||||
|
{
|
||||||
|
int row = journal.addRow ();
|
||||||
|
|
||||||
|
Date timestamp (atoi (when.substr (5).c_str ()));
|
||||||
|
journal.set (row, 0, timestamp.toString (context.config.get ("dateformat")));
|
||||||
|
|
||||||
|
Task before (previous.substr (4));
|
||||||
|
Task after (current.substr (4));
|
||||||
|
journal.set (row, 1, taskInfoDifferences (before, after));
|
||||||
|
|
||||||
|
// calculate the total active time
|
||||||
|
if (before.get ("start") == ""
|
||||||
|
&& after.get ("start") != "")
|
||||||
|
{
|
||||||
|
// task started
|
||||||
|
total_time -= timestamp.toEpoch ();
|
||||||
|
}
|
||||||
|
else if (before.get ("start") != ""
|
||||||
|
&& after.get ("start") == "")
|
||||||
|
{
|
||||||
|
// task stopped
|
||||||
|
total_time += timestamp.toEpoch ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add now() if task is still active
|
||||||
|
if (total_time < 0)
|
||||||
|
total_time += Date ().toEpoch ();
|
||||||
|
|
||||||
|
// print total active time
|
||||||
|
if (total_time > 0)
|
||||||
|
{
|
||||||
|
row = journal.addRow ();
|
||||||
|
journal.set (row, 0, "Total active time");
|
||||||
|
journal.set (row, 1, Duration (total_time).format (),
|
||||||
|
(context.color () ? Color ("bold") : Color ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out << optionalBlankLine ()
|
||||||
|
<< view.render ()
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
|
if (journal.rows () > 0)
|
||||||
|
out << journal.render ()
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! tasks.size ())
|
||||||
|
{
|
||||||
|
out << "No matches.\n";
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = out.str ();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
42
src/commands/CmdInfo.h
Normal file
42
src/commands/CmdInfo.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// taskwarrior - a command line task list manager.
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifndef INCLUDED_CMDINFO
|
||||||
|
#define INCLUDED_CMDINFO
|
||||||
|
#define L10N // Localization complete.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <Command.h>
|
||||||
|
|
||||||
|
class CmdInfo : public Command
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CmdInfo ();
|
||||||
|
int execute (const std::string&, std::string&);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
|
@ -31,6 +31,7 @@
|
||||||
#include <CmdCustom.h>
|
#include <CmdCustom.h>
|
||||||
#include <CmdExec.h>
|
#include <CmdExec.h>
|
||||||
#include <CmdHelp.h>
|
#include <CmdHelp.h>
|
||||||
|
#include <CmdInfo.h>
|
||||||
#include <CmdInstall.h>
|
#include <CmdInstall.h>
|
||||||
#include <CmdLogo.h>
|
#include <CmdLogo.h>
|
||||||
#include <CmdShow.h>
|
#include <CmdShow.h>
|
||||||
|
@ -49,6 +50,7 @@ void Command::factory (std::map <std::string, Command*>& all)
|
||||||
c = new CmdExec (); all[c->keyword ()] = c;
|
c = new CmdExec (); all[c->keyword ()] = c;
|
||||||
c = new CmdHelp (); all[c->keyword ()] = c;
|
c = new CmdHelp (); all[c->keyword ()] = c;
|
||||||
c = new CmdInstall (); all[c->keyword ()] = c;
|
c = new CmdInstall (); all[c->keyword ()] = c;
|
||||||
|
c = new CmdInfo (); all[c->keyword ()] = c;
|
||||||
c = new CmdLogo (); all[c->keyword ()] = c;
|
c = new CmdLogo (); all[c->keyword ()] = c;
|
||||||
c = new CmdShow (); all[c->keyword ()] = c;
|
c = new CmdShow (); all[c->keyword ()] = c;
|
||||||
c = new CmdTags (); all[c->keyword ()] = c;
|
c = new CmdTags (); all[c->keyword ()] = c;
|
||||||
|
|
|
@ -95,7 +95,6 @@ void handleDiagnostics (std::string&);
|
||||||
int handleEdit (std::string&);
|
int handleEdit (std::string&);
|
||||||
|
|
||||||
// report.cpp
|
// report.cpp
|
||||||
int handleInfo (std::string&);
|
|
||||||
int handleReportSummary (std::string&);
|
int handleReportSummary (std::string&);
|
||||||
int handleReportCalendar (std::string&);
|
int handleReportCalendar (std::string&);
|
||||||
int handleReportStats (std::string&);
|
int handleReportStats (std::string&);
|
||||||
|
|
355
src/report.cpp
355
src/report.cpp
|
@ -51,361 +51,6 @@ static void countTasks (const std::vector <Task>&, const std::string&, const std
|
||||||
|
|
||||||
extern Context context;
|
extern Context context;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Display all information for the given task.
|
|
||||||
int handleInfo (std::string& outs)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
// Get all the tasks.
|
|
||||||
std::vector <Task> tasks;
|
|
||||||
context.tdb.lock (context.config.getBoolean ("locking"));
|
|
||||||
handleRecurrence ();
|
|
||||||
context.tdb.loadPending (tasks, context.filter);
|
|
||||||
context.tdb.commit ();
|
|
||||||
context.tdb.unlock ();
|
|
||||||
|
|
||||||
// Filter sequence.
|
|
||||||
context.filter.applySequence (tasks, context.sequence);
|
|
||||||
|
|
||||||
// Read the undo file.
|
|
||||||
std::vector <std::string> undo;
|
|
||||||
if (context.config.getBoolean ("journal.info"))
|
|
||||||
{
|
|
||||||
Directory location (context.config.get ("data.location"));
|
|
||||||
std::string undoFile = location.data + "/undo.data";
|
|
||||||
File::read (undoFile, undo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the task.
|
|
||||||
std::stringstream out;
|
|
||||||
std::vector <Task>::iterator task;
|
|
||||||
for (task = tasks.begin (); task != tasks.end (); ++task)
|
|
||||||
{
|
|
||||||
ViewText view;
|
|
||||||
view.width (context.getWidth ());
|
|
||||||
view.add (Column::factory ("string", "Name"));
|
|
||||||
view.add (Column::factory ("string", "Value"));
|
|
||||||
|
|
||||||
// If an alternating row color is specified, notify the table.
|
|
||||||
if (context.color ())
|
|
||||||
{
|
|
||||||
Color alternate (context.config.get ("color.alternate"));
|
|
||||||
view.colorOdd (alternate);
|
|
||||||
view.intraColorOdd (alternate);
|
|
||||||
}
|
|
||||||
|
|
||||||
Date now;
|
|
||||||
|
|
||||||
// id
|
|
||||||
int row = view.addRow ();
|
|
||||||
view.set (row, 0, "ID");
|
|
||||||
view.set (row, 1, format (task->id));
|
|
||||||
|
|
||||||
std::string status = ucFirst (Task::statusToText (task->getStatus ()));
|
|
||||||
|
|
||||||
// description
|
|
||||||
Color c;
|
|
||||||
autoColorize (*task, c);
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Description");
|
|
||||||
view.set (row, 1, getFullDescription (*task, "info"), c);
|
|
||||||
|
|
||||||
// status
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Status");
|
|
||||||
view.set (row, 1, status);
|
|
||||||
|
|
||||||
// project
|
|
||||||
if (task->has ("project"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Project");
|
|
||||||
view.set (row, 1, task->get ("project"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// priority
|
|
||||||
if (task->has ("priority"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Priority");
|
|
||||||
view.set (row, 1, task->get ("priority"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// dependencies: blocked
|
|
||||||
{
|
|
||||||
std::vector <Task> blocked;
|
|
||||||
dependencyGetBlocking (*task, blocked);
|
|
||||||
if (blocked.size ())
|
|
||||||
{
|
|
||||||
std::stringstream message;
|
|
||||||
std::vector <Task>::const_iterator it;
|
|
||||||
for (it = blocked.begin (); it != blocked.end (); ++it)
|
|
||||||
message << it->id << " " << it->get ("description") << "\n";
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "This task blocked by");
|
|
||||||
view.set (row, 1, message.str ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dependencies: blocking
|
|
||||||
{
|
|
||||||
std::vector <Task> blocking;
|
|
||||||
dependencyGetBlocked (*task, blocking);
|
|
||||||
if (blocking.size ())
|
|
||||||
{
|
|
||||||
std::stringstream message;
|
|
||||||
std::vector <Task>::const_iterator it;
|
|
||||||
for (it = blocking.begin (); it != blocking.end (); ++it)
|
|
||||||
message << it->id << " " << it->get ("description") << "\n";
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "This task is blocking");
|
|
||||||
view.set (row, 1, message.str ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// recur
|
|
||||||
if (task->has ("recur"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Recurrence");
|
|
||||||
view.set (row, 1, task->get ("recur"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// until
|
|
||||||
if (task->has ("until"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Recur until");
|
|
||||||
|
|
||||||
Date dt (atoi (task->get ("until").c_str ()));
|
|
||||||
std::string format = context.config.get ("reportdateformat");
|
|
||||||
if (format == "")
|
|
||||||
format = context.config.get ("dateformat");
|
|
||||||
|
|
||||||
std::string until = getDueDate (*task, format);
|
|
||||||
view.set (row, 1, until);
|
|
||||||
}
|
|
||||||
|
|
||||||
// mask
|
|
||||||
if (task->getStatus () == Task::recurring)
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Mask");
|
|
||||||
view.set (row, 1, task->get ("mask"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task->has ("parent"))
|
|
||||||
{
|
|
||||||
// parent
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Parent task");
|
|
||||||
view.set (row, 1, task->get ("parent"));
|
|
||||||
|
|
||||||
// imask
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Mask Index");
|
|
||||||
view.set (row, 1, task->get ("imask"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// due (colored)
|
|
||||||
if (task->has ("due"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Due");
|
|
||||||
|
|
||||||
std::string format = context.config.get ("reportdateformat");
|
|
||||||
if (format == "")
|
|
||||||
format = context.config.get ("dateformat");
|
|
||||||
|
|
||||||
view.set (row, 1, getDueDate (*task, format));
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait
|
|
||||||
if (task->has ("wait"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Waiting until");
|
|
||||||
Date dt (atoi (task->get ("wait").c_str ()));
|
|
||||||
view.set (row, 1, dt.toString (context.config.get ("dateformat")));
|
|
||||||
}
|
|
||||||
|
|
||||||
// start
|
|
||||||
if (task->has ("start"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Start");
|
|
||||||
Date dt (atoi (task->get ("start").c_str ()));
|
|
||||||
view.set (row, 1, dt.toString (context.config.get ("dateformat")));
|
|
||||||
}
|
|
||||||
|
|
||||||
// end
|
|
||||||
if (task->has ("end"))
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "End");
|
|
||||||
Date dt (atoi (task->get ("end").c_str ()));
|
|
||||||
view.set (row, 1, dt.toString (context.config.get ("dateformat")));
|
|
||||||
}
|
|
||||||
|
|
||||||
// tags ...
|
|
||||||
std::vector <std::string> tags;
|
|
||||||
task->getTags (tags);
|
|
||||||
if (tags.size ())
|
|
||||||
{
|
|
||||||
std::string allTags;
|
|
||||||
join (allTags, " ", tags);
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Tags");
|
|
||||||
view.set (row, 1, allTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// uuid
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "UUID");
|
|
||||||
std::string uuid = task->get ("uuid");
|
|
||||||
view.set (row, 1, uuid);
|
|
||||||
|
|
||||||
// entry
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Entered");
|
|
||||||
Date dt (atoi (task->get ("entry").c_str ()));
|
|
||||||
std::string entry = dt.toString (context.config.get ("dateformat"));
|
|
||||||
|
|
||||||
std::string age;
|
|
||||||
std::string created = task->get ("entry");
|
|
||||||
if (created.length ())
|
|
||||||
{
|
|
||||||
Date dt (atoi (created.c_str ()));
|
|
||||||
age = Duration (now - dt).format ();
|
|
||||||
}
|
|
||||||
|
|
||||||
view.set (row, 1, entry + " (" + age + ")");
|
|
||||||
|
|
||||||
// fg
|
|
||||||
std::string color = task->get ("fg");
|
|
||||||
if (color != "")
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Foreground color");
|
|
||||||
view.set (row, 1, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bg
|
|
||||||
color = task->get ("bg");
|
|
||||||
if (color != "")
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Background color");
|
|
||||||
view.set (row, 1, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Task::urgency
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "Urgency");
|
|
||||||
view.set (row, 1, trimLeft (format (task->urgency (), 4, 4)));
|
|
||||||
|
|
||||||
// Create a second table, containing undo log change details.
|
|
||||||
ViewText journal;
|
|
||||||
|
|
||||||
// If an alternating row color is specified, notify the table.
|
|
||||||
if (context.color ())
|
|
||||||
{
|
|
||||||
Color alternate (context.config.get ("color.alternate"));
|
|
||||||
journal.colorOdd (alternate);
|
|
||||||
journal.intraColorOdd (alternate);
|
|
||||||
}
|
|
||||||
|
|
||||||
journal.width (context.getWidth ());
|
|
||||||
journal.add (Column::factory ("string", "Date"));
|
|
||||||
journal.add (Column::factory ("string", "Modification"));
|
|
||||||
|
|
||||||
if (context.config.getBoolean ("journal.info") &&
|
|
||||||
undo.size () > 3)
|
|
||||||
{
|
|
||||||
// Scan the undo data for entries matching this task.
|
|
||||||
std::string when;
|
|
||||||
std::string previous;
|
|
||||||
std::string current;
|
|
||||||
unsigned int i = 0;
|
|
||||||
long total_time = 0;
|
|
||||||
while (i < undo.size ())
|
|
||||||
{
|
|
||||||
when = undo[i++];
|
|
||||||
previous = "";
|
|
||||||
if (undo[i].substr (0, 3) == "old")
|
|
||||||
previous = undo[i++];
|
|
||||||
|
|
||||||
current = undo[i++];
|
|
||||||
i++; // Separator
|
|
||||||
|
|
||||||
if (current.find ("uuid:\"" + uuid) != std::string::npos)
|
|
||||||
{
|
|
||||||
if (previous != "")
|
|
||||||
{
|
|
||||||
int row = journal.addRow ();
|
|
||||||
|
|
||||||
Date timestamp (atoi (when.substr (5).c_str ()));
|
|
||||||
journal.set (row, 0, timestamp.toString (context.config.get ("dateformat")));
|
|
||||||
|
|
||||||
Task before (previous.substr (4));
|
|
||||||
Task after (current.substr (4));
|
|
||||||
journal.set (row, 1, taskInfoDifferences (before, after));
|
|
||||||
|
|
||||||
// calculate the total active time
|
|
||||||
if (before.get ("start") == ""
|
|
||||||
&& after.get ("start") != "")
|
|
||||||
{
|
|
||||||
// task started
|
|
||||||
total_time -= timestamp.toEpoch ();
|
|
||||||
}
|
|
||||||
else if (before.get ("start") != ""
|
|
||||||
&& after.get ("start") == "")
|
|
||||||
{
|
|
||||||
// task stopped
|
|
||||||
total_time += timestamp.toEpoch ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add now() if task is still active
|
|
||||||
if (total_time < 0)
|
|
||||||
total_time += Date ().toEpoch ();
|
|
||||||
|
|
||||||
// print total active time
|
|
||||||
if (total_time > 0)
|
|
||||||
{
|
|
||||||
row = journal.addRow ();
|
|
||||||
journal.set (row, 0, "Total active time");
|
|
||||||
journal.set (row, 1, Duration (total_time).format (),
|
|
||||||
(context.color () ? Color ("bold") : Color ()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out << optionalBlankLine ()
|
|
||||||
<< view.render ()
|
|
||||||
<< "\n";
|
|
||||||
|
|
||||||
if (journal.rows () > 0)
|
|
||||||
out << journal.render ()
|
|
||||||
<< "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! tasks.size ())
|
|
||||||
{
|
|
||||||
out << "No matches.\n";
|
|
||||||
rc = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
outs = out.str ();
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Project Remaining Avg Age Complete 0% 100%
|
// Project Remaining Avg Age Complete 0% 100%
|
||||||
// A 12 13d 55% XXXXXXXXXXXXX-----------
|
// A 12 13d 55% XXXXXXXXXXXXX-----------
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue