""" Report generation utility Combines multiple charts and data into a PDF report Usage: from report_generator import generate_pdf_report # Generate PDF report generate_pdf_report( charts=['chart1.png', 'chart2.png'], title='Sales Analysis Report', summary_data={'Total Revenue': 1000000} ) """ from pathlib import Path from datetime import datetime from config import COMPANY_NAME, OUTPUT_DIR, REPORTS_DIR, ensure_directories def generate_pdf_report( charts, title=None, summary_data=None, output_filename=None, output_dir=None ): """ Generate PDF report from charts and summary data Args: charts: List of chart file paths (PNG files) title: Report title (defaults to company name + date) summary_data: Dictionary of summary metrics output_filename: Output PDF filename (defaults to report_YYYYMMDD_HHMMSS.pdf) output_dir: Output directory (defaults to config.REPORTS_DIR) Returns: Path: Path to generated PDF file Raises: ImportError: If reportlab is not installed """ try: from reportlab.lib.pagesizes import letter, A4 from reportlab.lib.units import inch from reportlab.lib import colors from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle, PageBreak from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_CENTER, TA_LEFT except ImportError: raise ImportError( "reportlab is required for PDF generation. Install with: pip install reportlab" ) if output_dir is None: output_dir = REPORTS_DIR else: output_dir = Path(output_dir) ensure_directories() output_dir.mkdir(exist_ok=True) # Default filename if output_filename is None: timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') output_filename = f"report_{timestamp}.pdf" output_path = output_dir / output_filename # Create PDF document doc = SimpleDocTemplate( str(output_path), pagesize=letter, rightMargin=0.75*inch, leftMargin=0.75*inch, topMargin=0.75*inch, bottomMargin=0.75*inch ) # Container for PDF elements story = [] # Styles styles = getSampleStyleSheet() title_style = ParagraphStyle( 'CustomTitle', parent=styles['Heading1'], fontSize=20, textColor=colors.HexColor('#2E86AB'), spaceAfter=30, alignment=TA_CENTER ) heading_style = ParagraphStyle( 'CustomHeading', parent=styles['Heading2'], fontSize=14, textColor=colors.HexColor('#2E86AB'), spaceAfter=12 ) # Title if title is None: title = f"{COMPANY_NAME} Sales Analysis Report" story.append(Paragraph(title, title_style)) story.append(Spacer(1, 0.2*inch)) # Report metadata metadata_text = f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" story.append(Paragraph(metadata_text, styles['Normal'])) story.append(Spacer(1, 0.3*inch)) # Summary data table if summary_data: story.append(Paragraph("Summary", heading_style)) # Create table table_data = [['Metric', 'Value']] for key, value in summary_data.items(): # Format value if isinstance(value, (int, float)): if abs(value) >= 1e6: formatted_value = f"${value / 1e6:.2f}m" elif abs(value) >= 1e3: formatted_value = f"${value / 1e3:.2f}k" else: formatted_value = f"${value:.2f}" else: formatted_value = str(value) table_data.append([key, formatted_value]) table = Table(table_data, colWidths=[3*inch, 2*inch]) table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2E86AB')), ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), ('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 12), ('BOTTOMPADDING', (0, 0), (-1, 0), 12), ('BACKGROUND', (0, 1), (-1, -1), colors.beige), ('GRID', (0, 0), (-1, -1), 1, colors.black), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.lightgrey]) ])) story.append(table) story.append(Spacer(1, 0.3*inch)) # Add charts if charts: story.append(Paragraph("Charts", heading_style)) for i, chart_path in enumerate(charts, 1): chart_path = Path(chart_path) if not chart_path.exists(): print(f"Warning: Chart not found: {chart_path}") continue # Add chart title chart_title = f"Chart {i}: {chart_path.stem.replace('_', ' ').title()}" story.append(Paragraph(chart_title, styles['Heading3'])) story.append(Spacer(1, 0.1*inch)) # Add image try: img = Image(str(chart_path), width=6*inch, height=4*inch) story.append(img) except Exception as e: error_msg = f"Error loading chart: {e}" story.append(Paragraph(error_msg, styles['Normal'])) # Add page break between charts (except last one) if i < len(charts): story.append(PageBreak()) # Build PDF doc.build(story) print(f"PDF report generated: {output_path}") return output_path def generate_simple_report(charts, title=None, output_filename=None): """ Generate a simple PDF report (wrapper with defaults) Args: charts: List of chart file paths title: Report title output_filename: Output filename Returns: Path: Path to generated PDF """ return generate_pdf_report( charts=charts, title=title, output_filename=output_filename ) # ============================================================================ # EXAMPLE USAGE # ============================================================================ if __name__ == "__main__": """Example usage""" from config import OUTPUT_DIR # Find charts in output directory chart_files = list(OUTPUT_DIR.glob('*.png')) if chart_files: print(f"Found {len(chart_files)} charts") # Generate report report_path = generate_pdf_report( charts=[str(f) for f in chart_files[:5]], # Limit to 5 charts title="Sales Analysis Report", summary_data={ 'Total Charts': len(chart_files), 'Report Date': datetime.now().strftime('%Y-%m-%d') } ) print(f"Report saved to: {report_path}") else: print("No charts found in output directory")