Generate structured analytics reports with metrics, trends, and visualizations. Use when the user asks for a business review, monthly report, executive summary, deep dive, incident postmortem, or any deliverable that combines data, charts, and narrative for stakeholders.
You are a senior analytics professional generating a polished, stakeholder-ready report. Follow the instructions below based on the report type requested. Every report must be data-driven, clearly structured, and actionable.
Ask the user (or infer from context) which type of report to generate:
/weekly-report instead.)Also determine:
Generate the appropriate skeleton based on report type.
# Weekly Business Review: [Date Range]
## TL;DR
- [Bullet 1: most important finding]
- [Bullet 2: second most important]
- [Bullet 3: key risk or action item]
## Key Metrics Dashboard
| Metric | This Week | Last Week | WoW Change | 4-Week Avg | Status |
|--------|-----------|-----------|------------|------------|--------|
| [metric] | [value] | [value] | [+/-X%] | [value] | [indicator] |
Status indicators: UP (green, good direction), DOWN (red, bad direction), FLAT (neutral), ALERT (needs attention)
## Trends & Notable Changes
### [Topic 1]
[2-3 sentences with data support]
### [Topic 2]
[2-3 sentences with data support]
## Deep Dive: [One Topic Worth Investigating]
[3-5 paragraphs with supporting data and charts]
## Risks & Blockers
- [Risk 1]
- [Risk 2]
## Action Items
| Item | Owner | Due Date | Priority |
|------|-------|----------|----------|
| [action] | [person] | [date] | [P0/P1/P2] |
# Monthly Business Review: [Month Year]
## Executive Summary
[3-5 sentences summarizing the month]
## Key Metrics
### Revenue & Growth
| Metric | This Month | Last Month | MoM Change | Same Month LY | YoY Change |
|--------|-----------|------------|------------|---------------|------------|
### User Metrics
| Metric | Value | MoM | YoY | Trend (3mo) |
|--------|-------|-----|-----|-------------|
### Engagement Metrics
| Metric | Value | MoM | Target | vs Target |
|--------|-------|-----|--------|-----------|
## Cohort Analysis
[Retention heatmap or table for recent cohorts]
## Funnel Performance
[Conversion funnel with stage-by-stage analysis]
## Segment Breakdown
[Performance by key segments: platform, geography, user tier]
## Forecast vs Actuals
| Metric | Forecast | Actual | Variance | Notes |
|--------|----------|--------|----------|-------|
## Strategic Themes
### [Theme 1: e.g., "Mobile growth accelerating"]
[Analysis with data]
### [Theme 2: e.g., "Enterprise conversion improving"]
[Analysis with data]
## Recommendations
1. [Recommendation with expected impact]
2. [Recommendation]
3. [Recommendation]
## Appendix
[Detailed tables, methodology notes, data definitions]
# Deep Dive: [Question Being Investigated]
## Background
[Why this question matters, what triggered the investigation]
## Methodology
- Data sources: [list]
- Date range: [range]
- Filters applied: [filters]
- Key assumptions: [assumptions]
## Findings
### Finding 1: [Title]
[Analysis with charts and tables]
### Finding 2: [Title]
[Analysis]
### Finding 3: [Title]
[Analysis]
## Root Cause Analysis (if applicable)
[5 Whys or fishbone analysis]
## Recommendations
1. [Action with expected impact and effort estimate]
2. [Action]
3. [Action]
## Appendix
[SQL queries used, data tables, methodology details]
# Incident Postmortem: [Incident Title]
## Incident Summary
| Field | Value |
|-------|-------|
| Severity | [P0/P1/P2] |
| Detected | [timestamp] |
| Resolved | [timestamp] |
| Duration | [hours] |
| Impact | [quantified: users affected, revenue lost, data corrupted] |
## Timeline
| Time | Event |
|------|-------|
| [timestamp] | [what happened] |
| [timestamp] | [what happened] |
## Impact Quantification
[Precise numbers: X users affected, $Y revenue impact, Z records corrupted]
### Data Impact
- Records affected: [count]
- Date range affected: [range]
- Tables/metrics impacted: [list]
- Downstream reports affected: [list]
## Root Cause
[Clear, technical explanation]
## What Went Right
- [Positive 1]
- [Positive 2]
## What Went Wrong
- [Problem 1]
- [Problem 2]
## Action Items
| Item | Owner | Due Date | Status |
|------|-------|----------|--------|
| [preventive measure] | [person] | [date] | [status] |
## Data Recovery
[Steps taken or needed to fix corrupted data]
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import seaborn as sns
# Date range setup
end_date = pd.Timestamp('today').normalize()
# Adjust based on report type:
# Weekly: start_date = end_date - timedelta(days=7)
# Monthly: start_date = end_date.replace(day=1) - timedelta(days=1) # previous month start
# Custom: as specified by user
# Load data -- adapt to user's data source
# df = pd.read_csv('data.csv')
# df = pd.read_sql(query, connection)
# df = pd.read_parquet('data.parquet')
-- Daily active users (DAU)
SELECT
event_date,
COUNT(DISTINCT user_id) AS dau
FROM events
WHERE event_date BETWEEN :start_date AND :end_date
GROUP BY event_date
ORDER BY event_date;
-- Revenue summary
SELECT
DATE_TRUNC('WEEK', transaction_date) AS week,
COUNT(DISTINCT user_id) AS paying_users,
COUNT(*) AS transactions,
SUM(amount) AS gross_revenue,
SUM(amount) - SUM(COALESCE(refund_amount, 0)) AS net_revenue,
SUM(amount) / NULLIF(COUNT(DISTINCT user_id), 0) AS arpu
FROM transactions
WHERE transaction_date BETWEEN :start_date AND :end_date
GROUP BY DATE_TRUNC('WEEK', transaction_date)
ORDER BY week;
-- Conversion funnel
SELECT
COUNT(DISTINCT CASE WHEN step >= 1 THEN user_id END) AS step1_users,
COUNT(DISTINCT CASE WHEN step >= 2 THEN user_id END) AS step2_users,
COUNT(DISTINCT CASE WHEN step >= 3 THEN user_id END) AS step3_users,
COUNT(DISTINCT CASE WHEN step >= 4 THEN user_id END) AS step4_users
FROM user_funnel_events
WHERE event_date BETWEEN :start_date AND :end_date;
For every metric in the report:
def compute_metric_with_context(current, previous, target=None, historical=None):
"""
Compute a metric with all contextual information needed for reporting.
"""
result = {
'current': current,
'previous': previous,
'absolute_change': current - previous,
'pct_change': round((current - previous) / previous * 100, 1) if previous != 0 else None,
}
if target is not None:
result['target'] = target
result['vs_target_pct'] = round((current - target) / target * 100, 1) if target != 0 else None
if historical is not None and len(historical) >= 4:
rolling_mean = np.mean(historical[-4:])
rolling_std = np.std(historical[-4:])
result['rolling_avg'] = round(rolling_mean, 2)
result['is_anomaly'] = abs(current - rolling_mean) > 2 * rolling_std if rolling_std > 0 else False
# Trend: linear regression slope over last 4 periods
x = np.arange(len(historical[-4:]))
slope = np.polyfit(x, historical[-4:], 1)[0]
result['trend'] = 'UP' if slope > 0 else 'DOWN' if slope < 0 else 'FLAT'
return result
Create charts appropriate to the report. Save all as PNG files with descriptive names.
def create_time_series_chart(dates, values, title, ylabel, filename,
comparison_values=None, comparison_label=None,
target_value=None):
"""Standard time series line chart with optional comparison and target."""
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(dates, values, marker='o', linewidth=2, label='Current Period', color='#2563EB')
if comparison_values is not None:
ax.plot(dates, comparison_values, marker='s', linewidth=1.5,
linestyle='--', label=comparison_label or 'Previous Period', color='#9CA3AF')
if target_value is not None:
ax.axhline(y=target_value, color='#10B981', linestyle=':', linewidth=1.5, label=f'Target: {target_value:,.0f}')
ax.set_title(title, fontsize=14, fontweight='bold')
ax.set_ylabel(ylabel)
ax.legend()
ax.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig(filename, dpi=150, bbox_inches='tight')
plt.close()
def create_bar_chart(categories, values, title, ylabel, filename,
highlight_index=None, color='#2563EB'):
"""Standard bar chart with optional highlight."""
fig, ax = plt.subplots(figsize=(10, 5))
colors = [color] * len(categories)
if highlight_index is not None:
colors[highlight_index] = '#EF4444'
ax.bar(categories, values, color=colors, alpha=0.85, edgecolor='white')
ax.set_title(title, fontsize=14, fontweight='bold')
ax.set_ylabel(ylabel)
for i, v in enumerate(values):
ax.text(i, v + max(values) * 0.02, f'{v:,.0f}', ha='center', fontsize=10)
plt.xticks(rotation=45 if len(categories) > 6 else 0)
plt.tight_layout()
plt.savefig(filename, dpi=150, bbox_inches='tight')
plt.close()
def create_funnel_chart(stages, values, title, filename):
"""Horizontal funnel chart."""
fig, ax = plt.subplots(figsize=(10, 6))
colors = plt.cm.Blues(np.linspace(0.3, 0.9, len(stages)))
y_positions = range(len(stages) - 1, -1, -1)
bars = ax.barh(y_positions, values, color=colors, height=0.6, edgecolor='white')
ax.set_yticks(y_positions)
ax.set_yticklabels(stages)
ax.set_title(title, fontsize=14, fontweight='bold')
for i, (bar, val) in enumerate(zip(bars, values)):
pct = f'({val/values[0]*100:.1f}%)' if values[0] > 0 else ''
ax.text(bar.get_width() + max(values) * 0.02, bar.get_y() + bar.get_height()/2,
f'{val:,.0f} {pct}', va='center', fontsize=10)
if i > 0 and values[i-1] > 0:
conversion = val / values[i-1] * 100
ax.text(val / 2, bar.get_y() + bar.get_height()/2,
f'{conversion:.1f}%', va='center', ha='center', fontsize=9,
color='white', fontweight='bold')
ax.set_xlim(0, max(values) * 1.3)
plt.tight_layout()
plt.savefig(filename, dpi=150, bbox_inches='tight')
plt.close()
def create_comparison_table_chart(data_dict, title, filename):
"""
Create a styled table as an image for embedding in reports.
data_dict: dict of {column_name: [values]}
"""
df = pd.DataFrame(data_dict)
fig, ax = plt.subplots(figsize=(12, len(df) * 0.5 + 1.5))
ax.axis('off')
table = ax.table(
cellText=df.values,
colLabels=df.columns,
cellLoc='center',
loc='center'
)
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1.2, 1.8)
# Style header
for j in range(len(df.columns)):
table[0, j].set_facecolor('#2563EB')
table[0, j].set_text_props(color='white', fontweight='bold')
# Alternate row colors
for i in range(1, len(df) + 1):
for j in range(len(df.columns)):
if i % 2 == 0:
table[i, j].set_facecolor('#F3F4F6')
ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
plt.tight_layout()
plt.savefig(filename, dpi=150, bbox_inches='tight')
plt.close()
For a metric that improved:
[Metric] increased [X]% [WoW/MoM] to [value], up from [previous value]. This is [above/below/in line with] the [target/4-week average] of [value]. The improvement was primarily driven by [cause 1] and [cause 2]. If this trend continues, we are on track to [projected outcome].
For a metric that declined:
[Metric] declined [X]% [WoW/MoM] to [value], down from [previous value]. This marks the [Nth consecutive week/first time since date] of decline. The drop appears to be [driven by / correlated with] [factor]. Recommended action: [specific suggestion].
For an anomaly:
[Metric] showed an unusual [spike/drop] of [X]% on [date], deviating significantly from the rolling average of [value]. Investigation suggests [root cause or "further investigation needed"]. Impact: [quantified impact]. Status: [resolved/ongoing/monitoring].
Assemble all sections into a single markdown file. Include chart references:

Save the report as report_[type]_[date].md.
If the user requests HTML, wrap the markdown content in a styled HTML template:
def generate_html_report(markdown_content, title, chart_files):
"""Convert markdown report to styled HTML."""
# Use a clean, professional template
html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{title}</title>
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
color: #1F2937;
line-height: 1.6;
}}
h1 {{ color: #111827; border-bottom: 2px solid #2563EB; padding-bottom: 10px; }}
h2 {{ color: #1F2937; margin-top: 30px; }}
h3 {{ color: #374151; }}
table {{
border-collapse: collapse;
width: 100%;
margin: 15px 0;
}}
th {{
background-color: #2563EB;
color: white;
padding: 10px 15px;
text-align: left;
}}
td {{
padding: 8px 15px;
border-bottom: 1px solid #E5E7EB;
}}
tr:nth-child(even) {{ background-color: #F9FAFB; }}
.metric-up {{ color: #059669; }}
.metric-down {{ color: #DC2626; }}
.metric-flat {{ color: #6B7280; }}
.alert {{ background-color: #FEF2F2; border-left: 4px solid #DC2626; padding: 12px; margin: 10px 0; }}
.callout {{ background-color: #EFF6FF; border-left: 4px solid #2563EB; padding: 12px; margin: 10px 0; }}
img {{ max-width: 100%; height: auto; margin: 15px 0; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.12); }}
code {{ background-color: #F3F4F6; padding: 2px 6px; border-radius: 4px; font-size: 0.9em; }}
</style>
</head>
<body>
{{{{ content }}}}
</body>
</html>"""
return html
Before delivering the report, verify:
Save the following files:
report_[type]_[YYYY-MM-DD].md -- the main report.report_[type]_[YYYY-MM-DD].html -- HTML version if requested.report_*.png -- all charts, with descriptive filenames prefixed with report_.Category:business