def __init__(self, p_todo): # clients use this to associate this widget with the given todo item self.todo = p_todo todo_text = TEXT_FORMATTER.parse(p_todo) priority_text = PRIO_FORMATTER.parse(p_todo) # split todo_text at each occurrence of tag/project/context/url txt_pattern = r'|'.join([PRJ_CON_PATTERN, TAG_PATTERN, URL_PATTERN]) txt_pattern = r'(' + txt_pattern + r')' txt_splitted = re.split(txt_pattern, todo_text) txt_markup = [] # Examine each substring and apply relevant palette entry if needed for substring in txt_splitted: # re.split can generate empty strings when capturing group is used if not substring: continue if re.match(TAG_PATTERN, substring): txt_markup.append((PaletteItem.METADATA, substring)) elif re.match(URL_PATTERN, substring): txt_markup.append((PaletteItem.LINK, substring)) elif re.match(PRJ_CON_PATTERN, substring): if substring.startswith('+'): txt_markup.append((PaletteItem.PROJECT, substring)) else: txt_markup.append((PaletteItem.CONTEXT, substring)) else: txt_markup.append(substring) self.id_widget = urwid.Text('', align='right') priority_widget = urwid.Text(priority_text) self.text_widget = urwid.Text(txt_markup) progress = to_urwid_color(progress_color(p_todo)) if config().colors() else PaletteItem.DEFAULT self.progress_bar = urwid.AttrMap( urwid.SolidFill(' '), {}, ) self.update_progress() self.columns = urwid.Columns( [ (1, self.progress_bar), (4, self.id_widget), (3, priority_widget), ('weight', 1, self.text_widget), ], dividechars=1, box_columns=[0] # the progress bar adapts its height to the rest ) self.widget = urwid.AttrMap( self.columns, _markup(p_todo, p_focus=False), _markup(p_todo, p_focus=True) ) super().__init__(self.widget)
def test_progress30(self): """ Progress color determined by parent """ todolist = TodoList([ "Foo id:1", "Bar p:1", ]) color = progress_color(todolist.todo(2)) # the parent has no influence here self.assertEqual(color.color, 2)
def test_progress29(self): """ Progress color determined by parent """ todolist = TodoList([ "Overdue id:1 due:2015-12-31", "Bar p:1 t:2016-01-01 due:2016-01-01", ]) color = progress_color(todolist.todo(2)) # the parent has no influence here self.assertEqual(color.color, 3)
def test_progress28(self): """ Progress color determined by parent """ todolist = TodoList([ "Overdue id:1 due:2015-12-31", "Bar p:1", ]) color = progress_color(todolist.todo(2)) # color the subitem red because it has no color of its own and its # parent is overdue self.assertEqual(color.color, 1)
def test_progress10(self): set_256_colors() color = progress_color(Todo('Foo due:2016-01-02')) # a length of 14 days is assumed self.assertEqual(color.color, 208)
def test_progress8(self): """ Due today (256) """ set_256_colors() color = progress_color(Todo('Foo due:2016-01-01')) self.assertEqual(color.color, 202)
def test_progress27(self): """ Creation date after due date """ set_256_colors() color = progress_color(Todo('2016-01-03 Foo due:2016-01-02')) # a length of 14 days is assumed self.assertEqual(color.color, 208)
def test_progress24(self): """ Due tomorrow (creation date + start date) """ set_256_colors() color = progress_color( Todo('2015-12-01 Foo due:2016-01-02 t:2015-12-31')) self.assertEqual(color.color, 118)
def test_progress20(self): """ Due tomorrow (creation date + strict recurrence + start date) """ set_256_colors() color = progress_color( Todo('2015-12-01 Foo due:2016-01-02 rec:+1d t:2016-01-02')) self.assertEqual(color.color, 22)
def print_list(self, p_todos): def node_label(p_todo): """ Prints an HTML table for a node label with some todo details. """ def escape_dot_label(p_string): """ HTML like labels in Dot may not have raw ampersands, quotes or angle brackets. These should be properly replaced with the escaped character notation. """ return p_string.replace('&', '&').replace( '"', '"').replace('<', '<').replace('>', '>') 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(map(escape_dot_label, 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 sorted(list(children), key=lambda t: t.text()): 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
def test_progress5(self): """ Test overdue tasks """ color = progress_color(Todo('Foo due:2015-12-31')) self.assertEqual(color.color, 1)
def test_progress4(self): """ Test progress of task with no length (but with creation date). """ set_256_colors() color = progress_color(Todo('2016-02-11 Foo')) self.assertEqual(color.color, 22)
def test_progress3(self): """ Test progress of task with no length """ set_256_colors() color = progress_color(Todo('Foo')) self.assertEqual(color.color, 22)
def test_progress1(self): """ Test progress of task with no length """ color = progress_color(Todo('Foo')) self.assertEqual(color.color, 2)
def test_progress21(self): """ Due tomorrow (creation date + start date) """ color = progress_color(Todo('2016-12-01 Foo due:2016-01-02 t:2016-01-02')) self.assertEqual(color.color, 2)
def test_progress24(self): """ Due tomorrow (creation date + start date) """ set_256_colors() color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 t:2015-12-31')) self.assertEqual(color.color, 118)
def test_progress12(self): """ Due tomorrow (creation date) """ set_256_colors() color = progress_color(Todo('2016-01-01 Foo due:2016-01-02')) self.assertEqual(color.color, 22)
def test_progress6(self): """ Test overdue tasks """ set_256_colors() color = progress_color(Todo('Foo due:2015-12-31')) self.assertEqual(color.color, 196)
def test_progress16(self): """ Due tomorrow (creation date + recurrence) """ set_256_colors() color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 rec:1d')) self.assertEqual(color.color, 22)
def test_progress20(self): """ Due tomorrow (creation date + strict recurrence + start date) """ set_256_colors() color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 rec:+1d t:2016-01-02')) self.assertEqual(color.color, 22)
def test_progress14(self): """ Due tomorrow (recurrence) """ set_256_colors() color = progress_color(Todo('Foo due:2016-01-02 rec:1d')) self.assertEqual(color.color, 22)
def test_progress21(self): """ Due tomorrow (creation date + start date) """ color = progress_color( Todo('2016-12-01 Foo due:2016-01-02 t:2016-01-02')) self.assertEqual(color.color, 2)
def test_progress26(self): """ Start date after due date """ set_256_colors() color = progress_color(Todo('Foo due:2016-01-02 t:2016-01-03')) # a length of 14 days is assumed self.assertEqual(color.color, 208)
def test_progress7(self): """ Due today """ color = progress_color(Todo('Foo due:2016-01-01')) self.assertEqual(color.color, 3)
def test_progress9(self): """ Due tomorrow """ color = progress_color(Todo('Foo due:2016-01-02')) # a length of 14 days is assumed self.assertEqual(color.color, 3)
def test_progress11(self): """ Due tomorrow (creation date) """ color = progress_color(Todo('2016-01-01 Foo due:2016-01-02')) # a length of 14 days is assumed self.assertEqual(color.color, 2)
def update_progress(self): color = to_urwid_color(progress_color( self.todo)) if config().colors() else PaletteItem.DEFAULT self.progress_bar.set_attr_map( {None: urwid.AttrSpec(PaletteItem.DEFAULT, color, 256)})
def __init__(self, p_todo, p_id_width=4): # clients use this to associate this widget with the given todo item self.todo = p_todo todo_text = TEXT_FORMATTER.parse(p_todo) if p_todo.is_completed(): priority_text = ' x ' else: priority_text = PRIO_FORMATTER.parse(p_todo) # split todo_text at each occurrence of tag/project/context/url txt_pattern = r'|'.join([PRJ_CON_PATTERN, TAG_PATTERN, URL_PATTERN]) txt_pattern = r'(' + txt_pattern + r')' txt_splitted = re.split(txt_pattern, todo_text) txt_markup = [] # Examine each substring and apply relevant palette entry if needed for substring in txt_splitted: # re.split can generate empty strings when capturing group is used if not substring: continue if re.match(TAG_PATTERN, substring): txt_markup.append((PaletteItem.METADATA, substring)) elif re.match(URL_PATTERN, substring): txt_markup.append((PaletteItem.LINK, substring)) elif re.match(PRJ_CON_PATTERN, substring): if substring.startswith('+'): txt_markup.append((PaletteItem.PROJECT, substring)) else: txt_markup.append((PaletteItem.CONTEXT, substring)) else: txt_markup.append(substring) self.id_widget = urwid.Text('', align='right') priority_widget = urwid.Text(priority_text) self.text_widget = urwid.Text(txt_markup) progress = to_urwid_color(progress_color( p_todo)) if config().colors() else PaletteItem.DEFAULT self.progress_bar = urwid.AttrMap( urwid.SolidFill(' '), {}, ) self.update_progress() self.columns = urwid.Columns( [ (1, self.progress_bar), (p_id_width, self.id_widget), (3, priority_widget), ('weight', 1, self.text_widget), ], dividechars=1, box_columns=[0] # the progress bar adapts its height to the rest ) self.widget = urwid.AttrMap(self.columns, _markup(p_todo, p_focus=False), _markup(p_todo, p_focus=True)) super().__init__(self.widget)
def update_progress(self): color = to_urwid_color(progress_color(self.todo)) if config().colors() else PaletteItem.DEFAULT self.progress_bar.set_attr_map( {None: urwid.AttrSpec(PaletteItem.DEFAULT, color, 256)} )
def color_block(p_todo): return '{} {}'.format( progress_color(p_todo).as_ansi(p_background=True), config().priority_color(p_todo.priority()).as_ansi(), )
def print_list(self, p_todos): def node_label(p_todo): """ Prints an HTML table for a node label with some todo details. """ def escape_dot_label(p_string): """ HTML like labels in Dot may not have raw ampersands, quotes or angle brackets. These should be properly replaced with the escaped character notation. """ return p_string.replace('&', '&').replace('"', '"').replace( '<', '<').replace('>', '>') 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(map(escape_dot_label, 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 sorted(list(children), key=lambda t: t.text()): 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