iTunes Receipt Email Parser
class ReceiptParser | |
attr_reader :counts, :prices | |
def initialize(currency_symbol) | |
if (currency_symbol == "") then currency_symbol = "£" end | |
@currency_symbol = currency_symbol | |
@all_items = [] | |
@counts = { :ios_apps => 0, :iaps => 0 } | |
@prices = { :ios_apps => 0, :iaps => 0 } | |
end | |
def parse_email(file) | |
lines = slice_out_relevant_section(file) | |
if (lines) then | |
lines.map! { |line| | |
line.gsub!(/\Fr=\r\n/, "Free") | |
line.gsub!(/\=\r\n/, "") | |
line.gsub!(/\=C2=A3/, @currency_symbol) | |
line.gsub!(/\=A3=\r\n/, @currency_symbol) | |
line.gsub!(/\=A3/, @currency_symbol) | |
line | |
} | |
new_lines = [] | |
lines.each_with_index do |line, index| | |
if (line.end_with? @currency_symbol ) then | |
new_lines << (line + lines[index+1]) | |
end | |
if (line =~ / #{@currency_symbol}(\.|\d)+\r\n/) then | |
new_lines << (lines[index-1] + line) | |
end | |
if (line.end_with? "Free") then | |
new_lines << line | |
end | |
end | |
new_lines.select! { |line| | |
(line.include?("Seller:") || line.include?("App")) | |
} | |
new_lines.reject! { |line| | |
line.include?("Mac App") # Filter out all Mac Apps | |
} | |
# Clean up the data | |
new_lines.map! { |line| | |
line.gsub!(/^\d{1,2}\s+/, "") | |
line.gsub!(/^Q\d{4}\s+/, "") | |
line.gsub!(/\ {3,}/, "\t") | |
line.gsub!(/, Seller: /, "\t") | |
if (line.count("\t") == 2) then | |
line.gsub!(/\tFree$/, "\t\tFree") | |
line.gsub!(/\t#{@currency_symbol}/, "\t\t#{@currency_symbol}") | |
end | |
line | |
} | |
new_lines | |
end | |
end | |
def slice_out_relevant_section(file) | |
lines = File.readlines(file) | |
item_index = lines.index { |line| line =~ /^Item/ } | |
if (!item_index) then return end | |
lines.slice!(0, item_index+2) | |
line_index = lines.index { |line| line !~ /^-----------------------------------------------/ } | |
lines.slice!(0, line_index+1) | |
next_line_index = lines.index { |line| line =~ /^-----------------------------------------------/ } | |
lines.slice!(next_line_index, lines.size - next_line_index) | |
return lines | |
end | |
def price_for_part(part) | |
return 0 if (part == "") | |
return part.gsub(/#{@currency_symbol}/, "").to_f | |
end | |
def parse | |
Dir.glob("**/*.eml") do |file| | |
email_items = parse_email(file) | |
@all_items += email_items || [] | |
end | |
@all_items.each do |item| | |
parts = item.split("\t") | |
case parts[2] | |
when "" | |
@counts[:ios_apps] += 1 | |
@prices[:ios_apps] += price_for_part(parts[3]) | |
when "In-App Pu" | |
@counts[:iaps] += 1 | |
@prices[:iaps] += price_for_part(parts[3]) | |
when "In App Pu" | |
@counts[:iaps] += 1 | |
@prices[:iaps] += price_for_part(parts[3]) | |
when "App" | |
@counts[:ios_apps] += 1 | |
@prices[:ios_apps] += price_for_part(parts[3]) | |
when "iOS App" | |
@counts[:ios_apps] += 1 | |
@prices[:ios_apps] += price_for_part(parts[3]) | |
end | |
end | |
File.open("App.tsv", "w") do |file| | |
@all_items.each do |item| | |
file.write item | |
end | |
end | |
end | |
def price_for_type(item_type) | |
format_price(@prices[item_type]) | |
end | |
def format_price(price) | |
"#{@currency_symbol}#{price.round(2)}" | |
end | |
end | |
currency_symbol = [(print 'What currency symbol do you use? (default £) '), gets.rstrip][1] | |
parser = ReceiptParser.new(currency_symbol) | |
parser.parse | |
puts | |
puts "iOS Apps: #{parser.counts[:ios_apps]} (#{parser.price_for_type(:ios_apps)})" | |
puts "In-App Purchases: #{parser.counts[:iaps]} (#{parser.price_for_type(:iaps)})" | |
puts | |
puts "Total Purchases: #{parser.counts.values.inject(:+)} (#{parser.format_price(parser.prices.values.inject(:+))})" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment