← 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);