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:
Executive Summary
High-level overview of the assessment with overall risk score and level
Overall Risk Profile
Aggregate score (0-100) and traffic-light level (LOW/MODERATE/HIGH/CRITICAL)
Category Breakdown
Detailed analysis for all 7 risk categories with subcategory scores
Radar Chart Data
Visualization-ready data for multi-dimensional risk radar chart
Evidence & Narratives
AI-generated risk narratives with supporting evidence from documents
Recommendations
Prioritized action items (HIGH/MEDIUM/LOW) for each risk category
Generating a Report
1. Retrieve Report Data
First, fetch the complete report structure:
GET /api/assessments/:id/report
Response (ReportResponse)
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:
POST /api/assessments/:id/report/pdf
Response
3. Poll Job Status
Monitor PDF generation progress:
GET /api/jobs/:jobId
Response (PROCESSING)
Response (COMPLETED)
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:
🟢 LOW (0-24)
🟡 MODERATE (25-49)
🟠HIGH (50-74)
🔴 CRITICAL (75-100)
Visual Representation:
Background: Light green (#F0FDF4)
Text: Dark green (#166534)
Icon: 🟢 Green circle
Interpretation: Minimal risk. Standard monitoring sufficient.Visual Representation:
Background: Light yellow (#FEFCE8)
Text: Dark yellow (#854D0E)
Icon: 🟡 Yellow circle
Interpretation: Acceptable risk. Enhanced oversight recommended.Visual Representation:
Background: Light orange (#FFF7ED)
Text: Dark orange (#9A3412)
Icon: 🟠Orange circle
Interpretation: Significant risk. Active mitigation required.Visual Representation:
Background: Light red (#FEF2F2)
Text: Dark red (#991B1B)
Icon: 🔴 Red circle
Interpretation: Unacceptable risk. Immediate intervention required.
Radar Chart Visualization
The radarData array enables multi-dimensional risk visualization:
React Component (Recharts)
Data Structure
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:
Validate Assessment
Ensure assessment is in COMPLETE status with risk scores available
Fetch Report Data
Retrieve complete report response from ReportService.getReport()
Generate PDF
Use PdfService.generate() to create PDF buffer from report data Current Implementation: Simple text-based stubFuture Enhancement: Use Puppeteer or PDFKit for rich formatting
Upload to S3
Store PDF in S3 at key: assessments/{assessmentId}/reports/report-{timestamp}.pdf
Generate Presigned URL
Create temporary download URL (1-hour expiration)
Update Assessment
Set progress to 100%
ReportGenerationHandler.execute
S3 Key Convention
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
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
Related Resources
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