mirror of
https://github.com/topydo/topydo.git
synced 2024-05-20 13:58:33 +00:00
Merge branch 'dot'
This commit is contained in:
commit
7cdcce987d
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
test/data/* text eol=lf
|
||||
test/data/*.ics binary
|
|
@ -14,6 +14,7 @@
|
|||
# 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 os
|
||||
from test.topydo_testcase import TopydoTest
|
||||
from topydo.lib.Utils import escape_ansi
|
||||
|
||||
|
@ -27,12 +28,12 @@ class CommandTest(TopydoTest):
|
|||
def out(self, p_output):
|
||||
if isinstance(p_output, list) and p_output:
|
||||
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:
|
||||
self.output += str(p_output) + "\n"
|
||||
self.output += str(p_output) + os.linesep
|
||||
|
||||
def error(self, 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:
|
||||
self.errors += str(p_error) + "\n"
|
||||
self.errors += str(p_error) + os.linesep
|
||||
|
|
7
test/data/ListCommandDotTest.txt
Normal file
7
test/data/ListCommandDotTest.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
(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
|
14
test/data/ListCommandTest.dot
Normal file
14
test/data/ListCommandTest.dot
Normal file
|
@ -0,0 +1,14 @@
|
|||
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 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# 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.TodoFile import TodoFile
|
||||
from topydo.lib.TodoList import TodoList
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import unittest
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import os
|
|||
import sys
|
||||
import unittest
|
||||
from collections import namedtuple
|
||||
from freezegun import freeze_time
|
||||
|
||||
from test.command_testcase import CommandTest
|
||||
from test.facilities import load_file_to_todolist
|
||||
|
@ -527,5 +528,29 @@ class ListCommandIcalTest(CommandTest):
|
|||
replace_ical_tags(icaltext))
|
||||
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__':
|
||||
unittest.main()
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
from topydo.lib import Filter
|
||||
from topydo.lib.Command import Command, InvalidCommandArgument
|
||||
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.TodoListBase import InvalidTodoException
|
||||
from topydo.lib.View import View
|
||||
|
@ -130,6 +131,23 @@ class DepCommand(Command):
|
|||
except InvalidCommandArgument:
|
||||
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):
|
||||
if not super().execute():
|
||||
return False
|
||||
|
@ -140,6 +158,7 @@ class DepCommand(Command):
|
|||
'del': self._handle_rm,
|
||||
'ls': self._handle_ls,
|
||||
'clean': self.todolist.clean_dependencies,
|
||||
'dot': self._handle_dot,
|
||||
'gc': self.todolist.clean_dependencies,
|
||||
}
|
||||
|
||||
|
@ -154,6 +173,7 @@ class DepCommand(Command):
|
|||
dep add <NUMBER> <before|partof|after|parents-of|children-of> <NUMBER>
|
||||
dep ls <NUMBER> to
|
||||
dep ls to <NUMBER>
|
||||
dep dot <NUMBER>
|
||||
dep clean"""
|
||||
|
||||
def help(self):
|
||||
|
@ -163,5 +183,6 @@ class DepCommand(Command):
|
|||
item 1.
|
||||
* rm (alias: del) : Removes a dependency.
|
||||
* 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.\
|
||||
"""
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
from datetime import date
|
||||
|
||||
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.Recurrence import NoRecurrenceException, advance_recurring_todo
|
||||
from topydo.lib.RelativeDate import relative_date_to_date
|
||||
|
|
|
@ -21,7 +21,7 @@ import os
|
|||
from topydo.lib.Config import config
|
||||
from topydo.lib.ExpressionCommand import ExpressionCommand
|
||||
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.TodoListBase import InvalidTodoException
|
||||
from topydo.lib.Utils import get_terminal_size
|
||||
|
@ -64,12 +64,19 @@ class ListCommand(ExpressionCommand):
|
|||
self.sort_expression = value
|
||||
elif opt == '-f':
|
||||
if value == 'json':
|
||||
from topydo.lib.JsonPrinter import JsonPrinter
|
||||
from topydo.lib.printers.Json import JsonPrinter
|
||||
self.printer = JsonPrinter()
|
||||
elif value == 'ical':
|
||||
if self._poke_icalendar():
|
||||
from topydo.lib.IcalPrinter import IcalPrinter
|
||||
from topydo.lib.printers.Ical import IcalPrinter
|
||||
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:
|
||||
self.printer = None
|
||||
elif opt == '-F':
|
||||
|
@ -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.
|
||||
|
||||
-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.
|
||||
* '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
|
||||
that this is not a read-only operation, todo items may obtain
|
||||
an 'ical' tag with a unique ID. Completed todo items may be
|
||||
|
|
|
@ -45,6 +45,62 @@ class Color:
|
|||
'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):
|
||||
""" p_value is user input, be it a word color or an xterm code """
|
||||
self._value = None
|
||||
|
@ -117,3 +173,21 @@ class 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 @@
|
|||
|
||||
import getopt
|
||||
|
||||
from topydo.lib.PrettyPrinter import PrettyPrinter
|
||||
from topydo.lib.printers.PrettyPrinter import PrettyPrinter
|
||||
|
||||
|
||||
class InvalidCommandArgument(Exception):
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import re
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import re
|
|||
|
||||
from topydo.lib.Config import config
|
||||
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>.+?)}})?'
|
||||
r'(?P<placeholder>{ph}|\[{ph}\])'
|
||||
|
@ -39,12 +39,6 @@ def _filler(p_str, p_len):
|
|||
to_fill = p_len - len(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):
|
||||
"""
|
||||
Returns string with humanized versions of p_due, p_start and p_creation.
|
||||
|
|
|
@ -24,7 +24,7 @@ from datetime import date
|
|||
from topydo.lib import Filter
|
||||
from topydo.lib.Config import config
|
||||
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.View import View
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
Various utility functions.
|
||||
"""
|
||||
|
||||
import arrow
|
||||
import re
|
||||
|
||||
from collections import namedtuple
|
||||
|
@ -109,3 +110,10 @@ def translate_key_to_config(p_key):
|
|||
key = p_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')
|
||||
|
||||
|
|
127
topydo/lib/printers/Dot.py
Normal file
127
topydo/lib/printers/Dot.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
# 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
|
|||
import string
|
||||
from datetime import datetime, time
|
||||
|
||||
from topydo.lib.PrettyPrinter import Printer
|
||||
from topydo.lib.printers.PrettyPrinter import Printer
|
||||
|
||||
|
||||
def _convert_priority(p_priority):
|
|
@ -21,7 +21,7 @@ such that other applications can process it.
|
|||
|
||||
import json
|
||||
|
||||
from topydo.lib.PrettyPrinter import Printer
|
||||
from topydo.lib.printers.PrettyPrinter import Printer
|
||||
|
||||
|
||||
def _convert_todo(p_todo):
|
0
topydo/lib/printers/__init__.py
Normal file
0
topydo/lib/printers/__init__.py
Normal file
Loading…
Reference in a new issue