huginn_acumen_product_agent 1.7.3 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/huginn_acumen_product_agent.rb +9 -1
- data/lib/huginn_acumen_product_agent/acumen_agent_error.rb +10 -0
- data/lib/huginn_acumen_product_agent/acumen_client.rb +33 -0
- data/lib/huginn_acumen_product_agent/acumen_product_agent.rb +164 -45
- data/lib/huginn_acumen_product_agent/concerns/acumen_query_concern.rb +56 -0
- data/lib/huginn_acumen_product_agent/concerns/alternate_products_query_concern.rb +99 -0
- data/lib/huginn_acumen_product_agent/concerns/inv_product_query_concern.rb +144 -0
- data/lib/huginn_acumen_product_agent/concerns/inv_status_query_concern.rb +76 -0
- data/lib/huginn_acumen_product_agent/concerns/prod_mkt_query_concern.rb +163 -0
- data/lib/huginn_acumen_product_agent/concerns/product_categories_query_concern.rb +74 -0
- data/lib/huginn_acumen_product_agent/concerns/product_contributors_query_concern.rb +116 -0
- metadata +10 -3
- data/lib/huginn_acumen_product_agent/concerns/acumen_product_query_concern.rb +0 -511
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This module is responsible for reading/processing product Contributor data.
|
4
|
+
#
|
5
|
+
# The data in question here comes from multiple tables. The `ProdMkt_Contrib_Link`
|
6
|
+
# table defines the _relationship_ between products and contributors, and the
|
7
|
+
# `ProdMkt_Contributor` table defines the _contribution type_ (Author, Editor, etc)
|
8
|
+
module ProductContributorsQueryConcern
|
9
|
+
extend AcumenQueryConcern
|
10
|
+
|
11
|
+
# Updates the provided products with their associared contributor data
|
12
|
+
def fetch_product_contributors(acumen_client, products)
|
13
|
+
|
14
|
+
marketing_ids = products.map { |p| p['acumenAttributes']['product_marketing_id'] }
|
15
|
+
contributor_data = acumen_client.get_product_contributors(marketing_ids)
|
16
|
+
contributor_data = process_product_contributor_response(contributor_data)
|
17
|
+
|
18
|
+
contributor_ids = contributor_data.map { |c| c['contributor_id'] }
|
19
|
+
contributor_type_data = acumen_client.get_contributor_types(contributor_ids)
|
20
|
+
contributor_type_data = process_contributor_type_response(contributor_type_data)
|
21
|
+
|
22
|
+
return map_contributor_data(products, contributor_data, contributor_type_data)
|
23
|
+
end
|
24
|
+
|
25
|
+
# This function parses the raw data returned from the ProdMkt_Contrib_Link table
|
26
|
+
# This table holds the relationship between products and Contributors
|
27
|
+
# The resulting data is a hash mapping contributor arrays to Prod_Mkt.ID values
|
28
|
+
def process_product_contributor_response(raw_data)
|
29
|
+
contributors = []
|
30
|
+
|
31
|
+
raw_data.each do |contributor|
|
32
|
+
|
33
|
+
begin
|
34
|
+
mapped = response_mapper(contributor, {
|
35
|
+
'ProdMkt_Contrib_Link.ProdMkt_Contrib_ID' => 'contributor_id',
|
36
|
+
'ProdMkt_Contrib_Link.ProdMkt_ID' => 'product_marketing_id',
|
37
|
+
'ProdMkt_Contrib_Link.Inactive' => 'inactive',
|
38
|
+
})
|
39
|
+
|
40
|
+
if mapped['inactive'] == '0'
|
41
|
+
contributors.push(mapped)
|
42
|
+
end
|
43
|
+
rescue => error
|
44
|
+
issue_error(AcumenAgentError.new(
|
45
|
+
'process_product_contributor_response',
|
46
|
+
'Failed while processing contributor record',
|
47
|
+
contributor,
|
48
|
+
error,
|
49
|
+
))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
return contributors
|
54
|
+
end
|
55
|
+
|
56
|
+
# This function parses the raw data returned from the ProdMkt_Contributor table
|
57
|
+
# This table holds the contributor type (e.g. Author) for the
|
58
|
+
# contributor/product relationship
|
59
|
+
def process_contributor_type_response(raw_data)
|
60
|
+
results = {}
|
61
|
+
raw_data.map do |contributor_type|
|
62
|
+
|
63
|
+
begin
|
64
|
+
mapped = response_mapper(contributor_type, {
|
65
|
+
'ProdMkt_Contributor.ID' => 'contributor_id',
|
66
|
+
'ProdMkt_Contributor.Contrib_Type' => 'type',
|
67
|
+
})
|
68
|
+
|
69
|
+
|
70
|
+
if !results[mapped['contributor_id']]
|
71
|
+
results[mapped['contributor_id']] = mapped['type']
|
72
|
+
end
|
73
|
+
rescue => error
|
74
|
+
issue_error(AcumenAgentError.new(
|
75
|
+
'process_contributor_type_response',
|
76
|
+
'Failed while processing contributor type record',
|
77
|
+
contributor_type,
|
78
|
+
error,
|
79
|
+
))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return results
|
84
|
+
end
|
85
|
+
|
86
|
+
# This function maps parsed Contributor records to their matching Inv_Product record
|
87
|
+
def map_contributor_data(products, contributor_data, contributor_type_data)
|
88
|
+
products.each do |product|
|
89
|
+
|
90
|
+
begin
|
91
|
+
marketing_id = product['acumenAttributes']['product_marketing_id']
|
92
|
+
contributors = contributor_data.select { |c| c['product_marketing_id'] == marketing_id }
|
93
|
+
|
94
|
+
product['contributors'] = contributors.map do |c|
|
95
|
+
{
|
96
|
+
'@type' => 'Person',
|
97
|
+
'identifier' => c['contributor_id'],
|
98
|
+
'acumenAttributes' => {
|
99
|
+
'contrib_type' => contributor_type_data[c['contributor_id']]
|
100
|
+
}
|
101
|
+
}
|
102
|
+
end
|
103
|
+
rescue => error
|
104
|
+
issue_error(AcumenAgentError.new(
|
105
|
+
'map_contributor_data',
|
106
|
+
'Failed while mapping contributor data to products',
|
107
|
+
{ id: product['identifier'], sku: product['sku'] },
|
108
|
+
error
|
109
|
+
))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
return products
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: huginn_acumen_product_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Spizziri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -63,9 +63,16 @@ extra_rdoc_files: []
|
|
63
63
|
files:
|
64
64
|
- LICENSE.txt
|
65
65
|
- lib/huginn_acumen_product_agent.rb
|
66
|
+
- lib/huginn_acumen_product_agent/acumen_agent_error.rb
|
66
67
|
- lib/huginn_acumen_product_agent/acumen_client.rb
|
67
68
|
- lib/huginn_acumen_product_agent/acumen_product_agent.rb
|
68
|
-
- lib/huginn_acumen_product_agent/concerns/
|
69
|
+
- lib/huginn_acumen_product_agent/concerns/acumen_query_concern.rb
|
70
|
+
- lib/huginn_acumen_product_agent/concerns/alternate_products_query_concern.rb
|
71
|
+
- lib/huginn_acumen_product_agent/concerns/inv_product_query_concern.rb
|
72
|
+
- lib/huginn_acumen_product_agent/concerns/inv_status_query_concern.rb
|
73
|
+
- lib/huginn_acumen_product_agent/concerns/prod_mkt_query_concern.rb
|
74
|
+
- lib/huginn_acumen_product_agent/concerns/product_categories_query_concern.rb
|
75
|
+
- lib/huginn_acumen_product_agent/concerns/product_contributors_query_concern.rb
|
69
76
|
- spec/acumen_product_agent_spec.rb
|
70
77
|
homepage: https://github.com/5-Stones/huginn_acumen_product_agent
|
71
78
|
licenses:
|
@@ -1,511 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AcumenProductQueryConcern
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
UNIT_MAP = {
|
7
|
-
'oz.' => 'OZ',
|
8
|
-
'Inches (US)' => 'INH',
|
9
|
-
}
|
10
|
-
|
11
|
-
def get_products_by_ids(acumen_client, ids)
|
12
|
-
response = acumen_client.get_products(ids)
|
13
|
-
products = []
|
14
|
-
|
15
|
-
products = parse_product_request(response)
|
16
|
-
|
17
|
-
response = acumen_client.get_products_marketing(ids)
|
18
|
-
marketing = parse_product_marketing_request(response)
|
19
|
-
|
20
|
-
merge_products_and_marketing(products, marketing)
|
21
|
-
end
|
22
|
-
|
23
|
-
def get_variants_for_ids(acumen_client, ids)
|
24
|
-
result = get_linked_products_by_ids(acumen_client, ids)
|
25
|
-
|
26
|
-
# Filtering out duplicate links getting sent from acumen
|
27
|
-
filter = []
|
28
|
-
result.each do |link|
|
29
|
-
if (link['alt_format'].to_s != 0.to_s && !link.in?(filter))
|
30
|
-
filter.push(link)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
return filter
|
35
|
-
end
|
36
|
-
|
37
|
-
def get_linked_products_by_ids(acumen_client, ids)
|
38
|
-
response = acumen_client.get_linked_products(ids)
|
39
|
-
process_linked_product_query(response)
|
40
|
-
end
|
41
|
-
|
42
|
-
def get_product_contributors(acumen_client, products)
|
43
|
-
ids = products.map {|product| product['acumenAttributes']['product_marketing_id']}
|
44
|
-
response = acumen_client.get_product_contributors(ids)
|
45
|
-
product_contributors = process_product_contributor_query(response)
|
46
|
-
|
47
|
-
products.each do |product|
|
48
|
-
id = product['acumenAttributes']['product_marketing_id']
|
49
|
-
product_contributor = product_contributors[id]
|
50
|
-
|
51
|
-
|
52
|
-
if product_contributor
|
53
|
-
contributor_ids = product_contributor.map do |pc|
|
54
|
-
pc['contributor_id']
|
55
|
-
end
|
56
|
-
|
57
|
-
type_response = acumen_client.get_contributor_types(contributor_ids)
|
58
|
-
contributor_types = process_contributor_types_query(type_response)
|
59
|
-
|
60
|
-
product['contributors'] = product_contributor.map do |pc|
|
61
|
-
{
|
62
|
-
'@type' => 'Person',
|
63
|
-
'identifier' => pc['contributor_id'],
|
64
|
-
'acumenAttributes' => {
|
65
|
-
'contrib_type' => contributor_types[pc['contributor_id']]
|
66
|
-
}
|
67
|
-
}
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
products
|
72
|
-
end
|
73
|
-
|
74
|
-
def get_master_products_by_id(client, products)
|
75
|
-
master_products = []
|
76
|
-
|
77
|
-
products.each do |product|
|
78
|
-
wrapper_id = product['identifier']
|
79
|
-
master_id = 0
|
80
|
-
product['model'].each do |variant|
|
81
|
-
if variant['acumenAttributes']['is_master'] && variant['isAvailableForPurchase']
|
82
|
-
master_id = variant['identifier']
|
83
|
-
end
|
84
|
-
end
|
85
|
-
if wrapper_id == master_id || master_id == 0
|
86
|
-
master_products.push(product)
|
87
|
-
else
|
88
|
-
if (master_products.find { |p| p['identifier'] == master_id }).nil?
|
89
|
-
reloaded_product = get_products_by_ids(client, [master_id.to_s])[0]
|
90
|
-
|
91
|
-
unless reloaded_product.nil?
|
92
|
-
reloaded_product['model'] = product['model']
|
93
|
-
reloaded_product['additionalProperty'].push(product['additionalProperty'].select { |m| m['propertyID'] == 'baseSku'}[0])
|
94
|
-
master_products.push(reloaded_product)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
master_products
|
101
|
-
end
|
102
|
-
|
103
|
-
def get_product_variants(acumen_client, products, physical_formats, digital_formats)
|
104
|
-
ids = products.map { |product| product['identifier'] }
|
105
|
-
# fetch product/variant relationships
|
106
|
-
variant_links = get_variants_for_ids(acumen_client, ids)
|
107
|
-
variant_ids = variant_links.map { |link| link['to_id'] }
|
108
|
-
|
109
|
-
variant_ids = variant_links.map { |link| link['to_id'] }
|
110
|
-
|
111
|
-
# fetch product variants
|
112
|
-
variants = get_products_by_ids(acumen_client, variant_ids)
|
113
|
-
|
114
|
-
# merge variants and products together
|
115
|
-
process_products_and_variants(products, variants, variant_links, physical_formats, digital_formats)
|
116
|
-
end
|
117
|
-
|
118
|
-
def get_product_categories(acumen_client, products)
|
119
|
-
# fetch categories
|
120
|
-
categories_map = {}
|
121
|
-
|
122
|
-
skus = products.map { |product| product['model'].map { |m| m['sku'] } }
|
123
|
-
skus.each do |sku_set|
|
124
|
-
sku_set.each do |sku|
|
125
|
-
response = acumen_client.get_product_categories([sku])
|
126
|
-
categories = process_product_categories_query(response)
|
127
|
-
categories_map[sku] = categories != {} ? categories[sku] : []
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# map categories to products
|
132
|
-
products.each do |product|
|
133
|
-
product['model'].each do |variant|
|
134
|
-
variant['categories'] = []
|
135
|
-
categories = categories_map[variant['sku']].select { |c| c['inactive'] == '0' }
|
136
|
-
categories.map do |c|
|
137
|
-
variant['categories'].push({
|
138
|
-
'@type' => 'Thing',
|
139
|
-
'identifier' => c['category_id']
|
140
|
-
})
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
products
|
146
|
-
end
|
147
|
-
|
148
|
-
def parse_product_request(products)
|
149
|
-
products.map do |p|
|
150
|
-
variant = response_mapper(p, {
|
151
|
-
'Inv_Product.ID' => 'identifier',
|
152
|
-
'Inv_Product.ProdCode' => 'sku',
|
153
|
-
'Inv_Product.SubTitle' => 'disambiguatingDescription',
|
154
|
-
'Inv_Product.ISBN_UPC' => 'isbn',
|
155
|
-
'Inv_Product.Pub_Date' => 'datePublished',
|
156
|
-
'Inv_Product.Next_Release' => 'releaseDate',
|
157
|
-
})
|
158
|
-
variant['@type'] = 'ProductModel'
|
159
|
-
variant['isDefault'] = false
|
160
|
-
variant['isTaxable'] = field_value(p, 'Inv_Product.Taxable') == '1'
|
161
|
-
variant['isAvailableForPurchase'] = field_value(p, 'Inv_Product.Not_On_Website') == '0'
|
162
|
-
variant['acumenAttributes'] = {
|
163
|
-
'is_master' => field_value(p, 'Inv_Product.OnWeb_LinkOnly') == '0'
|
164
|
-
}
|
165
|
-
|
166
|
-
variant['offers'] = [{
|
167
|
-
'@type' => 'Offer',
|
168
|
-
'price' => field_value(p, 'Inv_Product.Price_1'),
|
169
|
-
'availability' => field_value(p, 'Inv_Product.BO_Reason')
|
170
|
-
}]
|
171
|
-
if field_value(p, 'Inv_Product.Price_2')
|
172
|
-
variant['offers'].push({
|
173
|
-
'@type' => 'Offer',
|
174
|
-
'price' => field_value(p, 'Inv_Product.Price_2'),
|
175
|
-
'availability' => field_value(p, 'Inv_Product.BO_Reason')
|
176
|
-
})
|
177
|
-
end
|
178
|
-
|
179
|
-
weight = field_value(p, 'Inv_Product.Weight')
|
180
|
-
variant['weight'] = quantitative_value(weight, 'oz.')
|
181
|
-
|
182
|
-
product = {
|
183
|
-
'@type' => 'Product',
|
184
|
-
'identifier' => variant['identifier'],
|
185
|
-
'sku' => variant['sku'],
|
186
|
-
'name' => field_value(p, 'Inv_Product.Full_Title'),
|
187
|
-
'disambiguatingDescription' => field_value(p, 'Inv_Product.SubTitle'),
|
188
|
-
'model' => [
|
189
|
-
variant
|
190
|
-
],
|
191
|
-
'additionalProperty' => [],
|
192
|
-
'acumenAttributes' => {
|
193
|
-
'info_alpha_1' => field_value(p, 'Inv_Product.Info_Alpha_1'),
|
194
|
-
'info_boolean_1' => field_value(p, 'Inv_Product.Info_Boolean_1'),
|
195
|
-
},
|
196
|
-
'isAvailableForPurchase' => field_value(p, variant['isAvailableForPurchase']),
|
197
|
-
}
|
198
|
-
|
199
|
-
category = field_value(p, 'Inv_Product.Category')
|
200
|
-
if category
|
201
|
-
|
202
|
-
if variant['acumenAttributes']
|
203
|
-
variant['acumenAttributes']['category'] = category
|
204
|
-
else
|
205
|
-
variant['acumenAttributes'] = { 'category' => category }
|
206
|
-
end
|
207
|
-
|
208
|
-
if category == 'Paperback'
|
209
|
-
product['additionalType'] = variant['additionalType'] = 'Book'
|
210
|
-
variant['bookFormat'] = "http://schema.org/Paperback"
|
211
|
-
variant['accessMode'] = "textual"
|
212
|
-
variant['isDigital'] = false
|
213
|
-
elsif category == 'Hardcover'
|
214
|
-
product['additionalType'] = variant['additionalType'] = 'Book'
|
215
|
-
variant['bookFormat'] = "http://schema.org/Hardcover"
|
216
|
-
variant['accessMode'] = "textual"
|
217
|
-
variant['isDigital'] = false
|
218
|
-
elsif category == 'eBook'
|
219
|
-
product['additionalType'] = variant['additionalType'] = 'Book'
|
220
|
-
variant['bookFormat'] = "http://schema.org/EBook"
|
221
|
-
variant['accessMode'] = "textual"
|
222
|
-
variant['isDigital'] = true
|
223
|
-
elsif category == 'CD'
|
224
|
-
product['additionalType'] = variant['additionalType'] = 'CreativeWork'
|
225
|
-
variant['accessMode'] = "auditory"
|
226
|
-
variant['isDigital'] = false
|
227
|
-
else
|
228
|
-
variant['isDigital'] = false
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
product
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def process_linked_product_query(links)
|
237
|
-
links.map do |link|
|
238
|
-
response_mapper(link, {
|
239
|
-
'Product_Link.Link_From_ID' => 'from_id',
|
240
|
-
'Product_Link.Link_To_ID' => 'to_id',
|
241
|
-
'Product_Link.Alt_Format' => 'alt_format',
|
242
|
-
})
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def parse_product_marketing_request(products)
|
247
|
-
results = {}
|
248
|
-
products.each do |product|
|
249
|
-
mapped = response_mapper(product, {
|
250
|
-
'ProdMkt.Product_ID' => 'product_id',
|
251
|
-
'ProdMkt.Product_Code' => 'sku',
|
252
|
-
'ProdMkt.ID' => 'id',
|
253
|
-
'ProdMkt.Pages' => 'pages',
|
254
|
-
'ProdMkt.Publisher' => 'publisher',
|
255
|
-
'ProdMkt.Description_Short' => 'description_short',
|
256
|
-
'ProdMkt.Description_Long' => 'description_long',
|
257
|
-
'ProdMkt.Height' => 'height',
|
258
|
-
'ProdMkt.Width' => 'width',
|
259
|
-
'ProdMkt.Thickness' => 'depth',
|
260
|
-
'ProdMkt.Meta_Keywords' => 'meta_keywords',
|
261
|
-
'ProdMkt.Meta_Description' => 'meta_description',
|
262
|
-
'ProdMkt.Extent_Unit' => 'extent_unit',
|
263
|
-
'ProdMkt.Extent_Value' => 'extent_value',
|
264
|
-
'ProdMkt.Age_Highest' => 'age_highest',
|
265
|
-
'ProdMkt.Age_Lowest' => 'age_lowest',
|
266
|
-
'ProdMkt.Awards' => 'awards',
|
267
|
-
'ProdMkt.Dimensions_Unit_Measure' => 'dimensions_unit_measure',
|
268
|
-
'ProdMkt.Excerpt' => 'excerpt',
|
269
|
-
'ProdMkt.Grade_Highest' => 'grade_highest',
|
270
|
-
'ProdMkt.Grade_Lowest' => 'grade_lowest',
|
271
|
-
'ProdMkt.Status' => 'status',
|
272
|
-
'ProdMkt.UPC' => 'upc',
|
273
|
-
'ProdMkt.Weight_Unit_Measure' => 'weight_unit_measure',
|
274
|
-
'ProdMkt.Weight' => 'weight',
|
275
|
-
'ProdMkt.Info_Text_01' => 'info_text_01',
|
276
|
-
'ProdMkt.Info_Text_02' => 'info_text_02',
|
277
|
-
'ProdMkt.Religious_Text_Identifier' => 'religious_text_identifier',
|
278
|
-
'ProdMkt.Info_Alpha_07' => 'info_alpha_07',
|
279
|
-
})
|
280
|
-
|
281
|
-
results[mapped['product_id']] = mapped
|
282
|
-
end
|
283
|
-
|
284
|
-
results
|
285
|
-
end
|
286
|
-
|
287
|
-
def merge_products_and_marketing(products, product_marketing)
|
288
|
-
products.each do |product|
|
289
|
-
marketing = product_marketing[product['identifier']]
|
290
|
-
if marketing
|
291
|
-
product['acumenAttributes']['product_marketing_id'] = marketing['id']
|
292
|
-
|
293
|
-
product['publisher'] = {
|
294
|
-
'@type': 'Organization',
|
295
|
-
'name' => marketing['publisher']
|
296
|
-
};
|
297
|
-
product['description'] = marketing['description_long']
|
298
|
-
product['abstract'] = marketing['description_short']
|
299
|
-
product['keywords'] = marketing['meta_keywords']
|
300
|
-
product['text'] = marketing['excerpt']
|
301
|
-
|
302
|
-
if marketing['age_lowest'] || marketing['age_highest']
|
303
|
-
product['typicalAgeRange'] = "#{marketing['age_lowest']}-#{marketing['age_highest']}"
|
304
|
-
end
|
305
|
-
|
306
|
-
# properties for product pages
|
307
|
-
if marketing['grade_lowest'] || marketing['grade_highest']
|
308
|
-
# educationalUse? educationalAlignment?
|
309
|
-
product['additionalProperty'].push({
|
310
|
-
'@type' => 'PropertyValue',
|
311
|
-
'name' => 'Grade',
|
312
|
-
'propertyID' => 'grade_range',
|
313
|
-
'minValue' => marketing['grade_lowest'],
|
314
|
-
'maxValue' => marketing['grade_highest'],
|
315
|
-
'value' => "#{marketing['grade_lowest']}-#{marketing['grade_highest']}",
|
316
|
-
})
|
317
|
-
end
|
318
|
-
if marketing['awards']
|
319
|
-
product['additionalProperty'].push({
|
320
|
-
'@type' => 'PropertyValue',
|
321
|
-
'propertyID' => 'awards',
|
322
|
-
'name' => 'Awards',
|
323
|
-
'value' => marketing['awards'],
|
324
|
-
})
|
325
|
-
end
|
326
|
-
|
327
|
-
# acumen specific properties
|
328
|
-
product['acumenAttributes']['extent_unit'] = marketing['extent_unit']
|
329
|
-
product['acumenAttributes']['extent_value'] = marketing['extent_value']
|
330
|
-
product['acumenAttributes']['info_text_01'] = marketing['info_text_01']
|
331
|
-
product['acumenAttributes']['info_text_02'] = marketing['info_text_02']
|
332
|
-
product['acumenAttributes']['info_alpha_07'] = marketing['info_alpha_07']
|
333
|
-
product['acumenAttributes']['meta_description'] = marketing['meta_description']
|
334
|
-
product['acumenAttributes']['religious_text_identifier'] = marketing['religious_text_identifier']
|
335
|
-
product['acumenAttributes']['status'] = marketing['status']
|
336
|
-
|
337
|
-
variant = product['model'][0]
|
338
|
-
variant['gtin12'] = marketing['upc']
|
339
|
-
variant['numberOfPages'] = marketing['pages']
|
340
|
-
|
341
|
-
variant['height'] = quantitative_value(
|
342
|
-
marketing['height'], marketing['dimensions_unit_measure']
|
343
|
-
)
|
344
|
-
variant['width'] = quantitative_value(
|
345
|
-
marketing['width'], marketing['dimensions_unit_measure']
|
346
|
-
)
|
347
|
-
variant['depth'] = quantitative_value(
|
348
|
-
marketing['thickness'], marketing['dimensions_unit_measure']
|
349
|
-
)
|
350
|
-
if variant['weight']['value'] == '0'
|
351
|
-
variant['weight'] = quantitative_value(
|
352
|
-
marketing['weight'], marketing['weight_unit_measure']
|
353
|
-
)
|
354
|
-
end
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
products
|
359
|
-
end
|
360
|
-
|
361
|
-
def process_product_categories_query(categories)
|
362
|
-
results = {}
|
363
|
-
categories.each do |category|
|
364
|
-
mapped = response_mapper(category, {
|
365
|
-
'ProdMkt_WPC.ProdCode' => 'sku',
|
366
|
-
'ProdMkt_WPC.WPC_ID' => 'category_id',
|
367
|
-
'ProdMkt_WPC.Inactive' => 'inactive',
|
368
|
-
})
|
369
|
-
|
370
|
-
if results[mapped['sku']]
|
371
|
-
results[mapped['sku']].push(mapped)
|
372
|
-
else
|
373
|
-
results[mapped['sku']] = [mapped]
|
374
|
-
end
|
375
|
-
end
|
376
|
-
|
377
|
-
results
|
378
|
-
end
|
379
|
-
|
380
|
-
def process_product_contributor_query(contributors)
|
381
|
-
results = {}
|
382
|
-
contributors.each do |contributor|
|
383
|
-
mapped = response_mapper(contributor, {
|
384
|
-
'ProdMkt_Contrib_Link.ProdMkt_Contrib_ID' => 'contributor_id',
|
385
|
-
'ProdMkt_Contrib_Link.ProdMkt_ID' => 'product_marketing_id',
|
386
|
-
'ProdMkt_Contrib_Link.Inactive' => 'inactive',
|
387
|
-
})
|
388
|
-
|
389
|
-
if mapped['inactive'] == '0'
|
390
|
-
|
391
|
-
if results[mapped['product_marketing_id']]
|
392
|
-
results[mapped['product_marketing_id']].push(mapped)
|
393
|
-
else
|
394
|
-
results[mapped['product_marketing_id']] = [mapped]
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
results
|
399
|
-
end
|
400
|
-
|
401
|
-
def process_contributor_types_query(types)
|
402
|
-
results = {}
|
403
|
-
types.each do |type|
|
404
|
-
mapped = response_mapper(type, {
|
405
|
-
'ProdMkt_Contributor.ID' => 'contributor_id',
|
406
|
-
'ProdMkt_Contributor.Contrib_Type' => 'type',
|
407
|
-
})
|
408
|
-
|
409
|
-
if !results[mapped['contributor_id']]
|
410
|
-
results[mapped['contributor_id']] = mapped['type']
|
411
|
-
end
|
412
|
-
end
|
413
|
-
|
414
|
-
results
|
415
|
-
end
|
416
|
-
|
417
|
-
def process_products_and_variants(products, variants, links, physical_formats, digital_formats)
|
418
|
-
products_map = {}
|
419
|
-
products.each { |product| products_map[product['identifier']] = product }
|
420
|
-
|
421
|
-
variants_map = {}
|
422
|
-
variants.each { |variant| variants_map[variant['identifier']] = variant }
|
423
|
-
|
424
|
-
links.each do |link|
|
425
|
-
from_id = link['from_id']
|
426
|
-
to_id = link['to_id']
|
427
|
-
if variants_map.key?(to_id)
|
428
|
-
variant = variants_map[to_id]
|
429
|
-
variant['isDefault'] = false
|
430
|
-
products_map[from_id]['model'].push(*variant['model'])
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
result = []
|
435
|
-
products_map.each_value { |p| result.push(p) }
|
436
|
-
|
437
|
-
result.each do |product|
|
438
|
-
if product['model'].length == 1
|
439
|
-
product['model'][0]['isDefault'] = true
|
440
|
-
set_base_sku(product, product['model'][0]['sku'])
|
441
|
-
next
|
442
|
-
else
|
443
|
-
physical_formats.each do |val|
|
444
|
-
match = product['model'].select { |v| v['acumenAttributes']['category'] == val }
|
445
|
-
|
446
|
-
if match && match.length > 0
|
447
|
-
match[0]['isDefault'] = true
|
448
|
-
break
|
449
|
-
end
|
450
|
-
end
|
451
|
-
|
452
|
-
digital_formats.each do |val|
|
453
|
-
match = product['model'].select { |v| v['acumenAttributes']['category'] == val }
|
454
|
-
|
455
|
-
if match && match.length > 0
|
456
|
-
match[0]['isDefault'] = true
|
457
|
-
break
|
458
|
-
end
|
459
|
-
end
|
460
|
-
end
|
461
|
-
|
462
|
-
model_ids = product['model'].map { |m| m['identifier'] }
|
463
|
-
primary_variant = product['model'].select { |m| m['identifier'] == model_ids.min }.first
|
464
|
-
|
465
|
-
# Set the base SKU to the SKU of the oldest record.
|
466
|
-
# The base_sku property is designed to be a system value specific to the inegration using this agent.
|
467
|
-
# As a result, we don't particularly care what that value is so long as we can retrieve it consistently
|
468
|
-
# across executions. If a paperback product is created first, this will always return that product's SKU
|
469
|
-
# as the base. This gives us a consistent way to link Acumen products to an external system where database
|
470
|
-
# IDs may not match.
|
471
|
-
set_base_sku(product, primary_variant ? primary_variant['sku'] : product['model'][0]['sku'])
|
472
|
-
end
|
473
|
-
result
|
474
|
-
end
|
475
|
-
|
476
|
-
private
|
477
|
-
|
478
|
-
def response_mapper(data, map)
|
479
|
-
result = {}
|
480
|
-
map.each do |key,val|
|
481
|
-
result[val] = field_value(data, key)
|
482
|
-
end
|
483
|
-
|
484
|
-
result
|
485
|
-
end
|
486
|
-
|
487
|
-
def field_value(field, key)
|
488
|
-
field[key]['__content__'] if field[key]
|
489
|
-
end
|
490
|
-
|
491
|
-
def set_base_sku(product, sku)
|
492
|
-
|
493
|
-
product['additionalProperty'].push({
|
494
|
-
'@type' => 'PropertyValue',
|
495
|
-
'propertyID' => 'baseSku',
|
496
|
-
'name' => 'Base SKU',
|
497
|
-
'value' => sku,
|
498
|
-
})
|
499
|
-
|
500
|
-
product
|
501
|
-
end
|
502
|
-
|
503
|
-
def quantitative_value(value, unit)
|
504
|
-
{
|
505
|
-
'@type' => 'QuantitativeValue',
|
506
|
-
'value' => value,
|
507
|
-
'unitText' => unit,
|
508
|
-
'unitCode' => (UNIT_MAP[unit] if unit),
|
509
|
-
} if value
|
510
|
-
end
|
511
|
-
end
|