Commit 7cdcce98 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge branch 'dot'

parents 0c87872f bc08f745
test/data/* text eol=lf
test/data/*.ics binary
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from test.topydo_testcase import TopydoTest from test.topydo_testcase import TopydoTest
from topydo.lib.Utils import escape_ansi from topydo.lib.Utils import escape_ansi
...@@ -27,12 +28,12 @@ class CommandTest(TopydoTest): ...@@ -27,12 +28,12 @@ class CommandTest(TopydoTest):
def out(self, p_output): def out(self, p_output):
if isinstance(p_output, list) and p_output: if isinstance(p_output, list) and p_output:
self.output += escape_ansi( self.output += escape_ansi(
"\n".join([str(s) for s in p_output]) + "\n") os.linesep.join([str(s) for s in p_output]) + os.linesep)
elif p_output: elif p_output:
self.output += str(p_output) + "\n" self.output += str(p_output) + os.linesep
def error(self, p_error): def error(self, p_error):
if isinstance(p_error, list) and p_error: if isinstance(p_error, list) and p_error:
self.errors += escape_ansi(p_error + "\n") + "\n" self.errors += escape_ansi(p_error + os.linesep) + os.linesep
elif p_error: elif p_error:
self.errors += str(p_error) + "\n" self.errors += str(p_error) + os.linesep
(C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project due:2016-11-18 t:2016-11-17
(D) Bar @Context1 +Project2 p:1
(C) Baz @Context1 +Project1 key:value id:1
(C) Drink beer @ home
(C) 13 + 29 = 42
x 2014-12-12 Completed but with date:2014-12-12
digraph topydo {
node [ shape="none" margin="0" fontsize="9" fontname="Helvetica" ]
_1 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>1</B></TD><TD BALIGN="LEFT"><B>Foo @Context2 Not@Context +Project1<BR />Not+Project</B></TD></TR><HR/><TR><TD ALIGN="RIGHT">Prio:</TD><TD ALIGN="LEFT">C</TD></TR><TR><TD ALIGN="RIGHT">Starts:</TD><TD ALIGN="LEFT">2016-11-17 (today)</TD></TR><TR><TD ALIGN="RIGHT">Due:</TD><TD ALIGN="LEFT">2016-11-18 (in a day)</TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_3 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>3</B></TD><TD BALIGN="LEFT"><B>Baz @Context1 +Project1</B></TD></TR><HR/><TR><TD ALIGN="RIGHT">Prio:</TD><TD ALIGN="LEFT">C</TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_4 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>4</B></TD><TD BALIGN="LEFT"><B>Drink beer @ home</B></TD></TR><HR/><TR><TD ALIGN="RIGHT">Prio:</TD><TD ALIGN="LEFT">C</TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_5 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>5</B></TD><TD BALIGN="LEFT"><B>13 + 29 = 42</B></TD></TR><HR/><TR><TD ALIGN="RIGHT">Prio:</TD><TD ALIGN="LEFT">C</TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_2 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>2</B></TD><TD BALIGN="LEFT"><B>Bar @Context1 +Project2</B></TD></TR><HR/><TR><TD ALIGN="RIGHT">Prio:</TD><TD ALIGN="LEFT">D</TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_6 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>6</B></TD><TD BALIGN="LEFT"><B><S>Completed but with</S></B></TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_3 -> _2
_1 -> _4 [style="invis"]
_4 -> _5 [style="invis"]
_5 -> _6 [style="invis"]
}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.lib.PrettyPrinter import PrettyPrinter from topydo.lib.printers.PrettyPrinter import PrettyPrinter
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from topydo.lib.TodoFile import TodoFile from topydo.lib.TodoFile import TodoFile
from topydo.lib.TodoList import TodoList from topydo.lib.TodoList import TodoList
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
import unittest import unittest
from test.topydo_testcase import TopydoTest from test.topydo_testcase import TopydoTest
from topydo.lib.JsonPrinter import JsonPrinter from topydo.lib.printers.Json import JsonPrinter
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
......
...@@ -20,6 +20,7 @@ import os ...@@ -20,6 +20,7 @@ import os
import sys import sys
import unittest import unittest
from collections import namedtuple from collections import namedtuple
from freezegun import freeze_time
from test.command_testcase import CommandTest from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist from test.facilities import load_file_to_todolist
...@@ -527,5 +528,29 @@ class ListCommandIcalTest(CommandTest): ...@@ -527,5 +528,29 @@ class ListCommandIcalTest(CommandTest):
replace_ical_tags(icaltext)) replace_ical_tags(icaltext))
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
@freeze_time('2016, 11, 17')
class ListCommandDotTest(CommandTest):
def setUp(self):
self.maxDiff = None
def test_dot(self):
todolist = load_file_to_todolist("test/data/ListCommandDotTest.txt")
command = ListCommand(["-x", "-f", "dot"], todolist, self.out,
self.error)
command.execute()
self.assertFalse(todolist.dirty)
dottext = ""
with codecs.open('test/data/ListCommandTest.dot', 'r',
encoding='utf-8') as dot:
dottext = dot.read()
self.assertEqual(self.output, dottext)
self.assertEqual(self.errors, "")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -17,7 +17,8 @@ ...@@ -17,7 +17,8 @@
from topydo.lib import Filter from topydo.lib import Filter
from topydo.lib.Command import Command, InvalidCommandArgument from topydo.lib.Command import Command, InvalidCommandArgument
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.PrettyPrinter import pretty_printer_factory from topydo.lib.printers.Dot import DotPrinter
from topydo.lib.printers.PrettyPrinter import pretty_printer_factory
from topydo.lib.Sorter import Sorter from topydo.lib.Sorter import Sorter
from topydo.lib.TodoListBase import InvalidTodoException from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.View import View from topydo.lib.View import View
...@@ -130,6 +131,23 @@ class DepCommand(Command): ...@@ -130,6 +131,23 @@ class DepCommand(Command):
except InvalidCommandArgument: except InvalidCommandArgument:
self.error(self.usage()) self.error(self.usage())
def _handle_dot(self):
""" Handles the dot subsubcommand. """
self.printer = DotPrinter(self.todolist)
arg = self.argument(1)
try:
todo = self.todolist.todo(arg)
todos = set([self.todolist.todo(arg)])
todos |= set(self.todolist.children(todo))
todos |= set(self.todolist.parents(todo))
self.out(self.printer.print_list(todos))
except InvalidTodoException:
self.error("Invalid todo number given.")
def execute(self): def execute(self):
if not super().execute(): if not super().execute():
return False return False
...@@ -140,6 +158,7 @@ class DepCommand(Command): ...@@ -140,6 +158,7 @@ class DepCommand(Command):
'del': self._handle_rm, 'del': self._handle_rm,
'ls': self._handle_ls, 'ls': self._handle_ls,
'clean': self.todolist.clean_dependencies, 'clean': self.todolist.clean_dependencies,
'dot': self._handle_dot,
'gc': self.todolist.clean_dependencies, 'gc': self.todolist.clean_dependencies,
} }
...@@ -154,6 +173,7 @@ class DepCommand(Command): ...@@ -154,6 +173,7 @@ class DepCommand(Command):
dep add <NUMBER> <before|partof|after|parents-of|children-of> <NUMBER> dep add <NUMBER> <before|partof|after|parents-of|children-of> <NUMBER>
dep ls <NUMBER> to dep ls <NUMBER> to
dep ls to <NUMBER> dep ls to <NUMBER>
dep dot <NUMBER>
dep clean""" dep clean"""
def help(self): def help(self):
...@@ -163,5 +183,6 @@ class DepCommand(Command): ...@@ -163,5 +183,6 @@ class DepCommand(Command):
item 1. item 1.
* rm (alias: del) : Removes a dependency. * rm (alias: del) : Removes a dependency.
* ls : Lists all dependencies to or from a certain todo. * ls : Lists all dependencies to or from a certain todo.
* dot : Prints a dependency tree as a Dot graph.
* clean (alias: gc) : Removes redundant id or p tags.\ * clean (alias: gc) : Removes redundant id or p tags.\
""" """
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
from datetime import date from datetime import date
from topydo.lib.DCommand import DCommand from topydo.lib.DCommand import DCommand
from topydo.lib.PrettyPrinter import PrettyPrinter from topydo.lib.printers.PrettyPrinter import PrettyPrinter
from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers
from topydo.lib.Recurrence import NoRecurrenceException, advance_recurring_todo from topydo.lib.Recurrence import NoRecurrenceException, advance_recurring_todo
from topydo.lib.RelativeDate import relative_date_to_date from topydo.lib.RelativeDate import relative_date_to_date
......
...@@ -21,7 +21,7 @@ import os ...@@ -21,7 +21,7 @@ import os
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.ExpressionCommand import ExpressionCommand from topydo.lib.ExpressionCommand import ExpressionCommand
from topydo.lib.Filter import HiddenTagFilter, InstanceFilter from topydo.lib.Filter import HiddenTagFilter, InstanceFilter
from topydo.lib.PrettyPrinter import pretty_printer_factory from topydo.lib.printers.PrettyPrinter import pretty_printer_factory
from topydo.lib.prettyprinters.Format import PrettyPrinterFormatFilter from topydo.lib.prettyprinters.Format import PrettyPrinterFormatFilter
from topydo.lib.TodoListBase import InvalidTodoException from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.Utils import get_terminal_size from topydo.lib.Utils import get_terminal_size
...@@ -64,12 +64,19 @@ class ListCommand(ExpressionCommand): ...@@ -64,12 +64,19 @@ class ListCommand(ExpressionCommand):
self.sort_expression = value self.sort_expression = value
elif opt == '-f': elif opt == '-f':
if value == 'json': if value == 'json':
from topydo.lib.JsonPrinter import JsonPrinter from topydo.lib.printers.Json import JsonPrinter
self.printer = JsonPrinter() self.printer = JsonPrinter()
elif value == 'ical': elif value == 'ical':
if self._poke_icalendar(): if self._poke_icalendar():
from topydo.lib.IcalPrinter import IcalPrinter from topydo.lib.printers.Ical import IcalPrinter
self.printer = IcalPrinter(self.todolist) self.printer = IcalPrinter(self.todolist)
elif value == 'dot':
from topydo.lib.printers.Dot import DotPrinter
self.printer = DotPrinter(self.todolist)
# a graph without dependencies is not so useful, hence
# show all
self.show_all = True
else: else:
self.printer = None self.printer = None
elif opt == '-F': elif opt == '-F':
...@@ -197,9 +204,12 @@ Lists all relevant todos. A todo is relevant when: ...@@ -197,9 +204,12 @@ Lists all relevant todos. A todo is relevant when:
When an EXPRESSION is given, only the todos matching that EXPRESSION are shown. When an EXPRESSION is given, only the todos matching that EXPRESSION are shown.
-f : Specify the OUTPUT FORMAT, being 'text' (default), 'ical' or 'json'. -f : Specify the OUTPUT format, being 'text' (default), 'dot' or 'ical' or
'json'.
* 'text' - Text output with colors and indentation if applicable. * 'text' - Text output with colors and indentation if applicable.
* 'dot' - Prints a dependency graph for the selected items in GraphViz
Dot format.
* 'ical' - iCalendar (RFC 2445). Is not supported in Python 3.2. Be aware * 'ical' - iCalendar (RFC 2445). Is not supported in Python 3.2. Be aware
that this is not a read-only operation, todo items may obtain that this is not a read-only operation, todo items may obtain
an 'ical' tag with a unique ID. Completed todo items may be an 'ical' tag with a unique ID. Completed todo items may be
......
...@@ -45,6 +45,62 @@ class Color: ...@@ -45,6 +45,62 @@ class Color:
'white': 15, 'white': 15,
} }
# Source: https://gist.github.com/jasonm23/2868981
html_color_dict = {
0: "#000000", 1: "#800000", 2: "#008000", 3: "#808000", 4: "#000080",
5: "#800080", 6: "#008080", 7: "#c0c0c0", 8: "#808080", 9: "#ff0000",
10: "#00ff00", 11: "#ffff00", 12: "#0000ff", 13: "#ff00ff", 14: "#00ffff",
15: "#ffffff", 16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af",
20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f", 24: "#005f87",
25: "#005faf", 26: "#005fd7", 27: "#005fff", 28: "#008700", 29: "#00875f",
30: "#008787", 31: "#0087af", 32: "#0087d7", 33: "#0087ff", 34: "#00af00",
35: "#00af5f", 36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff",
40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af", 44: "#00d7d7",
45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f", 48: "#00ff87", 49: "#00ffaf",
50: "#00ffd7", 51: "#00ffff", 52: "#5f0000", 53: "#5f005f", 54: "#5f0087",
55: "#5f00af", 56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f",
60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff", 64: "#5f8700",
65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7", 69: "#5f87ff",
70: "#5faf00", 71: "#5faf5f", 72: "#5faf87", 73: "#5fafaf", 74: "#5fafd7",
75: "#5fafff", 76: "#5fd700", 77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af",
80: "#5fd7d7", 81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87",
85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000", 89: "#87005f",
90: "#870087", 91: "#8700af", 92: "#8700d7", 93: "#8700ff", 94: "#875f00",
95: "#875f5f", 96: "#875f87", 97: "#875faf", 98: "#875fd7", 99: "#875fff",
100: "#878700", 101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7",
105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87", 109: "#87afaf",
110: "#87afd7", 111: "#87afff", 112: "#87d700", 113: "#87d75f", 114: "#87d787",
115: "#87d7af", 116: "#87d7d7", 117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f",
120: "#87ff87", 121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000",
125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7", 129: "#af00ff",
130: "#af5f00", 131: "#af5f5f", 132: "#af5f87", 133: "#af5faf", 134: "#af5fd7",
135: "#af5fff", 136: "#af8700", 137: "#af875f", 138: "#af8787", 139: "#af87af",
140: "#af87d7", 141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87",
145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700", 149: "#afd75f",
150: "#afd787", 151: "#afd7af", 152: "#afd7d7", 153: "#afd7ff", 154: "#afff00",
155: "#afff5f", 156: "#afff87", 157: "#afffaf", 158: "#afffd7", 159: "#afffff",
160: "#d70000", 161: "#d7005f", 162: "#d70087", 163: "#d700af", 164: "#d700d7",
165: "#d700ff", 166: "#d75f00", 167: "#d75f5f", 168: "#d75f87", 169: "#d75faf",
170: "#d75fd7", 171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787",
175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#dfaf00", 179: "#dfaf5f",
180: "#dfaf87", 181: "#dfafaf", 182: "#dfafdf", 183: "#dfafff", 184: "#dfdf00",
185: "#dfdf5f", 186: "#dfdf87", 187: "#dfdfaf", 188: "#dfdfdf", 189: "#dfdfff",
190: "#dfff00", 191: "#dfff5f", 192: "#dfff87", 193: "#dfffaf", 194: "#dfffdf",
195: "#dfffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087", 199: "#ff00af",
200: "#ff00df", 201: "#ff00ff", 202: "#ff5f00", 203: "#ff5f5f", 204: "#ff5f87",
205: "#ff5faf", 206: "#ff5fdf", 207: "#ff5fff", 208: "#ff8700", 209: "#ff875f",
210: "#ff8787", 211: "#ff87af", 212: "#ff87df", 213: "#ff87ff", 214: "#ffaf00",
215: "#ffaf5f", 216: "#ffaf87", 217: "#ffafaf", 218: "#ffafdf", 219: "#ffafff",
220: "#ffdf00", 221: "#ffdf5f", 222: "#ffdf87", 223: "#ffdfaf", 224: "#ffdfdf",
225: "#ffdfff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87", 229: "#ffffaf",
230: "#ffffdf", 231: "#ffffff", 232: "#080808", 233: "#121212", 234: "#1c1c1c",
235: "#262626", 236: "#303030", 237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e",
240: "#585858", 241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080",
245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8", 249: "#b2b2b2",
250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0", 253: "#dadada", 254: "#e4e4e4",
255: "#eeeeee",
}
def __init__(self, p_value=None): def __init__(self, p_value=None):
""" p_value is user input, be it a word color or an xterm code """ """ p_value is user input, be it a word color or an xterm code """
self._value = None self._value = None
...@@ -117,3 +173,21 @@ class Color: ...@@ -117,3 +173,21 @@ class Color:
color color
) )
def as_html(self):
try:
return Color.html_color_dict[self.color]
except KeyError:
return '#ffffff'
def as_rgb(self):
"""
Returns a tuple (r, g, b) of the color.
"""
html = self.as_html()
return (
int(html[1:3], 16),
int(html[3:5], 16),
int(html[5:7], 16)
)
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
import getopt import getopt
from topydo.lib.PrettyPrinter import PrettyPrinter from topydo.lib.printers.PrettyPrinter import PrettyPrinter
class InvalidCommandArgument(Exception): class InvalidCommandArgument(Exception):
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
import re import re
from topydo.lib.MultiCommand import MultiCommand from topydo.lib.MultiCommand import MultiCommand
from topydo.lib.PrettyPrinter import PrettyPrinter from topydo.lib.printers.PrettyPrinter import PrettyPrinter
from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers
......
...@@ -21,7 +21,7 @@ import re ...@@ -21,7 +21,7 @@ import re
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.ProgressColor import progress_color from topydo.lib.ProgressColor import progress_color
from topydo.lib.Utils import get_terminal_size, escape_ansi from topydo.lib.Utils import get_terminal_size, escape_ansi, humanize_date
MAIN_PATTERN = (r'^({{(?P<before>.+?)}})?' MAIN_PATTERN = (r'^({{(?P<before>.+?)}})?'
r'(?P<placeholder>{ph}|\[{ph}\])' r'(?P<placeholder>{ph}|\[{ph}\])'
...@@ -39,12 +39,6 @@ def _filler(p_str, p_len): ...@@ -39,12 +39,6 @@ def _filler(p_str, p_len):
to_fill = p_len - len(p_str) to_fill = p_len - len(p_str)
return to_fill*' ' + p_str return to_fill*' ' + p_str
def humanize_date(p_datetime):
""" Returns a relative date string from a datetime object. """
now = arrow.now()
date = now.replace(day=p_datetime.day, month=p_datetime.month, year=p_datetime.year)
return date.humanize().replace('just now', 'today')
def humanize_dates(p_due=None, p_start=None, p_creation=None): def humanize_dates(p_due=None, p_start=None, p_creation=None):
""" """
Returns string with humanized versions of p_due, p_start and p_creation. Returns string with humanized versions of p_due, p_start and p_creation.
......
...@@ -24,7 +24,7 @@ from datetime import date ...@@ -24,7 +24,7 @@ from datetime import date
from topydo.lib import Filter from topydo.lib import Filter
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.HashListValues import hash_list_values from topydo.lib.HashListValues import hash_list_values
from topydo.lib.PrettyPrinter import PrettyPrinter from topydo.lib.printers.PrettyPrinter import PrettyPrinter
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
from topydo.lib.View import View from topydo.lib.View import View
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
Various utility functions. Various utility functions.
""" """
import arrow
import re import re
from collections import namedtuple from collections import namedtuple
...@@ -109,3 +110,10 @@ def translate_key_to_config(p_key): ...@@ -109,3 +110,10 @@ def translate_key_to_config(p_key):
key = p_key key = p_key
return key return key
def humanize_date(p_datetime):
""" Returns a relative date string from a datetime object. """
now = arrow.now()
date = now.replace(day=p_datetime.day, month=p_datetime.month, year=p_datetime.year)
return date.humanize().replace('just now', 'today')
# 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 a graph in Dot
notation. Useful for displaying dependencies.
"""
from textwrap import wrap
from topydo.lib.printers.PrettyPrinter import Printer
from topydo.lib.ProgressColor import progress_color
from topydo.lib.Utils import humanize_date
class DotPrinter(Printer):
"""
A printer that converts a list of Todo items to a string in Dot format.
"""
def __init__(self, p_todolist):
super(DotPrinter, self).__init__()
self.todolist = p_todolist
def print_list(self, p_todos):
def node_label(p_todo):
"""
Prints an HTML table for a node label with some todo details.
"""
node_result = '<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top">'
def print_row(p_value1, p_value2):
return '<TR><TD ALIGN="RIGHT">{}</TD><TD ALIGN="LEFT">{}</TD></TR>'.format(p_value1, p_value2)
node_result += '<TR><TD><B>{}</B></TD><TD BALIGN="LEFT"><B>{}{}{}</B></TD></TR>'.format(
self.todolist.number(p_todo),
"<S>" if todo.is_completed() else "",
"<BR />".join(wrap(p_todo.text(), 35)),
"</S>" if todo.is_completed() else "",
)
priority = p_todo.priority()
start_date = p_todo.start_date()
due_date = p_todo.due_date()
if priority or start_date or due_date:
node_result += '<HR/>'
if priority:
node_result += print_row('Prio:', p_todo.priority())
if start_date:
node_result += print_row('Starts:', "{} ({})".format(
start_date.isoformat(),
humanize_date(start_date)
))
if due_date:
node_result += print_row('Due:', "{} ({})".format(
due_date.isoformat(),
humanize_date(due_date)
))
node_result += '</TABLE>>'
return node_result
def foreground(p_background):
"""
Chooses a suitable foreground color (black or white) given a
background color.
"""
(r, g, b) = p_background.as_rgb()
brightness = (r * 299 + g * 587 + b * 114) / ( 255 * 1000 )
return '#ffffff' if brightness < 0.5 else '#000000'
node_name = lambda t: '_' + str(self.todolist.number(t))
result = 'digraph topydo {\n'
result += 'node [ shape="none" margin="0" fontsize="9" fontname="Helvetica" ]\n';
# print todos
for todo in p_todos:
background_color = progress_color(todo)
result += ' {} [label={} style=filled fillcolor="{}" fontcolor="{}"]\n'.format(
node_name(todo),
node_label(todo),
background_color.as_html(),
foreground(background_color),
)
# print edges
for todo in p_todos:
# only print the children that are actually in the list of todos
children = set(p_todos) & set(self.todolist.children(todo,
p_only_direct=True))
for child in children:
result += ' {} -> {}\n'.format(
node_name(todo),
node_name(child)
)
todos_without_dependencies = [todo for todo in p_todos if not self.todolist.children(todo) and not self.todolist.parents(todo)]
for index in range(0, len(todos_without_dependencies) - 1):
this_todo = todos_without_dependencies[index]
next_todo = todos_without_dependencies[index + 1]
result += ' {} -> {} [style="invis"]\n'.format(node_name(this_todo), node_name(next_todo))
result += '}\n'
return result
...@@ -23,7 +23,7 @@ import random ...@@ -23,7 +23,7 @@ import random
import string import string
from datetime import datetime, time from datetime import datetime, time
from topydo.lib.PrettyPrinter import Printer from topydo.lib.printers.PrettyPrinter import Printer
def _convert_priority(p_priority): def _convert_priority(p_priority):
......
...@@ -21,7 +21,7 @@ such that other applications can process it. ...@@ -21,7 +21,7 @@ such that other applications can process it.
import json import json
from topydo.lib.PrettyPrinter import Printer from topydo.lib.printers.PrettyPrinter import Printer
def _convert_todo(p_todo): def _convert_todo(p_todo):
......
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