Logging with Day One, geek style

[Tweet : ADN : nvALT]

I have long kept a journal–more precisely, a log–using VoodooPad with the Scratchpad scripts by Ian Beck. It’s been a great system, but after years of usage it’s started to become a bit cumbersome. VoodooPad can handle the load, but running the custom scripts is inconvenient on a document with thousands of pages. In the interest of trying new things (and fiddling away some time this evening), I decided to try switching the system over to Day One.

Day One is a gorgeous app for keeping a personal journal. I discovered it, if I recall correctly, via David Sparks. It has iOS companion apps and iCloud syncing. It also has excellent search capabilities, likes MultiMarkdown and can export my entries to plain text at any time. I like it because it’s good-looking and concise; just what I need, with no feature bloat. Plus, it’s specifically designed to do exactly what I want: keep a timestamped journal of what I’m working on, have accomplished or have just discovered in my digital travels.

Day One already has a quick entry palette in the menubar. It also has a command line interface (/usr/local/bin/dayone)1 which provides some geeky options (try dayone in Terminal) and the flexibility needed to replace my current logging system. You can create entries quickly with either method, but I wanted just a little bit more out of it. I built a quick script which allows a basic syntax for starring entries and defining dates (using natural language) inline in the entry itself. It can be used from the command line, from LaunchBar (or similar) and can be incorporated into just about any scriptable workflow.

The script

The natural language portion of the script is built on the “Chronic” Ruby gem, so running this script as is requires that you have that installed. If you don’t have it available, just run gem install chronic to add the gem. If you run into errors doing that, try sudo gem install chronic and provide your system password. Here’s the script, complete with some explanation in the comments.

#!/usr/bin/ruby

# logtodayone.rb
# Brett Terpstra (http://brettterpstra.com)
# Use and modify freely, attribution appreciated
# 
# This script works with the Day One[1] command line utility
# It parses an input string for an exclamation point prefix to mark starred
# and/or a [date string] at the beginning to parse natural language dates
# 
# Requirements:
# Chronic ruby gem
#
# Example usage:
# logtodayone.rb "! This is a starred entry."
# logtodayone.rb "[yesterday 3pm] Something I did yesterday at 3:00PM"
# logtodayone.rb "! [-2 1:30am] A starred entry about something I did two days ago"

require 'rubygems'
require 'chronic' # (`gem install chronic`)

if ARGV.length > 0
  input = ARGV.join(" ").strip
else
  print "Log entry: "
  input = gets.strip
end

# If the input starts with an exclamation point, make it starred
starred = input =~ /^!/ ? "true" : "false"
# remove the bang from the input string
input = input.gsub(/^!\s*/,'')

# if there's a [date] specified, parse it
if input =~ /^\[(.*?)\]/
  datestring = $1
  # if the date starts with -X, assume it means X days ago
  if datestring =~ /^\-(\d+)/
    datestring.sub!(/\-(\d+)/,"\\1 days ago ")
  end
  # Replace a single 'y' within the date brackets with "Yesterday" for parsing
  datestring.sub!(/\by\b/,'yesterday')
  # Parse the resulting date string with Chronic
  d = Chronic.parse(datestring, {:context => :past, :ambiguous_time_range => 8})
  d = DateTime.now if d.nil?
else
  # if no [date] specified, make it right now
  d = DateTime.now
end
date = d.strftime("%m/%d/%Y %l:%M%p") # dayone formatted
input = input.gsub(/^\[.*?\]\s*/,'') # remove [date] from input

%x{echo "#{input}"|/usr/local/bin/dayone -d="#{date}" -s=#{starred} new}

Some usage tips

You can star an entry by beginning it with an exclamation point. I’m not sure how I’ll use stars yet, but I figured the option was there, so I’d add a syntax for it.

I often end up logging things after the fact but before I forget what I was doing, so it’s important for me to be able to enter an approximate date and time for the sake of organization. Date strings are entered in square brackets at the beginning (or after an exclamation point) of the entry. The natural language accepts basic strings such as “yesterday 3pm” or “noon” and converts them to Day One-compatible dates. It also accepts “-X” operators to specify a number of days ago (I’m assuming I won’t be logging entries in the future). “y” by itself will be converted to “yesterday.” A typical entry for me would look like “[y 11:45pm] working on silly scripts for logging to Day One.” An entry with no exclamation point and no date brackets is just read as a normal entry at the current time.

I’m using generic @tags–a habit carried over from my VoodooPad scratchpad–to make my entries easily searchable by topic or project. Beyond that, I’m just relying on Day One’s built-in chronological organization and full-text search.

The script is all fine and good, but it’s not much use if it’s not convenient to access. Here are a few of the ways I’m making it universally accessible in my workflow.

Bash alias

First things first, lets make it really easy to log entries from the command line. A simple alias in your ~/.bash_profile shortens things up:

alias log="~/scripts/logtodayone.rb"

Now you can make an entry with log "the thing I was just doing".

LaunchBar Action

Given that few of us are always on the command line, it would be nice to access the script easily from other utility apps. I personally use LaunchBar, but you can create similar scripts for Alfred, Quicksilver, etc. If you use LaunchBar, you can just save the three-line script below as Log to Day One.scpt in ~/Library/Application Support/LaunchBar/Actions:

on handle_string(message)
  do shell script "/Users/ttscoff/scripts/logtodayone.rb \"" & message & "\""
end handle_string

Now, just pop up LaunchBar and type ldo or similar to select the action, then type space to get a text field where you can type your log entry. The script syntax applies in full, so start with an exclamation point to star the entry, and use natural language date syntax between square brackets to specify a date, if needed.

Git commit wrapper

I do something similar to this with an nvALT note, so I thought I’d try it with Day One for a while. It’s a basic bash function to copy a note from a git commit into my daily log. It just wraps git -am (commit all, message), so more complex commit commands won’t work. It covers 90% of the commits I’d actually want in my journal, though.

function cdo(){
  msg=$*
  path=$(pwd)
  ~/scripts/logtodayone.rb "@${path##*/} $msg"
  git commit -am "$msg"
}

Update: I made the git wrapper significantly smarter, thanks to a little assist from @nnutter. It basically parses up your git tree for the actual repo name instead of using the directory name you’re currently in.

# experimental wrapper for git to log commits to Day One
# lots of credit to http://nnutter.com/2012/01/git-todo/
function cdo(){
  msg=$*
  GIT_DIR=$(git rev-parse --git-dir)
  if ! (( $? )); then
      GIT_DIR=$(echo "$GIT_DIR" | awk -F/ '{nlast = NF -1;print $nlast}')
      if [ -z "$GIT_DIR" ]; then
				path=$(pwd)
				GIT_DIR=${path##*/}
      fi
			~/scripts/logtodayone.rb "@$GIT_DIR $msg"
  fi

  git commit -am "$msg"
}

So that’s my fiddling for the evening. Back to doing something more useful (like preparing for my Macworld/iWorld talk with Merlin Mann and David Sparks).

  1. I forgot this originally, but you need to symlink the dayone CLI from the app bundle to your /usr/local/bin directory (the CLI now has to be downloaded from the Day One site). As mentioned in the comments, more information can be found in the faq.