Skip to content

Instantly share code, notes, and snippets.

@projectgus
Last active December 20, 2015 07:29
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 projectgus/6093951 to your computer and use it in GitHub Desktop.
Save projectgus/6093951 to your computer and use it in GitHub Desktop.
At CCHS hackerspace we make a lot of group parts orders to save shipping costs. This script can split up a a DigiKey order's total by participant (assuming each person's name is listed in the Customer Reference field for the order.)
#!/usr/bin/env python3
"""
Digikey order splitter script for group buys. For Python 3.x. Public Domain.
Supports cases where people are paying in a different currency to the Digikey currency,
and also pro rata splitting of shipping costs.
If you use this then please sanity check the results before sending them
Usage:
* Go to Digikey "My Digikey" -> "Web Order History" -> find the order (must be Submitted)
* Select "Download Order" -> "CSV w/ Header"
* Run script as:
digikey_ordersplit.py <order CSV file> [optional total amount]
Names of people in the order must have been used to fill the "Customer
Reference" field. Only the first word (up to a space) is used for the
name, which allows you to also add item-specific comments
(designators, etc.) in the field if necessary.
The optional argument "total amount" is for if people are paying in a
different currency to the Digikey order currency. All amounts are
adjusted pro rata to meet the new total.
If the order includes a shipping charge then this is split up pro rata
between the order participants.
Uses floating point math everywhere because lazy.
TODO: Support the situation where multiple people order the same thing.
"""
import sys, csv, re
def main():
if len(sys.argv) < 2 or len(sys.argv)> 3:
print("Usage: digikey_ordersplit <order CSV file w/ header> [optional total amount]")
return
with open(sys.argv[1], 'r') as orderfile:
reader = csv.reader(orderfile)
# Skip the general order details to the "Index" subheader above the specifics
for row in reader:
if len(row) > 0 and row[0] == "Index":
break
participants = split_order(reader)
shipping = find_shipping(reader)
apply_pro_rata_shipping(participants, shipping)
if len(sys.argv) == 3:
scale_order(participants, float(sys.argv[2]))
for name in participants:
print("%s:" % name)
for item in participants[name]:
print(" {:2} {:<25} {:<40} - {:>5.2f}".format(*item))
print("Total Share: {:>66.2f}\n".format(person_total(participants, name)))
print("Order total (check against invoice): {:.2f}".format(order_total(participants)))
def split_order(csv):
""" Split an order into a list of order items for each participant
Items are tuples (qty, code, item, total price)
"""
participants = {}
for row in csv:
if row[0] == "":
return participants # have reached subtotal row
name = row[4].title().split(" ")[0]
if not name in participants:
participants[name] = []
qty = int(row[1])
price = parse_price(row[8])
participants[name].append((qty,row[2],row[3],price))
return participants # should never get here but might if the CSV is the non-header kind
def find_shipping(csv):
""" Find the Shipping line and return the shipping total """
for row in csv:
if len(row) > 7 and row[6] == "Shipping":
return parse_price(row[7])
return 0.0
def apply_pro_rata_shipping(participants, shipping):
""" Apply a pro rata shipping charge to each participant, add it to their
list of items"""
if shipping == 0:
return
subtotal = order_total(participants)
shipping = shipping
for name in participants:
share = person_total(participants, name) / subtotal * shipping
participants[name].append((1,"","Share of Shipping",share))
def scale_order(participants, actual_total):
""" Scale everything in the order so the order total reaches actual_total """
old_total = order_total(participants)
for name in participants:
items = participants[name]
participants[name] = [(qty,part,desc,cost/old_total*actual_total)
for (qty,part,desc,cost) in items]
def person_total(participants, name):
""" Return the total for a given person's share of the order """
return sum([p[3] for p in participants[name]])
def order_total(participants):
""" Return the total for the order as given across all participants """
return sum([person_total(participants, name) for name in participants])
def parse_price(price):
""" Given a price of the form AU$0.40 (or similar), return a float """
price = re.search(r"[\d\.]+$", price).group(0)
return float(price)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment