diff --git a/src/Arguments.cpp b/src/Arguments.cpp index 8cd497b30..07ca94600 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -329,6 +329,7 @@ void Arguments::extract_sequence (std::vector & sequence) if (low > high) throw std::string ("Inverted sequence range high-low."); + // TODO Is this meaningful? if (high - low >= ARGUMENTS_SEQUENCE_MAX_RANGE) throw std::string ("ID Range too large."); diff --git a/src/Cmd.cpp b/src/Cmd.cpp index 0775fe2d0..e897259df 100644 --- a/src/Cmd.cpp +++ b/src/Cmd.cpp @@ -164,7 +164,6 @@ void Cmd::load () commands.push_back ("edit"); commands.push_back ("help"); commands.push_back ("import"); - commands.push_back ("info"); commands.push_back ("log"); commands.push_back ("prepend"); commands.push_back ("projects"); @@ -260,7 +259,6 @@ bool Cmd::isReadOnlyCommand () command == "diagnostics" || command == "config" || command == "help" || - command == "info" || command == "projects" || command == "push" || command == "shell" || diff --git a/src/Context.cpp b/src/Context.cpp index a60188ac2..ec0b53042 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -230,6 +230,9 @@ int Context::dispatch2 (std::string &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. // 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 == "config") { rc = handleConfig (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.annual") { rc = handleReportHistoryAnnual (out); } else if (cmd.command == "ghistory.monthly") { rc = handleReportGHistoryMonthly (out); } diff --git a/src/commands/CMakeLists.txt b/src/commands/CMakeLists.txt index 8905725ed..9e2d5550e 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -9,6 +9,7 @@ set (commands_SRCS Command.cpp Command.h CmdCustom.cpp CmdCustom.h CmdExec.cpp CmdExec.h CmdHelp.cpp CmdHelp.h + CmdInfo.cpp CmdInfo.h CmdInstall.cpp CmdInstall.h CmdLogo.cpp CmdLogo.h CmdShow.cpp CmdShow.h diff --git a/src/commands/CmdHelp.cpp b/src/commands/CmdHelp.cpp index 50b0c391e..58c7654bd 100644 --- a/src/commands/CmdHelp.cpp +++ b/src/commands/CmdHelp.cpp @@ -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, 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 (); view.set (row, 1, "task start ID"); view.set (row, 2, "Marks specified task as started."); diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp new file mode 100644 index 000000000..17db82647 --- /dev/null +++ b/src/commands/CmdInfo.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +extern Context context; +//////////////////////////////////////////////////////////////////////////////// +CmdInfo::CmdInfo () +{ + _keyword = "information"; + _usage = "task information "; + _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 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 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 ::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 blocked; + dependencyGetBlocking (*task, blocked); + if (blocked.size ()) + { + std::stringstream message; + std::vector ::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 blocking; + dependencyGetBlocked (*task, blocking); + if (blocking.size ()) + { + std::stringstream message; + std::vector ::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 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; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdInfo.h b/src/commands/CmdInfo.h new file mode 100644 index 000000000..c4d90b792 --- /dev/null +++ b/src/commands/CmdInfo.h @@ -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 +#include + +class CmdInfo : public Command +{ +public: + CmdInfo (); + int execute (const std::string&, std::string&); +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index 7310e46a4..f4b1af9b4 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ void Command::factory (std::map & all) c = new CmdExec (); all[c->keyword ()] = c; c = new CmdHelp (); 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 CmdShow (); all[c->keyword ()] = c; c = new CmdTags (); all[c->keyword ()] = c; diff --git a/src/main.h b/src/main.h index 0e5e1d837..a425b386a 100644 --- a/src/main.h +++ b/src/main.h @@ -95,7 +95,6 @@ void handleDiagnostics (std::string&); int handleEdit (std::string&); // report.cpp -int handleInfo (std::string&); int handleReportSummary (std::string&); int handleReportCalendar (std::string&); int handleReportStats (std::string&); diff --git a/src/report.cpp b/src/report.cpp index 87d2506cf..da39eacb9 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -51,361 +51,6 @@ static void countTasks (const std::vector &, const std::string&, const std extern Context context; -//////////////////////////////////////////////////////////////////////////////// -// Display all information for the given task. -int handleInfo (std::string& outs) -{ - int rc = 0; - - // Get all the tasks. - std::vector 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 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 ::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 blocked; - dependencyGetBlocking (*task, blocked); - if (blocked.size ()) - { - std::stringstream message; - std::vector ::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 blocking; - dependencyGetBlocked (*task, blocking); - if (blocking.size ()) - { - std::stringstream message; - std::vector ::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 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% // A 12 13d 55% XXXXXXXXXXXXX-----------