Source code for oneworldsync.models

"""
Models for the 1WorldSync API

This module defines data models for the 1WorldSync API responses.
"""

from typing import Dict, List, Any, Optional, Union
from .utils import extract_product_data, get_primary_image, format_dimensions


[docs] class Product: """ Model representing a product from the 1WorldSync API """
[docs] def __init__(self, data): """ Initialize a product from API data Args: data (dict): Product data from the API """ self.data = data self.item = data.get('item', {}) # Extract structured data for easier access self._extracted_data = extract_product_data(data)
@property def item_id(self) -> str: """Get the primary item ID""" if self._extracted_data.get('item_id'): return self._extracted_data['item_id'] identifiers = self.item.get('itemIdentificationInformation', {}).get('itemIdentifier', []) for identifier in identifiers: if identifier.get('isPrimary') == 'true': return identifier.get('itemId') return None @property def gtin(self) -> str: """Get the GTIN (Global Trade Item Number)""" return self._extracted_data.get('gtin', '') @property def brand_name(self) -> str: """Get the brand name""" if self._extracted_data.get('brand_name'): return self._extracted_data['brand_name'] try: info = self.item.get('tradeItemInformation', [])[0] desc_module = info.get('tradeItemDescriptionModule', {}) desc_info = desc_module.get('tradeItemDescriptionInformation', [])[0] return desc_info.get('brandNameInformation', {}).get('brandName') except (IndexError, KeyError, TypeError): return None @property def product_name(self) -> str: """Get the product name""" if self._extracted_data.get('product_name'): return self._extracted_data['product_name'] try: info = self.item.get('tradeItemInformation', [])[0] desc_module = info.get('tradeItemDescriptionModule', {}) desc_info = desc_module.get('tradeItemDescriptionInformation', [])[0] reg_names = desc_info.get('regulatedProductName', []) if reg_names: return reg_names[0].get('statement', {}).get('values', [])[0].get('value') except (IndexError, KeyError, TypeError): return None @property def description(self) -> str: """Get the product description""" if self._extracted_data.get('description'): return self._extracted_data['description'] try: info = self.item.get('tradeItemInformation', [])[0] desc_module = info.get('tradeItemDescriptionModule', {}) desc_info = desc_module.get('tradeItemDescriptionInformation', [])[0] desc = desc_info.get('additionalTradeItemDescription', {}) return desc.get('values', [])[0].get('value') except (IndexError, KeyError, TypeError): return None @property def images(self) -> List[Dict[str, Any]]: """Get product images""" if self._extracted_data.get('images'): return self._extracted_data['images'] try: info = self.item.get('tradeItemInformation', [])[0] file_module = info.get('referencedFileDetailInformationModule', {}) file_headers = file_module.get('referencedFileHeader', []) images = [] for file_header in file_headers: if file_header.get('referencedFileTypeCode', {}).get('value') == 'PRODUCT_IMAGE': images.append({ 'url': file_header.get('uniformResourceIdentifier'), 'is_primary': file_header.get('isPrimaryFile', {}).get('value') == 'true' }) return images except (IndexError, KeyError, TypeError): return [] @property def primary_image_url(self) -> str: """Get the primary image URL""" return get_primary_image(self._extracted_data) @property def dimensions(self) -> Dict[str, Dict[str, str]]: """Get product dimensions""" if self._extracted_data.get('dimensions'): return self._extracted_data['dimensions'] try: info = self.item.get('tradeItemInformation', [])[0] measurements_group = info.get('tradeItemMeasurementsModuleGroup', [])[0] measurements_module = measurements_group.get('tradeItemMeasurementsModule', {}) measurements = measurements_module.get('tradeItemMeasurements', {}) return { 'height': { 'value': measurements.get('height', {}).get('value'), 'unit': measurements.get('height', {}).get('qual') }, 'width': { 'value': measurements.get('width', {}).get('value'), 'unit': measurements.get('width', {}).get('qual') }, 'depth': { 'value': measurements.get('depth', {}).get('value'), 'unit': measurements.get('depth', {}).get('qual') } } except (IndexError, KeyError, TypeError): return {} @property def formatted_dimensions(self) -> str: """Get formatted dimensions as a string""" return format_dimensions(self.dimensions) @property def gpc_code(self) -> str: """Get the GPC (Global Product Classification) code""" return self._extracted_data.get('gpc_code', '') @property def category(self) -> str: """Get the product category""" return self._extracted_data.get('category', '') @property def ingredients(self) -> str: """Get the product ingredients""" return self._extracted_data.get('ingredients', '') @property def country_of_origin(self) -> str: """Get the country of origin""" return self._extracted_data.get('country_of_origin', '')
[docs] def to_dict(self) -> Dict[str, Any]: """ Convert the product to a dictionary with all extracted data Returns: dict: Dictionary representation of the product """ return { 'item_id': self.item_id, 'gtin': self.gtin, 'brand_name': self.brand_name, 'product_name': self.product_name, 'description': self.description, 'primary_image_url': self.primary_image_url, 'images': self.images, 'dimensions': self.dimensions, 'formatted_dimensions': self.formatted_dimensions, 'gpc_code': self.gpc_code, 'category': self.category, 'ingredients': self.ingredients, 'country_of_origin': self.country_of_origin }
[docs] def __str__(self): """String representation of the product""" return f"{self.brand_name} - {self.product_name} ({self.item_id})"
[docs] class SearchResults: """ Model representing search results from the 1WorldSync API """
[docs] def __init__(self, data): """ Initialize search results from API data Args: data (dict): Search results data from the API """ self.data = data self.response_code = data.get('responseCode') self.response_message = data.get('responseMessage') self.total_results = int(data.get('totalNumOfResults', '0')) self.next_cursor = data.get('nextCursorMark') # Parse products self.products = [] for result in data.get('results', []): self.products.append(Product(result))
[docs] def __len__(self): """Get the number of products in the results""" return len(self.products)
[docs] def __iter__(self): """Iterate through products""" return iter(self.products)
[docs] def __getitem__(self, index): """Get a product by index""" return self.products[index]
[docs] def to_dict(self) -> Dict[str, Any]: """ Convert the search results to a dictionary Returns: dict: Dictionary representation of the search results """ return { 'metadata': { 'response_code': self.response_code, 'response_message': self.response_message, 'total_results': self.total_results, 'next_cursor': self.next_cursor }, 'products': [product.to_dict() for product in self.products] }