Skip to content

Instantly share code, notes, and snippets.

@knewter
Created September 12, 2008 14:41
Show Gist options
  • Save knewter/10450 to your computer and use it in GitHub Desktop.
Save knewter/10450 to your computer and use it in GitHub Desktop.
require 'ostruct'
# This model takes a filename for a CSV file intended as a product
# database. It will match products against existing products,
# creating and deleting products as needed
class ProductCsvImporter
attr_accessor :file, :products, :headers, :contents, :product_groups_with_set_headers, :header_indices
def initialize options
@file = options[:file]
if options[:contents]
@contents = options[:contents]
end
@product_groups_with_set_headers = []
@header_indices = {}
set_products
end
def set_products
if @contents
@products = FasterCSV.parse(@contents)
else
@products = FasterCSV.read(@file)
end
# 2.times { @products.delete_at(0) } # The files start out with two useless lines...or at least they used to.
@headers = @products.delete_at(0).map{|cell| cell.to_s.strip } # this line contains the headers
end
def set_vendor_active_statuses
Vendor.find(:all).each do |v|
v.set_active_status
v.save
end
end
def create_product_from_row rownum
puts "Product ##{rownum}"
puts "-----------------"
if v = get_vendor(rownum)
p = get_product(rownum)
set_vendor(p, v)
set_content(p, rownum)
set_price(p, rownum)
set_item_id(p, rownum)
set_fiber_types(p, rownum)
set_group_num_and_rank(p, rownum)
set_weight_and_min_order(p, rownum)
["Gauge", "Pattern Type", "Needle Size", "Needle Type", "Needle Length", "Pattern Type", "Accessory Type", "Book Type", "Book Author", "Kit Type"].each do |key|
set_key key, p, rownum
end
[
["Needle Length", "Needle Page Attribute Position-Length"],
["Needle Size", "Needle Page Attribute Position-Size"]
].each do |arr|
key = arr[0]
position_key = arr[1]
set_keyed_tag_position(key, position_key, rownum)
end
set_active(p, rownum)
set_product_group_header(p, rownum)
set_related_products(p, rownum)
tag(v, rownum)
tag(p, rownum, true) # true removes old tags (so they can change product categories, duh)
v.save
p.save
return true
end
end
def set_keyed_tag_position key, position_key, rownum
position = prop(position_key, rownum)
key = KeyedTag.find_or_create_by_name_and_key prop(key, rownum), key
key.position = position
key.save
end
def get_vendor(rownum)
v = Vendor.find_or_create_by_code(prop("Manufacturer", rownum))
v.name = prop("Manufacturer", rownum)
v.save
puts "Got vendor: #{v.name}"
v
end
def set_content(product, rownum)
puts "Description: #{prop('Description', rownum)}"
product.description = prop("Description", rownum)
end
def set_related_products(product, rownum)
related_string = prop("Related Products", rownum)
related_item_ids = related_string.split(",").map(&:strip)
from_id = product.id
product.from_product_relationships.map(&:destroy)
related_item_ids.each do |item_id|
p = Product.find_by_item_id item_id
if p
to_id = p.id
ProductRelationship.find_or_create_by_from_id_and_to_id(from_id, to_id)
end
end
end
def get_product(rownum)
p = Product.find_or_create_by_sku(prop("UPC", rownum))
set_name(p, rownum)
puts "Got product: #{p.name}"
p
end
def set_vendor(product, vendor)
product.vendor = vendor
end
def set_product_group_header(product, rownum)
if product.product_group && (!@product_groups_with_set_headers.include?(product.product_group.id))
puts "Product Group header's getting created now..."
@product_groups_with_set_headers << product.product_group.id
c = Content.find_or_create_by_name product.product_group.content_name
attributes = []
attributes_to_check = ["Fiber", "Weight", "Length", "Gauge", "Needle Size", "Project Ideas"]
attributes_to_check.each do |c_att|
value = prop("Group Name #{c_att}", rownum)
attributes << OpenStruct.new(:label => c_att, :value => value) unless value.blank?
end
mab = Markaby::Builder.new
mab.div.product_group_header{
div.attributes{
attributes.each do |attribute|
div.attribute{
div.label attribute.label
div.value attribute.value
}
end
}
div.clear{}
}
c.contents = mab.to_s
c.save
end
end
def set_name(product, rownum)
product.name = prop("Description", rownum)
end
def set_price(product, rownum)
product.price = prop("Retail Price", rownum).gsub(/\$/, "").strip
end
def set_item_id(product, rownum)
product.item_id = prop("Item ID", rownum)
end
def set_active(product, rownum)
product.active = active?(rownum)
end
def set_fiber_types(product, rownum)
fiber_types = []
fiber_types << prop("Fiber Type 1", rownum)
fiber_types << prop("Fiber Type 2", rownum)
fiber_types << prop("Fiber Type 3", rownum)
product.tag_with_key 'Fiber Type', fiber_types.join(', ')
end
def set_key key, product, rownum
product.tag_with_key key, prop(key, rownum)
end
def set_weight_and_min_order(product, rownum)
values = prop("Weight:Min. Order", rownum)
weight, min_order = get_faux_columns(values)
product.weight = weight
product.minimum_quantity = min_order
end
def set_group_num_and_rank(product, rownum)
values = prop("Group Num:Rank", rownum)
group_num, rank = get_faux_columns(values)
unless group_num.nil? || group_num.to_s.blank?
group = ProductGroup.find_or_create_by_name(group_num)
group.tag_with product.tag_list
group.vendor = product.vendor
group.description = prop("Group Name", rownum)
product.product_group = group
group.save
end
product.rank = rank if rank
end
def tag(obj, rownum, remove_old=false)
tags = prop("Category", rownum)
tags += ", " + obj.tag_list unless remove_old
obj.tag_with tags
end
def active?(rownum)
prop("Web", rownum) == "Yes"
end
def create_products
(0...@products.length).each { |i| create_product_from_row i }
set_vendor_active_statuses
end
def get_faux_columns(values)
values.split(":") # We use colons to delimit our faux columns
end
def valid?
false
end
def get_property name, row
begin
index = get_index(name)
if !index
raise "No header found to match the name #{name}"
end
@products[row][index].to_s
rescue
end
end
alias prop get_property
def get_index name
output = @header_indices[name]
if !output
output = @headers.index(name)
@header_indices[name] = output
end
output
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment