示例#1
0
def search_brand(query: str,
                 start: t.Optional[int] = None,
                 limit: t.Optional[int] = None,
                 sortby: t.Optional[t.List[t.Tuple[str, str]]] = None,
                 ) -> t.List[models.Brand]:
    client = OctopartClient()
    res = client.search_brand(
        query=query, start=start, limit=limit, sortby=sortby)
    return [models.Brand(bd.get('item', {})) for bd in res.get('results', [])]
示例#2
0
文件: api.py 项目: peeter123/octopart
def part(
    uid: str,
    includes: t.Optional[t.List[str]] = None,
    hide: t.Optional[t.List[str]] = None,
    show: t.Optional[t.List[str]] = None,
) -> models.Part:
    client = OctopartClient()
    part_res = client.part(uid, includes=includes, hide=hide, show=show)
    return models.Part(part_res)
示例#3
0
def search_seller(query: str,
                  start: t.Optional[int] = None,
                  limit: t.Optional[int] = None,
                  sortby: t.Optional[t.List[t.Tuple[str, str]]] = None,
                  ) -> t.List[models.Seller]:
    client = OctopartClient()
    res = client.search_seller(
        query=query, start=start, limit=limit, sortby=sortby)
    return [
        models.Seller(res.get('item', {}), strict=False)
        for res in res.get('results', [])
    ]
示例#4
0
def search(query: str,
           start: int=0,
           limit: int=10,
           sortby: t.List[t.Tuple[str, str]]=None,
           filter_fields: t.Dict[str, str]=None,
           filter_queries: t.Dict[str, str]=None,
           show: t.List[str]=None,
           hide: t.List[str]=None,
           **kwargs
           ) -> models.PartsSearchResult:
    """
    Search Octopart for a general keyword (and optional filters).

    Args:
        query (str): Free-form keyword query

    Kwargs:
        start: Ordinal position of first result
        limit: Maximum number of results to return
        sortby: [(fieldname, order)] list of tuples
        filter_fields: {fieldname: value} dict
        filter_queries: {fieldname: value} dict
        include_*, e.g. include_cad_models (bool): by setting to True, the
            corresponding field is set in the include directive of the
            Octopart API call, resulting in optional information being
            returned (see enum `INCLUDES` for list of possible argument
            names)

    Returns:
        list of `models.PartsSearchResult` objects.
    """
    # assemble include[] directives as per
    # https://octopart.com/api/docs/v3/rest-api#include-directives
    includes = include_directives_from_kwargs(**kwargs)

    client = OctopartClient()
    response = client.search(
        query,
        start=start,
        limit=limit,
        sortby=sortby,
        filter_fields=filter_fields,
        filter_queries=filter_queries,
        includes=includes,
        show=show,
        hide=hide,
    )
    return models.PartsSearchResult(response)
示例#5
0
 def test_missing_api_token(self):
     # having the OCTOPART_API_KEY env var set could jinx this test, remove
     # it temporarily
     cached_env_var = os.environ.pop('OCTOPART_API_KEY', None)
     with pytest.raises(ValueError):
         OctopartClient()
     # if env var was set before, resurrect it
     if cached_env_var:
         os.environ['OCTOPART_API_KEY'] = cached_env_var
示例#6
0
class BrandSearchTests(TestCase):
    """Tests for the client's search_brand() and get_brand() methods"""
    def setUp(self):
        self.client = OctopartClient(api_key='TEST_TOKEN')

    def test_get_brand(self):
        response_body = {
            "__class__": "Brand",
            "homepage_url": "http://www.newark.com",
            "name": "Newark",
            "uid": "98785972bc7c4fbf"
        }

        with octopart_mock_response(response_body) as response:
            brand = self.client.get_brand('98785972bc7c4fbf')
            assert brand['uid'] == '98785972bc7c4fbf'
            called_url = request_url_from_request_mock(response)
            assert '/brands/98785972bc7c4fbf' in called_url

    def test_search_brand(self):
        response_body = {
            "__class__":
            "SearchResponse",
            "results": [{
                "__class__": "SearchResult",
                "item": {
                    "__class__": "Brand",
                    "homepage_url": "http://www.newark.com",
                    "name": "Newark",
                    "uid": "98785972bc7c4fbf"
                }
            }]
        }

        with octopart_mock_response(response_body) as response:
            response_data = self.client.search_brand('Newark')
            [result] = response_data['results']
            assert result['item']['name'] == 'Newark'
            called_url = request_url_from_request_mock(response)
            assert '/brands/search' in called_url
            assert 'q=Newark' in called_url
示例#7
0
 def setUp(self):
     self.client = OctopartClient(api_key='TEST_TOKEN')
示例#8
0
class SellerSearchTests(TestCase):
    """Tests for the client's search_seller() and get_seller() methods"""
    def setUp(self):
        self.client = OctopartClient(api_key='TEST_TOKEN')

    def test_get_seller(self):
        with octopart_mock_response() as response:
            self.client.get_seller('seller_uuid')
            called_url = request_url_from_request_mock(response)
            assert '/sellers/seller_uuid' in called_url

    def test_search_seller(self):
        response_body = {
            "__class__":
            "SearchResponse",
            "results": [{
                "__class__": "SearchResult",
                "item": {
                    "__class__": "Seller",
                    "display_flag": "US",
                    "has_ecommerce": True,
                    "homepage_url": "http://www.mouser.com",
                    "id": "2401",
                    "name": "Mouser",
                    "uid": "a5e060ea85e77627"
                }
            }]
        }

        with octopart_mock_response(response_body) as response:
            dict_ = self.client.search_seller('Mouser')
            [result] = dict_['results']
            assert result['item']['name'] == 'Mouser'
            called_url = request_url_from_request_mock(response)
            assert '/sellers/search' in called_url
            assert 'q=Mouser' in called_url

    def test_search_seller_with_sortby(self):
        response_body = {
            "__class__":
            "SearchResponse",
            "results": [{
                "__class__": "SearchResult",
                "item": {
                    "__class__": "Seller",
                    "display_flag": "US",
                    "has_ecommerce": True,
                    "homepage_url": "http://www.mouser.com",
                    "id": "2401",
                    "name": "Mouser",
                    "uid": "a5e060ea85e77627"
                }
            }]
        }

        with octopart_mock_response(response_body) as response:
            dict_ = self.client.search_seller('Mouser',
                                              sortby=[('name', 'asc')])
            assert 'results' in dict_
            called_url = request_url_from_request_mock(response)
            assert '/sellers/search' in called_url
            assert 'q=Mouser' in called_url
            assert 'sortby=name+asc' in called_url
示例#9
0
class CategorySearchTests(TestCase):
    """Tests for the client's search_category() and get_category() methods"""
    def setUp(self):
        self.client = OctopartClient(api_key='TEST_TOKEN')

    def test_get_category(self):
        response_body = {
            "__class__":
            "Category",
            "ancestor_names": ["Electronic Parts", "Passive Components"],
            "ancestor_uids": ["8a1e4714bb3951d9", "7542b8484461ae85"],
            "children_uids": [
                "db8a1680eaa4004d", "eb282622da0730ef", "e2992f93d3b3395e",
                "e7ca2ac0de173c0d", "f6473b4dcf9d2d80", "1db9d0b32462c67e",
                "e0b52dbfd96d3b72", "d820152ae1f903e7", "f0433d49c9e52b84"
            ],
            "name":
            "Capacitors",
            "num_parts":
            1395137,
            "parent_uid":
            "7542b8484461ae85",
            "uid":
            "f8883582c9a8234f"
        }

        with octopart_mock_response(response_body) as response:
            dict_ = self.client.get_category('f8883582c9a8234f')
            assert dict_['name'] == 'Capacitors'
            called_url = request_url_from_request_mock(response)
            assert '/categories/f8883582c9a8234f' in called_url

    def test_search_category(self):
        response_body = {
            "__class__":
            "SearchResponse",
            "results": [{
                "__class__": "SearchResult",
                "item": {
                    "__class__":
                    "Category",
                    "ancestor_names":
                    ["Electronic Parts", "Passive Components"],
                    "ancestor_uids": ["8a1e4714bb3951d9", "7542b8484461ae85"],
                    "children_uids": [
                        "db8a1680eaa4004d", "eb282622da0730ef",
                        "e2992f93d3b3395e", "e7ca2ac0de173c0d",
                        "f6473b4dcf9d2d80", "1db9d0b32462c67e",
                        "e0b52dbfd96d3b72", "d820152ae1f903e7",
                        "f0433d49c9e52b84"
                    ],
                    "name":
                    "Capacitors",
                    "num_parts":
                    1395137,
                    "parent_uid":
                    "7542b8484461ae85",
                    "uid":
                    "f8883582c9a8234f"
                }
            }]
        }

        with octopart_mock_response(response_body) as response:
            dict_ = self.client.search_category('Capacitors')
            [result] = dict_['results']
            assert result['item']['name'] == 'Capacitors'
            called_url = request_url_from_request_mock(response)
            assert '/categories/search' in called_url
            assert 'q=Capacitors' in called_url
示例#10
0
class PartMatchTests(TestCase):
    """Tests for the client's match() method"""
    def setUp(self):
        self.client = OctopartClient(api_key='TEST_TOKEN')

    @patch('requests.get')
    def test_too_many_match_queries(self, mock_get):
        queries = [{'q': 'fake-mpn'}] * 21
        with pytest.raises(ValueError):
            self.client.match(queries)
        # the exception should prevent any queries from being made
        assert not mock_get.called

    @patch('requests.get')
    def test_malformed_match_query(self, mock_get):
        with pytest.raises(OctopartError):
            self.client.match([{'q': ["not", "a", "string"]}])
        # the exception should prevent any queries from being made
        assert not mock_get.called

    def test_exact_match_true(self):
        response_body = {
            "__class__": "PartsMatchResponse",
            "results": [{
                "__class__": "PartsMatchResult",
                "items": []
            }]
        }

        with octopart_mock_response(response_body) as response:
            dict_ = self.client.match([{
                'q': 'ATTINY2313-20SU-zZz'
            }],
                                      exact_only=True)
            [result] = dict_['results']
            assert len(result['items']) == 0
            called_url = request_url_from_request_mock(response)
            assert '/parts/match' in called_url
            assert 'exact_only=true' in request_url_from_request_mock(response)

    def test_hide_directive(self):
        response_body = {
            "results": [{
                "__class__":
                "PartsMatchResult",
                "items": [{
                    "__class__":
                    "Part",
                    "brand": {
                        "__class__": "Brand",
                        "homepage_url": "http://www.microchip.com",
                        "name": "Microchip",
                        "uid": "ddaa1ca0f0300a6d"
                    },
                    "manufacturer": {
                        "__class__": "Manufacturer",
                        "homepage_url": "http://www.microchip.com",
                        "name": "Microchip",
                        "uid": "d78f1285fe150448"
                    },
                    "mpn":
                    "ATTINY2313-20SU",
                    "octopart_url":
                    "https://octopart.com/attiny2313-20su-microchip-77762028",  # noqa
                    "offers": [{
                        "__class__": "PartOffer",
                        "eligible_region": "",
                        "factory_lead_days": 94,
                        "factory_order_multiple": None,
                        "in_stock_quantity": 14517,
                        "is_authorized": True,
                        "is_realtime": False,
                        "last_updated": "2018-04-30T12:26:55Z",
                        "moq": 1,
                        "multipack_quantity": None,
                        "octopart_rfq_url": None,
                        "on_order_eta": None,
                        "on_order_quantity": None,
                        "order_multiple": None,
                        "packaging": "Tube",
                        "prices": {
                            "USD": [[1, "1.79000"], [25, "1.64320"],
                                    [100, "1.48400"]]
                        }
                    }],
                    "product_url":
                    "https://octopart.com/click/track?ak=a8cfd5a0&sig=09ff747&sid=459&ppid=77762028&vpid=408719850&ct=offers",  # noqa
                    "seller": {
                        "__class__": "Seller",
                        "display_flag": "US",
                        "has_ecommerce": True,
                        "homepage_url": "http://www.digikey.com",
                        "id": "459",
                        "name": "Digi-Key",
                        "uid": "2c3be9310496fffc"
                    },
                    "sku":
                    "ATTINY2313-20SU-ND"
                }]
            }]
        }

        with octopart_mock_response(response_body) as response:
            dict_ = self.client.match([{
                'q': 'ATTINY2313-20SU'
            }],
                                      hide=['offers'])
            [result] = dict_['results']
            [item] = result['items']
            assert item['mpn'] == "ATTINY2313-20SU"
            called_url = request_url_from_request_mock(response)
            assert '/parts/match' in called_url
            assert 'hide%5B%5D=offers' in called_url

    def test_show_directive(self):
        with octopart_mock_response() as response:
            self.client.match([{'q': 'FAKE_MPN'}], show=['offers'])
            called_url = request_url_from_request_mock(response)
            assert '/parts/match' in called_url
            assert 'show%5B%5D=offers' in called_url

    def test_include_directive(self):
        with octopart_mock_response() as response:
            self.client.match([{'q': 'FAKE_MPN'}], includes=['cad_models'])
            called_url = request_url_from_request_mock(response)
            assert '/parts/match' in called_url
            assert 'include%5B%5D=cad_models' in called_url

    def test_no_directives(self):
        with octopart_mock_response() as response:
            self.client.match([{'q': 'FAKE_MPN'}])
            called_url = request_url_from_request_mock(response)
            assert '/parts/match' in called_url
            assert 'hide%5B%5D=' not in called_url
            assert 'show%5B%5D=' not in called_url
            assert 'include%5B%5D=' not in called_url
            assert 'exact_only=' not in called_url

    def test_complete_example(self):
        with octopart_mock_response() as response:
            self.client.match([
                {
                    'mpn': 'MPN1',
                    'brand': 'Brand 1'
                },
                {
                    'mpn_or_sku': 'MPN-or#SKU2'
                },
            ],
                              exact_only=True,
                              show=['brand.name'],
                              includes=['imagesets'])
            called_url = request_url_from_request_mock(response)

            # %22brand%22%3A+%22Brand+1%22 is "brand": "Brand 1"
            assert '%22brand%22%3A+%22Brand+1%22' in called_url
            # %22mpn%22%3A+%22MPN1%22 is "mpn": "MPN1"
            assert '%22mpn%22%3A+%22MPN1%22' in called_url
            # %22mpn_or_sku%22%3A+%22MPN-or%23SKU2%22%7D%5D is
            # "22mpn_or_sku": "MPN-or#SKU2"
            assert '%22mpn_or_sku%22%3A+%22MPN-or%23SKU2%22%' in called_url
            assert 'exact_only=true' in called_url
            assert 'show%5B%5D=brand.name' in called_url
            assert 'include%5B%5D=imagesets' in called_url  # %5B%5D is []
示例#11
0
 def test_bad_api_token(self):
     client = OctopartClient(api_key='BAD_TOKEN')
     with pytest.raises(OctopartError):
         client.match([{'q': 'RUM001L02T2CL'}])
示例#12
0
class PartTests(TestCase):
    """Tests for the client's part() method"""
    def setUp(self):
        self.client = OctopartClient(api_key='TEST_TOKEN')

    @patch('requests.get')
    def test_malformed_uid(self, mock_get):
        with pytest.raises(OctopartError):
            self.client.part("this is not a 64-bit uid")
        # the exception should prevent any queries from being made
        assert not mock_get.called

    def test_hide_directive(self):
        response_body = {
            "__class__": "Part",
            "brand": {
                "__class__": "Brand",
                "homepage_url": "http://www.ohmite.com/",
                "name": "Ohmite",
                "uid": "574996437b3e808e"
            },
            "manufacturer": {
                "__class__": "Manufacturer",
                "homepage_url": "http://www.ohmite.com/",
                "name": "Ohmite",
                "uid": "5da998a375de8e6e"
            },
            "mpn": "D25K10KE",
            "octopart_url":
            "https://octopart.com/d25k10ke-ohmite-150892",  # noqa
            "redirected_uids": [],
            "uid": "3cc3f5cb54c9e304"
        }

        with octopart_mock_response(response_body) as response:
            result = self.client.part("3cc3f5cb54c9e304", hide=['offers'])
            assert result['mpn'] == "D25K10KE"
            called_url = request_url_from_request_mock(response)
            assert '/parts/3cc3f5cb54c9e304' in called_url
            assert 'hide%5B%5D=offers' in called_url

    def test_show_directive(self):
        response_body = {
            "offers": [{
                "__class__": "PartOffer",
                "eligible_region": "US",
                "factory_lead_days": None,
                "factory_order_multiple": None,
                "in_stock_quantity": 29,
                "is_authorized": True,
                "is_realtime": False,
                "last_updated": "2018-04-30T17:53:11Z",
                "moq": 1,
                "multipack_quantity": "1",
                "octopart_rfq_url": None,
                "on_order_eta": None,
                "on_order_quantity": None,
                "order_multiple": None,
                "packaging": None,
                "prices": {
                    "USD": [[1, "13.65000"], [10, "12.40000"],
                            [25, "11.17000"], [50, "10.56000"],
                            [100, "10.44000"], [250, "8.92000"],
                            [500, "8.75000"]]
                },
                "product_url":
                "https://octopart.com/click/track?ak=a8cfd5a0&sig=08c4b0d&sid=2402&ppid=150892&vpid=3009946&ct=offers",  # noqa
                "seller": {
                    "__class__": "Seller",
                    "display_flag": "US",
                    "has_ecommerce": True,
                    "homepage_url": "http://www.newark.com",
                    "id": "2402",
                    "name": "Newark",
                    "uid": "d294179ef2900153"
                },
                "sku": "64K4273"
            }]
        }

        with octopart_mock_response(response_body) as response:
            result = self.client.part("3cc3f5cb54c9e304", show=['offers'])
            [offer] = result['offers']
            assert offer['in_stock_quantity'] == 29
            called_url = request_url_from_request_mock(response)
            assert '/parts/3cc3f5cb54c9e304' in called_url
            assert 'show%5B%5D=offers' in called_url

    def test_include_directive(self):
        with octopart_mock_response() as response:
            self.client.part("3cc3f5cb54c9e304", includes=['cad_models'])
            called_url = request_url_from_request_mock(response)
            assert '/parts/3cc3f5cb54c9e304' in called_url
            assert 'include%5B%5D=cad_models' in called_url

    def test_no_directives(self):
        with octopart_mock_response() as response:
            self.client.part("3cc3f5cb54c9e304")
            called_url = request_url_from_request_mock(response)
            assert '/parts/3cc3f5cb54c9e304' in called_url
            assert 'hide%5B%5D=' not in called_url
            assert 'show%5B%5D=' not in called_url
            assert 'include%5B%5D=' not in called_url

    def test_complete_example(self):
        with octopart_mock_response() as response:
            self.client.part(
                "3cc3f5cb54c9e304",
                show=['brand.name'],
                includes=['imagesets'],
            )
            called_url = request_url_from_request_mock(response)
            assert '/parts/3cc3f5cb54c9e304' in called_url
            assert 'show%5B%5D=brand.name' in called_url  # %5B%5D is []
            assert 'include%5B%5D=imagesets' in called_url
示例#13
0
 def test_malformed_search_query(self):
     client = OctopartClient(api_key='TEST_TOKEN')
     with pytest.raises(OctopartError):
         client.search(["query1", "query2"])
示例#14
0
def get_brand(uid: str) -> models.Brand:
    client = OctopartClient()
    brand_dict = client.get_brand(uid)
    return models.Brand(brand_dict)
示例#15
0
def match(mpns: t.List[str],
          match_types: t.Optional[t.Tuple[str]] = None,
          partial_match: t.Optional[bool] = False,
          limit: t.Optional[int] = 3,
          sellers: t.Optional[t.Tuple[str]] = None,
          show: t.Optional[t.List[str]] = None,
          hide: t.Optional[t.List[str]] = None,
          **kwargs
          ) -> t.List[models.PartsMatchResult]:
    """
    Match a list of MPNs against Octopart.

    Args:
        mpns: list of str MPNs

    Kwargs:
        partial_match: whether to surround 'mpns' in wildcards to perform a
            partial part number match.
        limit: maximum number of results to return for each MPN
        sellers: list of str part sellers
        include_*, e.g. include_cad_models (bool): by setting to True, the
            corresponding field is set in the include directive of the
            Octopart API call, resulting in optional information being
            returned (see enum `IncludeDirectives` in directives.py for list of
            possible argument names)

    Returns:
        list of `models.PartsMatchResult` objects.
    """
    unique_mpns = utils.unique(mpns)
    match_types = match_types or (MatchType.MPN_OR_SKU,)
    sellers = sellers or ()

    if partial_match:
        # Append each MPN with a wildcard character so that Octopart performs
        # a partial match.
        unique_mpns = [f'{mpn}*' for mpn in unique_mpns]

    if not sellers:
        queries = [
            {
                match_type: mpn,
                'limit': limit,
                'reference': mpn,
            }
            for (match_type, mpn) in itertools.product(
                match_types, unique_mpns)
        ]
    else:
        queries = [
            {
                match_type: mpn,
                'seller': seller,
                'limit': limit,
                'reference': mpn,
            }
            for (match_type, mpn, seller) in itertools.product(
                match_types, unique_mpns, sellers)
        ]

    # assemble include[] directives as per
    # https://octopart.com/api/docs/v3/rest-api#include-directives
    includes = include_directives_from_kwargs(**kwargs)

    client = OctopartClient()

    def _request_chunk(chunk):
        return client.match(
            queries=chunk,
            includes=includes,
            show=show or [],
            hide=hide or [],
        )

    # Execute API calls concurrently to significantly speed up
    # issuing multiple HTTP requests.
    with ThreadPoolExecutor(max_workers=MAX_REQUEST_THREADS) as pool:
        responses = pool.map(_request_chunk, utils.chunk_queries(queries))

    return [
        models.PartsMatchResult(result)
        for response in responses
        for result in response['results']
    ]
示例#16
0
def get_category(uid: str) -> models.Category:
    client = OctopartClient()
    cat_dict = client.get_category(uid)
    return models.Category(cat_dict, strict=False)
示例#17
0
def get_seller(uid: str) -> models.Seller:
    client = OctopartClient()
    slr_dict = client.get_seller(uid)
    return models.Seller(slr_dict, strict=False)