# models/database_models.py - Updated with Marketplace Support from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Index, UniqueConstraint, Boolean from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from datetime import datetime Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) email = Column(String, unique=True, index=True, nullable=False) username = Column(String, unique=True, index=True, nullable=False) hashed_password = Column(String, nullable=False) role = Column(String, nullable=False, default="user") # 'admin' or 'user' is_active = Column(Boolean, default=True, nullable=False) last_login = Column(DateTime, nullable=True) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) def __repr__(self): return f"" class Product(Base): __tablename__ = "products" id = Column(Integer, primary_key=True, index=True) product_id = Column(String, unique=True, index=True, nullable=False) title = Column(String, nullable=False) description = Column(String) link = Column(String) image_link = Column(String) availability = Column(String, index=True) # Index for filtering price = Column(String) brand = Column(String, index=True) # Index for filtering gtin = Column(String, index=True) # Index for stock lookups mpn = Column(String) condition = Column(String) adult = Column(String) multipack = Column(Integer) is_bundle = Column(String) age_group = Column(String) color = Column(String) gender = Column(String) material = Column(String) pattern = Column(String) size = Column(String) size_type = Column(String) size_system = Column(String) item_group_id = Column(String) google_product_category = Column(String, index=True) # Index for filtering product_type = Column(String) custom_label_0 = Column(String) custom_label_1 = Column(String) custom_label_2 = Column(String) custom_label_3 = Column(String) custom_label_4 = Column(String) additional_image_link = Column(String) sale_price = Column(String) unit_pricing_measure = Column(String) unit_pricing_base_measure = Column(String) identifier_exists = Column(String) shipping = Column(String) currency = Column(String) # New marketplace fields marketplace = Column(String, index=True, nullable=True, default="Letzshop") # Index for marketplace filtering shop_name = Column(String, index=True, nullable=True) # Index for shop filtering created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) # Relationship to stock (one-to-many via GTIN) stock_entries = relationship("Stock", foreign_keys="Stock.gtin", primaryjoin="Product.gtin == Stock.gtin", viewonly=True) # Additional indexes for marketplace queries __table_args__ = ( Index('idx_marketplace_shop', 'marketplace', 'shop_name'), # Composite index for marketplace+shop queries Index('idx_marketplace_brand', 'marketplace', 'brand'), # Composite index for marketplace+brand queries ) def __repr__(self): return f"" class Stock(Base): __tablename__ = "stock" id = Column(Integer, primary_key=True, index=True) gtin = Column(String, index=True, nullable=False) # Foreign key relationship would be ideal location = Column(String, nullable=False, index=True) quantity = Column(Integer, nullable=False, default=0) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) # Composite unique constraint to prevent duplicate GTIN-location combinations __table_args__ = ( UniqueConstraint('gtin', 'location', name='uq_stock_gtin_location'), Index('idx_stock_gtin_location', 'gtin', 'location'), # Composite index for efficient queries ) def __repr__(self): return f"" class MarketplaceImportJob(Base): __tablename__ = "marketplace_import_jobs" id = Column(Integer, primary_key=True, index=True) status = Column(String, nullable=False, default="pending") # pending, processing, completed, failed, completed_with_errors source_url = Column(String, nullable=False) marketplace = Column(String, nullable=False, index=True, default="Letzshop") # Index for marketplace filtering shop_name = Column(String, nullable=False, index=True) # Index for shop filtering user_id = Column(Integer, ForeignKey('users.id')) # Foreign key to users table imported_count = Column(Integer, default=0) updated_count = Column(Integer, default=0) error_count = Column(Integer, default=0) total_processed = Column(Integer, default=0) error_message = Column(String) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) started_at = Column(DateTime) completed_at = Column(DateTime) # Relationship to user user = relationship("User", foreign_keys=[user_id]) # Additional indexes for marketplace import job queries __table_args__ = ( Index('idx_marketplace_import_user_marketplace', 'user_id', 'marketplace'), # User's marketplace imports Index('idx_marketplace_import_shop_status', 'shop_name', 'status'), # Shop import status ) def __repr__(self): return f""