org-query icon indicating copy to clipboard operation
org-query copied to clipboard

Create complex but readable skip-functions for org-agenda

[[https://travis-ci.org/remyhonig/org-query][https://travis-ci.org/remyhonig/org-query.svg?branch=master]]

  • Problem

I wanted to use the /Getting Things Done/ productivity method for managing my projects and tasks and the only way I knew how to do this was by copying the relevant functions from [[http://doc.norang.ca/org-mode.html#Projects][Bernt Hansen]] but when I wanted to customize the views I discovered this was error-prone and not very convenient. So I decided to write my own helper functions which I turned into =org-query=.

  • Solution

** Features

*** Selection DSL

To include entries in the agenda blocks you can use queries like this.

#+BEGIN_SRC emacs-lisp ;; Skip the whole tree if the condition is not met (org-query-select "tree"

;; All criteria must be satisfied (and

;; Don't match trees under the Someday / Maybe headline
(not (org-query-parent (org-query-stringmatch "^Someday / Maybe")))

;; The headline should have at least one child with a todo state
(org-query-child (org-query-todo))

;; The headline itself should have the NEXT todo state
(org-query-todo '("NEXT"))))

#+END_SRC

This in contrast to the [[https://www.gnu.org/software/emacs/manual/html_node/org/Special-agenda-views.html][standard skip functions]] included in =orgmode= by default. The problem with those functions is that they cannot be combined very easily or intuitively.

*** GTD style skipping functions

This package includes a selection of functions that help to implement GTD-style project management. See the example for their usage.

** Example

*** Orgmode Tree

#+BEGIN_SRC text

  • Inbox ** TODO do something
  • Areas of Focus ** Finances *** NEXT implement budget **** NEXT map current expenditures **** TODO map current income **** TODO set budgets **** TODO setup automatic transactions *** WAITING change bank **** DONE transfer all funds to new bank account **** DONE close previous bank account **** WAITING verify funds are in new bank account ** Programming *** TODO create org-query v2 **** NEXT make coffee **** TODO checkout repo from github **** WAITING pull request to other project **** Someday / Maybe ***** TODO caching ** Family *** NEXT go to the movies **** DONE choose movie **** TODO buy tickets **** TODO pickup tickets #+END_SRC

*** Agenda View

#+BEGIN_SRC text

Task to refile test: TODO do something

============================================================ Tasks waiting for something test: WAITING verify funds are in new bank account test: WAITING pull request to other project

============================================================ Next tasks in active projects test: NEXT map current expenditures

============================================================ Active projects with a next task test: NEXT implement budget

============================================================ Active projects without next task test: NEXT go to the movies

============================================================ Waiting projects test: WAITING change bank

============================================================ Backlog of active projects test: TODO map current income test: TODO set budgets test: TODO setup automatic transactions test: TODO buy tickets test: TODO pickup tickets #+END_SRC

** Usage

#+name: org-config #+BEGIN_SRC emacs-lisp (defun rmh/agendablock-tasks-waiting () `(tags-todo "/+WAITING|+DEFERRED" ((org-agenda-overriding-header "Tasks waiting for something") (org-tags-match-list-sublevels nil) (org-agenda-skip-function (org-query-select "headline" (not (org-query-gtd-project)))) (org-agenda-todo-ignore-scheduled t) (org-agenda-todo-ignore-deadlines t) )))

(defun rmh/agendablock-next-in-active () `(tags-todo "/+NEXT" ((org-agenda-overriding-header "Next tasks in active projects") (org-agenda-skip-function (org-query-select "headline" (org-query-gtd-active-project-next-task))) (org-tags-match-list-sublevels t) (org-agenda-todo-ignore-scheduled 't) (org-agenda-todo-ignore-deadlines 't) (org-agenda-todo-ignore-with-date 't) (org-agenda-sorting-strategy '(todo-state-down effort-up category-keep)))))

(defun rmh/agendablock-backlog-of-active () `(tags-todo "/+TODO" ((org-agenda-overriding-header "Backlog of active projects") (org-agenda-skip-function (org-query-select "headline" (org-query-gtd-backlog-task))) (org-agenda-todo-ignore-scheduled 't) (org-agenda-todo-ignore-deadlines 't) (org-agenda-todo-ignore-with-date 't) (org-agenda-sorting-strategy '(category-keep)))))

(defun rmh/agendablock-active-projects-without-next () `(tags-todo "/+NEXT" ((org-agenda-overriding-header "Active projects without next task") (org-agenda-skip-function (org-query-select "tree" (org-query-gtd-active-project-stuck))) (org-tags-match-list-sublevels 't) (org-agenda-sorting-strategy '(category-keep)))))

(defun rmh/agendablock-active-projects-with-next () `(tags-todo "/+NEXT" ((org-agenda-overriding-header "Active projects with a next task") (org-agenda-skip-function (org-query-select "tree" (org-query-gtd-active-project-armed))) (org-tags-match-list-sublevels 't) (org-agenda-sorting-strategy '(category-keep)))))

(defun rmh/agendablock-waiting-projects () `(tags-todo "/+WAITING" ((org-agenda-overriding-header "Waiting projects") (org-agenda-skip-function (org-query-select "tree" (org-query-gtd-project))) (org-tags-match-list-sublevels 't) (org-agenda-sorting-strategy '(category-keep)))))

(defun rmh/agendablock-loose-tasks () `(tags-todo "/+TODO" ((org-agenda-overriding-header "Tasks not belonging to a project") (org-agenda-skip-function (org-query-select "headline" (and (org-query-gtd-loose-task) (not (org-is-habit-p))))) (org-agenda-todo-ignore-scheduled 't) (org-agenda-todo-ignore-deadlines 't) (org-agenda-todo-ignore-with-date 't) (org-agenda-sorting-strategy '(category-keep)))))

(defun rmh/agendablock-checklists () `(tags "CHECKLIST" ((org-agenda-overriding-header "Checklists") (org-tags-match-list-sublevels nil))))

(defun rmh/agendablock-inbox () `(tags-todo "LEVEL=2" ((org-agenda-overriding-header "Tasks to refile") (org-agenda-skip-function (org-query-select "tree" (org-query-gtd-refile))) (org-tags-match-list-sublevels nil))))

(setq org-agenda-custom-commands `((" " "Agenda" ((agenda "" ((org-agenda-ndays 1))) ,(rmh/agendablock-inbox) ,(rmh/agendablock-tasks-waiting) ,(rmh/agendablock-next-in-active) ,(rmh/agendablock-active-projects-with-next) ,(rmh/agendablock-active-projects-without-next) ,(rmh/agendablock-waiting-projects) ,(rmh/agendablock-backlog-of-active) ,(rmh/agendablock-checklists)) nil) ("r" "Review Agenda" ((agenda "" ((org-agenda-ndays 1))) ,(rmh/agendablock-inbox) ,(rmh/agendablock-loose-tasks) ,(rmh/agendablock-tasks-waiting) ,(rmh/agendablock-next-in-active) ,(rmh/agendablock-active-projects-with-next) ,(rmh/agendablock-active-projects-without-next) ,(rmh/agendablock-backlog-of-active) ,(rmh/agendablock-checklists)) nil))) #+END_SRC