Spaces:
Runtime error
Runtime error
| """Render a :class:`Comparison` to HTML and (when WeasyPrint is available) PDF. | |
| The strategy is deliberately layered: | |
| 1. We always produce an HTML report — no system dependencies, just Jinja2. | |
| 2. We additionally produce a PDF when ``weasyprint`` is importable; if it's | |
| not, we log a friendly note and skip the PDF. WeasyPrint needs ``pango`` | |
| and ``cairo`` system libraries that aren't trivially available on every | |
| environment (e.g. plain macOS without Homebrew), so making PDF optional | |
| keeps the HTML path usable everywhere. | |
| The template lives at ``reports/templates/report.html.j2`` so designers can | |
| tweak the report without touching Python. | |
| """ | |
| from __future__ import annotations | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| from typing import Optional | |
| from ccdp.eval.comparison import Comparison | |
| REPORTS_DIR = Path("reports") | |
| TEMPLATES_DIR = REPORTS_DIR / "templates" | |
| def _load_template(): | |
| """Lazy-import Jinja2 so module import stays cheap.""" | |
| from jinja2 import Environment, FileSystemLoader, select_autoescape | |
| env = Environment( | |
| loader=FileSystemLoader(str(TEMPLATES_DIR)), | |
| autoescape=select_autoescape(["html", "xml"]), | |
| ) | |
| return env.get_template("report.html.j2") | |
| def render_html(comparison: Comparison, out_path: Optional[Path] = None) -> Path: | |
| """Write the HTML version of the report; always works.""" | |
| REPORTS_DIR.mkdir(parents=True, exist_ok=True) | |
| tmpl = _load_template() | |
| html = tmpl.render(comparison=comparison, generated_at=datetime.now(timezone.utc)) | |
| out_path = out_path or REPORTS_DIR / f"report_{_timestamp()}.html" | |
| out_path.write_text(html, encoding="utf-8") | |
| return out_path | |
| def render_pdf(html_path: Path, pdf_path: Optional[Path] = None) -> Optional[Path]: | |
| """Convert an existing HTML report to PDF via WeasyPrint, if available. | |
| Returns ``None`` (and prints a hint) when WeasyPrint is not installed. | |
| """ | |
| try: | |
| from weasyprint import HTML # type: ignore | |
| except ImportError: | |
| print("[report] WeasyPrint not installed; skipping PDF. " | |
| "Install with `pip install weasyprint` and the system deps " | |
| "(`pango`, `cairo`, `gdk-pixbuf`) for PDF output.") | |
| return None | |
| pdf_path = pdf_path or html_path.with_suffix(".pdf") | |
| HTML(filename=str(html_path)).write_pdf(str(pdf_path)) | |
| return pdf_path | |
| def generate(comparison: Comparison, also_pdf: bool = True) -> dict: | |
| """Single-call convenience: render HTML, then PDF if possible. Returns paths.""" | |
| html_path = render_html(comparison) | |
| pdf_path = render_pdf(html_path) if also_pdf else None | |
| return {"html": html_path, "pdf": pdf_path} | |
| def _timestamp() -> str: | |
| return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S") | |