Refactor task diffs to handle new attributes

This refactors task(Info)Differences to be methods of Task and to
correctly handle the `annotation_`, `tags_`, and `dep_` attributes.
This commit is contained in:
Dustin J. Mitchell
2021-08-29 04:08:13 +00:00
committed by Tomas Babej
parent 7aee9567a3
commit 309e99d49e
14 changed files with 269 additions and 223 deletions

View File

@@ -42,213 +42,6 @@
static void countTasks (const std::vector <Task>&, const std::string&, int&, int&);
////////////////////////////////////////////////////////////////////////////////
// Converts a vector of tasks to a human-readable string that represents the tasks.
std::string taskIdentifiers (const std::vector <Task>& tasks)
{
std::vector <std::string> identifiers;
identifiers.reserve(tasks.size());
for (const auto& task: tasks)
identifiers.push_back (task.identifier (true));
return join (", ", identifiers);
}
////////////////////////////////////////////////////////////////////////////////
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;
for (auto& att : before.data)
beforeAtts.push_back (att.first);
std::vector <std::string> afterAtts;
for (auto& att : after.data)
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;
for (auto& name : beforeOnly)
out << " - "
<< format ("{1} will be deleted.", Lexer::ucFirst (name))
<< "\n";
// TODO: #2572 - rewrite to look at dep_ and tag_
for (auto& name : afterOnly)
{
if (name == "depends")
{
auto deps_after = after.getDependencyTasks ();
out << " - "
<< format ("Dependencies will be set to '{1}'.", taskIdentifiers (deps_after))
<< "\n";
}
else
out << " - "
<< format ("{1} will be set to '{2}'.",
Lexer::ucFirst (name),
renderAttribute (name, after.get (name)))
<< "\n";
}
for (auto& name : beforeAtts)
{
// 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")
{
auto deps_before = before.getDependencyTasks ();
std::string from = taskIdentifiers (deps_before);
auto deps_after = after.getDependencyTasks ();
std::string to = taskIdentifiers (deps_after);
out << " - "
<< format ("Dependencies will be changed from '{1}' to '{2}'.", from, to)
<< "\n";
}
else
out << " - "
<< format ("{1} will be changed from '{2}' to '{3}'.",
Lexer::ucFirst (name),
renderAttribute (name, before.get (name)),
renderAttribute (name, after.get (name)))
<< "\n";
}
}
// Shouldn't just say nothing.
if (out.str ().length () == 0)
out << " - No changes will be made.\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;
for (auto& att : before.data)
beforeAtts.push_back (att.first);
std::vector <std::string> afterAtts;
for (auto& att : after.data)
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;
for (auto& name : beforeOnly)
{
if (name == "depends")
{
out << format ("Dependencies '{1}' deleted.", taskIdentifiers (before.getDependencyTasks ()))
<< "\n";
}
else if (name.substr (0, 11) == "annotation_")
{
out << format ("Annotation '{1}' deleted.\n", before.get (name));
}
else if (name == "start")
{
Datetime started (before.get ("start"));
Datetime stopped;
if (after.has ("end"))
// Task was marked as finished, use end time
stopped = Datetime (after.get ("end"));
else
// Start attribute was removed, use modification time
stopped = Datetime (current_timestamp);
out << format ("{1} deleted (duration: {2}).",
Lexer::ucFirst (name),
Duration (stopped - started).format ())
<< "\n";
}
else
{
out << format ("{1} deleted.\n", Lexer::ucFirst (name));
}
}
for (auto& name : afterOnly)
{
if (name == "depends")
{
out << format ("Dependencies set to '{1}'.", taskIdentifiers (after.getDependencyTasks ()))
<< "\n";
}
else if (name.substr (0, 11) == "annotation_")
{
out << format ("Annotation of '{1}' added.\n", after.get (name));
}
else
{
if (name == "start")
last_timestamp = current_timestamp;
out << format ("{1} set to '{2}'.",
Lexer::ucFirst (name),
renderAttribute (name, after.get (name), dateformat))
<< "\n";
}
}
for (auto& name : beforeAtts)
if (name != "uuid" &&
name != "modified" &&
before.get (name) != after.get (name) &&
before.get (name) != "" &&
after.get (name) != "")
{
if (name == "depends")
{
auto from = taskIdentifiers (before.getDependencyTasks ());
auto to = taskIdentifiers (after.getDependencyTasks ());
out << format ("Dependencies changed from '{1}' to '{2}'.\n", from, to);
}
else if (name.substr (0, 11) == "annotation_")
{
out << format ("Annotation changed to '{1}'.\n", after.get (name));
}
else
out << format ("{1} changed from '{2}' to '{3}'.",
Lexer::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 << "No changes made.\n";
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
std::string renderAttribute (const std::string& name, const std::string& value, const std::string& format /* = "" */)
{