Coverage for app / services / asin_data_service.py: 83%
40 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-06 04:49 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-06 04:49 +0000
1"""
2This module provides functionality to interact with the ASIN Data API for
3retrieving detailed product information based on an ASIN (Amazon Standard
4Identification Number). The `fetch_product_details` function sends a request
5to the API and processes the response to extract meaningful product attributes,
6such as title, authors, categories, ratings, and ISBN. The module is
7designed to be integrated with a Flask application and requires a valid API key
8to function.
9"""
10import requests
11from flask import current_app
13from app.helpers.utilities import sanitize, sanitize_categories_flat
16# Fetch the product details from the ASIN Data API
17def fetch_product_details(asin):
18 """
19 Fetches detailed product information using the ASIN Data API.
21 This function retrieves product details for a given ASIN (Amazon Standard
22 Identification Number) by performing a GET request to the ASIN Data
23 API. It processes the API response and extracts specific data attributes
24 related to the product, such as the title, rating, authors, description,
25 ISBNs, etc. The result is returned as a structured dictionary.
27 :param asin: A string representing the ASIN (Amazon Standard
28 Identification Number) of the desired product to fetch details for.
29 :return: A dictionary containing structured product details fetched
30 from the API, including attributes such as title, author, image,
31 ISBNs, categories, and more. Returns an empty dictionary
32 if the product details are unavailable.
33 :raises ValueError: If the API key for ASIN Data API is missing
34 in the application configuration.
35 :raises HTTPError: If the HTTP request to the ASIN Data API fails or
36 returns a response with a status code indicating an error.
37 """
38 # Retrieve the api_key from app.config
39 api_key = current_app.config.get('ASIN_DATA_API_KEY')
40 if not api_key:
41 raise ValueError('ASIN Data API key is missing from configuration!')
43 # See https://trajectdata.com/ecommerce/asin-data-api/
44 api_url = current_app.config.get('ASIN_DATA_API_URL', 'https://api.asindataapi.com/request')
46 # set up the request parameters
47 params = {
48 'api_key': api_key,
49 'amazon_domain': 'amazon.com',
50 'asin': asin,
51 'type': 'product',
52 'output': 'json'
53 }
54 # make the http GET request to ASIN Data API
55 response = requests.get(api_url, params, timeout=30)
56 response.raise_for_status() # Raise HTTPError for bad responses (4xx and 5xx)
58 catalog_data = response.json()
59 if catalog_data.get('product'): 59 ↛ 97line 59 didn't jump to line 97 because the condition on line 59 was always true
60 product = catalog_data.get('product')
61 if product: 61 ↛ 97line 61 didn't jump to line 97 because the condition on line 61 was always true
62 # these attributes are simple pass-throughs
63 attributes = [
64 'title',
65 'asin',
66 'book_description',
67 'rating',
68 'link',
69 'bestsellers_rank_flat',
70 'specifications_flat'
71 ]
72 return_value = {}
73 for attribute in attributes:
74 if product.get(attribute): 74 ↛ 73line 74 didn't jump to line 73 because the condition on line 74 was always true
75 return_value[attribute] = sanitize(product[attribute])
76 # Special processing attributes
77 if product.get('authors'): 77 ↛ 79line 77 didn't jump to line 79 because the condition on line 77 was always true
78 return_value['author'] = sanitize(product['authors'][0]['name'])
79 if product.get('categories'): 79 ↛ 82line 79 didn't jump to line 82 because the condition on line 79 was always true
80 cat_list = [c['name'] for c in product['categories'] if 'name' in c]
81 return_value['categories_flat'] = sanitize_categories_flat(' > '.join(cat_list))
82 if product.get('main_image'): 82 ↛ 84line 82 didn't jump to line 84 because the condition on line 82 was always true
83 return_value['image'] = product['main_image']['link']
84 if product.get('specifications'): 84 ↛ 96line 84 didn't jump to line 96 because the condition on line 84 was always true
85 specs = product['specifications']
86 hardcover_str = next((s['value'] for s in specs if s['name'] == 'Hardcover'), None)
87 if hardcover_str: 87 ↛ 89line 87 didn't jump to line 89 because the condition on line 87 was always true
88 return_value['hardcover'] = sanitize(hardcover_str)
89 isbn_10_str = next((s['value'] for s in specs if s['name'] == 'ISBN-10'), None)
90 if isbn_10_str: 90 ↛ 92line 90 didn't jump to line 92 because the condition on line 90 was always true
91 return_value['isbn_10'] = sanitize(isbn_10_str)
92 isbn_13_str = next((s['value'] for s in specs if s['name'] == 'ISBN-13'), None)
93 if isbn_13_str: 93 ↛ 96line 93 didn't jump to line 96 because the condition on line 93 was always true
94 return_value['isbn_13'] = sanitize(isbn_13_str)
96 return return_value
97 return {} # empty if errors