int CmdStatistics::execute (std::string& output) { int rc = 0; std::stringstream out; std::string dateformat = context.config.get ("dateformat"); // Go get the file sizes. size_t dataSize = context.tdb2.pending._file.size () + context.tdb2.completed._file.size () + context.tdb2.undo._file.size () /* // TODO Re-enable this once 2.1 has taskd support. + context.tdb2.backlog._file.size () + context.tdb2.synch_key._file.size () */ ; // Count the undo transactions. std::vector <std::string> undoTxns = context.tdb2.undo.get_lines (); int undoCount = 0; std::vector <std::string>::iterator tx; for (tx = undoTxns.begin (); tx != undoTxns.end (); ++tx) if (*tx == "---") ++undoCount; // Get all the tasks. std::vector <Task> all = context.tdb2.all_tasks (); std::vector <Task> filtered; filter (all, filtered); 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 <std::string, int> allTags; std::map <std::string, int> allProjects; std::vector <Task>::iterator task; for (task = filtered.begin (); task != filtered.end (); ++task) { ++totalT; Task::status status = task->getStatus (); switch (status) { case Task::deleted: ++deletedT; break; case Task::pending: ++pendingT; break; case Task::completed: ++completedT; break; case Task::recurring: ++recurringT; break; case Task::waiting: ++waitingT; break; } time_t entry = strtol (task->get ("entry").c_str (), NULL, 10); if (entry < earliest) earliest = entry; if (entry > latest) latest = entry; if (status == Task::completed) { time_t end = strtol (task->get ("end").c_str (), NULL, 10); daysPending += (end - entry) / 86400.0; } if (status == Task::pending) daysPending += (now.toEpoch () - entry) / 86400.0; descLength += task->get ("description").length (); std::map <std::string, std::string> annotations; task->getAnnotations (annotations); annotationsT += annotations.size (); std::vector <std::string> tags; task->getTags (tags); if (tags.size ()) ++taggedT; std::vector <std::string>::iterator t; for (t = tags.begin (); t != tags.end (); ++t) allTags[*t] = 0; std::string project = task->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", STRING_CMD_STATS_CATEGORY)); view.add (Column::factory ("string", STRING_CMD_STATS_DATA)); int row = view.addRow (); view.set (row, 0, STRING_COLUMN_LABEL_STAT_PE); view.set (row, 1, pendingT); row = view.addRow (); view.set (row, 0, STRING_COLUMN_LABEL_STAT_WA); view.set (row, 1, waitingT); row = view.addRow (); view.set (row, 0, STRING_COLUMN_LABEL_STAT_RE); view.set (row, 1, recurringT); row = view.addRow (); view.set (row, 0, STRING_COLUMN_LABEL_STAT_CO); view.set (row, 1, completedT); row = view.addRow (); view.set (row, 0, STRING_COLUMN_LABEL_STAT_DE); view.set (row, 1, deletedT); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_TOTAL); view.set (row, 1, totalT); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_ANNOTATIONS); view.set (row, 1, annotationsT); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_UNIQUE_TAGS); view.set (row, 1, (int)allTags.size ()); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_PROJECTS); view.set (row, 1, (int)allProjects.size ()); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_DATA_SIZE); view.set (row, 1, formatBytes (dataSize)); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_UNDO_TXNS); view.set (row, 1, undoCount); if (totalT) { row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_TAGGED); std::stringstream value; value << std::setprecision (3) << (100.0 * taggedT / totalT) << "%"; view.set (row, 1, value.str ()); } if (filtered.size ()) { Date e (earliest); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_OLDEST); view.set (row, 1, e.toString (dateformat)); Date l (latest); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_NEWEST); view.set (row, 1, l.toString (dateformat)); row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_USED_FOR); view.set (row, 1, Duration (latest - earliest).format ()); } if (totalT) { row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_ADD_EVERY); view.set (row, 1, Duration (((latest - earliest) / totalT)).format ()); } if (completedT) { row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_COMP_EVERY); view.set (row, 1, Duration ((latest - earliest) / completedT).format ()); } if (deletedT) { row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_DEL_EVERY); view.set (row, 1, Duration ((latest - earliest) / deletedT).format ()); } if (pendingT || completedT) { row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_AVG_PEND); view.set (row, 1, Duration ((int) ((daysPending / (pendingT + completedT)) * 86400)).format ()); } if (totalT) { row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_DESC_LEN); view.set (row, 1, format (STRING_CMD_STATS_CHARS, (int) (descLength / totalT))); } /* // TODO Re-enable this when 2.1 has taskd support. Until then, it makes no // sense to include this. row = view.addRow (); view.set (row, 0, STRING_CMD_STATS_LAST_SYNCH); if (context.tdb2.synch_key._file.exists ()) view.set (row, 1, Date (context.tdb2.synch_key._file.mtime ()).toISO ()); else view.set (row, 1, "-"); */ // 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; }
void TDB2::show_diff ( const std::string& current, const std::string& prior, const std::string& when) { ISO8601d lastChange (strtol (when.c_str (), NULL, 10)); // Set the colors. Color color_red (context.color () ? context.config.get ("color.undo.before") : ""); Color color_green (context.color () ? context.config.get ("color.undo.after") : ""); if (context.config.get ("undo.style") == "side") { std::cout << "\n" << format (STRING_TDB2_LAST_MOD, lastChange.toString ()) << "\n"; // Attributes are all there is, so figure the different attribute names // between before and after. ViewText view; view.width (context.getWidth ()); view.intraPadding (2); view.add (Column::factory ("string", "")); view.add (Column::factory ("string", STRING_TDB2_UNDO_PRIOR)); view.add (Column::factory ("string", STRING_TDB2_UNDO_CURRENT)); Color label (context.config.get ("color.label")); view.colorHeader (label); Task after (current); if (prior != "") { Task before (prior); std::vector <std::string> beforeAtts; for (auto& att : before) beforeAtts.push_back (att.first); std::vector <std::string> afterAtts; for (auto& att : after) afterAtts.push_back (att.first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); int row; for (auto& name : beforeOnly) { row = view.addRow (); view.set (row, 0, name); view.set (row, 1, renderAttribute (name, before.get (name)), color_red); } for (auto& att : before) { std::string priorValue = before.get (att.first); std::string currentValue = after.get (att.first); if (currentValue != "") { row = view.addRow (); view.set (row, 0, att.first); view.set (row, 1, renderAttribute (att.first, priorValue), (priorValue != currentValue ? color_red : Color ())); view.set (row, 2, renderAttribute (att.first, currentValue), (priorValue != currentValue ? color_green : Color ())); } } for (auto& name : afterOnly) { row = view.addRow (); view.set (row, 0, name); view.set (row, 2, renderAttribute (name, after.get (name)), color_green); } } else { int row; for (auto& att : after) { row = view.addRow (); view.set (row, 0, att.first); view.set (row, 2, renderAttribute (att.first, after.get (att.first)), color_green); } } std::cout << "\n" << view.render () << "\n"; } // This style looks like this: // --- before 2009-07-04 00:00:25.000000000 +0200 // +++ after 2009-07-04 00:00:45.000000000 +0200 // // - name: old // att deleted // + name: // // - name: old // att changed // + name: new // // - name: // + name: new // att added // else if (context.config.get ("undo.style") == "diff") { // Create reference tasks. Task before; if (prior != "") before.parse (prior); Task after (current); // Generate table header. ViewText view; view.width (context.getWidth ()); view.intraPadding (2); view.add (Column::factory ("string", "")); view.add (Column::factory ("string", "")); int row = view.addRow (); view.set (row, 0, STRING_TDB2_DIFF_PREV, color_red); view.set (row, 1, STRING_TDB2_DIFF_PREV_DESC, color_red); row = view.addRow (); view.set (row, 0, STRING_TDB2_DIFF_CURR, color_green); // Note trailing space. view.set (row, 1, format (STRING_TDB2_DIFF_CURR_DESC, lastChange.toString (context.config.get ("dateformat"))), color_green); view.addRow (); // Add rows to table showing diffs. std::vector <std::string> all = context.getColumns (); // Now factor in the annotation attributes. for (auto& it : before) if (it.first.substr (0, 11) == "annotation_") all.push_back (it.first); for (auto& it : after) if (it.first.substr (0, 11) == "annotation_") all.push_back (it.first); // Now render all the attributes. std::sort (all.begin (), all.end ()); std::string before_att; std::string after_att; std::string last_att; for (auto& a : all) { if (a != last_att) // Skip duplicates. { last_att = a; before_att = before.get (a); after_att = after.get (a); // Don't report different uuid. // Show nothing if values are the unchanged. if (a == "uuid" || before_att == after_att) { // Show nothing - no point displaying that which did not change. // row = view.addRow (); // view.set (row, 0, *a + ":"); // view.set (row, 1, before_att); } // Attribute deleted. else if (before_att != "" && after_att == "") { row = view.addRow (); view.set (row, 0, "-" + a + ":", color_red); view.set (row, 1, before_att, color_red); row = view.addRow (); view.set (row, 0, "+" + a + ":", color_green); } // Attribute added. else if (before_att == "" && after_att != "") { row = view.addRow (); view.set (row, 0, "-" + a + ":", color_red); row = view.addRow (); view.set (row, 0, "+" + a + ":", color_green); view.set (row, 1, after_att, color_green); } // Attribute changed. else { row = view.addRow (); view.set (row, 0, "-" + a + ":", color_red); view.set (row, 1, before_att, color_red); row = view.addRow (); view.set (row, 0, "+" + a + ":", color_green); view.set (row, 1, after_att, color_green); } } } std::cout << "\n" << view.render () << "\n"; } }