Skip to main content

Scenario

Customers at your downtown café are different from customers at your suburban location. Downtown draws office workers during lunch; suburbs get families on weekends. You extract review text with location context, identify distinct customer profiles per location, then create geo-specific Mavera personas. These local personas power hyper-targeted marketing for each location. Flow: Google reviews + location data → Identify per-location customer profiles → Mavera POST /personas → Geo-specific personas

Code

import os, requests, time, re
from collections import Counter

GOOG = os.environ["GOOGLE_ACCESS_TOKEN"]
ACCT = os.environ["GOOGLE_ACCOUNT_ID"]
MV = os.environ["MAVERA_API_KEY"]
GB_BASE = "https://mybusiness.googleapis.com/v4"
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
GB_H = {"Authorization": f"Bearer {GOOG}"}
STAR_MAP = {"ONE": 1, "TWO": 2, "THREE": 3, "FOUR": 4, "FIVE": 5}

# 1. Locations + reviews
locations = requests.get(f"{GB_BASE}/{ACCT}/locations",
    headers=GB_H, params={"pageSize": 100}).json().get("locations", [])

persona_ids = []
for loc in locations[:8]:
    loc_id = loc.get("name", "")
    loc_name = loc.get("locationName", "Unknown")
    city = loc.get("address", {}).get("locality", "Unknown")
    state = loc.get("address", {}).get("administrativeArea", "")
    category = loc.get("primaryCategory", {}).get("displayName", "Business")

    r = requests.get(f"{GB_BASE}/{loc_id}/reviews",
        headers=GB_H, params={"pageSize": 50})
    if r.status_code != 200:
        continue
    reviews = r.json().get("reviews", [])

    if len(reviews) < 5:
        continue

    # 2. Extract customer signals from review text
    all_text = " ".join(rev.get("comment", "") for rev in reviews).lower()
    signals = {
        "family": len(re.findall(r'\b(family|kids|children|stroller)\b', all_text)),
        "business": len(re.findall(r'\b(meeting|office|work|lunch break|colleague)\b', all_text)),
        "tourist": len(re.findall(r'\b(tourist|visiting|trip|vacation|hotel)\b', all_text)),
        "local_regular": len(re.findall(r'\b(regular|every week|always come|our spot)\b', all_text)),
        "date_night": len(re.findall(r'\b(date|anniversary|romantic|evening)\b', all_text)),
    }
    dominant_segment = max(signals, key=signals.get) if any(signals.values()) else "general"

    avg_rating = sum(STAR_MAP.get(r.get("starRating", "FIVE"), 5) for r in reviews) / len(reviews)

    # 3. Create geo-specific persona
    sample_quotes = [rev.get("comment", "")[:150] for rev in reviews if rev.get("comment")][:3]

    p = requests.post("https://app.mavera.io/api/v1/personas", headers=MV_H, json={
        "name": f"GBP: {loc_name}{dominant_segment.replace('_', ' ').title()}",
        "description": (
            f"Customer of {loc_name} in {city}, {state}. Category: {category}. "
            f"N={len(reviews)} reviews, avg {avg_rating:.1f}/5. "
            f"Dominant segment: {dominant_segment}. "
            f"Signal scores: {', '.join(f'{k}: {v}' for k, v in sorted(signals.items(), key=lambda x: -x[1])[:3])}. "
            f"Sample: \"{sample_quotes[0][:100]}...\""
        ),
        "demographic": {
            "location": f"{city}, {state}",
            "customer_type": dominant_segment,
        },
        "psychographic": {
            "visit_context": dominant_segment,
            "satisfaction": "high" if avg_rating >= 4 else "mixed" if avg_rating >= 3 else "low",
        },
    }).json()
    persona_ids.append({"id": p["id"], "location": loc_name, "city": city, "segment": dominant_segment})
    print(f"Persona: {p['id']}{loc_name} ({city}) → {dominant_segment}")
    time.sleep(0.5)

print(f"\nCreated {len(persona_ids)} local personas")

Example Output

{
  "personas_created": 5,
  "local_profiles": [
    { "location": "Downtown Café", "city": "Austin", "segment": "business", "avg_rating": 4.5,
      "insight": "Office workers on lunch. Care about speed, Wi-Fi, quiet spaces." },
    { "location": "Suburban Store", "city": "Round Rock", "segment": "family", "avg_rating": 4.1,
      "insight": "Families on weekends. Care about kid-friendliness, parking, portion sizes." },
    { "location": "Airport Kiosk", "city": "Austin", "segment": "tourist", "avg_rating": 3.7,
      "insight": "Travelers in a hurry. Care about grab-and-go, clear pricing, speed." },
    { "location": "University Ave", "city": "Austin", "segment": "local_regular", "avg_rating": 4.6,
      "insight": "Regulars who come weekly. Care about consistency, loyalty, and knowing the staff." }
  ]
}

Error Handling

Each location requires separate review API calls. Batch across locations with delays to stay within 300 req/min.
Some Google reviews are ratings-only with no text. Filter comment: null reviews before persona creation.
Simple keyword matching for segments is approximate. For better accuracy, send review text to Mave for classification.