Bug Fix - custom report filters

- Added support for using parent in a filter.
- Fixed bug that ignored custom report filters.
- Made Context::parse and Context::autoFilter reentrant.
This commit is contained in:
Paul Beckingham 2009-06-18 01:41:15 -04:00
parent 4d43b77441
commit b932d9b9b7
4 changed files with 67 additions and 28 deletions

View file

@ -42,6 +42,8 @@ static const char* internalNames[] =
"entry", "entry",
"start", "start",
"end", "end",
"parent",
"uuid",
"mask", "mask",
"imask", "imask",
"limit", "limit",
@ -378,8 +380,16 @@ bool Att::validNameValue (
"\" is not a valid status. Use 'pending', 'completed', 'deleted' or 'recurring'."; "\" is not a valid status. Use 'pending', 'completed', 'deleted' or 'recurring'.";
} }
else if (name == "parent")
{
}
else if (name == "uuid")
{
}
else else
throw std::string ("'") + name + "' is an unrecognized attribute."; throw std::string ("'") + name + "' is not a recognized attribute.";
return true; return true;
} }

View file

@ -339,6 +339,18 @@ void Context::loadCorrectConfigFile ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Context::parse () void Context::parse ()
{
parse (args, cmd, task, sequence, subst, filter);
}
////////////////////////////////////////////////////////////////////////////////
void Context::parse (
std::vector <std::string>& parseArgs,
Cmd& parseCmd,
Task& parseTask,
Sequence& parseSequence,
Subst& parseSubst,
Filter& parseFilter)
{ {
Att attribute; Att attribute;
tagAdditions.clear (); tagAdditions.clear ();
@ -348,7 +360,7 @@ void Context::parse ()
bool foundSequence = false; bool foundSequence = false;
bool foundSomethingAfterSequence = false; bool foundSomethingAfterSequence = false;
foreach (arg, args) foreach (arg, parseArgs)
{ {
if (!terminated) if (!terminated)
{ {
@ -361,12 +373,12 @@ void Context::parse ()
// Sequence // Sequence
// Note: "add" doesn't require an ID // Note: "add" doesn't require an ID
else if (cmd.command != "add" && else if (parseCmd.command != "add" &&
! foundSomethingAfterSequence && ! foundSomethingAfterSequence &&
sequence.valid (*arg)) parseSequence.valid (*arg))
{ {
header ("parse sequence '" + *arg + "'"); header ("parse sequence '" + *arg + "'");
sequence.parse (*arg); parseSequence.parse (*arg);
foundSequence = true; foundSequence = true;
} }
@ -379,8 +391,12 @@ void Context::parse ()
if (foundSequence) if (foundSequence)
foundSomethingAfterSequence = true; foundSomethingAfterSequence = true;
if (arg->find (',') != std::string::npos)
throw stringtable.get (TAGS_NO_COMMA,
"Tags are not permitted to contain commas.");
tagAdditions.push_back (arg->substr (1, std::string::npos)); tagAdditions.push_back (arg->substr (1, std::string::npos));
task.addTag (arg->substr (1, std::string::npos)); parseTask.addTag (arg->substr (1, std::string::npos));
} }
// Tags to remove begin with '-'. // Tags to remove begin with '-'.
@ -417,25 +433,25 @@ void Context::parse ()
attribute.mod (mod); attribute.mod (mod);
attribute.value (value); attribute.value (value);
task[attribute.name ()] = attribute; parseTask[attribute.name ()] = attribute;
} }
// Substitution of description and/or annotation text. // Substitution of description and/or annotation text.
else if (subst.valid (*arg)) else if (parseSubst.valid (*arg))
{ {
if (foundSequence) if (foundSequence)
foundSomethingAfterSequence = true; foundSomethingAfterSequence = true;
header ("parse subst '" + *arg + "'"); header ("parse subst '" + *arg + "'");
subst.parse (*arg); parseSubst.parse (*arg);
} }
// It might be a command if one has not already been found. // It might be a command if one has not already been found.
else if (cmd.command == "" && else if (parseCmd.command == "" &&
cmd.valid (*arg)) parseCmd.valid (*arg))
{ {
header ("parse cmd '" + *arg + "'"); header ("parse cmd '" + *arg + "'");
cmd.parse (*arg); parseCmd.parse (*arg);
if (foundSequence) if (foundSequence)
foundSomethingAfterSequence = true; foundSomethingAfterSequence = true;
@ -469,26 +485,26 @@ void Context::parse ()
if (descCandidate != "" && noVerticalSpace (descCandidate)) if (descCandidate != "" && noVerticalSpace (descCandidate))
{ {
header ("parse description '" + descCandidate + "'"); header ("parse description '" + descCandidate + "'");
task.set ("description", descCandidate); parseTask.set ("description", descCandidate);
} }
// TODO task.validate () ? // TODO task.validate () ?
// Read-only command (reports, status, info ...) use filters. Write commands // Read-only command (reports, status, info ...) use filters. Write commands
// (add, done ...) do not. // (add, done ...) do not.
if (cmd.isReadOnlyCommand ()) if (parseCmd.isReadOnlyCommand ())
autoFilter (); autoFilter (parseTask, parseFilter);
// If no command was specified, and there were no command line arguments // If no command was specified, and there were no command line arguments
// then invoke the default command. // then invoke the default command.
if (cmd.command == "" && args.size () == 0) if (parseCmd.command == "" && parseArgs.size () == 0)
{ {
std::string defaultCommand = config.get ("default.command"); std::string defaultCommand = config.get ("default.command");
if (defaultCommand != "") if (defaultCommand != "")
{ {
// Stuff the command line. // Stuff the command line.
args.clear (); parseArgs.clear ();
split (args, defaultCommand, ' '); split (parseArgs, defaultCommand, ' ');
header ("[task " + defaultCommand + "]"); header ("[task " + defaultCommand + "]");
// Reinitialize the context and recurse. // Reinitialize the context and recurse.
@ -500,9 +516,9 @@ void Context::parse ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Add all the attributes in the task to the filter. All except uuid. // Add all the attributes in the task to the filter. All except uuid.
void Context::autoFilter () void Context::autoFilter (Task& t, Filter& f)
{ {
foreach (att, task) foreach (att, t)
{ {
// Words are found in the description using the .has modifier. // Words are found in the description using the .has modifier.
if (att->first == "description") if (att->first == "description")
@ -511,7 +527,7 @@ void Context::autoFilter ()
split (words, att->second.value (), ' '); split (words, att->second.value (), ' ');
foreach (word, words) foreach (word, words)
{ {
filter.push_back (Att ("description", "has", *word)); f.push_back (Att ("description", "has", *word));
header ("auto filter: " + att->first + ".has:" + *word); header ("auto filter: " + att->first + ".has:" + *word);
} }
} }
@ -519,7 +535,7 @@ void Context::autoFilter ()
// Projects are matched left-most. // Projects are matched left-most.
else if (att->first == "project") else if (att->first == "project")
{ {
filter.push_back (Att ("project", "startswith", att->second.value ())); f.push_back (Att ("project", "startswith", att->second.value ()));
header ("auto filter: " + att->first + ".startswith:" + att->second.value ()); header ("auto filter: " + att->first + ".startswith:" + att->second.value ());
} }
@ -529,7 +545,7 @@ void Context::autoFilter ()
else if (att->first != "uuid" && else if (att->first != "uuid" &&
att->first != "tags") att->first != "tags")
{ {
filter.push_back (att->second); f.push_back (att->second);
header ("auto filter: " + att->first + ":" + att->second.value ()); header ("auto filter: " + att->first + ":" + att->second.value ());
} }
} }
@ -537,14 +553,14 @@ void Context::autoFilter ()
// Include tagAdditions. // Include tagAdditions.
foreach (tag, tagAdditions) foreach (tag, tagAdditions)
{ {
filter.push_back (Att ("tags", "has", *tag)); f.push_back (Att ("tags", "has", *tag));
header ("auto filter: +" + *tag); header ("auto filter: +" + *tag);
} }
// Include tagRemovals. // Include tagRemovals.
foreach (tag, tagRemovals) foreach (tag, tagRemovals)
{ {
filter.push_back (Att ("tags", "hasnt", *tag)); f.push_back (Att ("tags", "hasnt", *tag));
header ("auto filter: -" + *tag); header ("auto filter: -" + *tag);
} }
} }

View file

@ -58,10 +58,11 @@ public:
void clearMessages (); void clearMessages ();
void parse (); void parse ();
void parse (std::vector <std::string>&, Cmd&, Task&, Sequence&, Subst&, Filter&);
private: private:
void loadCorrectConfigFile (); void loadCorrectConfigFile ();
void autoFilter (); void autoFilter (Task&, Filter&);
public: public:
Config config; Config config;

View file

@ -72,7 +72,7 @@ std::string handleCustomReport (const std::string& report)
for (unsigned int i = 0; i < columns.size (); ++i) for (unsigned int i = 0; i < columns.size (); ++i)
columnLabels[columns[i]] = labels[i]; columnLabels[columns[i]] = labels[i];
std::string sortList = context.config.get ("report." + report + ".sort"); std::string sortList = context.config.get ("report." + report + ".sort");
std::vector <std::string> sortOrder; std::vector <std::string> sortOrder;
split (sortOrder, sortList, ','); split (sortOrder, sortList, ',');
validSortColumns (columns, sortOrder); validSortColumns (columns, sortOrder);
@ -80,11 +80,23 @@ std::string handleCustomReport (const std::string& report)
std::string filterList = context.config.get ("report." + report + ".filter"); std::string filterList = context.config.get ("report." + report + ".filter");
std::vector <std::string> filterArgs; std::vector <std::string> filterArgs;
split (filterArgs, filterList, ' '); split (filterArgs, filterList, ' ');
{
Cmd cmd (report);
Task task;
Sequence sequence;
Subst subst;
Filter filter;
context.parse (filterArgs, cmd, task, sequence, subst, filter);
context.sequence.combine (sequence);
foreach (att, filter)
context.filter.push_back (*att);
}
// Get all the tasks. // Get all the tasks.
std::vector <Task> tasks; std::vector <Task> tasks;
context.tdb.lock (context.config.get ("locking", true)); context.tdb.lock (context.config.get ("locking", true));
// TODO Include filter from custom report.
context.tdb.load (tasks, context.filter); context.tdb.load (tasks, context.filter);
handleRecurrence (tasks); handleRecurrence (tasks);
context.tdb.commit (); context.tdb.commit ();