Monthly Reports with Org-Mode
Like a lot of people, I have to submit a monthly “bullet” report, listing the things I’ve done in the previous month.
Since I use Org-Mode for planning, scheduling, and organizing tool (or rather: I tend to throw a bunch of notes into a file and tell this love child of a day planner and a wiki to tell me what I should do next), I figured I should use that.
I could use the timeline feature (C-c a L), but that only works for the current buffer, and I want a report that covers all buffers, just like the agenda.
What I’ve done in the past is to use C-c a a to get the agenda view, go back a month, toggle displaying completed/archived/whatever items, and go through that to make my bullet list.
But I finally got around to encapsulating that into a single M-x bullet command:
; Make it easier to generate bullets for $BOSS
(defvar bullet-entry-types
'(:closed)
"Org-mode agenda types that we want to see in the monthly bullet report
See `org-agenda-entry-types'."
)
(defun bullets ()
"Show a list of achievements for the past month, for monthly reports.
Uses `org-agenda'.
"
(interactive)
(require 'org-agenda)
; All we're doing here, really, is calling `org-agenda' with
; arguments giving a start date and a number of days. But to do
; that, we need to figure out
; - the date of the first of last month
; - the number of days in last month
(let* ((now (current-time))
; Figure out when last month was. Assuming that I run this
; close to the beginning of a month, then `now' minus two
; weeks was some time in the previous month. We can use that
; to extract the year and month that we're interested in.
(2weeks-ago
(time-subtract now
(days-to-time 14)))
; We'll also need to know when the first of this month was,
; to find out how long last month was. If today is the 12th
; of the month, then the first of the month was `now' minus
; 11 days.
(1st-of-this-month
(time-subtract now
(days-to-time
(- (nth 3 (decode-time now))
1))))
; Ditto to find the first of last month.
(1st-of-last-month
(time-subtract 2weeks-ago
(days-to-time
(- (nth 3 (decode-time 2weeks-ago))
1))))
; The length of last month is the difference (in days)
; between the first of last month, and the first of this
; month.
(len-last-month
(time-to-number-of-days
(time-subtract 1st-of-this-month
1st-of-last-month)))
(start-date (decode-time 1st-of-last-month))
(start-year (nth 5 start-date)) ; Year number
(start-mon (nth 4 start-date)) ; Month number
; Restrict the agenda to only those types of entries we're
; interested in. I think this takes advantage of dynamic
; scoping, which is normally an abomination unto the lord,
; but is useful here.
(org-agenda-entry-types bullet-entry-types)
)
; Create an agenda with the stuff we've prepared above
(org-agenda-list nil
(format "%04d-%02d-01"
start-year
start-mon)
len-last-month)
))
I hope this proves useful to someone.