pounce/backend/app/seeds/yield_partners.py
yves.gugger 58228e3d33
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
feat: integrate Pounce self-promotion & viral growth system
Pounce Eigenwerbung (from pounce_endgame.md):
- Add 'pounce_promo' as fallback partner for generic/unclear intent domains
- Create dedicated Pounce promo landing page with CTA to register
- Update footer on all yield pages: 'Monetized by Pounce • Own a domain? Start yielding'

Tech/Investment Domain Detection:
- Add 'investment_domains' category (invest, crypto, trading, domain, startup)
- Add 'tech_dev' category (developer, web3, fintech, proptech)
- Both categories have 'pounce_affinity' flag for higher Pounce conversion

Referral Tracking for Domain Owners:
- Add user fields: referred_by_user_id, referred_by_domain, referral_code
- Parse yield referral codes (yield_{user_id}_{domain_id}) on registration
- Domain owners earn lifetime commission when visitors sign up via their domain

DB Migrations:
- Add referral tracking columns to users table
2025-12-12 15:27:53 +01:00

475 lines
15 KiB
Python

"""
Seed data for Yield affiliate partners.
Run via: python -m app.seeds.yield_partners
Or: from app.seeds.yield_partners import seed_partners; await seed_partners(db)
"""
import asyncio
import logging
from decimal import Decimal
from typing import Any
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import AsyncSessionLocal
from app.models.yield_domain import AffiliatePartner
logger = logging.getLogger(__name__)
# Partner configurations grouped by category
PARTNER_SEED_DATA: list[dict[str, Any]] = [
# =========================================================================
# MEDICAL / HEALTH
# =========================================================================
{
"name": "Comparis Dental",
"slug": "comparis_dental",
"network": "direct",
"intent_categories": "medical_dental",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("25.00"),
"payout_currency": "CHF",
"description": "Dental treatment comparison platform. High conversion for Swiss dental searches.",
"priority": 100,
},
{
"name": "Swisssmile",
"slug": "swisssmile",
"network": "awin",
"intent_categories": "medical_dental",
"geo_countries": "CH,DE,AT",
"payout_type": "cpl",
"payout_amount": Decimal("30.00"),
"payout_currency": "CHF",
"description": "Premium dental clinics network.",
"priority": 90,
},
{
"name": "Comparis Health",
"slug": "comparis_health",
"network": "direct",
"intent_categories": "medical_general",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("20.00"),
"payout_currency": "CHF",
"description": "Health insurance comparison.",
"priority": 100,
},
{
"name": "Sanitas",
"slug": "sanitas",
"network": "awin",
"intent_categories": "medical_general",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("35.00"),
"payout_currency": "CHF",
"description": "Swiss health insurance provider.",
"priority": 80,
},
{
"name": "Swiss Esthetic",
"slug": "swissesthetic",
"network": "direct",
"intent_categories": "medical_beauty",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("40.00"),
"payout_currency": "CHF",
"description": "Aesthetic treatments and beauty clinics.",
"priority": 90,
},
# =========================================================================
# FINANCE / INSURANCE
# =========================================================================
{
"name": "Comparis Insurance",
"slug": "comparis_insurance",
"network": "direct",
"intent_categories": "finance_insurance",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("30.00"),
"payout_currency": "CHF",
"description": "All-in-one insurance comparison.",
"priority": 100,
},
{
"name": "Bonus.ch",
"slug": "bonus_ch",
"network": "awin",
"intent_categories": "finance_insurance",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("25.00"),
"payout_currency": "CHF",
"description": "Swiss insurance comparison portal.",
"priority": 80,
},
{
"name": "Comparis Hypo",
"slug": "comparis_hypo",
"network": "direct",
"intent_categories": "finance_mortgage",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("100.00"),
"payout_currency": "CHF",
"description": "Mortgage comparison - high value leads.",
"priority": 100,
},
{
"name": "MoneyPark",
"slug": "moneypark",
"network": "awin",
"intent_categories": "finance_mortgage",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("120.00"),
"payout_currency": "CHF",
"description": "Independent mortgage broker.",
"priority": 90,
},
{
"name": "Neon Bank",
"slug": "neon_bank",
"network": "partnerstack",
"intent_categories": "finance_banking",
"geo_countries": "CH",
"payout_type": "cps",
"payout_amount": Decimal("50.00"),
"payout_currency": "CHF",
"description": "Swiss mobile banking app.",
"priority": 80,
},
# =========================================================================
# LEGAL
# =========================================================================
{
"name": "Legal CH",
"slug": "legal_ch",
"network": "direct",
"intent_categories": "legal_general",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("50.00"),
"payout_currency": "CHF",
"description": "Lawyer matching service.",
"priority": 100,
},
{
"name": "Anwalt24",
"slug": "anwalt24",
"network": "awin",
"intent_categories": "legal_general",
"geo_countries": "DE,AT",
"payout_type": "cpl",
"payout_amount": Decimal("35.00"),
"payout_currency": "EUR",
"description": "German lawyer directory.",
"priority": 80,
},
# =========================================================================
# REAL ESTATE
# =========================================================================
{
"name": "Homegate",
"slug": "homegate",
"network": "awin",
"intent_categories": "realestate_buy,realestate_rent",
"geo_countries": "CH",
"payout_type": "cpc",
"payout_amount": Decimal("0.50"),
"payout_currency": "CHF",
"description": "Switzerland's #1 real estate platform.",
"priority": 100,
},
{
"name": "ImmoScout24",
"slug": "immoscout",
"network": "awin",
"intent_categories": "realestate_buy,realestate_rent",
"geo_countries": "CH,DE",
"payout_type": "cpc",
"payout_amount": Decimal("0.40"),
"payout_currency": "CHF",
"description": "Real estate marketplace.",
"priority": 90,
},
{
"name": "Comparis Immo",
"slug": "comparis_immo",
"network": "direct",
"intent_categories": "realestate_buy,realestate_rent",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("15.00"),
"payout_currency": "CHF",
"description": "Property valuation and search.",
"priority": 85,
},
# =========================================================================
# TRAVEL
# =========================================================================
{
"name": "Skyscanner",
"slug": "skyscanner",
"network": "awin",
"intent_categories": "travel_flights",
"geo_countries": "CH,DE,AT",
"payout_type": "cpc",
"payout_amount": Decimal("0.30"),
"payout_currency": "CHF",
"description": "Flight comparison engine.",
"priority": 90,
},
{
"name": "Booking.com",
"slug": "booking_com",
"network": "awin",
"intent_categories": "travel_hotels",
"geo_countries": "CH,DE,AT",
"payout_type": "cps",
"payout_amount": Decimal("4.00"), # 4% commission
"payout_currency": "CHF",
"description": "World's leading accommodation site.",
"priority": 100,
},
# =========================================================================
# AUTOMOTIVE
# =========================================================================
{
"name": "AutoScout24",
"slug": "autoscout",
"network": "awin",
"intent_categories": "auto_buy",
"geo_countries": "CH,DE",
"payout_type": "cpc",
"payout_amount": Decimal("0.60"),
"payout_currency": "CHF",
"description": "Auto marketplace.",
"priority": 100,
},
{
"name": "Comparis Auto",
"slug": "comparis_auto",
"network": "direct",
"intent_categories": "auto_buy,auto_service",
"geo_countries": "CH",
"payout_type": "cpl",
"payout_amount": Decimal("25.00"),
"payout_currency": "CHF",
"description": "Car insurance & leasing comparison.",
"priority": 90,
},
# =========================================================================
# JOBS
# =========================================================================
{
"name": "Jobs.ch",
"slug": "jobs_ch",
"network": "awin",
"intent_categories": "jobs",
"geo_countries": "CH",
"payout_type": "cpc",
"payout_amount": Decimal("0.40"),
"payout_currency": "CHF",
"description": "Swiss job board.",
"priority": 100,
},
{
"name": "Indeed",
"slug": "indeed",
"network": "awin",
"intent_categories": "jobs",
"geo_countries": "CH,DE,AT",
"payout_type": "cpc",
"payout_amount": Decimal("0.25"),
"payout_currency": "CHF",
"description": "Global job search engine.",
"priority": 80,
},
# =========================================================================
# EDUCATION
# =========================================================================
{
"name": "Udemy",
"slug": "udemy",
"network": "awin",
"intent_categories": "education",
"geo_countries": "CH,DE,AT",
"payout_type": "cps",
"payout_amount": Decimal("10.00"), # Per sale
"payout_currency": "USD",
"description": "Online courses platform.",
"priority": 80,
},
# =========================================================================
# TECHNOLOGY / HOSTING
# =========================================================================
{
"name": "Hostpoint",
"slug": "hostpoint",
"network": "partnerstack",
"intent_categories": "tech_hosting",
"geo_countries": "CH",
"payout_type": "cps",
"payout_amount": Decimal("30.00"),
"payout_currency": "CHF",
"description": "Swiss web hosting leader.",
"priority": 100,
},
{
"name": "Infomaniak",
"slug": "infomaniak",
"network": "direct",
"intent_categories": "tech_hosting",
"geo_countries": "CH",
"payout_type": "cps",
"payout_amount": Decimal("25.00"),
"payout_currency": "CHF",
"description": "Eco-friendly Swiss hosting.",
"priority": 90,
},
# =========================================================================
# SHOPPING
# =========================================================================
{
"name": "Galaxus",
"slug": "galaxus",
"network": "awin",
"intent_categories": "shopping_general",
"geo_countries": "CH",
"payout_type": "cps",
"payout_amount": Decimal("2.00"), # 2% commission
"payout_currency": "CHF",
"description": "Switzerland's largest online shop.",
"priority": 100,
},
{
"name": "Zalando",
"slug": "zalando",
"network": "awin",
"intent_categories": "shopping_fashion",
"geo_countries": "CH,DE,AT",
"payout_type": "cps",
"payout_amount": Decimal("8.00"), # 8% commission
"payout_currency": "CHF",
"description": "Fashion & lifestyle.",
"priority": 100,
},
# =========================================================================
# FOOD / DELIVERY
# =========================================================================
{
"name": "Uber Eats",
"slug": "uber_eats",
"network": "awin",
"intent_categories": "food_restaurant,food_delivery",
"geo_countries": "CH,DE",
"payout_type": "cps",
"payout_amount": Decimal("5.00"),
"payout_currency": "CHF",
"description": "Food delivery service.",
"priority": 90,
},
# =========================================================================
# POUNCE SELF-PROMOTION (Viral Growth)
# =========================================================================
{
"name": "Pounce Promo",
"slug": "pounce_promo",
"network": "internal",
"intent_categories": "investment_domains,tech_dev,generic",
"geo_countries": "CH,DE,AT",
"payout_type": "cps",
"payout_amount": Decimal("0"), # 30% lifetime commission handled separately
"payout_currency": "CHF",
"description": "Pounce self-promotion. Domain owners earn 30% lifetime commission on referrals.",
"priority": 50, # Higher than generic but lower than high-value partners
},
# =========================================================================
# GENERIC FALLBACK
# =========================================================================
{
"name": "Generic Affiliate",
"slug": "generic_affiliate",
"network": "internal",
"intent_categories": "generic",
"geo_countries": "CH,DE,AT",
"payout_type": "cpc",
"payout_amount": Decimal("0.10"),
"payout_currency": "CHF",
"description": "Fallback for unclassified domains - shows Pounce marketplace.",
"priority": 1,
},
]
async def seed_partners(db: AsyncSession) -> int:
"""
Seed affiliate partners into database.
Idempotent: updates existing partners by slug, creates new ones.
Returns:
Number of partners created/updated.
"""
count = 0
for data in PARTNER_SEED_DATA:
slug = data["slug"]
# Check if partner exists
result = await db.execute(
select(AffiliatePartner).where(AffiliatePartner.slug == slug)
)
existing = result.scalar_one_or_none()
if existing:
# Update existing partner
for key, value in data.items():
setattr(existing, key, value)
logger.info(f"Updated partner: {slug}")
else:
# Create new partner
partner = AffiliatePartner(**data)
db.add(partner)
logger.info(f"Created partner: {slug}")
count += 1
await db.commit()
logger.info(f"Seeded {count} affiliate partners")
return count
async def main():
"""Run seed script standalone."""
logging.basicConfig(level=logging.INFO)
async with AsyncSessionLocal() as db:
count = await seed_partners(db)
print(f"✅ Seeded {count} affiliate partners")
if __name__ == "__main__":
asyncio.run(main())