Feature - Urgency

- Implemented the urgency algorithm according to rfc31.
- Added a new '_urgency' command to test the algorithm.
This commit is contained in:
Paul Beckingham 2010-08-06 19:04:01 -04:00
parent 3ac627978c
commit d8544181ce
8 changed files with 237 additions and 8 deletions

View file

@ -135,6 +135,7 @@ void Cmd::load ()
commands.push_back ("_config"); commands.push_back ("_config");
commands.push_back ("_version"); commands.push_back ("_version");
commands.push_back ("_merge"); commands.push_back ("_merge");
commands.push_back ("_urgency");
commands.push_back ("export.csv"); commands.push_back ("export.csv");
commands.push_back ("export.ical"); commands.push_back ("export.ical");
commands.push_back ("export.yaml"); commands.push_back ("export.yaml");
@ -232,6 +233,7 @@ bool Cmd::isReadOnlyCommand ()
command == "_ids" || command == "_ids" ||
command == "_config" || command == "_config" ||
command == "_version" || command == "_version" ||
command == "_urgency" ||
command == "export.csv" || command == "export.csv" ||
command == "export.ical" || command == "export.ical" ||
command == "export.yaml" || command == "export.yaml" ||

View file

@ -101,6 +101,21 @@ std::string Config::defaults =
"journal.time.start.annotation=Started task # Annotation description for the start journal entry\n" "journal.time.start.annotation=Started task # Annotation description for the start journal entry\n"
"journal.time.stop.annotation=Stopped task # Annotation description for the stop journal entry\n" "journal.time.stop.annotation=Stopped task # Annotation description for the stop journal entry\n"
"\n" "\n"
"# Urgency Coefficients\n"
"urgency.next.coefficient=10.0 # Urgency coefficients for 'next' special tag\n"
"urgency.blocking.coefficient=9.0 # Urgency coefficients for blocking tasks\n"
"urgency.blocked.coefficient=8.0 # Urgency coefficients for blocked tasks\n"
"urgency.due.coefficient=7.0 # Urgency coefficients for due dates\n"
"urgency.priority.coefficient=6.0 # Urgency coefficients for priorities\n"
"urgency.waiting.coefficient=5.0 # Urgency coefficients for waiting status\n"
"urgency.active.coefficient=4.0 # Urgency coefficients for active tasks\n"
"urgency.project.coefficient=3.0 # Urgency coefficients for projects\n"
"urgency.tags.coefficient=2.0 # Urgency coefficients for tags\n"
"urgency.annotations.coefficient=1.0 # Urgency coefficients for annotations\n"
"\n"
"#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n"
"#urgency.user.tag.foo.coefficient=5.0 # Urgency coefficients for 'foo' tag\n"
"\n"
"# Color controls.\n" "# Color controls.\n"
"color=on # Enable color\n" "color=on # Enable color\n"
#ifdef LINUX #ifdef LINUX

View file

@ -249,6 +249,7 @@ int Context::dispatch (std::string &out)
else if (cmd.command == "_ids") { rc = handleCompletionIDs (out); } else if (cmd.command == "_ids") { rc = handleCompletionIDs (out); }
else if (cmd.command == "_config") { rc = handleCompletionConfig (out); } else if (cmd.command == "_config") { rc = handleCompletionConfig (out); }
else if (cmd.command == "_version") { rc = handleCompletionVersion (out); } else if (cmd.command == "_version") { rc = handleCompletionVersion (out); }
else if (cmd.command == "_urgency") { rc = handleUrgency (out); }
else if (cmd.command == "" && else if (cmd.command == "" &&
sequence.size ()) { rc = handleModify (out); } sequence.size ()) { rc = handleModify (out); }

View file

@ -34,6 +34,7 @@
#include "Task.h" #include "Task.h"
#include "text.h" #include "text.h"
#include "util.h" #include "util.h"
#include "main.h"
extern Context context; extern Context context;
@ -738,3 +739,171 @@ int Task::determineVersion (const std::string& line)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Urgency is defined as a polynomial, the value of which is calculated in this
// function, according to:
//
// U = A.t + B.t + C.t ...
// a b c
//
// U = urgency
// A = coefficient for term a
// t sub a = numeric scale from 0 -> 1, with 1 being the highest
// urgency, derived from one task attribute and mapped
// to the numeric scale
//
// See rfc31-urgency.txt for full details.
//
float Task::urgency ()
{
float urgency = 0.0;
// urgency.priority.coefficient
float coefficient = (float) context.config.getReal ("urgency.priority.coefficient");
float term;
std::string value = get ("priority");
if (value == "H") term = 1.0;
else if (value == "M") term = 0.65;
else if (value == "L") term = 0.3;
else term = 0.0;
urgency += term * coefficient;
// urgency.project.coefficient
coefficient = (float) context.config.getReal ("urgency.project.coefficient");
value = get ("project");
if (value != "") term = 1.0;
else term = 0.0;
urgency += term * coefficient;
// urgency.active.coefficient
coefficient = (float) context.config.getReal ("urgency.active.coefficient");
value = get ("start");
if (value != "") term = 1.0;
else term = 0.0;
urgency += term * coefficient;
// urgency.waiting.coefficient
coefficient = (float) context.config.getReal ("urgency.waiting.coefficient");
value = get ("status");
if (value == "pending") term = 1.0;
else if (value == "waiting") term = 0.0;
urgency += term * coefficient;
// urgency.blocked.coefficient
coefficient = (float) context.config.getReal ("urgency.blocked.coefficient");
value = get ("depends");
if (value != "") term = 0.0;
else term = 1.0;
urgency += term * coefficient;
// urgency.annotations.coefficient
coefficient = (float) context.config.getReal ("urgency.annotations.coefficient");
std::vector <Att> annos;
getAnnotations (annos);
if (annos.size () >= 3) term = 1.0;
else if (annos.size () == 2) term = 0.9;
else if (annos.size () == 1) term = 0.8;
else term = 0.0;
urgency += term * coefficient;
// urgency.tags.coefficient
coefficient = (float) context.config.getReal ("urgency.tags.coefficient");
int count = getTagCount ();
if (count >= 3) term = 1.0;
else if (count == 2) term = 0.9;
else if (count == 1) term = 0.8;
else term = 0.0;
urgency += term * coefficient;
// urgency.next.coefficient
coefficient = (float) context.config.getReal ("urgency.next.coefficient");
if (hasTag ("next")) term = 1.0;
else term = 0.0;
urgency += term * coefficient;
// urgency.due.coefficient
// days overdue, capped at 7 -> 0.8 - 1.0
// due today -> 0.7
// days until due, capped at 14 -> 0.4 - 0.6
// has due date -> 0.3
// no due date -> 0.0
coefficient = (float) context.config.getReal ("urgency.due.coefficient");
if (has ("due"))
{
Date now;
Date due (get ("due"));
int days_overdue = (now - due) / 86400;
if (days_overdue > 7) term = 1.0;
else if (days_overdue > 0) term = (0.2 * days_overdue / 7.0) + 0.8;
else if (due.sameDay (now)) term = 0.7;
else if (days_overdue < -14) term = 0.4;
else if (days_overdue < 0) term = (0.2 * days_overdue / 14.0) + 0.6;
else term = 0.3;
}
else
term = 0.0;
urgency += term * coefficient;
// Tag- and project-specific coefficients.
std::vector <std::string> all;
context.config.all (all);
foreach (var, all)
{
if (var->substr (0, 13) == "urgency.user.")
{
// urgency.user.project.<project>.coefficient
std::string::size_type end = std::string::npos;
if (var->substr (13, 8) == "project." &&
(end = var->find (".coefficient")) != std::string::npos)
{
std::string project = var->substr (21, end - 21);
coefficient = (float) context.config.getReal (*var);
if (get ("project").find (project) == 0)
urgency += coefficient;
}
// urgency.user.tag.<tag>.coefficient
if (var->substr (13, 4) == "tag." &&
(end = var->find (".coefficient")) != std::string::npos)
{
std::string tag = var->substr (17, end - 17);
coefficient = (float) context.config.getReal (*var);
if (hasTag (tag))
urgency += coefficient;
}
}
}
// urgency.blocking.coefficient
coefficient = (float) context.config.getReal ("urgency.blocking.coefficient");
if (dependencyIsBlocking (*this)) term = 1.0;
else term = 0.0;
urgency += term * coefficient;
// Return the sum of all terms.
return urgency;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -81,6 +81,8 @@ public:
void validate () const; void validate () const;
float urgency ();
private: private:
int determineVersion (const std::string&); int determineVersion (const std::string&);
void legacyParse (const std::string&); void legacyParse (const std::string&);

View file

@ -515,6 +515,36 @@ int handleCompletionVersion (std::string &outs)
return 0; return 0;
} }
////////////////////////////////////////////////////////////////////////////////
// Temporary command to display urgency for a task.
int handleUrgency (std::string &outs)
{
// Get all the tasks.
std::vector <Task> tasks;
context.tdb.lock (context.config.getBoolean ("locking"));
handleRecurrence ();
context.tdb.loadPending (tasks, context.filter);
context.tdb.commit ();
context.tdb.unlock ();
// Filter sequence.
context.filter.applySequence (tasks, context.sequence);
// Find the task(s).
std::stringstream out;
foreach (task, tasks)
{
out << "task "
<< task->id
<< " urgency "
<< task->urgency ()
<< "\n";
}
outs = out.str ();
return 0;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int handleCompletionIDs (std::string &outs) int handleCompletionIDs (std::string &outs)
{ {
@ -715,7 +745,12 @@ int handleShow (std::string &outs)
"import.synonym.status import.synonym.tags import.synonym.entry " "import.synonym.status import.synonym.tags import.synonym.entry "
"import.synonym.start import.synonym.due import.synonym.recur " "import.synonym.start import.synonym.due import.synonym.recur "
"import.synonym.end import.synonym.project import.synonym.priority " "import.synonym.end import.synonym.project import.synonym.priority "
"import.synonym.fg import.synonym.bg import.synonym.description "; "import.synonym.fg import.synonym.bg import.synonym.description "
"urgency.next.coefficient urgency.blocking.coefficient urgency.blocked.coefficient "
"urgency.due.coefficient urgency.priority.coefficient urgency.waiting.coefficient "
"urgency.active.coefficient urgency.project.coefficient urgency.tags.coefficient "
"urgency.annotations.coefficient ";
// This configuration variable is supported, but not documented. It exists // This configuration variable is supported, but not documented. It exists
// so that unit tests can force color to be on even when the output from task // so that unit tests can force color to be on even when the output from task
@ -732,13 +767,15 @@ int handleShow (std::string &outs)
{ {
// These are special configuration variables, because their name is // These are special configuration variables, because their name is
// dynamic. // dynamic.
if (i->substr (0, 14) != "color.keyword." && if (i->substr (0, 14) != "color.keyword." &&
i->substr (0, 14) != "color.project." && i->substr (0, 14) != "color.project." &&
i->substr (0, 10) != "color.tag." && i->substr (0, 10) != "color.tag." &&
i->substr (0, 8) != "holiday." && i->substr (0, 8) != "holiday." &&
i->substr (0, 7) != "report." && i->substr (0, 7) != "report." &&
i->substr (0, 6) != "alias." && i->substr (0, 6) != "alias." &&
i->substr (0, 5) != "hook.") i->substr (0, 5) != "hook." &&
i->substr (0, 21) != "urgency.user.project." &&
i->substr (0, 17) != "urgency.user.tag.")
{ {
unrecognized.push_back (*i); unrecognized.push_back (*i);
} }

View file

@ -69,6 +69,7 @@ int handleCompletionCommands (std::string &);
int handleCompletionIDs (std::string &); int handleCompletionIDs (std::string &);
int handleCompletionConfig (std::string &); int handleCompletionConfig (std::string &);
int handleCompletionVersion (std::string &); int handleCompletionVersion (std::string &);
int handleUrgency (std::string &);
int handleVersion (std::string &); int handleVersion (std::string &);
int handleConfig (std::string &); int handleConfig (std::string &);
int handleShow (std::string &); int handleShow (std::string &);

View file

@ -153,6 +153,8 @@ TODO Task::removeDependency
TODO Task::getDependencies TODO Task::getDependencies
TODO Task::getDependencies TODO Task::getDependencies
TODO Task::urgency
*/ */
// Task::operator== // Task::operator==