#!/usr/bin/env python3 """ Create Default Client ===================== Seeds the database with the Amazon client record, voice profiles, channel registry, and test user accounts. Designed to run inside the Docker container: docker compose exec backend python -m seed.create_default_client Or via the Makefile: make seed """ import asyncio import sys from pathlib import Path # Ensure the backend package is importable when run as a module # Inside Docker: /app/seed/ -> parent is /app (which has the app package) # Outside Docker: seed/ -> parent.parent/backend has the app package _seed_dir = Path(__file__).resolve().parent _backend_dir = _seed_dir.parent / "backend" if _backend_dir.is_dir(): sys.path.insert(0, str(_backend_dir)) else: sys.path.insert(0, str(_seed_dir.parent)) from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from app.config import settings from app.models.client import Client # --------------------------------------------------------------------------- # Amazon client configuration # --------------------------------------------------------------------------- AMAZON_CLIENT_NAME = "Amazon" AMAZON_SETTINGS = { "supported_locales": { "main": [ "de-DE", "fr-FR", "it-IT", "es-ES", "nl-NL", "sv-SE", "pl-PL", "pt-PT", ], "derived": ["de-AT", "fr-BE", "nl-BE", "ca-ES"], }, "voice_profiles": { "Retail": { "attributes": ["Real", "Clear", "Playful", "Witty"], "core_job": ( "Communicate value, convenience, and selection concisely." ), "register": "Helpful, snappy, joyful. Specific to category.", "preferred_structure": ( "Be specific. Every word must earn its place." ), }, "Prime": { "attributes": [ "Optimistic", "Honest", "Self-aware", "Witty", "Relatable", ], "core_job": ( "Transform customer expectations; make every day better." ), "register": ( "Fearlessly honest, unapologetically cheeky, " "self-deprecating with a wink." ), "preferred_structure": ( "Lead with the benefit; end with the wink." ), }, "Brand": { "attributes": [ "Authentic", "Customer-obsessed", "Intelligent", "Warm", "Understated", ], "core_job": ( "Make customers feel seen and understood. " "Let the brand recede so the customer comes forward." ), "register": ( "Restrained, emotionally intelligent, " "never grand or self-congratulatory." ), "preferred_structure": ( "Say one thing. Make every word earn its place. Leave space." ), }, }, "channel_registry": { "Mass": { "type": "standing_channel", "tm_pattern": "flat_MASS_{lc}.json", }, "Value": { "type": "standing_channel", "tm_pattern": "flat_value_{lc}.json", }, "Onsite": { "type": "standing_channel", "tm_pattern": "flat_Onsite_{lc}.json", }, "Outbound": { "type": "standing_channel", "tm_pattern": "flat_Outbound_{lc}.json", }, "The Kiss": { "type": "project", "brand_program": "Retail", "tm_pattern": "flat_TheKiss_{lc}.json", }, "Prime Mid-funnel": { "type": "project", "brand_program": "Prime", "tm_pattern": "flat_PrimeMidfunnel_{lc}.json", }, "Prime Gourmet Guard": { "type": "project", "brand_program": "Prime", "tm_pattern": "flat_PrimeGourmetGuard_{lc}.json", }, "Prime Dual Benefit": { "type": "project", "brand_program": "Prime", "tm_pattern": "flat_PrimeDualBenefit_{lc}.json", }, "Prime Brand (Speed)": { "type": "project", "brand_program": "Prime", "tm_pattern": "flat_PrimeSpeed_{lc}.json", }, "EU Selection": { "type": "project", "brand_program": "Retail", "tm_pattern": "flat_EUSelection_{lc}.json", }, "BDA": { "type": "project", "brand_program": "Brand", "tm_pattern": "flat_BDA_{lc}.json", }, "UEFA": { "type": "project", "brand_program": "Brand", "tm_pattern": "flat_UEFA_{lc}.json", }, }, } async def create_amazon_client(session: AsyncSession) -> Client: """Create or update the Amazon client record.""" # Check if client already exists result = await session.execute( select(Client).where(Client.name == AMAZON_CLIENT_NAME) ) existing = result.scalar_one_or_none() if existing: print(f" Client '{AMAZON_CLIENT_NAME}' already exists (id={existing.id})") print(" Updating settings...") existing.settings = AMAZON_SETTINGS await session.flush() return existing client = Client(name=AMAZON_CLIENT_NAME, settings=AMAZON_SETTINGS) session.add(client) await session.flush() print(f" Created client '{AMAZON_CLIENT_NAME}' (id={client.id})") return client async def seed() -> None: """Run the full seed process.""" engine = create_async_engine( settings.DATABASE_URL, echo=False, ) async_session = async_sessionmaker( bind=engine, class_=AsyncSession, expire_on_commit=False, ) print("=" * 60) print("Seeding: Create Default Client") print("=" * 60) async with async_session() as session: async with session.begin(): client = await create_amazon_client(session) # Print summary locales = AMAZON_SETTINGS["supported_locales"] channels = AMAZON_SETTINGS["channel_registry"] profiles = AMAZON_SETTINGS["voice_profiles"] print() print(" Configuration summary:") print(f" Main locales: {', '.join(locales['main'])}") print(f" Derived locales: {', '.join(locales['derived'])}") print(f" Voice profiles: {', '.join(profiles.keys())}") print(f" Channels: {len(channels)}") for name, config in channels.items(): ch_type = config["type"] brand = config.get("brand_program", "-") print(f" - {name:25s} type={ch_type:20s} brand={brand}") await engine.dispose() print() print(" Seed complete.") print("=" * 60) def main() -> None: asyncio.run(seed()) if __name__ == "__main__": main()