188 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from typing import Optional
 | 
						|
 | 
						|
from sqlalchemy import select
 | 
						|
from sqlalchemy.orm import selectinload
 | 
						|
 | 
						|
from external.marketplace import YandexMarketplaceApi
 | 
						|
from external.marketplace.base.product_synchronizer import BaseProductSynchronizer
 | 
						|
from marketplaces.base.core import BaseMarketplaceController
 | 
						|
from models import Product, YandexProduct, ProductBarcode, ProductImage
 | 
						|
 | 
						|
 | 
						|
class YandexProductSynchronizer(BaseProductSynchronizer):
 | 
						|
    api: YandexMarketplaceApi
 | 
						|
 | 
						|
    def _try_get_param(self, offer: dict, param: str) -> Optional[str]:
 | 
						|
        params = offer.get('params')
 | 
						|
        if not params:
 | 
						|
            return None
 | 
						|
        for p in params:
 | 
						|
            if p['name'] == param:
 | 
						|
                return str(p['value'])
 | 
						|
        return None
 | 
						|
 | 
						|
    def _create_product(self, offer: dict) -> Product:
 | 
						|
        return Product(
 | 
						|
            client_id=self.marketplace.client_id,
 | 
						|
            name=offer['name'],
 | 
						|
            article=offer['offerId'],
 | 
						|
            brand=self._try_get_param(offer, 'Бренд'),
 | 
						|
            size=self._try_get_param(offer, 'Размер'),
 | 
						|
            color=self._try_get_param(offer, 'Цвет товара'),
 | 
						|
            composition=self._try_get_param(offer, 'Состав материала'),
 | 
						|
        )
 | 
						|
 | 
						|
    def _create_barcodes(self, product: Product, offer: dict):
 | 
						|
        barcodes = []
 | 
						|
        for sku in offer['barcodes']:
 | 
						|
            barcode = ProductBarcode(
 | 
						|
                product=product,
 | 
						|
                barcode=sku
 | 
						|
            )
 | 
						|
            barcodes.append(barcode)
 | 
						|
        return barcodes
 | 
						|
 | 
						|
    def _create_images(self, product: Product, offer: dict):
 | 
						|
        product_images = []
 | 
						|
        images = offer.get('pictures', [])
 | 
						|
        for image in images[:1]:
 | 
						|
            product_image = ProductImage(
 | 
						|
                product=product,
 | 
						|
                image_url=image
 | 
						|
            )
 | 
						|
            product_images.append(product_image)
 | 
						|
        return product_images
 | 
						|
 | 
						|
    def _create_ym_product(self, product: Product):
 | 
						|
        return YandexProduct(
 | 
						|
            marketplace_id=self.marketplace.id,
 | 
						|
            product=product,
 | 
						|
        )
 | 
						|
 | 
						|
    async def create_products(self):
 | 
						|
        self._clear()
 | 
						|
 | 
						|
        synchronized_articles = await self._get_synchronized_products()
 | 
						|
        async for product in self.api.get_all_products():
 | 
						|
            try:
 | 
						|
                offer = product.get('offer')
 | 
						|
                if not offer:
 | 
						|
                    continue
 | 
						|
                if offer['offerId'] in synchronized_articles:
 | 
						|
                    continue
 | 
						|
                product = self._create_product(offer)
 | 
						|
                self.products.append(product)
 | 
						|
                barcodes = self._create_barcodes(product, offer)
 | 
						|
                product.barcodes.extend(barcodes)
 | 
						|
                images = self._create_images(product, offer)
 | 
						|
                product.images.extend(images)
 | 
						|
                ym_product = self._create_ym_product(product)
 | 
						|
                self.marketplace_products.append(ym_product)
 | 
						|
            except Exception as e:
 | 
						|
                print(e)
 | 
						|
        await self._write()
 | 
						|
 | 
						|
    def _update_barcodes(self, product: Product, offer: dict):
 | 
						|
        existing_barcodes = {barcode.barcode for barcode in product.barcodes}
 | 
						|
        new_barcodes = []
 | 
						|
        for barcode in offer['barcodes']:
 | 
						|
            if barcode not in existing_barcodes:
 | 
						|
                barcode = ProductBarcode(
 | 
						|
                    product=product,
 | 
						|
                    barcode=barcode
 | 
						|
                )
 | 
						|
                new_barcodes.append(barcode)
 | 
						|
        return new_barcodes
 | 
						|
 | 
						|
    def _update_images(self, product: Product, offer: dict):
 | 
						|
        existing_images = {image.image_url for image in product.images}
 | 
						|
        new_images = []
 | 
						|
        images = offer.get('pictures', [])
 | 
						|
        for image in images[:1]:
 | 
						|
            if image not in existing_images:
 | 
						|
                product_image = ProductImage(
 | 
						|
                    product=product,
 | 
						|
                    image_url=image
 | 
						|
                )
 | 
						|
                new_images.append(product_image)
 | 
						|
        return new_images
 | 
						|
 | 
						|
    async def _update_product(self, product: Product, offer: dict):
 | 
						|
        product.name = offer['name']
 | 
						|
        product.brand = self._try_get_param(offer, 'Бренд')
 | 
						|
        product.size = self._try_get_param(offer, 'Размер')
 | 
						|
        product.color = self._try_get_param(offer, 'Цвет товара')
 | 
						|
        product.composition = self._try_get_param(offer, 'Состав материала')
 | 
						|
 | 
						|
        barcodes = self._update_barcodes(product, offer)
 | 
						|
        product.barcodes.extend(barcodes)
 | 
						|
        images = self._update_images(product, offer)
 | 
						|
        product.images.extend(images)
 | 
						|
 | 
						|
    async def synchronize_products(self):
 | 
						|
        self._clear()
 | 
						|
        synchronized_products = (
 | 
						|
            select(
 | 
						|
                Product
 | 
						|
            )
 | 
						|
            .options(
 | 
						|
                selectinload(Product.barcodes),
 | 
						|
                selectinload(Product.images),
 | 
						|
            )
 | 
						|
            .select_from(
 | 
						|
                YandexProduct
 | 
						|
            )
 | 
						|
            .join(
 | 
						|
                Product
 | 
						|
            )
 | 
						|
            .where(
 | 
						|
                YandexProduct.marketplace_id == self.marketplace.id
 | 
						|
            )
 | 
						|
        )
 | 
						|
        result = await self.session.execute(synchronized_products)
 | 
						|
        synchronized_products = result.scalars().all()
 | 
						|
        synchronized_products_dict = {product.article: product for product in synchronized_products}
 | 
						|
        synchronized_articles = list(set(synchronized_products_dict.keys()))
 | 
						|
        async for product in self.api.get_products_by_offer_ids(synchronized_articles):
 | 
						|
            try:
 | 
						|
                offer = product.get('offer')
 | 
						|
                if not offer:
 | 
						|
                    continue
 | 
						|
                article = offer['offerId']
 | 
						|
                if article not in synchronized_articles:
 | 
						|
                    continue
 | 
						|
                product = synchronized_products_dict[article]
 | 
						|
                await self._update_product(product, offer)
 | 
						|
            except Exception as e:
 | 
						|
                print(f'Error: {e}')
 | 
						|
                continue
 | 
						|
        await self._write()
 | 
						|
 | 
						|
    async def _get_synchronized_products(self):
 | 
						|
        stmt = (
 | 
						|
            select(
 | 
						|
                Product.article
 | 
						|
            )
 | 
						|
            .select_from(
 | 
						|
                YandexProduct
 | 
						|
            )
 | 
						|
            .join(
 | 
						|
                Product
 | 
						|
            )
 | 
						|
        )
 | 
						|
        result = await self.session.execute(stmt)
 | 
						|
        return set(result.scalars().all())
 | 
						|
 | 
						|
 | 
						|
class YandexController(BaseMarketplaceController):
 | 
						|
 | 
						|
    def __init__(self, session, marketplace):
 | 
						|
        super().__init__(session, marketplace)
 | 
						|
        self.synchronizer = YandexProductSynchronizer(session, marketplace, self.api)
 | 
						|
 | 
						|
    async def synchronize_products(self):
 | 
						|
        await self.synchronizer.synchronize_products()
 | 
						|
 | 
						|
    async def create_products(self):
 | 
						|
        await self.synchronizer.create_products()
 |