Skip to main content

Overview

The CGIAR Risk Intelligence Tool generates professional PDF reports that synthesize risk analysis results into executive-ready documents. Reports include traffic-light risk indicators, visualization-ready data, and prioritized recommendations.

Report Structure

Every report contains:
1

Executive Summary

High-level overview of the assessment with overall risk score and level
2

Overall Risk Profile

Aggregate score (0-100) and traffic-light level (LOW/MODERATE/HIGH/CRITICAL)
3

Category Breakdown

Detailed analysis for all 7 risk categories with subcategory scores
4

Radar Chart Data

Visualization-ready data for multi-dimensional risk radar chart
5

Evidence & Narratives

AI-generated risk narratives with supporting evidence from documents
6

Recommendations

Prioritized action items (HIGH/MEDIUM/LOW) for each risk category

Generating a Report

1. Retrieve Report Data

First, fetch the complete report structure:
Reports are only available for assessments with status COMPLETE. Attempting to generate a report for an incomplete assessment will return a 400 error.

2. Generate PDF

Trigger PDF generation as a background job:

3. Poll Job Status

Monitor PDF generation progress:

4. Download PDF

Use the presigned URL from the job result:
const { result } = job;
window.location.href = result.downloadUrl;
Presigned URLs are valid for 1 hour. If the URL expires, regenerate the PDF or request a new presigned URL.

Traffic-Light Indicators

Reports use color-coded risk levels for quick visual assessment:
Visual Representation:
  • Background: Light green (#F0FDF4)
  • Text: Dark green (#166534)
  • Icon: 🟢 Green circle
Interpretation: Minimal risk. Standard monitoring sufficient.

Radar Chart Visualization

The radarData array enables multi-dimensional risk visualization:
import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis } from 'recharts';
import type { ReportResponse } from '@alliance-risk/shared';

export function RiskRadarChart({ report }: { report: ReportResponse }) {
  return (
    <RadarChart
      width={600}
      height={600}
      data={report.radarData}
      margin={{ top: 20, right: 30, bottom: 20, left: 30 }}
    >
      <PolarGrid stroke="#e5e7eb" />
      <PolarAngleAxis
        dataKey="category"
        tick={{ fill: '#6b7280', fontSize: 12 }}
      />
      <PolarRadiusAxis
        domain={[0, 100]}
        tick={{ fill: '#6b7280' }}
        tickCount={5}
      />
      <Radar
        name="Risk Score"
        dataKey="score"
        stroke="#4f46e5"
        fill="#4f46e5"
        fillOpacity={0.5}
      />
    </RadarChart>
  );
}

Report Sections

Executive Summary

AI-generated high-level summary:
executiveSummary: "Risk assessment for Green Valley Agritech has been completed with an overall risk score of 42. The assessment identifies moderate risks in financial and operational areas, with particular attention needed for revenue stability and supply chain resilience."
The executive summary is dynamically generated based on the overall risk level and highest-scoring categories.

Category Details

Each category includes:
Array of 5 subcategories with individual scores, evidence, and mitigation strategies:
{
  "name": "Revenue Stability",
  "indicator": "Year-over-year revenue variance",
  "score": 42,
  "level": "MODERATE",
  "evidence": "Revenue declined 8% in FY2025 but stabilized in Q4",
  "mitigation": "Diversify customer base and develop new revenue streams"
}
Consolidated evidence from document parsing and data analysis:
evidence: "Financial statements show moderate volatility with adequate liquidity. Operating margin below industry average."
AI-generated contextual explanation:
narrative: "The Financial risk level is MODERATE based on analysis of revenue trends, profitability metrics, debt levels, cash flow patterns, and financial sustainability indicators."
Prioritized action items with HIGH/MEDIUM/LOW urgency:
recommendations: [
  {
    "text": "Develop a 3-year financial sustainability plan...",
    "priority": "HIGH",
    "isEdited": false,
    "editedText": null
  }
]

PDF Generation Process

The report generation handler orchestrates PDF creation:
1

Validate Assessment

Ensure assessment is in COMPLETE status with risk scores available
2

Fetch Report Data

Retrieve complete report response from ReportService.getReport()
3

Generate PDF

Use PdfService.generate() to create PDF buffer from report dataCurrent Implementation: Simple text-based stubFuture Enhancement: Use Puppeteer or PDFKit for rich formatting
4

Upload to S3

Store PDF in S3 at key: assessments/{assessmentId}/reports/report-{timestamp}.pdf
5

Generate Presigned URL

Create temporary download URL (1-hour expiration)
6

Update Assessment

Set progress to 100%
async execute(input: ReportGenerationInput): Promise<ReportGenerationResult> {
  const assessment = await this.prisma.assessment.findUnique({
    where: { id: input.assessmentId },
  });

  if (!assessment) {
    throw new Error(`Assessment ${input.assessmentId} not found`);
  }

  const reportId = `report-${Date.now()}`;
  const pdfKey = this.storageService.buildReportKey(
    input.assessmentId,
    reportId
  );

  // In production: Generate actual PDF with PdfService
  // const pdfBuffer = await this.pdfService.generate(reportData);
  // await this.storageService.upload(pdfKey, pdfBuffer);

  const downloadUrl = await this.storageService.generatePresignedDownloadUrl(pdfKey);

  await this.prisma.assessment.update({
    where: { id: input.assessmentId },
    data: { progress: 100 },
  });

  return { assessmentId: input.assessmentId, pdfKey, downloadUrl };
}

React Hook: Report Generation

hooks/use-report.ts
import { useState } from 'react';
import { useJob } from './use-job';
import { apiClient } from '@/lib/api-client';

export function useReport(assessmentId: string) {
  const [jobId, setJobId] = useState<string | null>(null);
  const { job, isLoading } = useJob(jobId, { pollInterval: 3000 });

  const generatePdf = async () => {
    const response = await apiClient.post(
      `/api/assessments/${assessmentId}/report/pdf`
    );
    setJobId(response.jobId);
  };

  const downloadUrl = job?.status === 'COMPLETED' 
    ? job.result?.downloadUrl 
    : null;

  return {
    generatePdf,
    isGenerating: isLoading,
    downloadUrl,
    error: job?.status === 'FAILED' ? job.error : null,
  };
}

Code Example: Complete Report Flow

import { useReport } from '@/hooks/use-report';
import { Button } from '@/components/ui/button';

function ReportActions({ assessmentId }: { assessmentId: string }) {
  const { generatePdf, isGenerating, downloadUrl, error } = useReport(assessmentId);

  return (
    <div className="space-y-4">
      <Button
        onClick={generatePdf}
        disabled={isGenerating}
      >
        {isGenerating ? 'Generating PDF...' : 'Generate PDF Report'}
      </Button>

      {downloadUrl && (
        <Button
          variant="outline"
          onClick={() => window.location.href = downloadUrl}
        >
          Download Report
        </Button>
      )}

      {error && (
        <div className="text-red-600">
          Failed to generate report: {error}
        </div>
      )}
    </div>
  );
}

Future Enhancements

Rich PDF Formatting

Replace text-based stub with Puppeteer or PDFKit for professional layouts, charts, and branding

Custom Templates

Allow organizations to customize report templates with logos, colors, and section ordering

Multi-Language Support

Generate reports in multiple languages based on assessment country or user preference

Report Versioning

Track report versions and allow regeneration with updated scores after data corrections

Best Practices

Validate Completion

Always check assessment status is COMPLETE before generating reports

Cache Report Data

Cache the report response to avoid re-fetching when generating multiple PDFs

Handle Expired URLs

Presigned URLs expire after 1 hour. Regenerate if user returns later to download

Monitor Job Failures

Implement error tracking for failed PDF generation jobs to identify systemic issues

Risk Scoring

Understand the scoring methodology behind report data

AI Analysis

Learn how AI agents generate narratives and recommendations

Assessment Workflow

See the complete assessment lifecycle from creation to report

Build docs developers (and LLMs) love