#!/usr/bin/env python3 """ PDF Report Generator Generates professional PDF reports from UN motion analysis results. Supports multiple report types: - Vote analysis reports - Bilateral impact analysis reports - Custom markdown to PDF conversion Usage: python scripts/generate_pdf_report.py [--output output.pdf] Example: python scripts/generate_pdf_report.py analysis/01_gaza_ceasefire_resolution_analysis_REVISED.md """ import argparse import json import sys from datetime import datetime from pathlib import Path from typing import Optional # Add project root to path PROJECT_ROOT = Path(__file__).parent.parent sys.path.insert(0, str(PROJECT_ROOT)) def check_dependencies(): """Check if required dependencies are installed""" try: import markdown import weasyprint return True except ImportError as e: print(f"Missing dependencies: {e}") print("\nInstall with:") print(" pip install markdown weasyprint") return False def markdown_to_pdf(markdown_file: Path, output_pdf: Optional[Path] = None): """ Convert markdown file to PDF with professional styling Args: markdown_file: Path to markdown file output_pdf: Optional output PDF path (default: same name as markdown with .pdf) """ import markdown from weasyprint import HTML, CSS # Read markdown with open(markdown_file, 'r', encoding='utf-8') as f: md_content = f.read() # Convert markdown to HTML md = markdown.Markdown(extensions=[ 'extra', 'codehilite', 'toc', 'tables', 'fenced_code' ]) html_content = md.convert(md_content) # Generate styled HTML document styled_html = f""" {markdown_file.stem} {html_content}

Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}

""" # Determine output path if output_pdf is None: output_pdf = markdown_file.parent / "pdf" / f"{markdown_file.stem}.pdf" # Create output directory output_pdf.parent.mkdir(parents=True, exist_ok=True) # Convert HTML to PDF print(f"Generating PDF: {output_pdf}") HTML(string=styled_html).write_pdf(output_pdf) print(f"āœ“ PDF generated successfully") return output_pdf def generate_bilateral_impact_pdf(json_file: Path, output_pdf: Optional[Path] = None): """ Generate PDF report from bilateral impact analysis JSON Args: json_file: Path to JSON analysis results output_pdf: Optional output PDF path """ # Load JSON data with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) # Generate markdown report md_content = f"""# Israel Bilateral Relationship Impact Analysis **Motion:** {data['motion_id']} **Analysis Date:** {data['timestamp']} **Model:** {data['model']} **Countries Analyzed:** {data['total_analyzed']} --- ## Executive Summary This report analyzes how the Gaza ceasefire resolution vote affects Israel's bilateral relationships with {data['total_analyzed']} UN member states. ### Impact Distribution """ # Add impact summary table md_content += "| Impact Category | Count | Percentage |\n" md_content += "|----------------|-------|------------|\n" total = data['total_analyzed'] for category, count in data['impact_summary'].items(): pct = (count / total * 100) if total > 0 else 0 md_content += f"| {category.replace('_', ' ').title()} | {count} | {pct:.1f}% |\n" md_content += "\n---\n\n" # Group analyses by impact category by_category = {} for analysis in data['analyses']: category = analysis['impact_analysis']['impact_category'] if category not in by_category: by_category[category] = [] by_category[category].append(analysis) # Add detailed analyses by category category_order = [ 'strengthened_significantly', 'strengthened_moderately', 'strengthened_slightly', 'neutral', 'strained_slightly', 'strained_moderately', 'strained_significantly' ] for category in category_order: if category not in by_category or not by_category[category]: continue md_content += f"## {category.replace('_', ' ').title()}\n\n" for analysis in by_category[category]: md_content += f"### {analysis['country']}\n\n" md_content += f"**Vote:** {analysis['vote'].upper()} \n" md_content += f"**Confidence:** {analysis['impact_analysis']['confidence']} \n\n" md_content += f"**Analysis:** \n" md_content += f"{analysis['impact_analysis']['reasoning']}\n\n" md_content += f"**Key Factors:**\n" for factor in analysis['impact_analysis']['key_factors']: md_content += f"- {factor}\n" md_content += f"\n**Country Statement:** \n" md_content += f"> {analysis['statement']}\n\n" md_content += "---\n\n" # Save markdown temporarily temp_md = json_file.parent / f"{json_file.stem}_report.md" with open(temp_md, 'w', encoding='utf-8') as f: f.write(md_content) # Convert to PDF pdf_path = markdown_to_pdf(temp_md, output_pdf) # Clean up temp markdown temp_md.unlink() return pdf_path def main(): parser = argparse.ArgumentParser( description="Generate professional PDF reports from analysis results", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Convert markdown to PDF python scripts/generate_pdf_report.py analysis/report.md # Convert JSON bilateral impact to PDF python scripts/generate_pdf_report.py analysis/01_gaza_ceasefire_resolution_israel_bilateral_impact_latest.json # Specify output path python scripts/generate_pdf_report.py analysis/report.md --output custom_report.pdf """ ) parser.add_argument( "input_file", type=Path, help="Input file (.md or .json)" ) parser.add_argument( "--output", type=Path, help="Output PDF file (optional)" ) args = parser.parse_args() # Check dependencies if not check_dependencies(): sys.exit(1) # Validate input file if not args.input_file.exists(): print(f"Error: Input file not found: {args.input_file}") sys.exit(1) try: # Determine file type and process accordingly if args.input_file.suffix == '.json': print("Processing bilateral impact JSON...") pdf_path = generate_bilateral_impact_pdf(args.input_file, args.output) elif args.input_file.suffix == '.md': print("Processing markdown file...") pdf_path = markdown_to_pdf(args.input_file, args.output) else: print(f"Error: Unsupported file type: {args.input_file.suffix}") print("Supported types: .md, .json") sys.exit(1) print(f"\nāœ“ PDF report generated: {pdf_path}") print(f" Size: {pdf_path.stat().st_size / 1024:.1f} KB") except Exception as e: print(f"\nāŒ Error generating PDF: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()