""" Report generation service. Generates PDF and JSON reports from inspection results. """ import json import logging from datetime import datetime from pathlib import Path from typing import Optional from urllib.parse import urlparse from jinja2 import Environment, FileSystemLoader from slugify import slugify logger = logging.getLogger(__name__) TEMPLATES_DIR = Path(__file__).parent.parent / "templates" # Grade color mapping GRADE_COLORS = { "A+": "#22C55E", "A": "#22C55E", "B": "#3B82F6", "C": "#F59E0B", "D": "#F97316", "F": "#EF4444", } SEVERITY_COLORS = { "critical": "#EF4444", "major": "#F97316", "minor": "#EAB308", "info": "#3B82F6", } CATEGORY_LABELS = { "html_css": "HTML/CSS 표준", "accessibility": "접근성 (WCAG)", "seo": "SEO 최적화", "performance_security": "성능/보안", } class ReportService: """PDF and JSON report generation service.""" def __init__(self): self.env = Environment( loader=FileSystemLoader(str(TEMPLATES_DIR)), autoescape=True, ) # Register custom filters self.env.filters["grade_color"] = lambda g: GRADE_COLORS.get(g, "#6B7280") self.env.filters["severity_color"] = lambda s: SEVERITY_COLORS.get(s, "#6B7280") self.env.filters["category_label"] = lambda c: CATEGORY_LABELS.get(c, c) async def generate_pdf(self, inspection: dict) -> bytes: """Generate PDF report from inspection result.""" try: from weasyprint import HTML template = self.env.get_template("report.html") html_string = template.render( inspection=inspection, generated_at=datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC"), grade_colors=GRADE_COLORS, severity_colors=SEVERITY_COLORS, category_labels=CATEGORY_LABELS, ) pdf_bytes = HTML(string=html_string).write_pdf() return pdf_bytes except ImportError: logger.error("WeasyPrint is not installed") raise RuntimeError("PDF generation is not available (WeasyPrint not installed)") except Exception as e: logger.error("PDF generation failed: %s", str(e)) raise RuntimeError(f"PDF generation failed: {str(e)}") async def generate_json(self, inspection: dict) -> bytes: """Generate JSON report from inspection result.""" # Remove MongoDB internal fields clean_data = {k: v for k, v in inspection.items() if k != "_id"} json_str = json.dumps(clean_data, ensure_ascii=False, indent=2, default=str) return json_str.encode("utf-8") @staticmethod def generate_filename(url: str, extension: str) -> str: """Generate download filename: web-inspector-{url-slug}-{date}.{ext}""" parsed = urlparse(url) hostname = parsed.hostname or "unknown" url_slug = slugify(hostname, max_length=50) date_str = datetime.utcnow().strftime("%Y-%m-%d") return f"web-inspector-{url_slug}-{date_str}.{extension}"