Files
bakery-ia/services/tenant/app/jobs/startup_seeder.py

128 lines
3.8 KiB
Python
Raw Permalink Normal View History

2026-01-12 14:24:14 +01:00
"""
Startup seeder for tenant service.
Seeds initial data (like pilot coupons) on service startup.
All operations are idempotent - safe to run multiple times.
"""
import os
import uuid
from datetime import datetime, timedelta, timezone
from typing import Optional
import structlog
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.coupon import CouponModel
logger = structlog.get_logger()
async def ensure_pilot_coupon(session: AsyncSession) -> Optional[CouponModel]:
"""
Ensure the PILOT2025 coupon exists in the database.
This coupon provides 3 months (90 days) free trial extension
for the first 20 pilot customers.
This function is idempotent - it will not create duplicates.
Args:
session: Database session
Returns:
The coupon model (existing or newly created), or None if disabled
"""
# Check if pilot mode is enabled via environment variable
pilot_mode_enabled = os.getenv("VITE_PILOT_MODE_ENABLED", "true").lower() == "true"
if not pilot_mode_enabled:
logger.info("Pilot mode is disabled, skipping coupon seeding")
return None
coupon_code = os.getenv("VITE_PILOT_COUPON_CODE", "PILOT2025")
trial_months = int(os.getenv("VITE_PILOT_TRIAL_MONTHS", "3"))
max_redemptions = int(os.getenv("PILOT_MAX_REDEMPTIONS", "20"))
# Check if coupon already exists
result = await session.execute(
select(CouponModel).where(CouponModel.code == coupon_code)
)
existing_coupon = result.scalars().first()
if existing_coupon:
logger.info(
"Pilot coupon already exists",
code=coupon_code,
current_redemptions=existing_coupon.current_redemptions,
max_redemptions=existing_coupon.max_redemptions,
active=existing_coupon.active
)
return existing_coupon
# Create new coupon
now = datetime.now(timezone.utc)
valid_until = now + timedelta(days=180) # Valid for 6 months
trial_days = trial_months * 30 # Approximate days
coupon = CouponModel(
id=uuid.uuid4(),
code=coupon_code,
discount_type="trial_extension",
discount_value=trial_days,
max_redemptions=max_redemptions,
current_redemptions=0,
valid_from=now,
valid_until=valid_until,
active=True,
created_at=now,
extra_data={
"program": "pilot_launch_2025",
"description": f"Programa piloto - {trial_months} meses gratis para los primeros {max_redemptions} clientes",
"terms": "Válido para nuevos registros únicamente. Un cupón por cliente."
}
)
session.add(coupon)
await session.commit()
await session.refresh(coupon)
logger.info(
"Pilot coupon created successfully",
code=coupon_code,
type="Trial Extension",
value=f"{trial_days} days ({trial_months} months)",
max_redemptions=max_redemptions,
valid_until=valid_until.isoformat(),
id=str(coupon.id)
)
return coupon
async def run_startup_seeders(database_manager) -> None:
"""
Run all startup seeders.
This function is called during service startup to ensure
required seed data exists in the database.
Args:
database_manager: The database manager instance
"""
logger.info("Running startup seeders...")
try:
async with database_manager.get_session() as session:
# Seed pilot coupon
await ensure_pilot_coupon(session)
logger.info("Startup seeders completed successfully")
except Exception as e:
# Log but don't fail startup - seed data is not critical
logger.warning(
"Startup seeder encountered an error (non-fatal)",
error=str(e)
)