Skip to content

Instantly share code, notes, and snippets.

@DivineDominion
Last active August 16, 2022 00:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DivineDominion/f7be383776bef5a57a4a812fabc4e8b7 to your computer and use it in GitHub Desktop.
Save DivineDominion/f7be383776bef5a57a4a812fabc4e8b7 to your computer and use it in GitHub Desktop.
Convert Day One (macOS) Journal.txt to Emacs org-mode diary/journal — https://christiantietze.de/posts/2020/10/export-day-one-journal-to-org-mode/
#!/usr/bin/env ruby
#
# convert_dayone_to_org.rb
# Copyright © 2020 Christian Tietze. All rights reserved. Distributed under the MIT License.
#
# Converts a Day One plain text export (Journal.txt) into org-mode journal format. It does
# - Markdown image conversion from ![](thepath.jpg) to [[thepath.jpg]]
# - Extraction of images from the front of an entry; moves it right after the property drawer.
# - Creation of nested year/month/day date sections (see example below)
#
# TODOs:
# - changing the image directory name during import using an option
# - changing the hash-based image file names to date-time format
#
# Usage:
#
# ./convert_dayone_to_org.rb Journal.txt output.org
#
# Output:
#
# * 2020
# ** 2020-03 March
# *** 2020-03-14 Saturday
# **** Here's a diary entry imported from Day One.
# :PROPERTIES:
# :CREATED: [2020-03-14 Sat 14:33]
# :END_PROPERTIES:
# **** And another one from the same day.
# :PROPERTIES:
# :CREATED: [2020-03-14 Sat 19:12]
# :END_PROPERTIES:
# ** 2020-06 June
# *** 2020-06-22 Monday
# **** Year, month, and day sections are created automatically.
# :PROPERTIES:
# :CREATED: [2020-06-22 Mon 08:06]
# :END_PROPERTIES:
require "date"
input, output, *rest = ARGV
if input.nil?
STDERR.puts "Usage: #{__FILE__} DAYONE_INPUT_PATH ORG_OUTPUT_PATH"
STDERR.puts "Missing input file path"
exit 1
elsif output.nil?
STDERR.puts "Usage: #{__FILE__} DAYONE_INPUT_PATH ORG_OUTPUT_PATH"
STDERR.puts "Missing output file path"
exit 1
end
File.open(output, "w") do |out|
# Cached values to make sub-headings
year = nil
month = nil
day = nil
props = {}
File.readlines(input).each do |line|
if /\A\t(?<key>\w+):\t(?<value>.*+)$/ =~ line
# Collect metadata in `props` dictionary
case key
when "Date"
date = DateTime.parse(value)
props["Created"] = date.strftime("[%Y-%m-%d %a %H:%M]")
# Convert date lines to new entries in org
# Output: "* 2020"
if year != date.year
out.puts "* #{date.year}"
year = date.year
month = nil
end
# Output: "** 2020-03 March"
if month != date.month
out.puts "** #{date.strftime("%Y-%m %B")}"
month = date.month
end
# Output: "*** 2020-03-12 Thursday"
this_day = date.strftime("%Y-%m-%d %A")
if day != this_day
out.puts "*** #{this_day}"
end
else
props[key] = value
end
elsif !props.empty?
# Produce entry title and metadata
if line.strip.empty?
# Skip empty line separator after metadata
else
# Add entry heading, handling leading entry images
cached_image = nil
if /\A!\[]\((?<path>.+)\)(?<rest>.*)$/ =~ line
cached_image = "[[./#{path}]]"
out.puts "**** #{rest}"
else
out.puts "**** #{line}"
end
# Append property drawer
out.puts ":PROPERTIES:"
out.puts(props.map { |key, value| ":" + key.upcase.to_s + ": " + value.to_s }
.join("\n"))
out.puts ":END_PROPERTIES:"
props = {}
if !cached_image.nil?
out.puts ""
out.puts cached_image
end
end
else
line = line.gsub(/!\[\]\((.+)\)/) { "[[./#{$1}]]" }
out.puts line
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment