Skip to content

Commit

Permalink
Merge pull request #37 from redglory/master
Browse files Browse the repository at this point in the history
Added method to load variant images and 'variant_cost_price' method
  • Loading branch information
tom statter committed Mar 5, 2015
2 parents d6e2718 + 52953cd commit 936b96e
Show file tree
Hide file tree
Showing 2 changed files with 289 additions and 6 deletions.
291 changes: 286 additions & 5 deletions lib/loaders/spree/product_loader.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright:: (c) Autotelik Media Ltd 2010
# Copyright:: (c) Autotelik Media Ltd 2010
# Author :: Tom Statter
# Date :: Aug 2010
# License:: MIT ?
Expand Down Expand Up @@ -42,8 +42,8 @@ def perform_load( file_name, opts = {} )

# In >= 1.1.0 Image moved to master Variant from Product so no association called Images on Product anymore

# Non Product/database fields we can still process
@we_can_process_these_anyway = ['images', "variant_price", "variant_sku", "stock_items"]
# Non Product/database fields we can still process
@we_can_process_these_anyway = ["images","variant_sku","variant_cost_price","variant_price","variant_images","stock_items"]

# In >= 1.3.0 price moved to master Variant from Product so no association called Price on Product anymore
# taking care of it here, means users can still simply just include a price column
Expand Down Expand Up @@ -89,9 +89,15 @@ def process(method_detail, value)

add_properties

# This loads images to Product or Product Master Variant depending on Spree version
elsif(current_method_detail.operator?('images') && current_value)

add_images( load_object.master )

# This loads images to Product Variants
elsif(current_method_detail.operator?('variant_images') && current_value)

add_variant_images(current_value)

elsif(current_method_detail.operator?('variant_price') && current_value)

Expand All @@ -113,6 +119,27 @@ def process(method_detail, value)
else
super
end

elsif(current_method_detail.operator?('variant_cost_price') && current_value)

if(@load_object.variants.size > 0)

if(current_value.to_s.include?(Delimiters::multi_assoc_delim))

# Check if we processed Option Types and assign per option
values = current_value.to_s.split(Delimiters::multi_assoc_delim)

if(@load_object.variants.size == values.size)
@load_object.variants.each_with_index {|v, i| v.cost_price = values[i].to_f }
@load_object.save
else
puts "WARNING: Cost Price entries did not match number of Variants - None Set"
end
end

else
super
end

elsif(current_method_detail.operator?('variant_sku') && current_value)

Expand Down Expand Up @@ -269,7 +296,7 @@ def add_options_variants

i = @load_object.variants.size + 1

variant = @load_object.variants.create( :sku => "#{load_object.sku}_#{i}", :price => load_object.price, :weight => load_object.weight, :height => load_object.height, :width => load_object.width, :depth => load_object.depth)
variant = @load_object.variants.create( :sku => "#{load_object.sku}_#{i}", :price => load_object.price, :weight => load_object.weight, :height => load_object.height, :width => load_object.width, :depth => load_object.depth, :tax_category_id => load_object.tax_category_id)

variant.option_values << ov_list if(variant)
end
Expand Down Expand Up @@ -378,7 +405,7 @@ def add_taxons
end

end

def add_variants_stock(current_value)

save_if_new
Expand Down Expand Up @@ -458,6 +485,260 @@ def add_variants_stock(current_value)
end
end

def add_variant_images(current_value)

save_if_new

# do we have Variants?
if(@load_object.variants.size > 0)

logger.info "[VARIANT IMAGES] - number of variants to process #{@load_object.variants.size}"

if(current_value.to_s.include?(Delimiters::multi_assoc_delim))
# Check if we've already processed Variants and assign count per variant
values = current_value.to_s.split(Delimiters::multi_assoc_delim)
# variants and variant_images number match?
raise "WARNING: Variant Images entries did not match number of Variants - None Set" unless (@load_object.variants.size == values.size)
end

variants = @load_object.variants # just for readability and logic

logger.info "Variants: #{@load_object.variants.inspect}"

# we expect to get corresponding images for every variant (might have more than one image for each variant!)
variants_images_list = get_each_assoc

variants_images_list.each_with_index do |variant_images, i|

if(variant_images.to_s.include?(Delimiters::multi_value_delim))
# multiple images
images = variant_images.to_s.split(Delimiters::multi_value_delim)
else
# single image
images = []
images << variant_images
end

logger.info "Setting #{images.count} images for variant #{variants[i].name}..."

# reset variant images to attach to variant
var_images = []

# Image processing...
logger.debug "Images to process: #{images.inspect} for variant #{variants[i].name}"
images.each do |image|
@spree_uri_regexp ||= Regexp::new('(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:\/~\+#]*[\w\-\@?^=%&amp;\/~\+#])?' )

if(image.match(@spree_uri_regexp))

uri, attributes = image.split(Delimiters::attribute_list_start)

uri.strip!

logger.info("Processing IMAGE from URI [#{uri.inspect}]")

if(attributes)
#TODO move to ColumnPacker unpack ?
attributes = attributes.split(', ').map{|h| h1,h2 = h.split('=>'); {h1.strip! => h2.strip!}}.reduce(:merge)
logger.debug("IMAGE has additional attributes #{attributes.inspect}")
else
attributes = {} # will blow things up later if we pass nil where {} expected
end

agent = Mechanize.new

image = begin
agent.get(uri)
rescue => e
puts "ERROR: Failed to fetch image from URL #{uri}", e.message
raise DataShift::BadUri.new("Failed to fetch image from URL #{uri}")
end

# Expected image is_a Mechanize::Image
# image.filename& image.extract_filename do not handle query string well e,g blah.jpg?v=1234
# so for now use URI
# extname = image.respond_to?(:filename) ? File.extname(image.filename) : File.extname(uri)
extname = File.extname( uri.gsub(/\?.*=.*/, ''))

base = image.respond_to?(:filename) ? File.basename(image.filename, '.*') : File.basename(uri, '.*')

logger.debug("Storing Image in TempFile #{base.inspect}.#{extname.inspect}")

@current_image_temp_file = Tempfile.new([base, extname], :encoding => 'ascii-8bit')

begin

# TODO can we handle embedded img src e.g from Mechanize::Page::Image ?

# If I call image.save(@current_image_temp_file.path) then it creates a new file with a .1 extension
# so the real temp file data is empty and paperclip chokes
# so this is a copy from the Mechanize::Image save method. don't like it much, very brittle, but what to do ...
until image.body_io.eof? do
@current_image_temp_file.write image.body_io.read 16384
end

@current_image_temp_file.rewind

logger.info("IMAGE downloaded from URI #{uri.inspect}")

attachment = create_attachment(Spree::Image, @current_image_temp_file.path, nil, nil, attributes)

rescue => e
logger.error(e.message)
logger.error("Failed to create Image from URL #{uri}")
raise DataShift::DataProcessingError.new("Failed to create Image from URL #{uri}")

ensure
@current_image_temp_file.close
@current_image_temp_file.unlink
end

else

path, alt_text = image.split(Delimiters::name_value_delim)

alt_text = variants[i].name if !alt_text #ensure alt_text is filled

logger.debug("Processing IMAGE from PATH #{path.inspect} #{alt_text.inspect}")

path = File.join(config[:image_path_prefix], path) if(config[:image_path_prefix])

attachment = create_attachment(Spree::Image, path, nil, nil, :alt => alt_text)

end

logger.debug "#{attachment.inspect}"
var_images << attachment if attachment

end # images loop

# we have our variant images. Save them!
begin
# Link images to corresponding variant
variants[i].images << var_images
variants[i].save
logger.debug("Variant assigned Images from : #{var_images.inspect}")
rescue => e
puts "ERROR - Failed to assign attachments to #{variants[i].class} #{variants[i].id}"
logger.error("Failed to assign attachments to #{variants[i].class} #{variants[i].id}")
end

end # variants_images_list loop

# ... or just single Master Product?
elsif(@load_object.variants.size == 0)

if(current_value.to_s.include?(Delimiters::multi_value_delim))
# multiple images
images = current_value.to_s.split(Delimiters::multi_value_delim)
else
# single image
images << variant_images
end

logger.info "Setting #{images.count} images for Master variant #{@load_object.master.name}..."

# Image processing...
images.each do |image|
@spree_uri_regexp ||= Regexp::new('(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:\/~\+#]*[\w\-\@?^=%&amp;\/~\+#])?' )

if(image.match(@spree_uri_regexp))

uri, attributes = image.split(Delimiters::attribute_list_start)

uri.strip!

logger.info("Processing IMAGE from URI [#{uri.inspect}]")

if(attributes)
#TODO move to ColumnPacker unpack ?
attributes = attributes.split(', ').map{|h| h1,h2 = h.split('=>'); {h1.strip! => h2.strip!}}.reduce(:merge)
logger.debug("IMAGE has additional attributes #{attributes.inspect}")
else
attributes = {} # will blow things up later if we pass nil where {} expected
end

agent = Mechanize.new

image = begin
agent.get(uri)
rescue => e
puts "ERROR: Failed to fetch image from URL #{uri}", e.message
raise DataShift::BadUri.new("Failed to fetch image from URL #{uri}")
end

# Expected image is_a Mechanize::Image
# image.filename& image.extract_filename do not handle query string well e,g blah.jpg?v=1234
# so for now use URI
# extname = image.respond_to?(:filename) ? File.extname(image.filename) : File.extname(uri)
extname = File.extname( uri.gsub(/\?.*=.*/, ''))

base = image.respond_to?(:filename) ? File.basename(image.filename, '.*') : File.basename(uri, '.*')

logger.debug("Storing Image in TempFile #{base.inspect}.#{extname.inspect}")

@current_image_temp_file = Tempfile.new([base, extname], :encoding => 'ascii-8bit')

begin

# TODO can we handle embedded img src e.g from Mechanize::Page::Image ?

# If I call image.save(@current_image_temp_file.path) then it creates a new file with a .1 extension
# so the real temp file data is empty and paperclip chokes
# so this is a copy from the Mechanize::Image save method. don't like it much, very brittle, but what to do ...
until image.body_io.eof? do
@current_image_temp_file.write image.body_io.read 16384
end

@current_image_temp_file.rewind

logger.info("IMAGE downloaded from URI #{uri.inspect}")

attachment = create_attachment(Spree::Image, @current_image_temp_file.path, nil, nil, attributes)

rescue => e
logger.error(e.message)
logger.error("Failed to create Image from URL #{uri}")
raise DataShift::DataProcessingError.new("Failed to create Image from URL #{uri}")

ensure
@current_image_temp_file.close
@current_image_temp_file.unlink
end

else

path, alt_text = image.split(Delimiters::name_value_delim)

alt_text = @load_object.master.name if !alt_text #ensure alt_text is filled

logger.debug("Processing IMAGE from PATH #{path.inspect} #{alt_text.inspect}")

path = File.join(config[:image_path_prefix], path) if(config[:image_path_prefix])

# create_attachment(klass, attachment_path, record = nil, attach_to_record_field = nil, options = {})
attachment = create_attachment(Spree::Image, path, nil, nil, :alt => alt_text)

end

var_images << attachment if attachment

end # images loop

# we have our variant images. Save them!
begin
# Link images to corresponding variant
@load_object.master.images << var_images
@load_object.master.save
logger.debug("Master Variant assigned Images from : #{var_images.inspect}")
rescue => e
puts "ERROR - Failed to assign attachment to #{@load_object.master.class} #{@load_object.master.id}"
logger.error("Failed to assign attachment to #{@load_object.master.class} #{@load_object.master.id}")
end

end
end

end
end
end
4 changes: 3 additions & 1 deletion lib/loaders/spree/spree_base_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ def add_images( record )
# different versions have moved images around from Prod to Variant
owner = DataShift::SpreeEcom::get_image_owner(record)

get_each_assoc.each do |image|
#get_each_assoc.each do |image|
# multiple files should maintain comma separated logic with 'Delimiters::multi_value_delim' and not 'Delimiters::multi_assoc_delim'
@populator.current_value.to_s.split(Delimiters::multi_value_delim).each do |image|

#TODO - make this Delimiters::attributes_start_delim and support {alt=> 'blah, :position => 2 etc}

Expand Down

0 comments on commit 936b96e

Please sign in to comment.