← back to Designer Wallcoverings

DW-Agents/dw-agents/generate-eod-report.ts

319 lines

#!/usr/bin/env tsx
/**
 * End of Day Report Generator
 *
 * Generates comprehensive daily reports covering:
 * - Finance statistics (from Accounting Agent)
 * - Sales statistics (from Shopify Store Agent)
 * - Support statistics (from Zendesk Chat Agent)
 * - System health and performance metrics
 */

import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY || ''
});

interface AgentStats {
  name: string;
  port: number;
  status: 'online' | 'offline';
  data?: any;
}

// Agent endpoints
const AGENTS = {
  accounting: { name: 'Accounting', port: 9882, url: 'http://localhost:9882' },
  zendesk: { name: 'Zendesk Chat', port: 9884, url: 'http://localhost:9884' },
  shopify: { name: 'Shopify Store', port: 7238, url: 'http://localhost:7238' },
  digital: { name: 'Digital Samples', port: 9879, url: 'http://localhost:9879' },
  marketing: { name: 'Marketing', port: 9881, url: 'http://localhost:9881' }
};

async function fetchAgentData(url: string): Promise<any> {
  try {
    const response = await fetch(url, {
      method: 'GET',
      headers: { 'Accept': 'application/json' }
    });
    if (response.ok) {
      const text = await response.text();
      try {
        return JSON.parse(text);
      } catch {
        return { html: text };
      }
    }
    return null;
  } catch (error) {
    return null;
  }
}

async function getShopifyOrderStats(): Promise<any> {
  try {
    const SHOPIFY_DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
    const SHOPIFY_TOKEN = process.env.SHOPIFY_ACCESS_TOKEN || '';

    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const todayISO = today.toISOString();

    const response = await fetch(
      `https://${SHOPIFY_DOMAIN}/admin/api/2024-01/orders.json?created_at_min=${todayISO}&status=any`,
      {
        headers: {
          'X-Shopify-Access-Token': SHOPIFY_TOKEN,
          'Content-Type': 'application/json'
        }
      }
    );

    if (response.ok) {
      const data = await response.json();
      const orders = data.orders || [];

      const totalRevenue = orders.reduce((sum: number, order: any) => {
        return sum + parseFloat(order.total_price || 0);
      }, 0);

      return {
        totalOrders: orders.length,
        totalRevenue: totalRevenue.toFixed(2),
        orders: orders.map((o: any) => ({
          id: o.id,
          name: o.name,
          total: o.total_price,
          status: o.financial_status,
          createdAt: o.created_at
        }))
      };
    }
  } catch (error) {
    console.error('Error fetching Shopify stats:', error);
  }
  return null;
}

async function getZendeskTicketStats(): Promise<any> {
  try {
    const ZENDESK_DOMAIN = 'designerwallcoverings.zendesk.com';
    const ZENDESK_EMAIL = 'info@designerwallcoverings.com';
    const ZENDESK_PASSWORD = '*Zopimaccess911*';

    const auth = Buffer.from(`${ZENDESK_EMAIL}/token:${ZENDESK_PASSWORD}`).toString('base64');

    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const todayTimestamp = Math.floor(today.getTime() / 1000);

    const response = await fetch(
      `https://${ZENDESK_DOMAIN}/api/v2/search.json?query=type:ticket created>${todayTimestamp}`,
      {
        headers: {
          'Authorization': `Basic ${auth}`,
          'Content-Type': 'application/json'
        }
      }
    );

    if (response.ok) {
      const data = await response.json();
      const tickets = data.results || [];

      const statusCounts = tickets.reduce((acc: any, ticket: any) => {
        acc[ticket.status] = (acc[ticket.status] || 0) + 1;
        return acc;
      }, {});

      return {
        totalTickets: tickets.length,
        statusBreakdown: statusCounts,
        newTickets: statusCounts.new || 0,
        openTickets: statusCounts.open || 0,
        pendingTickets: statusCounts.pending || 0,
        solvedTickets: statusCounts.solved || 0
      };
    }
  } catch (error) {
    console.error('Error fetching Zendesk stats:', error);
  }
  return null;
}

async function getPM2ProcessStats(): Promise<any> {
  try {
    const { execSync } = require('child_process');
    const output = execSync('pm2 jlist', { encoding: 'utf8' });
    const processes = JSON.parse(output);

    const agentProcesses = processes.filter((p: any) => p.name?.startsWith('dw-'));

    return {
      totalProcesses: agentProcesses.length,
      onlineProcesses: agentProcesses.filter((p: any) => p.pm2_env?.status === 'online').length,
      restarts: agentProcesses.reduce((sum: number, p: any) => sum + (p.pm2_env?.restart_time || 0), 0),
      uptime: agentProcesses.map((p: any) => ({
        name: p.name,
        uptime: Math.floor((Date.now() - p.pm2_env?.pm_uptime) / 1000 / 60), // minutes
        status: p.pm2_env?.status
      }))
    };
  } catch (error) {
    console.error('Error fetching PM2 stats:', error);
  }
  return null;
}

async function generateReportWithClaude(data: any): Promise<string> {
  const prompt = `Generate a professional end-of-day report for Designer Wallcoverings based on the following data:

${JSON.stringify(data, null, 2)}

Please format the report with:
1. Executive Summary (2-3 sentences highlighting key metrics)
2. Financial Performance
   - Revenue summary
   - Notable transactions
   - Trends
3. Sales Activity
   - Orders processed
   - Popular products (if data available)
   - Order status breakdown
4. Customer Support
   - Ticket volume
   - Resolution rate
   - Outstanding issues
5. System Health
   - Agent uptime
   - Performance metrics
   - Any issues or alerts
6. Action Items & Recommendations

Format the report in clean markdown with clear sections and bullet points.`;

  try {
    const message = await anthropic.messages.create({
      model: 'claude-opus-4-8',
      max_tokens: 4096,
      messages: [{
        role: 'user',
        content: prompt
      }]
    });

    const content = message.content[0];
    return content.type === 'text' ? content.text : 'Unable to generate report';
  } catch (error) {
    console.error('Error generating report with Claude:', error);
    return generateFallbackReport(data);
  }
}

function generateFallbackReport(data: any): string {
  const date = new Date().toLocaleDateString('en-US', {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  });

  return `# End of Day Report - Designer Wallcoverings
## ${date}

### Executive Summary
- Total Orders: ${data.sales?.totalOrders || 0}
- Revenue: $${data.sales?.totalRevenue || '0.00'}
- Support Tickets: ${data.support?.totalTickets || 0}
- System Health: ${data.system?.onlineProcesses || 0}/${data.system?.totalProcesses || 0} agents online

### Financial Performance
${data.sales ? `
- **Total Revenue Today:** $${data.sales.totalRevenue}
- **Orders Processed:** ${data.sales.totalOrders}
- **Average Order Value:** $${data.sales.totalOrders > 0 ? (parseFloat(data.sales.totalRevenue) / data.sales.totalOrders).toFixed(2) : '0.00'}
` : 'Financial data unavailable'}

### Sales Activity
${data.sales?.orders?.length > 0 ? `
Recent orders:
${data.sales.orders.slice(0, 5).map((o: any) => `- Order ${o.name}: $${o.total} (${o.status})`).join('\n')}
` : 'No orders recorded today'}

### Customer Support
${data.support ? `
- **Total Tickets:** ${data.support.totalTickets}
- **New:** ${data.support.newTickets || 0}
- **Open:** ${data.support.openTickets || 0}
- **Pending:** ${data.support.pendingTickets || 0}
- **Solved:** ${data.support.solvedTickets || 0}
` : 'Support data unavailable'}

### System Health
${data.system ? `
- **Active Agents:** ${data.system.onlineProcesses}/${data.system.totalProcesses}
- **Total Restarts Today:** ${data.system.restarts}

Agent Status:
${data.system.uptime?.slice(0, 10).map((p: any) => `- ${p.name}: ${p.status} (${p.uptime}m uptime)`).join('\n')}
` : 'System data unavailable'}

### Recommendations
- Monitor any offline agents and investigate restart patterns
- Follow up on pending support tickets
- Review order fulfillment for today's sales

---
*Report generated automatically at ${new Date().toLocaleString()}*
`;
}

async function main() {
  console.log('🚀 Generating End of Day Report...\n');

  const reportData: any = {
    date: new Date().toISOString(),
    sales: null,
    support: null,
    system: null,
    agents: {}
  };

  // Gather data from all sources
  console.log('📊 Gathering sales data from Shopify...');
  reportData.sales = await getShopifyOrderStats();

  console.log('💬 Gathering support data from Zendesk...');
  reportData.support = await getZendeskTicketStats();

  console.log('⚙️  Gathering system health data...');
  reportData.system = await getPM2ProcessStats();

  console.log('🤖 Generating report with Claude AI...\n');
  const report = await generateReportWithClaude(reportData);

  // Save report to file
  const reportDate = new Date().toISOString().split('T')[0];
  const reportPath = `/root/Projects/Designer-Wallcoverings/DW-Agents/reports/eod-report-${reportDate}.md`;

  const fs = require('fs');
  const path = require('path');

  // Ensure reports directory exists
  const reportsDir = path.dirname(reportPath);
  if (!fs.existsSync(reportsDir)) {
    fs.mkdirSync(reportsDir, { recursive: true });
  }

  fs.writeFileSync(reportPath, report);

  console.log('✅ Report generated successfully!');
  console.log(`📄 Saved to: ${reportPath}\n`);
  console.log('─'.repeat(80));
  console.log(report);
  console.log('─'.repeat(80));
}

main().catch(console.error);