Enhancements - filters

- The project attribute is now automatically filtered with
    project.startswith:<value>
  to provide leftmost matching (ie subprojects).
- Unmodifiable attributes (uuid, start ...) are now prevented from
  being updated if the command is designated as a "write" command.
This commit is contained in:
Paul Beckingham 2009-06-16 10:42:53 -04:00
parent 5691ed0588
commit 97d732e5f7
2 changed files with 42 additions and 30 deletions

View file

@ -44,7 +44,7 @@ static const char* internalNames[] =
"end", "end",
"mask", "mask",
"imask", "imask",
// "limit", "limit",
"status", "status",
"description", "description",
}; };
@ -200,7 +200,6 @@ bool Att::validName (const std::string& name)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// TODO Obsolete
bool Att::validModifiableName (const std::string& name) bool Att::validModifiableName (const std::string& name)
{ {
for (unsigned int i = 0; i < NUM_MODIFIABLE_NAMES; ++i) for (unsigned int i = 0; i < NUM_MODIFIABLE_NAMES; ++i)
@ -294,6 +293,14 @@ bool Att::validNameValue (
mod = matches[0]; mod = matches[0];
} }
// Some attributes are intended to be private, unless the command is read-
// only, in which cased these are perfectly valid elements of a filter.
if (context.cmd.isWriteCommand () &&
!validModifiableName (name))
throw std::string ("\"") +
name +
"\" is not an attribute you may modify directly.";
// Thirdly, make sure the value has the expected form or values. // Thirdly, make sure the value has the expected form or values.
if (name == "project") if (name == "project")
{ {
@ -333,13 +340,8 @@ bool Att::validNameValue (
Text::guessColor (value); Text::guessColor (value);
} }
else if (name == "due") else if (name == "due" ||
{ name == "until")
if (value != "")
Date (value);
}
else if (name == "until")
{ {
if (value != "") if (value != "")
Date (value); Date (value);
@ -351,28 +353,30 @@ bool Att::validNameValue (
Duration (value); Duration (value);
} }
// TODO Not ready for prime time.
else if (name == "limit") else if (name == "limit")
{ {
if (value == "" || !digitsOnly (value)) if (value == "" || !digitsOnly (value))
throw std::string ("The '") + name + "' attribute must be an integer."; throw std::string ("The '") + name + "' attribute must be an integer.";
} }
// Some attributes are intended to be private, unless the command is read- else if (name == "status")
// only, in which cased these are perfectly valid elements of a filter.
else if (name == "entry" ||
name == "start" ||
name == "end" ||
name == "mask" ||
name == "imask" ||
name == "uuid" ||
name == "status" ||
name == "description")
{ {
if (context.cmd.isWriteCommand ()) value = lowerCase (value);
std::vector <std::string> matches;
std::vector <std::string> candidates;
candidates.push_back ("pending");
candidates.push_back ("completed");
candidates.push_back ("deleted");
candidates.push_back ("recurring");
autoComplete (value, candidates, matches);
if (matches.size () == 1)
value = matches[0];
else
throw std::string ("\"") + throw std::string ("\"") +
name + value +
"\" is not an attribute you may modify directly."; "\" is not a valid status. Use 'pending', 'completed', 'deleted' or 'recurring'.";
} }
else else

View file

@ -494,7 +494,7 @@ void Context::constructFilter ()
{ {
foreach (att, task) foreach (att, task)
{ {
// TODO this doesn't work. // Words are found in the description using the .has modifier.
if (att->first == "description") if (att->first == "description")
{ {
std::vector <std::string> words; std::vector <std::string> words;
@ -502,19 +502,27 @@ void Context::constructFilter ()
foreach (word, words) foreach (word, words)
{ {
filter.push_back (Att ("description", "has", *word)); filter.push_back (Att ("description", "has", *word));
std::cout << "Context::constructFilter " << att->first << "=" << *word << std::endl; std::cout << "# auto filter: " << att->first << ".has:" << *word << "" << std::endl;
} }
} }
// Projects are matched left-most.
else if (att->first == "project")
{
filter.push_back (Att ("project", "startswith", att->second.value ()));
std::cout << "# auto filter: " << att->first << ".startswith:" << att->second.value () << "" << std::endl;
}
// TODO Don't create a uuid for every task? // TODO Don't create a uuid for every task?
// Every task has a unique uuid by default, and it shouldn't be included. // Every task has a unique uuid by default, and it shouldn't be included.
// The mechanism for filtering on tags is +/-<tag>, not tags:foo which // The mechanism for filtering on tags is +/-<tag>, not tags:foo which
// means that there can only be one tag, "foo". // means that there can only be one tag, "foo".
else if (att->first != "uuid" && else if (att->first != "uuid" &&
att->first != "tags") att->first != "tags" &&
att->first != "project")
{ {
filter.push_back (att->second); filter.push_back (att->second);
std::cout << "Context::constructFilter " << att->first << "=" << att->second.value () << std::endl; std::cout << "# auto filter: " << att->first << ":" << att->second.value () << "" << std::endl;
} }
} }
@ -524,14 +532,14 @@ void Context::constructFilter ()
foreach (tag, tagAdditions) foreach (tag, tagAdditions)
{ {
filter.push_back (Att ("tags", "has", *tag)); filter.push_back (Att ("tags", "has", *tag));
std::cout << "Context::constructFilter tags=+" << *tag << std::endl; std::cout << "# auto filter: +" << *tag << "" << std::endl;
} }
// TODO Include tagRemovals. // Include tagRemovals.
foreach (tag, tagRemovals) foreach (tag, tagRemovals)
{ {
filter.push_back (Att ("tags", "hasnt", *tag)); filter.push_back (Att ("tags", "hasnt", *tag));
std::cout << "Context::constructFilter tags=-" << *tag << std::endl; std::cout << "# auto filter: -" << *tag << "" << std::endl;
} }
} }