bool taskDiff (const Task& before, const Task& after) { // Attributes are all there is, so figure the different attribute names // between before and after. std::vector <std::string> beforeAtts; Task::const_iterator att; for (att = before.begin (); att != before.end (); ++att) beforeAtts.push_back (att->first); std::vector <std::string> afterAtts; for (att = after.begin (); att != after.end (); ++att) afterAtts.push_back (att->first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); if (beforeOnly.size () != afterOnly.size ()) return true; std::vector <std::string>::iterator name; for (name = beforeAtts.begin (); name != beforeAtts.end (); ++name) if (*name != "uuid" && before.get (*name) != after.get (*name)) return true; return false; }
bool taskDiff (const Task& before, const Task& after) { // Attributes are all there is, so figure the different attribute names // between before and after. std::vector <std::string> beforeAtts; foreach (att, before) beforeAtts.push_back (att->first); std::vector <std::string> afterAtts; foreach (att, after) afterAtts.push_back (att->first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); if (beforeOnly.size () || afterOnly.size ()) return true; foreach (name, beforeAtts) if (*name != "uuid" && before.get (*name) != after.get (*name)) return true; return false; }
int main (int argc, char** argv) { UnitTest t (24); // Ensure environment has no influence. unsetenv ("TASKDATA"); unsetenv ("TASKRC"); // 1,2,3 <=> 2,3,4 std::vector <std::string> string_one; string_one.push_back ("1"); string_one.push_back ("2"); string_one.push_back ("3"); std::vector <std::string> string_two; string_two.push_back ("2"); string_two.push_back ("3"); string_two.push_back ("4"); std::vector <std::string> string_three; string_three.push_back ("2"); string_three.push_back ("3"); string_three.push_back ("4"); std::vector <std::string> string_four; // Differences? t.ok (!listDiff (string_one, string_one), "std::string (1,2,3) == (1,2,3)"); t.ok (listDiff (string_one, string_two), "std::string (1,2,3) != (2,3,4)"); t.ok (listDiff (string_one, string_three), "std::string (1,2,3) != (2,3,4)"); t.ok (listDiff (string_one, string_four), "std::string (1,2,3) != ()"); t.ok (!listDiff (string_four, string_four), "std::string () == ()"); // What are the differences? std::vector<std::string> string_leftOnly; std::vector<std::string> string_rightOnly; listDiff (string_one, string_two, string_leftOnly, string_rightOnly); t.is ((int) string_leftOnly.size (), 1, "std::string (1,2,3) <=> (2,3,4) = 1<-"); t.is (string_leftOnly[0], "1", "std::string (1,2,3) <=> (2,3,4) = 1<-"); t.is ((int) string_rightOnly.size (), 1, "std::string (1,2,3) <=> (2,3,4) = ->4"); t.is (string_rightOnly[0], "4", "std::string (1,2,3) <=> (2,3,4) = ->4"); // What is the intersection? std::vector <std::string> string_join; listIntersect (string_one, string_two, string_join); t.is ((int) string_join.size (), 2, "std::string (1,2,3) intersect (2,3,4) = (2,3)"); t.is (string_join[0], "2", "std::string (1,2,3) intersect (2,3,4) = (2,3)"); t.is (string_join[1], "3", "std::string (1,2,3) intersect (2,3,4) = (2,3)"); // Now do it all again, with integers. // 1,2,3 <=> 2,3,4 std::vector <int> int_one; int_one.push_back (1); int_one.push_back (2); int_one.push_back (3); std::vector <int> int_two; int_two.push_back (2); int_two.push_back (3); int_two.push_back (4); std::vector <int> int_three; int_three.push_back (2); int_three.push_back (3); int_three.push_back (4); std::vector <int> int_four; // Differences? t.ok (!listDiff (int_one, int_one), "int (1,2,3) == (1,2,3)"); t.ok (listDiff (int_one, int_two), "int (1,2,3) != (2,3,4)"); t.ok (listDiff (int_one, int_three), "int (1,2,3) != (2,3,4)"); t.ok (listDiff (int_one, int_four), "int (1,2,3) != ()"); t.ok (!listDiff (int_four, int_four), "int () == ()"); // What are the differences? std::vector<int> int_leftOnly; std::vector<int> int_rightOnly; listDiff (int_one, int_two, int_leftOnly, int_rightOnly); t.is ((int) int_leftOnly.size (), 1, "int (1,2,3) <=> (2,3,4) = 1<-"); t.is (int_leftOnly[0], "1", "int (1,2,3) <=> (2,3,4) = 1<-"); t.is ((int) int_rightOnly.size (), 1, "int (1,2,3) <=> (2,3,4) = ->4"); t.is (int_rightOnly[0], "4", "int (1,2,3) <=> (2,3,4) = ->4"); // What is the intersection? std::vector <int> int_join; listIntersect (int_one, int_two, int_join); t.is ((int) int_join.size (), 2, "int (1,2,3) intersect (2,3,4) = (2,3)"); t.is (int_join[0], "2", "int (1,2,3) intersect (2,3,4) = (2,3)"); t.is (int_join[1], "3", "int (1,2,3) intersect (2,3,4) = (2,3)"); return 0; }
std::string taskDifferences (const Task& before, const Task& after) { // Attributes are all there is, so figure the different attribute names // between before and after. std::vector <std::string> beforeAtts; Task::const_iterator att; for (att = before.begin (); att != before.end (); ++att) beforeAtts.push_back (att->first); std::vector <std::string> afterAtts; for (att = after.begin (); att != after.end (); ++att) afterAtts.push_back (att->first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); // Now start generating a description of the differences. std::stringstream out; std::vector <std::string>::iterator name; for (name = beforeOnly.begin (); name != beforeOnly.end (); ++name) out << " - " << format (STRING_FEEDBACK_DELETED, ucFirst (*name)) << "\n"; for (name = afterOnly.begin (); name != afterOnly.end (); ++name) { if (*name == "depends") { std::vector <int> deps_after; after.getDependencies (deps_after); std::string to; join (to, ", ", deps_after); out << " - " << format (STRING_FEEDBACK_DEP_SET, to) << "\n"; } else out << " - " << format (STRING_FEEDBACK_ATT_SET, ucFirst (*name), renderAttribute (*name, after.get (*name))) << "\n"; } for (name = beforeAtts.begin (); name != beforeAtts.end (); ++name) { // Ignore UUID differences, and find values that changed, but are not also // in the beforeOnly and afterOnly lists, which have been handled above.. if (*name != "uuid" && before.get (*name) != after.get (*name) && std::find (beforeOnly.begin (), beforeOnly.end (), *name) == beforeOnly.end () && std::find (afterOnly.begin (), afterOnly.end (), *name) == afterOnly.end ()) { if (*name == "depends") { std::vector <int> deps_before; before.getDependencies (deps_before); std::string from; join (from, ", ", deps_before); std::vector <int> deps_after; after.getDependencies (deps_after); std::string to; join (to, ", ", deps_after); out << " - " << format (STRING_FEEDBACK_DEP_MOD, from, to) << "\n"; } else out << " - " << format (STRING_FEEDBACK_ATT_MOD, ucFirst (*name), renderAttribute (*name, before.get (*name)), renderAttribute (*name, after.get (*name))) << "\n"; } } // Shouldn't just say nothing. if (out.str ().length () == 0) out << " - " << STRING_FEEDBACK_NOP << "\n"; return out.str (); }
std::string taskInfoDifferences ( const Task& before, const Task& after, const std::string& dateformat, long& last_timestamp, const long current_timestamp) { // Attributes are all there is, so figure the different attribute names // between before and after. std::vector <std::string> beforeAtts; Task::const_iterator att; for (att = before.begin (); att != before.end (); ++att) beforeAtts.push_back (att->first); std::vector <std::string> afterAtts; for (att = after.begin (); att != after.end (); ++att) afterAtts.push_back (att->first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); // Now start generating a description of the differences. std::stringstream out; std::vector <std::string>::iterator name; for (name = beforeOnly.begin (); name != beforeOnly.end (); ++name) { if (*name == "depends") { std::vector <int> deps_before; before.getDependencies (deps_before); std::string from; join (from, ", ", deps_before); out << format (STRING_FEEDBACK_DEP_DEL, from) << "\n"; } else if (name->substr (0, 11) == "annotation_") { out << format (STRING_FEEDBACK_ANN_DEL, before.get (*name)) << "\n"; } else if (*name == "start") { out << format (STRING_FEEDBACK_ATT_DEL_DUR, ucFirst (*name), Duration (current_timestamp - last_timestamp).formatPrecise ()) << "\n"; } else { out << format (STRING_FEEDBACK_ATT_DEL, ucFirst (*name)) << "\n"; } } for (name = afterOnly.begin (); name != afterOnly.end (); ++name) { if (*name == "depends") { std::vector <int> deps_after; after.getDependencies (deps_after); std::string to; join (to, ", ", deps_after); out << format (STRING_FEEDBACK_DEP_WAS_SET, to) << "\n"; } else if (name->substr (0, 11) == "annotation_") { out << format (STRING_FEEDBACK_ANN_ADD, after.get (*name)) << "\n"; } else { if (*name == "start") last_timestamp = current_timestamp; out << format (STRING_FEEDBACK_ATT_WAS_SET, ucFirst (*name), renderAttribute (*name, after.get (*name), dateformat)) << "\n"; } } for (name = beforeAtts.begin (); name != beforeAtts.end (); ++name) if (*name != "uuid" && before.get (*name) != after.get (*name) && before.get (*name) != "" && after.get (*name) != "") { if (*name == "depends") { std::vector <int> deps_before; before.getDependencies (deps_before); std::string from; join (from, ", ", deps_before); std::vector <int> deps_after; after.getDependencies (deps_after); std::string to; join (to, ", ", deps_after); out << format (STRING_FEEDBACK_DEP_WAS_MOD, from, to) << "\n"; } else if (name->substr (0, 11) == "annotation_") { out << format (STRING_FEEDBACK_ANN_WAS_MOD, after.get (*name)) << "\n"; } else out << format (STRING_FEEDBACK_ATT_WAS_MOD, ucFirst (*name), renderAttribute (*name, before.get (*name), dateformat), renderAttribute (*name, after.get (*name), dateformat)) << "\n"; } // Shouldn't just say nothing. if (out.str ().length () == 0) out << STRING_FEEDBACK_WAS_NOP << "\n"; return out.str (); }
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"; } }
std::string taskInfoDifferences (const Task& before, const Task& after) { // Attributes are all there is, so figure the different attribute names // between before and after. std::vector <std::string> beforeAtts; foreach (att, before) beforeAtts.push_back (att->first); std::vector <std::string> afterAtts; foreach (att, after) afterAtts.push_back (att->first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); // Now start generating a description of the differences. std::stringstream out; foreach (name, beforeOnly) { if (*name == "depends") { std::vector <int> deps_before; before.getDependencies (deps_before); std::string from; join (from, ",", deps_before); out << "dependencies '" << from << "' deleted\n"; } else if (name->substr (0, 11) == "annotation_") { out << "annotation '" << before.get (*name) << "' deleted"; } else { out << *name << " deleted\n"; } } foreach (name, afterOnly) { if (*name == "depends") { std::vector <int> deps_after; after.getDependencies (deps_after); std::string to; join (to, ",", deps_after); out << *name << " set to '" << to << "'\n"; } else if (name->substr (0, 11) == "annotation_") { out << "annotation added '" << after.get (*name) << "'\n"; } else out << *name << " set to '" << renderAttribute (*name, after.get (*name)) << "'\n"; } foreach (name, beforeAtts) if (*name != "uuid" && before.get (*name) != after.get (*name)) { if (*name == "depends") { std::vector <int> deps_before; before.getDependencies (deps_before); std::string from; join (from, ",", deps_before); std::vector <int> deps_after; after.getDependencies (deps_after); std::string to; join (to, ",", deps_after); out << *name << " changed from '" << from << "' to '" << to << "'\n"; } else if (name->substr (0, 11) == "annotation_") { out << "annotation '" << after.get (*name) << "'\n"; } else out << *name << " changed from '" << renderAttribute (*name, before.get (*name)) << "' to '" << renderAttribute (*name, after.get (*name)) << "'\n"; } // Shouldn't just say nothing. if (out.str ().length () == 0) out << "No changes made.\n"; return out.str (); }