Skip to content

reporting

PDF report generation from any module's AnalysisOutput.

from climate_change import ReportBuilder

report_builder

PDF report builder using reportlab. Assembles analysis metadata, AI interpretation, embedded map PNG, charts, and recommendations into a structured PDF.

ReportBuilder

ReportBuilder(output_path: str | Path)

Builds a PDF report for any completed AnalysisOutput. Drought module gets additional CDI sub-index and forecast sections.

Source code in reporting/report_builder.py
def __init__(self, output_path: str | Path) -> None:
    self.output_path = Path(output_path)
    self.output_path.parent.mkdir(parents=True, exist_ok=True)
    self._styles = _styles()

build

build(output: AnalysisOutput, ai_interpretation: str | None = None, map_png_bytes: bytes | None = None) -> Path

Assemble and write the PDF.

Parameters:

Name Type Description Default
output AnalysisOutput

completed AnalysisOutput from any module.

required
ai_interpretation str | None

human-centered AI text (optional; section omitted if None).

None
map_png_bytes bytes | None

PNG screenshot of the Leaflet result map (optional).

None

Returns:

Type Description
Path

Path to the written PDF file.

Source code in reporting/report_builder.py
def build(
    self,
    output: AnalysisOutput,
    ai_interpretation: str | None = None,
    map_png_bytes: bytes | None = None,
) -> Path:
    """
    Assemble and write the PDF.

    Args:
        output: completed AnalysisOutput from any module.
        ai_interpretation: human-centered AI text (optional; section omitted if None).
        map_png_bytes: PNG screenshot of the Leaflet result map (optional).

    Returns:
        Path to the written PDF file.
    """
    doc = SimpleDocTemplate(
        str(self.output_path),
        pagesize=A4,
        leftMargin=2.5 * cm,
        rightMargin=2.5 * cm,
        topMargin=2.5 * cm,
        bottomMargin=2.5 * cm,
    )
    story: list[Any] = []

    self._add_cover(story, output)
    story.append(PageBreak())
    self._add_metadata_table(story, output)
    self._add_rule(story)

    map_bytes = (
        self._render_raster_bytes(output)
        or self._render_choropleth_bytes(output)
        or map_png_bytes
    )
    if map_bytes:
        self._add_map_section(story, output, map_bytes)

    self._add_statistics_section(story, output)

    if output.module == "drought":
        self._add_drought_sections(story, output)
    elif output.module == "disease":
        self._add_disease_sections(story, output)
    elif output.module == "food_security":
        self._add_food_security_sections(story, output)
    elif output.module == "flood":
        self._add_flood_sections(story, output)
    elif output.module == "land_degradation":
        self._add_land_degradation_sections(story, output)

    if ai_interpretation:
        self._add_ai_section(story, ai_interpretation)

    self._add_glossary(story, output)
    self._add_appendix(story, output)

    doc.build(story)
    return self.output_path