rankora-api / app /services /analytics /advanced_services.py
Shoaib898's picture
Deploy Rankora API: buy box competitors, offers persistence, scraper fixes
5aa2bd9 verified
Raw
History Blame Contribute Delete
15.5 kB
import re
import math
from typing import Optional
# ══════════════════════════════════════════════════════════════════════════════
# 1. SALES ESTIMATOR v2 β€” category-specific BSR β†’ monthly sales
# ══════════════════════════════════════════════════════════════════════════════
CATEGORY_MULTIPLIERS = {
"kitchen": {"base": 8000, "decay": 0.55},
"home": {"base": 7000, "decay": 0.55},
"sports": {"base": 6000, "decay": 0.52},
"toys": {"base": 9000, "decay": 0.58},
"beauty": {"base": 7500, "decay": 0.54},
"health": {"base": 6500, "decay": 0.53},
"tools": {"base": 5000, "decay": 0.50},
"office": {"base": 5500, "decay": 0.51},
"pet": {"base": 6000, "decay": 0.52},
"garden": {"base": 4500, "decay": 0.50},
"default": {"base": 6000, "decay": 0.52},
}
def estimate_sales_v2(bsr: int, category: str = "default", price: float = 25.0) -> dict:
cat = CATEGORY_MULTIPLIERS.get(category.lower(), CATEGORY_MULTIPLIERS["default"])
if bsr <= 0:
bsr = 50000
monthly_sales = int(cat["base"] * math.pow(bsr, -cat["decay"]) * 1000)
monthly_sales = max(1, min(monthly_sales, 50000))
monthly_revenue = round(monthly_sales * price, 2)
daily_sales = round(monthly_sales / 30, 1)
# Confidence based on BSR range
if bsr < 5000:
confidence = "High"
elif bsr < 50000:
confidence = "Medium"
else:
confidence = "Low"
return {
"monthly_sales": monthly_sales,
"daily_sales": daily_sales,
"monthly_revenue": monthly_revenue,
"confidence": confidence,
"category": category,
"bsr": bsr,
"price": price,
"annual_revenue": round(monthly_revenue * 12, 2),
}
# ══════════════════════════════════════════════════════════════════════════════
# 2. REVIEW ANALYZER β€” sentiment + patterns
# ══════════════════════════════════════════════════════════════════════════════
POSITIVE_WORDS = ["great","excellent","perfect","love","amazing","best","good","quality","easy","fast","recommend","happy","pleased","works","nice","fantastic","awesome","solid","durable","comfortable"]
NEGATIVE_WORDS = ["poor","bad","terrible","worst","broken","useless","cheap","waste","disappointed","defective","fragile","flimsy","slow","wrong","missing","return","refund","fake","stopped","horrible"]
QUALITY_WORDS = ["quality","durable","sturdy","solid","premium","cheap","flimsy","fragile","well-made","poorly made"]
DELIVERY_WORDS = ["fast","quick","slow","delivery","shipping","arrived","late","early","package","damaged"]
VALUE_WORDS = ["worth","value","price","expensive","cheap","affordable","overpriced","deal","bargain"]
def analyze_reviews(rating: float, review_count: int, title: str = "") -> dict:
"""Analyze product based on available data β€” real NLP would need actual review text."""
# Star distribution estimate
if rating >= 4.5:
stars = {"5": 70, "4": 20, "3": 5, "2": 3, "1": 2}
elif rating >= 4.0:
stars = {"5": 55, "4": 25, "3": 10, "2": 5, "1": 5}
elif rating >= 3.5:
stars = {"5": 40, "4": 25, "3": 15, "2": 10, "1": 10}
elif rating >= 3.0:
stars = {"5": 30, "4": 20, "3": 20, "2": 15, "1": 15}
else:
stars = {"5": 20, "4": 15, "3": 15, "2": 20, "1": 30}
positive_pct = stars["5"] + stars["4"]
negative_pct = stars["1"] + stars["2"]
# Sentiment
if rating >= 4.3:
sentiment = "Very Positive"
sentiment_color = "#34d399"
elif rating >= 3.8:
sentiment = "Positive"
sentiment_color = "#86efac"
elif rating >= 3.3:
sentiment = "Mixed"
sentiment_color = "#fbbf24"
elif rating >= 2.8:
sentiment = "Negative"
sentiment_color = "#fb923c"
else:
sentiment = "Very Negative"
sentiment_color = "#f87171"
# Review velocity
if review_count > 10000:
velocity = "Saturated"
elif review_count > 1000:
velocity = "High"
elif review_count > 200:
velocity = "Medium"
elif review_count > 50:
velocity = "Growing"
else:
velocity = "Low"
# Market opportunity from reviews
if review_count < 100 and rating >= 4.0:
opportunity = "High β€” low reviews, good rating. Easy entry."
elif review_count < 500 and rating >= 3.8:
opportunity = "Medium β€” manageable competition."
elif review_count > 1000 and rating < 3.8:
opportunity = "Medium β€” saturated but poor quality. Can disrupt."
elif review_count > 1000 and rating >= 4.2:
opportunity = "Low β€” strong competition, hard to enter."
else:
opportunity = "Medium β€” assess further."
return {
"rating": rating,
"review_count": review_count,
"sentiment": sentiment,
"sentiment_color": sentiment_color,
"positive_pct": positive_pct,
"negative_pct": negative_pct,
"star_distribution": stars,
"review_velocity": velocity,
"market_opportunity": opportunity,
"common_positives": ["Good quality", "Fast delivery", "Value for money", "Easy to use", "As described"][:3],
"common_negatives": ["Quality issues", "Poor packaging", "Not as described"][:2] if negative_pct > 15 else [],
"entry_barrier": "Low" if review_count < 100 else "Medium" if review_count < 500 else "High",
"entry_barrier_color": "#34d399" if review_count < 100 else "#fbbf24" if review_count < 500 else "#f87171",
}
# ══════════════════════════════════════════════════════════════════════════════
# 3. BUY BOX ANALYZER
# ══════════════════════════════════════════════════════════════════════════════
def analyze_buy_box(price: float, rating: float, review_count: int, is_fba: bool = True, is_prime: bool = True) -> dict:
score = 0
factors = []
# Price competitiveness
if price < 20:
score += 25
factors.append({"factor": "Price", "status": "Competitive", "color": "#34d399", "detail": f"${price:.2f} β€” low price helps win buy box"})
elif price < 50:
score += 20
factors.append({"factor": "Price", "status": "Moderate", "color": "#fbbf24", "detail": f"${price:.2f} β€” average price range"})
else:
score += 10
factors.append({"factor": "Price", "status": "High", "color": "#f87171", "detail": f"${price:.2f} β€” high price hurts buy box chances"})
# FBA
if is_fba:
score += 30
factors.append({"factor": "Fulfillment", "status": "FBA βœ“", "color": "#34d399", "detail": "FBA strongly favored for buy box"})
else:
score += 5
factors.append({"factor": "Fulfillment", "status": "FBM", "color": "#f87171", "detail": "FBM sellers rarely win buy box"})
# Prime
if is_prime:
score += 20
factors.append({"factor": "Prime", "status": "Eligible βœ“", "color": "#34d399", "detail": "Prime eligibility required for buy box"})
else:
score += 0
factors.append({"factor": "Prime", "status": "Not Eligible", "color": "#f87171", "detail": "No Prime = very low buy box chance"})
# Seller rating
if rating >= 4.5:
score += 15
factors.append({"factor": "Seller Rating", "status": "Excellent", "color": "#34d399", "detail": f"{rating}β˜… β€” Amazon favors high-rated sellers"})
elif rating >= 4.0:
score += 10
factors.append({"factor": "Seller Rating", "status": "Good", "color": "#fbbf24", "detail": f"{rating}β˜… β€” acceptable for buy box"})
else:
score += 3
factors.append({"factor": "Seller Rating", "status": "Needs Work", "color": "#f87171", "detail": f"{rating}β˜… β€” low rating hurts buy box"})
# Review count
if review_count > 100:
score += 10
factors.append({"factor": "Reviews", "status": "Established", "color": "#34d399", "detail": f"{review_count:,} reviews β€” trusted seller signal"})
else:
score += 5
factors.append({"factor": "Reviews", "status": "New Seller", "color": "#fbbf24", "detail": f"{review_count} reviews β€” building trust"})
score = min(score, 100)
if score >= 75:
verdict = "Strong Buy Box Candidate"
verdict_color = "#34d399"
elif score >= 50:
verdict = "Moderate Chance"
verdict_color = "#fbbf24"
elif score >= 25:
verdict = "Low Chance"
verdict_color = "#fb923c"
else:
verdict = "Unlikely to Win"
verdict_color = "#f87171"
return {
"score": score,
"verdict": verdict,
"verdict_color": verdict_color,
"factors": factors,
"tips": [
"Use FBA for best buy box eligibility",
"Price within 2% of lowest FBA offer",
"Maintain 95%+ seller feedback rating",
"Keep order defect rate below 1%",
"Ship on time β€” late shipments hurt score",
][:3]
}
# ══════════════════════════════════════════════════════════════════════════════
# 4. NICHE FINDER
# ══════════════════════════════════════════════════════════════════════════════
def analyze_niche(keyword: str, avg_bsr: float, avg_reviews: float, avg_price: float, product_count: int = 100) -> dict:
# Demand score (lower BSR = higher demand)
if avg_bsr < 5000:
demand = 95
elif avg_bsr < 20000:
demand = 80
elif avg_bsr < 50000:
demand = 60
elif avg_bsr < 100000:
demand = 40
else:
demand = 20
# Competition score (lower reviews = less competition)
if avg_reviews < 50:
competition = 10 # low competition
elif avg_reviews < 200:
competition = 30
elif avg_reviews < 500:
competition = 55
elif avg_reviews < 1000:
competition = 75
else:
competition = 90
# Price attractiveness
if 20 <= avg_price <= 60:
price_score = 90
price_note = "Sweet spot for FBA ($20-$60)"
elif 10 <= avg_price < 20:
price_score = 60
price_note = "Low margin risk under $20"
elif 60 < avg_price <= 100:
price_score = 75
price_note = "Good margin, lower volume"
else:
price_score = 40
price_note = "High price = niche market"
# Opportunity score
opportunity = round((demand * 0.4) + ((100 - competition) * 0.4) + (price_score * 0.2))
if opportunity >= 70:
verdict = "🟒 Great Niche"
verdict_color = "#34d399"
elif opportunity >= 50:
verdict = "🟑 Decent Niche"
verdict_color = "#fbbf24"
elif opportunity >= 35:
verdict = "🟠 Challenging"
verdict_color = "#fb923c"
else:
verdict = "πŸ”΄ Avoid"
verdict_color = "#f87171"
est_monthly_sales = estimate_sales_v2(int(avg_bsr), price=avg_price)["monthly_sales"]
market_size = round(est_monthly_sales * avg_price * product_count / 1000, 0)
return {
"keyword": keyword,
"opportunity_score": opportunity,
"verdict": verdict,
"verdict_color": verdict_color,
"demand_score": demand,
"competition_score": competition,
"price_score": price_score,
"price_note": price_note,
"avg_bsr": int(avg_bsr),
"avg_reviews": int(avg_reviews),
"avg_price": avg_price,
"est_monthly_sales": est_monthly_sales,
"market_size_k": market_size,
"recommendation": (
"Low competition β€” great entry opportunity!" if competition < 30
else "Medium competition β€” differentiate product." if competition < 60
else "High competition β€” need strong differentiation."
)
}
# ══════════════════════════════════════════════════════════════════════════════
# 5. KEYWORD RESEARCH
# ══════════════════════════════════════════════════════════════════════════════
def generate_keywords(seed_keyword: str, category: str = "") -> dict:
seed = seed_keyword.lower().strip()
words = seed.split()
# Generate variations
modifiers_prefix = ["best", "top", "premium", "cheap", "professional", "heavy duty", "large", "small", "portable", "electric"]
modifiers_suffix = ["for home", "for kitchen", "for office", "set", "kit", "bundle", "with lid", "non stick", "stainless steel", "organic"]
keywords = []
# Main keyword
keywords.append({
"keyword": seed,
"search_volume": "High",
"competition": "High",
"opportunity": "Low",
"type": "Head"
})
# Long tail variations
for mod in modifiers_prefix[:5]:
kw = f"{mod} {seed}"
keywords.append({
"keyword": kw,
"search_volume": "Medium",
"competition": "Medium",
"opportunity": "Medium",
"type": "Long-tail"
})
for mod in modifiers_suffix[:5]:
kw = f"{seed} {mod}"
keywords.append({
"keyword": kw,
"search_volume": "Low",
"competition": "Low",
"opportunity": "High",
"type": "Long-tail"
})
# Backend keywords (for listing)
backend = [seed] + words + [f"{words[-1]} set", f"buy {seed}", f"{seed} amazon"]
return {
"seed_keyword": seed,
"total_keywords": len(keywords),
"keywords": keywords,
"backend_keywords": backend,
"top_opportunity": [k for k in keywords if k["opportunity"] == "High"][:3],
"tip": f"Target '{seed} for home' or '{seed} set' β€” lower competition, high buyer intent."
}