mcpField

Report Builder

github.com/eddiebe147ドキュメント51 回インストール
リポジトリ

Report Builder について

Report Builderは、データを基に業務レポートを作成するスキルです。定期報告や意思決定資料を作るチーム向けで、要約、指標、図表をまとめ、PDF、Word、HTMLに出力できます。

主な機能

  1. 01業務・財務レポート
  2. 02エグゼクティブサマリーと提案
  3. 03図表・テーブル・傾向分析
  4. 04プロジェクト状況とマイルストーンの報告
  5. 05PDF、Word、HTMLへの出力
  6. 06定期レポートのスケジュール設定

活用シーン

  1. 01月次パフォーマンスレビューの作成
  2. 02プロジェクトの進捗とリスクの要約
  3. 03期間ごとの財務結果の比較
  4. 04関係者向け定期レポートの設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/eddiebe147/claude-settings --skill report-builder

インストール後、Claude Code で/report-builderを入力して読み込まれたか確認します。

詳細

レポート作成

レポート作成スキルは、データ分析、可視化、エグゼクティブサマリー、書式設定されたセクションを備えた、プロフェッショナルなビジネスレポートの作成を自動化します。財務レポート、プロジェクト状況報告、市場分析、パフォーマンスレビュー、分析レポートなど、さまざまな種類のレポートに対応します。このスキルは複数のソースからデータを統合し、明確で実行につながる形式で提示します。

チャート、表、トレンド分析、重要な知見を盛り込んだレポートを、PDF、Word、HTML形式で作成します。定期レポート、自動ダッシュボード、データに基づく意思決定の文書化に最適です。

主なワークフロー

ワークフロー 1:エグゼクティブレポートの作成

目的: サマリー、主要な指標、推奨事項を含む、概要レベルのレポートを作成します

手順:

  1. さまざまなソースからデータを収集します
  2. 主要業績評価指標(KPI)を計算します
  3. 重要なポイントを強調したエグゼクティブサマリーを作成します
  4. データを可視化します(チャート、グラフ)
  5. 裏付けとなるデータを含む詳細なセクションを追加します
  6. 推奨事項とアクションアイテムを含めます
  7. プロフェッショナルなレイアウトに整えます
  8. ブランド要素を適用してPDFにエクスポートします

実装:

const PDFDocument = require('pdfkit');
const ChartJS = require('chartjs-node-canvas');
const fs = require('fs');

async function generateExecutiveReport(reportData, outputPath) {
  const doc = new PDFDocument({ margin: 50 });
  const stream = fs.createWriteStream(outputPath);
  doc.pipe(stream);

  // Cover page
  doc.fontSize(32)
     .fillColor('#2C3E50')
     .text(reportData.title, { align: 'center' })
     .moveDown()
     .fontSize(18)
     .fillColor('#7F8C8D')
     .text(reportData.subtitle, { align: 'center' })
     .moveDown(2)
     .fontSize(12)
     .text(`Reporting Period: ${reportData.period}`, { align: 'center' })
     .text(`Generated: ${new Date().toLocaleDateString()}`, { align: 'center' });

  // Company logo if provided
  if (reportData.logo) {
    doc.image(reportData.logo, doc.page.width / 2 - 50, 150, { width: 100 });
  }

  // Executive Summary
  doc.addPage();
  doc.fontSize(24)
     .fillColor('#2C3E50')
     .text('Executive Summary', { underline: true })
     .moveDown();

  doc.fontSize(11)
     .fillColor('#34495E')
     .text(reportData.executiveSummary, { align: 'justify', lineGap: 4 });

  // Key Metrics Section
  doc.moveDown(2);
  doc.fontSize(18)
     .fillColor('#2C3E50')
     .text('Key Performance Indicators');

  doc.moveDown();

  // KPI Cards (2x2 grid)
  const kpiWidth = 220;
  const kpiHeight = 100;
  const spacing = 20;

  reportData.kpis.forEach((kpi, index) => {
    const row = Math.floor(index / 2);
    const col = index % 2;
    const x = 50 + col * (kpiWidth + spacing);
    const y = doc.y + row * (kpiHeight + spacing);

    // KPI Card background
    doc.fillColor(kpi.trend === 'up' ? '#27AE60' : kpi.trend === 'down' ? '#E74C3C' : '#3498DB')
       .opacity(0.1)
       .rect(x, y, kpiWidth, kpiHeight)
       .fill();

    doc.opacity(1);

    // KPI Label
    doc.fontSize(10)
       .fillColor('#7F8C8D')
       .text(kpi.label, x + 10, y + 10, { width: kpiWidth - 20 });

    // KPI Value
    doc.fontSize(28)
       .fillColor('#2C3E50')
       .text(kpi.value, x + 10, y + 30);

    // KPI Change
    const changeColor = kpi.trend === 'up' ? '#27AE60' : kpi.trend === 'down' ? '#E74C3C' : '#7F8C8D';
    doc.fontSize(12)
       .fillColor(changeColor)
       .text(kpi.change, x + 10, y + 70);
  });

  // Adjust Y position after KPI grid
  doc.y += Math.ceil(reportData.kpis.length / 2) * (kpiHeight + spacing) + spacing;

  // Detailed Sections
  reportData.sections.forEach(async (section) => {
    doc.addPage();

    // Section Title
    doc.fontSize(20)
       .fillColor('#2C3E50')
       .text(section.title, { underline: true })
       .moveDown();

    // Section Content
    doc.fontSize(11)
       .fillColor('#34495E')
       .text(section.content, { align: 'justify', lineGap: 4 });

    // Add chart if section has data
    if (section.chartData) {
      doc.moveDown();
      const chartImage = await generateChart(section.chartData);
      doc.image(chartImage, 100, doc.y, { width: 400 });
      doc.moveDown(15); // Space for chart
    }

    // Add table if section has table data
    if (section.tableData) {
      doc.moveDown();
      addTable(doc, section.tableData);
    }
  });

  // Recommendations
  doc.addPage();
  doc.fontSize(20)
     .fillColor('#2C3E50')
     .text('Recommendations', { underline: true })
     .moveDown();

  reportData.recommendations.forEach((rec, index) => {
    doc.fontSize(11)
       .fillColor('#34495E')
       .text(`${index + 1}. ${rec}`, { lineGap: 6 });
    doc.moveDown(0.5);
  });

  // Footer on all pages
  const pages = doc.bufferedPageRange();
  for (let i = 0; i < pages.count; i++) {
    doc.switchToPage(i);
    doc.fontSize(8)
       .fillColor('#95A5A6')
       .text(
         `${reportData.company} | Confidential | Page ${i + 1} of ${pages.count}`,
         50,
         doc.page.height - 50,
         { align: 'center', width: doc.page.width - 100 }
       );
  }

  doc.end();

  return new Promise((resolve, reject) => {
    stream.on('finish', () => resolve(outputPath));
    stream.on('error', reject);
  });
}

async function generateChart(chartData) {
  const chartCanvas = new ChartJS.ChartJSNodeCanvas({ width: 800, height: 400 });

  const configuration = {
    type: chartData.type || 'bar',
    data: {
      labels: chartData.labels,
      datasets: [{
        label: chartData.label,
        data: chartData.values,
        backgroundColor: chartData.colors || '#3498DB',
        borderColor: '#2C3E50',
        borderWidth: 1
      }]
    },
    options: {
      responsive: true,
      scales: {
        y: {
          beginAtZero: true
        }
      }
    }
  };

  return await chartCanvas.renderToBuffer(configuration);
}

function addTable(doc, tableData) {
  const tableTop = doc.y;
  const cellPadding = 8;
  const rowHeight = 25;
  const colWidth = (doc.page.width - 100) / tableData.headers.length;

  // Header row
  doc.fillColor('#34495E')
     .rect(50, tableTop, doc.page.width - 100, rowHeight)
     .fill();

  doc.fillColor('#FFFFFF')
     .fontSize(10);

  tableData.headers.forEach((header, i) => {
    doc.text(header, 50 + i * colWidth + cellPadding, tableTop + cellPadding, {
      width: colWidth - cellPadding * 2
    });
  });

  // Data rows
  doc.fillColor('#34495E');

  tableData.rows.forEach((row, rowIndex) => {
    const y = tableTop + (rowIndex + 1) * rowHeight;

    // Alternate row colors
    if (rowIndex % 2 === 1) {
      doc.fillColor('#F8F9FA')
         .rect(50, y, doc.page.width - 100, rowHeight)
         .fill();
    }

    doc.fillColor('#2C3E50');

    row.forEach((cell, cellIndex) => {
      doc.text(cell, 50 + cellIndex * colWidth + cellPadding, y + cellPadding, {
        width: colWidth - cellPadding * 2
      });
    });
  });

  doc.y = tableTop + (tableData.rows.length + 1) * rowHeight + 10;
}
ワークフロー 2:月次パフォーマンスレポート

目的: 指標、トレンド、比較を含む月次レポートを自動作成します

手順:

  1. 当月および過去の期間のデータを照会します
  2. 前月比および前年比の変化を計算します
  3. トレンドチャートを作成します
  4. 業績上位の項目と懸念事項を特定します
  5. 比較表を作成します
  6. データの傾向に基づく説明と知見を追加します
  7. レポートを整えて配布します

実装:

async function generateMonthlyReport(month, year, dataSource) {
  // Fetch data
  const currentData = await dataSource.getMonthData(month, year);
  const previousMonthData = await dataSource.getMonthData(month - 1, year);
  const lastYearData = await dataSource.getMonthData(month, year - 1);

  // Calculate metrics
  const metrics = {
    revenue: {
      current: currentData.revenue,
      momChange: calculatePercentChange(currentData.revenue, previousMonthData.revenue),
      yoyChange: calculatePercentChange(currentData.revenue, lastYearData.revenue)
    },
    customers: {
      current: currentData.customers,
      momChange: calculatePercentChange(currentData.customers, previousMonthData.customers),
      yoyChange: calculatePercentChange(currentData.customers, lastYearData.customers)
    },
    avgOrderValue: {
      current: currentData.revenue / currentData.orders,
      momChange: calculatePercentChange(
        currentData.revenue / currentData.orders,
        previousMonthData.revenue / previousMonthData.orders
      )
    }
  };

  // Generate report data
  const reportData = {
    title: 'Monthly Performance Report',
    subtitle: `${getMonthName(month)} ${year}`,
    period: `${getMonthName(month)} 1-${new Date(year, month, 0).getDate()}, ${year}`,
    company: 'Your Company',

    executiveSummary: generateExecutiveSummary(metrics, currentData),

    kpis: [
      {
        label: 'Total Revenue',
        value: formatCurrency(metrics.revenue.current),
        change: `${metrics.revenue.momChange > 0 ? '+' : ''}${metrics.revenue.momChange.toFixed(1)}% MoM`,
        trend: metrics.revenue.momChange > 0 ? 'up' : 'down'
      },
      {
        label: 'New Customers',
        value: metrics.customers.current.toLocaleString(),
        change: `${metrics.customers.yoyChange > 0 ? '+' : ''}${metrics.customers.yoyChange.toFixed(1)}% YoY`,
        trend: metrics.customers.yoyChange > 0 ? 'up' : 'down'
      },
      {
        label: 'Avg Order Value',
        value: formatCurrency(metrics.avgOrderValue.current),
        change: `${metrics.avgOrderValue.momChange > 0 ? '+' : ''}${metrics.avgOrderValue.momChange.toFixed(1)}% MoM`,
        trend: metrics.avgOrderValue.momChange > 0 ? 'up' : 'down'
      },
      {
        label: 'Customer Satisfaction',
        value: `${currentData.satisfaction}%`,
        change: 'Stable',
        trend: 'neutral'
      }
    ],

    sections: [
      {
        title: 'Revenue Analysis',
        content: `Revenue for ${getMonthName(month)} totaled ${formatCurrency(metrics.revenue.current)}, representing a ${Math.abs(metrics.revenue.momChange).toFixed(1)}% ${metrics.revenue.momChange > 0 ? 'increase' : 'decrease'} compared to last month...`,
        chartData: {
          type: 'line',
          labels: getLast6Months(month, year),
          values: await dataSource.getRevenueHistory(6),
          label: 'Revenue Trend'
        }
      },
      {
        title: 'Customer Acquisition',
        content: `Customer acquisition efforts resulted in ${metrics.customers.current} new customers this month...`,
        tableData: {
          headers: ['Channel', 'Customers', 'Cost', 'CAC'],
          rows: currentData.acquisitionChannels.map(channel => [
            channel.name,
            channel.customers.toString(),
            formatCurrency(channel.cost),
            formatCurrency(channel.cost / channel.customers)
          ])
        }
      },
      {
        title: 'Product Performance',
        content: 'Top-performing products and categories for the period...',
        chartData: {
          type: 'bar',
          labels: currentData.topProducts.map(p => p.name),
          values: currentData.topProducts.map(p => p.revenue),
          label: 'Revenue by Product'
        }
      }
    ],

    recommendations: generateRecommendations(metrics, currentData)
  };

  return await generateExecutiveReport(reportData, `./reports/monthly-${year}-${month}.pdf`);
}

function calculatePercentChange(current, previous) {
  if (previous === 0) return 0;
  return ((current - previous) / previous) * 100;
}

function formatCurrency(amount) {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD'
  }).format(amount);
}

function generateExecutiveSummary(metrics, data) {
  const revenueDirection = metrics.revenue.momChange > 0 ? 'increased' : 'decreased';
  const revenuePercent = Math.abs(metrics.revenue.momChange).toFixed(1);

  return `
    Revenue ${revenueDirection} by ${revenuePercent}% compared to the previous month,
    driven primarily by ${data.primaryGrowthDriver}. Customer acquisition
    ${metrics.customers.momChange > 0 ? 'exceeded' : 'fell short of'} targets, with
    ${metrics.customers.current} new customers. The average order value ${metrics.avgOrderValue.momChange > 0 ? 'improved' : 'declined'}
    by ${Math.abs(metrics.avgOrderValue.momChange).toFixed(1)}%, indicating
    ${metrics.avgOrderValue.momChange > 0 ? 'successful upselling efforts' : 'price pressure in the market'}.
  `.trim();
}

function generateRecommendations(metrics, data) {
  const recommendations = [];

  if (metrics.revenue.momChange < -5) {
    recommendations.push('Consider launching promotional campaigns to boost short-term revenue');
  }

  if (metrics.customers.momChange < 0) {
    recommendations.push('Increase marketing spend in top-performing acquisition channels');
  }

  if (metrics.avgOrderValue.momChange < 0) {
    recommendations.push('Implement cross-selling and bundling strategies to increase order values');
  }

  if (data.satisfaction < 80) {
    recommendations.push('Focus on improving customer satisfaction through enhanced support and service quality');
  }

  return recommendations;
}
ワークフロー 3:プロジェクト状況報告

目的: マイルストーン、タスク、リスクを含むプロジェクト進捗レポートを作成します

手順:

  1. プロジェクトデータ(タスク、マイルストーン、チームメンバー)を収集します
  2. 完了率とスケジュールの状況を計算します
  3. 完了済み、進行中、期限超過の項目を特定します
  4. リスクと課題を列挙します
  5. ガントチャートまたはタイムラインの可視化を作成します
  6. チームメンバーの貢献を追加します
  7. 次のステップとアクションアイテムを含めます
  8. 関係者に提出できる形式に整えます

実装:

async function generateProjectReport(projectData, outputPath) {
  const completionRate = calculateCompletionRate(projectData.tasks);
  const scheduleStatus = assessScheduleStatus(projectData);

  const reportData = {
    title: `Project Status Report: ${projectData.name}`,
    subtitle: `Status as of ${new Date().toLocaleDateString()}`,
    period: `${projectData.startDate} - ${projectData.endDate}`,
    company: projectData.organization,

    executiveSummary: `
      The ${projectData.name} project is currently ${completionRate}% complete.
      The project is ${scheduleStatus.status} schedule by ${scheduleStatus.variance} days.
      ${projectData.risks.length} risks have been identified and are being actively managed.
    `,

    kpis: [
      {
        label: 'Completion',
        value: `${completionRate}%`,
        change: `${projectData.tasksCompletedThisWeek} tasks completed this week`,
        trend: completionRate > 50 ? 'up' : 'neutral'
      },
      {
        label: 'Schedule Status',
        value: scheduleStatus.status,
        change: `${Math.abs(scheduleStatus.variance)} days ${scheduleStatus.variance > 0 ? 'ahead' : 'behind'}`,
        trend: scheduleStatus.variance >= 0 ? 'up' : 'down'
      },
      {
        label: 'Budget',
        value: formatCurrency(projectData.spentBudget),
        change: `${((projectData.spentBudget / projectData.totalBudget) * 100).toFixed(1)}% of total`,
        trend: projectData.spentBudget <= projectData.totalBudget ? 'up' : 'down'
      },
      {
        label: 'Team Health',
        value: projectData.teamMorale,
        change: 'Based on last survey',
        trend: 'neutral'
      }
    ],

    sections: [
      {
        title: 'Milestones',
        content: 'Current status of project milestones:',
        tableData: {
          headers: ['Milestone', 'Due Date', 'Status', 'Completion'],
          rows: projectData.milestones.map(m => [
            m.name,
            m.dueDate,
            m.status,
            `${m.completion}%`
          ])
        }
      },
      {
        title: 'Completed This Period',
        content: projectData.completedTasks.map(t => `• ${t.name}`).join('\n')
      },
      {
        title: 'In Progress',
        content: projectData.inProgressTasks.map(t => `• ${t.name} (${t.assignee})`).join('\n')
      },
      {
        title: 'Risks and Issues',
        content: projectData.risks.length > 0
          ? projectData.risks.map((r, i) => `${i + 1}. ${r.description} (Impact: ${r.impact}, Mitigation: ${r.mitigation})`).join('\n\n')
          : 'No significant risks identified at this time.'
      }
    ],

    recommendations: [
      ...projectData.nextSteps.map(step => `Complete: ${step}`),
      ...projectData.risks.filter(r => r.impact === 'High').map(r => `Address high-impact risk: ${r.description}`)
    ]
  };

  return await generateExecutiveReport(reportData, outputPath);
}

function calculateCompletionRate(tasks) {
  const total = tasks.length;
  const completed = tasks.filter(t => t.status === 'completed').length;
  return Math.round((completed / total) * 100);
}

function assessScheduleStatus(projectData) {
  const today = new Date();
  const expectedCompletion = new Date(projectData.expectedCompletionDate);
  const originalEnd = new Date(projectData.endDate);

  const daysVariance = Math.round((expectedCompletion - originalEnd) / (1000 * 60 * 60 * 24));

  return {
    status: daysVariance <= 0 ? 'On' : 'Behind',
    variance: daysVariance
  };
}
ワークフロー 4:財務レポートの作成

目的: 財務諸表と分析レポートを作成します

手順:

  1. 会計システムから財務データを抽出します
  2. 財務比率と指標を計算します
  3. 貸借対照表、損益計算書、キャッシュフロー計算書を作成します
  4. トレンド分析と比較を行います
  5. 売上、費用、利益率のチャートを追加します
  6. 差異に関する注記と説明を含めます
  7. 会計基準に従って書式を整えます
  8. 適切な書式でPDFにエクスポートします
ワークフロー 5:レポートの自動スケジュール設定

目的: 定期レポートをスケジュールし、自動作成します

手順:

  1. レポートのスケジュール(日次、週次、月次)を定義します
  2. データソースへの接続を設定します
  3. レポートのテンプレートを作成します
  4. レポート作成ジョブをスケジュールします
  5. 指定した間隔で自動作成します
  6. メールまたはファイルストレージを通じて配布します
  7. 作成したレポートをアーカイブします
  8. 完了時に通知を送信します

実装:

const cron = require('node-cron');
const nodemailer = require('nodemailer');

class ReportScheduler {
  constructor() {
    this.schedules = [];
  }

  scheduleReport(config) {
    const job = cron.schedule(config.cronExpression, async () => {
      try {
        console.log(`Generating ${config.reportType} report...`);

        // Generate report
        const reportPath = await this.generateReport(config);

        // Send report
        if (config.emailRecipients) {
          await this.emailReport(reportPath, config.emailRecipients, config);
        }

        // Archive
        if (config.archivePath) {
          await this.archiveReport(reportPath, config.archivePath);
        }

        console.log(`Report generated successfully: ${reportPath}`);
      } catch (error) {
        console.error(`Report generation failed: ${error.message}`);
        this.notifyFailure(config, error);
      }
    });

    this.schedules.push({ config, job });
  }

  async generateReport(config) {
    switch (config.reportType) {
      case 'monthly':
        return await generateMonthlyReport(new Date().getMonth(), new Date().getFullYear(), config.dataSource);
      case 'project':
        return await generateProjectReport(await config.dataSource.getProjectData(), config.outputPath);
      default:
        throw new Error(`Unknown report type: ${config.reportType}`);
    }
  }

  async emailReport(reportPath, recipients, config) {
    const transporter = nodemailer.createTransporter(config.emailConfig);

    await transporter.sendMail({
      from: config.emailConfig.from,
      to: recipients.join(', '),
      subject: `${config.reportName} - ${new Date().toLocaleDateString()}`,
      html: `
        <p>Please find attached the automated ${config.reportName}.</p>
        <p>This report was generated on ${new Date().toLocaleString()}.</p>
      `,
      attachments: [
        {
          filename: `${config.reportName}.pdf`,
          path: reportPath
        }
      ]
    });
  }

  async archiveReport(reportPath, archivePath) {
    const fs = require('fs').promises;
    const path = require('path');

    const timestamp = new Date().toISOString().replace(/:/g, '-');
    const archiveFilename = `report-${timestamp}.pdf`;

    await fs.copyFile(reportPath, path.join(archivePath, archiveFilename));
  }
}

// Usage:
const scheduler = new ReportScheduler();

scheduler.scheduleReport({
  reportType: 'monthly',
  reportName: 'Monthly Performance Report',
  cronExpression: '0 9 1 * *', // 9 AM on 1st of each month
  dataSource: myDataSource,
  outputPath: './reports/monthly-latest.pdf',
  emailRecipients: ['[email protected]', '[email protected]'],
  emailConfig: { /* nodemailer config */ },
  archivePath: './reports/archive'
});

クイックリファレンス

アクションコマンド/トリガー
レポートを作成「[期間]の[レポートの種類]を作成」
エグゼクティブレポート「エグゼクティブサマリーを作成」
月次レポート「月次パフォーマンスレポートを作成」
プロジェクト状況「プロジェクト状況報告を作成」
チャートを追加「レポートに[チャートの種類]を含める」
レポートをスケジュール「[レポート]の作成を自動化」
カスタムレポート「[セクション]を含むカスタムレポートを作成」

ベストプラクティス

  • データの正確性: レポートを生成する前に、すべてのデータソースを検証します
  • 一貫した形式: 繰り返し使用するレポートの種類にはテンプレートを使用します
  • エグゼクティブサマリー: 忙しい経営幹部向けに、必ず重要なポイントから始めます
  • ビジュアルを優先: トレンドをすばやく伝えるために、チャートやグラフを使用します
  • 実行可能なインサイト: データだけでなく、具体的な提案を含めます
  • バージョン管理: レポートのバージョンと変更を記録します
  • 配布: 対象読者を把握し、それに応じて内容の詳しさを調整します
  • 適時性: 一貫したスケジュールでレポートを生成します
  • コンテキスト: 比較情報を提示します(月次、前年比、目標比)
  • プロフェッショナルなレイアウト: ブランドと書式を一貫して保ちます
  • アクセシビリティ: すべてのデバイスでレポートを読みやすくします
  • アーカイブ: トレンド分析のために過去のレポートを保管します

よくあるパターン

四半期ビジネスレビュー:

const qbrData = {
  title: 'Q4 2025 Business Review',
  sections: ['Financial Performance', 'Operational Metrics', 'Market Position', 'Strategic Initiatives'],
  compareAgainst: ['Q3 2025', 'Q4 2024'],
  includeForecast: true,
  forecastPeriod: 'Q1 2026'
};

営業ダッシュボード:

const salesReport = {
  kpis: ['Revenue', 'Deals Closed', 'Pipeline Value', 'Conversion Rate'],
  breakdowns: ['by Region', 'by Product', 'by Sales Rep'],
  trends: ['Last 12 months', 'Year over Year'],
  targets: true
};

依存関係

必要なパッケージをインストールします:

npm install pdfkit chartjs-node-canvas
npm install node-cron nodemailer  # For scheduling and email
npm install exceljs                # For Excel export option
npm install handlebars             # For templating

エラー処理

  • データの欠落: null/undefined 値を適切に処理します
  • データソースの障害: API/データベースクエリに再試行ロジックを適用します
  • チャート生成: チャート生成に失敗した場合は、表に切り替えます
  • ファイルシステム: ファイルを書き込む前にアクセス権を確認します
  • メール送信の失敗: エラーを記録し、送信を再試行します
  • スケジュールの競合: レポート生成が重複しないようにします

パフォーマンスのヒント

  • チャート画像をキャッシュして再利用します
  • 大規模なデータセットにはストリーミングを使用します
  • 可能であればチャートを並列生成します
  • 埋め込む前に画像を圧縮します
  • 非常に大規模なレポートにはページネーションを使用します
  • パフォーマンス向上のためにデータを事前集計します

高度な機能

インタラクティブな HTML レポート:

const html = `
  <html>
    <head>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    </head>
    <body>
      <h1>${reportData.title}</h1>
      <canvas id="chart"></canvas>
      <script>
        // Interactive Chart.js visualization
      </script>
    </body>
  </html>
`;

複数形式へのエクスポート:

async function exportReport(reportData, formats = ['pdf', 'html', 'xlsx']) {
  const outputs = {};
  if (formats.includes('pdf')) outputs.pdf = await generatePDF(reportData);
  if (formats.includes('html')) outputs.html = await generateHTML(reportData);
  if (formats.includes('xlsx')) outputs.xlsx = await generateExcel(reportData);
  return outputs;
}

コメント