Created
July 15, 2013 18:28
-
-
Save sawyer/6002225 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This program will take swag.csv and create a batch of USPS | |
# shipping labels in one PDF file for easy printing using | |
# the EasyPost simple shipping API. | |
# | |
# If you have any questions about EasyPost or want help | |
# modifying this script for other carriers or a non-US | |
# source address please email us anytime at contact@easypost.com | |
# | |
# Usage: | |
# easypost_batch_from_csv.rb | |
# -i filename.csv (defaults to swag.csv) (or --in) | |
# -b (or --buy) | |
# | |
# Requirements: | |
# EasyPost account: https://easypost.com | |
# EasyPost ruby gem: gem install easypost | |
require 'easypost' | |
require 'csv' | |
require 'optparse' | |
# command line options and defaults | |
OptionParser.new do |o| | |
o.on('-i FILENAME') { |filename| @csv_filename = filename } | |
o.on('--in FILENAME') { |filename| @csv_filename ||= filename } | |
o.on('-b') { |b| @buy = b } | |
o.on('--buy') { |b| @buy ||= b } | |
o.on('-h') { puts o; exit } | |
o.parse! | |
end | |
@csv_filename = 'swag.csv' unless @csv_filename | |
# EasyPost API key: https://easypost.com/account/keys | |
EasyPost.api_key = '{YOUR-API-KEY}' | |
def generate_batch_label(batch) | |
batch.refresh | |
raise StandardError.new("Cannot generate batch label, all shipments have not been purchased.") if batch.status[:postage_purchased] != batch.shipments.length | |
if batch.label_url | |
puts "Batch Label: #{batch.label}" | |
return true | |
end | |
batch.label({:file_format => 'pdf'}) | |
print "Generating Batch Label" | |
while !batch.label_url | |
batch.refresh | |
sleep(3) | |
print "." | |
end | |
puts "\n#{batch.label_url}" | |
return true | |
end | |
# read csv and remove header row | |
csv = CSV.read(@csv_filename) | |
csv.shift | |
# look for existing batch_id in last row of csv | |
batch_id = csv[csv.length - 1][1] | |
if batch_id && batch_id.include?('batch_') | |
batch = EasyPost::Batch.retrieve(batch_id) | |
# batch completed, print total cost and postage label | |
if batch.status[:postage_purchased] == batch.shipments.length | |
batch_cost = 0 | |
batch.shipments.each do |shipment| | |
shipment.refresh | |
batch_cost += shipment.selected_rate.rate.to_f | |
end | |
puts "Batch Successfull Purchased for Total: $#{batch_cost}" | |
generate_batch_label(batch) | |
exit | |
end | |
else | |
batch = nil | |
end | |
# not ready to buy yet | |
if @buy != true | |
# the batch already exists: output its status | |
if batch | |
# loop through batch shipments and calculate costs | |
batch_cost = 0 | |
est_batch_cost = 0 | |
print "Updating batch rate estimate" | |
batch.shipments.each do |shipment| | |
shipment.get_rates | |
if shipment.batch_status == 'created' | |
est_batch_cost += shipment.lowest_rate('USPS', ['!MediaMail', '!LibraryMail']).rate.to_f | |
else | |
batch_cost += shipment.selected_rate.rate.to_f | |
end | |
print '.' | |
end | |
puts "\nAlready purchased total: $#{batch_cost}" | |
puts "Estimated remaining cost for batch: $#{est_batch_cost}" | |
puts "Run with buy option (-b) to purchase postage." | |
exit | |
end | |
# address you'll be mailing from | |
from_address = EasyPost::Address.create( | |
:company => '{YOUR-COMPANY}', | |
:street1 => '{YOUR-ADDRESS}', | |
:city => '{YOUR-CITY}', | |
:state => '{YOUR-2-CHAR-STATE}', | |
:zip => '{YOUR-POSTAL-ZIP-CODE}', | |
:country => '{YOUR-2-CHAR-COUNTRY}', | |
:phone => '{YOUR-PHONE}', | |
:email => '{YOUR-EMAIL}' | |
) | |
# parcel type and weight | |
parcel = EasyPost::Parcel.create( | |
:predefined_package => 'Flat', # USPS thin flexible package perfect for t-shirt | |
:weight => 10.0 # 10 ounces is a rough guess for 1 t-shirt | |
) | |
# customs forms in case any of your recipients are international | |
customs_item = EasyPost::CustomsItem.create( | |
:description => 'T-shirt', | |
:quantity => 1, | |
:value => 10, | |
:weight => 10, | |
:hs_tariff_number => 610910, # http://hts.usitc.gov/ if you're not mailing t-shirts | |
:origin_country => 'US' | |
) | |
customs_info = EasyPost::CustomsInfo.create( | |
:eel_pfc => 'NOEEI 30.37(a)', # for packages up to $2500 in value | |
:customs_certify => true, | |
:customs_signer => '{YOUR-NAME}', | |
:contents_type => 'gift', | |
:contents_explanation => '', | |
:restriction_type => 'none', | |
:restriction_comments => '', | |
:non_delivery_option => 'return', | |
:customs_items => [customs_item] | |
) | |
# loop over csv and create shipments | |
shipments = [] | |
csv.each do |swag_recipient| | |
# skip empty rows and the final batch_id row | |
next unless swag_recipient[0] && swag_recipient[4] | |
# set destination address | |
to_address = { | |
:name => swag_recipient[0], | |
:street1 => swag_recipient[1], | |
:city => swag_recipient[2], | |
:state => swag_recipient[3], | |
:zip => swag_recipient[4], | |
:country => swag_recipient[5] | |
} | |
shipment = { | |
:to_address => to_address, | |
:from_address => from_address, | |
:parcel => parcel, | |
:reference => swag_recipient[6] | |
} | |
if swag_recipient[5] != 'US' | |
shipment[:customs_info] = customs_info | |
end | |
shipments << shipment | |
end | |
batch = EasyPost::Batch.create({:shipment => shipments}) | |
# output the completed batch_id to the csv's final row | |
# this allows us to query the status of a created batch | |
# without creating a new one each run through the script | |
if batch.id | |
CSV.open(@csv_filename, 'a') do |csv| | |
csv << [] | |
csv << ['easypost_batch_id', batch[:id]] | |
end | |
end | |
puts batch | |
puts "Batch created successfully! Run with buy flag (-b) to purchase postage and generate label." | |
end | |
# buy postage for batch referenced in csv | |
if @buy == true | |
if batch | |
# loop through batch shipments purchasing postage | |
batch_cost = 0 | |
est_batch_cost = 0 | |
unsuccessful_count = 0 | |
batch.shipments.each do |shipment| | |
shipment.get_rates | |
if shipment.batch_status == 'created' | |
begin | |
# buy the cheapest rate from USPS that isn't LibraryMail or MediaMail | |
# you can remove the service restrictions if your shipment qualifies for them | |
shipment.buy(shipment.lowest_rate('USPS', ['!MediaMail', '!LibraryMail'])) | |
batch_cost += shipment.lowest_rate('USPS', ['!MediaMail', '!LibraryMail']).rate.to_f | |
rescue | |
unsuccessful_count += 1 | |
est_batch_cost += shipment.lowest_rate('USPS', ['!MediaMail', '!LibraryMail']).rate.to_f | |
end | |
else | |
batch_cost += shipment.lowest_rate('USPS', ['!MediaMail', '!LibraryMail']).rate.to_f | |
end | |
end | |
# batch purchasing not completed successfully - rerun script | |
if unsuccessful_count > 0 | |
puts "Unsuccessful Purchases: #{unsuccessful_count}" | |
puts "Please re-run script with buy flag to try again.\n" | |
puts "Already Purchased Total: $#{batch_cost}" | |
puts "Estimated Remaining Cost for Batch: $#{est_batch_cost}" | |
else | |
# batch purchased successfully, generate label | |
puts "Batch Successfull Purchased for Total: $#{batch_cost}" | |
generate_batch_label(batch) | |
end | |
else | |
puts "No batch_id found in csv, run script without buy flag first!" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment