Nathan Grigg

Day One Export

Here is a Python script to export Day One journal entries into any text-based format (such as html, markdown, plain text, or Latex).

pip  install  dayone_export
View on github
Read the Docs

Note: the Day One Mac app can export to plain text, and in the future the iOS apps will export to PDF. This is for people who need or want extra customization options.

My Day One journal

I have always been a fan of the Day One Mac and iOS Apps. I was keeping an electronic journal before Day One came along (in Latex, if you must know), but I quickly changed my ways. Day One makes things easier and more fun.

I switched to Day One quickly, but not without an abundance of caution. The last thing I want is for my journal to be unreadable 5 years from now. I was reassured to see that Day One stores each journal entry as a plist file. Furthermore, the entry itself is 100% certified organic plain text. The rest of the plist is just metadata.

As time passed, Day One added features. Most recently, they added the ability to include photos, location information, and weather. All this talk about new features scared me, because more features almost always means more complication. In this case, there was a good chance the extra complication would make my data less future-proof. But in the end, there was no need for me to worry. These guys are good.

A Day One entry is still a simple plist file. The entry itself is still plain text. Location and weather are just more metadata. Best of all, photos can be included with but not inserted into an entry. There is no need for any markup within the journal entry saying “this is where the photo goes.” You don’t have to base64-encode or link to an external file or any of the other awful things word processors have done when dealing with images. A photo is just another piece of metadata that says “This photo goes with that journal entry.”

A Day One export tool

I put together a Python script to export my journal entries. It uses a Jinja template to combine my Day One entries into a single file of whatever format I want. A simple template and a few lines of css turned my journal into enough html to fill 80 printed pages:

I wrote it for myself, but I thought others might find it useful, so I have posted it on github.

Reading plist files in Python

This part is easy. Just import plistlib and do


Using Jinja templates in Python

This is more complicated, and the main thing I learned by making this script. The following script fills in a Jinja template:

 1: from jinja2 import Environment, FileSystemLoader
 2: import os

 4: # j contains list of entries, template is the name of the template file,
 5: # and markdown_filter is a function which converts markdown to html
 6: path, base = os.path.split(template)
 7: env = Environment(loader=FileSystemLoader(path), trim_blocks=True)
 8: env.filters['markdown'] = markdown_filter
 9: template = env.get_template(base)
10: output = template.render(journal=j)

Loading the template file was a little confusing. First, you set up the environment with a FileSystemLoader in line 7, which takes one or more search paths as an argument. Then the get_template command in line 9 searches through all of the paths for the template you are looking for. I could not find a way to just load an arbitrary template file, hence the awkward workaround in line 6.

Writing Jinja templates

If you’ve ever used Liquid or any other templating language, Jinja looks pretty familiar. Here is a basic template to produce an html version of my journal:

 1: <!DOCTYPE html>
 2: <html>
 3: <head>
 4:     <title>Journal Entries</title>
 5:     <link rel="stylesheet" href="style.css" type="text/css">
 6:     <meta charset="UTF-8" />
 7: </head>
 8: <body>
 9: <h1 class="page-title">Journal Entries</h1>
10: {% for entry in journal %}
11: <article class="entry">
12:     <h1 class="entry-title">{{ entry['Date'].strftime('%A, %b %e, %Y') }}</h1>
13:     <p class="location time">
14:         {% if 'Location' in entry %}
15:         {{"United States") }},
16:         {% endif %}
17:         {{ entry['Date'].strftime("%-I:%M %p %Z") }}
18:     </p>
19:     <div class="entry-text">
20:         {% if 'Photo' in entry %}
21:         <img class="entry-photo" src="{{ entry['Photo']  }}"/>
22:         {% endif %}
23:         {{ entry['Text'] | markdown }}
24:     </div>
25: </article>
26: {% endfor %}
27: </body>
28: </html>

Control statements go inside {%...%} blocks, like the for loop from line 10 to line 26 which loops over all the entries. Recall that the journal variable is passed to the template from Python (in line 10 of the Python code above).

Variables are inserted into the document using {{...}} blocks, like the date on line 12 and the photo on line 21. Jinja allows a lot of Python into variable blocks (more than Liquid allows Ruby), which means I can call strftime to format the date in the way that I want. You see more of this on line 14 with the in in the if block, and on line 15 which uses the place method of the entry object.

Line 23 shows how to apply a filter to a variable. The text of the variable passed through the markdown filter. This is a custom filter defined in my Python script, but there are also several built-in filters.