Git Product home page Git Product logo

todd's Introduction

todd

todd is an interactive console TODO-list manager with VI key bindings.

It provides a minimalistic interface with a view on your todo list, following in the spirit of spirit of ranger and mutt.

The file format conforms to todo.txt. This means you have full control over your data and can choose to store it locally or in the cloud (like Dropbox). You can use different applications to access your tasks on your desktop or mobile devices.

Thanks to the creator and contributors of todotxt-machine for providing the basis for this project.

Features

  • View your tasks in a column formatted list with relative due dates
  • Switch context (only view tasks that you can work on)
  • Sort by due date or priority
  • Search/filter
  • Edit in plain text with tab completion or adjust priority and due
  • Due dates can be set with
    • weekday name (mo, tu, ...)
    • offset (2d in two days, 3m in three moths, 1y in a year)
    • a fixed date (YYYY-MM-DD)

Installation

Supports Python 3.6 on Linux or macOS.

Using pip

pip3 install todd

(Or pip install todd if Python 3 is the default for your system.)

Manually

Download or clone this repo and run the todd.py script.

git clone https://github.com/laktak/todd
cd todd
pip3 install -r requirements.txt
./todd.py

Command Line Options

todd

Usage:
  todd [--config FILE] [TODOFILE] [DONEFILE]
  todd (-h | --help)
  todd --version
  todd --show-default-bindings

Options:
  -c FILE --config=FILE               Path to your todd configuraton file [default: ~/.toddrc]
  -h --help                           Show this screen.
  --version                           Show version.
  --show-default-bindings             Show default keybindings in config parser format

Config File

You can tell todd to use the same todo.txt file whenever it starts up by adding a file entry to the ~/.toddrc file. If you want to archive done tasks, you can specify a done.txt file using an archive entry. You can also set you preferred colorscheme or even define new themes.

Here is a short example:

[settings]
file = ~/todo.txt
archive = ~/done.txt
enable-word-wrap = True
colorscheme = myawesometheme

Color Schemes

Here is a config file with a complete colorscheme definition:

[settings]
file = ~/todo.txt
colorscheme = myawesometheme

[colorscheme-myawesometheme]
plain=h250
...

You can add colorschemes by adding sections with names that start with colorscheme-. Then under the [settings] section you can say which colorscheme you want to use.

The format for a color definitions is:

name=[foreground],[background]

Foreground and background colors are follow the 256 color formats defined by urwid. You can see all the colors defined here.

Key Bindings

See active key bindings with h or ? in todd.

You can customize any key binding by adding a setting to the [keys] section of your config file ~/.toddrc.

For a list of the default key bindings run:

todd --show-default-bindings

Contributing

Bug reports and fixes are always welcome!

Before submitting pull requests, run black and tests with:

$ black .
$ python setup.py test

todd's People

Contributors

anthonydigirolamo avatar khughitt avatar laktak avatar matze avatar meatballs avatar nicwest avatar ojdo avatar rpesche avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

todd's Issues

Using an unknown "due" value results in a crash

I'd tried due:tomorrow and it crashed with:

Traceback (most recent call last):
  File "/home/peter/.local/bin/todd", line 11, in <module>
    sys.exit(main())
  File "/home/peter/.local/lib/python3.6/site-packages/todd/main.py", line 132, in main
    view.main(enable_word_wrap)  # start up the urwid UI event loop
  File "/home/peter/.local/lib/python3.6/site-packages/todd/taskui/main_ui.py", line 467, in main
    self.loop.run()
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/main_loop.py", line 286, in run
    self._run()
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/main_loop.py", line 384, in _run
    self.event_loop.run()
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/main_loop.py", line 788, in run
    self._loop()
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/main_loop.py", line 825, in _loop
    self._watch_files[fd]()
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/raw_display.py", line 404, in <lambda>
    event_loop, callback, self.get_available_raw_input())
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/raw_display.py", line 502, in parse_input
    callback(processed, processed_codes)
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/main_loop.py", line 411, in _update
    self.process_input(keys)
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/main_loop.py", line 511, in process_input
    k = self._topmost_widget.keypress(self.screen_size, k)
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/container.py", line 2271, in keypress
    key = w.keypress((mc,) + size[1:], key)
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/container.py", line 1131, in keypress
    return self.body.keypress( (maxcol, remaining), key )
  File "/home/peter/.local/lib/python3.6/site-packages/todd/taskui/components.py", line 116, in keypress
    key = super(ViListBox, self).keypress(size, key)
  File "/home/peter/.local/lib/python3.6/site-packages/urwid/listbox.py", line 999, in keypress
    key = focus_widget.keypress((maxcol,),key)
  File "/home/peter/.local/lib/python3.6/site-packages/todd/taskui/taskitem.py", line 103, in keypress
    self.end_edit()
  File "/home/peter/.local/lib/python3.6/site-packages/todd/taskui/taskitem.py", line 96, in end_edit
    self.parent_ui.task_changed()
  File "/home/peter/.local/lib/python3.6/site-packages/todd/taskui/main_ui.py", line 266, in task_changed
    t.update_relative_due_date()
  File "/home/peter/.local/lib/python3.6/site-packages/todd/tasklib/task.py", line 191, in update_relative_due_date
    date = Util.mod_date_by(Util.get_today(), match.group(1))
  File "/home/peter/.local/lib/python3.6/site-packages/todd/tasklib/util.py", line 91, in mod_date_by
    (prefix, value, itype) = Util._interval_parts_regex.match(text).groups()
AttributeError: 'NoneType' object has no attribute 'groups'

A python library like Arrow might make the time conversions a cinch if you wanted to grab human readable date/time values and try to convert them.

Either way, the app should do something graceful if a bad due value is entered.

Multiple Contexts

It would be very useful to be able to select multiple contexts in the swithching pane

Use case:

I have tasks that can only be done at home and others only in my office, but there are some that can be done at either as long as i have my laptop and an internet connection.

If I'm at the office, I might want to select both @office and @online as my current contexts.

Alternatively, if I could exclude the @home context, that would also be neat!

Sort and Filter by Project

Excellent TUI!! Many thanks indeed.

I can see that projects are scanned on a task, but I can't see a column for them or the ability to sort/filter.

Home and End keys cause an unhandled exception

Checked out master then:
python3.7 ./todd.py

Version 0.0.7

It feels kind of natural to use Home to go to the top issue or End to go to the last issue.

Using these keys seems to be unhandled.

Home:

Traceback (most recent call last):
  File "todd.py", line 9, in <module>
    main()
  File "/home/peter/Code/todd/todd/main.py", line 132, in main
    view.main(enable_word_wrap)  # start up the urwid UI event loop
  File "/home/peter/Code/todd/todd/taskui/main_ui.py", line 467, in main
    self.loop.run()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 287, in run
    self._run()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 385, in _run
    self.event_loop.run()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 790, in run
    self._loop()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 827, in _loop
    self._watch_files[fd]()
  File "/usr/lib64/python3.7/site-packages/urwid/raw_display.py", line 417, in <lambda>
    event_loop, callback, self.get_available_raw_input())
  File "/usr/lib64/python3.7/site-packages/urwid/raw_display.py", line 515, in parse_input
    callback(processed, processed_codes)
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 412, in _update
    self.process_input(keys)
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 513, in process_input
    k = self._topmost_widget.keypress(self.screen_size, k)
  File "/usr/lib64/python3.7/site-packages/urwid/container.py", line 2310, in keypress
    key = w.keypress((mc,) + size[1:], key)
  File "/usr/lib64/python3.7/site-packages/urwid/container.py", line 1131, in keypress
    return self.body.keypress( (maxcol, remaining), key )
  File "/home/peter/Code/todd/todd/taskui/components.py", line 116, in keypress
    key = super(ViListBox, self).keypress(size, key)
  File "/usr/lib64/python3.7/site-packages/urwid/listbox.py", line 978, in keypress
    return actual_key(self._keypress_max_left((maxcol, maxrow)))
TypeError: _keypress_max_left() takes 1 positional argument but 2 were given

End:

Traceback (most recent call last):
  File "./todd.py", line 9, in <module>
    main()
  File "/home/peter/Code/todd/todd/main.py", line 132, in main
    view.main(enable_word_wrap)  # start up the urwid UI event loop
  File "/home/peter/Code/todd/todd/taskui/main_ui.py", line 467, in main
    self.loop.run()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 287, in run
    self._run()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 385, in _run
    self.event_loop.run()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 790, in run
    self._loop()
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 827, in _loop
    self._watch_files[fd]()
  File "/usr/lib64/python3.7/site-packages/urwid/raw_display.py", line 417, in <lambda>
    event_loop, callback, self.get_available_raw_input())
  File "/usr/lib64/python3.7/site-packages/urwid/raw_display.py", line 515, in parse_input
    callback(processed, processed_codes)
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 412, in _update
    self.process_input(keys)
  File "/usr/lib64/python3.7/site-packages/urwid/main_loop.py", line 513, in process_input
    k = self._topmost_widget.keypress(self.screen_size, k)
  File "/usr/lib64/python3.7/site-packages/urwid/container.py", line 2310, in keypress
    key = w.keypress((mc,) + size[1:], key)
  File "/usr/lib64/python3.7/site-packages/urwid/container.py", line 1131, in keypress
    return self.body.keypress( (maxcol, remaining), key )
  File "/home/peter/Code/todd/todd/taskui/components.py", line 116, in keypress
    key = super(ViListBox, self).keypress(size, key)
  File "/usr/lib64/python3.7/site-packages/urwid/listbox.py", line 981, in keypress
    return actual_key(self._keypress_max_right((maxcol, maxrow)))
TypeError: _keypress_max_right() takes 1 positional argument but 2 were given

Hitting edit on a view with no tasks listed causes a crash

If there's no tasks in the current view then you hit enter to edit, it'll crash with:

Traceback (most recent call last):
  File "/usr/local/bin/todd", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.7/site-packages/todd/main.py", line 129, in main
    view.main(enable_word_wrap)  # start up the urwid UI event loop
  File "/usr/local/lib/python3.7/site-packages/todd/taskui/main_ui.py", line 369, in main
    self.loop.run()
  File "/usr/local/lib/python3.7/site-packages/urwid/main_loop.py", line 286, in run
    self._run()
  File "/usr/local/lib/python3.7/site-packages/urwid/main_loop.py", line 384, in _run
    self.event_loop.run()
  File "/usr/local/lib/python3.7/site-packages/urwid/main_loop.py", line 788, in run
    self._loop()
  File "/usr/local/lib/python3.7/site-packages/urwid/main_loop.py", line 825, in _loop
    self._watch_files[fd]()
  File "/usr/local/lib/python3.7/site-packages/urwid/raw_display.py", line 404, in <lambda>
    event_loop, callback, self.get_available_raw_input())
  File "/usr/local/lib/python3.7/site-packages/urwid/raw_display.py", line 502, in parse_input
    callback(processed, processed_codes)
  File "/usr/local/lib/python3.7/site-packages/urwid/main_loop.py", line 411, in _update
    self.process_input(keys)
  File "/usr/local/lib/python3.7/site-packages/urwid/main_loop.py", line 517, in process_input
    something_handled |= bool(self.unhandled_input(k))
  File "/usr/local/lib/python3.7/site-packages/urwid/main_loop.py", line 563, in unhandled_input
    return self._unhandled_input(input)
  File "/usr/local/lib/python3.7/site-packages/todd/taskui/main_ui.py", line 343, in keystroke
    elif self.key_bindings.is_bound_to(key, "edit"): self.edit_task()
  File "/usr/local/lib/python3.7/site-packages/todd/taskui/main_ui.py", line 197, in edit_task
    focus.edit_item(normal_mode)
AttributeError: 'NoneType' object has no attribute 'edit_item'

So do a search with something that returns empty results and then try to edit to reproduce this.

end edit mode on esc

I find myself hitting esc if I pressed enter by accident. I would expect to leave task edit mode.

Just saying thanks :)

Hi,

Just found your todo.txt client and looks pretty awesome so far.
I've been using todotxt-machine for a few weeks and yours feels like an improvement in a number of ways.

I've crashed it once already, but wasn't paying attention how, so once I work that out I'll log a bug with the repro.

๐Ÿ‘

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.