Last active
June 17, 2016 16:28
-
-
Save alabamaair/8abd7a233268d141396b28aafdcba9f3 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
module Spree | |
class PropertiesImport < ActiveRecord::Base | |
include XxHashUsage | |
require 'translit' | |
require 'rubygems' | |
require 'zip' | |
require 'fileutils' | |
has_attached_file :property_file, | |
:path => ':rails_root/public/properties_imports/:id_:basename.:extension', | |
:url => '/attachment/:id/download' | |
do_not_validate_attachment_file_type :property_file | |
def start_import | |
return if self.state != 'pending' and self.state != 'processing' | |
self.message = 'Распаковка архива...' | |
self.state = :processing | |
save | |
errors_list = [] | |
products_total = 0 | |
products_updated = 0 | |
products_not_found = 0 | |
content = [] | |
logger.properties_import.info '[IMPORT] extract files started' | |
dest_dir = File.dirname(property_file.path) #папка, куда всё сохраняется | |
file_name_xml = File.basename(property_file.path, File.extname(property_file.path)) #имя архива без расширения | |
dest_file = "#{dest_dir}/#{file_name_xml}.xml" #полный путь до xml-файла вместе с именем | |
Zip::File.open(property_file.path) do |zip_file| | |
zip_file.each do |entry| | |
entry.extract("#{dest_dir}/#{entry.name}"){true} | |
#xml-файл сливается в новый dest_file | |
if entry.name.downcase =~ /.xml/ | |
content << entry.get_input_stream.read.force_encoding('utf-8') | |
File.open(dest_file, "a"){ |somefile| somefile.puts content} | |
end | |
end | |
end | |
if File.exist?(dest_file) | |
self.message = 'Обрабатывается...' | |
self.state = :processing | |
save | |
logger.properties_import.info "[IMPORT] files extracted success." | |
logger.properties_import.info '[IMPORT] started' | |
file = File.open(dest_file) #открываем полученный xml-файл | |
node_set = Nokogiri::XML.parse(file) | |
#если xml валиден начинаем импорт | |
if node_set.errors.empty? | |
node_set.remove_namespaces! | |
#парсим вендор-код из файла | |
node_set.xpath('//offer').each do |product_set| | |
products_total +=1 | |
vc = product_set.xpath('vendorCode').text | |
#выбираем товар из БД | |
unless vc.nil? | |
products = Spree::Product.joins(:properties).where("#{Spree::ProductProperty.table_name}.value LIKE ? OR #{Spree::ProductProperty.table_name}.value LIKE ?", vc, "#{vc},%").where(Spree::Product.property_conditions('partnomer')) | |
#если по одному коду найдено несколько товаров, то не обновляем их свойства | |
if products.count > 1 | |
#errors_list << {:error => "По вендорКоду #{vc} найдено несколько товаров", :id => vc} #просто пишем в лог, слишком много ошибок | |
logger.properties_import.info "[WARNING] Under one #{vc} found multiple products" | |
else | |
#выбираем товар по партномеру | |
product = products.first | |
if product.nil? | |
products_not_found += 1 | |
#errors_list << {:error => "По вендорКоду #{vc} не найдено товаров", :id => vc} # просто пишем в лог, слишком много ошибок | |
logger.properties_import.info "[WARNING] Product not found: #{vc}" | |
else | |
#парсим и обновляем свойства выбранного товара | |
product_set.xpath('param').each do |param_element| | |
param_presentation = param_element['name'] | |
param_name = string_transliterate2(param_element['name']) | |
param_value = param_element.text | |
unless param_value.blank? | |
product.set_property(param_name,param_value,param_presentation) | |
if product.save | |
products_updated +=1 | |
self.updated_product_ids_will_change! | |
self.updated_product_ids << product.id | |
logger.properties_import.info "[INFO] Product properties saved: #{vc}" | |
else | |
errors_list << {:error => "Товар не сохранен: \n#{product.errors_list.messages.to_a.join("\n")}", :id => vc} | |
end | |
end | |
end | |
#парсим и добавляем картинку | |
product_set.xpath('picture').each do |picture_element| | |
localpath = picture_element.text.sub(/^https?:\/\/(www.)?[a-zA-Z0-9_-]*\.(\w*)/,'') | |
fullpath = "#{dest_dir}#{localpath}" | |
#проверяем наличие файла | |
unless File.exists?(fullpath) && File.readable?(fullpath) | |
logger.properties_import.info "[WARNING] Image not found: #{fullpath}" #нужно ли это в логе? | |
else | |
#если файл найден, добавляем его к продукту (если такой же уже не добавлен) | |
fileimage = File.open(fullpath, 'rb') | |
newimage = product.images.new(:attachment => fileimage, :alt => product.name) | |
newname = newimage.attachment_file_name | |
newsize = newimage.attachment_file_size | |
checkimage = product.images.find_by(attachment_file_name: newname, attachment_file_size: newsize) | |
if checkimage.nil? | |
newimage.save | |
products_updated +=1 | |
self.updated_product_ids_will_change! | |
self.updated_product_ids << product.id | |
logger.properties_import.info "[INFO] Product image saved: #{vc}" | |
else | |
logger.properties_import.info "[INFO] Product image already uploaded: #{vc}" | |
end | |
end | |
end | |
#конец парсинга и добавления картинки | |
end | |
end | |
end | |
end | |
if errors_list.any? | |
self.message = "Ошибки: #{errors_list.join("\n")}" | |
self.state = :failure | |
save | |
logger.properties_import.error "[ERROR] #{self.message}" | |
else | |
self.message = "Обработано успешно. Всего продуктов в файле: #{products_total}. Обновлено свойств: #{products_updated}." | |
self.state = :success | |
self.endtime = DateTime.now | |
save | |
logger.properties_import.info "[SUCCESS] #{self.message}" | |
end | |
else | |
self.message = 'Неверный формат файла xml, импорт невозможен' | |
self.state = :failure | |
save | |
logger.properties_import.error "[ERROR] #{self.message}" | |
end | |
#уборка распакованного (в dest_dir удаляем images и все xml) | |
Dir.chdir(dest_dir) | |
FileUtils.rm_rf('images') | |
Dir.glob('*.xml').each { |filename| File.delete(filename) } | |
logger.properties_import.info "[INFO] unzipped files has been deleted" | |
else | |
self.message = 'Не получен xml-файл, импорт невозможен' | |
self.state = :failure | |
save | |
logger.properties_import.error "[ERROR] #{self.message}" | |
end | |
end | |
def string_transliterate2(str) | |
Translit.convert(str, :english) | |
.parameterize('_') | |
.to_s | |
end | |
end | |
end |
This file contains hidden or 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
module Spree | |
module Admin | |
class PropertiesImportsController < BaseController | |
require 'nokogiri' | |
def show | |
@properties_import = Spree::PropertiesImport.find(params[:id]) | |
if params[:filter] | |
case params[:filter] | |
when 'updated_products' | |
@header = 'Список обновленных товаров' | |
@collection = Spree::Product.where id: @properties_import.updated_product_ids | |
else | |
set_header_and_collection_by_default | |
end | |
else | |
set_header_and_collection_by_default | |
end | |
@collection = Kaminari.paginate_array(@collection).page(params[:page]).per(100) | |
render 'spree/admin/shared/processed_products' | |
end | |
def index | |
redirect_to :action => :new | |
end | |
def new | |
@properties_import = Spree::PropertiesImport.new | |
@properties_list = Spree::PropertiesImport.all.order(updated_at: :desc, created_at: :desc) | |
end | |
def create | |
begin | |
@properties_import = Spree::PropertiesImport.create(properties_import_params) | |
@properties_import.message = 'Ожидает обработки' | |
@properties_import.state = :pending | |
@properties_import.save | |
unless @properties_import.blank? | |
if @properties_import.property_file.blank? | |
@properties_import.destroy | |
flash[:notice] = 'No file' | |
else | |
content_type = @properties_import.property_file.content_type | |
if content_type == 'application/zip' | |
# удаление старых файлов | |
file_count = Spree::PropertiesImport.all.where(:state => :success).count | |
if file_count > 5 | |
products_list = Spree::PropertiesImport.all.where(:state => :success).order(updated_at: :desc, created_at: :desc).offset(5) | |
products_list.each do |file| | |
file.destroy | |
end | |
end | |
flash[:notice] = 'File saved' | |
@properties_import.delay(:queue => 'properties_import').start_import | |
else | |
@properties_import.destroy | |
flash[:notice] = 'File must be in zip format' | |
end | |
end | |
end | |
rescue | |
flash[:notice] = 'No file. Add file and try again' | |
end | |
redirect_to :action => :new | |
end | |
private | |
def properties_import_params | |
params.require(:properties_import).permit(:property_file, :product_ids, :state, :endtime) | |
end | |
def set_header_and_collection_by_default | |
@header = 'Список обновленных товаров' | |
@collection = Spree::Product.where id: @properties_import.updated_product_ids | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment