From 41a76c6798eeee1aa3e8b1640280da95cb38fa90 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 24 Jul 2015 23:46:24 -0400 Subject: [PATCH] Tags: New 'UDA' and 'ORPHAN' virtual tags. --- ChangeLog | 3 ++- NEWS | 1 + doc/man/task.1.in | 44 +++++++++++++++++++++------------------- src/Task.cpp | 25 +++++++++++++++++++++++ src/Task.h | 2 ++ src/commands/CmdInfo.cpp | 3 +++ src/commands/CmdTags.cpp | 44 +++++++++++++++++++++------------------- test/tag.t | 31 +++++++++++++++++++++++++++- 8 files changed, 109 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index 351e2e1c2..90c093c29 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 --------------------------- diff --git a/NEWS b/NEWS index 9ff5d7ad6..4c5428053 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/doc/man/task.1.in b/doc/man/task.1.in index 2965af7e4..ea70fed3b 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -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 diff --git a/src/Task.cpp b/src/Task.cpp index 88b8946f1..77907f0a9 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -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 tags; diff --git a/src/Task.h b/src/Task.h index 139aefd60..4b891aad8 100644 --- a/src/Task.h +++ b/src/Task.h @@ -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; diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index a69fdb627..c87f0bbe6 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -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); diff --git a/src/commands/CmdTags.cpp b/src/commands/CmdTags.cpp index 5f2d368fc..a675fa9b4 100644 --- a/src/commands/CmdTags.cpp +++ b/src/commands/CmdTags.cpp @@ -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) diff --git a/test/tag.t b/test/tag.t index a897d5278..104960798 100755 --- a/test/tag.t +++ b/test/tag.t @@ -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):