Class: Feed::OpenaiAds::CatalogFeedGenerator
- Inherits:
-
BaseService
- Object
- BaseService
- Feed::OpenaiAds::CatalogFeedGenerator
- Defined in:
- app/services/feed/openai_ads/catalog_feed_generator.rb
Overview
Builds the OpenAI Ads product feed — a Google-Merchant-Center-compatible CSV
of our sellable catalog — and optionally writes it to a file for delivery to
OpenAI (uploaded in the Ads Manager Feeds area, or pushed to the SFTP location
OpenAI provisions there).
Mirrors Google::ListGenerator (same catalog scope + presenter
pipeline), but emits a single CSV document rather than caching per-item XML:
OpenAI ingests an uploaded/pushed catalog file, not a hosted feed URL, so
there's no per-request assembly to optimize and no cache table to maintain.
Usage:
Feed::OpenaiAds::CatalogFeedGenerator.new.process(output_file_path: path)
Defined Under Namespace
Classes: Result
Constant Summary collapse
- COLUMNS =
Ordered CSV columns. GMC-standard attribute names (OpenAI's ads feed is
GMC-shaped) plus OpenAI's canonicalis_ads_eligibleflag. Each value is a
lambda over the ProductPresenter. { 'id' => ->(p) { p.id }, 'title' => ->(p) { p.title }, 'description' => ->(p) { p.description }, 'link' => ->(p) { p.url }, 'image_link' => ->(p) { p.image_link }, 'additional_image_link' => ->(p) { p.additional_image_link }, 'availability' => ->(p) { p.availability }, 'price' => ->(p) { p.price_with_currency }, 'sale_price' => ->(p) { p.sale_price_with_currency if p.sale_price_in_effect? }, 'brand' => ->(p) { p.brand }, 'gtin' => ->(p) { p.upc }, 'mpn' => ->(p) { p.mpn }, 'condition' => ->(p) { p.condition }, 'product_type' => ->(p) { p.product_type }, 'google_product_category' => ->(p) { p.google_product_category }, 'item_group_id' => ->(p) { p.item_group_name }, 'color' => ->(p) { p.color_info }, 'size' => ->(p) { p.size_info }, 'shipping_weight' => ->(p) { p.shipping_weight }, 'is_ads_eligible' => ->(p) { p.ads_eligible? } }.freeze
Instance Method Summary collapse
-
#build_csv(presenters) ⇒ String
CSV with a header row + one row per presenter.
-
#load_products(catalog, limit: nil) ⇒ Object
Catalog-item presenters for one catalog, in the catalog's locale, restricted to products that belong in the feed file.
- #process(catalogs: nil, output_file_path: nil, limit: nil) ⇒ Result
-
#row_for(presenter) ⇒ Array
One CSV row aligned to COLUMNS.
Instance Method Details
#build_csv(presenters) ⇒ String
Returns CSV with a header row + one row per presenter.
85 86 87 88 89 90 |
# File 'app/services/feed/openai_ads/catalog_feed_generator.rb', line 85 def build_csv(presenters) CSV.generate do |csv| csv << COLUMNS.keys presenters.each { |presenter| csv << row_for(presenter) } end end |
#load_products(catalog, limit: nil) ⇒ Object
Catalog-item presenters for one catalog, in the catalog's locale, restricted
to products that belong in the feed file.
72 73 74 75 76 77 78 79 80 81 |
# File 'app/services/feed/openai_ads/catalog_feed_generator.rb', line 72 def load_products(catalog, limit: nil) I18n.with_locale(catalog.locale_for_catalog) do scope = catalog.view_product_catalogs .includes(:catalog_item, :item) .joins(:catalog_item) .merge(CatalogItem.for_google_feed) scope = scope.limit(limit) if limit.present? scope.map { |ci| Feed::OpenaiAds::ProductPresenter.new(ci) }.select(&:feed_includable?) end end |
#process(catalogs: nil, output_file_path: nil, limit: nil) ⇒ Result
57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'app/services/feed/openai_ads/catalog_feed_generator.rb', line 57 def process(catalogs: nil, output_file_path: nil, limit: nil) catalogs ||= Catalog.for_google_feed presenters = catalogs.flat_map { |catalog| load_products(catalog, limit:) } csv = build_csv(presenters) if output_file_path logger.info "Writing OpenAI Ads product feed (#{presenters.size} products) to #{output_file_path}" File.write(output_file_path, csv) end Result.new(csv:, output_file_path: output_file_path&.to_s, product_count: presenters.size) end |