Estimated reading time: 4 minutes

“Kick me” birthday reminders

Tony Collins asks on the EADS Linux list:

I almost forgot the wife’s birthday last week, not wanting to do that again I’m turning to technology not deeper consideration. So guys, how do you manage it?

My answer is to combine the excellent abook and remind tools in to a really cool solution that nags you about birthdays every time you login.

For those that haven’t used it, abook is a simple ncurses based address book. For some years now I’ve been using abook to manage my personal address book, as it is both small and quite featureful. The address file is in a standard ini format that I can easily import from and export to practically anything with only a small Python script using ConfigParser.

remind is a functional to the point of insanity text-based reminder tool. I use it mainly to remind me of tasks that need completing when I login, although I do have a hook in awesome to display the remind output at other times. The file format is simple and expressive which makes writing importers and exporters easy enough. As an example, I generate my ~/.reminders.d/meetings file from a hCalender page.

abook displaying custom tab

Now the birthday trick is a cool one, abook allows the use of custom fields that you can use for anything you like. I use a custom field to store birthdays in ISO-8601 format as in “1974-04-12”, and enable it by adding the following to my ~/.abook/abookrc:

field birthday = Birthday, date

A little python script is all that is needed to generate a remind input file from our address book now.

#! /usr/bin/python3 -tt

from configparser import ConfigParser, NoOptionError
from datetime import datetime
from operator import attrgetter
from os import path

tmpl = 'REM {} +4 MSG {}’s [grn][_yr_num({})][nrm] Birthday %a'
data = ConfigParser(converters={
    'isodate': lambda s: datetime.strptime(s, '%Y-%m-%d')
})
data.read(path.expanduser('~/.abook/addressbook'))

birthdays = filter(lambda x: 'birthday' in data.options(x),
                   data.sections())

print('# THIS FILE IS AUTOGENERATED FROM ABOOK DATA')

for record in birthdays:
    try:
        name = data.get(record, 'nick')
    except NoOptionError:
        name = data.get(record, 'name')
    birthdate = data.getisodate(record, 'birthday')
    print(tmpl.format(birthdate.strftime('%d %B'), name, birthdate.year))

I use the following make snippet in my ~/Makefile to generate the ~/.reminders.d/birthdays file:

.reminders.d/birthdays: .abook/addressbook .reminders.d/birthdays.py
    $(info - Generating remind’s birthdays file)
    $(word 2, $^) >$@

And finally, we need to tell remind to include our newly created file by editing ~/.reminders:

INCLUDE /home/jay/.reminders.d/birthdays
shell login screenshot

You could trigger a rebuild in your ~/.bashrc before you call rem to see the reminders, so they are always up to date at login. Or, you could be like me and have a post commit hook for git to manage this… because you are keeping your home directory version controlled as a sanity measure I hope!


Authenticate this page by pasting this signature into Keybase.

Have a suggestion or see a typo? Edit this page