Class: Feed::Google::ListGenerator

Inherits:
BaseService
  • Object
show all
Defined in:
app/services/feed/google/list_generator.rb

Overview

Pre-generates the google merchant product feed and store it in the database for fast retrieval

Instance Method Summary collapse

Instance Method Details

#append_xml_catalog_item(xml, cip) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'app/services/feed/google/list_generator.rb', line 78

def append_xml_catalog_item(xml, cip)
  xml.entry do
    xml.send :'g:id', cip.id
    xml.send :'g:title', cip.reported_name_for_google
    xml.send :'g:description', cip.description
    xml.send :'g:link', cip.url
    xml.send :'g:image_link', cip.image_link
    xml.send :'g:condition', cip.condition
    cip.additional_image_links.each { |link| xml.send :'g:additional_image_link', link }
    xml.send :'g:google_product_category', cip.google_product_category
    xml.send :'g:product_type', cip.product_type
    xml.send :'g:availability', cip.availability
    xml.send :'g:price', cip.price_with_currency
    xml.send :'g:suggested_retail_price', cip.price_with_currency
    xml.send(:'g:gtin', cip.upc) if cip.upc.present?
    xml.send :'g:mpn', cip.mpn
    xml.send :'g:brand', cip.brand
    # https://support.google.com/merchants/answer/6231538?hl=en
    # https://support.google.com/merchants/answer/9450803?hl=en#zippy=%2Cstep-add-variant-attributes
    if cip.has_variants? && (cip.color_info.present? || cip.size_info.present?)
      xml.send(:'g:size', cip.size_info) if cip.size_info.present?
      xml.send(:'g:color', cip.color_info) if cip.color_info.present?
      xml.send :'g:item_group_id', cip.item_group_name
    end
    xml.send :'g:shipping_weight', cip.shipping_weight
    xml.send :'g:shipping_width', cip.shipping_width
    xml.send :'g:shipping_length', cip.shipping_length
    xml.send :'g:shipping_height', cip.shipping_height
    xml.send :'g:max_handling_time', 1
    if cip.sale_price_in_effect?
      xml.send :'g:sale_price', cip.sale_price_with_currency
      xml.send :'g:sale_price_effective_date', cip.sale_price_date_range if cip.sale_price_date_range.present?
    end
    xml.send :'g:ships_from_country', cip.ships_from_country_iso
    xml.send :'g:pickup_method', cip.pickup_method
    xml.send :'g:pickup_sla', cip.pickup_sla
    xml.send :'g:link_template', "#{cip.url}?g_store_code={store_code}"
  end
end

#load_products(catalog, limit: nil, catalog_item_ids: nil) ⇒ Object



68
69
70
71
72
73
74
# File 'app/services/feed/google/list_generator.rb', line 68

def load_products(catalog, limit: nil, catalog_item_ids: nil)
  products = catalog.view_product_catalogs.includes(:catalog_item, :item).joins(:catalog_item).merge(CatalogItem.for_google_feed)
  products = products.limit(limit) if limit.present?
  products = products.where(catalog_item_id: catalog_item_ids) if catalog_item_ids.present?
  products = products.map { |ci| Feed::Google::GoogleProductPresenter.new(ci) }
  products.select(&:valid_for_google?)
end

#process(catalogs: nil, catalog_item_ids: nil, limit: nil, delete_all: nil) ⇒ Object

Main method.

  • catalogs: specify catalogs otherwise will be main catalogs (US and CA)
  • catalog_item_ids: filter by specific catalog_item_ids, e.g you want to update only one product
  • limit: optional limit to number of records to process
  • delete_all: truncate all google feed records in the catalog, useful for periodic cleanups


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/services/feed/google/list_generator.rb', line 8

def process(catalogs: nil, catalog_item_ids: nil, limit: nil, delete_all: nil)
  require 'activerecord-import/base'
  require 'activerecord-import/active_record/adapters/postgresql_adapter'

  res = { products: 0 }

  catalogs ||= Catalog.for_google_feed
  if delete_all.nil?
    if catalog_item_ids.present?
      delete_all = false
    elsif catalogs.present?
      delete_all = true
    end
  end
  # If catalog_item_ids are specified, we need to delete them first regardless
  if catalog_item_ids.present?
    GoogleFeed.where(catalog_item_id: catalog_item_ids).delete_all
    # If catalog item ids are specified, we only need catalogs which includes these
    catalogs = catalogs.joins(:catalog_items).where(catalog_items: { id: catalog_item_ids })
  end

  catalogs.each do |catalog|
    I18n.with_locale catalog.locale_for_catalog do
      logger.info "Processing GoogleFeed items for catalog #{catalog.id}"
      products = load_products(catalog, limit: limit, catalog_item_ids: catalog_item_ids)
      inserts = []

      products.each do |p|
        logger.info "Generating xml data for product #{p.id} mpn:#{p.mpn} upc:#{p.upc}"
        builder = Nokogiri::XML::Builder.new do |xml|
          append_xml_catalog_item(xml, p)
        end
        xml_data = builder.to_xml save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
        catalog = p.catalog_item.catalog
        res[:products] += 1
        inserts << {
          catalog_id: catalog.id,
          catalog_item_id: p.catalog_item_id,
          sku: p.item_sku,
          target_country_iso: catalog.country&.iso,
          locale: catalog.locale_for_catalog,
          xml_data: xml_data
        }
      end
      GoogleFeed.transaction do
        if delete_all
          logger.info 'Purging GoogleFeed'
          GoogleFeed.where(catalog_id: catalog.id).delete_all
        end
        logger.info "Importing #{inserts.size} GoogleFeedRecords"
        GoogleFeed.import inserts, on_duplicate_key_update: {
          conflict_target: [:catalog_item_id],
          columns: %i[xml_data sku target_country_iso locale]
        }
      end
    end
  end
  res
end