From 56e4c8172b05870b5c1ae6908e494f823b0251f9 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 28 May 2011 15:11:32 -0400 Subject: [PATCH] Commands - stats - Migrated handleReportStats to CmdStatistics. --- src/Cmd.cpp | 4 - src/Context.cpp | 1 - src/commands/CMakeLists.txt | 1 + src/commands/CmdHelp.cpp | 4 - src/commands/CmdStatistics.cpp | 278 +++++++++++++++++++++++++++++++++ src/commands/CmdStatistics.h | 42 +++++ src/commands/Command.cpp | 2 + src/main.h | 1 - src/report.cpp | 226 --------------------------- 9 files changed, 323 insertions(+), 236 deletions(-) create mode 100644 src/commands/CmdStatistics.cpp create mode 100644 src/commands/CmdStatistics.h diff --git a/src/Cmd.cpp b/src/Cmd.cpp index c6e07d899..6486a3cf7 100644 --- a/src/Cmd.cpp +++ b/src/Cmd.cpp @@ -156,13 +156,11 @@ void Cmd::load () commands.push_back ("delete"); commands.push_back ("done"); commands.push_back ("duplicate"); - commands.push_back ("help"); commands.push_back ("import"); commands.push_back ("log"); commands.push_back ("prepend"); commands.push_back ("projects"); commands.push_back ("start"); - commands.push_back ("stats"); commands.push_back ("stop"); commands.push_back ("summary"); commands.push_back ("timesheet"); @@ -246,10 +244,8 @@ bool Cmd::isReadOnlyCommand () command == "calendar" || command == "colors" || command == "config" || - command == "help" || command == "projects" || command == "push" || - command == "stats" || command == "summary" || command == "timesheet" || validCustom (command)) diff --git a/src/Context.cpp b/src/Context.cpp index eebbf3644..e24ba1363 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -250,7 +250,6 @@ int Context::dispatch (std::string &out) if (cmd.command == "projects") { rc = handleProjects (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 == "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 b91a6c4a6..f9bf3d843 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -17,6 +17,7 @@ set (commands_SRCS Command.cpp Command.h CmdLogo.cpp CmdLogo.h CmdShell.cpp CmdShell.h CmdShow.cpp CmdShow.h + CmdStatistics.cpp CmdStatistics.h CmdTags.cpp CmdTags.h CmdTip.cpp CmdTip.h CmdUrgency.cpp CmdUrgency.h diff --git a/src/commands/CmdHelp.cpp b/src/commands/CmdHelp.cpp index 1d7bf0244..0acd2fa2d 100644 --- a/src/commands/CmdHelp.cpp +++ b/src/commands/CmdHelp.cpp @@ -195,10 +195,6 @@ int CmdHelp::execute (const std::string& command_line, std::string& output) view.set (row, 1, "task calendar [due|month year|year]"); view.set (row, 2, "Shows a calendar, with due tasks marked."); - row = view.addRow (); - view.set (row, 1, "task stats"); - view.set (row, 2, "Shows task database statistics."); - row = view.addRow (); view.set (row, 1, "task import"); view.set (row, 2, "Imports tasks from a variety of formats."); diff --git a/src/commands/CmdStatistics.cpp b/src/commands/CmdStatistics.cpp new file mode 100644 index 000000000..a0cf38cd6 --- /dev/null +++ b/src/commands/CmdStatistics.cpp @@ -0,0 +1,278 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 +#include + +extern Context context; + +//////////////////////////////////////////////////////////////////////////////// +CmdStatistics::CmdStatistics () +{ + _keyword = "stats"; + _usage = "task stats"; + _description = "Shows task database statistics."; + _read_only = true; + _displays_id = false; +} + +//////////////////////////////////////////////////////////////////////////////// +int CmdStatistics::execute (const std::string& command_line, std::string& output) +{ + int rc = 0; + std::stringstream out; + + // Go get the file sizes. + size_t dataSize = 0; + + Directory location (context.config.get ("data.location")); + File pending (location.data + "/pending.data"); + dataSize += pending.size (); + + File completed (location.data + "/completed.data"); + dataSize += completed.size (); + + File undo (location.data + "/undo.data"); + dataSize += undo.size (); + + std::vector undoTxns; + File::read (undo, undoTxns); + int undoCount = 0; + std::vector ::iterator tx; + for (tx = undoTxns.begin (); tx != undoTxns.end (); ++tx) + if (tx->substr (0, 3) == "---") + ++undoCount; + + // Get all the tasks. + std::vector tasks; + context.tdb.lock (context.config.getBoolean ("locking")); + handleRecurrence (); + context.tdb.load (tasks, context.filter); + context.tdb.commit (); + context.tdb.unlock (); + + Date now; + time_t earliest = time (NULL); + time_t latest = 1; + int totalT = 0; + int deletedT = 0; + int pendingT = 0; + int completedT = 0; + int waitingT = 0; + int taggedT = 0; + int annotationsT = 0; + int recurringT = 0; + float daysPending = 0.0; + int descLength = 0; + std::map allTags; + std::map allProjects; + + std::vector ::iterator it; + for (it = tasks.begin (); it != tasks.end (); ++it) + { + ++totalT; + if (it->getStatus () == Task::deleted) ++deletedT; + if (it->getStatus () == Task::pending) ++pendingT; + if (it->getStatus () == Task::completed) ++completedT; + if (it->getStatus () == Task::recurring) ++recurringT; + if (it->getStatus () == Task::waiting) ++waitingT; + + time_t entry = atoi (it->get ("entry").c_str ()); + if (entry < earliest) earliest = entry; + if (entry > latest) latest = entry; + + if (it->getStatus () == Task::completed) + { + time_t end = atoi (it->get ("end").c_str ()); + daysPending += (end - entry) / 86400.0; + } + + if (it->getStatus () == Task::pending) + daysPending += (now.toEpoch () - entry) / 86400.0; + + descLength += it->get ("description").length (); + + std::vector annotations; + it->getAnnotations (annotations); + annotationsT += annotations.size (); + + std::vector tags; + it->getTags (tags); + if (tags.size ()) ++taggedT; + + std::vector ::iterator t; + for (t = tags.begin (); t != tags.end (); ++t) + allTags[*t] = 0; + + std::string project = it->get ("project"); + if (project != "") + allProjects[project] = 0; + } + + // Create a table for output. + ViewText view; + view.width (context.getWidth ()); + view.intraPadding (2); + view.add (Column::factory ("string", "Category")); + view.add (Column::factory ("string", "Data")); + + int row = view.addRow (); + view.set (row, 0, "Pending"); + view.set (row, 1, pendingT); + + row = view.addRow (); + view.set (row, 0, "Waiting"); + view.set (row, 1, waitingT); + + row = view.addRow (); + view.set (row, 0, "Recurring"); + view.set (row, 1, recurringT); + + row = view.addRow (); + view.set (row, 0, "Completed"); + view.set (row, 1, completedT); + + row = view.addRow (); + view.set (row, 0, "Deleted"); + view.set (row, 1, deletedT); + + row = view.addRow (); + view.set (row, 0, "Total"); + view.set (row, 1, totalT); + + row = view.addRow (); + view.set (row, 0, "Annotations"); + view.set (row, 1, annotationsT); + + row = view.addRow (); + view.set (row, 0, "Unique tags"); + view.set (row, 1, (int)allTags.size ()); + + row = view.addRow (); + view.set (row, 0, "Projects"); + view.set (row, 1, (int)allProjects.size ()); + + row = view.addRow (); + view.set (row, 0, "Data size"); + view.set (row, 1, formatBytes (dataSize)); + + row = view.addRow (); + view.set (row, 0, "Undo transactions"); + view.set (row, 1, undoCount); + + if (totalT) + { + row = view.addRow (); + view.set (row, 0, "Tasks tagged"); + + std::stringstream value; + value << std::setprecision (3) << (100.0 * taggedT / totalT) << "%"; + view.set (row, 1, value.str ()); + } + + if (tasks.size ()) + { + Date e (earliest); + row = view.addRow (); + view.set (row, 0, "Oldest task"); + view.set (row, 1, e.toString (context.config.get ("dateformat"))); + + Date l (latest); + row = view.addRow (); + view.set (row, 0, "Newest task"); + view.set (row, 1, l.toString (context.config.get ("dateformat"))); + + row = view.addRow (); + view.set (row, 0, "Task used for"); + view.set (row, 1, Duration (latest - earliest).format ()); + } + + if (totalT) + { + row = view.addRow (); + view.set (row, 0, "Task added every"); + view.set (row, 1, Duration (((latest - earliest) / totalT)).format ()); + } + + if (completedT) + { + row = view.addRow (); + view.set (row, 0, "Task completed every"); + view.set (row, 1, Duration ((latest - earliest) / completedT).format ()); + } + + if (deletedT) + { + row = view.addRow (); + view.set (row, 0, "Task deleted every"); + view.set (row, 1, Duration ((latest - earliest) / deletedT).format ()); + } + + if (pendingT || completedT) + { + row = view.addRow (); + view.set (row, 0, "Average time pending"); + view.set (row, 1, Duration ((int) ((daysPending / (pendingT + completedT)) * 86400)).format ()); + } + + if (totalT) + { + row = view.addRow (); + view.set (row, 0, "Average desc length"); + std::stringstream value; + value << (int) (descLength / totalT) << " characters"; + view.set (row, 1, value.str ()); + } + + // If an alternating row color is specified, notify the table. + if (context.color ()) + { + Color alternate (context.config.get ("color.alternate")); + if (alternate.nontrivial ()) + { + view.colorOdd (alternate); + view.intraColorOdd (alternate); + view.extraColorOdd (alternate); + } + } + + out << optionalBlankLine () + << view.render () + << optionalBlankLine (); + + output = out.str (); + return rc; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdStatistics.h b/src/commands/CmdStatistics.h new file mode 100644 index 000000000..e5bbf5cee --- /dev/null +++ b/src/commands/CmdStatistics.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_CMDSTATISTICS +#define INCLUDED_CMDSTATISTICS +#define L10N // Localization complete. + +#include +#include + +class CmdStatistics : public Command +{ +public: + CmdStatistics (); + int execute (const std::string&, std::string&); +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index 213994582..dd318992c 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,7 @@ void Command::factory (std::map & all) c = new CmdLogo (); all[c->keyword ()] = c; c = new CmdShell (); all[c->keyword ()] = c; c = new CmdShow (); all[c->keyword ()] = c; + c = new CmdStatistics (); all[c->keyword ()] = c; c = new CmdTags (); all[c->keyword ()] = c; c = new CmdTip (); all[c->keyword ()] = c; c = new CmdUrgency (); all[c->keyword ()] = c; diff --git a/src/main.h b/src/main.h index 83cc66324..5ee8f4c45 100644 --- a/src/main.h +++ b/src/main.h @@ -86,7 +86,6 @@ int deltaSubstitutions (Task&); // report.cpp int handleReportSummary (std::string&); int handleReportCalendar (std::string&); -int handleReportStats (std::string&); int handleReportTimesheet (std::string&); std::string getFullDescription (Task&, const std::string&); std::string getDueDate (Task&, const std::string&); diff --git a/src/report.cpp b/src/report.cpp index da39eacb9..dcf62091d 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -869,232 +869,6 @@ int handleReportCalendar (std::string& outs) return rc; } -//////////////////////////////////////////////////////////////////////////////// -int handleReportStats (std::string& outs) -{ - int rc = 0; - std::stringstream out; - - // Go get the file sizes. - size_t dataSize = 0; - - Directory location (context.config.get ("data.location")); - File pending (location.data + "/pending.data"); - dataSize += pending.size (); - - File completed (location.data + "/completed.data"); - dataSize += completed.size (); - - File undo (location.data + "/undo.data"); - dataSize += undo.size (); - - std::vector undoTxns; - File::read (undo, undoTxns); - int undoCount = 0; - foreach (tx, undoTxns) - if (tx->substr (0, 3) == "---") - ++undoCount; - - // Get all the tasks. - std::vector tasks; - context.tdb.lock (context.config.getBoolean ("locking")); - handleRecurrence (); - context.tdb.load (tasks, context.filter); - context.tdb.commit (); - context.tdb.unlock (); - - Date now; - time_t earliest = time (NULL); - time_t latest = 1; - int totalT = 0; - int deletedT = 0; - int pendingT = 0; - int completedT = 0; - int waitingT = 0; - int taggedT = 0; - int annotationsT = 0; - int recurringT = 0; - float daysPending = 0.0; - int descLength = 0; - std::map allTags; - std::map allProjects; - - std::vector ::iterator it; - for (it = tasks.begin (); it != tasks.end (); ++it) - { - ++totalT; - if (it->getStatus () == Task::deleted) ++deletedT; - if (it->getStatus () == Task::pending) ++pendingT; - if (it->getStatus () == Task::completed) ++completedT; - if (it->getStatus () == Task::recurring) ++recurringT; - if (it->getStatus () == Task::waiting) ++waitingT; - - time_t entry = atoi (it->get ("entry").c_str ()); - if (entry < earliest) earliest = entry; - if (entry > latest) latest = entry; - - if (it->getStatus () == Task::completed) - { - time_t end = atoi (it->get ("end").c_str ()); - daysPending += (end - entry) / 86400.0; - } - - if (it->getStatus () == Task::pending) - daysPending += (now.toEpoch () - entry) / 86400.0; - - descLength += it->get ("description").length (); - - std::vector annotations; - it->getAnnotations (annotations); - annotationsT += annotations.size (); - - std::vector tags; - it->getTags (tags); - if (tags.size ()) ++taggedT; - - foreach (t, tags) - allTags[*t] = 0; - - std::string project = it->get ("project"); - if (project != "") - allProjects[project] = 0; - } - - // Create a table for output. - ViewText view; - view.width (context.getWidth ()); - view.intraPadding (2); - view.add (Column::factory ("string", "Category")); - view.add (Column::factory ("string", "Data")); - - int row = view.addRow (); - view.set (row, 0, "Pending"); - view.set (row, 1, pendingT); - - row = view.addRow (); - view.set (row, 0, "Waiting"); - view.set (row, 1, waitingT); - - row = view.addRow (); - view.set (row, 0, "Recurring"); - view.set (row, 1, recurringT); - - row = view.addRow (); - view.set (row, 0, "Completed"); - view.set (row, 1, completedT); - - row = view.addRow (); - view.set (row, 0, "Deleted"); - view.set (row, 1, deletedT); - - row = view.addRow (); - view.set (row, 0, "Total"); - view.set (row, 1, totalT); - - row = view.addRow (); - view.set (row, 0, "Annotations"); - view.set (row, 1, annotationsT); - - row = view.addRow (); - view.set (row, 0, "Unique tags"); - view.set (row, 1, (int)allTags.size ()); - - row = view.addRow (); - view.set (row, 0, "Projects"); - view.set (row, 1, (int)allProjects.size ()); - - row = view.addRow (); - view.set (row, 0, "Data size"); - view.set (row, 1, formatBytes (dataSize)); - - row = view.addRow (); - view.set (row, 0, "Undo transactions"); - view.set (row, 1, undoCount); - - if (totalT) - { - row = view.addRow (); - view.set (row, 0, "Tasks tagged"); - - std::stringstream value; - value << std::setprecision (3) << (100.0 * taggedT / totalT) << "%"; - view.set (row, 1, value.str ()); - } - - if (tasks.size ()) - { - Date e (earliest); - row = view.addRow (); - view.set (row, 0, "Oldest task"); - view.set (row, 1, e.toString (context.config.get ("dateformat"))); - - Date l (latest); - row = view.addRow (); - view.set (row, 0, "Newest task"); - view.set (row, 1, l.toString (context.config.get ("dateformat"))); - - row = view.addRow (); - view.set (row, 0, "Task used for"); - view.set (row, 1, Duration (latest - earliest).format ()); - } - - if (totalT) - { - row = view.addRow (); - view.set (row, 0, "Task added every"); - view.set (row, 1, Duration (((latest - earliest) / totalT)).format ()); - } - - if (completedT) - { - row = view.addRow (); - view.set (row, 0, "Task completed every"); - view.set (row, 1, Duration ((latest - earliest) / completedT).format ()); - } - - if (deletedT) - { - row = view.addRow (); - view.set (row, 0, "Task deleted every"); - view.set (row, 1, Duration ((latest - earliest) / deletedT).format ()); - } - - if (pendingT || completedT) - { - row = view.addRow (); - view.set (row, 0, "Average time pending"); - view.set (row, 1, Duration ((int) ((daysPending / (pendingT + completedT)) * 86400)).format ()); - } - - if (totalT) - { - row = view.addRow (); - view.set (row, 0, "Average desc length"); - std::stringstream value; - value << (int) (descLength / totalT) << " characters"; - view.set (row, 1, value.str ()); - } - - // If an alternating row color is specified, notify the table. - if (context.color ()) - { - Color alternate (context.config.get ("color.alternate")); - if (alternate.nontrivial ()) - { - view.colorOdd (alternate); - view.intraColorOdd (alternate); - view.extraColorOdd (alternate); - } - } - - out << optionalBlankLine () - << view.render () - << optionalBlankLine (); - - outs = out.str (); - return rc; -} - /////////////////////////////////////////////////////////////////////////////// std::string getFullDescription (Task& task, const std::string& report) {