229 lines
7.1 KiB
Python
229 lines
7.1 KiB
Python
"""
|
|
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")
|