Last active
August 29, 2015 14:02
-
-
Save surjikal/78be475b6e3c0aa6d319 to your computer and use it in GitHub Desktop.
Sears Hackathon
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
_ = require 'lodash' | |
ent = require 'ent' | |
assert = require 'assert' | |
Promise = require 'bluebird' | |
SearsAPI = require './sears-api' | |
CONSUMER_KEY = '<your key here>' | |
getTopItems = (store, api) -> | |
mapCategory = (category) -> | |
category.VerticalName | |
parsePrice = (price) -> | |
throw new Error("missing price") if not price | |
return price if not _.isNaN(parseFloat(price)) | |
price = price.replace(',','').replace(' ', '').match(/(\$\d+\.\d\d)/)[0] | |
throw new Error("price parse error") if not price | |
price = price.replace('$', '') | |
return parseFloat(price).toString() if not _.isNaN(parseFloat(price)) | |
throw new Error("price parse error") | |
mapItem = (item) -> | |
id: item.Id.SkuPartNumber or item.Id.PartNumber | |
brand: ent.decode item.Description.BrandName or '' | |
name: ent.decode item.Description.Name | |
price: parsePrice (item.Price.DisplayPrice or item.Availability.FreeShippingThreshold) | |
_orig: JSON.stringify(item) | |
_store: store | |
fetchTopItems = (category) -> | |
console.error "--> fetching to items for category `#{category}`" | |
api.topItems( | |
category: category | |
startIndex: '0' | |
endIndex: '100' | |
).then (items) -> | |
return [] if not items | |
items.map (item) -> | |
item = mapItem(item) | |
item.category = category | |
item.price = "10.00" if item.id is '00344821000' | |
Object.keys(item).forEach (key) -> | |
assert not _.isUndefined(item[key]), "Item key `#{key}` is undefined for item: #{JSON.stringify(item)}" | |
Object.keys(item).reduce ((result, key) -> | |
result[key] = item[key].toLowerCase() if key isnt '_orig' | |
return result | |
), {} | |
api.categories() | |
.then (categories) -> | |
categories = categories.map(mapCategory) | |
categories = _.difference categories, ['Automotive', 'Food & Grocery', 'Seasonal', 'Shipping', 'Gifts'] | |
# Merge all the item sets together, preserving rank | |
Promise.all(categories.map(fetchTopItems)) | |
.then (itemSets) -> | |
result = [] | |
while itemSets.reduce(((sum, x) -> sum + x.length), 0) > 0 | |
nextItemSets = [] | |
itemSets.forEach (itemSet) -> | |
item = itemSet.shift() | |
result.push(item) if item | |
nextItemSets.push(itemSet) if itemSet.length isnt 0 | |
itemSets = nextItemSets | |
return result | |
apis = | |
sears: new SearsAPI({store:'sears', consumerKey:CONSUMER_KEY}) | |
kmart: new SearsAPI({store:'kmart', consumerKey:CONSUMER_KEY}) | |
stores = Object.keys(apis) | |
result = [] | |
allTopItems = Promise.all stores.map (store) -> | |
getTopItems(store, apis[store]).then (items) -> | |
result = result.concat items | |
return items | |
allTopItems.then -> | |
result = _.values result.reduce ((seen, item) -> | |
seen[item.id] = item if not result[item.id] | |
return seen | |
), {} | |
return _.groupBy result, (x) -> | |
store = x._store | |
delete x._store | |
return store | |
.then (result) -> | |
console.log JSON.stringify(result, null, 2) |
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
{ | |
"name": "sears", | |
"version": "0.0.42", | |
"description": "", | |
"main": "app.coffee", | |
"author": "42", | |
"license": "Public Domain", | |
"dependencies": { | |
"bluebird": "~2.0.6", | |
"lodash": "~2.4.1", | |
"request": "~2.36.0", | |
"underscore.string": "~2.3.3", | |
"assert": "~1.1.1", | |
"ent": "~2.0.0" | |
} | |
} |
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
_ = require 'lodash' | |
_.str = require 'underscore.string' | |
assert = require 'assert' | |
request = require 'request' | |
Promise = require 'bluebird' | |
class SearsAPI | |
headers: | |
"Pragma": "no-cache" | |
"Accept-Encoding": "" | |
"Accept-Language": "en-US,en;q=0.8" | |
"User-Agent": "42.crawler.sears" | |
"Accept": "application/json, text/javascript, */*; q=0.01" | |
"Connection": "keep-alive" | |
"Cache-Control": "no-cache" | |
baseUrl: 'http://api.developer.sears.com' | |
STORES: {'sears', 'kmart', 'mygopher'} | |
constructor: ({@consumerKey, @store} = {}) -> | |
throw new ReferenceError("Missing required `consumerKey` property.") if not @consumerKey | |
throw new ReferenceError("Missing required `storeName` property.") if not @store | |
throw new ReferenceError("Store `#{@store}` is not supported") if not (@store.toLowerCase() in Object.keys(@STORES)) | |
categories: (options = {}) => | |
@call("/v2.1/products/browse/categories/#{@store}/json", options) | |
.then (result) -> result.SearchResults.Verticals | |
topItems: (options = {}) => | |
throw new ReferenceError("Missing required `category` property.") if not options.category | |
@call("/v2.1/products/browse/topSellers/#{@store}/json/searchType/unit", options) | |
.then (result) -> result.SearchResults.Products | |
call: (endpoint, options = {}) -> | |
escape = encodeURIComponent | |
queryString = do => | |
options = JSON.parse(JSON.stringify(options)) | |
options.apikey = @consumerKey | |
return Object.keys(options) | |
.filter((key) -> options[key] isnt undefined) | |
.map((key) -> [_.str.underscored(key), escape(options[key])]) | |
.map((pair) -> pair.join('=')) | |
.join('&') | |
requestOptions = | |
method: 'GET' | |
json: true | |
url: "#{@baseUrl}#{endpoint}?#{queryString}" | |
headers: @headers | |
deferred = Promise.defer() | |
request requestOptions, (err, res, body) -> | |
return deferred.reject(err) if err | |
return deferred.reject(body) if res.statusCode isnt 200 | |
return deferred.resolve(body) | |
return deferred.promise | |
module.exports = SearsAPI |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment