diff --git a/LaserStation.ods b/LaserStation.ods new file mode 100644 index 0000000000000000000000000000000000000000..4ab047fd2609a64d8ea1d72ec2162b7eaf00edf9 Binary files /dev/null and b/LaserStation.ods differ diff --git a/README.md b/README.md index e6062d80361d356a879bc5ad41559ee77df89579..5950b52dc4c1dc75e56cb2ad970b6d5c31af243e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # pcb-laserstation +## Digikey part search tool + +This tool provides the automated fetching of price and availability information for the BOM file. It uses the Digikey API. Follow the steps to use this tool. + +1. Install the required Python modules +2. Sign up for the Digikey API, create an application for using the part searching API. See https://github.com/peeter123/digikey-api for more information on the Python module used for the Digikey API access. +3. Rename the file "digikey_parts_no_values.json" to "digikey_parts.json" and enter the API information +4. In the same folder, create a "digikey" subfolder which is used by the Digikey Python module cache. +5. Export the BOM in ods or xlsx format. In row 1, the column header "DIGIKEY" marks the column with Digikey part numbers. If a Digikey part number is not available in a row, it is skipped. Column header "Qty" must be filled in. Column headers "DIGIKEY_PRICE" and "DIGIKEY_STOCK" must exist and are filled in by this tool. +6. Use this tool by typing 'python3 digikey_parts.py "LaserStation.ods" "10 100"'. This also calculates the total price for a total of 10 and 100 manufactured units (USD). \ No newline at end of file diff --git a/digikey_parts.py b/digikey_parts.py new file mode 100644 index 0000000000000000000000000000000000000000..d3236de141ff64d684ad505be23a5e21e11ecbce --- /dev/null +++ b/digikey_parts.py @@ -0,0 +1,76 @@ +import os +import sys +import digikey +from digikey.v3.productinformation import KeywordSearchRequest +import json +import pandas as pd + +# check if number of arguments is correct +if (len(sys.argv) == 1): + print("No filename given") + sys.exit() +elif (len(sys.argv) == 2): + quantities_given = False +elif (len(sys.argv) == 3): + quantities_given = True +else: + print("Too many arguments") + sys.exit() + +# check if file exists +try: + filename = sys.argv[1] +except ValueError: + print("Invalid file name") + sys.exit() + +# read the configuration file +conf_file = open("digikey_parts.json") +conf = json.load(conf_file) +conf_file.close() + +# set the digikey module parameters +os.environ['DIGIKEY_CLIENT_ID'] = conf['digikey_api_conf']["DIGIKEY_CLIENT_ID"] +os.environ['DIGIKEY_CLIENT_SECRET'] = conf['digikey_api_conf']["DIGIKEY_CLIENT_SECRET"] +os.environ['DIGIKEY_CLIENT_SANDBOX'] = "False" +os.environ['DIGIKEY_STORAGE_PATH'] = "./digikey/" + +# read the input table +table = pd.read_excel(filename) + +# Function for minimizing the per part price +def minimize_price(part, qty): + minimum_price = max(part.standard_pricing[0].total_price / qty, part.standard_pricing[0].unit_price) + minimum_price_index = 0 + for prices in part.standard_pricing: + price = max(prices.total_price / qty, prices.unit_price) + if price < minimum_price: + minimum_price = price + return minimum_price + +part_list = [] +qty_list = [] + +# iterate over the rows in the DIGIKEY column of the input table +for i in range(table["DIGIKEY"].size): + if pd.notna(table["DIGIKEY"].iloc[i]): # check if an entry in the DIGIKEY column exists at row i + part = digikey.product_details(table["DIGIKEY"].iloc[i]) + if pd.notna(part): # check if the digikey module returned something + table["DIGIKEY_PRICE"].iloc[i] = part.unit_price + table["DIGIKEY_STOCK"].iloc[i] = part.quantity_available + qty_list.append(table["Qty"].iloc[i]) + part_list.append(part) + +# write the output table +table.to_excel(excel_writer="output.ods") + +# calculate the total price for all requested quantities +if quantities_given: + quantities = list(map(int, sys.argv[2].split())) + total_price = 0 + for qty in quantities: + for part, qty_per_unit in zip(part_list, qty_list): + total_price = total_price + (minimize_price(part, qty_per_unit * qty) * qty_per_unit) + print("At a total number of " + str(qty) + " manufactured units, the unit price is " + str(total_price)) + total_price = 0 + diff --git a/digikey_parts_no_values.json b/digikey_parts_no_values.json new file mode 100644 index 0000000000000000000000000000000000000000..77ce59e0d29615c2860bff2c5ff2c93dc44ec3e9 --- /dev/null +++ b/digikey_parts_no_values.json @@ -0,0 +1,6 @@ +{ + "digikey_api_conf":{ + "DIGIKEY_CLIENT_ID":"", + "DIGIKEY_CLIENT_SECRET":"" + } +} \ No newline at end of file diff --git a/pcbdata.json b/pcbdata.json index cb85f4b32fe42687e2395183530cdeb8238352ba..7529956b02537e9356b11a81c36f7c6ef1be71c3 100644 --- a/pcbdata.json +++ b/pcbdata.json @@ -236,6 +236,7 @@ var pcbdata = { [1,"10.1k metal film","R0603",["RA305"],"NAME;POPULARITY;SPICEPREFIX;VALUE","RA305;70;R;10.1k metal film","F"], [1,"10k","R0603",["RA204"],"NAME;POPULARITY;SPICEPREFIX;VALUE","RA204;70;R;10k","F"], [1,"10k","R0603",["RA603"],"NAME;POPULARITY;SPICEPREFIX;VALUE","RA603;70;R;10k","F"], + [1,"10k","R0603",["RA695"],"NAME;POPULARITY;SPICEPREFIX;VALUE","RA695;70;R;10k","F"], [1,"10k","R0603",["RA706"],"NAME;POPULARITY;SPICEPREFIX;VALUE","RA706;70;R;10k","F"], [1,"10k","R0603",["RA805"],"NAME;POPULARITY;SPICEPREFIX;VALUE","RA805;70;R;10k","F"], [1,"10k","R0603",["RA1031"],"NAME;POPULARITY;SPICEPREFIX;VALUE","RA1031;70;R;10k","F"], @@ -429,7 +430,7 @@ var pcbdata = { }, "metadata": { "company": "", - "date": "2021-06-22 12:47:28", + "date": "2022-05-10 20:02:48", "revision": "", "title": "LaserStation" }, @@ -8040,10 +8041,10 @@ var pcbdata = { }, "CA615": { "bbox":{ - "pos":[28.06100,49.42227], + "pos":[27.76100,50.42227], "size":[2.07800,3.75546], }, - "center":[29.10000,51.30000], + "center":[28.80000,52.30000], "drawings":[], "layer":["F"], "pads":[ @@ -8052,7 +8053,7 @@ var pcbdata = { "layers":["F"], "offset": [0.0,0.0], "pin1":1, - "pos": [29.10000,50.45000], + "pos": [28.80000,51.45000], "shape": "rect", "size": [1.10000,1.00000], "type": "smd" @@ -8062,7 +8063,7 @@ var pcbdata = { "layers":["F"], "offset": [0.0,0.0], "pin1":0, - "pos": [29.10000,52.15000], + "pos": [28.80000,53.15000], "shape": "rect", "size": [1.10000,1.00000], "type": "smd" @@ -16626,8 +16627,8 @@ var pcbdata = { }, "RA691": { "bbox":{ - "pos":[27.84258,44.98525], - "size":[1.91484,1.82950], + "pos":[27.84258,45.04275], + "size":[1.91484,1.71450], }, "center":[28.80000,45.90000], "drawings":[], @@ -16752,6 +16753,38 @@ var pcbdata = { ], "ref":"RA694" }, + "RA695": { + "bbox":{ + "pos":[27.94275,47.97433], + "size":[1.71450,2.25135], + }, + "center":[28.80000,49.10000], + "drawings":[], + "layer":["F"], + "pads":[ + { + "angle": 270.00000, + "layers":["F"], + "offset": [0.0,0.0], + "pin1":1, + "pos": [28.80000,49.95000], + "shape": "rect", + "size": [1.00000,1.10000], + "type": "smd" + }, + { + "angle": 270.00000, + "layers":["F"], + "offset": [0.0,0.0], + "pin1":0, + "pos": [28.80000,48.25000], + "shape": "rect", + "size": [1.00000,1.10000], + "type": "smd" + }, + ], + "ref":"RA695" + }, "RA701": { "bbox":{ "pos":[70.30269,98.01100],