Skip to content

assembler

tit.reporting.core.assembler

Report assembler for combining reportlets into complete HTML reports.

The ReportAssembler class provides a high-level interface for building reports from individual reportlets organized into sections.

ReportAssembler

ReportAssembler(metadata: ReportMetadata | None = None, title: str | None = None)

Assembles reportlets into a complete HTML report.

The assembler manages sections, handles ordering, generates the table of contents, and renders the final HTML document.

Initialize the report assembler.

Parameters:

Name Type Description Default
metadata ReportMetadata | None

Report metadata (title, subject, etc.)

None
title str | None

Report title (overrides metadata.title if provided)

None
Source code in tit/reporting/core/assembler.py
def __init__(
    self,
    metadata: ReportMetadata | None = None,
    title: str | None = None,
):
    """
    Initialize the report assembler.

    Args:
        metadata: Report metadata (title, subject, etc.)
        title: Report title (overrides metadata.title if provided)
    """
    self.metadata = metadata or ReportMetadata(title=title or "Report")
    if title:
        self.metadata.title = title

    self.sections: list[ReportSection] = []
    self._custom_css: str = ""
    self._custom_js: str = ""

add_section

add_section(section_id: str, title: str, description: str | None = None, collapsed: bool = False, order: int | None = None) -> ReportSection

Add a new section to the report.

Parameters:

Name Type Description Default
section_id str

Unique identifier for the section

required
title str

Section title

required
description str | None

Optional section description

None
collapsed bool

Whether section starts collapsed

False
order int | None

Sort order (lower = earlier in report)

None

Returns:

Type Description
ReportSection

The created ReportSection object

Source code in tit/reporting/core/assembler.py
def add_section(
    self,
    section_id: str,
    title: str,
    description: str | None = None,
    collapsed: bool = False,
    order: int | None = None,
) -> ReportSection:
    """
    Add a new section to the report.

    Args:
        section_id: Unique identifier for the section
        title: Section title
        description: Optional section description
        collapsed: Whether section starts collapsed
        order: Sort order (lower = earlier in report)

    Returns:
        The created ReportSection object
    """
    if order is None:
        order = len(self.sections)

    section = ReportSection(
        section_id=section_id,
        title=title,
        description=description,
        collapsed=collapsed,
        order=order,
    )
    self.sections.append(section)
    return section

get_section

get_section(section_id: str) -> ReportSection | None

Get a section by its ID.

Parameters:

Name Type Description Default
section_id str

The section identifier

required

Returns:

Type Description
ReportSection | None

The ReportSection or None if not found

Source code in tit/reporting/core/assembler.py
def get_section(self, section_id: str) -> ReportSection | None:
    """
    Get a section by its ID.

    Args:
        section_id: The section identifier

    Returns:
        The ReportSection or None if not found
    """
    for section in self.sections:
        if section.section_id == section_id:
            return section
    return None

add_reportlet_to_section

add_reportlet_to_section(section_id: str, reportlet: Any, create_if_missing: bool = True, section_title: str | None = None) -> None

Add a reportlet to a specific section.

Parameters:

Name Type Description Default
section_id str

The section identifier

required
reportlet Any

The reportlet to add

required
create_if_missing bool

Create section if it doesn't exist

True
section_title str | None

Title for new section (if created)

None
Source code in tit/reporting/core/assembler.py
def add_reportlet_to_section(
    self,
    section_id: str,
    reportlet: Any,
    create_if_missing: bool = True,
    section_title: str | None = None,
) -> None:
    """
    Add a reportlet to a specific section.

    Args:
        section_id: The section identifier
        reportlet: The reportlet to add
        create_if_missing: Create section if it doesn't exist
        section_title: Title for new section (if created)
    """
    section = self.get_section(section_id)

    if section is None:
        if create_if_missing:
            title = section_title or section_id.replace("_", " ").title()
            section = self.add_section(section_id, title)
        else:
            raise ValueError(f"Section '{section_id}' not found")

    section.add_reportlet(reportlet)

set_custom_css

set_custom_css(css: str) -> None

Add custom CSS styles to the report.

Source code in tit/reporting/core/assembler.py
def set_custom_css(self, css: str) -> None:
    """Add custom CSS styles to the report."""
    self._custom_css = css

set_custom_js

set_custom_js(js: str) -> None

Add custom JavaScript to the report.

Source code in tit/reporting/core/assembler.py
def set_custom_js(self, js: str) -> None:
    """Add custom JavaScript to the report."""
    self._custom_js = js

render_toc

render_toc() -> str

Render the table of contents as HTML.

Returns:

Type Description
str

HTML string for the table of contents

Source code in tit/reporting/core/assembler.py
def render_toc(self) -> str:
    """
    Render the table of contents as HTML.

    Returns:
        HTML string for the table of contents
    """
    sorted_sections = sorted(self.sections, key=lambda s: s.order)

    links = []
    for section in sorted_sections:
        links.append(
            f'<li><a href="#{section.section_id}">{section.title}</a></li>'
        )

    return f'<ul class="toc-list">{"".join(links)}</ul>'

render_metadata

render_metadata() -> str

Render the header metadata as HTML.

Returns:

Type Description
str

HTML string for the header metadata

Source code in tit/reporting/core/assembler.py
def render_metadata(self) -> str:
    """
    Render the header metadata as HTML.

    Returns:
        HTML string for the header metadata
    """
    parts = []

    if self.metadata.subject_id:
        parts.append(
            f"<span>Subject: <strong>{self.metadata.subject_id}</strong></span>"
        )

    if self.metadata.session_id:
        parts.append(
            f"<span>Session: <strong>{self.metadata.session_id}</strong></span>"
        )

    parts.append(
        f'<span>Generated: <strong>{self.metadata.generation_time.strftime("%Y-%m-%d %H:%M:%S")}</strong></span>'
    )

    return f'<div class="header-meta">{"".join(parts)}</div>'

render_sections

render_sections() -> str

Render all sections as HTML.

Returns:

Type Description
str

HTML string for all sections

Source code in tit/reporting/core/assembler.py
def render_sections(self) -> str:
    """
    Render all sections as HTML.

    Returns:
        HTML string for all sections
    """
    sorted_sections = sorted(self.sections, key=lambda s: s.order)
    return "\n".join(section.render_html() for section in sorted_sections)

render_html

render_html() -> str

Render the complete report as HTML.

Returns:

Type Description
str

Complete HTML document as a string

Source code in tit/reporting/core/assembler.py
def render_html(self) -> str:
    """
    Render the complete report as HTML.

    Returns:
        Complete HTML document as a string
    """
    content = self.render_sections()
    toc_html = self.render_toc()
    metadata_html = self.render_metadata()

    return get_html_template(
        title=self.metadata.title,
        content=content,
        toc_html=toc_html,
        metadata_html=metadata_html,
        custom_css=self._custom_css,
        custom_js=self._custom_js,
    )

save

save(output_path: str | Path, create_dirs: bool = True) -> Path

Save the report to a file.

Parameters:

Name Type Description Default
output_path str | Path

Path to save the HTML file

required
create_dirs bool

Create parent directories if needed

True

Returns:

Type Description
Path

Path to the saved file

Source code in tit/reporting/core/assembler.py
def save(
    self,
    output_path: str | Path,
    create_dirs: bool = True,
) -> Path:
    """
    Save the report to a file.

    Args:
        output_path: Path to save the HTML file
        create_dirs: Create parent directories if needed

    Returns:
        Path to the saved file
    """
    output_path = Path(output_path)

    if create_dirs:
        output_path.parent.mkdir(parents=True, exist_ok=True)

    html_content = self.render_html()
    output_path.write_text(html_content, encoding="utf-8")

    return output_path

to_dict

to_dict() -> dict[str, Any]

Convert the report to a dictionary representation.

Returns:

Type Description
dict[str, Any]

Dictionary containing all report data

Source code in tit/reporting/core/assembler.py
def to_dict(self) -> dict[str, Any]:
    """
    Convert the report to a dictionary representation.

    Returns:
        Dictionary containing all report data
    """
    return {
        "metadata": self.metadata.to_dict(),
        "sections": [s.to_dict() for s in self.sections],
    }

from_dict classmethod

from_dict(data: dict[str, Any]) -> Self

Create a ReportAssembler from a dictionary.

Parameters:

Name Type Description Default
data dict[str, Any]

Dictionary containing report data

required

Returns:

Type Description
Self

Reconstructed ReportAssembler instance

Note

This reconstructs the structure but not the reportlet instances. Use this for loading report metadata, not for full reconstruction.

Source code in tit/reporting/core/assembler.py
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
    """
    Create a ReportAssembler from a dictionary.

    Args:
        data: Dictionary containing report data

    Returns:
        Reconstructed ReportAssembler instance

    Note:
        This reconstructs the structure but not the reportlet instances.
        Use this for loading report metadata, not for full reconstruction.
    """
    metadata_dict = data.get("metadata", {})
    metadata = ReportMetadata(
        title=metadata_dict.get("title", "Report"),
        subject_id=metadata_dict.get("subject_id"),
        session_id=metadata_dict.get("session_id"),
        report_type=metadata_dict.get("report_type", "general"),
        project_dir=metadata_dict.get("project_dir"),
    )

    assembler = cls(metadata=metadata)

    for section_data in data.get("sections", []):
        assembler.add_section(
            section_id=section_data["section_id"],
            title=section_data["title"],
            description=section_data.get("description"),
            collapsed=section_data.get("collapsed", False),
            order=section_data.get("order", 0),
        )

    return assembler