Omniboard tutorial: dates and filters

Published
11 March 2016
Tagged
Thanks, Omnifocus!

Thanks, Omnifocus!

I see the official Omnifocus twitter account linked to my Omniboard post recently, with the normal spike in visitors. Hello, everyone! I'm glad to see you like these things I make.

Reader Sean asks:

Is there any way to specify criteria such as due date or defer date? I do things a bit differently and would like to have the due dates be what control my active column.

There is! I didn't think much about using dates to filter or sort in my columns, but the flexible nature of Omniboard's filtering and sorting criteria means that it's really simple to make something like this work. Of course, it's easy for me to say that, since I'm pretty intimately familiar with exactly how Rubyfocus interacts with Omnifocus' objects. In this post, I'll show you a couple of handy date-related methods you can use to corral your projects and tasks.

First, a couple of side notes:

Hours or days?

Omnifocus stores defer, due, and completion times as just that: times. When it comes to filtering projects and tasks, though, sometimes you want to talk in terms of days. For example: it's 9am on Monday. Would you consider a task that you deferred until noon on Tuesday as "starting in one day"? What about one deferred to midnight on Tuesday morning?

Most of my considerations regarding tasks are date-based rather than time-based: that is, a task starting tomorrow is one day away, regardless of when tomorrow it starts. If we start looking at time-based filters in Omniboard, these considerations will come up, so it's a good idea to think about what sort of filter you want to use, and stick to that. In the examples below, I'll look at how you can do both time-based and date-based filters.

Projects or tasks?

OmniFocus treats projects as a subclass of tasks. This means that an OmniFocus project has all the properties of a task, including defer, due, and completion times. So what's the difference between

  • a project which is deferred until tomorrow, and
  • a project whose tasks are all deferred until tomorrow?

That's up to you, but it's good to know the difference between the two. Again, I'll make sure to have a look at both these cases in the examples below.


Anyway, with those corner cases out of the way, onto the good stuff.

Case study 1: Completed in the last three days

If you don't archive your projects and tasks regularly, they will start to clutter up your "Completed" column. If you would only prefer to see what you've completed recently, you can very easily filter by completion date:

three_days_ago = Time.now - (60 * 60 * 24 * 3)

Omniboard::Column.new "Completed" do
	conditions do |p|
		p.completed? && p.completed >= three_days_ago
	end
end

Any task completed more than three days[1] ago will drop off your kanban board, never to be seen again. If you wanted to, you could even sort the projects in your completed column:

	sort{ |x,y| y.completed <=> x.completed }

This will sort the column in reverse-date order, so the project completed most recently appears at the top. When you complete a project it will appear at the top of the list, and over time it will slowly make its way down the list until it drops off the bottom.

Case study 2: Due within the next week

Let's say you'd like to make a column that contains incomplete projects due within the next week. Instead of the previous case study, where we were happy comparing times, we actually want to allow and group these based on the day they're due. To filter those tasks due within one week, we use a similar trick to the one we used above:

within_one_week = Date.today + 7

Omniboard::Column.new "Due soon" do
	conditions do |p|
		p.active? && p.due && p.due.to_date <= within_one_week
	end
end

We have three conditions here:

  • The project must be active: that is, we don't care about on hold, dropped, or completed projects.
  • The project must have a due date. If the project has no due date, this value is nil, and comparisons with nil on one side tend to throw errors.
  • Finally, the project must have a due date within one week (seven days) of today.

If you have a lot of projects due soon, you can even group them by the day they're due on:

	group_by do |p|
		# Assume project has a due date
		days_until_due = (p.due.to_date - Date.today).to_i
		"Due in #{days_until_due} day#{days_until_due == 1 ? "" : "s"}"
	end

In the absense of any group sorthing algorithm, Omniboard will sort these groups alphabetically, which is fine by me.

Note that as it stands, Omniboard allows projects to be in multiple columns as long as they fulfil the conditions on each column. This means that if you have any "low priority" columns on your board, you may need to add a line to the filter that prevents these tasks from showing up there as well. For example, you might have:

within_one_week = Date.today + 7

Omniboard::Column.new "Low priority" do
	conditions do |p|
		p.active? && !(p.due && p.due.to_date <= within_one_week)
	end
end

Case study 3: Starting tomorrow

I often find myself confronted by a task or project that I'd really like to work on, but which would benefit from my sleeping on it. It's handy enough that I have an Applescript in my Omnifocus toolbar to set the defer date of the currently selected object to tomorrow.

I'll usually just defer the next action of a task, but it's possible that sometimes I'll defer the whole project. Wouldn't it be nice if I could see, at a glance, what tasks and projects I've deferred until tomorrow?

The basic outline of such a column should be pretty obvious:

tomorrow = Date.today + 1

Omniboard::Column.new "Starting tomorrow" do
	conditions do |p|
		p.active? && p.start && p.start.to_date == tomorrow
	end
end

This will catch all active projects which have been deferred until tomorrow. What about a project where we've just set all of its tasks to start tomorrow?

Rubyfocus Tasks and Projects have a variety of convenience methods you can use when fetching their subtasks. These include:

  • Task#tasks: Returns an array of every atomic task contained by this task. Note that tasks that have subtasks are not included: that is, I assume that these tasks will be completed when all their sub-tasks are completed. Here's an illustration of what's included in Task#tasks: !The tasks outlined in red are included in , as they have no subtasks.
  • Task#immediate_tasks: Returns an array of all tasks which are immediate subtasks of this task (including those which have their own subtasks).
  • Task#incomplete_tasks: Returns an array of all atomic tasks which are not marked complete.
  • Task#next_tasks: Returns an array of all incomplete tasks which are not blocked (i.e. which do not need another task to be completed before they can be attempted).
  • Task#actionable_tasks: Returns an array of next tasks which are not deferred to some date in the future.

Obviously you can re-create most of these by running various filters on Task#tasks, but sometimes it's nice to have a quick shortcut to get every task you could attempt right now if you wanted to. In this case, we can use actionable_tasks and next_tasks to work out if a project is delayed until tomorrow:

p.actionable_tasks.empty? &&
	p.next_tasks.any?{ |t| t.start && t.start.to_date == tomorrow }

This will return true if the project:

  • has no actionable (i.e. incomplete, non-blocked, non-deferred) tasks and
  • has at least one next (i.e. incomplete, non-blocked) task which has been deferred until tomorrow.

So our whole column looks like this:

tomorrow = Date.today + 1

Omniboard::Column.new "Starting tomorrow" do
	conditions do |p|
		(p.active? && p.start && p.start.to_date == tomorrow) ||
		(p.actionable_tasks.empty? &&
			p.next_tasks.any?{ |t| t.start && t.start.to_date == tomorrow })
	end
end

In the previous case studies, we've seen how to use Task#start, Task#due, and Task.completed to filter, group, and sort projects within columns. In addition, we've looked at the difference between project and task start dates. With those tools, you should be able to set up your entire Kanban board to take advantage of your projects' set dates.

How do you use your kanban board? Is there anything you wish it could do?


  1. To be precise, 259,200 seconds. ↩︎