Refactoring code for modular approach
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from models.database_models import Product
|
||||
from models.api_models import ProductCreate
|
||||
from models.database_models import Product, Stock
|
||||
from models.api_models import ProductCreate, ProductUpdate, StockLocationResponse, StockSummaryResponse
|
||||
from utils.data_processing import GTINProcessor, PriceProcessor
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, Generator
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -41,6 +42,10 @@ class ProductService:
|
||||
logger.info(f"Created product {db_product.product_id}")
|
||||
return db_product
|
||||
|
||||
def get_product_by_id(self, db: Session, product_id: str) -> Optional[Product]:
|
||||
"""Get a product by its ID"""
|
||||
return db.query(Product).filter(Product.product_id == product_id).first()
|
||||
|
||||
def get_products_with_filters(
|
||||
self,
|
||||
db: Session,
|
||||
@@ -48,7 +53,9 @@ class ProductService:
|
||||
limit: int = 100,
|
||||
brand: Optional[str] = None,
|
||||
category: Optional[str] = None,
|
||||
availability: Optional[str] = None,
|
||||
marketplace: Optional[str] = None,
|
||||
shop_name: Optional[str] = None,
|
||||
search: Optional[str] = None
|
||||
) -> tuple[List[Product], int]:
|
||||
"""Get products with filtering and pagination"""
|
||||
@@ -59,14 +66,20 @@ class ProductService:
|
||||
query = query.filter(Product.brand.ilike(f"%{brand}%"))
|
||||
if category:
|
||||
query = query.filter(Product.google_product_category.ilike(f"%{category}%"))
|
||||
if availability:
|
||||
query = query.filter(Product.availability == availability)
|
||||
if marketplace:
|
||||
query = query.filter(Product.marketplace.ilike(f"%{marketplace}%"))
|
||||
if shop_name:
|
||||
query = query.filter(Product.shop_name.ilike(f"%{shop_name}%"))
|
||||
if search:
|
||||
# Search in title, description, marketplace, and shop_name
|
||||
search_term = f"%{search}%"
|
||||
query = query.filter(
|
||||
(Product.title.ilike(search_term)) |
|
||||
(Product.description.ilike(search_term)) |
|
||||
(Product.marketplace.ilike(search_term))
|
||||
(Product.marketplace.ilike(search_term)) |
|
||||
(Product.shop_name.ilike(search_term))
|
||||
)
|
||||
|
||||
total = query.count()
|
||||
@@ -74,6 +87,114 @@ class ProductService:
|
||||
|
||||
return products, total
|
||||
|
||||
def update_product(self, db: Session, product_id: str, product_update: ProductUpdate) -> Product:
|
||||
"""Update product with validation"""
|
||||
product = db.query(Product).filter(Product.product_id == product_id).first()
|
||||
if not product:
|
||||
raise ValueError("Product not found")
|
||||
|
||||
# Update fields
|
||||
update_data = product_update.dict(exclude_unset=True)
|
||||
|
||||
# Validate GTIN if being updated
|
||||
if "gtin" in update_data and update_data["gtin"]:
|
||||
normalized_gtin = self.gtin_processor.normalize(update_data["gtin"])
|
||||
if not normalized_gtin:
|
||||
raise ValueError("Invalid GTIN format")
|
||||
update_data["gtin"] = normalized_gtin
|
||||
|
||||
# Process price if being updated
|
||||
if "price" in update_data and update_data["price"]:
|
||||
parsed_price, currency = self.price_processor.parse_price_currency(update_data["price"])
|
||||
if parsed_price:
|
||||
update_data["price"] = parsed_price
|
||||
update_data["currency"] = currency
|
||||
|
||||
for key, value in update_data.items():
|
||||
setattr(product, key, value)
|
||||
|
||||
product.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
logger.info(f"Updated product {product_id}")
|
||||
return product
|
||||
|
||||
def delete_product(self, db: Session, product_id: str) -> bool:
|
||||
"""Delete product and associated stock"""
|
||||
product = db.query(Product).filter(Product.product_id == product_id).first()
|
||||
if not product:
|
||||
raise ValueError("Product not found")
|
||||
|
||||
# Delete associated stock entries if GTIN exists
|
||||
if product.gtin:
|
||||
db.query(Stock).filter(Stock.gtin == product.gtin).delete()
|
||||
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Deleted product {product_id}")
|
||||
return True
|
||||
|
||||
def get_stock_info(self, db: Session, gtin: str) -> Optional[StockSummaryResponse]:
|
||||
"""Get stock information for a product by GTIN"""
|
||||
stock_entries = db.query(Stock).filter(Stock.gtin == gtin).all()
|
||||
if not stock_entries:
|
||||
return None
|
||||
|
||||
total_quantity = sum(entry.quantity for entry in stock_entries)
|
||||
locations = [
|
||||
StockLocationResponse(location=entry.location, quantity=entry.quantity)
|
||||
for entry in stock_entries
|
||||
]
|
||||
|
||||
return StockSummaryResponse(
|
||||
gtin=gtin,
|
||||
total_quantity=total_quantity,
|
||||
locations=locations
|
||||
)
|
||||
|
||||
def generate_csv_export(
|
||||
self,
|
||||
db: Session,
|
||||
marketplace: Optional[str] = None,
|
||||
shop_name: Optional[str] = None
|
||||
) -> Generator[str, None, None]:
|
||||
"""Generate CSV export with streaming for memory efficiency"""
|
||||
# CSV header
|
||||
yield ("product_id,title,description,link,image_link,availability,price,currency,brand,"
|
||||
"gtin,marketplace,shop_name\n")
|
||||
|
||||
batch_size = 1000
|
||||
offset = 0
|
||||
|
||||
while True:
|
||||
query = db.query(Product)
|
||||
|
||||
# Apply marketplace filters
|
||||
if marketplace:
|
||||
query = query.filter(Product.marketplace.ilike(f"%{marketplace}%"))
|
||||
if shop_name:
|
||||
query = query.filter(Product.shop_name.ilike(f"%{shop_name}%"))
|
||||
|
||||
products = query.offset(offset).limit(batch_size).all()
|
||||
if not products:
|
||||
break
|
||||
|
||||
for product in products:
|
||||
# Create CSV row with marketplace fields
|
||||
row = (f'"{product.product_id}","{product.title or ""}","{product.description or ""}",'
|
||||
f'"{product.link or ""}","{product.image_link or ""}","{product.availability or ""}",'
|
||||
f'"{product.price or ""}","{product.currency or ""}","{product.brand or ""}",'
|
||||
f'"{product.gtin or ""}","{product.marketplace or ""}","{product.shop_name or ""}"\n')
|
||||
yield row
|
||||
|
||||
offset += batch_size
|
||||
|
||||
def product_exists(self, db: Session, product_id: str) -> bool:
|
||||
"""Check if product exists by ID"""
|
||||
return db.query(Product).filter(Product.product_id == product_id).first() is not None
|
||||
|
||||
|
||||
# Create service instance
|
||||
product_service = ProductService()
|
||||
|
||||
Reference in New Issue
Block a user