Today I’m releasing tastic, a python package for working with taskpaper documents. It’s crammed packed with tools to do pretty much everything you’d want to do with a taskpaper document. You can add projects, add tasks, set tags, sort documents and much more.
Installation
The easiest way to install tastic us to use pip
:
pip install tastic
Or you can clone the github repo and install from a local version of the code:
git clone git@github.com:thespacedoctor/tastic.git
cd tastic
python setup.py install
To upgrade to the latest version of tastic use the command:
pip install tastic --upgrade
Documentation
Documentation for tastic is hosted by Read the Docs (last stable version and latest version).
Tutorial
Before we start, you’ll need an example taskpaper document to work with. Copy and paste the following example document content into a taskpaper file somewhere on your file system:
- invite friends over for drinks
make coffee: @coffee @flag
- scoop 3 heaped tablespoons of coffee into cafetiere
- fill cafetiere with boiled water from kettle @hot @water
- wait for 3 minutes @wait
- plunge the coffee in the cafetiere
- pour into cup @hot
- drink
ahhhhhhh that's good
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
- do get hair cut @due
tidy the garden: @flag
build bbq: @someday
cut the grass:
- has it stopped raining yet @hold
you can check the weather here: http://forecast.io/
- get the mower out
- put welly boots on
- cut the grass
replace hedge with fence: @due
- watch a couple of youtube videos about putting up a fence @flag
buy fence materials:
the hedge at the rear of the garden
- ask neighbours if I can work from their garden to fix the fence
this is a rolling document where I can add projects and task I know I can only get done on saturdays
- take the boys to the cinema if it's raining @someday
grocery shop: @due
- carrots
- shampoo
- beer
- washing detergent
The super-market closes at 8pm on saturdays
- take the boys to the park @next
- put up shelves in living room @flag
Archive:
- research the price of fencing online @done(2016-09-15) @project(tidy the garden / replace hedge with fence)
- clear the garden @done(2016-09-15) @project(tidy the garden / cut the grass)
[Searches]: @hide
- do: due @search(/project @due//* union //@due and not @done)
- do: flag @search(/project @flag//* union //@flag and not @done)
- do: projects to tag @search(/project not "@" and not "archive"//*)
- review: next or someday @search(project @next or @someday//* )
- Project List @search(/project not @someday)
- Next and Someday List @search(/project @next or @someday)
Taskpaper Objects
If you’re unfamiliar with the taskpaper syntax, head over to Jesse Grosjean’s User Guide for Taskpaper 3.
There are 5 basic components to the taskpaper syntax that tastic recognises; these are:
- documents
- projects
- tasks
- notes
- tags
Working with documents
I’m going to assume that you’ve saved the example file above to your desktop and named the file saturday-tasks.taskpaper. Fire up ipython and let’s get stuck in.
Reading a document
To read the file into memory use the following python code:
from tastic.tastic import document
doc = document("/Users/<yourusername>/Desktop/saturday-tasks.taskpaper")
This command reads the content of the file and automatically tidies it for you. To view the content of the file run the following:
print doc.content
And as you can see we now have a nice clean, ordered document; notes first, then tasks, then projects, then searches:
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
this is a rolling document where I can add projects and task I know I can only get done on saturdays
- invite friends over for drinks
- do get hair cut @due
- take the boys to the cinema if it's raining @someday
- take the boys to the park @next
- put up shelves in living room @flag
make coffee: @coffee @flag
- scoop 3 heaped tablespoons of coffee into cafetiere
- fill cafetiere with boiled water from kettle @hot @water
- wait for 3 minutes @wait
- plunge the coffee in the cafetiere
- pour into cup @hot
- drink
ahhhhhhh that's good
tidy the garden: @flag
build bbq: @someday
cut the grass:
- has it stopped raining yet @hold
you can check the weather here: http://forecast.io/
- get the mower out
- put welly boots on
- cut the grass
replace hedge with fence: @due
the hedge at the rear of the garden
- watch a couple of youtube videos about putting up a fence @flag
- ask neighbours if I can work from their garden to fix the fence
buy fence materials:
grocery shop: @due
The super-market closes at 8pm on saturdays
- carrots
- shampoo
- beer
- washing detergent
Archive:
- research the price of fencing online @done(2016-09-15) @project(tidy the garden / replace hedge with fence)
- clear the garden @done(2016-09-15) @project(tidy the garden / cut the grass)
[Searches]: @hide
- do: due @search(/project @due//* union //@due and not @done)
- do: flag @search(/project @flag//* union //@flag and not @done)
- do: projects to tag @search(/project not "@" and not "archive"//*)
- review: next or someday @search(project @next or @someday//* )
- Project List @search(/project not @someday)
- Next and Someday List @search(/project @next or @someday)
If at any stage in your code you want to tidy the document again (not that you should need to), run the command:
doc.tidy()
Writing a document
Note any changes you make to the content of the document will have to be saved back to the file. To save the document at any stage run the command:
doc.save()
or to save the content to a different file:
doc.save("/Users/<yourusername>/Desktop/saturday-tasks-copy.taskpaper")
Note, if you save the content to another file, any further edits to the content of the file will be saved to this new location with save().
Working with projects
Both documents and projects themselves can contain sub-projects.
Get a project by name
To select out a single project by it’s title use the get_project method:
gardenProject = doc.get_project("tidy the garden")
print gardenProject.to_string()
tidy the garden: @flag
build bbq: @someday
cut the grass:
- has it stopped raining yet @hold
you can check the weather here: http://forecast.io/
- get the mower out
- put welly boots on
- cut the grass
replace hedge with fence: @due
the hedge at the rear of the garden
- watch a couple of youtube videos about putting up a fence @flag
- ask neighbours if I can work from their garden to fix the fence
buy fence materials:
Also note the use of the to_string() method. This method can be used on documents, projects, tasks and notes to convert the object to a string.
Lising projects
To compile a list of root-level projects within your document, use the projects attribute:
docProjects = doc.projects
for p in docProjects:
print p.title
make coffee:
tidy the garden:
grocery shop:
Archive:
All projects also have a projects attribute so you can drill down into a document’s project tree to work with any sub-project. For example:
subProjects = gardenProject.projects
for p in subProjects:
print p.title
build bbq:
cut the grass:
replace hedge with fence:
Filtering projects by tag
To filter projects by an associated tag, use the tagged_projects method:
dueProjects = doc.tagged_projects("@due")
for p in dueProjects:
print p.title
replace hedge with fence:
grocery shop:
The keen eyed among you will notice that this filter is in fact recursive, picking up all projects within the document with the @due
tag and not just the root level projects. Again each project has a tagged_projects method to allow for finer grain filtering of projects.
Sorting projects by tags
sort_projects is one of my favorite methods. Given a list of workflow tags, you can sort projects recursively within a taskpaper document or project. In the example below projects tagged with @due rise to the top of their parent object, followed by @flag projects and so on. Projects not associated with any of the workflow tags are sorted after matched projects.
doc.sort_projects("@due, @flag, @hold, @next, @someday, @wait")
doc.save()
print doc.content()
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
this is a rolling document where I can add projects and task I know I can only get done on saturdays
- invite friends over for drinks
- do get hair cut @due
- take the boys to the cinema if it's raining @someday
- take the boys to the park @next
- put up shelves in living room @flag
grocery shop: @due
The super-market closes at 8pm on saturdays
- carrots
- shampoo
- beer
- washing detergent
make coffee: @coffee @flag
- scoop 3 heaped tablespoons of coffee into cafetiere
- fill cafetiere with boiled water from kettle @hot @water
- wait for 3 minutes @wait
- plunge the coffee in the cafetiere
- pour into cup @hot
- drink
ahhhhhhh that's good
tidy the garden: @flag
replace hedge with fence: @due
the hedge at the rear of the garden
- watch a couple of youtube videos about putting up a fence @flag
- ask neighbours if I can work from their garden to fix the fence
buy fence materials:
build bbq: @someday
cut the grass:
- has it stopped raining yet @hold
you can check the weather here: http://forecast.io/
- get the mower out
- put welly boots on
- cut the grass
Archive:
- research the price of fencing online @done(2016-09-15) @project(tidy the garden / replace hedge with fence)
- clear the garden @done(2016-09-15) @project(tidy the garden / cut the grass)
[Searches]: @hide
- do: due @search(/project @due//* union //@due and not @done)
- do: flag @search(/project @flag//* union //@flag and not @done)
- do: projects to tag @search(/project not "@" and not "archive"//*)
- review: next or someday @search(project @next or @someday//* )
- Project List @search(/project not @someday)
- Next and Someday List @search(/project @next or @someday)
Marking a project as done
To mark a project as done, use the done() method:
coffee = doc.get_project("make coffee").done()
print coffee.to_string()
make coffee: @done(2016-09-17 21:49:49)
- scoop 3 heaped tablespoons of coffee into cafetiere
- fill cafetiere with boiled water from kettle @hot @water
- wait for 3 minutes @wait
- plunge the coffee in the cafetiere
- pour into cup @hot
- drink
ahhhhhhh that's good
It’s also possible to mark all descendant items of the object as @done by using .done(“all”).
Adding a project
After sorting all the projects in the document you may have to use the refresh attribute for any project you have in the local namespace to refresh its attributes.
gardenProject.refresh
Now to add a sub-project use the add_project method (this also works on the document object):
# ADD A NEW PROJECT
shedProject = gardenProject.add_project(
title="build a shed",
tags="@someday @garden"
)
researchShedProject = shedProject.add_project(
title="research shed designs",
tags="@research"
)
print doc.content
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
this is a rolling document where I can add projects and task I know I can only get done on saturdays
- invite friends over for drinks
- do get hair cut @due
- take the boys to the cinema if it's raining @someday
- take the boys to the park @next
- put up shelves in living room @flag
grocery shop: @due
The super-market closes at 8pm on saturdays
- carrots
- shampoo
- beer
- washing detergent
make coffee: @coffee @flag
- scoop 3 heaped tablespoons of coffee into cafetiere
- fill cafetiere with boiled water from kettle @hot @water
- wait for 3 minutes @wait
- plunge the coffee in the cafetiere
- pour into cup @hot
- drink
ahhhhhhh that's good
tidy the garden: @flag
replace hedge with fence: @due
the hedge at the rear of the garden
- watch a couple of youtube videos about putting up a fence @flag
- ask neighbours if I can work from their garden to fix the fence
buy fence materials:
build bbq: @someday
cut the grass:
- has it stopped raining yet @hold
you can check the weather here: http://forecast.io/
- get the mower out
- put welly boots on
- cut the grass
build a shed: @someday @garden
research shed designs: @research
Archive:
- research the price of fencing online @done(2016-09-15) @project(tidy the garden / replace hedge with fence)
- clear the garden @done(2016-09-15) @project(tidy the garden / cut the grass)
[Searches]: @hide
- do: due @search(/project @due//* union //@due and not @done)
- do: flag @search(/project @flag//* union //@flag and not @done)
- do: projects to tag @search(/project not "@" and not "archive"//*)
- review: next or someday @search(project @next or @someday//* )
- Project List @search(/project not @someday)
- Next and Someday List @search(/project @next or @someday)
Deleting a project
To delete a project, use the delete() method
doc.get_project("replace hedge with fence").delete()
print doc.content
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
this is a rolling document where I can add projects and task I know I can only get done on saturdays
- invite friends over for drinks
- do get hair cut @due
- take the boys to the cinema if it's raining @someday
- take the boys to the park @next
- put up shelves in living room @flag
grocery shop: @due
The super-market closes at 8pm on saturdays
- carrots
- shampoo
- beer
- washing detergent
make coffee: @done(2016-09-19 10:02:58)
- scoop 3 heaped tablespoons of coffee into cafetiere
- fill cafetiere with boiled water from kettle @hot @water
- wait for 3 minutes @wait
- plunge the coffee in the cafetiere
- pour into cup @hot
- drink
ahhhhhhh that's good
tidy the garden: @flag
build bbq: @someday
cut the grass:
- has it stopped raining yet @hold
you can check the weather here: http://forecast.io/
- get the mower out
- put welly boots on
- cut the grass
build a shed: @someday @garden
research shed designs: @research
Archive:
- research the price of fencing online @done(2016-09-15) @project(tidy the garden / replace hedge with fence)
- clear the garden @done(2016-09-15) @project(tidy the garden / cut the grass)
[Searches]: @hide
- do: due @search(/project @due//* union //@due and not @done)
- do: flag @search(/project @flag//* union //@flag and not @done)
- do: projects to tag @search(/project not "@" and not "archive"//*)
- review: next or someday @search(project @next or @someday//* )
- Project List @search(/project not @someday)
- Next and Someday List @search(/project @next or @someday)
Working with tasks
Listing Tasks
Documents, projects and tasks can all contain tasks. To get a list of the objects tasks, use its tasks attribute.
docTasks = doc.tasks
for t in docTasks:
print t.title
- invite friends over for drinks
- do get hair cut
- take the boys to the cinema if it's raining
- take the boys to the park
- put up shelves in living room
Filtering Tasks by tags
To filter tasks by an associated tag, use the tagged_tasks method:
hotTasks = doc.tagged_tasks("@hot")
for t in hotTasks:
print t.title
- fill cafetiere with boiled water from kettle
- pour into cup
As with the project filter, the task filter is recursive, picking up all tasks within the document with the %22@hot" tag and not just the root level tasks. Again each project and task has a tagged_tasks method to allow for finer grain filtering of tasks.
Sorting tasks by tags
Given a list of workflow tags, you can sort tasks recursively within a taskpaper document, project or task. In the example below tasks tagged with @due rise to the top of their parent object, followed by @flag task and so on. Tasks not associated with any of the workflow tags are sorted after matched tasks.
doc.sort_tasks("@due, @flag, @hold, @next, @someday, @wait")
doc.save()
print doc.content
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
this is a rolling document where I can add projects and task I know I can only get done on saturdays
- do get hair cut @due
- put up shelves in living room @flag
- take the boys to the park @next
- take the boys to the cinema if it's raining @someday
- invite friends over for drinks
grocery shop: @due
The super-market closes at 8pm on saturdays
- carrots
- shampoo
- beer
- washing detergent
make coffee: @done(2016-09-19 13:27:19)
- wait for 3 minutes @wait
- scoop 3 heaped tablespoons of coffee into cafetiere
- fill cafetiere with boiled water from kettle @hot @water
- plunge the coffee in the cafetiere
- pour into cup @hot
- drink
ahhhhhhh that's good
tidy the garden: @flag
build bbq: @someday
cut the grass:
- has it stopped raining yet @hold
you can check the weather here: http://forecast.io/
- get the mower out
- put welly boots on
- cut the grass
build a shed: @someday @garden
research shed designs: @research
Archive:
- research the price of fencing online @done(2016-09-15) @project(tidy the garden / replace hedge with fence)
- clear the garden @done(2016-09-15) @project(tidy the garden / cut the grass)
[Searches]: @hide
- do: due @search(/project @due//* union //@due and not @done)
- do: flag @search(/project @flag//* union //@flag and not @done)
- do: projects to tag @search(/project not "@" and not "archive"//*)
- review: next or someday @search(project @next or @someday//* )
- Project List @search(/project not @someday)
- Next and Someday List @search(/project @next or @someday)
Marking a task as done
To mark a task as done, use the done() method:
coffee.refresh
for t in coffee.tasks:
t.done("all")
print coffee.to_string()
make coffee: @done(2016-09-19 16:05:50)
- wait for 3 minutes @done(2016-09-19 16:05:50)
- scoop 3 heaped tablespoons of coffee into cafetiere @done(2016-09-19 16:05:50)
- fill cafetiere with boiled water from kettle @done(2016-09-19 16:05:50)
- plunge the coffee in the cafetiere @done(2016-09-19 16:05:50)
- pour into cup @done(2016-09-19 16:05:50)
- drink @done(2016-09-19 16:05:50)
ahhhhhhh that's good
Adding a task
A task can be added to a document, project or task object using the add_task method:
aTask = researchShedProject.add_task("look for 5 videos on youtube", "@online")
aTask.add_task("note the urls of the most useful videos")
print researchShedProject.to_string()
research shed designs: @research
- look for 5 videos on youtube @online
- note the urls of the most useful videos
Working with notes
Documents, project and tasks can all have notes assigned to them.
Listing notes
To list the notes for any given object use the notestr() method.
doc.notestr()
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
this is a rolling document where I can add projects and task I know I can only get done on saturdays
print doc.get_project("grocery shop").notestr()
The super-market closes at 8pm on saturdays
Adding a note
Use the add_note() method to add notes to documents, projects and tasks:
newNote = doc.add_note("make sure to make time to do nothing")
print doc.notestr()
I need to review this document every month or so to add new tasks and project, refresh and tidy current projects and clear out stale ones.
this is a rolling document where I can add projects and task I know I can only get done on saturdays
make sure to make time to do nothing
newNote = aTask.add_note(
"good video: https://www.youtube.com/watch?v=nMaGTP82DtI")
print aTask.to_string()
- look for 5 videos on youtube @online
good video: https://www.youtube.com/watch?v=nMaGTP82DtI
- note the urls of the most useful videos
Working with tags
Adding a tag to a project or task
To add (append) a tag to a task or project use the add_tag method.
aTask.add_tag("@due")
print aTask.to_string()
- look for 5 videos on youtube @online @due
good video: https://www.youtube.com/watch?v=nMaGTP82DtI
- note the urls of the most useful videos
researchShedProject.add_tag("@hold")
print researchShedProject.to_string()
research shed designs: @research @hold
- look for 5 videos on youtube @online @due
good video: https://www.youtube.com/watch?v=nMaGTP82DtI
- note the urls of the most useful videos
Setting a project’s or task’s tags
Instead of adding a tag, you can replace all of the tags using the set_tags() method.
researchShedProject.set_tags("@someday")
print researchShedProject.to_string()
research shed designs: @someday
- look for 5 videos on youtube @someday
good video: https://www.youtube.com/watch?v=nMaGTP82DtI
- note the urls of the most useful videos
researchShedProject.set_tags("@someday")
print researchShedProject.to_string()
research shed designs: @someday
- look for 5 videos on youtube @someday
good video: https://www.youtube.com/watch?v=nMaGTP82DtI
- note the urls of the most useful videos
Removing all tags from a project or task
To delete all of the tags, use the set_tags() method with no argument:
researchShedProject.set_tags()
print researchShedProject.to_string()
- look for 5 videos on youtube
good video: https://www.youtube.com/watch?v=nMaGTP82DtI
- note the urls of the most useful videos