Skip to content

base

tit.reporting.core.base

Base reportlet classes for the TI-Toolbox reporting system.

This module provides the foundational reportlet implementations that can be used directly or extended for specialized purposes.

BaseReportlet

BaseReportlet(title: str | None = None)

Bases: ABC

Abstract base class for all reportlets.

Source code in tit/reporting/core/base.py
def __init__(self, title: str | None = None):
    self._title = title
    self._id = str(uuid.uuid4())[:8]

reportlet_type abstractmethod property

reportlet_type: ReportletType

Return the type of this reportlet.

reportlet_id property

reportlet_id: str

Return a unique identifier for this reportlet.

title property

title: str | None

Return the title of this reportlet.

render_html abstractmethod

render_html() -> str

Render the reportlet as HTML.

Source code in tit/reporting/core/base.py
@abstractmethod
def render_html(self) -> str:
    """Render the reportlet as HTML."""
    pass

to_dict abstractmethod

to_dict() -> dict[str, Any]

Convert the reportlet to a dictionary representation.

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

MetadataReportlet

MetadataReportlet(data: dict[str, Any], title: str | None = None, display_mode: str = 'table', columns: int = 2)

Bases: BaseReportlet

Reportlet for displaying metadata as key-value pairs.

Supports two display modes: - 'table': Traditional table layout - 'cards': Modern card grid layout

Source code in tit/reporting/core/base.py
def __init__(
    self,
    data: dict[str, Any],
    title: str | None = None,
    display_mode: str = "table",
    columns: int = 2,
):
    super().__init__(title)
    self.data = data
    self.display_mode = display_mode
    self.columns = columns

render_html

render_html() -> str

Render metadata as HTML table or cards.

Source code in tit/reporting/core/base.py
def render_html(self) -> str:
    """Render metadata as HTML table or cards."""
    if self.display_mode == "cards":
        return self._render_cards()
    return self._render_table()

ImageReportlet

ImageReportlet(image_source: str | Path | bytes | Any | None = None, title: str | None = None, caption: str | None = None, alt_text: str | None = None, width: str | None = None, height: str | None = None)

Bases: BaseReportlet

Reportlet for displaying images.

Supports embedding images as base64 or referencing external paths. Images can be loaded from file paths, PIL Images, or raw bytes.

Source code in tit/reporting/core/base.py
def __init__(
    self,
    image_source: str | Path | bytes | Any | None = None,
    title: str | None = None,
    caption: str | None = None,
    alt_text: str | None = None,
    width: str | None = None,
    height: str | None = None,
):
    super().__init__(title)
    self.caption = caption
    self.alt_text = alt_text or title or "Image"
    self.width = width
    self.height = height
    self._base64_data: str | None = None
    self._mime_type: str = "image/png"

    if image_source is not None:
        self._load_image(image_source)

set_base64_data

set_base64_data(data: str, mime_type: str = 'image/png') -> None

Directly set base64 encoded image data.

Source code in tit/reporting/core/base.py
def set_base64_data(self, data: str, mime_type: str = "image/png") -> None:
    """Directly set base64 encoded image data."""
    self._base64_data = data
    self._mime_type = mime_type

render_html

render_html() -> str

Render image as HTML.

Source code in tit/reporting/core/base.py
def render_html(self) -> str:
    """Render image as HTML."""
    if not self._base64_data:
        return f"""
        <div class="reportlet image-reportlet" id="{self.reportlet_id}">
            <div class="image-placeholder">
                <em>No image available</em>
            </div>
        </div>
        """

    style_parts = []
    if self.width:
        style_parts.append(f"max-width: {self.width}")
    if self.height:
        style_parts.append(f"max-height: {self.height}")
    style = "; ".join(style_parts) if style_parts else ""

    title_html = f"<h3>{self._title}</h3>" if self._title else ""
    caption_html = (
        f'<figcaption class="image-caption">{self.caption}</figcaption>'
        if self.caption
        else ""
    )

    return f"""
    <div class="reportlet image-reportlet" id="{self.reportlet_id}">
        {title_html}
        <figure class="image-figure">
            <img src="data:{self._mime_type};base64,{self._base64_data}"
                 alt="{self.alt_text}"
                 style="{style}"
                 class="report-image" />
            {caption_html}
        </figure>
    </div>
    """

TableReportlet

TableReportlet(data: list[dict] | list[list] | Any, title: str | None = None, headers: list[str] | None = None, sortable: bool = False, striped: bool = True, compact: bool = False)

Bases: BaseReportlet

Reportlet for displaying tabular data.

Supports various input formats including lists of dicts, lists of lists, and pandas DataFrames.

Source code in tit/reporting/core/base.py
def __init__(
    self,
    data: list[dict] | list[list] | Any,
    title: str | None = None,
    headers: list[str] | None = None,
    sortable: bool = False,
    striped: bool = True,
    compact: bool = False,
):
    super().__init__(title)
    self.headers: list[str] = []
    self.rows: list[list[Any]] = []
    self.sortable = sortable
    self.striped = striped
    self.compact = compact

    self._process_data(data, headers)

render_html

render_html() -> str

Render table as HTML.

Source code in tit/reporting/core/base.py
def render_html(self) -> str:
    """Render table as HTML."""
    if not self.rows and not self.headers:
        return f"""
        <div class="reportlet table-reportlet" id="{self.reportlet_id}">
            <em>No data available</em>
        </div>
        """

    classes = ["data-table"]
    if self.striped:
        classes.append("striped")
    if self.compact:
        classes.append("compact")
    if self.sortable:
        classes.append("sortable")

    header_html = ""
    if self.headers:
        header_cells = "".join(f"<th>{h}</th>" for h in self.headers)
        header_html = f"<thead><tr>{header_cells}</tr></thead>"

    body_rows = []
    for row in self.rows:
        cells = "".join(f"<td>{self._format_cell(c)}</td>" for c in row)
        body_rows.append(f"<tr>{cells}</tr>")
    body_html = f"<tbody>{''.join(body_rows)}</tbody>"

    title_html = f"<h3>{self._title}</h3>" if self._title else ""

    return f"""
    <div class="reportlet table-reportlet" id="{self.reportlet_id}">
        {title_html}
        <div class="table-wrapper">
            <table class="{' '.join(classes)}">
                {header_html}
                {body_html}
            </table>
        </div>
    </div>
    """

TextReportlet

TextReportlet(content: str, title: str | None = None, content_type: str = 'text', copyable: bool = False, monospace: bool = False)

Bases: BaseReportlet

Reportlet for displaying text content.

Supports plain text, HTML, and markdown-style formatting. Includes optional copy-to-clipboard functionality for boilerplate text.

Source code in tit/reporting/core/base.py
def __init__(
    self,
    content: str,
    title: str | None = None,
    content_type: str = "text",
    copyable: bool = False,
    monospace: bool = False,
):
    super().__init__(title)
    self.content = content
    self.content_type = content_type  # 'text', 'html', 'code'
    self.copyable = copyable
    self.monospace = monospace

render_html

render_html() -> str

Render text content as HTML.

Source code in tit/reporting/core/base.py
def render_html(self) -> str:
    """Render text content as HTML."""
    classes = ["text-content"]
    if self.monospace:
        classes.append("monospace")
    if self.copyable:
        classes.append("copyable")

    # Format content based on type
    if self.content_type == "html":
        formatted_content = self.content
    elif self.content_type == "code":
        formatted_content = (
            f"<pre><code>{self._escape_html(self.content)}</code></pre>"
        )
    else:
        # Plain text - convert newlines to paragraphs
        paragraphs = self.content.split("\n\n")
        formatted_content = "".join(f"<p>{p}</p>" for p in paragraphs if p.strip())

    title_html = f"<h3>{self._title}</h3>" if self._title else ""

    copy_button = ""
    if self.copyable:
        copy_button = f"""
        <button class="copy-btn" onclick="copyToClipboard('{self.reportlet_id}-content')">
            Copy to Clipboard
        </button>
        """

    return f"""
    <div class="reportlet text-reportlet" id="{self.reportlet_id}">
        {title_html}
        {copy_button}
        <div class="{' '.join(classes)}" id="{self.reportlet_id}-content">
            {formatted_content}
        </div>
    </div>
    """

ErrorReportlet

ErrorReportlet(messages: list[dict[str, Any]] | None = None, title: str | None = None)

Bases: BaseReportlet

Reportlet for displaying errors and warnings.

Supports different severity levels with appropriate styling.

Source code in tit/reporting/core/base.py
def __init__(
    self,
    messages: list[dict[str, Any]] | None = None,
    title: str | None = None,
):
    super().__init__(title or "Errors and Warnings")
    self.messages: list[dict[str, Any]] = messages or []

add_message

add_message(message: str, severity: SeverityLevel = ERROR, context: str | None = None, step: str | None = None) -> None

Add an error or warning message.

Source code in tit/reporting/core/base.py
def add_message(
    self,
    message: str,
    severity: SeverityLevel = SeverityLevel.ERROR,
    context: str | None = None,
    step: str | None = None,
) -> None:
    """Add an error or warning message."""
    self.messages.append(
        {
            "message": message,
            "severity": (
                severity.value if isinstance(severity, SeverityLevel) else severity
            ),
            "context": context,
            "step": step,
        }
    )

add_error

add_error(message: str, context: str | None = None, step: str | None = None) -> None

Add an error message.

Source code in tit/reporting/core/base.py
def add_error(
    self, message: str, context: str | None = None, step: str | None = None
) -> None:
    """Add an error message."""
    self.add_message(message, SeverityLevel.ERROR, context, step)

add_warning

add_warning(message: str, context: str | None = None, step: str | None = None) -> None

Add a warning message.

Source code in tit/reporting/core/base.py
def add_warning(
    self, message: str, context: str | None = None, step: str | None = None
) -> None:
    """Add a warning message."""
    self.add_message(message, SeverityLevel.WARNING, context, step)

render_html

render_html() -> str

Render errors and warnings as HTML.

Source code in tit/reporting/core/base.py
def render_html(self) -> str:
    """Render errors and warnings as HTML."""
    if not self.messages:
        return f"""
        <div class="reportlet error-reportlet success" id="{self.reportlet_id}">
            <div class="success-message">
                <span class="status-icon">[OK]</span>
                No errors or warnings
            </div>
        </div>
        """

    message_items = []
    for msg in self.messages:
        severity = msg.get("severity", "error")
        icon = (
            "[!]"
            if severity == "warning"
            else "[X]" if severity in ("error", "critical") else "[i]"
        )
        context = msg.get("context", "")
        step = msg.get("step", "")

        context_html = (
            f'<span class="error-context">[{context}]</span>' if context else ""
        )
        step_html = f'<span class="error-step">Step: {step}</span>' if step else ""

        message_items.append(f"""
            <div class="message-item {severity}">
                <span class="severity-icon">{icon}</span>
                <div class="message-content">
                    {context_html}
                    <span class="message-text">{msg["message"]}</span>
                    {step_html}
                </div>
            </div>
            """)

    title_html = f"<h3>{self._title}</h3>" if self._title else ""

    return f"""
    <div class="reportlet error-reportlet" id="{self.reportlet_id}">
        {title_html}
        <div class="messages-list">
            {"".join(message_items)}
        </div>
    </div>
    """

ReferencesReportlet

ReferencesReportlet(references: list[dict[str, str]] | None = None, title: str | None = None)

Bases: BaseReportlet

Reportlet for displaying citations and references.

Automatically formats references in a consistent style.

Source code in tit/reporting/core/base.py
def __init__(
    self,
    references: list[dict[str, str]] | None = None,
    title: str | None = None,
):
    super().__init__(title or "References")
    self.references: list[dict[str, str]] = references or []

add_reference

add_reference(key: str, citation: str, url: str | None = None, doi: str | None = None) -> None

Add a reference.

Source code in tit/reporting/core/base.py
def add_reference(
    self,
    key: str,
    citation: str,
    url: str | None = None,
    doi: str | None = None,
) -> None:
    """Add a reference."""
    self.references.append(
        {
            "key": key,
            "citation": citation,
            "url": url,
            "doi": doi,
        }
    )

render_html

render_html() -> str

Render references as HTML.

Source code in tit/reporting/core/base.py
def render_html(self) -> str:
    """Render references as HTML."""
    if not self.references:
        return ""

    ref_items = []
    for ref in self.references:
        citation = ref["citation"]
        key = ref.get("key", "")

        # Add DOI link if available
        if ref.get("doi"):
            doi_link = (
                f'<a href="https://doi.org/{ref["doi"]}" target="_blank">[DOI]</a>'
            )
            citation = f"{citation} {doi_link}"
        elif ref.get("url"):
            url_link = f'<a href="{ref["url"]}" target="_blank">[Link]</a>'
            citation = f"{citation} {url_link}"

        ref_items.append(f"""
            <li class="reference-item" id="ref-{key}">
                <span class="ref-key">[{key}]</span>
                <span class="ref-citation">{citation}</span>
            </li>
            """)

    title_html = f"<h3>{self._title}</h3>"

    return f"""
    <div class="reportlet references-reportlet" id="{self.reportlet_id}">
        {title_html}
        <ol class="references-list">
            {"".join(ref_items)}
        </ol>
    </div>
    """