> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Product Feedback Focus Group Mirror

> Search Reddit for product mentions, create subreddit-demographic personas, and run Focus Groups comparing synthetic vs real feedback

## Scenario

Real product feedback exists on Reddit — buried in search results and comments. This job searches for mentions, creates Mavera personas mirroring the subreddit population (frustrated newcomer, power user, comparison shopper), then runs a Focus Group. Comparing real feedback to synthetic responses validates persona accuracy.

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A["Reddit GET /search?q={product}"] --> B["Extract archetypes"]
    B --> C["Mavera POST /personas"]
    C --> D["POST /focus-groups"]
    D --> E["Compare real vs synthetic"]
```

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time

  # --- Auth setup same as Job 1 ---

  PRODUCT = "Notion"

  # 1. Search Reddit for product mentions
  r = requests.get(f"{RD}/search", headers=RD_H,
      params={"q": PRODUCT, "sort": "relevance", "limit": 50, "type": "link", "raw_json": 1})
  r.raise_for_status()
  results = [p["data"] for p in r.json()["data"]["children"]]

  real_feedback = []
  for post in results:
      if post.get("selftext") or post.get("title"):
          real_feedback.append({"title": post["title"], "text": post.get("selftext","")[:600],
              "subreddit": post.get("subreddit",""), "score": post.get("score",0)})
      if post.get("num_comments", 0) > 5:
          cr = requests.get(f"{RD}/comments/{post['id']}", headers=RD_H,
              params={"limit": 5, "depth": 1, "sort": "top", "raw_json": 1})
          if cr.ok and len(cr.json()) > 1:
              for c in cr.json()[1]["data"]["children"]:
                  body = c.get("data",{}).get("body","")
                  if body and body not in ("[removed]","[deleted]"):
                      real_feedback.append({"title": f"Re: {post['title']}", "text": body[:400],
                          "subreddit": post.get("subreddit",""), "score": c["data"].get("score",0)})
          time.sleep(0.7)

  # 2. Create subreddit-demographic personas
  PERSONAS = [
      {"name": f"Frustrated {PRODUCT} Newcomer", "desc": f"New to {PRODUCT}, overwhelmed. Compares to simpler alternatives. Frustrated, seeks validation."},
      {"name": f"{PRODUCT} Power User", "desc": f"Uses {PRODUCT} for everything. Builds templates. Enthusiastic, slightly defensive of the product."},
      {"name": "Comparison Shopper", "desc": f"Evaluating {PRODUCT} vs Obsidian, Coda, Airtable. Analytical, price-sensitive, feature-focused."},
      {"name": "Team Lead / Manager", "desc": f"Rolled out {PRODUCT} to a team. Cares about adoption friction, onboarding, per-seat pricing."},
  ]
  persona_ids = []
  for p in PERSONAS:
      created = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
          "name": f"Reddit: {p['name']}", "description": p["desc"],
          "psychographic": {"source": "reddit_search", "product": PRODUCT},
      }).json()
      persona_ids.append({"id": created["id"], "name": p["name"]}); time.sleep(0.3)

  # 3. Focus Group
  fg = requests.post(f"{MV_BASE}/focus-groups", headers=MV_H, json={
      "name": f"Reddit Mirror: {PRODUCT} Feedback",
      "persona_ids": [p["id"] for p in persona_ids],
      "questions": [
          f"What was your first impression of {PRODUCT}?",
          f"What is the biggest problem you have with {PRODUCT} right now?",
          f"If you switched away from {PRODUCT}, what would you switch to and why?",
          f"What one feature would you add to {PRODUCT}?",
      ],
      "responses_per_persona": 2,
  }).json()

  for _ in range(20):
      time.sleep(5)
      data = requests.get(f"{MV_BASE}/focus-groups/{fg['id']}", headers=MV_H).json()
      if data.get("status") == "completed": break

  for resp in data.get("responses", []):
      name = next((p["name"] for p in persona_ids if p["id"] == resp.get("persona_id")), "?")
      print(f"[{name}] Q: {resp.get('question','')[:80]}")
      print(f"  A: {resp.get('answer','')[:300]}\n")

  print(f"\nREAL REDDIT (top 3 by score):")
  for fb in sorted(real_feedback, key=lambda x: -x["score"])[:3]:
      print(f"[/r/{fb['subreddit']}] (score: {fb['score']}) {fb['title'][:80]}")
      print(f"  {fb['text'][:200]}\n")
  ```

  ```javascript JavaScript theme={"dark"}
  // --- Auth setup same as Job 1 ---

  const PRODUCT = "Notion";

  // 1. Search
  const results = (await (await fetch(
    `${RD}/search?q=${encodeURIComponent(PRODUCT)}&sort=relevance&limit=50&type=link&raw_json=1`,
    { headers: RD_H })).json()).data.children.map(p => p.data);

  const realFeedback = [];
  for (const post of results) {
    if (post.selftext || post.title)
      realFeedback.push({ title: post.title, text: (post.selftext||"").slice(0,600),
        subreddit: post.subreddit, score: post.score || 0 });
    if ((post.num_comments||0) > 5) {
      const cr = await fetch(`${RD}/comments/${post.id}?limit=5&depth=1&sort=top&raw_json=1`, { headers: RD_H });
      if (cr.ok) for (const c of ((await cr.json())[1]?.data?.children || [])) {
        const body = c.data?.body || "";
        if (body && body !== "[removed]" && body !== "[deleted]")
          realFeedback.push({ title: `Re: ${post.title}`, text: body.slice(0,400),
            subreddit: post.subreddit, score: c.data?.score || 0 });
      }
      await new Promise(r => setTimeout(r, 700));
    }
  }

  // 2. Personas
  const PERSONAS = [
    { name: `Frustrated ${PRODUCT} Newcomer`, desc: `New, overwhelmed. Compares to simpler alternatives.` },
    { name: `${PRODUCT} Power User`, desc: `Uses for everything. Builds templates. Enthusiastic.` },
    { name: "Comparison Shopper", desc: `Evaluating vs Obsidian, Coda, Airtable. Analytical.` },
    { name: "Team Lead / Manager", desc: `Team rollout. Adoption friction, onboarding, pricing.` },
  ];
  const personaIds = [];
  for (const p of PERSONAS) {
    const created = await fetch(`${MV_BASE}/personas`, { method: "POST", headers: MV_H,
      body: JSON.stringify({ name: `Reddit: ${p.name}`, description: p.desc,
        psychographic: { source: "reddit_search", product: PRODUCT } }),
    }).then(r => r.json());
    personaIds.push({ id: created.id, name: p.name });
    await new Promise(r => setTimeout(r, 300));
  }

  // 3. Focus Group
  const fg = await fetch(`${MV_BASE}/focus-groups`, { method: "POST", headers: MV_H,
    body: JSON.stringify({
      name: `Reddit Mirror: ${PRODUCT}`, persona_ids: personaIds.map(p => p.id),
      questions: [`First impression of ${PRODUCT}?`, `Biggest problem with ${PRODUCT}?`,
        `If you switched, what to and why?`, `One feature to add?`],
      responses_per_persona: 2,
    }),
  }).then(r => r.json());

  let data; for (let i = 0; i < 20; i++) {
    await new Promise(r => setTimeout(r, 5000));
    data = await fetch(`${MV_BASE}/focus-groups/${fg.id}`, { headers: MV_H }).then(r => r.json());
    if (data.status === "completed") break;
  }

  for (const resp of data.responses || []) {
    const name = personaIds.find(p => p.id === resp.persona_id)?.name || "?";
    console.log(`[${name}] Q: ${(resp.question||"").slice(0,80)}`);
    console.log(`  A: ${(resp.answer||"").slice(0,300)}\n`);
  }
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
[Frustrated Notion Newcomer] Q: First impression of Notion?
  A: Overwhelming. Came from Apple Notes, got a blank page and a YouTube
     tutorial rabbit hole. Three hours later, still no functioning to-do list.

[Comparison Shopper] Q: If you switched, what to and why?
  A: Obsidian for local-first. Coda for spreadsheet logic. Template ecosystem
     keeps me in Notion, but offline is a dealbreaker.

REAL REDDIT (top by score):
[/r/Notion] (847) "I love Notion but the mobile app makes me want to throw my phone"
```

## Error Handling

<AccordionGroup>
  <Accordion title="Search relevance">Add `restrict_sr=true` or use `sort=top&t=year` for higher-quality posts.</Accordion>
  <Accordion title="NSFW filtering">Add `nsfw=false` and filter `over_18 === true` posts.</Accordion>
</AccordionGroup>

***

<CardGroup cols={2}>
  <Card title="Reddit Integration" icon="arrow-left" href="/integrations/reddit" />

  <Card title="Focus Groups" icon="users" href="/features/focus-groups" />
</CardGroup>
