diff --git a/Makefile b/Makefile index 89df468e..66df5892 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,9 @@ endif PYTHON := python PIP := pip +# Set PYTHONPATH for scripts +export PYTHONPATH := $(shell pwd) + # ============================================================================= # INSTALLATION & SETUP # ============================================================================= @@ -141,7 +144,19 @@ db-setup: migrate-up init-prod seed-demo @echo "✅ Database setup complete!" @echo "✨ Run 'make dev' to start development server" -db-reset: migrate-down migrate-up init-prod seed-demo-reset +db-reset: + @echo "⚠️ WARNING: This will DELETE ALL existing data!" +ifeq ($(DETECTED_OS),Windows) + @set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) -m alembic downgrade -1 + @set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) -m alembic upgrade head + @set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) scripts/init_production.py + @set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) scripts/seed_demo.py +else + $(PYTHON) -m alembic downgrade -1 + $(PYTHON) -m alembic upgrade head + $(PYTHON) scripts/init_production.py + SEED_MODE=reset FORCE_RESET=true $(PYTHON) scripts/seed_demo.py +endif @echo "" @echo "✅ Database completely reset!" diff --git a/scripts/seed_demo.py b/scripts/seed_demo.py index eea4c4a0..689821c8 100644 --- a/scripts/seed_demo.py +++ b/scripts/seed_demo.py @@ -21,10 +21,16 @@ Usage: make seed-demo # Normal demo seeding make seed-demo-minimal # Minimal seeding (1 vendor only) make seed-demo-reset # Delete all data and reseed (DANGEROUS!) + make db-reset # Full reset (migrate down/up + init + seed reset) + +Environment Variables: + SEED_MODE=normal|minimal|reset - Seeding mode (default: normal) + FORCE_RESET=true - Skip confirmation in reset mode (for non-interactive use) This script is idempotent when run normally. """ +import argparse import sys from datetime import UTC, datetime from decimal import Decimal @@ -51,6 +57,7 @@ from models.database.company import Company from models.database.customer import Customer, CustomerAddress from models.database.marketplace_import_job import MarketplaceImportJob from models.database.marketplace_product import MarketplaceProduct +from models.database.marketplace_product_translation import MarketplaceProductTranslation from models.database.order import Order, OrderItem from models.database.product import Product from models.database.user import User @@ -59,6 +66,7 @@ from models.database.vendor_domain import VendorDomain from models.database.vendor_theme import VendorTheme SEED_MODE = os.getenv("SEED_MODE", "normal") # normal, minimal, reset +FORCE_RESET = os.getenv("FORCE_RESET", "false").lower() in ("true", "1", "yes") # ============================================================================= # DEMO DATA CONFIGURATION @@ -243,11 +251,20 @@ def reset_all_data(db: Session): print(" This will delete all vendors, customers, orders, etc.") print(" Admin user will be preserved.") - # Get confirmation - response = input("\n Type 'DELETE ALL DATA' to confirm: ") - if response != "DELETE ALL DATA": - print(" Reset cancelled.") - sys.exit(0) + # Skip confirmation if FORCE_RESET is set (for non-interactive use) + if FORCE_RESET: + print_warning("FORCE_RESET enabled - skipping confirmation") + else: + # Get confirmation interactively + try: + response = input("\n Type 'DELETE ALL DATA' to confirm: ") + if response != "DELETE ALL DATA": + print(" Reset cancelled.") + sys.exit(0) + except EOFError: + print_error("No interactive terminal available.") + print(" Use FORCE_RESET=true to skip confirmation in non-interactive mode.") + sys.exit(1) # Delete in correct order (respecting foreign keys) tables_to_clear = [ @@ -514,10 +531,10 @@ def create_demo_products(db: Session, vendor: Vendor, count: int) -> list[Produc marketplace_product_id = f"{vendor.vendor_code}-MP-{i:04d}" product_id = f"{vendor.vendor_code}-PROD-{i:03d}" - # Check if this product already exists + # Check if this product already exists (by vendor_sku) existing_product = ( db.query(Product) - .filter(Product.vendor_id == vendor.id, Product.product_id == product_id) + .filter(Product.vendor_id == vendor.id, Product.vendor_sku == product_id) .first() ) @@ -538,9 +555,7 @@ def create_demo_products(db: Session, vendor: Vendor, count: int) -> list[Produc # Create the MarketplaceProduct (base product data) marketplace_product = MarketplaceProduct( marketplace_product_id=marketplace_product_id, - title=f"Sample Product {i} - {vendor.name}", - description=f"This is a demo product for testing purposes in {vendor.name}. High quality and affordable.", - link=f"https://{vendor.subdomain}.example.com/products/sample-{i}", + source_url=f"https://{vendor.subdomain}.example.com/products/sample-{i}", image_link=f"https://{vendor.subdomain}.example.com/images/product-{i}.jpg", price=str(Decimal(f"{(i * 10) % 500 + 9.99}")), # Store as string brand=vendor.name, @@ -557,19 +572,34 @@ def create_demo_products(db: Session, vendor: Vendor, count: int) -> list[Produc db.add(marketplace_product) db.flush() # Flush to get the marketplace_product.id + # Check if English translation already exists + existing_translation = ( + db.query(MarketplaceProductTranslation) + .filter( + MarketplaceProductTranslation.marketplace_product_id + == marketplace_product.id, + MarketplaceProductTranslation.language == "en", + ) + .first() + ) + + if not existing_translation: + # Create English translation + translation = MarketplaceProductTranslation( + marketplace_product_id=marketplace_product.id, + language="en", + title=f"Sample Product {i} - {vendor.name}", + description=f"This is a demo product for testing purposes in {vendor.name}. High quality and affordable.", + ) + db.add(translation) + # Create the Product (vendor-specific entry) product = Product( vendor_id=vendor.id, marketplace_product_id=marketplace_product.id, - product_id=product_id, + vendor_sku=product_id, # Use vendor_sku for vendor's internal product reference price=float(Decimal(f"{(i * 10) % 500 + 9.99}")), # Store as float - availability="in stock", - condition="new", - currency="EUR", is_active=True, - is_featured=(i % 5 == 0), # Every 5th product is featured - display_order=i, - min_quantity=1, created_at=datetime.now(UTC), updated_at=datetime.now(UTC), )