Fri, 09 Nov 2012

Org-Mode and Project Overviews

I recently started a new job and my new manager wanted me to give him a periodically-updated task list showing what I was working on, its progress, and any deadlines I had. I like Emacs' Org Mode, so I set out to set up something in Org Mode that worked for both of us. This is my story.

In general, I want to have a main directory for my files and, within that directory, use a separate file for each distinct project I work on. My manager wanted to easily see project progress via milestones, so each level-1 heading represents a milestone, while the deeper headings are more fluid and are more for my benefit than his. It was pretty easy to set up HTML publishing from the Org file directory to a web server, so my manager could browse through the details of my org files.

My manager also, however, wanted to see an overview page with sections for current, future, and recently-past projects, with detail lines for the milestones on current projects. That proved to be the trickiest thing to implement: since I dislike repeating myself, I wanted as much of the "current project status" part of the overview to be automatically generated from the individual project files, including any milestone deadlines but ignoring all of the more minor details.

Org Mode is big, so I spent a while reading through the manual about custom agenda views and such until I stumbled on the C-c / d key sequence, which folds all the file's subheadings and displays any deadlines on the level-1 headings. In combination with C-c C-e v Spc, which exports only the visible portions of the current buffer to a new org-mode buffer, I was able to create an org-mode formatted project overview. Thus, my general overview page (named index.org so it becomes index.html during publishing) looks something like this:

* Active Projects

** [[file:project-1.org][Project 1 Description]]
   DEADLINE: <2012-11-16 Fri>
#+INCLUDE: "project-1.org_overview" :minlevel 2

** [[file:project-2.org][Project 2 Description]]
   DEADLINE: <2012-11-30 Fri>
#+INCLUDE: "project-2.org_overview" :minlevel 2

* Future Projects

** [[file:project-a.org][Project A Description]]
** [[file:project-b.org][Project B Description]]

* Recently Completed Projects

** [2012-11-01] [[file:project-x.org][Project X Description]]

I added a few CSS tweaks at the top of the index.org file to better fit its purpose. Mostly, they just put related things closer together and deemphasize some of the extra information on the page:

#+OPTIONS:   H:2 num:nil toc:nil tasks:t
#+STYLE: <style>
#+STYLE: .outline-3 h3 {
#+STYLE:   margin-bottom: 0;
#+STYLE: }
#+STYLE: .outline-text-3 p {
#+STYLE:   margin: 0;
#+STYLE: }
#+STYLE: .outline-4 {
#+STYLE:   line-height: 0.5em;
#+STYLE: }
#+STYLE: .outline-text-4 {
#+STYLE:   font-size: 0.75em;
#+STYLE: }
#+STYLE: .outline-4 h4 {
#+STYLE:   margin-bottom: 0;
#+STYLE: }
#+STYLE: </style>

I also wrote the following two elisp functions. The first one generates the overview file for a given buffer, while the second looks through a buffer for included overview files and regenerates all of them.

(defun org-export-overview (file)
  (save-window-excursion
    (find-file file)
    (org-check-deadlines org-deadline-warning-days)
    (org-export-visible ?\  0) ; calls switch-to-buffer-other-window
    (write-file (concat file "_overview"))
    (kill-buffer)))

(defun org-export-overviews-for (buffer-or-name)
  (let ((plan-dir (file-name-directory (buffer-file-name (get-buffer buffer-or-name)))))
    (with-current-buffer buffer-or-name
      (save-excursion
        (goto-char 1)
        (while (re-search-forward "^#\\+INCLUDE: +\"\\(.*\\)_overview\"" nil t)
          (org-export-overview (concat plan-dir (match-string 1))))))))

Finally, I wrote a simple function that goes through all the stuff I need to publish my org files and I call that hourly, so my manager can see my current project status at pretty much any time with no manual steps on my part:

(defun pmg-org-publish ()
  (org-save-all-org-buffers)
  (org-export-overviews-for "index.org")
  (org-publish-all))

(run-at-time "00:45" 3600 'pmg-org-publish)

All I need to do is periodically update index.org as I add or complete projects, and the rest of my work is done, as it normally is, in the project files. Thanks, Org Mode!


Phil! Gold