Commit 82ab00a6 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge branch 'ical'

parents 23985524 57a6ab53
......@@ -8,6 +8,9 @@ setup(
author = "Bram Schoenmakers",
author_email = "me@bramschoenmakers.nl",
url = "https://github.com/bram85/topydo",
extras_require = {
'ical': ['icalendar'],
},
entry_points= {
'console_scripts': ['topydo = topydo.cli.Main:main'],
},
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import unittest
from topydo.lib.Config import config
import CommandTest
from topydo.lib.IcalCommand import IcalCommand
import TestFacilities
class IcalCommandTest(CommandTest.CommandTest):
def setUp(self):
super(IcalCommandTest, self).setUp()
self.todolist = TestFacilities.load_file_to_todolist("test/data/ListCommandTest.txt")
def test_ical(self):
def replace_ical_tags(p_text):
# replace identifiers with dots, since they're random.
result = re.sub(r'\bical:....\b', 'ical:....', p_text)
result = re.sub(r'\bUID:....\b', 'UID:....', result)
return result
command = IcalCommand([""], self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
icaltext = ""
with open('test/data/ListCommandTest.ics', 'r') as ical:
icaltext = "".join(ical.readlines())
self.assertEquals(replace_ical_tags(self.output), replace_ical_tags(icaltext))
self.assertEquals(self.errors, "")
def test_help(self):
command = IcalCommand(["help"], self.todolist, self.out, self.error)
command.execute()
self.assertEquals(self.output, "")
self.assertEquals(self.errors, command.usage() + "\n\n" + command.help() + "\n")
if __name__ == '__main__':
unittest.main()
......@@ -26,10 +26,6 @@ class ListCommandTest(CommandTest.CommandTest):
super(ListCommandTest, self).setUp()
self.todolist = TestFacilities.load_file_to_todolist("test/data/ListCommandTest.txt")
def tearDown(self):
# restore to the default configuration in case a custom one was set
config("")
def test_list1(self):
command = ListCommand([""], self.todolist, self.out, self.error)
command.execute()
......
......@@ -26,10 +26,6 @@ class SortCommandTest(CommandTest.CommandTest):
super(SortCommandTest, self).setUp()
self.todolist = TestFacilities.load_file_to_todolist("test/data/SorterTest1.txt")
def tearDown(self):
# restore to the default configuration in case a custom one was set
config("")
def test_sort1(self):
""" Alphabetically sorted """
command = SortCommand(["text"], self.todolist, self.out, self.error)
......
......@@ -36,10 +36,6 @@ class TodoListTester(TopydoTest):
self.text = ''.join(lines)
self.todolist = TodoList(lines)
def tearDown(self):
# restore to the default configuration in case a custom one was set
config("")
def test_contexts(self):
self.assertEquals(set(['Context1', 'Context2']), \
self.todolist.contexts())
......
......@@ -19,9 +19,9 @@ import unittest
from topydo.lib.Config import config
class TopydoTest(unittest.TestCase):
def setUp(self):
def tearDown(self):
"""
Make sure that every test case starts with a clean configuration.
Make sure that every test case leaves a clean configuration.
"""
config("")
This diff was suppressed by a .gitattributes entry.
......@@ -41,6 +41,7 @@ Available commands:
* dep
* do
* edit
* ical
* ls
* listcon (lscon)
* listprojects (lsprj)
......@@ -97,6 +98,7 @@ from topydo.lib.DepCommand import DepCommand
from topydo.lib.DepriCommand import DepriCommand
from topydo.lib.DoCommand import DoCommand
from topydo.lib.EditCommand import EditCommand
from topydo.lib.IcalCommand import IcalCommand
from topydo.lib.ListCommand import ListCommand
from topydo.lib.ListContextCommand import ListContextCommand
from topydo.lib.ListProjectCommand import ListProjectCommand
......@@ -118,6 +120,7 @@ SUBCOMMAND_MAP = {
'depri': DepriCommand,
'do': DoCommand,
'edit': EditCommand,
'ical': IcalCommand,
'ls': ListCommand,
'lscon': ListContextCommand,
'listcon': ListContextCommand,
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -21,7 +21,7 @@ import re
from topydo.lib.Config import config
from topydo.lib.Command import Command
from topydo.lib.PrettyPrinter import pretty_print
from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers
from topydo.lib.RelativeDate import relative_date_to_date
from topydo.lib.TodoListBase import InvalidTodoException
......@@ -93,7 +93,8 @@ class AddCommand(Command):
self.todo = self.todolist.add(self.text)
self._postprocess_input_todo()
self.out(pretty_print(self.todo, [self.todolist.pp_number()]))
self.printer.add_filter(PrettyPrinterNumbers(self.todolist))
self.out(self.printer.print_todo(self.todo))
else:
self.error(self.usage())
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib.PrettyPrinter import pretty_print
from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers
from topydo.lib.TodoListBase import InvalidTodoException
class AppendCommand(Command):
......@@ -37,7 +37,9 @@ class AppendCommand(Command):
if text:
todo = self.todolist.todo(number)
self.todolist.append(todo, text)
self.out(pretty_print(todo, [self.todolist.pp_number()]))
self.printer.add_filter(PrettyPrinterNumbers(self.todolist))
self.out(self.printer.print_todo(todo))
else:
self.error(self.usage())
except InvalidCommandArgument:
......
......@@ -16,6 +16,8 @@
import getopt
from topydo.lib.PrettyPrinter import PrettyPrinter
class InvalidCommandArgument(Exception):
pass
......@@ -49,6 +51,9 @@ class Command(object):
self.error = p_err
self.prompt = p_prompt
# make pretty printer available
self.printer = PrettyPrinter()
def execute(self):
"""
Execute the command. Intercepts the help subsubcommand to show the help
......
......@@ -16,8 +16,9 @@
import re
from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib.PrettyPrinter import pretty_print, pretty_print_list
from topydo.lib.Command import Command
from topydo.lib.PrettyPrinter import PrettyPrinter
from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers
from topydo.lib.TodoListBase import InvalidTodoException
class DCommand(Command):
......@@ -72,8 +73,9 @@ class DCommand(Command):
)
def _print_list(self, p_todos):
filters = [self.todolist.pp_number()]
self.out("\n".join(pretty_print_list(p_todos, filters)))
printer = PrettyPrinter()
printer.add_filter(PrettyPrinterNumbers(self.todolist))
self.out(printer.print_list(p_todos))
def prompt_text(self):
return "Yes or no? [y/N] "
......@@ -93,7 +95,7 @@ class DCommand(Command):
if not self.force and re.match('^y(es)?$', confirmation, re.I):
for child in children:
self.execute_specific_core(child)
self.out(self.prefix() + pretty_print(child))
self.out(self.prefix() + self.printer.print_todo(child))
def _print_unlocked_todos(self, p_old, p_new):
delta = [todo for todo in p_new if todo not in p_old]
......
......@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.DCommand import DCommand
from topydo.lib.PrettyPrinter import pretty_print
class DeleteCommand(DCommand):
def __init__(self, p_args, p_todolist,
......@@ -35,7 +34,7 @@ class DeleteCommand(DCommand):
self.todolist.delete(p_todo)
def execute_specific(self, p_todo):
self.out(self.prefix() + pretty_print(p_todo))
self.out(self.prefix() + self.printer.print_todo(p_todo))
self.execute_specific_core(p_todo)
def usage(self):
......
......@@ -97,7 +97,8 @@ class DepCommand(Command):
if todos:
sorter = Sorter(config().sort_string())
instance_filter = Filter.InstanceFilter(todos)
view = View(sorter, [instance_filter], self.todolist)
view = View(sorter, [instance_filter], self.todolist,
self.printer)
self.out(view.pretty_print())
except InvalidTodoException:
self.error("Invalid todo number given.")
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib.PrettyPrinter import pretty_print
from topydo.lib.TodoListBase import InvalidTodoException
class DepriCommand(Command):
......@@ -37,7 +36,7 @@ class DepriCommand(Command):
if todo.priority() != None:
self.todolist.set_priority(todo, None)
self.out("Priority removed.")
self.out(pretty_print(todo))
self.out(self.printer.print_todo(todo))
except InvalidCommandArgument:
self.error(self.usage())
except (InvalidTodoException):
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -17,7 +17,8 @@
from datetime import date
from topydo.lib.DCommand import DCommand
from topydo.lib.PrettyPrinter import pretty_print
from topydo.lib.PrettyPrinter import PrettyPrinter
from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers
from topydo.lib.Recurrence import advance_recurring_todo, strict_advance_recurring_todo
from topydo.lib.Utils import date_string_to_date
......@@ -56,7 +57,10 @@ class DoCommand(DCommand):
self.completion_date)
self.todolist.add_todo(new_todo)
self.out(pretty_print(new_todo, [self.todolist.pp_number()]))
printer = PrettyPrinter()
printer.add_filter(PrettyPrinterNumbers(self.todolist))
self.out(printer.print_todo(new_todo))
def prompt_text(self):
return "Also mark subtasks as done? [y/N] "
......@@ -77,7 +81,9 @@ class DoCommand(DCommand):
""" Actions specific to this command. """
self._handle_recurrence(p_todo)
self.execute_specific_core(p_todo)
self.out(self.prefix() + pretty_print(p_todo))
printer = PrettyPrinter()
self.out(self.prefix() + printer.print_todo(p_todo))
def execute_specific_core(self, p_todo):
"""
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Implements a subcommand that outputs an iCalendar file.
"""
from topydo.lib.IcalPrinter import IcalPrinter
from topydo.lib.ListCommand import ListCommand
class IcalCommand(ListCommand):
def __init__(self, p_args, p_todolist,
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(IcalCommand, self).__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.printer = IcalPrinter(p_todolist)
def _print(self):
self.out(str(self._view()))
def execute(self):
try:
import icalendar as _
except ImportError:
self.error("icalendar package is not installed.")
return False
return super(IcalCommand, self).execute()
def usage(self):
return """Synopsis: ical [-x] [expression]"""
def help(self):
return """\
Similar to the 'ls' subcommand, except that the todos are printed in iCalendar
format (RFC 2445) that can be imported by other calendar applications.
By default prints the active todo items, possibly filtered by the given
expression.
For the supported options, please refer to the help text of 'ls'
(topydo help ls).
While specifying the sort order is supported (-s flag), like in 'ls', this is
not meaningful in the context of an iCalendar file.
Note: be aware that this is not necessarily a read-only operation. This
subcommand may add ical tags to the printed todo items containing a unique ID.
Completed todo items may be archived.
Note: topydo does not support reading iCal files, this is merely a dump.
Changes made with other iCalendar enabled applications will not be processed.
Suggested usage is to use the output as a read-only calendar.
"""
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Provides a printer that transforms a list of Todo items to an iCalendar
file according to RFC 2445.
"""
try:
import icalendar as ical
ICAL_PRESENT = True
except ImportError:
ICAL_PRESENT = False
from datetime import datetime, time
import random
import string
from topydo.lib.PrettyPrinter import Printer
def _convert_priority(p_priority):
"""
Converts todo.txt priority to an iCalendar priority (RFC 2445).
Priority A gets priority 1, priority B gets priority 5 and priority C-F get
priorities 6-9. This scheme makes sure that clients that use "high",
"medium" and "low" show the correct priority.
"""
result = 0
prio_map = {
'A': 1,
'B': 5,
'C': 6,
'D': 7,
'E': 8,
'F': 9,
}
try:
result = prio_map[p_priority]
except KeyError:
if p_priority:
# todos with no priority have priority None, and result of this
# function will be 0. For all other letters, return 9 (lowest
# priority in RFC 2445).
result = 9
return result
class IcalPrinter(Printer):
"""
A printer that converts a list of Todo items to a string in iCalendar
format (RFC 2445).
https://www.rfc-editor.org/rfc/rfc2445.txt
"""
def __init__(self, p_todolist):
super(IcalPrinter, self).__init__()
self.todolist = p_todolist
def print_todo(self, p_todo):
return self._convert_todo(p_todo).to_ical() if ICAL_PRESENT else ""
def print_list(self, p_todos):
result = ""
if ICAL_PRESENT:
cal = ical.Calendar()
cal.add('prodid', '-//bramschoenmakers.nl//topydo//')
cal.add('version', '2.0')
for todo in p_todos:
cal.add_component(self._convert_todo(todo))
result = cal.to_ical()
return result
def _convert_todo(self, p_todo):
""" Converts a Todo instance (Topydo) to an icalendar Todo instance. """
def _get_uid(p_todo):
"""
Gets a unique ID from a todo item, stored by the ical tag. If the
tag is not present, a random value is assigned to it and returned.
"""
def generate_uid(p_length=4):
"""
Generates a random string of the given length, used as
identifier.
"""
return ''.join(
random.choice(string.ascii_letters + string.digits)
for i in xrange(p_length))
uid = p_todo.tag_value('ical')
if not uid:
uid = generate_uid()
p_todo.set_tag('ical', uid)
self.todolist.set_dirty()
return uid
result = ical.Todo()
# this should be called first, it may set the ical: tag and therefore
# change the source() output.
result['uid'] = _get_uid(p_todo)
result['summary'] = ical.vText(p_todo.text())
result['description'] = ical.vText(p_todo.source())
result.add('priority', _convert_priority(p_todo.priority()))
start = p_todo.start_date()
if start:
result.add('dtstart', start)
due = p_todo.due_date()
if due:
result.add('due', due)
created = p_todo.creation_date()
if created:
result.add('created', created)
completed = p_todo.completion_date()
if completed:
completed = datetime.combine(completed, time(0, 0))
result.add('completed', completed)
return result
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -19,8 +19,9 @@ import re
from topydo.lib.Command import Command
from topydo.lib.Config import config
from topydo.lib import Filter
from topydo.lib.PrettyPrinter import pp_indent
from topydo.lib.PrettyPrinterFilter import PrettyPrinterIndentFilter
from topydo.lib.Sorter import Sorter
from topydo.lib.View import View
class ListCommand(Command):
def __init__(self, p_args, p_todolist,
......@@ -74,17 +75,23 @@ class ListCommand(Command):
return filters
def _view(self):
sorter = Sorter(self.sort_expression)
filters = self._filters()
return View(sorter, filters, self.todolist, self.printer)
def _print(self):
""" Prints the todos. """
indent = config().list_indent()
self.out(self._view().pretty_print([PrettyPrinterIndentFilter(indent)]))
def execute(self):
if not super(ListCommand, self).execute():
return False
self._process_flags()
sorter = Sorter(self.sort_expression)
filters = self._filters()
pp_filters = [pp_indent(config().list_indent())]
self.out(self.todolist.view(sorter, filters).pretty_print(pp_filters))
self._print()
def usage(self):
return """Synopsis: ls [-x] [-s <sort_expression>] [expression]"""
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -18,7 +18,7 @@ from datetime import date, timedelta
from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib.Config import config
from topydo.lib.PrettyPrinter import pretty_print
from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers
from topydo.lib.RelativeDate import relative_date_to_date
from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.Utils import date_string_to_date
......@@ -74,7 +74,8 @@ class PostponeCommand(Command):
todo.set_tag(config().tag_due(), new_due.isoformat())
self.todolist.set_dirty()
self.out(pretty_print(todo, [self.todolist.pp_number()]))
self.printer.add_filter(PrettyPrinterNumbers(self.todolist))
self.out(self.printer.print_todo(todo))
else:
self.error("Invalid date pattern given.")
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -14,75 +14,52 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Provides a function to pretty print a list of todo items. """
import re
from topydo.lib.Config import config
PRIORITY_COLORS = {
'A': '\033[36m', # cyan
'B': '\033[33m', # yellow
'C': '\033[34m' # blue
}
PROJECT_COLOR = '\033[31m' # red
NEUTRAL_COLOR = '\033[0m'
def pp_color(p_todo_str, p_todo):
class Printer(object):
"""
Adds colors to the todo string by inserting ANSI codes.
An abstract class that turns todo items into strings.
Should be passed as a filter in the filter list of pretty_print()
Subclasses must at least implement the print_todo method.
"""
if config().colors():
color = NEUTRAL_COLOR
try:
color = PRIORITY_COLORS[p_todo.priority()]
except KeyError:
pass
p_todo_str = color + p_todo_str + NEUTRAL_COLOR
if config().highlight_projects_contexts():
p_todo_str = re.sub(
r'\B(\+|@)(\S*\w)',
PROJECT_COLOR + r'\g<0>' + color,
p_todo_str)
p_todo_str += NEUTRAL_COLOR
return p_todo_str
def pp_indent(p_indent=0):
return lambda s, t: ' ' * p_indent + s
def pretty_print(p_todo, p_filters=None):
def print_todo(self, p_todo):
""" Base implementation. Simply returns the string conversion. """
return str(p_todo)
def print_list(self, p_todos):
"""
Given a list of todo items, pretty print it and return a list of
formatted strings.
"""
return "\n".join([self.print_todo(todo) for todo in p_todos])
class PrettyPrinter(Printer):
"""
Given a todo item, pretty print it and return a list of formatted strings.
p_filters is a list of functions that transform the output string, each
function accepting two arguments:
Prints todo items on a single line, decorated by the filters passed by
the caller.
* the todo's text that has to be modified;
* the todo object itself which allows for obtaining relevant information.
Example is pp_color in this fle.
The caller can adjust the output by passing on a set of filters, that may
add colors, indentation, etc. These filters are found in the
PrettyPrinterFilter module.
"""
p_filters = p_filters or []
def __init__(self):
"""
Constructor.
"""
super(PrettyPrinter, self).__init__()
self.filters = []
todo_str = str(p_todo)
def add_filter(self, p_filter):
"""
Adds a filter to be applied when calling print_todo.
for f in p_filters:
todo_str = f(todo_str, p_todo)
p_filter is an instance of a PrettyPrinterFilter.
"""
self.filters.append(p_filter)
return todo_str
def print_todo(self, p_todo):
""" Given a todo item, pretty print it. """
todo_str = str(p_todo)
def pretty_print_list(p_todos, p_filters=None):
"""
Given a list of todo items, pretty print it and return a list of
formatted strings.
"""
p_filters = p_filters or []
return [pretty_print(todo, p_filters) for todo in p_todos]
for ppf in self.filters:
todo_str = ppf.filter(todo_str, p_todo)
return todo_str
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Provides filters used for pretty printing. """
import re
from topydo.lib.Config import config
class PrettyPrinterFilter(object):
"""
Base class for a pretty printer filter.
Subclasses must reimplement the filter method.
"""
def filter(self, p_todo_str, _):
""" Default implementation returns an unmodified todo string. """
return p_todo_str
PRIORITY_COLORS = {
'A': '\033[36m', # cyan
'B': '\033[33m', # yellow
'C': '\033[34m' # blue
}
PROJECT_COLOR = '\033[31m' # red
NEUTRAL_COLOR = '\033[0m'
class PrettyPrinterColorFilter(PrettyPrinterFilter):
"""
Adds colors to the todo string by inserting ANSI codes.
Should be passed as a filter in the filter list of pretty_print()
"""
def filter(self, p_todo_str, p_todo):
""" Applies the colors. """
if config().colors():
color = NEUTRAL_COLOR
try:
color = PRIORITY_COLORS[p_todo.priority()]
except KeyError:
pass
p_todo_str = color + p_todo_str + NEUTRAL_COLOR
if config().highlight_projects_contexts():
p_todo_str = re.sub(
r'\B(\+|@)(\S*\w)',
PROJECT_COLOR + r'\g<0>' + color,
p_todo_str)
p_todo_str += NEUTRAL_COLOR
return p_todo_str
class PrettyPrinterIndentFilter(PrettyPrinterFilter):
""" Adds indentation to the todo item. """
def __init__(self, p_indent=0):
super(PrettyPrinterIndentFilter, self).__init__()
self.indent = p_indent
def filter(self, p_todo_str, _):
""" Applies the indentation. """
return ' ' * self.indent + p_todo_str
class PrettyPrinterNumbers(PrettyPrinterFilter):
""" Prepends the todo's number, retrieved from the todolist. """
def __init__(self, p_todolist):
super(PrettyPrinterNumbers, self).__init__()
self.todolist = p_todolist
def filter(self, p_todo_str, p_todo):
""" Prepends the number to the todo string. """
return "|{:>3}| {}".format(self.todolist.number(p_todo), p_todo_str)
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib.PrettyPrinter import pretty_print
from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.Utils import is_valid_priority
......@@ -48,7 +47,7 @@ class PriorityCommand(Command):
elif not old_priority:
self.out("Priority set to {}.".format(priority))
self.out(pretty_print(todo))
self.out(self.printer.print_todo(todo))
else:
self.error("Invalid priority given.")
except InvalidCommandArgument:
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -15,8 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib.PrettyPrinterFilter import PrettyPrinterNumbers
from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.PrettyPrinter import pretty_print
class TagCommand(Command):
def __init__(self, p_args, p_todolist,
......@@ -60,7 +60,8 @@ class TagCommand(Command):
self.value = ""
def _print(self):
self.out(pretty_print(self.todo, [self.todolist.pp_number()]))
self.printer.add_filter(PrettyPrinterNumbers(self.todolist))
self.out(self.printer.print_todo(self.todo))
def _choose(self):
"""
......
......@@ -24,7 +24,7 @@ import re
from topydo.lib.Config import config
from topydo.lib import Filter
from topydo.lib.HashListValues import hash_list_values
from topydo.lib.PrettyPrinter import pretty_print_list
from topydo.lib.PrettyPrinter import PrettyPrinter
from topydo.lib.Todo import Todo
from topydo.lib.View import View
......@@ -242,14 +242,6 @@ class TodoListBase(object):
except (ValueError, KeyError):
raise InvalidTodoException
def pp_number(self):
"""
A filter for the pretty printer to append the todo number to the
printed todo.
"""
return lambda p_todo_str, p_todo: \
"|{:>3}| {}".format(self.number(p_todo), p_todo_str)
def _update_todo_ids(self):
# the idea is to have a hash that is independent of the position of the
# todo. Use the text (without tags) of the todo to keep the id as stable
......@@ -264,5 +256,6 @@ class TodoListBase(object):
self._id_todo_map[uid] = todo
def __str__(self):
return '\n'.join(pretty_print_list(self._todos))
printer = PrettyPrinter()
return printer.print_list(self._todos)
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 Bram Schoenmakers <me@bramschoenmakers.nl>
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -16,19 +16,29 @@
""" A view is a list of todos, sorted and filtered. """
from topydo.lib.PrettyPrinter import pretty_print_list, pp_color
from topydo.lib.PrettyPrinterFilter import (
PrettyPrinterColorFilter,
PrettyPrinterNumbers
)
from topydo.lib.PrettyPrinter import PrettyPrinter
class View(object):
"""
A view is instantiated by a todo list, usually obtained from a todo.txt
file. Also a sorter and a list of filters should be given that is applied
to the list.
A printer can be passed, but it won't be used when pretty_print() is
called, since it will instantiate its own pretty printer instance.
"""
def __init__(self, p_sorter, p_filters, p_todolist):
def __init__(self, p_sorter, p_filters, p_todolist,
p_printer=PrettyPrinter()):
self._todolist = p_todolist
self._viewdata = []
self._sorter = p_sorter
self._filters = p_filters
self._printer = p_printer
self.update()
......@@ -45,8 +55,16 @@ class View(object):
def pretty_print(self, p_pp_filters=None):
""" Pretty prints the view. """
p_pp_filters = p_pp_filters or []
pp_filters = [self._todolist.pp_number(), pp_color] + p_pp_filters
return '\n'.join(pretty_print_list(self._viewdata, pp_filters))
# since we're using filters, always use PrettyPrinter
printer = PrettyPrinter()
printer.add_filter(PrettyPrinterNumbers(self._todolist))
printer.add_filter(PrettyPrinterColorFilter())
for ppf in p_pp_filters:
printer.add_filter(ppf)
return printer.print_list(self._viewdata)
def __str__(self):
return '\n'.join(pretty_print_list(self._viewdata))
return self._printer.print_list(self._viewdata)
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