Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
# 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
You can’t perform that action at this time.