amazon-transcreation/seed/create_default_client.py
DJP 98fa16bfc3 feat: complete Phase 1-2 scaffold — backend, frontend, pipeline skeleton
Full-stack Amazon AI Transcreation Platform with:
- FastAPI backend (async, PostgreSQL, Redis, Celery) with 11 DB tables
- JWT auth (SSO-ready abstract provider pattern)
- 6-agent pipeline orchestrator with deterministic modules
- Next.js 14 frontend with Amazon branding (Ember fonts, orange/dark theme)
- Job wizard, monitoring HUD, output review, admin screens
- 154 TM/reference files imported, 12 locales configured
- Docker Compose for all services

Agents 2-5 (TM retrieval, ranker, transcreator, compliance) are stubs
pending Phase 3 LLM integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 12:31:43 -04:00

224 lines
6.9 KiB
Python

#!/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()