Commit 5bcf70e6 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge pull request #30 from mruwek/add-todos-from-file

Add todos from file with '-f' flag.
parents f31b49f1 4380d872
...@@ -16,7 +16,16 @@ ...@@ -16,7 +16,16 @@
from datetime import date from datetime import date
import unittest import unittest
# We're searching for 'mock'
# pylint: disable=no-name-in-module
try:
from unittest import mock
except ImportError:
import mock
from six import u from six import u
from io import StringIO
from topydo.commands import AddCommand from topydo.commands import AddCommand
from topydo.commands import ListCommand from topydo.commands import ListCommand
...@@ -242,6 +251,21 @@ class AddCommandTest(CommandTest): ...@@ -242,6 +251,21 @@ class AddCommandTest(CommandTest):
self.assertEqual(self.output, utf8(u("| 1| {} Special \u25c4\n").format(self.today))) self.assertEqual(self.output, utf8(u("| 1| {} Special \u25c4\n").format(self.today)))
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
@mock.patch("topydo.commands.AddCommand.stdin", StringIO(u("Fo\u00f3 due:tod id:1\nB\u0105r before:1")))
def test_add_from_stdin(self):
command = AddCommand.AddCommand(["-f", "-"], self.todolist, self.out, self.error)
command.execute()
self.assertEqual(self.output, utf8(u("| 1| {tod} Fo\u00f3 due:{tod} id:1\n| 2| {tod} B\u0105r p:1\n".format(tod=self.today))))
self.assertEqual(self.errors, "")
def test_add_from_file(self):
command = AddCommand.AddCommand(["-f", "test/data/AddCommandTest-from_file.txt"], self.todolist, self.out, self.error)
command.execute()
self.assertEqual(self.output, utf8(u("| 1| {tod} Foo @fo\u00f3b\u0105r due:{tod} id:1\n| 2| {tod} Bar +baz t:{tod} p:1\n".format(tod=self.today))))
self.assertEqual(self.errors, "")
def test_help(self): def test_help(self):
command = AddCommand.AddCommand(["help"], self.todolist, self.out, self.error) command = AddCommand.AddCommand(["help"], self.todolist, self.out, self.error)
command.execute() command.execute()
......
Foo @foóbąr due:tod id:1
Bar +baz t:tod before:1
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
from datetime import date from datetime import date
import re import re
from sys import stdin
import codecs
from os.path import expanduser
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.Command import Command from topydo.lib.Command import Command
...@@ -33,73 +36,109 @@ class AddCommand(Command): ...@@ -33,73 +36,109 @@ class AddCommand(Command):
super(AddCommand, self).__init__( super(AddCommand, self).__init__(
p_args, p_todolist, p_out, p_err, p_prompt) p_args, p_todolist, p_out, p_err, p_prompt)
self.text = ' '.join(p_args) self.text = ' '.join(p_args)
self.todo = None self.from_file = None
def _preprocess_input_todo(self): def _process_flags(self):
""" opts, args = self.getopt('f:')
Preprocesses user input when adding a task.
It detects a priority mid-sentence and puts it at the start. for opt, value in opts:
""" if opt == '-f':
self.text = re.sub(r'^(.+) (\([A-Z]\))(.*)$', r'\2 \1\3', self.text) self.from_file = expanduser(value)
def _postprocess_input_todo(self): self.args = args
"""
Post-processes a parsed todo when adding it to the list.
* It converts relative dates to absolute ones.
* Automatically inserts a creation date if not present.
* Handles more user-friendly dependencies with before:, partof: and
after: tags
"""
def convert_date(p_tag):
value = self.todo.tag_value(p_tag)
if value: def get_todos_from_file(self):
dateobj = relative_date_to_date(value) if self.from_file == '-':
if dateobj: f = stdin
self.todo.set_tag(p_tag, dateobj.isoformat()) else:
f = codecs.open(self.from_file, 'r', encoding='utf-8')
todos = f.read().splitlines()
return todos
def _add_todo(self, p_todo_text):
def _preprocess_input_todo(p_todo_text):
"""
Preprocesses user input when adding a task.
It detects a priority mid-sentence and puts it at the start.
"""
todo_text = re.sub(r'^(.+) (\([A-Z]\))(.*)$', r'\2 \1\3', p_todo_text)
return todo_text
def _postprocess_input_todo(p_todo):
"""
Post-processes a parsed todo when adding it to the list.
def add_dependencies(p_tag): * It converts relative dates to absolute ones.
for value in self.todo.tag_values(p_tag): * Automatically inserts a creation date if not present.
try: * Handles more user-friendly dependencies with before:, partof: and
dep = self.todolist.todo(value) after: tags
"""
def convert_date(p_tag):
value = p_todo.tag_value(p_tag)
if p_tag == 'after': if value:
self.todolist.add_dependency(self.todo, dep) dateobj = relative_date_to_date(value)
elif p_tag == 'before' or p_tag == 'partof': if dateobj:
self.todolist.add_dependency(dep, self.todo) p_todo.set_tag(p_tag, dateobj.isoformat())
except InvalidTodoException:
pass
self.todo.remove_tag(p_tag, value) def add_dependencies(p_tag):
for value in p_todo.tag_values(p_tag):
try:
dep = self.todolist.todo(value)
convert_date(config().tag_start()) if p_tag == 'after':
convert_date(config().tag_due()) self.todolist.add_dependency(p_todo, dep)
elif p_tag == 'before' or p_tag == 'partof':
self.todolist.add_dependency(dep, p_todo)
except InvalidTodoException:
pass
add_dependencies('partof') p_todo.remove_tag(p_tag, value)
add_dependencies('before')
add_dependencies('after')
self.todo.set_creation_date(date.today()) convert_date(config().tag_start())
convert_date(config().tag_due())
add_dependencies('partof')
add_dependencies('before')
add_dependencies('after')
p_todo.set_creation_date(date.today())
todo_text = _preprocess_input_todo(p_todo_text)
todo = self.todolist.add(todo_text)
_postprocess_input_todo(todo)
self.out(self.printer.print_todo(todo))
def execute(self): def execute(self):
""" Adds a todo item to the list. """ """ Adds a todo item to the list. """
if not super(AddCommand, self).execute(): if not super(AddCommand, self).execute():
return False return False
if self.text: self.printer.add_filter(PrettyPrinterNumbers(self.todolist))
self._preprocess_input_todo() self._process_flags()
self.todo = self.todolist.add(self.text)
self._postprocess_input_todo() if self.from_file:
new_todos = self.get_todos_from_file()
self.printer.add_filter(PrettyPrinterNumbers(self.todolist)) for todo in new_todos:
self.out(self.printer.print_todo(self.todo)) self._add_todo(todo)
else: else:
self.error(self.usage()) if self.text:
self._add_todo(self.text)
else:
self.error(self.usage())
def usage(self): def usage(self):
return """Synopsis: add <text>""" return """Synopsis:
add <text>
add -f <file>
add -f -"""
def help(self): def help(self):
return """\ return """\
...@@ -114,4 +153,6 @@ This subcommand automatically adds the creation date to the added item. ...@@ -114,4 +153,6 @@ This subcommand automatically adds the creation date to the added item.
todo number (not the dependency number). todo number (not the dependency number).
Example: add "Subtask partof:1" Example: add "Subtask partof:1"
-f : Add todo items from specified <file> or from standard input.
""" """
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment