โ† Back
โ˜†
// Claude API Report Generator โ€” freelance template
// Typical Upwork project: $800-1,200
// Stack: @anthropic-ai/sdk + csv-parse + nodemailer

const Anthropic = require('@anthropic-ai/sdk')
const fs = require('fs')
const path = require('path')

// ---- Config ----
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || 'your-key'
const REPORT_MODEL = 'claude-sonnet-4-20250514'

const client = new Anthropic({ apiKey: ANTHROPIC_API_KEY })

// ---- CSV Parser (simple, no deps) ----
function parseCSV(filePath) {
  const content = fs.readFileSync(filePath, 'utf8')
  const lines = content.trim().split('\n')
  const headers = lines[0].split(',').map(h => h.trim())

  return lines.slice(1).map(line => {
    const values = line.split(',').map(v => v.trim())
    const row = {}
    headers.forEach((h, i) => { row[h] = values[i] || '' })
    return row
  })
}

// ---- Report Generation ----
async function generateReport(data, config = {}) {
  const {
    companyName = 'Company',
    reportType = 'Weekly Sales Report',
    period = 'This Week',
    extraContext = '',
  } = config

  // Summarize data for the prompt
  const dataPreview = JSON.stringify(data.slice(0, 50), null, 2)
  const totalRows = data.length

  const prompt = `You are a business analyst generating an executive report.

## Context
- Company: ${companyName}
- Report Type: ${reportType}
- Period: ${period}
${extraContext ? `- Additional: ${extraContext}` : ''}

## Data (${totalRows} rows, showing first 50)
\`\`\`json
${dataPreview}
\`\`\`

## Instructions
Generate a professional executive summary report with:

1. **Key Metrics** โ€” top 3-5 numbers that matter (revenue, growth, etc.)
2. **Trends** โ€” what's going up, what's going down, why
3. **Highlights** โ€” notable wins or concerns
4. **Recommendations** โ€” 2-3 actionable items for next week
5. **Risk Flags** โ€” anything that needs immediate attention

Format as clean Markdown. Be concise, data-driven, no fluff.
Use bullet points and bold for key numbers.`

  const response = await client.messages.create({
    model: REPORT_MODEL,
    max_tokens: 2000,
    messages: [{ role: 'user', content: prompt }],
  })

  return response.content[0].text
}

// ---- HTML Wrapper ----
function wrapHTML(markdown, title) {
  // Simple markdown โ†’ HTML (bold, headers, bullets)
  let html = markdown
    .replace(/^### (.+)$/gm, '<h3>$1</h3>')
    .replace(/^## (.+)$/gm, '<h2>$1</h2>')
    .replace(/^# (.+)$/gm, '<h1>$1</h1>')
    .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
    .replace(/^- (.+)$/gm, '<li>$1</li>')
    .replace(/(<li>.*<\/li>\n?)+/g, '<ul>$&</ul>')
    .replace(/\n\n/g, '</p><p>')
    .replace(/\n/g, '<br>')

  return `<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: -apple-system, Arial, sans-serif; max-width: 700px; margin: 40px auto; color: #333; line-height: 1.6; }
    h1 { color: #1a1a2e; border-bottom: 2px solid #4361ee; padding-bottom: 8px; }
    h2 { color: #4361ee; margin-top: 24px; }
    h3 { color: #555; }
    strong { color: #1a1a2e; }
    ul { padding-left: 20px; }
    li { margin: 4px 0; }
    .footer { margin-top: 40px; font-size: 12px; color: #999; border-top: 1px solid #eee; padding-top: 12px; }
  </style>
</head>
<body>
  <h1>${title}</h1>
  <p>${html}</p>
  <div class="footer">Generated automatically by AI Report Generator ยท ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}</div>
</body>
</html>`
}

// ---- Main ----
async function main() {
  const inputFile = process.argv[2] || 'sample-data.csv'

  if (!fs.existsSync(inputFile)) {
    console.log('Creating sample data...')
    createSampleData(inputFile)
  }

  console.log(`[Report] Reading ${inputFile}...`)
  const data = parseCSV(inputFile)
  console.log(`[Report] ${data.length} rows loaded`)

  console.log('[Report] Generating with Claude...')
  const report = await generateReport(data, {
    companyName: 'Acme Corp',
    reportType: 'Weekly Sales Report',
    period: 'Apr 28 โ€” May 4, 2026',
  })

  // Save Markdown
  const mdFile = 'report.md'
  fs.writeFileSync(mdFile, `# Weekly Sales Report\n\n${report}`)
  console.log(`[Report] Markdown saved: ${mdFile}`)

  // Save HTML
  const htmlFile = 'report.html'
  fs.writeFileSync(htmlFile, wrapHTML(report, 'Weekly Sales Report โ€” Acme Corp'))
  console.log(`[Report] HTML saved: ${htmlFile}`)

  console.log('\n--- REPORT PREVIEW ---\n')
  console.log(report)
}

// ---- Sample Data Generator ----
function createSampleData(file) {
  const products = ['Widget A', 'Widget B', 'Pro Plan', 'Enterprise', 'Add-on Pack']
  const regions = ['US-West', 'US-East', 'EU', 'APAC']
  const lines = ['date,product,region,revenue,units,refunds']

  for (let d = 0; d < 7; d++) {
    const date = new Date(2026, 4, d + 28) // Apr 28 - May 4
    for (const product of products) {
      const region = regions[Math.floor(Math.random() * regions.length)]
      const units = Math.floor(Math.random() * 50) + 5
      const price = product.includes('Enterprise') ? 499 : product.includes('Pro') ? 99 : 29
      const revenue = units * price
      const refunds = Math.floor(Math.random() * 3)
      lines.push(`${date.toISOString().split('T')[0]},${product},${region},${revenue},${units},${refunds}`)
    }
  }

  fs.writeFileSync(file, lines.join('\n'))
  console.log(`[Sample] Created ${file} with ${lines.length - 1} rows`)
}

main().catch(console.error)