Tags: New 'UDA' and 'ORPHAN' virtual tags.

This commit is contained in:
Paul Beckingham 2015-07-24 23:46:24 -04:00
parent 297b0c4b04
commit 41a76c6798
8 changed files with 109 additions and 44 deletions

View file

@ -100,8 +100,9 @@
- "import" can now import JSON arrays, the new default "export" output.
- The '_tags' helper command now includes virtual tags (thanks to Daniel
Shahaf).
-When multiple tasks are 'edit'ed, a failure causes the editing to stop (thanks
- When multiple tasks are 'edit'ed, a failure causes the editing to stop (thanks
to Daniel Shahaf).
- New 'UDA' and 'ORPHAN' virtual tags.
------ current release ---------------------------

1
NEWS
View file

@ -3,6 +3,7 @@ New Features in Taskwarrior 2.4.5
- The active context, if one is set, is now identified in "task context list"
- It is an error to attempt and add or remove of a virtual tag.
- New 'UDA' and 'ORPHAN' virtual tags.
New commands in Taskwarrior 2.4.5

View file

@ -652,31 +652,33 @@ There are also virtual tags, which represent task metadata in tag form. These
tags do not exist, but can be used to filter tasks. The supported virtual tags
are:
BLOCKED Matches if the task is blocked
UNBLOCKED Matches if the task is not blocked
BLOCKING Matches if the task is blocking
YESTERDAY Matches if the task was due sometime yesterday
DUE Matches if the task is due
DUETODAY Matches if the task is due today
TODAY Matches if the task is due today
TOMORROW Matches if the task is due sometime tomorrow
WEEK Matches if the task is due this week
MONTH Matches if the task is due this month
YEAR Matches if the task is due this year
OVERDUE Matches if the task is overdue
ACTIVE Matches if the task is started
SCHEDULED Matches if the task is scheduled
READY Matches if the task is actionable
PARENT Matches if the task is a parent
CHILD Matches if the task has a parent
UNTIL Matches if the task expires
WAITING Matches if the task is waiting
ANNOTATED Matches if the task has annotations
TAGGED Matches if the task has tags
PENDING Matches if the task has pending status
BLOCKED Matches if the task is blocked
BLOCKING Matches if the task is blocking
CHILD Matches if the task has a parent
COMPLETED Matches if the task has completed status
DELETED Matches if the task has deleted status
.\" If you update the above list, update src/commands/CmdTags.cpp as well.
DUE Matches if the task is due
DUETODAY Matches if the task is due today
MONTH Matches if the task is due this month
ORPHAN Matches if the task has any orphaned UDA values
OVERDUE Matches if the task is overdue
PARENT Matches if the task is a parent
PENDING Matches if the task has pending status
READY Matches if the task is actionable
SCHEDULED Matches if the task is scheduled
TAGGED Matches if the task has tags
TODAY Matches if the task is due today
TOMORROW Matches if the task is due sometime tomorrow
UDA Matches if the task has any UDA values
UNBLOCKED Matches if the task is not blocked
UNTIL Matches if the task expires
WAITING Matches if the task is waiting
WEEK Matches if the task is due this week
YEAR Matches if the task is due this year
YESTERDAY Matches if the task was due sometime yesterday
.\" If you update the above list, update src/commands/CmdInfo.cpp and src/commands/CmdTags.cpp as well.
You can use +BLOCKED to filter blocked tasks, or -BLOCKED for unblocked tasks.
Similarly, -BLOCKED is equivalent to +UNBLOCKED. It is an error to attempt to

View file

@ -495,6 +495,29 @@ bool Task::is_dueyear () const
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Task::is_udaPresent () const
{
for (auto& col : context.columns)
if (col.first.substr (0, 11) != "annotation_")
if (col.second->is_uda () &&
has (col.first))
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Task::is_orphanPresent () const
{
for (auto& att : *this)
if (att.first.substr (0, 11) != "annotation_")
if (context.columns.find (att.first) == context.columns.end ())
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Task::is_overdue () const
{
@ -1120,6 +1143,8 @@ bool Task::hasTag (const std::string& tag) const
if (tag == "PENDING") return get ("status") == "pending";
if (tag == "COMPLETED") return get ("status") == "completed";
if (tag == "DELETED") return get ("status") == "deleted";
if (tag == "UDA") return is_udaPresent();
if (tag == "ORPHAN") return is_orphanPresent();
// Concrete tags.
std::vector <std::string> tags;

View file

@ -111,6 +111,8 @@ public:
bool is_duemonth () const;
bool is_dueyear () const;
bool is_overdue () const;
bool is_udaPresent () const;
bool is_orphanPresent () const;
#endif
status getStatus () const;

View file

@ -316,6 +316,7 @@ int CmdInfo::execute (std::string& output)
if (task.hasTag ("DUE")) virtualTags += "DUE ";
if (task.hasTag ("DUETODAY")) virtualTags += "DUETODAY ";
if (task.hasTag ("MONTH")) virtualTags += "MONTH ";
if (task.hasTag ("ORPHAN")) virtualTags += "ORPHAN ";
if (task.hasTag ("OVERDUE")) virtualTags += "OVERDUE ";
if (task.hasTag ("PARENT")) virtualTags += "PARENT ";
if (task.hasTag ("PENDING")) virtualTags += "PENDING ";
@ -324,12 +325,14 @@ int CmdInfo::execute (std::string& output)
if (task.hasTag ("TAGGED")) virtualTags += "TAGGED ";
if (task.hasTag ("TODAY")) virtualTags += "TODAY ";
if (task.hasTag ("TOMORROW")) virtualTags += "TOMORROW ";
if (task.hasTag ("UDA")) virtualTags += "UDA ";
if (task.hasTag ("UNBLOCKED")) virtualTags += "UNBLOCKED ";
if (task.hasTag ("UNTIL")) virtualTags += "UNTIL ";
if (task.hasTag ("WAITING")) virtualTags += "WAITING ";
if (task.hasTag ("WEEK")) virtualTags += "WEEK ";
if (task.hasTag ("YEAR")) virtualTags += "YEAR ";
if (task.hasTag ("YESTERDAY")) virtualTags += "YESTERDAY ";
// If you update the above list, update src/commands/CmdInfo.cpp and src/commands/CmdTags.cpp as well.
row = view.addRow ();
view.set (row, 0, STRING_CMD_INFO_VIRTUAL_TAGS);

View file

@ -177,31 +177,33 @@ int CmdCompletionTags::execute (std::string& output)
unique["nonag"] = 0;
unique["nocal"] = 0;
unique["next"] = 0;
unique["BLOCKED"] = 0;
unique["UNBLOCKED"] = 0;
unique["BLOCKING"] = 0;
unique["YESTERDAY"] = 0;
unique["DUE"] = 0;
unique["DUETODAY"] = 0;
unique["TODAY"] = 0;
unique["TOMORROW"] = 0;
unique["WEEK"] = 0;
unique["MONTH"] = 0;
unique["YEAR"] = 0;
unique["OVERDUE"] = 0;
unique["ACTIVE"] = 0;
unique["SCHEDULED"] = 0;
unique["READY"] = 0;
unique["PARENT"] = 0;
unique["CHILD"] = 0;
unique["UNTIL"] = 0;
unique["WAITING"] = 0;
unique["ANNOTATED"] = 0;
unique["TAGGED"] = 0;
unique["PENDING"] = 0;
unique["BLOCKED"] = 0;
unique["BLOCKING"] = 0;
unique["CHILD"] = 0;
unique["COMPLETED"] = 0;
unique["DELETED"] = 0;
// If you update this list, update doc/man/task.1.in as well.
unique["DUE"] = 0;
unique["DUETODAY"] = 0;
unique["MONTH"] = 0;
unique["ORPHAN"] = 0;
unique["OVERDUE"] = 0;
unique["PARENT"] = 0;
unique["PENDING"] = 0;
unique["READY"] = 0;
unique["SCHEDULED"] = 0;
unique["TAGGED"] = 0;
unique["TODAY"] = 0;
unique["TOMORROW"] = 0;
unique["UDA"] = 0;
unique["UNBLOCKED"] = 0;
unique["UNTIL"] = 0;
unique["WAITING"] = 0;
unique["WEEK"] = 0;
unique["YEAR"] = 0;
unique["YESTERDAY"] = 0;
// If you update the above list, update src/commands/CmdInfo.cpp and src/commands/CmdTags.cpp as well.
std::stringstream out;
for (auto& it : unique)

View file

@ -354,7 +354,6 @@ class TestVirtualTags(TestCase):
self.assertIn("due_eom", out)
self.assertIn("due_eow", out)
def test_virtual_tags_helper(self):
"""Verify '_tags' shows appropriate tags"""
code, out, err = self.t("_tags")
@ -366,6 +365,36 @@ class TestVirtualTags(TestCase):
self.assertIn("tag", out)
class TestVirtualTagUDA(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t.config("uda.animal.type", "string")
self.t.config("uda.animal.label", "Animal")
self.t("add one animal:donkey")
self.t("add two")
def test_virtual_tag_UDA(self):
"""Verify 'UDA' appears when expected"""
code, out, err = self.t("+UDA all")
self.assertIn("one", out)
self.assertNotIn("two", out)
class TestVirtualTagORPHAN(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t("add one rc.uda.animal.type:string rc.uda.animal.label:Animal animal:donkey")
self.t("add two")
def test_virtual_tag_ORPHAN(self):
"""Verify 'ORPHAN' appears when expected"""
code, out, err = self.t("+ORPHAN all")
self.assertIn("one", out)
self.assertNotIn("two", out)
class Test285(TestCase):
@classmethod
def setUpClass(cls):