From e91f02df33da10e7f18ac5c582f08134e9bea3c8 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Fri, 25 Sep 2015 16:52:19 +0200 Subject: wip --- .gitignore | 1 + category_search.py | 16 ----- category_search_cmd.py | 16 +++++ octopart.py | 167 ------------------------------------------------ octopart/__init__.py | 2 + octopart/core.py | 149 ++++++++++++++++++++++++++++++++++++++++++ octopart/part_search.py | 83 ++++++++++++++++++++++++ part_search.py | 83 ------------------------ part_search_cmd.py | 24 +++++++ 9 files changed, 275 insertions(+), 266 deletions(-) delete mode 100644 category_search.py create mode 100644 category_search_cmd.py delete mode 100644 octopart.py create mode 100644 octopart/__init__.py create mode 100644 octopart/core.py create mode 100644 octopart/part_search.py delete mode 100644 part_search.py create mode 100644 part_search_cmd.py diff --git a/.gitignore b/.gitignore index f40c3d8..035433e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.sqlite *.pyc +venv diff --git a/category_search.py b/category_search.py deleted file mode 100644 index 680df09..0000000 --- a/category_search.py +++ /dev/null @@ -1,16 +0,0 @@ -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/category_search_cmd.py b/category_search_cmd.py new file mode 100644 index 0000000..680df09 --- /dev/null +++ b/category_search_cmd.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 deleted file mode 100644 index 1d7527b..0000000 --- a/octopart.py +++ /dev/null @@ -1,167 +0,0 @@ -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/octopart/__init__.py b/octopart/__init__.py new file mode 100644 index 0000000..798bcd7 --- /dev/null +++ b/octopart/__init__.py @@ -0,0 +1,2 @@ +from core import * +from part_search import * diff --git a/octopart/core.py b/octopart/core.py new file mode 100644 index 0000000..8278b29 --- /dev/null +++ b/octopart/core.py @@ -0,0 +1,149 @@ +import json +import requests +import requests_cache +import iso8601 + +import sys +reload(sys) +sys.setdefaultencoding("utf-8") + +requests_cache.install_cache(cache_name='http_cache', backend='sqlite', expire_after=180) + +apikey = '2452f140' +base_url = "http://octopart.com/api/v3" + +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + return type('Enum', (), enums) + +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 + +# ############################################################################# +# Part Search diff --git a/octopart/part_search.py b/octopart/part_search.py new file mode 100644 index 0000000..438cb67 --- /dev/null +++ b/octopart/part_search.py @@ -0,0 +1,83 @@ +from octopart import enum +import octopart + +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 = octopart.get('/parts/search', p) + return octopart.SearchResponse(json) + +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 + + diff --git a/part_search.py b/part_search.py deleted file mode 100644 index faa3f03..0000000 --- a/part_search.py +++ /dev/null @@ -1,83 +0,0 @@ -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/part_search_cmd.py b/part_search_cmd.py new file mode 100644 index 0000000..76f2c28 --- /dev/null +++ b/part_search_cmd.py @@ -0,0 +1,24 @@ +import octopart + +search = octopart.ResistorSearch() +search['resistance'] = 1000 +search['package_type'] = octopart.PackageType.smd +search['case'] = '1206' +search['seller'] = 'farnell' + +search_response = octopart.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 -- cgit v1.2.3