> ## 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.

# Page Performance → Focus Group

> Segment WordPress posts by engagement proxies, create reader personas for high and low performers, and run a focus group to understand content resonance

### Scenario

Some of your content resonates and some doesn't — but you don't always know why. You pull posts with comment counts and modification dates as engagement proxies, segment them into high and low performers, create Mavera personas for each segment's reader profile, then run a focus group to understand what makes certain content connect.

### Architecture

```mermaid theme={"dark"}
flowchart LR
    A["WordPress GET /wp-json/wp/v2/posts"] --> B["Segment by engagement proxy"]
    B --> C["POST /api/v1/personas"]
    C --> D["POST /api/v1/focus-groups"]
    D --> E["Content resonance insights"]
```

### Code

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

  WP, MV = os.environ["WORDPRESS_URL"], os.environ["MAVERA_API_KEY"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  strip_html = lambda h: re.sub(r"\s+", " ", re.sub(r"<[^>]+>", "", h or "")).strip()

  posts, page = [], 1
  while True:
      resp = requests.get(f"{WP}/wp-json/wp/v2/posts", params={
          "status": "publish", "per_page": 100, "page": page,
          "_fields": "id,title,content,comment_count,date,modified,link"})
      if resp.status_code == 400: break
      resp.raise_for_status()
      batch = resp.json()
      if not batch: break
      posts.extend(batch)
      if page >= int(resp.headers.get("X-WP-TotalPages", 1)): break
      page += 1
  print(f"Fetched {len(posts)} posts")

  posts.sort(key=lambda p: p.get("comment_count", 0), reverse=True)
  cutoff = max(len(posts) // 4, 3)
  high, low = posts[:cutoff], [p for p in posts[-cutoff:] if p.get("comment_count", 0) == 0] or posts[-cutoff:]
  print(f"High: {len(high)} | Low: {len(low)}")

  persona_ids = []
  for label, group, desc in [
      ("Engaged Reader", high, "Readers who comment on top-performing content"),
      ("Silent Visitor", low, "Visitors who read low-engagement content but never comment"),
  ]:
      titles = ", ".join(p["title"]["rendered"] for p in group[:5])
      r = requests.post(f"{MB}/personas", json={"name": f"WordPress {label}", "description": f"{desc}. Posts: {titles}."}, headers=MH)
      r.raise_for_status(); persona_ids.append(r.json()["id"])
      print(f"  Persona: {r.json()['name']} ({r.json()['id']})")

  high_s = "\n".join(f"- \"{p['title']['rendered']}\" ({p.get('comment_count',0)} comments)" for p in high[:5])
  low_s = "\n".join(f"- \"{p['title']['rendered']}\" ({p.get('comment_count',0)} comments)" for p in low[:5])
  fg = requests.post(f"{MB}/focus-groups", json={
      "name": "WordPress Content Performance Analysis", "persona_ids": persona_ids,
      "questions": ["What about high-performing content makes you engage?",
          "Why might low-performing posts fail to hold attention?",
          "What change would make you comment on a post?",
          "How important is the opening paragraph?"],
      "context": f"HIGH:\n{high_s}\n\nExcerpt: {strip_html(high[0]['content']['rendered'])[:400]}\n\n"
                 f"LOW:\n{low_s}\n\nExcerpt: {strip_html(low[0]['content']['rendered'])[:400]}",
  }, headers=MH)
  fg.raise_for_status(); result = fg.json()
  print(f"\nFocus group: {result['id']}")
  for r in result.get("responses", []):
      print(f"\n[{r['persona_name']}] {r['question']}\n  → {r['answer']}")
  ```

  ```javascript JavaScript theme={"dark"}
  const WP = process.env.WORDPRESS_URL, MV = process.env.MAVERA_API_KEY;
  const MB = "https://app.mavera.io/api/v1";
  const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  const stripHtml = h => (h || "").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();

  (async () => {
    const posts = []; let page = 1;
    while (true) {
      const resp = await fetch(`${WP}/wp-json/wp/v2/posts?status=publish&per_page=100&page=${page}&_fields=id,title,content,comment_count,date,modified,link`);
      if (resp.status === 400) break;
      const batch = await resp.json();
      if (!batch.length) break;
      posts.push(...batch);
      if (page >= parseInt(resp.headers.get("X-WP-TotalPages") || "1")) break;
      page++;
    }
    console.log(`Fetched ${posts.length} posts`);

    posts.sort((a, b) => (b.comment_count || 0) - (a.comment_count || 0));
    const cut = Math.max(Math.floor(posts.length / 4), 3);
    const high = posts.slice(0, cut);
    const low = posts.slice(-cut).filter(p => !(p.comment_count || 0));
    const lowGroup = low.length ? low : posts.slice(-cut);

    const personaIds = [];
    for (const [label, group, desc] of [
      ["Engaged Reader", high, "Readers who comment on top content"],
      ["Silent Visitor", lowGroup, "Visitors who read but never comment"],
    ]) {
      const r = await fetch(`${MB}/personas`, { method: "POST", headers: MH,
        body: JSON.stringify({ name: `WordPress ${label}`, description: `${desc}. Posts: ${group.slice(0,5).map(p=>p.title.rendered).join(", ")}.` }) });
      personaIds.push((await r.json()).id);
    }
    const fmt = (arr) => arr.slice(0,5).map(p => `- "${p.title.rendered}" (${p.comment_count||0} comments)`).join("\n");
    const fg = await fetch(`${MB}/focus-groups`, { method: "POST", headers: MH,
      body: JSON.stringify({ name: "WordPress Content Performance Analysis", persona_ids: personaIds,
        questions: ["What about high-performing content makes you engage?", "Why might low-performing posts fail?",
          "What change would make you comment?", "How important is the opening paragraph?"],
        context: `HIGH:\n${fmt(high)}\n\nExcerpt: ${stripHtml(high[0]?.content?.rendered).slice(0,400)}\n\nLOW:\n${fmt(lowGroup)}\n\nExcerpt: ${stripHtml(lowGroup[0]?.content?.rendered).slice(0,400)}` }) });
    const result = await fg.json();
    console.log(`\nFocus group: ${result.id}`);
    for (const r of result.responses || []) console.log(`\n[${r.persona_name}] ${r.question}\n  → ${r.answer}`);
  })();
  ```
</CodeGroup>

### Example Output

```json theme={"dark"}
{
  "id": "fg_3c7d2e9b",
  "name": "WordPress Content Performance Analysis",
  "responses": [
    {
      "persona_name": "WordPress Engaged Reader",
      "question": "What about the high-performing content makes you want to engage?",
      "answer": "The high-comment posts take a clear stance — they don't just explain, they argue a position. That gives me something to agree or disagree with. The listicle-style posts with no opinion get a skim and a close."
    },
    {
      "persona_name": "WordPress Silent Visitor",
      "question": "Why might the low-performing posts fail to hold your attention?",
      "answer": "They read like documentation — accurate but flat. There's no hook in the first paragraph, no reason to keep scrolling. I found the information I needed in the first 200 words and left."
    }
  ]
}
```

### Error Handling

<AccordionGroup>
  <Accordion title="comment_count not available or always zero">
    Some WordPress configurations disable comments entirely or use a plugin like Disqus that doesn't sync counts back. If `comment_count` is 0 for all posts, consider integrating with GA4 for pageview data (`/integrations/ga4`) or use the `modified` date relative to `date` as an engagement proxy — frequently updated posts indicate editorial investment.
  </Accordion>

  <Accordion title="Content field too large for focus group context">
    Full post HTML can be very large. The code truncates excerpts to 500 characters. For richer context without token overflow, use a summarization step: send each post's content through Mavera responses for a 2-sentence summary, then pass those summaries into the focus group context.
  </Accordion>
</AccordionGroup>
