summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--category_search.py16
-rw-r--r--octopart.py167
-rw-r--r--part_search.py83
-rw-r--r--requirements.txt5
5 files changed, 273 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f40c3d8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.sqlite
+*.pyc
diff --git a/category_search.py b/category_search.py
new file mode 100644
index 0000000..680df09
--- /dev/null
+++ b/category_search.py
@@ -0,0 +1,16 @@
+import octopart
+
+#search_response = octopart.category_search_raw('resistor')
+#
+## print number of hits
+#print "Hits: {}".format(search_response['hits'])
+#
+## print results
+#for result in res['results']:
+# item = result['item']
+# print "{}: {}".format(item['uid'], item['name'])
+
+categories = octopart.category_search('resistor')
+
+for c in categories:
+ print "{}: {}".format(c.uid, c.name)
diff --git a/octopart.py b/octopart.py
new file mode 100644
index 0000000..1d7527b
--- /dev/null
+++ b/octopart.py
@@ -0,0 +1,167 @@
+import json
+import requests
+import requests_cache
+import iso8601
+
+import sys
+reload(sys)
+sys.setdefaultencoding("utf-8")
+
+apikey = '2452f140'
+base_url = "http://octopart.com/api/v3"
+
+def extract_int(json, key):
+ try:
+ val = json[key]
+ if val is not None:
+ return int(val)
+ return None
+ except KeyError:
+ return None
+
+def extract_date(json, key):
+ try:
+ val = json[key]
+ if val is not None:
+ return iso8601.parse_date(val)
+ return None
+ except KeyError:
+ return None
+
+class Category():
+ def __init__(self, json):
+ self.uid = json['uid']
+ self.name = json['name']
+ self.parent_uid = json['parent_uid']
+ self.children_uids = json['children_uids']
+ self.ancestor_uids = json['ancestor_uids']
+ self.ancestor_names = json['ancestor_names']
+ self.num_parts = json['num_parts']
+
+def map_price(json):
+ print 'price: ' + str(json)
+ return json
+
+class Price():
+ def __init__(self, currency, quantity, amount):
+ self.currency = currency
+ self.quantity = quantity
+ self.amount = amount
+
+class PartOffer():
+ def __init__(self, json):
+ self.sku = json['sku']
+# self.seller = Seller(json['seller'])
+ self.eligible_region = json['eligible_region']
+ self.product_url = json['product_url']
+ self.octopart_rfq_url = json['octopart_rfq_url']
+ self.prices = {}
+ for currency, breaks in json['prices'].items():
+ bs = self.prices[currency] = []
+ for b in breaks:
+ bs.append(Price(currency, b[0], float(b[1])))
+
+ self.in_stock_quantity = extract_int(json, 'in_stock_quantity')
+ self.on_order_quantity = extract_int(json, 'on_order_quantity')
+ self.on_eta = extract_date(json, 'on_eta')
+ self.factory_lead_days = extract_int(json, 'factory_lead_days')
+ self.factory_order_multiple = extract_int(json, 'factory_order_multiple')
+ self.order_multiple = extract_int(json, 'order_multiple')
+ self.moq = extract_int(json, 'moq')
+ self.last_updated = extract_date(json, 'last_updated')
+
+class Part():
+ def __init__(self, json):
+ self.uid = json['uid']
+ self.mpn = json['mpn']
+
+ self.offers = map(PartOffer, json['offers'])
+
+
+class SearchResult():
+ def __init__(self, json):
+ self.item = Part(json['item'])
+
+ # Undocumented
+ self.snippet = json['snippet']
+ pass
+
+class SearchResponse():
+ def __init__(self, json):
+ self.results = map(SearchResult, json['results'])
+ self.hits = json['hits']
+
+def params(p):
+ p['apikey'] = apikey
+ return p
+
+def get(path, p):
+ url = base_url + path
+# print('path: {}, params: {}'.format(path, p))
+ print('path: {}'.format(path))
+ for k, v in p.iteritems():
+ print(' {0:60} = {1}'.format(k, v))
+
+ res = requests.get(url, params=params(p))
+# print res.url
+ j = res.json()
+ print(json.dumps(j, indent=2, sort_keys=True))
+ return j
+
+class Seller():
+ def __init__(self, json):
+ self.uid = json['uid']
+ self.name = json['name']
+
+def seller_search_raw(q):
+ p = {'q': q, 'start': 0, 'limit': 10}
+ return get('/sellers/search', p)
+
+def seller_search(q):
+ res = seller_search_raw(q)
+
+ sellers = []
+ for result in res['results']:
+ item = result['item']
+ sellers.append(Seller(item))
+ return sellers
+
+def category_search_raw(q):
+ p = {'q': q, 'start': 0, 'limit': 10}
+ return get('/categories/search', p)
+
+def category_search(q):
+ res = category_search_raw(q)
+
+ categories = []
+ for result in res['results']:
+ item = result['item']
+ categories.append(Category(item))
+ return categories
+
+def part_search(q, start = 0, limit = False, fields={}, include=[]):
+ p = {'q': q, 'start': 0}
+ if limit:
+ p['limit'] = limit
+
+ for field, data in fields.iteritems():
+ if data is not None and data['q']:
+ p['filter[fields][' + field + '][]'] = data['q']
+# key = 'filter[fields][' + field + '][]'
+# if not key in p:
+# p[key] = arr = []
+# else:
+# arr = p[key]
+# arr.append(data['q'])
+ pass
+
+ if len(include) > 0:
+ p['include[]'] = include
+
+
+# p['spec_drilldown[include]'] = 'true'
+
+ json = get('/parts/search', p)
+ return SearchResponse(json)
+
+requests_cache.install_cache(cache_name='http_cache', backend='sqlite', expire_after=180)
diff --git a/part_search.py b/part_search.py
new file mode 100644
index 0000000..faa3f03
--- /dev/null
+++ b/part_search.py
@@ -0,0 +1,83 @@
+import octopart
+
+def enum(*sequential, **named):
+ enums = dict(zip(sequential, range(len(sequential))), **named)
+ return type('Enum', (), enums)
+
+PackageType = enum('through_hole', 'smd')
+
+class ResistorSearch():
+ def __init__(self):
+ self.params = {}
+ self.fields = ['package_type', 'case', 'resistance', 'tolerance', 'seller']
+
+ def has_key(self, key):
+ try:
+ self.fields.index(key)
+ except ValueError:
+ raise Exception('Invalid key for search: ' + key)
+
+ def __setitem__(self, key, item):
+ self.has_key(key)
+ self.params[key] = item
+
+ def __getitem__(self, key):
+ self.has_key(key)
+ return self.params[key]
+
+# lifecycle_status
+# specs.resistance_tolerance.value
+def resistor_search(search):
+ q = ''
+
+ categories = octopart.category_search('Resistors')
+ categories = filter(lambda c: c.name == 'Resistors', categories)
+ if len(categories) == 0:
+ raise Exception("Could not find category for resistors")
+ category = categories[0]
+
+ fields = {}
+# 'specs.case_package.value': {'q': '1206'},
+ fields['category_uids'] = {'q': category.uid}
+ fields['specs.resistance.value'] = {'q': search['resistance']}
+
+ # case_package is broken (always returns 0 hits), but it is usually in the description
+ if search['case'] is not None:
+ q += ' ' + search['case']
+
+# if package_type == PackageType.smd:
+# fields['specs.case_package.value'] = {'q': '1210'}
+
+ seller = search['seller']
+ if seller is not None:
+ sellers = octopart.seller_search(search['seller'])
+ if len(sellers) == 0:
+ raise Exception('Could not find seller: ' + seller)
+ fields['offers.seller.name'] = {'q': sellers[0].name}
+
+ res = octopart.part_search(q, limit=100, fields=fields)
+ return res
+
+#search_response = resistor_search(1000, PackageType.smd, '1206')
+search = ResistorSearch()
+search['resistance'] = 1000
+search['package_type'] = PackageType.smd
+search['case'] = '1206'
+search['seller'] = 'farnell'
+
+search_response = resistor_search(search)
+
+print "Hits: {}".format(search_response.hits)
+
+for search_result in search_response.results:
+ p = search_result.item
+
+ print "{}: Snippet: {}".format(p.uid, search_result.snippet)
+# print "Uid: {}".format(p.uid)
+# print "Offers: "
+# for o in p.offers:
+# print " SKU: {}".format(o.sku)
+# for (currency, prices) in o.prices.iteritems():
+# for price in prices:
+# print " {}: {}@{}".format(currency, price.amount, price.quantity)
+# pass
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..b7345fe
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,5 @@
+argparse==1.2.1
+iso8601==0.1.10
+requests==2.7.0
+requests-cache==0.4.10
+wsgiref==0.1.2