#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Demo Subscription Seeding Script for Tenant Service Creates subscriptions for demo template tenants This script creates subscription records for the demo template tenants so they have proper subscription limits and features. Usage: python /app/scripts/demo/seed_demo_subscriptions.py Environment Variables Required: TENANT_DATABASE_URL - PostgreSQL connection string for tenant database LOG_LEVEL - Logging level (default: INFO) """ import asyncio import uuid import sys import os from datetime import datetime, timezone, timedelta from pathlib import Path # Add app to path sys.path.insert(0, str(Path(__file__).parent.parent.parent)) from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker from sqlalchemy import select import structlog from app.models.tenants import Subscription # Configure logging structlog.configure( processors=[ structlog.stdlib.add_log_level, structlog.processors.TimeStamper(fmt="iso"), structlog.dev.ConsoleRenderer() ] ) logger = structlog.get_logger() # Fixed Demo Tenant IDs (must match tenant service) DEMO_TENANT_SAN_PABLO = uuid.UUID("a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6") DEMO_TENANT_LA_ESPIGA = uuid.UUID("b2c3d4e5-f6a7-48b9-c0d1-e2f3a4b5c6d7") SUBSCRIPTIONS_DATA = [ { "tenant_id": DEMO_TENANT_SAN_PABLO, "plan": "enterprise", "status": "active", "monthly_price": 0.0, # Free for demo "max_users": -1, # Unlimited users "max_locations": -1, # Unlimited locations "max_products": -1, # Unlimited products "features": { "inventory_management": "advanced", "demand_prediction": "advanced", "production_reports": "advanced", "analytics": "predictive", "support": "priority", "ai_model_configuration": "advanced", "multi_location": True, "custom_integrations": True, "api_access": True, "dedicated_support": True }, "trial_ends_at": None, "next_billing_date": datetime.now(timezone.utc) + timedelta(days=90), # 90 days for demo }, { "tenant_id": DEMO_TENANT_LA_ESPIGA, "plan": "enterprise", "status": "active", "monthly_price": 0.0, # Free for demo "max_users": -1, # Unlimited users "max_locations": -1, # Unlimited locations "max_products": -1, # Unlimited products "features": { "inventory_management": "advanced", "demand_prediction": "advanced", "production_reports": "advanced", "analytics": "predictive", "support": "priority", "ai_model_configuration": "advanced", "multi_location": True, "custom_integrations": True, "api_access": True, "dedicated_support": True }, "trial_ends_at": None, "next_billing_date": datetime.now(timezone.utc) + timedelta(days=90), } ] async def seed_subscriptions(db: AsyncSession) -> dict: """ Seed subscriptions for demo template tenants Returns: Dict with seeding statistics """ logger.info("=" * 80) logger.info("💳 Starting Demo Subscription Seeding") logger.info("=" * 80) created_count = 0 updated_count = 0 for subscription_data in SUBSCRIPTIONS_DATA: tenant_id = subscription_data["tenant_id"] # Check if subscription already exists for this tenant result = await db.execute( select(Subscription).where( Subscription.tenant_id == tenant_id, Subscription.status == "active" ) ) existing_subscription = result.scalars().first() if existing_subscription: logger.info( "Subscription already exists - updating", tenant_id=str(tenant_id), subscription_id=str(existing_subscription.id) ) # Update existing subscription for key, value in subscription_data.items(): if key != "tenant_id": # Don't update the tenant_id setattr(existing_subscription, key, value) existing_subscription.updated_at = datetime.now(timezone.utc) updated_count += 1 else: logger.info( "Creating new subscription", tenant_id=str(tenant_id), plan=subscription_data["plan"] ) # Create new subscription subscription = Subscription(**subscription_data) db.add(subscription) created_count += 1 # Commit all changes await db.commit() logger.info("=" * 80) logger.info( "✅ Demo Subscription Seeding Completed", created=created_count, updated=updated_count, total=len(SUBSCRIPTIONS_DATA) ) logger.info("=" * 80) return { "service": "subscriptions", "created": created_count, "updated": updated_count, "total": len(SUBSCRIPTIONS_DATA) } async def main(): """Main execution function""" logger.info("Demo Subscription Seeding Script Starting") logger.info("Log Level: %s", os.getenv("LOG_LEVEL", "INFO")) # Get database URL from environment database_url = os.getenv("TENANT_DATABASE_URL") or os.getenv("DATABASE_URL") if not database_url: logger.error("❌ TENANT_DATABASE_URL or DATABASE_URL environment variable must be set") return 1 # Convert to async URL if needed if database_url.startswith("postgresql://"): database_url = database_url.replace("postgresql://", "postgresql+asyncpg://", 1) logger.info("Connecting to tenant database") # Create engine and session engine = create_async_engine( database_url, echo=False, pool_pre_ping=True, pool_size=5, max_overflow=10 ) async_session = sessionmaker( engine, class_=AsyncSession, expire_on_commit=False ) try: async with async_session() as session: result = await seed_subscriptions(session) logger.info("") logger.info("📊 Seeding Summary:") logger.info(f" ✅ Created: {result['created']}") logger.info(f" 🔄 Updated: {result['updated']}") logger.info(f" 📦 Total: {result['total']}") logger.info("") logger.info("🎉 Success! Demo subscriptions are ready.") logger.info("") return 0 except Exception as e: logger.error("=" * 80) logger.error("❌ Demo Subscription Seeding Failed") logger.error("=" * 80) logger.error("Error: %s", str(e)) logger.error("", exc_info=True) return 1 finally: await engine.dispose() if __name__ == "__main__": exit_code = asyncio.run(main()) sys.exit(exit_code)