Skip to content

Commit

Permalink
after_product_built hook
Browse files Browse the repository at this point in the history
  • Loading branch information
jzw committed Jul 22, 2011
1 parent 476740c commit ad6c61c
Showing 1 changed file with 64 additions and 49 deletions.
113 changes: 64 additions & 49 deletions app/models/product_import.rb
Expand Up @@ -28,15 +28,15 @@ def import_data!
@names_of_products_before_import << product.name
end
log("#{@names_of_products_before_import}")

rows = CSV.read(self.data_file.path)

if IMPORT_PRODUCT_SETTINGS[:first_row_is_headings]
col = get_column_mappings(rows[0])
else
col = IMPORT_PRODUCT_SETTINGS[:column_mappings]
end

log("Importing products for #{self.data_file_file_name} began at #{Time.now}")
rows[IMPORT_PRODUCT_SETTINGS[:rows_to_skip]..-1].each do |row|
product_information = {}
Expand All @@ -48,12 +48,12 @@ def import_data!
col.each do |key, value|
product_information[key] = row[value]
end


#Manually set available_on if it is not already set
product_information[:available_on] = DateTime.now - 1.day if product_information[:available_on].nil?


#Trim whitespace off the beginning and end of row fields
row.each do |r|
next unless r.is_a?(String)
Expand Down Expand Up @@ -91,21 +91,21 @@ def import_data!


private


# create_variant_for
# This method assumes that some form of checking has already been done to
# This method assumes that some form of checking has already been done to
# make sure that we do actually want to create a variant.
# It performs a similar task to a product, but it also must pick up on
# size/color options
def create_variant_for(product, options = {:with => {}})
return if options[:with].nil?
variant = product.variants.new

#Remap the options - oddly enough, Spree's product model has master_price and cost_price, while
#variant has price and cost_price.
options[:with][:price] = options[:with].delete(:master_price)

#First, set the primitive fields on the object (prices, etc.)
options[:with].each do |field, value|
variant.send("#{field}=", value) if variant.respond_to?("#{field}=")
Expand All @@ -121,102 +121,104 @@ def create_variant_for(product, options = {:with => {}})
)
end
end


if variant.valid?
variant.save

#Associate our new variant with any new taxonomies
IMPORT_PRODUCT_SETTINGS[:taxonomy_fields].each do |field|
IMPORT_PRODUCT_SETTINGS[:taxonomy_fields].each do |field|
associate_product_with_taxon(variant.product, field.to_s, options[:with][field.to_sym])
end

#Finally, attach any images that have been specified
IMPORT_PRODUCT_SETTINGS[:image_fields].each do |field|
find_and_attach_image_to(variant, options[:with][field.to_sym])
end

#Log a success message
log("Variant of SKU #{variant.sku} successfully imported.\n")
log("Variant of SKU #{variant.sku} successfully imported.\n")
else
log("A variant could not be imported - here is the information we have:\n" +
"#{pp options[:with]}, :error")
return false
end
end


# create_product_using
# This method performs the meaty bit of the import - taking the parameters for the
# This method performs the meaty bit of the import - taking the parameters for the
# product we have gathered, and creating the product and related objects.
# It also logs throughout the method to try and give some indication of process.
def create_product_using(params_hash)
product = Product.new
#The product is inclined to complain if we just dump all params
# into the product (including images and taxonomies).

#The product is inclined to complain if we just dump all params
# into the product (including images and taxonomies).
# What this does is only assigns values to products if the product accepts that field.
params_hash.each do |field, value|
product.send("#{field}=", value) if product.respond_to?("#{field}=")
end


after_product_built(product, params_hash)

#We can't continue without a valid product here
unless product.valid?
log("A product could not be imported - here is the information we have:\n" +
"#{pp params_hash}, :error")
return false
end

#Just log which product we're processing
log(product.name)

#This should be caught by code in the main import code that checks whether to create
#variants or not. Since that check can be turned off, however, we should double check.
if @names_of_products_before_import.include? product.name
log("#{product.name} is already in the system.\n")
else
#Save the object before creating asssociated objects
product.save


#Associate our new product with any taxonomies that we need to worry about
IMPORT_PRODUCT_SETTINGS[:taxonomy_fields].each do |field|
IMPORT_PRODUCT_SETTINGS[:taxonomy_fields].each do |field|
associate_product_with_taxon(product, field.to_s, params_hash[field.to_sym])
end

#Finally, attach any images that have been specified
IMPORT_PRODUCT_SETTINGS[:image_fields].each do |field|
find_and_attach_image_to(product, params_hash[field.to_sym])
end

if IMPORT_PRODUCT_SETTINGS[:multi_domain_importing] && product.respond_to?(:stores)
begin
store = Store.find(
:first,
:conditions => ["id = ? OR code = ?",
params_hash[IMPORT_PRODUCT_SETTINGS[:store_field]],
:first,
:conditions => ["id = ? OR code = ?",
params_hash[IMPORT_PRODUCT_SETTINGS[:store_field]],
params_hash[IMPORT_PRODUCT_SETTINGS[:store_field]]
]
)

product.stores << store
rescue
log("#{product.name} could not be associated with a store. Ensure that Spree's multi_domain extension is installed and that fields are mapped to the CSV correctly.")
end
end

#Log a success message
log("#{product.name} successfully imported.\n")
end
return true
end

# get_column_mappings
# This method attempts to automatically map headings in the CSV files
# with fields in the product and variant models.
# If the headings of columns are going to be called something other than this,
# or if the files will not have headings, then the manual initializer
# mapping of columns must be used.
# mapping of columns must be used.
# Row is an array of headings for columns - SKU, Master Price, etc.)
# @return a hash of symbol heading => column index pairs
def get_column_mappings(row)
Expand All @@ -226,8 +228,8 @@ def get_column_mappings(row)
end
mappings
end


### MISC HELPERS ####

#Log a message to a file - logs in standard Rails format to logfile set up in the import_products initializer
Expand All @@ -245,11 +247,11 @@ def log(message, severity = :info)
### IMAGE HELPERS ###

# find_and_attach_image_to
# This method attaches images to products. The images may come
# This method attaches images to products. The images may come
# from a local source (i.e. on disk), or they may be online (HTTP/HTTPS).
def find_and_attach_image_to(product_or_variant, filename)
return if filename.blank?

#The image can be fetched from an HTTP or local source - either method returns a Tempfile
file = filename =~ /\Ahttp[s]*:\/\// ? fetch_remote_image(filename) : fetch_local_image(filename)
#An image has an attachment (the image file) and some object which 'views' it
Expand Down Expand Up @@ -295,32 +297,45 @@ def fetch_remote_image(filename)
# associate_product_with_taxon
# This method accepts three formats of taxon hierarchy strings which will
# associate the given products with taxons:
# 1. A string on it's own will will just find or create the taxon and
# 1. A string on it's own will will just find or create the taxon and
# add the product to it. e.g. taxonomy = "Category", taxon_hierarchy = "Tools" will
# add the product to the 'Tools' category.
# 2. A item > item > item structured string will read this like a tree - allowing
# a particular taxon to be picked out
# a particular taxon to be picked out
# 3. An item > item & item > item will work as above, but will associate multiple
# taxons with that product. This form should also work with format 1.
# taxons with that product. This form should also work with format 1.
def associate_product_with_taxon(product, taxonomy, taxon_hierarchy)
return if product.nil? || taxonomy.nil? || taxon_hierarchy.nil?
#Using find_or_create_by_name is more elegant, but our magical params code automatically downcases
#Using find_or_create_by_name is more elegant, but our magical params code automatically downcases
# the taxonomy name, so unless we are using MySQL, this isn't going to work.
taxonomy_name = taxonomy
taxonomy = Taxonomy.find(:first, :conditions => ["lower(name) = ?", taxonomy])
taxonomy = Taxonomy.create(:name => taxonomy_name.capitalize) if taxonomy.nil? && IMPORT_PRODUCT_SETTINGS[:create_missing_taxonomies]

taxon_hierarchy.split(/\s*\&\s*/).each do |hierarchy|
hierarchy = hierarchy.split(/\s*>\s*/)
last_taxon = taxonomy.root
hierarchy.each do |taxon|
last_taxon = last_taxon.children.find_or_create_by_name_and_taxonomy_id(taxon, taxonomy.id)
end

#Spree only needs to know the most detailed taxonomy item
product.taxons << last_taxon unless product.taxons.include?(last_taxon)
end
end
### END TAXON HELPERS ###

# May be implemented via decorator if useful:
#
# ProductImport.class_eval do
#
# private
#
# def after_product_built(product, params_hash)
# # so something with the product
# end
# end
def after_product_built(product, params_hash)
end
end

0 comments on commit ad6c61c

Please sign in to comment.