Due to overwhelming demand :-) poodledo now has a proper setup.py and has been added to pypi. All the credit goes to @iandennismiller for doing all the hard work to make this change; thanks also to @dsmcfarl and @Ciemaar for their help and support.
pip install poodledo for the win!
Also, while I was poking around, I added preliminary support for Python 3, mostly using the "six" module. I can't say it offers full support yet because it requires
parsedatetime, and the version in pypi doesn't support Python 3; however, the 1.0.0 version on github does support Python 3, and poodledo works great with that. All of the other Python-3-supporting changes have been regression-tested and shouldn't cause any trouble on Python 2.6+. The minimum version requirement has been raised to Python 2.6; it's probably effectively been Python 2.6+ for over a year (code using "except foo as bar" has been in the repo at least that long), so now I'm making it official.
poodledo is a Python library for working with the web-based task management software Toodledo.
- Python 2.6+, including 3+
tdcli requires the following Python modules (both available by
poodledo handles almost all of the API calls described in the Toodledo docs.
ApiClient object and call
authenticate() to get a session key. This key is re-used in subsequent API calls until it expires.
from apiclient import ApiClient c = ApiClient() c.authenticate('firstname.lastname@example.org', 'password')
Toodledo's API is broken down into six basic object types:
along with several other miscellaneous methods (
Each object type supports the same basic operations: "add", "delete", "edit", and "get". I'll use Folders for these examples, but the other object types have equivalent methods with the appropriate names (i.e.,
getFolders to retrieve a list of folder objects from the API. This list is cached by the ApiClient until a change is made with another API call.
getFolder to retrieve a specific folder object. This method takes one argument, which can be either the object's ID or its name/title.
c.getFolder('My name is') c.getFolder(3472958)
addFolder to add a new folder with the API.
c.addFolder('New Folder Name')
You can also specify object parameters as keyword arguments.
c.addFolder('Private, Archived Folder', archived=True, private=True)
deleteFolder to delete a folder with the API. This method uses
getFolder to identify its argument, so you can specify either the folder's name or its ID.
editFolder to change a folder's characteristics. Use the same arguments as
getFolder to identify the target, and pass the changes as keyword arguments.
c.editFolder('Current Folder Name', name='New Folder Name', private=True)
- In order to use this library, you will need to get your own API token. I have not included mine in the code. Add it to the dot file (
- Pull requests always welcome!
- Write better documentation for adding, editing, and closing tasks
- Write several sample scripts for reference
- Add batch processing for notes and tasks
- Write a "pythonic" wrapper that makes the returned objects smart (e.g., doing
task_object.name = "New Name"would actually update the task's name with the API)
- Make objects which have an ordering (folders and subtasks) a) honor that ordering, and b) be re-orderable
- Write scripts to write the poodledo state to various formats (JSON, XML, org-mode) and possibly also to import from those formats
- Make it easier to find and close tasks from the command line somehow
poodledo is released under a BSD License. See the LICENSE file for details.
You can email me at email@example.com.
To report bugs or request features, please use the Issues feature.
I spent some time the last couple of days making some substantial changes to the new-task parser and the
I tore out the plex-based lexer component (the bit that parsed arbitrary strings describing new tasks) and replaced it with a regex-based matching series, inspired by the way the Ruby toodledo gem does it. The lexer vocabulary was getting way too complicated and brittle, and I was having trouble making even small improvements or bug fixes.
The user-facing behavior should be pretty much identical, if not improved in several respects (fewer things should blow up the parser). I've also fixed a few bugs I'd run into while adding my own tasks.
I added readline support to the
tdcli new task reader (what you get when you run
tdcli all by itself). This means you can use standard interactive prompt keyboard shortcuts (Control+A for beginning of line, etc.). This also let me build in two big new features:
- Support for multi-line notes When adding a new task, pressing "Enter" will move to a new line, and that line (and all subsequent ones) will be incorporated into the new task as its note. Press Control+C or Control+D to stop editing and add the task.
- Tab-completion for several fields When adding a new task, typing the symbol for context (@), folder (*), goal (+), location (-) or status ($) and then hitting TAB will offer to tab-complete that field based on the available options already created in your account.
I also added a couple of other new improvements not strictly related to the above two:
- If you specify a context, folder, goal, or location which does not already exist when adding a task,
tdcliwill notice and offer to create it for you first.
tdclinow supports listing contexts and locations (in addition to listing tasks, folders, and goals like it already could).
I added an
__init__.py to address Issue#4, so that
poodledo can be included as a module.
I also made several improvements to the
cycle tool, as detailed in README-cycle.
The Toodledo 3rd-party apps directory has been updated with a link to poodledo. Hopefully that will increase the usage of this library.
I've taken ralesi's suggestion in Issue#2 and moved the API application ID and token to the config file (
~/.tdcli/tdclirc by default). This way,
git pulls will not conflict with your local ID and token. Thanks for the suggestion, Rich!
2011-11-11, part 2
Since I was on a roll, I decided to keep working.
The big changes are a refactoring of the CLI resources into a separate module and a new tool,
I refactored the code needed to get a CLI running (such as loading the config file and opening a connection to the API) into its own module, with a simple entry point (
do_login will attempt to load the config file and set up an API client object, and return it if successful. This change makes creating a new CLI tool a lot simpler.
I wrote another CLI tool,
cycle, which attempts to implement the Cycle System from Time Management for System Administrators by Thomas Limoncelli. The Cycle System, in this context, has five main activities:
- Display the tasks assigned to today's list
- Add a new task
- Complete a task
- Move a task
- Reprioritize a task
It's a lot to explain in a short blurb here, but basically, the idea is to assign your tasks to a specific day, and by the end of the day, to have taken some action with each task (either completing it, deleting it, or moving it to another specific day).
cycle implements this system by keying off of the
duedate field in Toodledo; when you run
cycle by itself, it will show a list of all of your tasks which have a
duedate of today, along with their priorities. Really though, if this sounds at all intriguing, you should read the book; Tom explains it a lot better than I ever could.
I did some work to fix Issue#2:
- Configuration moved to
- To improve startup speed,
tdclicaches a Python "pickle" of the lexer in
~/.tdcli/lexer.pickle. This saves
tdclifrom having to regenerate the entire lexer each time. This file is safe to delete at any time;
tdcliwill just regenerate it the next time it's run.
I did not write code to automatically migrate the configuration file from the old location, so please keep in mind that you'll need to move it yourself.
I've made a few more improvements to the
tdclinow supports listing tasks with
tdclinow stores session and login data in
~/.tdclircinstead of a file called "config" in the current directory
The next improvements I want to make are to have the list of tasks accept some filters (so I can say, "list all tasks with the tag 'work'"), and also to automatically list subtasks under their parents if your account supports them. Further on I'd like to implement more of the featureset of the Ruby toodledo client, specifically the "online" mode where you can poke around in your tasks and create folders, contexts, etc.
I made some significant improvements to the
Much better handling of the "config" file; in particular, if no valid session key or credentials are specified, it will ask for such on the command line, get a session key, and then cache the key (but not the credentials). This way, you don't have to store your password in plaintext anywhere.
Added usage statement to tdcli to explain how the lexer works:
$ tdcli -h Enter a task and associated metadata: context: @<context> due date: #<date>; toodledo parses dates smartly, including "#next thursday" due time: =<time>; translates time smartly folder: *<name> goal: +<goal> length: ~<time>; like "~4hours" location: -<location> note: ?<note data> priority: default is zero; single ! = 1, !! = 2, !!! = 3 (top) reminder: :<lead time>; ":5 hours" repeat: &<schedule> star: * alone makes the task starred start date: ><date> (<) start time: ^<time> status: $<status> tag: %<tag>; can select multiple with "%tag1, tag2"
And in non-tdcli news, I fixed a couple of bugs elsewhere in the code.
As of today,
tdcli should be able to meet all of my command-line-task-adding needs; if there's something else you'd like it to do, please open a new issue or submit a pull request.
I've added a few new features to the library:
- Pretty-printing for ToodledoData objects (tasks and such)
- Smarter object detection and parsing for API client calls; now you can call "addTask('task name', folder='folder name')" and the folder ID will be figured out automatically
- And the biggie: parsing the Toodledo email syntax for adding new tasks!
There is now a lexer.parse() function that will accept a string with the Toodledo email syntax and return a parsed task dictionary which addTask() will happily accept.
The new program
tdcli demonstrates this feature:
$ tdcli Enter a task description: This task is important !! #Today $Next Action modified: 1314577488 id: 36647367 title: This task is important priority: 2 duedate: 1314532800 status: 1 added: 1314532800
Not all of the syntax options work yet, and the parsing is very fragile, so if you run into any trouble please submit an issue request.