- Fixed bug #1022, where dependencies were note released when a blocking task
  was completed (thanks to Arkady Grudzinsky).
- The Task object now caches ::is_blocked and ::is_blocking Booleans that are
  determined on pending.data load.
- Simplified and sped up color rule processing using cached values, reducing
  the number of map lookups, and removed loop invariants when the rules are
  not defined.
- Simplified urgency calculations given the cached values for blocked/blocking.
- On load, pending.data is scanned for accurate blocked/blocking status
  determination.
- Obsoleted and removed complex single-task dependency calculations.
- Sped up 'nag' processing by using cached values..
- Modified the 'show' command to consider color.blocking to be valid.
- Added default config value for color.blocking, and included it in the
  precedence list ahead of blocked, as it is more important.
- Updated taskrc.5 man page to include the new color.blocking rule, and its
  place in the rule precedence.
This commit is contained in:
Paul Beckingham
2012-07-09 01:18:11 -04:00
parent 02053f7300
commit 79e2c591f1
14 changed files with 182 additions and 178 deletions

View File

@@ -207,6 +207,7 @@ std::string Config::_defaults =
"color.pri.L=rgb245 # Color of priority:L tasks\n"
"color.tagged=rgb031 # Color of tagged tasks\n"
"color.blocked=white on color8 # Color of blocked tasks\n"
"color.blocking=white on color7 # Color of blocking tasks\n"
"#color.completed=on blue # Color of completed tasks\n"
"#color.deleted=on blue # Color of deleted tasks\n"
#else
@@ -267,7 +268,7 @@ std::string Config::_defaults =
"# Here is the rule precedence order, highest to lowest.\n"
"# Note that these are just the color rule names, without the leading 'color.'\n"
"# and any trailing '.value'.\n"
"rule.precedence.color=due.today,active,blocked,overdue,due,scheduled,keyword.,project.,tag.,recurring,pri.,tagged,completed,deleted\n"
"rule.precedence.color=due.today,active,blocking,blocked,overdue,due,scheduled,keyword.,project.,tag.,recurring,pri.,tagged,completed,deleted\n"
"\n"
"# Shadow file support\n"
"#shadow.file=/tmp/shadow.txt # Location of shadow file\n"

View File

@@ -67,6 +67,7 @@ TF2::TF2 ()
, _loaded_tasks (false)
, _loaded_lines (false)
, _has_ids (false)
, _auto_dep_scan (false)
{
}
@@ -311,6 +312,9 @@ void TF2::load_tasks ()
_U2I[task.get ("uuid")] = task.id;
}
}
if (_auto_dep_scan)
dependency_scan ();
_loaded_tasks = true;
}
@@ -382,6 +386,12 @@ void TF2::has_ids ()
_has_ids = true;
}
////////////////////////////////////////////////////////////////////////////////
void TF2::auto_dep_scan ()
{
_auto_dep_scan = true;
}
////////////////////////////////////////////////////////////////////////////////
// Completely wipe it all clean.
void TF2::clear ()
@@ -391,9 +401,10 @@ void TF2::clear ()
_loaded_tasks = false;
_loaded_lines = false;
// Note that the actual file name, and _has_ids are deliberately not cleared.
// Note that these are deliberately not cleared.
//_file._data = "";
//_has_ids = false;
//_auto_dep_scan = false;
_tasks.clear ();
_added_tasks.clear ();
@@ -404,6 +415,49 @@ void TF2::clear ()
_U2I.clear ();
}
////////////////////////////////////////////////////////////////////////////////
// For any task that has depenencies, follow the chain of dependencies until the
// end. Along the way, update the Task::is_blocked and Task::is_blocking data
// cache.
void TF2::dependency_scan ()
{
// Iterate and modify TDB2 in-place. Don't do this at home.
std::vector <Task>::iterator left;
for (left = _tasks.begin ();
left != _tasks.end ();
++left)
{
if (left->has ("depends"))
{
std::vector <std::string> deps;
left->getDependencies (deps);
std::vector <std::string>::iterator d;
for (d = deps.begin (); d != deps.end (); ++d)
{
std::vector <Task>::iterator right;
for (right = _tasks.begin ();
right != _tasks.end ();
++right)
{
if (right->get ("uuid") == *d)
{
Task::status status = right->getStatus ();
if (status != Task::completed &&
status != Task::deleted)
{
left->is_blocked = true;
right->is_blocking = true;
}
break;
}
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
const std::string TF2::dump ()
{
@@ -454,6 +508,10 @@ TDB2::TDB2 ()
{
// Mark the pending file as the only one that has ID numbers.
pending.has_ids ();
// Indicate that dependencies should be automatically scanned on startup,
// setting Task::is_blocked and Task::is_blocking accordingly.
pending.auto_dep_scan ();
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -66,15 +66,20 @@ public:
int id (const std::string&);
void has_ids ();
void auto_dep_scan ();
void clear ();
const std::string dump ();
private:
void dependency_scan ();
public:
bool _read_only;
bool _dirty;
bool _loaded_tasks;
bool _loaded_lines;
bool _has_ids;
bool _auto_dep_scan;
std::vector <Task> _tasks;
std::vector <Task> _added_tasks;
std::vector <Task> _modified_tasks;

View File

@@ -100,6 +100,8 @@ Task::Task ()
: id (0)
, urgency_value (0.0)
, recalc_urgency (true)
, is_blocked (false)
, is_blocking (false)
{
}
@@ -119,6 +121,8 @@ Task& Task::operator= (const Task& other)
id = other.id;
urgency_value = other.urgency_value;
recalc_urgency = other.recalc_urgency;
is_blocked = other.is_blocked;
is_blocking = other.is_blocking;
}
return *this;
@@ -1409,22 +1413,8 @@ float Task::urgency_waiting () const
// A task is blocked only if the task it depends upon is pending/waiting.
float Task::urgency_blocked () const
{
if (has ("depends"))
{
std::vector <std::string> deps;
getDependencies (deps);
std::vector <std::string>::iterator d;
for (d = deps.begin (); d != deps.end (); ++d)
{
Task t;
if (context.tdb2.get (*d, t) &&
(t.getStatus () == Task::pending || t.getStatus () == Task::waiting))
{
return 1.0;
}
}
}
if (is_blocked)
return 1.0;
return 0.0;
}
@@ -1524,7 +1514,7 @@ float Task::urgency_age () const
////////////////////////////////////////////////////////////////////////////////
float Task::urgency_blocking () const
{
if (dependencyIsBlocking (*this))
if (is_blocking)
return 1.0;
return 0.0;

View File

@@ -59,6 +59,9 @@ public:
float urgency_value;
bool recalc_urgency;
bool is_blocked;
bool is_blocking;
// Series of helper functions.
static status textToStatus (const std::string&);
static std::string statusToText (status);

View File

@@ -85,6 +85,7 @@ int CmdShow::execute (std::string& output)
" color.active"
" color.alternate"
" color.blocked"
" color.blocking"
" color.burndown.done"
" color.burndown.pending"
" color.burndown.started"

View File

@@ -39,28 +39,6 @@
extern Context context;
////////////////////////////////////////////////////////////////////////////////
// A task is blocked if it depends on tasks that are pending or waiting.
//
// 1 --> 2(pending) = blocked
// 3 --> 4(completed) = not blocked any more
bool dependencyIsBlocked (const Task& task)
{
std::string depends = task.get ("depends");
if (depends != "")
{
const std::vector <Task>& all = context.tdb2.pending.get_tasks ();
std::vector <Task>::const_iterator it;
for (it = all.begin (); it != all.end (); ++it)
if ((it->getStatus () == Task::pending ||
it->getStatus () == Task::waiting) &&
depends.find (it->get ("uuid")) != std::string::npos)
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void dependencyGetBlocked (const Task& task, std::vector <Task>& blocked)
{
@@ -76,26 +54,6 @@ void dependencyGetBlocked (const Task& task, std::vector <Task>& blocked)
blocked.push_back (*it);
}
////////////////////////////////////////////////////////////////////////////////
// To be a blocking task, there must be at least one other task that depends on
// this task, that is either pending or waiting.
bool dependencyIsBlocking (const Task& task)
{
std::string uuid = task.get ("uuid");
const std::vector <Task>& all = context.tdb2.pending.get_tasks ();
std::vector <Task>::const_iterator it;
for (it = all.begin (); it != all.end (); ++it)
if ((it->getStatus () == Task::pending ||
it->getStatus () == Task::waiting) &&
it->has ("depends") &&
it->get ("depends").find (uuid) != std::string::npos)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
void dependencyGetBlocking (const Task& task, std::vector <Task>& blocking)
{

View File

@@ -59,9 +59,7 @@ std::string colorizeError (const std::string&);
std::string colorizeDebug (const std::string&);
// dependency.cpp
bool dependencyIsBlocked (const Task&);
void dependencyGetBlocked (const Task&, std::vector <Task>&);
bool dependencyIsBlocking (const Task&);
void dependencyGetBlocking (const Task&, std::vector <Task>&);
bool dependencyIsCircular (const Task&);
void dependencyChainOnComplete (Task&);

View File

@@ -505,12 +505,12 @@ bool nag (Task& task)
}
// General form is "if there are no more deserving tasks", suppress the nag.
if (isOverdue ) return false;
if (pri == 'H' && !overdue ) return false;
if (pri == 'M' && !overdue && !high ) return false;
if (pri == 'L' && !overdue && !high && !medium ) return false;
if (pri == ' ' && !overdue && !high && !medium && !low ) return false;
if (dependencyIsBlocking (task) && !dependencyIsBlocked (task)) return false;
if (isOverdue ) return false;
if (pri == 'H' && !overdue ) return false;
if (pri == 'M' && !overdue && !high ) return false;
if (pri == 'L' && !overdue && !high && !medium ) return false;
if (pri == ' ' && !overdue && !high && !medium && !low ) return false;
if (task.is_blocking && !task.is_blocked ) return false;
// All the excuses are made, all that remains is to nag the user.
context.footnote (nagMessage);

View File

@@ -27,7 +27,6 @@
#define L10N // Localization complete.
#include <iostream>
#include <stdlib.h>
#include <Context.h>
#include <Date.h>
@@ -84,85 +83,80 @@ void initializeColorRules ()
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeBlocked (Task& task, const std::string& rule, Color& c)
static void colorizeBlocked (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (dependencyIsBlocked (task))
c.blend (gsColor[rule]);
if (task.is_blocked)
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeTagged (Task& task, const std::string& rule, Color& c)
static void colorizeBlocking (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.getTagCount ())
c.blend (gsColor[rule]);
if (task.is_blocking)
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizePriorityL (Task& task, const std::string& rule, Color& c)
static void colorizeTagged (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.get ("priority") == "L")
c.blend (gsColor[rule]);
if (task.getTagCount ())
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizePriorityM (Task& task, const std::string& rule, Color& c)
static void colorizePriorityL (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.get ("priority") == "M")
c.blend (gsColor[rule]);
if (task.get ("priority") == "L")
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizePriorityH (Task& task, const std::string& rule, Color& c)
static void colorizePriorityM (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.get ("priority") == "H")
c.blend (gsColor[rule]);
if (task.get ("priority") == "M")
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizePriorityNone (Task& task, const std::string& rule, Color& c)
static void colorizePriorityH (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.get ("priority") == "")
c.blend (gsColor[rule]);
if (task.get ("priority") == "H")
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeActive (Task& task, const std::string& rule, Color& c)
static void colorizePriorityNone (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.has ("start") &&
!task.has ("end"))
c.blend (gsColor[rule]);
if (task.get ("priority") == "")
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeScheduled (Task& task, const std::string& rule, Color& c)
static void colorizeActive (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.has ("scheduled") &&
Date (task.get_date ("scheduled")) <= now)
c.blend (gsColor[rule]);
if (task.has ("start") &&
!task.has ("end"))
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeTag (Task& task, const std::string& rule, Color& c)
static void colorizeScheduled (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.hasTag (rule.substr (10)))
c.blend (gsColor[rule]);
if (task.has ("scheduled") &&
Date (task.get_date ("scheduled")) <= now)
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeProject (Task& task, const std::string& rule, Color& c)
static void colorizeTag (Task& task, const std::string& rule, const Color& base, Color& c)
{
if (!gsColor[rule].nontrivial ())
return;
if (task.hasTag (rule.substr (10)))
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeProject (Task& task, const std::string& rule, const Color& base, Color& c)
{
// Observe the case sensitivity setting.
bool sensitive = context.config.getBoolean ("search.case.sensitive");
@@ -172,38 +166,33 @@ static void colorizeProject (Task& task, const std::string& rule, Color& c)
// Match project names leftmost.
if (rule_trunc.length () <= project.length ())
if (compare (rule_trunc, project.substr (0, rule_trunc.length ()), sensitive))
c.blend (gsColor[rule]);
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeProjectNone (Task& task, const std::string& rule, Color& c)
static void colorizeProjectNone (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.get ("project") == "")
c.blend (gsColor[rule]);
if (task.get ("project") == "")
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeTagNone (Task& task, const std::string& rule, Color& c)
static void colorizeTagNone (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.getTagCount () == 0)
c.blend (gsColor[rule]);
if (task.getTagCount () == 0)
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeKeyword (Task& task, const std::string& rule, Color& c)
static void colorizeKeyword (Task& task, const std::string& rule, const Color& base, Color& c)
{
if (!gsColor[rule].nontrivial ())
return;
// Observe the case sensitivity setting.
bool sensitive = context.config.getBoolean ("search.case.sensitive");
// The easiest thing to check is the description, because it is just one
// attribute.
if (find (task.get ("description"), rule.substr (14), sensitive) != std::string::npos)
c.blend (gsColor[rule]);
c.blend (base);
// Failing the description check, look at all annotations, returning on the
// first match.
@@ -215,7 +204,7 @@ static void colorizeKeyword (Task& task, const std::string& rule, Color& c)
if (it->first.substr (0, 11) == "annotation_" &&
find (it->second, rule.substr (14), sensitive) != std::string::npos)
{
c.blend (gsColor[rule]);
c.blend (base);
return;
}
}
@@ -223,11 +212,8 @@ static void colorizeKeyword (Task& task, const std::string& rule, Color& c)
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeDue (Task& task, const std::string& rule, Color& c)
static void colorizeDue (Task& task, const Color& base, Color& c)
{
if (!gsColor[rule].nontrivial ())
return;
Task::status status = task.getStatus ();
if (task.has ("due") &&
@@ -235,16 +221,13 @@ static void colorizeDue (Task& task, const std::string& rule, Color& c)
status != Task::deleted)
{
if (getDueState (task.get ("due")) == 1)
c.blend (gsColor[rule]);
c.blend (base);
}
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeDueToday (Task& task, const std::string& rule, Color& c)
static void colorizeDueToday (Task& task, const Color& base, Color& c)
{
if (!gsColor[rule].nontrivial ())
return;
Task::status status = task.getStatus ();
if (task.has ("due") &&
@@ -252,16 +235,13 @@ static void colorizeDueToday (Task& task, const std::string& rule, Color& c)
status != Task::deleted)
{
if (getDueState (task.get ("due")) == 2)
c.blend (gsColor[rule]);
c.blend (base);
}
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeOverdue (Task& task, const std::string& rule, Color& c)
static void colorizeOverdue (Task& task, const Color& base, Color& c)
{
if (!gsColor[rule].nontrivial ())
return;
Task::status status = task.getStatus ();
if (task.has ("due") &&
@@ -269,32 +249,29 @@ static void colorizeOverdue (Task& task, const std::string& rule, Color& c)
status != Task::deleted)
{
if (getDueState (task.get ("due")) == 3)
c.blend (gsColor[rule]);
c.blend (base);
}
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeRecurring (Task& task, const std::string& rule, Color& c)
static void colorizeRecurring (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.has ("recur"))
c.blend (gsColor[rule]);
if (task.has ("recur"))
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeCompleted (Task& task, const std::string& rule, Color& c)
static void colorizeCompleted (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.getStatus () == Task::completed)
c.blend (gsColor[rule]);
if (task.getStatus () == Task::completed)
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
static void colorizeDeleted (Task& task, const std::string& rule, Color& c)
static void colorizeDeleted (Task& task, const Color& base, Color& c)
{
if (gsColor[rule].nontrivial ())
if (task.getStatus () == Task::completed)
c.blend (gsColor[rule]);
if (task.getStatus () == Task::completed)
c.blend (base);
}
////////////////////////////////////////////////////////////////////////////////
@@ -314,27 +291,32 @@ void autoColorize (Task& task, Color& c)
std::vector <std::string>::reverse_iterator r;
for (r = gsPrecedence.rbegin (); r != gsPrecedence.rend (); ++r)
{
if (*r == "color.blocked") colorizeBlocked (task, *r, c);
else if (*r == "color.tagged") colorizeTagged (task, *r, c);
else if (*r == "color.pri.L") colorizePriorityL (task, *r, c);
else if (*r == "color.pri.M") colorizePriorityM (task, *r, c);
else if (*r == "color.pri.H") colorizePriorityH (task, *r, c);
else if (*r == "color.pri.none") colorizePriorityNone (task, *r, c);
else if (*r == "color.active") colorizeActive (task, *r, c);
else if (*r == "color.scheduled") colorizeScheduled (task, *r, c);
else if (*r == "color.project.none") colorizeProjectNone (task, *r, c);
else if (*r == "color.tag.none") colorizeTagNone (task, *r, c);
else if (*r == "color.due") colorizeDue (task, *r, c);
else if (*r == "color.due.today") colorizeDueToday (task, *r, c);
else if (*r == "color.overdue") colorizeOverdue (task, *r, c);
else if (*r == "color.recurring") colorizeRecurring (task, *r, c);
else if (*r == "color.completed") colorizeCompleted (task, *r, c);
else if (*r == "color.deleted") colorizeDeleted (task, *r, c);
Color base = gsColor[*r];
if (base.nontrivial ())
{
if (*r == "color.blocked") colorizeBlocked (task, base, c);
else if (*r == "color.blocking") colorizeBlocking (task, base, c);
else if (*r == "color.tagged") colorizeTagged (task, base, c);
else if (*r == "color.pri.L") colorizePriorityL (task, base, c);
else if (*r == "color.pri.M") colorizePriorityM (task, base, c);
else if (*r == "color.pri.H") colorizePriorityH (task, base, c);
else if (*r == "color.pri.none") colorizePriorityNone (task, base, c);
else if (*r == "color.active") colorizeActive (task, base, c);
else if (*r == "color.scheduled") colorizeScheduled (task, base, c);
else if (*r == "color.project.none") colorizeProjectNone (task, base, c);
else if (*r == "color.tag.none") colorizeTagNone (task, base, c);
else if (*r == "color.due") colorizeDue (task, base, c);
else if (*r == "color.due.today") colorizeDueToday (task, base, c);
else if (*r == "color.overdue") colorizeOverdue (task, base, c);
else if (*r == "color.recurring") colorizeRecurring (task, base, c);
else if (*r == "color.completed") colorizeCompleted (task, base, c);
else if (*r == "color.deleted") colorizeDeleted (task, base, c);
// Wildcards
else if (r->substr (0, 10) == "color.tag.") colorizeTag (task, *r, c);
else if (r->substr (0, 14) == "color.project.") colorizeProject (task, *r, c);
else if (r->substr (0, 14) == "color.keyword.") colorizeKeyword (task, *r, c);
// Wildcards
else if (r->substr (0, 10) == "color.tag.") colorizeTag (task, *r, base, c);
else if (r->substr (0, 14) == "color.project.") colorizeProject (task, *r, base, c);
else if (r->substr (0, 14) == "color.keyword.") colorizeKeyword (task, *r, base, c);
}
}
}