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

# Create regional sales review decks from CSV

> Calculate pipeline and performance metrics, then generate one leadership review per sales region.

This tutorial converts a sales-operations export into regional business review decks. Python calculates the metrics; Presenton explains performance, highlights risks, and structures the leadership discussion.

## Business outcome

Each regional vice president receives a consistent six-slide review covering attainment, pipeline coverage, conversion, sales velocity, retention, and the decisions needed for the next quarter.

## 1. Prepare the sales export

Save as `regional_sales.csv`:

```csv theme={null}
Region,Quarter,Revenue,Target,Qualified Pipeline,Win Rate,Average Sales Cycle,New Logos,Expansion Revenue,Churned ARR,Top Segment,Key Risk
North America,Q1 2026,4200000,4000000,11800000,31%,74,18,860000,190000,Enterprise,"Two strategic renewals are delayed"
North America,Q2 2026,4650000,4500000,12600000,34%,69,21,940000,160000,Enterprise,"Pipeline is concentrated in three accounts"
EMEA,Q1 2026,2850000,3200000,7900000,27%,91,13,510000,240000,Mid-market,"Procurement cycles increased in Germany"
EMEA,Q2 2026,3180000,3400000,9200000,29%,86,15,620000,210000,Mid-market,"Late-stage coverage remains below plan"
APAC,Q1 2026,1960000,1800000,5400000,33%,78,11,390000,90000,Enterprise,"Partner-sourced pipeline lacks consistency"
APAC,Q2 2026,2240000,2100000,6100000,35%,72,14,470000,110000,Enterprise,"Hiring is behind plan in two markets"
```

## 2. Install and configure

```bash theme={null}
python -m pip install pandas requests
export PRESENTON_API_KEY=YOUR_API_KEY
```

## 3. Generate one review per region

Save as `generate_regional_reviews.py`:

```python theme={null}
import os
from pathlib import Path

import pandas as pd
import requests

API_URL = "https://api.presenton.ai/api/v3/presentation/generate"
API_KEY = os.environ["PRESENTON_API_KEY"]
OUTPUT_DIR = Path("sales-reviews")
OUTPUT_DIR.mkdir(exist_ok=True)


def currency(value: float) -> str:
    return f"${value:,.0f}"


def build_review(region: str, rows: pd.DataFrame) -> str:
    rows = rows.reset_index(drop=True)
    latest = rows.iloc[-1]
    previous = rows.iloc[-2]
    attainment = latest["Revenue"] / latest["Target"]
    coverage = latest["Qualified Pipeline"] / latest["Target"]
    revenue_growth = latest["Revenue"] / previous["Revenue"] - 1

    quarterly_table = "\n".join(
        f"| {row['Quarter']} | {currency(row['Revenue'])} | "
        f"{currency(row['Target'])} | {row['Win Rate']} | "
        f"{row['Average Sales Cycle']} days |"
        for _, row in rows.iterrows()
    )

    return f"""
# {region} regional sales review — {latest['Quarter']}

Verified leadership metrics:
- Revenue: {currency(latest['Revenue'])}
- Target: {currency(latest['Target'])}
- Attainment: {attainment:.1%}
- Quarter-over-quarter revenue growth: {revenue_growth:.1%}
- Qualified pipeline: {currency(latest['Qualified Pipeline'])}
- Pipeline coverage: {coverage:.1f}x
- Win rate: {latest['Win Rate']}
- Average sales cycle: {latest['Average Sales Cycle']} days
- New logos: {latest['New Logos']}
- Expansion revenue: {currency(latest['Expansion Revenue'])}
- Churned ARR: {currency(latest['Churned ARR'])}
- Strongest segment: {latest['Top Segment']}
- Key risk: {latest['Key Risk']}

Quarterly history:
| Quarter | Revenue | Target | Win rate | Sales cycle |
| --- | ---: | ---: | ---: | ---: |
{quarterly_table}

Create a six-slide regional leadership review:
1. Executive scorecard
2. Revenue and attainment trend
3. Pipeline coverage and conversion
4. New-logo, expansion, and retention performance
5. Risks, root causes, and forecast implications
6. Three decisions and a 30/60/90-day action plan

Preserve every supplied value. Distinguish source facts from analysis, and do
not invent forecast numbers, customer names, or committed deal values.
""".strip()


def generate_review(region: str, rows: pd.DataFrame) -> None:
    response = requests.post(
        API_URL,
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "content": build_review(region, rows),
            "instructions": "Write for a CRO and regional VP audience. Prioritize decisions over description.",
            "n_slides": 6,
            "tone": "professional",
            "verbosity": "standard",
            "language": "English",
            "standard_template": "neo-modern",
            "theme": "professional-dark",
            "export_as": "pptx",
        },
        timeout=600,
    )
    response.raise_for_status()
    result = response.json()

    filename = region.lower().replace(" ", "-") + "-sales-review.pptx"
    output_path = OUTPUT_DIR / filename
    download = requests.get(
        result["path"],
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=120,
    )
    download.raise_for_status()
    output_path.write_bytes(download.content)
    print(f"Created {output_path} — review at {result['edit_path']}")


def main() -> None:
    sales = pd.read_csv("regional_sales.csv")
    numeric_columns = [
        "Revenue", "Target", "Qualified Pipeline", "Average Sales Cycle",
        "New Logos", "Expansion Revenue", "Churned ARR",
    ]
    if sales[numeric_columns].isnull().any().any():
        raise ValueError("Numeric sales fields must not be empty")

    for region, rows in sales.groupby("Region", sort=True):
        rows = rows.sort_values("Quarter")
        if len(rows) < 2:
            print(f"Skipping {region}: at least two quarters are required")
            continue
        try:
            generate_review(region, rows)
        except requests.RequestException as error:
            print(f"Failed for {region}: {error}")


if __name__ == "__main__":
    main()
```

## 4. Review the management narrative

Before the operating review, Sales Operations should verify:

* Attainment and pipeline coverage calculations.
* Quarter ordering and comparable reporting periods.
* Whether churn and expansion use the same currency and definitions.
* Claims about root causes, which are analysis rather than source facts.
* The feasibility and ownership of every proposed action.

## Extend the workflow

* Add forecast categories and rep capacity when those fields are governed.
* Generate one consolidated global deck after regional approval.
* Use structured JSON when every region must use exactly the same layouts.
* Trigger generation from the warehouse only after the quarter is closed.
* Store the source-query version beside the presentation ID for auditability.

<CardGroup cols={2}>
  <Card title="Create from structured JSON" icon="brackets-curly" href="/cloud/guides/create-from-json">
    Use fixed layouts when reporting consistency matters more than AI-planned structure.
  </Card>

  <Card title="Generate asynchronously" icon="clock-rotate-left" href="/cloud/guides/async-generation">
    Queue regional decks in a production batch workflow.
  </Card>
</CardGroup>
