feat(affiliates): register Kiwi grocery retailer programs at startup
refs kiwi#74
This commit is contained in:
parent
25027762cf
commit
b9dd1427de
2 changed files with 111 additions and 0 deletions
|
|
@ -9,6 +9,9 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||
|
||||
from app.api.routes import api_router
|
||||
from app.core.config import settings
|
||||
from app.services.meal_plan.affiliates import register_kiwi_programs
|
||||
|
||||
register_kiwi_programs()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
108
app/services/meal_plan/affiliates.py
Normal file
108
app/services/meal_plan/affiliates.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# app/services/meal_plan/affiliates.py
|
||||
"""Register Kiwi-specific affiliate programs and provide search URL builders.
|
||||
|
||||
Called once at API startup. Programs not yet in core.affiliates are registered
|
||||
here. The actual affiliate IDs are read from environment variables at call
|
||||
time, so the process can start before accounts are approved (plain URLs
|
||||
returned when env vars are absent).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from circuitforge_core.affiliates import AffiliateProgram, register_program, wrap_url
|
||||
|
||||
|
||||
# ── URL builders ──────────────────────────────────────────────────────────────
|
||||
|
||||
def _walmart_search(url: str, affiliate_id: str) -> str:
|
||||
sep = "&" if "?" in url else "?"
|
||||
return f"{url}{sep}affil=apa&affiliateId={affiliate_id}"
|
||||
|
||||
|
||||
def _target_search(url: str, affiliate_id: str) -> str:
|
||||
sep = "&" if "?" in url else "?"
|
||||
return f"{url}{sep}afid={affiliate_id}"
|
||||
|
||||
|
||||
def _thrive_search(url: str, affiliate_id: str) -> str:
|
||||
sep = "&" if "?" in url else "?"
|
||||
return f"{url}{sep}raf={affiliate_id}"
|
||||
|
||||
|
||||
def _misfits_search(url: str, affiliate_id: str) -> str:
|
||||
sep = "&" if "?" in url else "?"
|
||||
return f"{url}{sep}ref={affiliate_id}"
|
||||
|
||||
|
||||
# ── Registration ──────────────────────────────────────────────────────────────
|
||||
|
||||
def register_kiwi_programs() -> None:
|
||||
"""Register Kiwi retailer programs. Safe to call multiple times (idempotent)."""
|
||||
register_program(AffiliateProgram(
|
||||
name="Walmart",
|
||||
retailer_key="walmart",
|
||||
env_var="WALMART_AFFILIATE_ID",
|
||||
build_url=_walmart_search,
|
||||
))
|
||||
register_program(AffiliateProgram(
|
||||
name="Target",
|
||||
retailer_key="target",
|
||||
env_var="TARGET_AFFILIATE_ID",
|
||||
build_url=_target_search,
|
||||
))
|
||||
register_program(AffiliateProgram(
|
||||
name="Thrive Market",
|
||||
retailer_key="thrive",
|
||||
env_var="THRIVE_AFFILIATE_ID",
|
||||
build_url=_thrive_search,
|
||||
))
|
||||
register_program(AffiliateProgram(
|
||||
name="Misfits Market",
|
||||
retailer_key="misfits",
|
||||
env_var="MISFITS_AFFILIATE_ID",
|
||||
build_url=_misfits_search,
|
||||
))
|
||||
|
||||
|
||||
# ── Search URL helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
_SEARCH_TEMPLATES: dict[str, str] = {
|
||||
"amazon": "https://www.amazon.com/s?k={q}",
|
||||
"instacart": "https://www.instacart.com/store/search_v3/term?term={q}",
|
||||
"walmart": "https://www.walmart.com/search?q={q}",
|
||||
"target": "https://www.target.com/s?searchTerm={q}",
|
||||
"thrive": "https://thrivemarket.com/search?q={q}",
|
||||
"misfits": "https://www.misfitsmarket.com/shop?search={q}",
|
||||
}
|
||||
|
||||
KIWI_RETAILERS = list(_SEARCH_TEMPLATES.keys())
|
||||
|
||||
|
||||
def get_retailer_links(ingredient_name: str) -> list[dict]:
|
||||
"""Return affiliate-wrapped search links for *ingredient_name*.
|
||||
|
||||
Returns a list of dicts: {"retailer": str, "label": str, "url": str}.
|
||||
Falls back to plain search URL when no affiliate ID is configured.
|
||||
"""
|
||||
q = quote_plus(ingredient_name)
|
||||
links = []
|
||||
for key, template in _SEARCH_TEMPLATES.items():
|
||||
plain_url = template.format(q=q)
|
||||
try:
|
||||
affiliate_url = wrap_url(plain_url, retailer=key)
|
||||
except Exception:
|
||||
affiliate_url = plain_url
|
||||
links.append({"retailer": key, "label": _label(key), "url": affiliate_url})
|
||||
return links
|
||||
|
||||
|
||||
def _label(key: str) -> str:
|
||||
return {
|
||||
"amazon": "Amazon",
|
||||
"instacart": "Instacart",
|
||||
"walmart": "Walmart",
|
||||
"target": "Target",
|
||||
"thrive": "Thrive Market",
|
||||
"misfits": "Misfits Market",
|
||||
}.get(key, key.title())
|
||||
Loading…
Reference in a new issue