Skip to content

Instantly share code, notes, and snippets.

@sinebeef
Last active April 1, 2019 19:46
Show Gist options
  • Save sinebeef/8ea048da04ef4130f6d154de3fbd6f79 to your computer and use it in GitHub Desktop.
Save sinebeef/8ea048da04ef4130f6d154de3fbd6f79 to your computer and use it in GitHub Desktop.
Create woocommerce import for variable products from csv of variations
'''
CSV to JSON for Woocommerce Variation Products via wpallimport.
Woocommerce variable products need a parent product sku to attach to.
This script creates a pseudo parent from a clone of one of the children and
then assigns it a unique/modifed SKU and provides that modified SKU to the
children in the parent column/key.
As the source CSV is exported from a magento site there is a bit of parsing that
needs doing on certain fields for it to work with woocommerce importing.
wpallimport likes listed dicts format:
[{'name':'screw'},{'name':'hinge'}]
@url https://gist.github.com/sinebeef/8ea048da04ef4130f6d154de3fbd6f79
@date 20190327
'''
import re, csv, json, time
''' cat_set is used to add data to and echo out at the end for debugging'''
cat_set = set()
def randFile():
''' Create a random filename for use in save file '''
a = 'wpallimport_json_' + str(time.time()).split('.')[0] + '.json'
return a
def fix_titles_fix_sizes(a):
''' Ready to go '''
return a
'''The folling 3 functions deal with single products that are made into
variation products by spltting the name and creating the varaition attributes
from them'''
def woven_mesh(a):
tb = a['name'].split(' ')
a['name'] = tb[0] + ' ' + tb[1] + ' ' + tb[2]
a['size'] = tb[4] + ' ' + tb[5]
a['finish'] = tb[5]
return a
def polished_sheets(a):
'''Satin Polished Sheet - 1.0mm Satin Finish'''
tb = a['name'].split(' ')
a['name'] = tb[1] + ' ' + tb[2]
a['size'] = tb[4]
a['finish'] = tb[5]
return a
def perf_sheets(a):
''''Perforated Sheet - ø3mm Round Hole'''
tb = a['name'].split(' ')
a['name'] = tb[1] + ' ' + tb[2]
a['size'] = tb[4]
return a
def js_csv_to_list_of_dicts():
'''Read in CSV file and create list of dicts object whilst also creating
additional field for the parent sku to go for refernce from child'''
with open('magfix.csv', 'r', newline='') as csv_file:
a = []
csv_reader = csv.DictReader(csv_file, delimiter=',')
for row in csv_reader:
'''Skip Metal Hardware Products and also the test one...'''
if row['sku'].startswith('MH') or row['sku'] == 'TESTSKU1':
continue
'''
if not re.search(r'Angle Trim', row['name']):
continue
'''
'''These use regular expression to match titles that are then
passed to be turned into variation products'''
if re.search(r'Woven Wire Mesh', row['name']):
row = woven_mesh(row)
if re.search(r'Polished Sheet', row['name']):
row = polished_sheets(row)
if re.search(r'Finish Sheet', row['name']):
row = polished_sheets(row)
if re.search(r'Perforated Sheet', row['name']):
row = perf_sheets(row)
''' Redundant function'''
row = fix_titles_fix_sizes(row)
''' Catches any NULL categories'''
if row['category'] == 'NULL':
print(row)
'''Process the magento cat from 'Something::1' to 'Something' '''
row['category'] = row['category'].split(':')[0]
row['subcategory'] = row['subcategory'].split(':')[0]
'''Strip image filename from url /this/is/where/images/live.jpg'''
row['small_image'] = row['small_image'].split('/')[-1]
row['image'] = row['image'].split('/')[-1]
row['cad_image'] = row['cad_image'].split('/')[-1]
row['thumbnailxxx'] = row['thumbnailxxx'].split('/')[-1]
'''If price is null or empty make 0'''
if not row['price'] or row['price'] == 'NULL':
#print('There is a null price...')
row_price = float(0)
else:
row_price = float(row['price'])
row['price'] = row_price
'''if price is 0 then make no stock
so purchase is not possible'''
if row_price == 0 or row_price == float(0):
row_stock = 0
else:
row_stock = 69
row['stock'] = row_stock
'''If there is no weight, make it stock 0 so product
cannot be purchased for free'''
row_weight_value = 0
if not row['weight'] or row['weight'] == 'NULL':
row_weight_value = 0
else:
try:
row_weight_value = int(round(float(row['weight'])))
except:
print(row['sku'] + ' ' + row['weight'])
pass
if row_weight_value == 0:
row['js_stock'] = 0
row['weight'] = row_weight_value
if re.search(r'[0-9]', row['category']):
print(row)
row['category'] = row['category'].replace('&','and')
if row['category'] == 'Shackle':
row['category'] = 'Shackles'
# cat_set.add(row['attribute_set'])
'''Removes any instances of NULL in any value as woocommerce treats the word
NULL as a string where it should be set as nothing like: "" '''
tempDict = {}
for k,v in row.items():
if v == 'NULL':
v = ''
tempDict[k] = v
tempDict['js_parent'] = ''
a.append(tempDict)
return a
def js_uniq_list_of_names(b):
'''Return uniq list of product_name from dict of products, these names are used to
group together variations of the same product although with new parent sku method
this may be redundant however it is the only primary method of grouping for parent
child creation.'''
uniq_name_set = set()
for dictRow in b: uniq_name_set.add(dictRow['name'])
return uniq_name_set
def js_add_parent(dic, names):
'''Return LIST variation with additional parent from cloned child with _par added to
sku to make unique'''
'''append to a all the rows that match this product name'''
rows_that_match = []
for item in dic:
if item['name'] == names:
rows_that_match.append(item)
''' [ Create Cloned Parent Product ]
If product name appears more than once then it will be a variable
product item and needs a parent product. This parent will be created from
cloning the first item in the group and then nullifying the attributes so
as to not conflict with the origional product. Not knowing this caused me
hours of pain'''
if len(rows_that_match) > 1:
clone = rows_that_match[0].copy()
modified_parent_sku = clone['sku'] + '_par'
clone['sku'] = modified_parent_sku
for attr in ['finish','size','price','grade','material','option','weight','pitch','thickness','delivery_group']:
clone[attr] = ''
more_rows_that_match = []
for items in rows_that_match:
items['js_parent'] = modified_parent_sku
more_rows_that_match.append(items)
'''Insert the parent as the first list item because woocommerce needs it to be
first with the children following, This caused me more hours of pain'''
more_rows_that_match.insert(0, clone)
return more_rows_that_match
'''Only a single will be a simple product with no parent'''
else:
return rows_that_match
'''Create fresh array!'''
updated = []
'''Get the json formatted data from CSV with added fields parsed'''
data = js_csv_to_list_of_dicts()
'''Get list of unique product names from that data'''
uniq_names = js_uniq_list_of_names(data)
# Process through the names and create parent for all variations
for names in uniq_names:
updated += js_add_parent(data, names)
# Save the data to a json file which is a list of dicts that wpallimport loves
with open( randFile(),"w") as w:
json.dump(updated, w)
w.close()
foo = list(cat_set)
foo.sort()
for a in foo:
print(a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment