Repository: fniessink/next-action

Issue:
Make --due argument optional to pick next action from tasks with any due date

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4622239..c5192b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [Unreleased]
+
+### Added
+
+- The due date argument to `--due` is now optional. Closes #92.
+
 ## [0.14.1] - 2018-06-01
 
 ### Fixed
diff --git a/README.md b/README.md
index 42a13a2..ad017f2 100644
--- a/README.md
+++ b/README.md
@@ -40,8 +40,8 @@ Don't know what *Todo.txt* is? See <https://github.com/todotxt/todo.txt> for the
 
 ```console
 $ next-action --help
-usage: next-action [-h] [--version] [-c [<config.cfg>]] [-f <todo.txt>] [-n <number> | -a] [-d <due date>]
-[-o] [-p [<priority>]] [-s [<style>]] [<context|project> ...]
+usage: next-action [-h] [--version] [-c [<config.cfg>]] [-f <todo.txt>] [-n <number> | -a] [-d [<due date>] |
+-o] [-p [<priority>]] [-s [<style>]] [<context|project> ...]
 
 Show the next action in your todo.txt. The next action is selected from the tasks in the todo.txt file based
 on task properties such as priority, due date, and creation date. Limit the tasks from which the next action
@@ -60,8 +60,9 @@ optional arguments:
   -n <number>, --number <number>
                         number of next actions to show (default: 1)
   -a, --all             show all next actions
-  -d <due date>, --due <due date>
-                        show only next actions due on or before the date
+  -d [<due date>], --due [<due date>]
+                        show only next actions with a due date; if a date is given, show only next actions
+                        due on or before that date
   -o, --overdue         show only overdue next actions
   -p [<priority>], --priority [<priority>]
                         minimum priority (A-Z) of next actions to show (default: None)
@@ -91,6 +92,8 @@ Completed tasks (~~`x This is a completed task`~~) and tasks with a creation dat
 
 ### Limiting the tasks from which next actions are selected
 
+#### By contexts and/or projects
+
 You can limit the tasks from which *Next-action* picks the next action by passing contexts and/or projects:
 
 ```console
@@ -123,11 +126,20 @@ $ next-action -+PaintHouse @store
 (G) Buy wood for new +DogHouse @store
 ```
 
-To limit the the tasks from which the next action is selected to actions with a due date, specify a due date:
+#### By due date
+
+To limit the the tasks from which the next action is selected to actions with a due date, use the `--due` option:
 
 ```console
-$ next-action @home --due june
-Pay invoice @home due:2018-06-28
+$ next-action @home --due
+(K) Pay July invoice @home due:2018-07-28
+```
+
+Add a due date to select a next action from tasks due on or before that date:
+
+```console
+$ next-action @home --due "june 2018"
+(L) Pay June invoice @home due:2018-06-28
 ```
 
 To make sure you have no overdue actions, or work on overdue actions first, limit the tasks from which the next action is selected to overdue actions:
@@ -137,6 +149,8 @@ $ next-action --overdue
 Buy flowers due:2018-02-14
 ```
 
+#### By priority
+
 To make sure you work on important tasks rather than urgent tasks, you can make sure the tasks from which the next action is selected have at least a minimum priority:
 
 ```console
@@ -271,9 +285,9 @@ To run the unit tests:
 
 ```console
 $ python -m unittest
-.......................................................................................................................................................
+.........................................................................................................................................................
 ----------------------------------------------------------------------
-Ran 151 tests in 0.587s
+Ran 153 tests in 0.548s
 
 OK
 ```
diff --git a/docs/todo.txt b/docs/todo.txt
index 9e3f99c..06842c9 100644
--- a/docs/todo.txt
+++ b/docs/todo.txt
@@ -5,6 +5,7 @@
 Get rid of old +DogHouse @home
 Borrow ladder from the neighbors +PaintHouse @home
 Buy flowers due:2018-02-14
-Pay invoice @home due:2018-06-28
+(L) Pay June invoice @home due:2018-06-28
+(K) Pay July invoice @home due:2018-07-28
 x This is a completed task
 9999-01-01 Start preparing for five-digit years
diff --git a/docs/update_readme.py b/docs/update_readme.py
index 591c40a..6d5d826 100644
--- a/docs/update_readme.py
+++ b/docs/update_readme.py
@@ -1,12 +1,13 @@
 """ Insert the output of console commands into the README.md file. """
 
 import os
+import shlex
 import subprocess  # nosec
 
 
 def do_command(line):
     """ Run the command on the line and return its stdout and stderr. """
-    command = line[2:].split(" ")
+    command = shlex.split(line[2:])
     if command[0] == "next-action" and "--write-config-file" not in command:
         command.extend(["--config", "docs/.next-action.cfg"])
     command_output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
diff --git a/next_action/arguments/parser.py b/next_action/arguments/parser.py
index cd6fec0..953b952 100644
--- a/next_action/arguments/parser.py
+++ b/next_action/arguments/parser.py
@@ -25,7 +25,7 @@ class NextActionArgumentParser(argparse.ArgumentParser):
                         "the tasks from which the next action is selected by specifying contexts the tasks must have "
                         "and/or projects the tasks must belong to.",
             usage=textwrap.fill("next-action [-h] [--version] [-c [<config.cfg>]] [-f <todo.txt>] "
-                                "[-n <number> | -a] [-d <due date>] [-o] [-p [<priority>]] [-s [<style>]] "
+                                "[-n <number> | -a] [-d [<due date>] | -o] [-p [<priority>]] [-s [<style>]] "
                                 "[<context|project> ...]",
                                 width=shutil.get_terminal_size().columns - len("usage: ")))
         self.__default_filenames = ["~/todo.txt"]
@@ -55,8 +55,9 @@ class NextActionArgumentParser(argparse.ArgumentParser):
             "-a", "--all", help="show all next actions", action="store_const", dest="number", const=sys.maxsize)
         date = self.add_mutually_exclusive_group()
         date.add_argument(
-            "-d", "--due", metavar="<due date>", type=date_type,
-            help="show only next actions due on or before the date")
+            "-d", "--due", metavar="<due date>", type=date_type, nargs="?", const=datetime.date.max,
+            help="show only next actions with a due date; if a date is given, show only next actions due on or "
+                 "before that date")
         date.add_argument("-o", "--overdue", help="show only overdue next actions", action="store_true")
         self.add_argument(
             "-p", "--priority", metavar="<priority>", choices=string.ascii_uppercase, nargs="?",


Test patch:
diff --git a/tests/unittests/arguments/test_parser.py b/tests/unittests/arguments/test_parser.py
index aa5dfc4..403c66e 100644
--- a/tests/unittests/arguments/test_parser.py
+++ b/tests/unittests/arguments/test_parser.py
@@ -11,7 +11,7 @@ from next_action.arguments import config, parse_arguments
 
 
 USAGE_MESSAGE = textwrap.fill(
-    "usage: next-action [-h] [--version] [-c [<config.cfg>]] [-f <todo.txt>] [-n <number> | -a] [-d <due date>] [-o] "
+    "usage: next-action [-h] [--version] [-c [<config.cfg>]] [-f <todo.txt>] [-n <number> | -a] [-d [<due date>] | -o] "
     "[-p [<priority>]] [-s [<style>]] [<context|project> ...]", 120) + "\n"
 
 
@@ -221,6 +221,11 @@ class DueDateTest(ParserTestCase):
         """ Test that the default value for due date is None. """
         self.assertEqual(None, parse_arguments()[1].due)
 
+    @patch.object(sys, "argv", ["next-action", "--due"])
+    def test_no_due_date(self):
+        """ Test that the due date is the max date if the user doesn't specify a date. """
+        self.assertEqual(datetime.date.max, parse_arguments()[1].due)
+
     @patch.object(sys, "argv", ["next-action", "--due", "2018-01-01"])
     def test_due_date(self):
         """ Test that the default value for due date is None. """
diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
index 388ff6f..8148d29 100644
--- a/tests/unittests/test_cli.py
+++ b/tests/unittests/test_cli.py
@@ -65,7 +65,7 @@ class CLITest(unittest.TestCase):
         os.environ['COLUMNS'] = "120"  # Fake that the terminal is wide enough.
         self.assertRaises(SystemExit, next_action)
         self.assertEqual(call("""\
-usage: next-action [-h] [--version] [-c [<config.cfg>]] [-f <todo.txt>] [-n <number> | -a] [-d <due date>] [-o] [-p
+usage: next-action [-h] [--version] [-c [<config.cfg>]] [-f <todo.txt>] [-n <number> | -a] [-d [<due date>] | -o] [-p
 [<priority>]] [-s [<style>]] [<context|project> ...]
 
 Show the next action in your todo.txt. The next action is selected from the tasks in the todo.txt file based on task
@@ -85,8 +85,9 @@ optional arguments:
   -n <number>, --number <number>
                         number of next actions to show (default: 1)
   -a, --all             show all next actions
-  -d <due date>, --due <due date>
-                        show only next actions due on or before the date
+  -d [<due date>], --due [<due date>]
+                        show only next actions with a due date; if a date is given, show only next actions due on or
+                        before that date
   -o, --overdue         show only overdue next actions
   -p [<priority>], --priority [<priority>]
                         minimum priority (A-Z) of next actions to show (default: None)
diff --git a/tests/unittests/test_pick_action.py b/tests/unittests/test_pick_action.py
index 5874fb8..05d4db5 100644
--- a/tests/unittests/test_pick_action.py
+++ b/tests/unittests/test_pick_action.py
@@ -205,6 +205,15 @@ class DueTasks(PickActionTestCase):
         self.namespace.due = datetime.date(2000, 1, 1)
         self.assertEqual([overdue], pick_action.next_actions([no_duedate, future_duedate, overdue], self.namespace))
 
+    def test_any_due_tasks(self):
+        """ Test that tasks that are not due are filtered. """
+        no_duedate = todotxt.Task("Task")
+        future_duedate = todotxt.Task("Task due:9999-01-01")
+        overdue = todotxt.Task("Task due:2000-01-01")
+        self.namespace.due = datetime.date.max
+        self.assertEqual([overdue, future_duedate],
+                         pick_action.next_actions([no_duedate, future_duedate, overdue], self.namespace))
+
 
 class MinimimPriorityTest(PickActionTest):
     """ Unit test for the mininum priority filter. """