From 8c236331df1bacdb75bb58653aa2dfdef7165d2e Mon Sep 17 00:00:00 2001 From: Nic West Date: Mon, 1 Feb 2016 01:27:02 +0000 Subject: [PATCH 1/5] add pep8 and flake8 to setup.cfg --- setup.cfg | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/setup.cfg b/setup.cfg index f4047d6..affbce9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,13 @@ [pytest] addopts = -rsx --exitfirst todotxt_machine/test/ + [bdist_wheel] universal=1 + +[pep8] +ignore=E501 +max-line-length=100 + +[flake8] +ignore=E501 +max-line-length=100 From 5742ef8948e4292373784eff8d4b8bd0d6c16863 Mon Sep 17 00:00:00 2001 From: Nic West Date: Mon, 1 Feb 2016 01:28:17 +0000 Subject: [PATCH 2/5] autopep8 run --- todotxt_machine/cli.py | 26 +- todotxt_machine/colorscheme.py | 9 +- todotxt_machine/keys.py | 92 ++--- todotxt_machine/terminal_operations.py | 9 +- .../test/terminal_operations_test.py | 30 +- todotxt_machine/test/todo_test.py | 169 ++++---- todotxt_machine/todo.py | 101 ++--- todotxt_machine/urwid_ui.py | 386 +++++++++--------- 8 files changed, 438 insertions(+), 384 deletions(-) diff --git a/todotxt_machine/cli.py b/todotxt_machine/cli.py index e707780..3d82619 100755 --- a/todotxt_machine/cli.py +++ b/todotxt_machine/cli.py @@ -47,6 +47,7 @@ autosave_lock = threading.Lock() + def autosave(): if not enable_autosave: return @@ -65,11 +66,13 @@ def autosave(): timer = threading.Timer(30.0, autosave) + def exit_with_error(message): - sys.stderr.write(message.strip(' \n')+'\n') + sys.stderr.write(message.strip(' \n') + '\n') print(__doc__.split('\n\n')[1]) exit(1) + def get_real_path(filename, description): # expand enviroment variables and username, get canonical path file_path = os.path.realpath(os.path.expanduser(os.path.expandvars(filename))) @@ -87,6 +90,7 @@ def get_real_path(filename, description): return file_path + def get_boolean_config_option(cfg, section, option, default=False): value = dict(cfg.items(section)).get(option, default) if (type(value) != bool and @@ -98,6 +102,7 @@ def get_boolean_config_option(cfg, section, option, default=False): value = False return value + def main(): random.seed() @@ -114,7 +119,7 @@ def main(): cfg.add_section('keys') if arguments['--show-default-bindings']: - d = {k: ", ".join(v) for k,v in KeyBindings({}).key_bindings.items()} + d = {k: ", ".join(v) for k, v in KeyBindings({}).key_bindings.items()} cfg._sections['keys'] = OrderedDict(sorted(d.items(), key=lambda t: t[0])) cfg.write(sys.stdout) exit(0) @@ -123,10 +128,10 @@ def main(): cfg.read(os.path.expanduser(arguments['--config'])) # Load keybindings specified in the [keys] section of the config file - keyBindings = KeyBindings(dict( cfg.items('keys') )) + keyBindings = KeyBindings(dict(cfg.items('keys'))) # load the colorscheme defined in the user config, else load the default scheme - colorscheme = ColorScheme(dict( cfg.items('settings') ).get('colorscheme', 'default'), cfg) + colorscheme = ColorScheme(dict(cfg.items('settings')).get('colorscheme', 'default'), cfg) # Get auto-saving setting (defaults to False) global enable_autosave @@ -134,7 +139,7 @@ def main(): # Load the todo.txt file specified in the [settings] section of the config file # a todo.txt file on the command line takes precedence - todotxt_file = dict( cfg.items('settings') ).get('file', arguments['TODOFILE']) + todotxt_file = dict(cfg.items('settings')).get('file', arguments['TODOFILE']) if arguments['TODOFILE']: todotxt_file = arguments['TODOFILE'] @@ -143,11 +148,10 @@ def main(): # Load the done.txt file specified in the [settings] section of the config file # a done.txt file on the command line takes precedence - donetxt_file = dict( cfg.items('settings') ).get('archive', arguments['DONEFILE']) + donetxt_file = dict(cfg.items('settings')).get('archive', arguments['DONEFILE']) if arguments['DONEFILE']: donetxt_file = arguments['DONEFILE'] - todotxt_file_path = get_real_path(todotxt_file, 'todo.txt') if donetxt_file is not None: @@ -162,17 +166,17 @@ def main(): exit_with_error("ERROR: unable to open {0}\n\nEither specify one as an argument on the command line or set it in your configuration file ({0}).".format(todotxt_file_path, arguments['--config'])) todos = Todos([], todotxt_file_path, donetxt_file_path) - show_toolbar = get_boolean_config_option(cfg, 'settings', 'show-toolbar') + show_toolbar = get_boolean_config_option(cfg, 'settings', 'show-toolbar') show_filter_panel = get_boolean_config_option(cfg, 'settings', 'show-filter-panel') - enable_borders = get_boolean_config_option(cfg, 'settings', 'enable-borders') - enable_word_wrap = get_boolean_config_option(cfg, 'settings', 'enable-word-wrap') + enable_borders = get_boolean_config_option(cfg, 'settings', 'enable-borders') + enable_word_wrap = get_boolean_config_option(cfg, 'settings', 'enable-word-wrap') global view view = UrwidUI(todos, keyBindings, colorscheme) timer.start() - view.main( # start up the urwid UI event loop + view.main( # start up the urwid UI event loop enable_borders, enable_word_wrap, show_toolbar, diff --git a/todotxt_machine/colorscheme.py b/todotxt_machine/colorscheme.py index 666a923..8ad240c 100644 --- a/todotxt_machine/colorscheme.py +++ b/todotxt_machine/colorscheme.py @@ -12,6 +12,7 @@ import ConfigParser config_parser_module = ConfigParser + class ColorScheme: def __init__(self, name, user_config): @@ -27,7 +28,7 @@ def load_colors(self, name): # Use user defined theme in the user_config if it exists if self.user_config.has_section(colorscheme_section): - self.colors = dict( self.user_config.items(colorscheme_section) ) + self.colors = dict(self.user_config.items(colorscheme_section)) else: # Try to load a built in theme cfg = config_parser_module.ConfigParser() @@ -38,7 +39,7 @@ def load_colors(self, name): cfg.read(self.built_in_colors_directory + "/default") colorscheme_section = "colorscheme-default" if cfg.has_section(colorscheme_section): - self.colors = dict( cfg.items(colorscheme_section) ) + self.colors = dict(cfg.items(colorscheme_section)) # Split foreground and background values for key, value in self.colors.items(): @@ -53,8 +54,8 @@ def load_colors(self, name): # dialog_button_color = self.colors['dialog_button_color']['bg'] for key, value in list(self.colors.items()): if key not in ['selected', 'dialog_color', 'dialog_button_color']: - self.colors[key+'_selected'] = {'fg': self.colors[key]['fg'], 'bg': selected_background_color} - self.colors[key+'_dialog_color'] = {'fg': self.colors[key]['fg'], 'bg': dialog_color} + self.colors[key + '_selected'] = {'fg': self.colors[key]['fg'], 'bg': selected_background_color} + self.colors[key + '_dialog_color'] = {'fg': self.colors[key]['fg'], 'bg': dialog_color} # self.colors[key+'_dialog_button_color'] = {'fg': self.colors[key]['fg'], 'bg': dialog_button_color} self.focus_map[key] = key + '_selected' self.dialog_focus_map[key] = key + '_dialog_color' diff --git a/todotxt_machine/keys.py b/todotxt_machine/keys.py index 393792e..240a716 100644 --- a/todotxt_machine/keys.py +++ b/todotxt_machine/keys.py @@ -1,10 +1,10 @@ class KeyBindings: - user_keys = [] + user_keys = [] key_bindings = {} def __init__(self, user_keys): self.user_keys = user_keys - self.fillWithDefault(); + self.fillWithDefault() self.fillWithUserKeys(user_keys) def fillWithUserKeys(self, users_keys): @@ -14,53 +14,53 @@ def fillWithUserKeys(self, users_keys): default = self.key_bindings[bind] self.key_bindings[bind] = key except KeyError: - print("KeyBind \""+bind+"\" not found") + print("KeyBind \"" + bind + "\" not found") def fillWithDefault(self): - self.key_bindings['toggle-help'] = ['h', '?'] - self.key_bindings['quit'] = ['q'] - self.key_bindings['toggle-toolbar'] = ['t'] - self.key_bindings['toggle-borders'] = ['b'] - self.key_bindings['toggle-wrapping'] = ['w'] - self.key_bindings['save'] = ['S'] - self.key_bindings['reload'] = ['R'] - self.key_bindings['down'] = ['j', 'down'] - self.key_bindings['up'] = ['k', 'up'] - self.key_bindings['top'] = ['g'] - self.key_bindings['right'] = ['L', 'right'] - self.key_bindings['left'] = ['H', 'left'] - self.key_bindings['bottom'] = ['G'] - self.key_bindings['change-focus'] = ['tab'] - self.key_bindings['toggle-complete'] = ['x'] - self.key_bindings['archive'] = ['X'] - self.key_bindings['append'] = ['n'] - self.key_bindings['insert-after'] = ['o'] - self.key_bindings['insert-before'] = ['O'] - self.key_bindings['priority-up'] = ['p'] - self.key_bindings['priority-down'] = ['P'] - self.key_bindings['save-item'] = ['enter'] - self.key_bindings['edit'] = ['enter', 'A', 'e'] - self.key_bindings['delete'] = ['D'] - self.key_bindings['swap-down'] = ['J'] - self.key_bindings['swap-up'] = ['K'] - self.key_bindings['edit-complete'] = ['tab'] - self.key_bindings['edit-save'] = ['return'] - self.key_bindings['edit-move-left'] = ['left'] - self.key_bindings['edit-move-right'] = ['right'] - self.key_bindings['edit-word-left'] = ['meta b', 'ctrl b'] - self.key_bindings['edit-word-right'] = ['meta f', 'ctrl f'] - self.key_bindings['edit-end'] = ['ctrl e', 'end'] - self.key_bindings['edit-home'] = ['ctrl a', 'home'] + self.key_bindings['toggle-help'] = ['h', '?'] + self.key_bindings['quit'] = ['q'] + self.key_bindings['toggle-toolbar'] = ['t'] + self.key_bindings['toggle-borders'] = ['b'] + self.key_bindings['toggle-wrapping'] = ['w'] + self.key_bindings['save'] = ['S'] + self.key_bindings['reload'] = ['R'] + self.key_bindings['down'] = ['j', 'down'] + self.key_bindings['up'] = ['k', 'up'] + self.key_bindings['top'] = ['g'] + self.key_bindings['right'] = ['L', 'right'] + self.key_bindings['left'] = ['H', 'left'] + self.key_bindings['bottom'] = ['G'] + self.key_bindings['change-focus'] = ['tab'] + self.key_bindings['toggle-complete'] = ['x'] + self.key_bindings['archive'] = ['X'] + self.key_bindings['append'] = ['n'] + self.key_bindings['insert-after'] = ['o'] + self.key_bindings['insert-before'] = ['O'] + self.key_bindings['priority-up'] = ['p'] + self.key_bindings['priority-down'] = ['P'] + self.key_bindings['save-item'] = ['enter'] + self.key_bindings['edit'] = ['enter', 'A', 'e'] + self.key_bindings['delete'] = ['D'] + self.key_bindings['swap-down'] = ['J'] + self.key_bindings['swap-up'] = ['K'] + self.key_bindings['edit-complete'] = ['tab'] + self.key_bindings['edit-save'] = ['return'] + self.key_bindings['edit-move-left'] = ['left'] + self.key_bindings['edit-move-right'] = ['right'] + self.key_bindings['edit-word-left'] = ['meta b', 'ctrl b'] + self.key_bindings['edit-word-right'] = ['meta f', 'ctrl f'] + self.key_bindings['edit-end'] = ['ctrl e', 'end'] + self.key_bindings['edit-home'] = ['ctrl a', 'home'] self.key_bindings['edit-delete-word'] = ['ctrl w'] - self.key_bindings['edit-delete-end'] = ['ctrl k'] - self.key_bindings['edit-delete-beginning'] = ['ctrl u'] - self.key_bindings['edit-paste'] = ['ctrl y'] - self.key_bindings['toggle-filter'] = ['f'] - self.key_bindings['clear-filter'] = ['F'] - self.key_bindings['toggle-sorting'] = ['s'] - self.key_bindings['search'] = ['/'] - self.key_bindings['search-end'] = ['enter'] - self.key_bindings['search-clear'] = ['C'] + self.key_bindings['edit-delete-end'] = ['ctrl k'] + self.key_bindings['edit-delete-beginning'] = ['ctrl u'] + self.key_bindings['edit-paste'] = ['ctrl y'] + self.key_bindings['toggle-filter'] = ['f'] + self.key_bindings['clear-filter'] = ['F'] + self.key_bindings['toggle-sorting'] = ['s'] + self.key_bindings['search'] = ['/'] + self.key_bindings['search-end'] = ['enter'] + self.key_bindings['search-clear'] = ['C'] def __getitem__(self, index): return ", ".join(self.key_bindings[index]) diff --git a/todotxt_machine/terminal_operations.py b/todotxt_machine/terminal_operations.py index 4ec6fd4..d640495 100644 --- a/todotxt_machine/terminal_operations.py +++ b/todotxt_machine/terminal_operations.py @@ -10,11 +10,12 @@ import fcntl import struct + class TerminalOperations: """For interacting with the terminal""" _escape_sequence_regex = re.compile(r'\x1b\[[0-9;]*m') - _screen_size_regex = re.compile(r'\[8;(.*);(.*)t') + _screen_size_regex = re.compile(r'\[8;(.*);(.*)t') @staticmethod def foreground_color(index): @@ -52,10 +53,10 @@ def clear_screen(self): def screen_size(self, use_tput=False): # Method: Usint tput if use_tput: - return ( int(subprocess.check_output(["tput", "cols"])), int(subprocess.check_output(["tput", "lines"])) ) + return (int(subprocess.check_output(["tput", "cols"])), int(subprocess.check_output(["tput", "lines"]))) else: # this is how urwid does it in raw_terminal display mode - buf = fcntl.ioctl(0, termios.TIOCGWINSZ, ' '*4) + buf = fcntl.ioctl(0, termios.TIOCGWINSZ, ' ' * 4) y, x = struct.unpack('hh', buf) return x, y @@ -85,7 +86,6 @@ def screen_size(self, use_tput=False): # Method using curses module # return self.window.getmaxyx() - def move_cursor(self, row, column): self.output("\x1B[{0};{1}H".format(row, column)) @@ -158,4 +158,3 @@ def ljust_with_escapes(line, columns, string_length=0): # ensure # `stty #{state}` # end - diff --git a/todotxt_machine/test/terminal_operations_test.py b/todotxt_machine/test/terminal_operations_test.py index 48d3689..7a9e155 100644 --- a/todotxt_machine/test/terminal_operations_test.py +++ b/todotxt_machine/test/terminal_operations_test.py @@ -3,30 +3,36 @@ import pytest from .. import terminal_operations + @pytest.fixture def term(): - return terminal_operations.TerminalOperations(use_tput=True) + return terminal_operations.TerminalOperations(use_tput=True) + def test_terminal_operations_init(term): - assert type(term.rows) == int - assert type(term.columns) == int - assert term.rows > 0 - assert term.columns > 0 + assert type(term.rows) == int + assert type(term.columns) == int + assert term.rows > 0 + assert term.columns > 0 + def test_terminal_operations_foreground_color(term): - for i in range(0, 256): - assert term.foreground_color(i) == "\x1B[38;5;{}m".format(i) + for i in range(0, 256): + assert term.foreground_color(i) == "\x1B[38;5;{}m".format(i) + def test_terminal_operations_move_cursor(term, capsys): - for i in range(0, 5): - term.move_cursor(i, i) - term.output("X") - out, error = capsys.readouterr() - assert out == "\x1B[0;0HX\x1B[1;1HX\x1B[2;2HX\x1B[3;3HX\x1B[4;4HX" + for i in range(0, 5): + term.move_cursor(i, i) + term.output("X") + out, error = capsys.readouterr() + assert out == "\x1B[0;0HX\x1B[1;1HX\x1B[2;2HX\x1B[3;3HX\x1B[4;4HX" + def test_terminal_operations_length_ignoring_escapes(term): assert len("(A) 2013-10-25 This is a +Very @cool test") == term.length_ignoring_escapes("\x1b[m(A) \x1b[38;5;2m2013-10-25\x1b[38;5;13m This is a \x1b[38;5;4m+Very\x1b[38;5;13m \x1b[38;5;1m@cool\x1b[38;5;13m test") + def test_terminal_operations_ljust_with_escapes(term): test_string = "(A) 2013-10-25 This is a +Very @cool test" test_string_with_escapes = "\x1b[m(A) \x1b[38;5;2m2013-10-25\x1b[38;5;13m This is a \x1b[38;5;4m+Very\x1b[38;5;13m \x1b[38;5;1m@cool\x1b[38;5;13m test" diff --git a/todotxt_machine/test/todo_test.py b/todotxt_machine/test/todo_test.py index 2e2e070..745a4c1 100644 --- a/todotxt_machine/test/todo_test.py +++ b/todotxt_machine/test/todo_test.py @@ -7,6 +7,7 @@ import pprint pp = pprint.PrettyPrinter(indent=4).pprint + @pytest.fixture def todos(): return todo.Todos([ @@ -14,103 +15,116 @@ def todos(): "(B) Schedule Goodwill pickup +GarageSale @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ], './todo.txt', './archive.txt') + "x 2013-10-01 @GroceryStore Eskimo pies"], './todo.txt', './archive.txt') + @pytest.fixture def today(): return date.today() + def test_todos_init(todos): assert len(todos) == 5 assert len(todos.todo_items) == 5 + def test_todos_parse_entries(todos): todo = todos.todo_items[0] - assert todo.raw == "(A) Thank Mom for the dinner @phone" + assert todo.raw == "(A) Thank Mom for the dinner @phone" assert todo.contexts == ["@phone"] assert todo.projects == [] assert todo.priority == "A" todo = todos.todo_items[1] - assert todo.raw == "(B) Schedule Goodwill pickup +GarageSale @phone" + assert todo.raw == "(B) Schedule Goodwill pickup +GarageSale @phone" assert todo.contexts == ["@phone"] assert todo.projects == ["+GarageSale"] assert todo.priority == "B" todo = todos.todo_items[2] - assert todo.raw == "Unpack the guest bedroom +Unpacking due:2013-10-20" + assert todo.raw == "Unpack the guest bedroom +Unpacking due:2013-10-20" assert todo.contexts == [] assert todo.projects == ["+Unpacking"] assert todo.due_date == "2013-10-20" todo = todos.todo_items[3] - assert todo.raw == "2013-10-19 Post signs around the neighborhood +GarageSale" - assert todo.contexts == [] - assert todo.projects == ["+GarageSale"] + assert todo.raw == "2013-10-19 Post signs around the neighborhood +GarageSale" + assert todo.contexts == [] + assert todo.projects == ["+GarageSale"] assert todo.creation_date == "2013-10-19" todo = todos.todo_items[4] - assert todo.raw == "x 2013-10-01 @GroceryStore Eskimo pies" - assert todo.contexts == ["@GroceryStore"] - assert todo.projects == [] + assert todo.raw == "x 2013-10-01 @GroceryStore Eskimo pies" + assert todo.contexts == ["@GroceryStore"] + assert todo.projects == [] assert todo.completed_date == "2013-10-01" + def test_todos_iterable(todos): for todo in todos: assert todo.raw != "" for todo in todos: assert todo.raw != "" + def test_todos_contexts(todos): assert "@phone" in todos.contexts("(A) Thank Mom for the meatballs @phone") assert ["@home", "@phone"] == todos.contexts("Make phonecalls from home @phone @home") + def test_todos_projects(todos): assert "+GarageSale" in todos.projects("(B) Schedule Goodwill pickup +GarageSale @phone") assert ["+deck", "+portch"] == todos.projects("Finish outdoor projects +portch +deck") + def test_context_project_regex(todos): todos.update([ "(A) 1999-12-24 Thank Mom for the dinner @phone @email mom@email.com", "(B) Schedule Goodwill pickup +GarageSale NotA+Project @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ]) + "x 2013-10-01 @GroceryStore Eskimo pies"]) todo = todos[0] - assert todo.raw == "(A) 1999-12-24 Thank Mom for the dinner @phone @email mom@email.com" + assert todo.raw == "(A) 1999-12-24 Thank Mom for the dinner @phone @email mom@email.com" assert todo.contexts == ["@email", "@phone"] assert todo.projects == [] assert todo.priority == "A" todo = todos[1] - assert todo.raw == "(B) Schedule Goodwill pickup +GarageSale NotA+Project @phone" + assert todo.raw == "(B) Schedule Goodwill pickup +GarageSale NotA+Project @phone" assert todo.contexts == ["@phone"] assert todo.projects == ["+GarageSale"] assert todo.priority == "B" + def test_todos_all_contexts(todos): assert ["@GroceryStore", "@phone"] == todos.all_contexts() + def test_todos_all_projects(todos): assert ["+GarageSale", "+Unpacking"] == todos.all_projects() + def test_todos_completed_date(todos): - assert todos.completed_date("2011-03-02 Document +TodoTxt task format") == "" - assert todos.completed_date("(A) 2011-03-02 Document +TodoTxt task format") == "" - assert todos.completed_date("x 2012-03-03 2011-03-02 Document +TodoTxt task format") == "2012-03-03" + assert todos.completed_date("2011-03-02 Document +TodoTxt task format") == "" + assert todos.completed_date("(A) 2011-03-02 Document +TodoTxt task format") == "" + assert todos.completed_date("x 2012-03-03 2011-03-02 Document +TodoTxt task format") == "2012-03-03" assert todos.completed_date("x 2012-03-03 (A) 2011-03-02 Document +TodoTxt task format") == "2012-03-03" + def test_todos_creation_date(todos): - assert todos.creation_date("2011-03-02 Document +TodoTxt task format") == "2011-03-02" - assert todos.creation_date("(A) 2011-03-02 Document +TodoTxt task format") == "2011-03-02" - assert todos.creation_date("x 2012-03-03 2011-03-02 Document +TodoTxt task format") == "2011-03-02" + assert todos.creation_date("2011-03-02 Document +TodoTxt task format") == "2011-03-02" + assert todos.creation_date("(A) 2011-03-02 Document +TodoTxt task format") == "2011-03-02" + assert todos.creation_date("x 2012-03-03 2011-03-02 Document +TodoTxt task format") == "2011-03-02" assert todos.creation_date("x 2012-03-03 (A) 2011-03-02 Document +TodoTxt task format") == "2011-03-02" + def test_todos_due_date(todos): assert todos.due_date("2011-03-02 Document +TodoTxt task format due:2013-10-25") == "2013-10-25" assert todos.due_date("2011-03-02 due:2013-10-25 Document +TodoTxt task format") == "2013-10-25" # with pytest.raises(todo.NoDueDateError): assert todos.due_date("2011-03-02 Document +TodoTxt task format") == "" + def test_todos_priority(todos): assert todos.priority("(A) Priority A") == "A" assert todos.priority("(Z) Priority Z") == "Z" @@ -120,13 +134,14 @@ def test_todos_priority(todos): assert todos.priority("(A)No Priority") == "" assert todos.priority("(A)->No Priority") == "" + def test_todos_sorted(todos): todos.parse_raw_entries([ "(B) Schedule Goodwill pickup +GarageSale @phone", "(A) Thank Mom for the dinner @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ]) + "x 2013-10-01 @GroceryStore Eskimo pies"]) assert [todo.raw_index for todo in todos.todo_items] == [0, 1, 2, 3, 4] todos.sorted() @@ -135,7 +150,7 @@ def test_todos_sorted(todos): "(B) Schedule Goodwill pickup +GarageSale @phone", "2013-10-19 Post signs around the neighborhood +GarageSale", "Unpack the guest bedroom +Unpacking due:2013-10-20", - "x 2013-10-01 @GroceryStore Eskimo pies" ] + "x 2013-10-01 @GroceryStore Eskimo pies"] assert [todo.raw_index for todo in todos.todo_items] == [1, 0, 3, 2, 4] todos.sorted_raw() @@ -144,9 +159,10 @@ def test_todos_sorted(todos): "(A) Thank Mom for the dinner @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ] + "x 2013-10-01 @GroceryStore Eskimo pies"] assert [todo.raw_index for todo in todos.todo_items] == [0, 1, 2, 3, 4] + def test_todos_sorted_reverese(todos): todos.sorted_reverse() assert [todo.raw for todo in todos.todo_items] == [ @@ -154,30 +170,35 @@ def test_todos_sorted_reverese(todos): "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", "(B) Schedule Goodwill pickup +GarageSale @phone", - "(A) Thank Mom for the dinner @phone" ] + "(A) Thank Mom for the dinner @phone"] assert [todo.raw_index for todo in todos.todo_items] == [4, 2, 3, 1, 0] + def test_todos_filter_context(todos): assert [t.raw for t in todos.filter_context("@phone")] == [ "(A) Thank Mom for the dinner @phone", "(B) Schedule Goodwill pickup +GarageSale @phone"] assert [t.raw for t in todos.filter_context("@GroceryStore")] == [ - "x 2013-10-01 @GroceryStore Eskimo pies" ] + "x 2013-10-01 @GroceryStore Eskimo pies"] + def test_todos_filter_project(todos): assert [t.raw for t in todos.filter_project("+GarageSale")] == [ "(B) Schedule Goodwill pickup +GarageSale @phone", - "2013-10-19 Post signs around the neighborhood +GarageSale" ] + "2013-10-19 Post signs around the neighborhood +GarageSale"] assert [t.raw for t in todos.filter_project("+Unpacking")] == [ - "Unpack the guest bedroom +Unpacking due:2013-10-20" ] + "Unpack the guest bedroom +Unpacking due:2013-10-20"] + def test_todo_highlight(todos): todos.parse_raw_entries(["2013-10-25 This is a +Very @cool test"]) assert todos.todo_items[0].colored == ('plain', ['', ('creation_date', '2013-10-25'), ' This is a ', ('project', '+Very'), ' ', ('context', '@cool'), ' test']) + def test_todos_filter_context_and_project(todos): assert [t.raw for t in todos.filter_context_and_project("@phone", "+GarageSale")] == [ - "(B) Schedule Goodwill pickup +GarageSale @phone" ] + "(B) Schedule Goodwill pickup +GarageSale @phone"] + def test_todos_update(todos): todos.update([ @@ -185,25 +206,26 @@ def test_todos_update(todos): "(B) Schedule Goodwill pickup +GarageSale @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ]) + "x 2013-10-01 @GroceryStore Eskimo pies"]) assert [t.raw for t in todos] == [ "(A) 1999-12-24 Thank Mom for the dinner @phone", "(B) Schedule Goodwill pickup +GarageSale @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ] + "x 2013-10-01 @GroceryStore Eskimo pies"] todos.update([ "x 1999-12-25 (A) 1999-12-24 Thank Mom for the dinner @phone" "x 1999-11-10 (B) Schedule Goodwill pickup +GarageSale @phone" "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ]) + "x 2013-10-01 @GroceryStore Eskimo pies"]) assert [t.raw for t in todos] == [ "x 1999-12-25 (A) 1999-12-24 Thank Mom for the dinner @phone" "x 1999-11-10 (B) Schedule Goodwill pickup +GarageSale @phone" "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ] + "x 2013-10-01 @GroceryStore Eskimo pies"] + def test_todos_complete(todos): today = date.today() @@ -212,7 +234,7 @@ def test_todos_complete(todos): "(B) Schedule Goodwill pickup +GarageSale @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ]) + "x 2013-10-01 @GroceryStore Eskimo pies"]) todos[0].complete() todos[1].complete() assert [t.raw for t in todos] == [ @@ -220,20 +242,20 @@ def test_todos_complete(todos): "x {} (B) Schedule Goodwill pickup +GarageSale @phone".format(today), "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ] + "x 2013-10-01 @GroceryStore Eskimo pies"] assert [t.creation_date for t in todos] == [ "1999-12-24", "", "", "2013-10-19", - "" ] + ""] assert [t.completed_date for t in todos] == [ "{}".format(today), "{}".format(today), "", "", - "2013-10-01" ] - assert [t.is_complete() for t in todos] == [ True, True, False, False, True ] + "2013-10-01"] + assert [t.is_complete() for t in todos] == [True, True, False, False, True] todos[1].incomplete() assert todos[1].raw == "(B) Schedule Goodwill pickup +GarageSale @phone" assert todos[1].completed_date == "" @@ -244,13 +266,14 @@ def test_todos_complete(todos): assert todos[3].creation_date == "2013-10-19" assert todos[3].completed_date == "{}".format(today) + def test_todo_incomplete(todos): todos.update([ "x 1999-12-25 (A) 1999-12-24 Thank Mom for the dinner @phone", "x 1999-11-10 (B) Schedule Goodwill pickup +GarageSale @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ]) + "x 2013-10-01 @GroceryStore Eskimo pies"]) assert todos[1].creation_date == "" assert todos[1].completed_date == "1999-11-10" todos[1].incomplete() @@ -263,59 +286,62 @@ def test_todo_incomplete(todos): assert todos[0].creation_date == "1999-12-24" assert todos[0].completed_date == "" + def test_todo_is_complete(todos): todos.update([ "x 1999-12-25 (A) 1999-12-24 Thank Mom for the dinner @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x @GroceryStore Eskimo pies" ]) + "x @GroceryStore Eskimo pies"]) assert [t.is_complete() for t in todos] == [ True, False, False, - True ] + True] + def test_todo_update(todos): t = todos[0] t.update("(A) 2000-20-12 Thank Mom for the dinner @phone @home +GiveThanks") - assert t.raw == "(A) 2000-20-12 Thank Mom for the dinner @phone @home +GiveThanks" - assert t.priority == "A" - assert t.contexts == ["@home", "@phone"] - assert t.projects == ["+GiveThanks"] - assert t.creation_date == "2000-20-12" - assert t.due_date == "" + assert t.raw == "(A) 2000-20-12 Thank Mom for the dinner @phone @home +GiveThanks" + assert t.priority == "A" + assert t.contexts == ["@home", "@phone"] + assert t.projects == ["+GiveThanks"] + assert t.creation_date == "2000-20-12" + assert t.due_date == "" assert t.completed_date == "" - assert t.is_complete() == False + assert t.is_complete() == False t = todos[1] - assert t.raw == "(B) Schedule Goodwill pickup +GarageSale @phone" + assert t.raw == "(B) Schedule Goodwill pickup +GarageSale @phone" assert t.contexts == ["@phone"] assert t.projects == ["+GarageSale"] assert t.priority == "B" t = todos[2] t.update("x 2013-10-25 Unpack the guest bedroom +Unpacking due:2013-10-20") - assert t.raw == "x 2013-10-25 Unpack the guest bedroom +Unpacking due:2013-10-20" - assert t.priority == "" - assert t.contexts == [] - assert t.projects == ["+Unpacking"] - assert t.creation_date == "" - assert t.due_date == "2013-10-20" + assert t.raw == "x 2013-10-25 Unpack the guest bedroom +Unpacking due:2013-10-20" + assert t.priority == "" + assert t.contexts == [] + assert t.projects == ["+Unpacking"] + assert t.creation_date == "" + assert t.due_date == "2013-10-20" assert t.completed_date == "2013-10-25" - assert t.is_complete() == True + assert t.is_complete() == True t = todos[3] - assert t.raw == "2013-10-19 Post signs around the neighborhood +GarageSale" - assert t.contexts == [] - assert t.projects == ["+GarageSale"] + assert t.raw == "2013-10-19 Post signs around the neighborhood +GarageSale" + assert t.contexts == [] + assert t.projects == ["+GarageSale"] assert t.creation_date == "2013-10-19" t = todos[4] - assert t.raw == "x 2013-10-01 @GroceryStore Eskimo pies" - assert t.contexts == ["@GroceryStore"] - assert t.projects == [] + assert t.raw == "x 2013-10-01 @GroceryStore Eskimo pies" + assert t.contexts == ["@GroceryStore"] + assert t.projects == [] assert t.completed_date == "2013-10-01" + def test_todo_add_creation_date(todos, today): todos[2].add_creation_date() assert todos[2].raw == "{} Unpack the guest bedroom +Unpacking due:2013-10-20".format(today) @@ -325,6 +351,7 @@ def test_todo_add_creation_date(todos, today): assert todos[3].raw == "2013-10-19 Post signs around the neighborhood +GarageSale" assert todos[3].creation_date == "2013-10-19".format(today) + def test_todos_append(todos, today): todos.append("THIS IS A TEST @testing") todos.append("THIS IS A TEST @testing", add_creation_date=False) @@ -338,6 +365,7 @@ def test_todos_append(todos, today): "THIS IS A TEST @testing".format(today)] assert [todo.raw_index for todo in todos.todo_items] == [0, 1, 2, 3, 4, 5, 6] + def test_todos_delete(todos): todos.delete(0) assert [t.raw for t in todos] == [ @@ -353,6 +381,7 @@ def test_todos_delete(todos): "2013-10-19 Post signs around the neighborhood +GarageSale"] assert [todo.raw_index for todo in todos.todo_items] == [0, 1, 2] + def test_todos_insert(todos, today): todos.insert(1, "THIS IS A TEST @testing") todos.insert(1, "(B) THIS IS ANOTHER TEST @testing") @@ -369,35 +398,37 @@ def test_todos_insert(todos, today): ] assert [todo.raw_index for todo in todos.todo_items] == [0, 1, 2, 3, 4, 5, 6, 7] + def test_todos_search(todos): assert [t.raw for t in todos.search("the")] == [ "(A) Thank Mom for the dinner @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale"] # one match per line! - assert [t.search_matches for t in todos.search("the")] == [ ('the',), ('the',), ('the',) ] + assert [t.search_matches for t in todos.search("the")] == [('the',), ('the',), ('the',)] assert [t.raw for t in todos.search("te")] == [ "(A) Thank Mom for the dinner @phone", "Unpack the guest bedroom +Unpacking due:2013-10-20", "2013-10-19 Post signs around the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ] - assert [t.search_matches for t in todos.search("te")] == [ ('the',), ('t be',), ('the',), ('tore',) ] + "x 2013-10-01 @GroceryStore Eskimo pies"] + assert [t.search_matches for t in todos.search("te")] == [('the',), ('t be',), ('the',), ('tore',)] - assert todos.search(".*") == [ ] - assert todos.search("{b}") == [ ] + assert todos.search(".*") == [] + assert todos.search("{b}") == [] todos.update([ "(A) 1999-12-24 .Thank* Mom for the .dinner* @phone", "(B) Schedule Goodwill pickup +GarageSale @phone", "Unpack the guest {bedroom} +Unpacking due:2013-10-20", "2013-10-19 Post signs (around) the neighborhood +GarageSale", - "x 2013-10-01 @GroceryStore Eskimo pies" ]) + "x 2013-10-01 @GroceryStore Eskimo pies"]) - assert [t.raw for t in todos.search(".*")] == [ "(A) 1999-12-24 .Thank* Mom for the .dinner* @phone" ] - assert [t.search_matches for t in todos.search(".*")] == [('.dinner*',)] - assert [t.raw for t in todos.search("{b}")] == [ "Unpack the guest {bedroom} +Unpacking due:2013-10-20" ] + assert [t.raw for t in todos.search(".*")] == ["(A) 1999-12-24 .Thank* Mom for the .dinner* @phone"] + assert [t.search_matches for t in todos.search(".*")] == [('.dinner*',)] + assert [t.raw for t in todos.search("{b}")] == ["Unpack the guest {bedroom} +Unpacking due:2013-10-20"] assert [t.search_matches for t in todos.search("{b}")] == [('{bedroom}',)] + def test_todos_swap(todos): todos.swap(0, 1) assert [todo.raw_index for todo in todos.todo_items] == [1, 0, 2, 3, 4] diff --git a/todotxt_machine/todo.py b/todotxt_machine/todo.py index a3d2d22..3a66736 100644 --- a/todotxt_machine/todo.py +++ b/todotxt_machine/todo.py @@ -7,45 +7,46 @@ from todotxt_machine.terminal_operations import TerminalOperations + class Todo: """Single Todo item""" _priority_regex = re.compile(r'\(([A-Z])\) ') def __init__(self, item, index, - colored="", priority="", contexts=[], projects=[], - creation_date="", due_date="", completed_date=""): - self.raw = item.strip() - self.raw_index = index - self.creation_date = creation_date - self.priority = priority - self.contexts = contexts - self.projects = projects - self.due_date = due_date + colored="", priority="", contexts=[], projects=[], + creation_date="", due_date="", completed_date=""): + self.raw = item.strip() + self.raw_index = index + self.creation_date = creation_date + self.priority = priority + self.contexts = contexts + self.projects = projects + self.due_date = due_date self.completed_date = completed_date - self.colored = self.highlight() + self.colored = self.highlight() # self.colored_length = TerminalOperations.length_ignoring_escapes(self.colored) def update(self, item): - self.raw = item.strip() - self.priority = Todos.priority(item) - self.contexts = Todos.contexts(item) - self.projects = Todos.projects(item) - self.creation_date = Todos.creation_date(item) - self.due_date = Todos.due_date(item) + self.raw = item.strip() + self.priority = Todos.priority(item) + self.contexts = Todos.contexts(item) + self.projects = Todos.projects(item) + self.creation_date = Todos.creation_date(item) + self.due_date = Todos.due_date(item) self.completed_date = Todos.completed_date(item) - self.colored = self.highlight() + self.colored = self.highlight() # self.colored_length = TerminalOperations.length_ignoring_escapes(self.colored) def __repr__(self): return repr({ - "raw": self.raw, - "colored": self.colored, - "raw_index": self.raw_index, - "priority": self.priority, - "contexts": self.contexts, - "projects": self.projects, - "creation_date": self.creation_date, - "due_date": self.due_date, + "raw": self.raw, + "colored": self.colored, + "raw_index": self.raw_index, + "priority": self.priority, + "contexts": self.contexts, + "projects": self.projects, + "creation_date": self.creation_date, + "due_date": self.due_date, "completed_date": self.completed_date }) @@ -58,7 +59,7 @@ def highlight(self, line="", show_due_date=True, show_contexts=True, show_projec else: words_to_be_highlighted = self.contexts + self.projects if self.due_date: - words_to_be_highlighted.append("due:"+self.due_date) + words_to_be_highlighted.append("due:" + self.due_date) if self.creation_date: words_to_be_highlighted.append(self.creation_date) @@ -69,7 +70,7 @@ def highlight(self, line="", show_due_date=True, show_contexts=True, show_projec color_list[index] = ('context', w) if show_contexts else '' elif w in self.projects: color_list[index] = ('project', w) if show_projects else '' - elif w == "due:"+self.due_date: + elif w == "due:" + self.due_date: color_list[index] = ('due_date', w) if show_due_date else '' elif w == self.creation_date: color_list[index] = ('creation_date', w) @@ -131,15 +132,15 @@ def add_creation_date(self): class Todos: """Todo items""" - _context_regex = re.compile(r'(?:^|\s+)(@\S+)') - _project_regex = re.compile(r'(?:^|\s+)(\+\S+)') + _context_regex = re.compile(r'(?:^|\s+)(@\S+)') + _project_regex = re.compile(r'(?:^|\s+)(\+\S+)') _creation_date_regex = re.compile(r'^' r'(?:x \d\d\d\d-\d\d-\d\d )?' r'(?:\(\w\) )?' r'(\d\d\d\d-\d\d-\d\d)\s*') - _due_date_regex = re.compile(r'\s*due:(\d\d\d\d-\d\d-\d\d)\s*') - _priority_regex = re.compile(r'\(([A-Z])\) ') - _completed_regex = re.compile(r'^x (\d\d\d\d-\d\d-\d\d) ') + _due_date_regex = re.compile(r'\s*due:(\d\d\d\d-\d\d-\d\d)\s*') + _priority_regex = re.compile(r'\(([A-Z])\) ') + _completed_regex = re.compile(r'^x (\d\d\d\d-\d\d-\d\d) ') def __init__(self, todo_items, file_path, archive_path): self.file_path = file_path @@ -173,10 +174,10 @@ def update(self, todo_items): def append(self, item, add_creation_date=True): self.insert(len(self.todo_items), item, add_creation_date) - return len(self.todo_items)-1 + return len(self.todo_items) - 1 def insert(self, index, item, add_creation_date=True): - self.todo_items.insert(index, self.create_todo(item, index) ) + self.todo_items.insert(index, self.create_todo(item, index)) self.update_raw_indices() newtodo = self.todo_items[index] if add_creation_date and newtodo.creation_date == "": @@ -222,16 +223,16 @@ def __getitem__(self, index): return self.todo_items[index] def __repr__(self): - return repr( [i for i in self.todo_items] ) + return repr([i for i in self.todo_items]) def create_todo(self, todo, index): return Todo(todo, index, - contexts = Todos.contexts(todo), - projects = Todos.projects(todo), - priority = Todos.priority(todo), - creation_date = Todos.creation_date(todo), - due_date = Todos.due_date(todo), - completed_date = Todos.completed_date(todo)) + contexts=Todos.contexts(todo), + projects=Todos.projects(todo), + priority=Todos.priority(todo), + creation_date=Todos.creation_date(todo), + due_date=Todos.due_date(todo), + completed_date=Todos.completed_date(todo)) def parse_raw_entries(self, raw_items): self.todo_items = [ @@ -244,11 +245,11 @@ def update_raw_indices(self): @staticmethod def contexts(item): - return sorted( Todos._context_regex.findall(item) ) + return sorted(Todos._context_regex.findall(item)) @staticmethod def projects(item): - return sorted( Todos._project_regex.findall(item) ) + return sorted(Todos._project_regex.findall(item)) @staticmethod def creation_date(item): @@ -285,7 +286,7 @@ def all_contexts(self): # Join all items and use one regex.findall # return sorted(set( Todos._context_regex.findall(" ".join(self.raw_items)))) - return sorted(set( [context for todo in self.todo_items for context in todo.contexts] )) + return sorted(set([context for todo in self.todo_items for context in todo.contexts])) def all_projects(self): # List comprehension @@ -294,16 +295,16 @@ def all_projects(self): # Join all items and use one regex.findall # return sorted(set( Todos._project_regex.findall(" ".join(self.raw_items)))) - return sorted(set([project for todo in self.todo_items for project in todo.projects] )) + return sorted(set([project for todo in self.todo_items for project in todo.projects])) def sorted(self, reversed_sort=False): - self.todo_items.sort( key=lambda todo: todo.raw, reverse=reversed_sort ) + self.todo_items.sort(key=lambda todo: todo.raw, reverse=reversed_sort) def sorted_reverse(self): self.sorted(reversed_sort=True) def sorted_raw(self): - self.todo_items.sort( key=lambda todo: todo.raw_index ) + self.todo_items.sort(key=lambda todo: todo.raw_index) def swap(self, first, second): """ @@ -344,11 +345,11 @@ def search(self, search_string): for index, substring in enumerate(substrings): s = ".*?".join(substring) # s.replace(" .*?", " ") - if 0 < index < len(substrings)-1: + if 0 < index < len(substrings) - 1: s += ".*?" ss.append(s) # print(repr(ss)) - search_string_regex = '^.*(' + search_string_regex = '^.*(' search_string_regex += "\\".join(ss) search_string_regex += ').*' # print(search_string_regex) @@ -514,4 +515,4 @@ def search(self, search_string): @staticmethod def quote(): - return Todos.quotes[ random.randrange(len(Todos.quotes)) ] + return Todos.quotes[random.randrange(len(Todos.quotes))] diff --git a/todotxt_machine/urwid_ui.py b/todotxt_machine/urwid_ui.py index c0edb9c..62db6da 100644 --- a/todotxt_machine/urwid_ui.py +++ b/todotxt_machine/urwid_ui.py @@ -5,6 +5,8 @@ import collections # Modified from http://wiki.goffi.org/wiki/Urwid-satext/en + + class AdvancedEdit(urwid.Edit): """Edit box with some custom improvments new chars: @@ -34,9 +36,9 @@ def setCompletionMethod(self, callback): def keypress(self, size, key): # import ipdb; ipdb.set_trace() if self.key_bindings.is_binded_to(key, 'edit-home'): - self.set_edit_pos(0) # move to the beginning of the line + self.set_edit_pos(0) # move to the beginning of the line elif self.key_bindings.is_binded_to(key, 'edit-end'): - self.set_edit_pos(len(self.edit_text) - 1) # move to the end of the line + self.set_edit_pos(len(self.edit_text) - 1) # move to the end of the line elif self.key_bindings.is_binded_to(key, 'edit-delete-end'): self.parent_ui.yanked_text = self.edit_text[self.edit_pos:] self._delete_highlighted() @@ -49,7 +51,7 @@ def keypress(self, size, key): self.set_edit_pos(self.edit_pos + len(self.parent_ui.yanked_text)) elif self.key_bindings.is_binded_to(key, 'edit-delete-word'): before = self.edit_text[:self.edit_pos] - pos = before.rstrip().rfind(" ")+1 + pos = before.rstrip().rfind(" ") + 1 self.parent_ui.yanked_text = self.edit_text[pos:self.edit_pos] self.set_edit_text(before[:pos] + self.edit_text[self.edit_pos:]) self.set_edit_pos(pos) @@ -60,34 +62,36 @@ def keypress(self, size, key): self.set_edit_pos(0) elif self.key_bindings.is_binded_to(key, 'edit-word-left'): before = self.edit_text[:self.edit_pos] - pos = before.rstrip().rfind(" ")+1 + pos = before.rstrip().rfind(" ") + 1 self.set_edit_pos(pos) elif self.key_bindings.is_binded_to(key, 'edit-word-right'): after = self.edit_text[self.edit_pos:] - pos = after.rstrip().find(" ")+1 - self.set_edit_pos(self.edit_pos+pos) + pos = after.rstrip().find(" ") + 1 + self.set_edit_pos(self.edit_pos + pos) elif self.key_bindings.is_binded_to(key, 'edit-complete'): try: before = self.edit_text[:self.edit_pos] if self.completion_data: if (not self.completion_data['completed'] - or self.completion_data['position'] != self.edit_pos - or not before.endswith(self.completion_data['completed'])): + or self.completion_data['position'] != self.edit_pos + or not before.endswith(self.completion_data['completed'])): self.completion_data.clear() else: before = before[:-len(self.completion_data['completed'])] complet = self.completion_cb(before, self.completion_data) self.completion_data['completed'] = complet[len(before):] - self.set_edit_text(complet+self.edit_text[self.edit_pos:]) + self.set_edit_text(complet + self.edit_text[self.edit_pos:]) self.set_edit_pos(len(complet)) self.completion_data['position'] = self.edit_pos return except AttributeError: - #No completion method defined + # No completion method defined pass return super(AdvancedEdit, self).keypress(size, key) + class SearchWidget(urwid.Edit): + def __init__(self, parent_ui, key_bindings, edit_text=""): self.parent_ui = parent_ui self.key_bindings = key_bindings @@ -98,16 +102,18 @@ def keypress(self, size, key): self.parent_ui.finalize_search() return super(SearchWidget, self).keypress(size, key) + class TodoWidget(urwid.Button): + def __init__(self, todo, key_bindings, colorscheme, parent_ui, editing=False, wrapping='clip', border='no border'): super(TodoWidget, self).__init__("") - self.todo = todo + self.todo = todo self.key_bindings = key_bindings - self.wrapping = wrapping - self.border = border + self.wrapping = wrapping + self.border = border self.colorscheme = colorscheme - self.parent_ui = parent_ui - self.editing = editing + self.parent_ui = parent_ui + self.editing = editing # urwid.connect_signal(self, 'click', callback) if editing: self.edit_item() @@ -127,19 +133,19 @@ def update_todo(self): text = urwid.Text(self.todo.colored, wrap=self.wrapping) if self.border == 'bordered': - lt='' + lt = '' if self.todo.due_date: lt = ('due_date', "due:{0}".format(self.todo.due_date)) t = [] - t.append( ('context', ' '.join(self.todo.contexts)) ) + t.append(('context', ' '.join(self.todo.contexts))) if self.todo.contexts and self.todo.projects: t.append(' ') - t.append( ('project', ' '.join(self.todo.projects)) ) + t.append(('project', ' '.join(self.todo.projects))) bc = 'plain' if self.todo.priority and self.todo.priority in "ABCDEF": bc = "priority_{0}".format(self.todo.priority.lower()) text = TodoLineBox(text, top_left_title=lt, bottom_right_title=t, border_color=bc, ) - self._w = urwid.AttrMap( urwid.AttrMap( + self._w = urwid.AttrMap(urwid.AttrMap( text, None, 'selected'), None, self.colorscheme.focus_map) @@ -152,18 +158,18 @@ def edit_item(self): def completions(self, text, completion_data={}): space = text.rfind(" ") - start = text[space+1:] + start = text[space + 1:] words = self.parent_ui.todos.all_contexts() + self.parent_ui.todos.all_projects() try: - start_idx=words.index(completion_data['last_word'])+1 + start_idx = words.index(completion_data['last_word']) + 1 if start_idx == len(words): start_idx = 0 - except (KeyError,ValueError): + except (KeyError, ValueError): start_idx = 0 - for idx in list(range(start_idx,len(words))) + list(range(0,start_idx)): + for idx in list(range(start_idx, len(words))) + list(range(0, start_idx)): if words[idx].lower().startswith(start.lower()): completion_data['last_word'] = words[idx] - return text[:space+1] + words[idx] + (': ' if space < 0 else '') + return text[:space + 1] + words[idx] + (': ' if space < 0 else '') return text def save_item(self): @@ -176,7 +182,7 @@ def save_item(self): def keypress(self, size, key): if self.editing: if key in ['down', 'up']: - return None # don't pass up or down to the ListBox + return None # don't pass up or down to the ListBox elif self.key_bindings.is_binded_to(key, 'save-item'): self.save_item() return key @@ -189,6 +195,7 @@ def keypress(self, size, key): else: return key + class TodoLineBox(urwid.WidgetDecoration, urwid.WidgetWrap): def __init__(self, original_widget, top_left_title="", bottom_right_title="", border_color='plain', @@ -213,7 +220,7 @@ def __init__(self, original_widget, top_left_title="", bottom_right_title="", bo """ - tline, bline = urwid.AttrMap(urwid.Divider(tline), border_color), urwid.AttrMap(urwid.Divider(bline), border_color) + tline, bline = urwid.AttrMap(urwid.Divider(tline), border_color), urwid.AttrMap(urwid.Divider(bline), border_color) lline, rline = urwid.AttrMap(urwid.SolidFill(lline), border_color), urwid.AttrMap(urwid.SolidFill(rline), border_color) tlcorner, trcorner = urwid.AttrMap(urwid.Text(tlcorner), border_color), urwid.AttrMap(urwid.Text(trcorner), border_color) blcorner, brcorner = urwid.AttrMap(urwid.Text(blcorner), border_color), urwid.AttrMap(urwid.Text(brcorner), border_color) @@ -256,6 +263,7 @@ def __init__(self, original_widget, top_left_title="", bottom_right_title="", bo class ViPile(urwid.Pile): + def __init__(self, key_bindings, widget_list, focus_item=None): """Pile with Vi-like navigation.""" super(ViPile, self).__init__(widget_list, focus_item) @@ -273,6 +281,7 @@ def __init__(self, key_bindings, widget_list, focus_item=None): class ViColumns(urwid.Columns): + def __init__(self, key_bindings, widget_list, dividechars=0, focus_column=None, min_width=1, box_columns=None): super(ViColumns, self).__init__(widget_list, dividechars, focus_column, min_width, box_columns) command_map = urwid.command_map.copy() @@ -286,7 +295,9 @@ def __init__(self, key_bindings, widget_list, dividechars=0, focus_column=None, self._command_map = command_map + class ViListBox(urwid.ListBox): + def __init__(self, key_bindings, *args, **kwargs): super(ViListBox, self).__init__(*args, **kwargs) command_map = urwid.command_map.copy() @@ -300,32 +311,34 @@ def __init__(self, key_bindings, *args, **kwargs): self._command_map = command_map + class UrwidUI: + def __init__(self, todos, key_bindings, colorscheme): - self.wrapping = collections.deque(['clip', 'space']) - self.border = collections.deque(['no border', 'bordered']) - self.sorting = collections.deque(["Unsorted", "Descending", "Ascending"]) + self.wrapping = collections.deque(['clip', 'space']) + self.border = collections.deque(['no border', 'bordered']) + self.sorting = collections.deque(["Unsorted", "Descending", "Ascending"]) self.sorting_display = {"Unsorted": "-", "Descending": "v", "Ascending": "^"} - self.todos = todos + self.todos = todos self.key_bindings = key_bindings self.colorscheme = colorscheme - self.palette = [ (key, '', '', '', value['fg'], value['bg']) for key, value in self.colorscheme.colors.items() ] + self.palette = [(key, '', '', '', value['fg'], value['bg']) for key, value in self.colorscheme.colors.items()] self.active_projects = [] self.active_contexts = [] - self.toolbar_is_open = False - self.help_panel_is_open = False - self.filter_panel_is_open = False - self.filtering = False - self.searching = False - self.search_string = '' - self.yanked_text = '' + self.toolbar_is_open = False + self.help_panel_is_open = False + self.filter_panel_is_open = False + self.filtering = False + self.searching = False + self.search_string = '' + self.yanked_text = '' def visible_lines(self): - lines = self.loop.screen_size[1] - 1 # minus one for the header + lines = self.loop.screen_size[1] - 1 # minus one for the header if self.toolbar_is_open: lines -= 1 if self.searching: @@ -342,7 +355,7 @@ def move_selection_top(self): self.listbox.set_focus(0) def move_selection_bottom(self): - self.listbox.set_focus(len(self.listbox.body)-1) + self.listbox.set_focus(len(self.listbox.body) - 1) def toggle_help_panel(self, button=None): if self.filter_panel_is_open: @@ -355,7 +368,7 @@ def toggle_help_panel(self, button=None): # header_column[0].set_wrap_mode('space') else: self.help_panel = self.create_help_panel() - self.view.contents.append( (self.help_panel, self.view.options(width_type='weight', width_amount=3)) ) + self.view.contents.append((self.help_panel, self.view.options(width_type='weight', width_amount=3))) self.view.set_focus(1) self.help_panel_is_open = True # set header line to clip contents @@ -383,7 +396,7 @@ def toggle_filter_panel(self, button=None): self.filter_panel_is_open = False else: self.filter_panel = self.create_filter_panel() - self.view.contents.append( (self.filter_panel, self.view.options(width_type='weight', width_amount=1)) ) + self.view.contents.append((self.filter_panel, self.view.options(width_type='weight', width_amount=1))) self.filter_panel_is_open = True def toggle_wrapping(self, checkbox=None, state=None): @@ -409,12 +422,12 @@ def toggle_toolbar(self): def swap_down(self): focus, focus_index = self.listbox.get_focus() if not self.filtering and not self.searching: - if focus_index+1 < len(self.listbox.body): + if focus_index + 1 < len(self.listbox.body): self.todos.swap(focus_index, focus_index + 1) self.listbox.body[focus_index].todo = self.todos[focus_index] - self.listbox.body[focus_index+1].todo = self.todos[focus_index+1] + self.listbox.body[focus_index + 1].todo = self.todos[focus_index + 1] self.listbox.body[focus_index].update_todo() - self.listbox.body[focus_index+1].update_todo() + self.listbox.body[focus_index + 1].update_todo() self.move_selection_down() def swap_up(self): @@ -423,9 +436,9 @@ def swap_up(self): if focus_index > 0: self.todos.swap(focus_index, focus_index - 1) self.listbox.body[focus_index].todo = self.todos[focus_index] - self.listbox.body[focus_index-1].todo = self.todos[focus_index-1] + self.listbox.body[focus_index - 1].todo = self.todos[focus_index - 1] self.listbox.body[focus_index].update_todo() - self.listbox.body[focus_index-1].update_todo() + self.listbox.body[focus_index - 1].update_todo() self.move_selection_up() def save_todos(self, button=None): @@ -445,7 +458,7 @@ def reload_todos_from_file(self, button=None): self.todos.reload_from_file() for t in self.todos.todo_items: - self.listbox.body.append( TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0]) ) + self.listbox.body.append(TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0])) self.update_header("Reloaded") @@ -580,7 +593,7 @@ def add_new_todo(self, position=False): self.listbox.body.append(TodoWidget(self.todos[new_index], self.key_bindings, self.colorscheme, self, editing=True, wrapping=self.wrapping[0], border=self.border[0])) else: if position is 'insert_after': - new_index = self.todos.insert(focus_index+1, '', add_creation_date=False) + new_index = self.todos.insert(focus_index + 1, '', add_creation_date=False) elif position is 'insert_before': new_index = self.todos.insert(focus_index, '', add_creation_date=False) @@ -588,7 +601,7 @@ def add_new_todo(self, position=False): if position: if self.filtering: - self.listbox.set_focus(len(self.listbox.body)-1) + self.listbox.set_focus(len(self.listbox.body) - 1) else: self.listbox.set_focus(new_index) # edit_widget = self.listbox.body[new_index]._w @@ -598,48 +611,48 @@ def add_new_todo(self, position=False): def create_header(self, message=""): return urwid.AttrMap( - urwid.Columns( [ - urwid.Text( [ + urwid.Columns([ + urwid.Text([ ('header_todo_count', "{0} Todos ".format(self.todos.__len__())), ('header_todo_pending_count', " {0} Pending ".format(self.todos.pending_items_count())), ('header_todo_done_count', " {0} Done ".format(self.todos.done_items_count())), ]), # urwid.Text( " todotxt-machine ", align='center' ), - urwid.Text( ('header_file', "{0} {1} ".format(message, self.todos.file_path)), align='right' ) + urwid.Text(('header_file', "{0} {1} ".format(message, self.todos.file_path)), align='right') ]), 'header') def create_toolbar(self): - return urwid.AttrMap(urwid.Columns( [ + return urwid.AttrMap(urwid.Columns([ urwid.Padding( - urwid.AttrMap( - urwid.CheckBox([('header_file', 'w'), 'ord wrap'], state=(self.wrapping[0] == 'space'), on_state_change=self.toggle_wrapping), - 'header', 'plain_selected'), right=2 ), + urwid.AttrMap( + urwid.CheckBox([('header_file', 'w'), 'ord wrap'], state=(self.wrapping[0] == 'space'), on_state_change=self.toggle_wrapping), + 'header', 'plain_selected'), right=2), urwid.Padding( - urwid.AttrMap( - urwid.CheckBox([('header_file', 'b'), 'orders'], state=(self.border[0] == 'bordered'), on_state_change=self.toggle_border), - 'header', 'plain_selected'), right=2 ), + urwid.AttrMap( + urwid.CheckBox([('header_file', 'b'), 'orders'], state=(self.border[0] == 'bordered'), on_state_change=self.toggle_border), + 'header', 'plain_selected'), right=2), urwid.Padding( - urwid.AttrMap( - urwid.Button([('header_file', 'R'), 'eload'], on_press=self.reload_todos_from_file), - 'header', 'plain_selected'), right=2 ), + urwid.AttrMap( + urwid.Button([('header_file', 'R'), 'eload'], on_press=self.reload_todos_from_file), + 'header', 'plain_selected'), right=2), urwid.Padding( - urwid.AttrMap( - urwid.Button([('header_file', 'S'), 'ave'], on_press=self.save_todos), - 'header', 'plain_selected'), right=2 ), + urwid.AttrMap( + urwid.Button([('header_file', 'S'), 'ave'], on_press=self.save_todos), + 'header', 'plain_selected'), right=2), urwid.Padding( - urwid.AttrMap( - urwid.Button([('header_file', 's'), 'ort: '+self.sorting_display[self.sorting[0]]], on_press=self.toggle_sorting), - 'header', 'plain_selected'), right=2 ), + urwid.AttrMap( + urwid.Button([('header_file', 's'), 'ort: ' + self.sorting_display[self.sorting[0]]], on_press=self.toggle_sorting), + 'header', 'plain_selected'), right=2), urwid.Padding( - urwid.AttrMap( - urwid.Button([('header_file', 'f'), 'ilter'], on_press=self.toggle_filter_panel), - 'header', 'plain_selected'), right=2 ) - ] ), 'header') + urwid.AttrMap( + urwid.Button([('header_file', 'f'), 'ilter'], on_press=self.toggle_filter_panel), + 'header', 'plain_selected'), right=2) + ]), 'header') def search_box_updated(self, edit_widget, new_contents): old_contents = edit_widget.edit_text @@ -653,7 +666,7 @@ def search_todo_list(self, search_string=""): self.searching = True for t in self.todos.search(search_string): - self.listbox.body.append( TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0]) ) + self.listbox.body.append(TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0])) def start_search(self): self.searching = True @@ -681,7 +694,7 @@ def create_footer(self): self.search_box, (16, urwid.AttrMap( urwid.Button([('header_file', 'C'), 'lear Search'], on_press=self.clear_search_term), - 'header', 'plain_selected') ) + 'header', 'plain_selected')) ]), 'footer') urwid.connect_signal(self.search_box, 'change', self.search_box_updated) else: @@ -693,16 +706,16 @@ def create_help_panel(self): header_highlight = 'plain_selected' return urwid.AttrMap( urwid.LineBox( - urwid.Padding( - ViListBox(self.key_bindings, - [ urwid.Divider() ] + + urwid.Padding( + ViListBox(self.key_bindings, + [urwid.Divider()] + - [ urwid.AttrWrap(urwid.Text(""" + [ urwid.AttrWrap(urwid.Text(""" General """.strip()), header_highlight) ] + - # [ urwid.Divider(u'─') ] + + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [ urwid.Text(""" {0} - show / hide this help message {1} - quit and save {2} - show / hide toolbar @@ -711,21 +724,21 @@ def create_help_panel(self): {5} - save current todo file {6} - reload the todo file (discarding changes) """.format( - self.key_bindings["toggle-help" ].ljust(key_column_width), - self.key_bindings["quit" ].ljust(key_column_width), - self.key_bindings["toggle-toolbar" ].ljust(key_column_width), - self.key_bindings["toggle-wrapping" ].ljust(key_column_width), - self.key_bindings["toggle-borders" ].ljust(key_column_width), - self.key_bindings["save" ].ljust(key_column_width), - self.key_bindings["reload" ].ljust(key_column_width), -))] + - - [ urwid.AttrWrap(urwid.Text(""" + self.key_bindings["toggle-help"].ljust(key_column_width), + self.key_bindings["quit"].ljust(key_column_width), + self.key_bindings["toggle-toolbar"].ljust(key_column_width), + self.key_bindings["toggle-wrapping"].ljust(key_column_width), + self.key_bindings["toggle-borders"].ljust(key_column_width), + self.key_bindings["save"].ljust(key_column_width), + self.key_bindings["reload"].ljust(key_column_width), + ))] + + + [ urwid.AttrWrap(urwid.Text(""" Movement """.strip()), header_highlight) ] + - # [ urwid.Divider(u'─') ] + + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [ urwid.Text(""" {0} - select any todo, checkbox or button {1} - move selection down {2} - move selection up @@ -735,22 +748,22 @@ def create_help_panel(self): {6} {7} - toggle focus between todos, filter panel, and toolbar """.format( - "mouse click".ljust(key_column_width), - self.key_bindings["down" ].ljust(key_column_width), - self.key_bindings["up" ].ljust(key_column_width), - self.key_bindings["top" ].ljust(key_column_width), - self.key_bindings["bottom" ].ljust(key_column_width), - self.key_bindings["left" ].ljust(key_column_width), - self.key_bindings["right" ].ljust(key_column_width), - self.key_bindings["change-focus" ].ljust(key_column_width), -))] + - - [ urwid.AttrWrap(urwid.Text(""" + "mouse click".ljust(key_column_width), + self.key_bindings["down"].ljust(key_column_width), + self.key_bindings["up"].ljust(key_column_width), + self.key_bindings["top"].ljust(key_column_width), + self.key_bindings["bottom"].ljust(key_column_width), + self.key_bindings["left"].ljust(key_column_width), + self.key_bindings["right"].ljust(key_column_width), + self.key_bindings["change-focus"].ljust(key_column_width), + ))] + + + [ urwid.AttrWrap(urwid.Text(""" Manipulating Todo Items """.strip()), header_highlight) ] + - # [ urwid.Divider(u'─') ] + + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [ urwid.Text(""" {0} - complete / un-complete selected todo item {1} - archive completed todo items to done.txt (if specified) {2} - add a new todo to the end of the list @@ -761,23 +774,23 @@ def create_help_panel(self): {7} - swap with item below {8} - swap with item above """.format( - self.key_bindings["toggle-complete" ].ljust(key_column_width), - self.key_bindings["archive" ].ljust(key_column_width), - self.key_bindings["append" ].ljust(key_column_width), - self.key_bindings["insert-after" ].ljust(key_column_width), - self.key_bindings["insert-before" ].ljust(key_column_width), - self.key_bindings["edit" ].ljust(key_column_width), - self.key_bindings["delete" ].ljust(key_column_width), - self.key_bindings["swap-down" ].ljust(key_column_width), - self.key_bindings["swap-up" ].ljust(key_column_width), -))] + - - [ urwid.AttrWrap(urwid.Text(""" + self.key_bindings["toggle-complete"].ljust(key_column_width), + self.key_bindings["archive"].ljust(key_column_width), + self.key_bindings["append"].ljust(key_column_width), + self.key_bindings["insert-after"].ljust(key_column_width), + self.key_bindings["insert-before"].ljust(key_column_width), + self.key_bindings["edit"].ljust(key_column_width), + self.key_bindings["delete"].ljust(key_column_width), + self.key_bindings["swap-down"].ljust(key_column_width), + self.key_bindings["swap-up"].ljust(key_column_width), + ))] + + + [ urwid.AttrWrap(urwid.Text(""" While Editing a Todo """.strip()), header_highlight) ] + - # [ urwid.Divider(u'─') ] + + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [ urwid.Text(""" {0} - tab complete contexts and projects {1} - save todo item {2} - move cursor left and right @@ -791,100 +804,99 @@ def create_help_panel(self): {10} - delete from the cursor to the beginning of the line {11} - paste last deleted text """.format( - self.key_bindings["edit-complete" ].ljust(key_column_width), - self.key_bindings["edit-save" ].ljust(key_column_width), - self.key_bindings["edit-move-left" ].ljust(key_column_width), - self.key_bindings["edit-move-right" ].ljust(key_column_width), - self.key_bindings["edit-word-left" ].ljust(key_column_width), - self.key_bindings["edit-word-right" ].ljust(key_column_width), - self.key_bindings["edit-home" ].ljust(key_column_width), - self.key_bindings["edit-end" ].ljust(key_column_width), - self.key_bindings["edit-delete-word" ].ljust(key_column_width), - self.key_bindings["edit-delete-end" ].ljust(key_column_width), - self.key_bindings["edit-delete-beginning" ].ljust(key_column_width), - self.key_bindings["edit-paste" ].ljust(key_column_width), -))] + - - [ urwid.AttrWrap(urwid.Text(""" + self.key_bindings["edit-complete"].ljust(key_column_width), + self.key_bindings["edit-save"].ljust(key_column_width), + self.key_bindings["edit-move-left"].ljust(key_column_width), + self.key_bindings["edit-move-right"].ljust(key_column_width), + self.key_bindings["edit-word-left"].ljust(key_column_width), + self.key_bindings["edit-word-right"].ljust(key_column_width), + self.key_bindings["edit-home"].ljust(key_column_width), + self.key_bindings["edit-end"].ljust(key_column_width), + self.key_bindings["edit-delete-word"].ljust(key_column_width), + self.key_bindings["edit-delete-end"].ljust(key_column_width), + self.key_bindings["edit-delete-beginning"].ljust(key_column_width), + self.key_bindings["edit-paste"].ljust(key_column_width), + ))] + + + [ urwid.AttrWrap(urwid.Text(""" Sorting """.strip()), header_highlight) ] + - # [ urwid.Divider(u'─') ] + + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [ urwid.Text(""" {0} - toggle sort order (Unsorted, Ascending, Descending) sort order is saved on quit """.format( - self.key_bindings["toggle-sorting"].ljust(key_column_width), -))] + - [ urwid.AttrWrap(urwid.Text(""" + self.key_bindings["toggle-sorting"].ljust(key_column_width), + ))] + + [ urwid.AttrWrap(urwid.Text(""" Filtering """.strip()), header_highlight) ] + - # [ urwid.Divider(u'─') ] + + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [ urwid.Text(""" {0} - open / close the filtering panel {1} - clear any active filters """.format( - self.key_bindings["toggle-filter" ].ljust(key_column_width), - self.key_bindings["clear-filter" ].ljust(key_column_width), -))] + - [ urwid.AttrWrap(urwid.Text(""" + self.key_bindings["toggle-filter"].ljust(key_column_width), + self.key_bindings["clear-filter"].ljust(key_column_width), + ))] + + [ urwid.AttrWrap(urwid.Text(""" Searching """.strip()), header_highlight) ] + - # [ urwid.Divider(u'─') ] + + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [ urwid.Text(""" {0} - start search {1} - finalize search {2} - clear search """.format( - self.key_bindings["search" ].ljust(key_column_width), - self.key_bindings["search-end" ].ljust(key_column_width), - self.key_bindings["search-clear" ].ljust(key_column_width), -))] - ), - left=1, right=1, min_width=10 ), title='Key Bindings'), 'default') + self.key_bindings["search"].ljust(key_column_width), + self.key_bindings["search-end"].ljust(key_column_width), + self.key_bindings["search-clear"].ljust(key_column_width), + ))] + ), + left=1, right=1, min_width=10), title='Key Bindings'), 'default') def create_filter_panel(self): w = urwid.AttrMap( urwid.Padding( - urwid.ListBox( - [ - ViPile( - self.key_bindings, - [ urwid.Text('Contexts & Projects', align='center') ] + - [ urwid.Divider(u'─') ] + - [urwid.AttrWrap(urwid.CheckBox(c, state=(c in self.active_contexts), on_state_change=self.checkbox_clicked, user_data=['context', c]), 'context_dialog_color', 'context_selected') for c in self.todos.all_contexts()] + - [ urwid.Divider(u'─') ] + - [urwid.AttrWrap(urwid.CheckBox(p, state=(p in self.active_projects), on_state_change=self.checkbox_clicked, user_data=['project', p]), 'project_dialog_color', 'project_selected') for p in self.todos.all_projects()] + - [ urwid.Divider(u'─') ] + - [ urwid.AttrMap(urwid.Button(['Clear ', ('header_file_dialog_color','F'), 'ilters'], on_press=self.clear_filters), 'dialog_color', 'plain_selected') ] - ) - ] + - [ urwid.Divider() ], - ), - left=1, right=1, min_width=10 ) - , - 'dialog_color') - - bg = urwid.AttrWrap(urwid.SolidFill(u" "), 'dialog_background') # u"\u2592" + urwid.ListBox( + [ + ViPile( + self.key_bindings, + [urwid.Text('Contexts & Projects', align='center')] + + [urwid.Divider(u'─')] + + [urwid.AttrWrap(urwid.CheckBox(c, state=(c in self.active_contexts), on_state_change=self.checkbox_clicked, user_data=['context', c]), 'context_dialog_color', 'context_selected') for c in self.todos.all_contexts()] + + [urwid.Divider(u'─')] + + [urwid.AttrWrap(urwid.CheckBox(p, state=(p in self.active_projects), on_state_change=self.checkbox_clicked, user_data=['project', p]), 'project_dialog_color', 'project_selected') for p in self.todos.all_projects()] + + [urwid.Divider(u'─')] + + [urwid.AttrMap(urwid.Button(['Clear ', ('header_file_dialog_color', 'F'), 'ilters'], on_press=self.clear_filters), 'dialog_color', 'plain_selected')] + ) + ] + + [urwid.Divider()], + ), + left=1, right=1, min_width=10), + 'dialog_color') + + bg = urwid.AttrWrap(urwid.SolidFill(u" "), 'dialog_background') # u"\u2592" shadow = urwid.AttrWrap(urwid.SolidFill(u" "), 'dialog_shadow') - bg = urwid.Overlay( shadow, bg, - ('fixed left', 2), ('fixed right', 1), - ('fixed top', 2), ('fixed bottom', 1)) - w = urwid.Overlay( w, bg, - ('fixed left', 1), ('fixed right', 2), - ('fixed top', 1), ('fixed bottom', 2)) + bg = urwid.Overlay(shadow, bg, + ('fixed left', 2), ('fixed right', 1), + ('fixed top', 2), ('fixed bottom', 1)) + w = urwid.Overlay(w, bg, + ('fixed left', 1), ('fixed right', 2), + ('fixed top', 1), ('fixed bottom', 2)) return w def delete_todo_widgets(self): - for i in range(len(self.listbox.body)-1, -1, -1): + for i in range(len(self.listbox.body) - 1, -1, -1): self.listbox.body.pop(i) def reload_todos_from_memory(self): for t in self.todos.todo_items: - self.listbox.body.append( TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0]) ) + self.listbox.body.append(TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0])) def clear_filters(self, button=None): self.delete_todo_widgets() @@ -918,7 +930,7 @@ def filter_todo_list(self): self.delete_todo_widgets() for t in self.todos.filter_contexts_and_projects(self.active_contexts, self.active_projects): - self.listbox.body.append( TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0]) ) + self.listbox.body.append(TodoWidget(t, self.key_bindings, self.colorscheme, self, wrapping=self.wrapping[0], border=self.border[0])) self.filtering = True @@ -959,12 +971,12 @@ def main(self, [TodoWidget(t, self.key_bindings, self.colorscheme, self) for t in self.todos.todo_items] )) - self.frame = urwid.Frame(urwid.AttrMap(self.listbox, 'plain'), header=self.header, footer=self.footer) + self.frame = urwid.Frame(urwid.AttrMap(self.listbox, 'plain'), header=self.header, footer=self.footer) self.view = ViColumns(self.key_bindings, - [ - ('weight', 2, self.frame ) - ]) + [ + ('weight', 2, self.frame) + ]) self.loop = urwid.MainLoop(self.view, self.palette, unhandled_input=self.keystroke) self.loop.screen.set_terminal_properties(colors=256) From 1cb0bf337322ffbd1aba581794a2d8785708b6a2 Mon Sep 17 00:00:00 2001 From: Nic West Date: Mon, 1 Feb 2016 01:43:17 +0000 Subject: [PATCH 3/5] fix some leftover errors and strip legacy code --- setup.cfg | 1 + setup.py | 21 ++++++++--- todotxt-machine.py | 1 - todotxt_machine/cli.py | 17 ++++----- todotxt_machine/colorscheme.py | 3 -- todotxt_machine/keys.py | 1 - todotxt_machine/terminal_operations.py | 3 -- todotxt_machine/todo.py | 3 -- todotxt_machine/urwid_ui.py | 49 +++++++++++++------------- 9 files changed, 47 insertions(+), 52 deletions(-) diff --git a/setup.cfg b/setup.cfg index affbce9..ca37fd1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,3 +11,4 @@ max-line-length=100 [flake8] ignore=E501 max-line-length=100 +exclude=.svn,CVS,.bzr,.hg,.git,__pycache,.venv,.cache,todotxt_machine/test/* diff --git a/setup.py b/setup.py index 725c5e6..2eb3407 100644 --- a/setup.py +++ b/setup.py @@ -24,12 +24,24 @@ """ from setuptools import setup, find_packages -from sys import version_info from setuptools.command.test import test as TestCommand +import todotxt_machine import sys -import todotxt_machine + +class PyTest(TestCommand): + + def finalize_options(self): + TestCommand.finalize_options(self) + self.test_args = [] + self.test_suite = True + + def run_tests(self): + # import here, cause outside the eggs aren't loaded + import pytest + errno = pytest.main(self.test_args) + sys.exit(errno) NAME = "todotxt-machine" @@ -50,8 +62,7 @@ packages=find_packages(exclude=["todotxt_machine/test*"]), include_package_data=True, entry_points={ - 'console_scripts': - ['todotxt-machine = todotxt_machine.cli:main'] + 'console_scripts': ['todotxt-machine = todotxt_machine.cli:main'] }, classifiers=[ "Development Status :: 4 - Beta", @@ -67,4 +78,4 @@ ], install_requires=['setuptools', 'docopt>=0.6.2', 'urwid>=1.2.1'], tests_require=['pytest'], - ) + cmdclass={'test': PyTest}) diff --git a/todotxt-machine.py b/todotxt-machine.py index 78c3ba6..d63285b 100755 --- a/todotxt-machine.py +++ b/todotxt-machine.py @@ -3,4 +3,3 @@ import todotxt_machine.cli todotxt_machine.cli.main() - diff --git a/todotxt_machine/cli.py b/todotxt_machine/cli.py index 3d82619..7be7298 100755 --- a/todotxt_machine/cli.py +++ b/todotxt_machine/cli.py @@ -22,12 +22,14 @@ import random import threading from collections import OrderedDict -from time import sleep +from docopt import docopt -# import ipdb; # ipdb.set_trace() +import todotxt_machine +from todotxt_machine.todo import Todos +from todotxt_machine.urwid_ui import UrwidUI +from todotxt_machine.colorscheme import ColorScheme +from todotxt_machine.keys import KeyBindings -# import pprint -# pp = pprint.PrettyPrinter(indent=4).pprint # Import the correct version of configparser if sys.version_info[0] >= 3: @@ -37,13 +39,6 @@ import ConfigParser config_parser_module = ConfigParser -from docopt import docopt - -import todotxt_machine -from todotxt_machine.todo import Todos -from todotxt_machine.urwid_ui import UrwidUI -from todotxt_machine.colorscheme import ColorScheme -from todotxt_machine.keys import KeyBindings autosave_lock = threading.Lock() diff --git a/todotxt_machine/colorscheme.py b/todotxt_machine/colorscheme.py index 8ad240c..e4b9a6a 100644 --- a/todotxt_machine/colorscheme.py +++ b/todotxt_machine/colorscheme.py @@ -51,12 +51,9 @@ def load_colors(self, name): # Create Selected attributes using the selected_background_color selected_background_color = self.colors['selected']['bg'] dialog_color = self.colors['dialog_color']['bg'] - # dialog_button_color = self.colors['dialog_button_color']['bg'] for key, value in list(self.colors.items()): if key not in ['selected', 'dialog_color', 'dialog_button_color']: self.colors[key + '_selected'] = {'fg': self.colors[key]['fg'], 'bg': selected_background_color} self.colors[key + '_dialog_color'] = {'fg': self.colors[key]['fg'], 'bg': dialog_color} - # self.colors[key+'_dialog_button_color'] = {'fg': self.colors[key]['fg'], 'bg': dialog_button_color} self.focus_map[key] = key + '_selected' self.dialog_focus_map[key] = key + '_dialog_color' - # self.dialog_focus_map[key] = key + '_dialog_button_color' diff --git a/todotxt_machine/keys.py b/todotxt_machine/keys.py index 240a716..90847b6 100644 --- a/todotxt_machine/keys.py +++ b/todotxt_machine/keys.py @@ -11,7 +11,6 @@ def fillWithUserKeys(self, users_keys): for bind in users_keys: key = self.userKeysToList(users_keys[bind]) try: - default = self.key_bindings[bind] self.key_bindings[bind] = key except KeyError: print("KeyBind \"" + bind + "\" not found") diff --git a/todotxt_machine/terminal_operations.py b/todotxt_machine/terminal_operations.py index d640495..ea103dc 100644 --- a/todotxt_machine/terminal_operations.py +++ b/todotxt_machine/terminal_operations.py @@ -1,12 +1,9 @@ #!/usr/bin/env python # coding=utf-8 import sys -# import os import subprocess -import tty import termios import re -# import curses import fcntl import struct diff --git a/todotxt_machine/todo.py b/todotxt_machine/todo.py index 3a66736..e956a5f 100644 --- a/todotxt_machine/todo.py +++ b/todotxt_machine/todo.py @@ -2,11 +2,8 @@ # coding=utf-8 import re import random -import urwid from datetime import date -from todotxt_machine.terminal_operations import TerminalOperations - class Todo: """Single Todo item""" diff --git a/todotxt_machine/urwid_ui.py b/todotxt_machine/urwid_ui.py index 62db6da..baa5615 100644 --- a/todotxt_machine/urwid_ui.py +++ b/todotxt_machine/urwid_ui.py @@ -72,9 +72,9 @@ def keypress(self, size, key): try: before = self.edit_text[:self.edit_pos] if self.completion_data: - if (not self.completion_data['completed'] - or self.completion_data['position'] != self.edit_pos - or not before.endswith(self.completion_data['completed'])): + if (not self.completion_data['completed'] or + self.completion_data['position'] != self.edit_pos or + not before.endswith(self.completion_data['completed'])): self.completion_data.clear() else: before = before[:-len(self.completion_data['completed'])] @@ -655,7 +655,6 @@ def create_toolbar(self): ]), 'header') def search_box_updated(self, edit_widget, new_contents): - old_contents = edit_widget.edit_text self.search_string = new_contents self.search_todo_list(self.search_string) @@ -710,12 +709,12 @@ def create_help_panel(self): ViListBox(self.key_bindings, [urwid.Divider()] + - [ urwid.AttrWrap(urwid.Text(""" + [urwid.AttrWrap(urwid.Text(""" General -""".strip()), header_highlight) ] + +""".strip()), header_highlight)] + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [urwid.Text(""" {0} - show / hide this help message {1} - quit and save {2} - show / hide toolbar @@ -733,12 +732,12 @@ def create_help_panel(self): self.key_bindings["reload"].ljust(key_column_width), ))] + - [ urwid.AttrWrap(urwid.Text(""" + [urwid.AttrWrap(urwid.Text(""" Movement -""".strip()), header_highlight) ] + +""".strip()), header_highlight)] + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [urwid.Text(""" {0} - select any todo, checkbox or button {1} - move selection down {2} - move selection up @@ -758,12 +757,12 @@ def create_help_panel(self): self.key_bindings["change-focus"].ljust(key_column_width), ))] + - [ urwid.AttrWrap(urwid.Text(""" + [urwid.AttrWrap(urwid.Text(""" Manipulating Todo Items -""".strip()), header_highlight) ] + +""".strip()), header_highlight)] + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [urwid.Text(""" {0} - complete / un-complete selected todo item {1} - archive completed todo items to done.txt (if specified) {2} - add a new todo to the end of the list @@ -785,12 +784,12 @@ def create_help_panel(self): self.key_bindings["swap-up"].ljust(key_column_width), ))] + - [ urwid.AttrWrap(urwid.Text(""" + [urwid.AttrWrap(urwid.Text(""" While Editing a Todo -""".strip()), header_highlight) ] + +""".strip()), header_highlight)] + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [urwid.Text(""" {0} - tab complete contexts and projects {1} - save todo item {2} - move cursor left and right @@ -818,35 +817,35 @@ def create_help_panel(self): self.key_bindings["edit-paste"].ljust(key_column_width), ))] + - [ urwid.AttrWrap(urwid.Text(""" + [urwid.AttrWrap(urwid.Text(""" Sorting -""".strip()), header_highlight) ] + +""".strip()), header_highlight)] + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [urwid.Text(""" {0} - toggle sort order (Unsorted, Ascending, Descending) sort order is saved on quit """.format( self.key_bindings["toggle-sorting"].ljust(key_column_width), ))] + - [ urwid.AttrWrap(urwid.Text(""" + [urwid.AttrWrap(urwid.Text(""" Filtering -""".strip()), header_highlight) ] + +""".strip()), header_highlight)] + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [urwid.Text(""" {0} - open / close the filtering panel {1} - clear any active filters """.format( self.key_bindings["toggle-filter"].ljust(key_column_width), self.key_bindings["clear-filter"].ljust(key_column_width), ))] + - [ urwid.AttrWrap(urwid.Text(""" + [urwid.AttrWrap(urwid.Text(""" Searching -""".strip()), header_highlight) ] + +""".strip()), header_highlight)] + # [ urwid.Divider(u'─') ] + - [ urwid.Text(""" + [urwid.Text(""" {0} - start search {1} - finalize search {2} - clear search From 9488ca1e90b9f970cd1940e72642496874aa2fe1 Mon Sep 17 00:00:00 2001 From: Nic West Date: Fri, 23 Dec 2016 12:11:23 +0000 Subject: [PATCH 4/5] fix flake 8 issue --- todotxt_machine/todo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todotxt_machine/todo.py b/todotxt_machine/todo.py index e956a5f..0cc0771 100644 --- a/todotxt_machine/todo.py +++ b/todotxt_machine/todo.py @@ -7,7 +7,7 @@ class Todo: """Single Todo item""" - _priority_regex = re.compile(r'\(([A-Z])\) ') + _priority_regex = re.compile(r'\(([A-Z])\) ') def __init__(self, item, index, colored="", priority="", contexts=[], projects=[], From b12093e6746b3db70681239ed3d3693f43a2b92d Mon Sep 17 00:00:00 2001 From: Nic West Date: Fri, 23 Dec 2016 12:17:54 +0000 Subject: [PATCH 5/5] update tox for flake8 --- todotxt_machine/cli.py | 2 ++ tox.ini | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/todotxt_machine/cli.py b/todotxt_machine/cli.py index 7be7298..538f7ed 100755 --- a/todotxt_machine/cli.py +++ b/todotxt_machine/cli.py @@ -59,6 +59,7 @@ def autosave(): timer = threading.Timer(30.0, autosave) timer.start() + timer = threading.Timer(30.0, autosave) @@ -190,5 +191,6 @@ def main(): exit(0) + if __name__ == '__main__': main() diff --git a/tox.ini b/tox.ini index 8a6fcac..38a95a4 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ commands=py.test [testenv:flake8] deps = flake8 -commands = flake8 +commands = flake8 todotxt_machine [tox:travis] 2.7 = py27