# Placeholder for report generation logic import json import logging from fpdf import FPDF from datetime import datetime import io # To output PDF to bytes # Configure logging logging.basicConfig(level=logging.INFO) # --- JSON Report --- # def generate_json_report(analysis_data): """Generates a JSON string from the analysis data.""" try: return json.dumps(analysis_data, indent=2, ensure_ascii=False) except TypeError as e: logging.error(f"Error serializing analysis data to JSON: {e}") # Return an error JSON instead of raising exception return json.dumps({"error": "Failed to serialize analysis data to JSON", "details": str(e)}, indent=2) # --- PDF Report --- # class PDFReport(FPDF): """Custom PDF class to handle header/footer if needed later.""" def header(self): self.set_font('Helvetica', 'B', 14) self.cell(0, 10, 'Google Discover Analysis Report', 0, 1, 'C') self.ln(5) def footer(self): self.set_y(-15) self.set_font('Helvetica', 'I', 8) self.cell(0, 10, f'Page {self.page_no()} - Generated on {datetime.now().strftime("%Y-%m-%d %H:%M")}', 0, 0, 'C') def chapter_title(self, title): self.set_font('Helvetica', 'B', 12) self.set_fill_color(230, 230, 230) # Light grey background self.cell(0, 8, title, 0, 1, 'L', fill=True) self.ln(4) def chapter_body(self, content): self.set_font('Times', '', 11) self.multi_cell(0, 5, content) self.ln() def write_key_value(self, key, value, level=0): indent = " " * level key_str = f"{indent}{key.replace('_', ' ').title()}:" if isinstance(value, dict): self.set_font('Helvetica', 'B', 10 + max(0, 1-level)) self.multi_cell(0, 5, key_str) self.ln(1) for sub_key, sub_value in value.items(): self.write_key_value(sub_key, sub_value, level + 1) elif isinstance(value, list): self.set_font('Helvetica', 'B', 10 + max(0, 1-level)) self.multi_cell(0, 5, key_str) self.ln(1) for i, item in enumerate(value): # Special handling for recommendations list formatting if key == 'optimization_recommendations' and isinstance(item, dict): self.set_font('Helvetica', 'B', 10) self.multi_cell(0, 5, f"{indent} Recommendation {i+1}:") for rec_key, rec_value in item.items(): self.write_key_value(rec_key, rec_value, level + 2) elif isinstance(item, dict): self.write_key_value(f"Item {i+1}", item, level + 1) else: self.set_font('Times', '', 10) self.multi_cell(0, 5, f"{indent} - {str(item)}") self.ln(1) else: # Write simple key-value pair self.set_font('Helvetica', 'B', 10 + max(0, 1-level)) # Use multi_cell for key to handle potential wrapping key_width = self.get_string_width(key_str + ' ') + 1 # Estimate width self.multi_cell(key_width, 5, key_str, ln=3) # ln=3 means move next cell to right self.set_font('Times', '', 10) try: value_str = str(value) if value is not None else "N/A" self.multi_cell(0, 5, value_str, ln=1) # ln=1 means move to start of next line except Exception as e: logging.warning(f"Could not convert value to string for PDF: {value} ({type(value)}). Error: {e}") self.multi_cell(0, 5, "[Error converting value]", ln=1) self.ln(1) # Add a little space def generate_pdf_report(analysis_data): """Generates a PDF report from the analysis data and returns it as bytes.""" pdf = PDFReport() pdf.add_page() pdf.set_auto_page_break(auto=True, margin=15) if not isinstance(analysis_data, dict): logging.error("Cannot generate PDF: analysis_data is not a dictionary.") pdf.set_font("Helvetica", "B", 12) pdf.set_text_color(255, 0, 0) # Red pdf.cell(0, 10, "Error: Invalid analysis data format.", 0, 1, 'C') return pdf.output(dest='S').encode('latin-1') # Return minimal error PDF # Iterate through the main sections of the analysis data for section_key, section_data in analysis_data.items(): title = section_key.replace('_', ' ').title() pdf.chapter_title(title) if isinstance(section_data, dict): for key, value in section_data.items(): pdf.write_key_value(key, value, level=0) elif isinstance(section_data, list): # Handle top-level lists like recommendations if structured that way for i, item in enumerate(section_data): if section_key == 'optimization_recommendations' and isinstance(item, dict): pdf.set_font('Helvetica', 'B', 10) pdf.multi_cell(0, 5, f"Recommendation {i+1}:", ln=1) for rec_key, rec_value in item.items(): pdf.write_key_value(rec_key, rec_value, level=1) elif isinstance(item, dict): pdf.write_key_value(f"Item {i+1}", item, level=0) else: pdf.set_font('Times', '', 10) pdf.multi_cell(0, 5, f"- {str(item)}", ln=1) elif section_key != 'input_type': # Don't just print the input type as plain text pdf.chapter_body(str(section_data) if section_data is not None else "N/A") pdf.ln(5) # Add space between sections try: pdf_bytes = pdf.output(dest='S') # FPDF outputs latin-1 encoded bytes string in Python 3 # No further encoding needed typically for sending as binary data return pdf_bytes except Exception as e: logging.exception("Error generating PDF output.") # Fallback: create a simple error PDF pdf = FPDF() pdf.add_page() pdf.set_font("Helvetica", "B", 12) pdf.set_text_color(255, 0, 0) # Red pdf.cell(0, 10, f"Error generating PDF: {e}", 0, 1, 'C') try: return pdf.output(dest='S') except: return b"PDF Generation Failed" # Ultimate fallback