Skip to content

Instantly share code, notes, and snippets.

@wking
Last active September 18, 2015 22:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wking/cf87063bd5f76be73db0 to your computer and use it in GitHub Desktop.
Save wking/cf87063bd5f76be73db0 to your computer and use it in GitHub Desktop.
Upload product ↔ category associations
#!/usr/bin/env python3
#
# https://gist.github.com/wking/cf87063bd5f76be73db0
import csv
import json
import logging
import sys
import time
import urllib.request
logging.basicConfig(level=logging.INFO)
def read_categories(stream=sys.stdin):
categories = {'children': {}}
logging.info('loading categories')
for row in csv.DictReader(stream):
parent = categories
for field in ['Department', 'Aisle', 'Section', 'Shelf', 'Space']:
category = {
'name': row[field],
'short-name': row['{} URL'.format(field)],
'children': {},
}
if (category['name'] in ['', '*'] or
category['short-name'] in ['', '*']):
break
if category['name'] not in parent['children']:
parent['children'][category['name']] = category
parent = parent['children'][category['name']]
return categories
def upload_categories(base_url, headers, categories, parent_id=None):
logging.info('uploading categories')
if categories['children']:
current_categories = download_categories(
base_url=base_url, headers=headers, parent_id=parent_id)
for name, category in categories['children'].items():
category['parent'] = parent_id
if category['name'] not in current_categories:
category_id = upload_category(
base_url=base_url, headers=headers, category=category)
else:
category_id = current_categories[category['name']]
upload_categories(
base_url=base_url, headers=headers, categories=category,
parent_id=category_id)
def download_categories(base_url, headers, parent_id=None):
logging.info('download categories with parent {}'.format(parent_id))
if parent_id is None:
pid = 'null'
else:
pid = parent_id
request = urllib.request.Request(
url='{base}/categories?parent={pid}&limit=250'.format(
base=base_url, pid=pid),
headers=headers,
method='GET',
)
with urllib.request.urlopen(request) as response:
categories_bytes = response.read()
charset = response.headers.get_content_charset()
categories_json = categories_bytes.decode(charset)
categories = json.loads(categories_json)
name_ids = {cat['name']: cat['id'] for cat in categories}
logging.info('downloaded categories with parent {}: {}'.format(
parent_id, name_ids))
time.sleep(1)
return name_ids
def upload_category(base_url, headers, category):
data = category.copy()
data.pop('children')
logging.info('upload category {}'.format(data))
request = urllib.request.Request(
url='{base}/categories'.format(base=base_url),
data=json.dumps(data).encode('UTF-8'),
headers=headers,
method='POST',
)
with urllib.request.urlopen(request) as response:
new_category_bytes = response.read()
charset = response.headers.get_content_charset()
new_category_json = new_category_bytes.decode(charset)
new_category = json.loads(new_category_json)
logging.info('uploaded category {} with id {}'.format(
new_category['name'], new_category['id']))
time.sleep(1)
return new_category['id']
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('categories')
parser.add_argument('--base-url', default='https://api.azurestandard.com')
parser.add_argument(
'-H', '--header', action='append', default=[], dest='headers')
args = parser.parse_args()
headers = {}
for header in args.headers:
key, value = [x.strip() for x in header.split(':', 1)]
headers[key] = value
with open(args.categories, 'r') as stream:
categories = read_categories(stream=stream)
upload_categories(
base_url=args.base_url, headers=headers, categories=categories)
#!/usr/bin/env python3
#
# https://gist.github.com/wking/cf87063bd5f76be73db0
import csv
import json
import logging
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
logging.basicConfig(level=logging.INFO)
def get_categories(base_url, cache='/tmp/categories.json'):
try:
with open(cache, 'r') as stream:
categories_json = stream.read()
except FileNotFoundError:
logging.info('requesting categories')
categories = []
count = None
start = 0
limit = 250
while True:
logging.info('requesting categories ({} from {} of {})'.format(
limit, start, count))
with urllib.request.urlopen(
'{}/categories?{}'.format(
base_url,
urllib.parse.urlencode({
'start': start,
'limit': limit,
}))
) as response:
categories_bytes = response.read()
charset = response.headers.get_content_charset()
if count is None:
count = int(response.headers['Count'])
new_json = categories_bytes.decode(charset)
categories.extend(json.loads(new_json))
if len(categories) >= count:
break
start += limit
categories_json = json.dumps(categories)
with open(cache, 'w') as stream:
stream.write(categories_json)
categories = {}
cats = json.loads(categories_json)
for category in cats:
key = (category['name'], category.get('parent'))
categories[key] = category['id']
logging.debug('category {} -> {}'.format(key, category['id']))
return categories
def get_subheaders(base_url, headers, categories, stream=sys.stdin):
subheaders = {}
logging.info('loading subheaders')
for row in csv.DictReader(stream):
subheader = row['SubHeaders'].strip().lower()
if not subheader:
continue
parent_id = None
for field in ['Department', 'Aisle', 'Section', 'Shelf', 'Space']:
name = row[field]
if name in ['', '*']:
break
short_name = row['{} URL'.format(field)]
key = (name, parent_id)
try:
id = categories[key]
except KeyError as error:
logging.info('missing category {} ({})'.format(key, row))
try:
id = upload_category(
base_url=base_url, headers=headers, category={
'name': name,
'short-name': name,
'parent': parent_id,
})
except urllib.error.HTTPError:
with urllib.request.urlopen(
'{}/categories?{}'.format(
base_url,
urllib.parse.urlencode({'parent': parent_id}))
) as response:
categories_bytes = response.read()
charset = response.headers.get_content_charset()
categories_json = categories_bytes.decode(charset)
cats = json.loads(categories_json)
category = [cat for cat in cats if cat['name'] == name][0]
id = category['id']
categories[key] = id
parent_id = id
if subheader not in subheaders:
subheaders[subheader] = []
subheaders[subheader].append(id)
logging.debug('subheaders {} -> {}'.format(
subheader, subheaders[subheader]))
return subheaders
def upload_category(base_url, headers, category):
logging.info('upload category {}'.format(category))
request = urllib.request.Request(
url='{base}/categories'.format(base=base_url),
data=json.dumps(category).encode('UTF-8'),
headers=headers,
method='POST',
)
with urllib.request.urlopen(request) as response:
new_category_bytes = response.read()
charset = response.headers.get_content_charset()
new_category_json = new_category_bytes.decode(charset)
new_category = json.loads(new_category_json)
logging.info('uploaded category {} with id {}'.format(
new_category['name'], new_category['id']))
time.sleep(1)
return new_category['id']
def associate_products(base_url, headers, subheaders, stream=sys.stdin):
logging.info('associating products')
missing = set()
for row in csv.DictReader(stream):
subheader = row['SubHeaders'].strip().lower()
if subheader in [
'soon to be discontinued',
]:
continue
code = row['Item code'].strip()
try:
category_ids = subheaders[subheader]
except KeyError as error:
if subheader not in missing:
logging.error(str(error))
missing.add(subheader)
continue
for category_id in category_ids:
associate_product(
base_url=base_url, headers=headers,
code=code, category_id=category_id)
def associate_product(base_url, headers, code, category_id):
logging.info('associate {} with {}'.format(code, category_id))
request = urllib.request.Request(
url='{base}/packaged-product/{code}/category/{id}'.format(
base=base_url, code=code, id=category_id),
headers=headers,
method='POST',
)
with urllib.request.urlopen(request) as response:
logging.info('associated {} with {}'.format(code, category_id))
time.sleep(1)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('categories')
parser.add_argument('products')
parser.add_argument('--base-url', default='https://api.azurestandard.com')
parser.add_argument(
'-H', '--header', action='append', default=[], dest='headers')
args = parser.parse_args()
headers = {}
for header in args.headers:
key, value = [x.strip() for x in header.split(':', 1)]
headers[key] = value
categories = get_categories(base_url=args.base_url)
with open(args.categories, 'r') as stream:
subheaders = get_subheaders(
base_url=args.base_url, headers=headers,
categories=categories, stream=stream)
with open(args.products, 'r') as stream:
associate_products(
base_url=args.base_url, headers=headers,
subheaders=subheaders, stream=stream)
@wking
Copy link
Author

wking commented Jan 11, 2015

I spent a day trying to connect the missing subheaders with fix_subheaders, but eventually gave up (about 60% of the way through the product file). These were the remaining subheaders that weren't listed in the category file:

ERROR:root:'bath tissue, toilet paper'
ERROR:root:'bedding-pillows'
ERROR:root:'beeswax, bulk'
ERROR:root:'bleach'
ERROR:root:'bleach, chlorine free'
ERROR:root:'candle, beeswax'
ERROR:root:'candles, soy'
ERROR:root:'canning supplies'
ERROR:root:'cheesecloth'
ERROR:root:'cleaner, all purpose'
ERROR:root:'cleaner, bathroom'
ERROR:root:'cleaner, carpet shampoo'
ERROR:root:'cleaner, degreaser'
ERROR:root:'cleaner, drain & septic system treatment'
ERROR:root:'cleaner, glass'
ERROR:root:'cleaner, soy'
ERROR:root:'cleaner, toilet'
ERROR:root:'cleaners'
ERROR:root:'cleaners, indoor/outdoor'
ERROR:root:'cleaners, the home collection'
ERROR:root:'cleaning, cloths'
ERROR:root:'clothes, socks'
ERROR:root:'clothes, t-shirts'
ERROR:root:'coffee filters'
ERROR:root:'container, tote for multiple uses'
ERROR:root:'cooking twine'
ERROR:root:'cutlery'
ERROR:root:'dish soap, automatic'
ERROR:root:'dishwasher detergent'
ERROR:root:'disinfecting wipes'
ERROR:root:'fabric softener'
ERROR:root:'facial tissue'
ERROR:root:'feminine hygiene products'
ERROR:root:'firelighters'
ERROR:root:'food covers'
ERROR:root:'laundry booster & hard water treatment'
ERROR:root:'laundry care products'
ERROR:root:'laundry detergent'
ERROR:root:'laundry soap'
ERROR:root:'laundry soap, cold water'
ERROR:root:'laundry, soap nuts'
ERROR:root:'laundry, stain & odor remover'
ERROR:root:'lid for plastic pail'
ERROR:root:'lid remover'
ERROR:root:'lid, spout for pouring'
ERROR:root:'lids'
ERROR:root:'light bulbs'
ERROR:root:'light bulbs, 100 watt'
ERROR:root:'light bulbs, 13 watt'
ERROR:root:'light bulbs, 60 watt'
ERROR:root:'light bulbs, bug lights'
ERROR:root:'light bulbs, night lights'
ERROR:root:'light bulbs, twist'
ERROR:root:'mug'
ERROR:root:'mylar bags with oxygen absorbers'
ERROR:root:'napkins'
ERROR:root:'odor eliminator'
ERROR:root:'odor eliminator for refrigerator'
ERROR:root:'oxygen absorbers'
ERROR:root:'paper bowls'
ERROR:root:'paper plates'
ERROR:root:'paper towels'
ERROR:root:'parchment paper'
ERROR:root:'personal hygiene products'
ERROR:root:'plastic wrap'
ERROR:root:'produce wash'
ERROR:root:'pump for containers'
ERROR:root:'soap berries'
ERROR:root:'soap, coconut oil'
ERROR:root:'soap, dishwashing'
ERROR:root:'soaps & cleaners chemical sensitive'
ERROR:root:'sponges'
ERROR:root:'storage, container'
ERROR:root:'storage, glass jar, no lid'
ERROR:root:'storage, lid, for pail'
ERROR:root:'storage, lids metal'
ERROR:root:'storage, pail/bucket'
ERROR:root:'trash bags'
ERROR:root:'trash bags, biodigradable'
ERROR:root:'wax paper'
ERROR:root:'alkalizing support'
ERROR:root:'alkalizing support, tea'
ERROR:root:'aloe vera crystals'
ERROR:root:'bee pollen'
ERROR:root:'beverages nutritional , mineral drink'
ERROR:root:'beverages, super food drink mix'
ERROR:root:'coral calcuim, products'
ERROR:root:'cordyceps power'
ERROR:root:'feminine, female enhancments'
ERROR:root:'fulvic acid'
ERROR:root:'grapefruit seed extract products'
ERROR:root:'hemp drink mixes'
ERROR:root:'human growth hormone'
ERROR:root:'juice beverages, nutritional'
ERROR:root:'juice powders, supplements, nutritional, greens'
ERROR:root:'lecithin'
ERROR:root:'maca products & smoothie blends'
ERROR:root:'maca products, extracts'
ERROR:root:'nutritional supplements'
ERROR:root:'probiotics'
ERROR:root:'probiotics, yogurt starter'
ERROR:root:'protein powder'
ERROR:root:'protein, hemp powder'
ERROR:root:'protien powder'
ERROR:root:'royal jelly'
ERROR:root:'salve, silver colloidal'
ERROR:root:'silver biotics'
ERROR:root:'silver, colloidal'
ERROR:root:'spirit reishi'
ERROR:root:'super food'
ERROR:root:'supplement, sprays'
ERROR:root:'supplements & minerals'
ERROR:root:'supplements, acidophilus'
ERROR:root:'supplements, amino acids'
ERROR:root:'supplements, children'
ERROR:root:'supplements, chlorophyll'
ERROR:root:'supplements, clay products'
ERROR:root:'supplements, dietary'
ERROR:root:'supplements, enzymes'
ERROR:root:'supplements, fiber'
ERROR:root:'supplements, for pain relief & stress management'
ERROR:root:'supplements, liquid'
ERROR:root:'supplements, minerals'
ERROR:root:'supplements, nutritional'
ERROR:root:'supplements, nutritional minerals'
ERROR:root:'supplements, nutritional powders, drink mix'
ERROR:root:'supplements, nutritional powders, weight loss'
ERROR:root:'supplements, nutritional, cod liver oil products'
ERROR:root:'supplements, nutritional, enzymes'
ERROR:root:'supplements, nutritional, greens'
ERROR:root:'supplements, nutritional, omega boost'
ERROR:root:'supplements, nutritional, vitamins'
ERROR:root:'supplements, probiotics'
ERROR:root:'supplements, special formulas'
ERROR:root:'supplements, vitamins'
ERROR:root:'supplements, weightloss support'
ERROR:root:'supplements. sleeping aid'
ERROR:root:'symptom relief, feminine'
ERROR:root:'symptom relief, for cold sore'
ERROR:root:'vitamins'
ERROR:root:'water, alkaline'
ERROR:root:'weight gain'
ERROR:root:'wellness drink'
ERROR:root:'yeast, nutritional'
ERROR:root:'chocolate covered nuts'
ERROR:root:'nuts, almonds'
ERROR:root:'nuts, brazil nuts'
ERROR:root:'nuts, cashews'
ERROR:root:'nuts, hazelnuts'
ERROR:root:'nuts, macadamia nuts'
ERROR:root:'nuts, peanuts'
ERROR:root:'nuts, pecans'
ERROR:root:'nuts, pinenuts'
ERROR:root:'nuts, pistachios'
ERROR:root:'nuts, walnuts'
ERROR:root:'trail mix'
ERROR:root:'chia seed oil, artisan cold-pressed'
ERROR:root:'oil, almond'
ERROR:root:'oil, apricot kernel'
ERROR:root:'oil, avocado'
ERROR:root:'oil, canola'
ERROR:root:'oil, coconut'
ERROR:root:'oil, coconut mana'
ERROR:root:'oil, flax'
ERROR:root:'oil, flax seed'
ERROR:root:'oil, garlic'
ERROR:root:'oil, grapeseed'
ERROR:root:'oil, grapeseed oil'
ERROR:root:'oil, hemp'
ERROR:root:'oil, hemp seed'
ERROR:root:'oil, olive'
ERROR:root:'oil, palm'
ERROR:root:'oil, peanut'
ERROR:root:'oil, red palm'
ERROR:root:'oil, rice'
ERROR:root:'oil, rice bran'
ERROR:root:'oil, safflower'
ERROR:root:'oil, sesame'
ERROR:root:'oil, shortening'
ERROR:root:'oil, sprays'
ERROR:root:'oil, sunflower'
ERROR:root:'oil, walnut'
ERROR:root:'shortening, palmfruit'
ERROR:root:'mac-n-cheese'
ERROR:root:'noodes'
ERROR:root:'pasta, 3 color'
ERROR:root:'pasta, brown rice'
ERROR:root:'pasta, corn'
ERROR:root:'pasta, dinner mixes & seasonings'
ERROR:root:'pasta, egg noodles'
ERROR:root:'pasta, for kids'
ERROR:root:'pasta, gluten free'
ERROR:root:'pasta, jerusalem artichoke'
ERROR:root:'pasta, kamut'
ERROR:root:'pasta, kelp noodles'
ERROR:root:'pasta, quinoa'
ERROR:root:'pasta, rainbow'
ERROR:root:'pasta, salad mixes & seasonings'
ERROR:root:'pasta, semolina'
ERROR:root:'pasta, soba'
ERROR:root:'pasta, spelt'
ERROR:root:'pasta, spelt white'
ERROR:root:'pasta, spelt whole grain'
ERROR:root:'pasta, spinach'
ERROR:root:'pasta, sprouted grain'
ERROR:root:'pasta, udon'
ERROR:root:'pasta, vegetable'
ERROR:root:'pasta, white einkorn'
ERROR:root:'pasta, whole wheat'
ERROR:root:'pasta, whole wheat, einkorn'
ERROR:root:'bouillon cubes'
ERROR:root:'broth'
ERROR:root:'brown gravy'
ERROR:root:'dip mixes'
ERROR:root:'dip mixes'
ERROR:root:'gravy mixes'
ERROR:root:'gravy, mixes'
ERROR:root:'miso paste'
ERROR:root:'nutritional yeast extract'
ERROR:root:'sauce, mixes'
ERROR:root:'seasoning, & spice mixes'
ERROR:root:'seasoning, mixes'
ERROR:root:'seasonings, bean soup'
ERROR:root:'seasonings, popcorn'
ERROR:root:'seasonings, salsa'
ERROR:root:'seasonings, spaghetti'
ERROR:root:'soup, mixes'
ERROR:root:'candy, drops'
ERROR:root:'candy, ginger'
ERROR:root:'candy, lollipops'
ERROR:root:'candy, maple treats'
ERROR:root:'candy, pops'
ERROR:root:'candy, sweet tarts'
ERROR:root:'carob coated almonds'
ERROR:root:'carob coated nuts'
ERROR:root:'carob coated treats'
ERROR:root:'chocolate bar'
ERROR:root:'chocolate bar, mini'
ERROR:root:'chocolate bars'
ERROR:root:'chocolate covered cacao nibs'
ERROR:root:'chocolate covered fruit'
ERROR:root:'chocolate covered goji berries'
ERROR:root:'chocolate rainbow drops'
ERROR:root:'chocolate treats'
ERROR:root:'chocolate, raw'
ERROR:root:'cookies'
ERROR:root:'cookies, cream filled'
ERROR:root:'cookies, crunchy'
ERROR:root:'cookies, einkorn'
ERROR:root:'cookies, fig filled'
ERROR:root:'cookies, ginger shortbread'
ERROR:root:'cookies, maple waffle'
ERROR:root:'cookies, soft'
ERROR:root:'dessert topping'
ERROR:root:'dessert toppings'
ERROR:root:'energy bars'
ERROR:root:'fig bars'
ERROR:root:'fruit bars'
ERROR:root:'fruit leather'
ERROR:root:'fruit, nut & seed bars/ energy bars'
ERROR:root:'licorice'
ERROR:root:'mints'
ERROR:root:'snacks, sea vegtable crunch'
ERROR:root:'sweets'
ERROR:root:'yogurt covered fruit & nuts'
ERROR:root:'chia seeds, milled'
ERROR:root:'seeds, alfalfa'
ERROR:root:'seeds, broccoli'
ERROR:root:'seeds, caraway'
ERROR:root:'seeds, chia'
ERROR:root:'seeds, clover'
ERROR:root:'seeds, flax'
ERROR:root:'seeds, flaxseed cold milled'
ERROR:root:'seeds, hemp'
ERROR:root:'seeds, poppy'
ERROR:root:'seeds, pumpkin'
ERROR:root:'seeds, radish'
ERROR:root:'seeds, sesame'
ERROR:root:'seeds, sesame black'
ERROR:root:'seeds, sprouted snack packs'
ERROR:root:'seeds, sprouting mix'
ERROR:root:'seeds, sunflower'
ERROR:root:'sprouting seeds'
ERROR:root:'beef sticks'
ERROR:root:'buffalo jerky snacks'
ERROR:root:'builder bars'
ERROR:root:'bulk, trail mix'
ERROR:root:'cereal bars'
ERROR:root:'cheese puffs'
ERROR:root:'chips, bean'
ERROR:root:'chips, cassava'
ERROR:root:'chips, corn'
ERROR:root:'chips, multigrain'
ERROR:root:'chips, plentils'
ERROR:root:'chips, potato'
ERROR:root:'chips, rice'
ERROR:root:'chips, sea vegetable'
ERROR:root:'chips, tortilla'
ERROR:root:'chips, tortillia'
ERROR:root:'clif kid z bar, cookie'
ERROR:root:'coconut bars'
ERROR:root:'crackers'
ERROR:root:'crackers, animal'
ERROR:root:'crackers, classic'
ERROR:root:'crackers, flax snacks'
ERROR:root:'crackers, japanese rice'
ERROR:root:'crackers, lunch packs'
ERROR:root:'crackers, nut thins'
ERROR:root:'crackers, rice'
ERROR:root:'crackers, rice snaps'
ERROR:root:'crackers, rice toast'
ERROR:root:'crackers, sprouted'
ERROR:root:'fiber bars'
ERROR:root:'flatbread'
ERROR:root:'fruit snacks'
ERROR:root:'graham, crackers'
ERROR:root:'granola bars'
ERROR:root:'granola bars, chewy'
ERROR:root:'granola bars, crunchy'
ERROR:root:'granola, superfoods, raw'
ERROR:root:'granola, trail bars'
ERROR:root:'jerky, beef'
ERROR:root:'junobar'
ERROR:root:"kit's fruit & nut bars"
ERROR:root:'nuts & seeds, trail packs'
ERROR:root:'popcorn, microwavable'
ERROR:root:'popcorn, vegan'
ERROR:root:'popcorn, white cheddar'
ERROR:root:'pretzel snacks'
ERROR:root:'protein bars'
ERROR:root:'protein bars, gluten free'
ERROR:root:'pumpkin seed snack packs'
ERROR:root:'rice cakes'
ERROR:root:'savory bar'
ERROR:root:'seaweed crumbles, flavored'
ERROR:root:'seaweed snack packs'
ERROR:root:'sesame sticks'
ERROR:root:'snack mix'
ERROR:root:'super food kale chips'
ERROR:root:'superfoods, raw'
ERROR:root:'toaster pastries'
ERROR:root:'toaster pastries, frosted'
ERROR:root:'trail mix bars, mojo'
ERROR:root:'trail mix, bulk'
ERROR:root:'z bars nutritional, kids'
ERROR:root:'z fruit, fruit & veggie snacks for kids'
ERROR:root:'z fruit, fruit snacks for kids'
ERROR:root:''
ERROR:root:'butter, substitutes'
ERROR:root:'cheese alternatives'
ERROR:root:'cheese alternatives, shredded'
ERROR:root:'cream cheese alternative'
ERROR:root:'dips & spreads'
ERROR:root:'meat alternative, deli slices'
ERROR:root:'meat alternative, ground beef'
ERROR:root:'meat alternative, hot dogs'
ERROR:root:'meat alternative, sausage'
ERROR:root:'meat substitute'
ERROR:root:'meat substitutes, soy curls'
ERROR:root:'meat substitutes, vegetable protein'
ERROR:root:'milk, soy'
ERROR:root:'milk, substitute'
ERROR:root:'milk, substitute powders'
ERROR:root:'milk, substitutes'
ERROR:root:'sour cream, alternative'
ERROR:root:'tempeh'
ERROR:root:'tofu'
ERROR:root:'sweeteners, agave syrup'
ERROR:root:'sweeteners, agave, syrup'
ERROR:root:'sweeteners, barley malt'
ERROR:root:'sweeteners, brown sugar'
ERROR:root:'sweeteners, cane juice crystals'
ERROR:root:'sweeteners, cane sugar crystals'
ERROR:root:'sweeteners, coconut crystals'
ERROR:root:'sweeteners, coconut nectar'
ERROR:root:'sweeteners, coconut palm sugar'
ERROR:root:'sweeteners, coconut sugar'
ERROR:root:'sweeteners, corn syrup'
ERROR:root:'sweeteners, date sugar'
ERROR:root:'sweeteners, erythritol'
ERROR:root:'sweeteners, fructose'
ERROR:root:'sweeteners, fruit sweeteners'
ERROR:root:'sweeteners, honey'
ERROR:root:'sweeteners, honey crystals'
ERROR:root:'sweeteners, honey infused'
ERROR:root:'sweeteners, manuka honey'
ERROR:root:'sweeteners, maple butter'
ERROR:root:'sweeteners, maple sugar'
ERROR:root:'sweeteners, maple syrup'
ERROR:root:'sweeteners, molasses'
ERROR:root:'sweeteners, powdered sugar'
ERROR:root:'sweeteners, rice syrup'
ERROR:root:'sweeteners, sorghum'
ERROR:root:'sweeteners, stevia'
ERROR:root:'sweeteners, stevia, squeeze bottle'
ERROR:root:'sweeteners, sunroot'
ERROR:root:'sweeteners, turbinado'
ERROR:root:'sweeteners, xylitol'
ERROR:root:'sweeteners, yacon'
ERROR:root:'slim tea'

@wking
Copy link
Author

wking commented Feb 25, 2015

For the live upload, I dropped fix_subheaders. I converted the Excel files to CSV and ran:

$ ./category-upload.py -H 'Authorization: Basic ....' Category\ UI\ and\ URL\ FINAL.csv
$ ./product-category-upload.py -H 'Authorization: Basic ...' Category\ UI\ and\ URL\ FINAL.csv Catalog\ Data\ Source\ File\ FINAL.csv

The existing-categories download in product-category-upload.py doesn't work with the new limited queries from azurestandard/beehive@2728652 (Merge branch 'api-limit', 2015-02-20), but I worked around that by seeding /tmp/categories.json with data dumped from a Django shell. Now that we support start (azurestandard/api-spec@86d4d29, public.json: Add 'start=...' for offsetting list results, 2015-02-24), you could go that way too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment