Skip to content

metadata

tit.reporting.reportlets.metadata

Metadata-focused reportlets for TI-Toolbox reports.

This module provides specialized reportlets for displaying conductivity tables, processing steps, and other structured metadata.

ConductivityTableReportlet

ConductivityTableReportlet(conductivities: dict[str, dict[str, Any]] | None = None, title: str | None = None, show_sources: bool = True, conductivity_type: str = 'scalar')

Bases: BaseReportlet

Reportlet for displaying tissue conductivity values.

Shows conductivity values for different tissue types with their sources/references.

Initialize the conductivity table reportlet.

Parameters:

Name Type Description Default
conductivities dict[str, dict[str, Any]] | None

Dict mapping tissue names to conductivity info

None
title str | None

Title for the table

None
show_sources bool

Whether to show source references

True
conductivity_type str

Type of conductivity (scalar, anisotropic, etc.)

'scalar'
Source code in tit/reporting/reportlets/metadata.py
def __init__(
    self,
    conductivities: dict[str, dict[str, Any]] | None = None,
    title: str | None = None,
    show_sources: bool = True,
    conductivity_type: str = "scalar",
):
    """
    Initialize the conductivity table reportlet.

    Args:
        conductivities: Dict mapping tissue names to conductivity info
        title: Title for the table
        show_sources: Whether to show source references
        conductivity_type: Type of conductivity (scalar, anisotropic, etc.)
    """
    super().__init__(title or "Tissue Conductivities")
    self.conductivities = conductivities or DEFAULT_CONDUCTIVITIES.copy()
    self.show_sources = show_sources
    self.conductivity_type = conductivity_type

set_conductivity

set_conductivity(tissue: str, value: float, unit: str = 'S/m', source: str | None = None) -> None

Set conductivity for a tissue type.

Parameters:

Name Type Description Default
tissue str

Tissue name

required
value float

Conductivity value

required
unit str

Unit of measurement

'S/m'
source str | None

Source reference

None
Source code in tit/reporting/reportlets/metadata.py
def set_conductivity(
    self,
    tissue: str,
    value: float,
    unit: str = "S/m",
    source: str | None = None,
) -> None:
    """
    Set conductivity for a tissue type.

    Args:
        tissue: Tissue name
        value: Conductivity value
        unit: Unit of measurement
        source: Source reference
    """
    self.conductivities[tissue] = {
        "value": value,
        "unit": unit,
        "source": source or "User-defined",
    }

render_html

render_html() -> str

Render the conductivity table as HTML.

Source code in tit/reporting/reportlets/metadata.py
def render_html(self) -> str:
    """Render the conductivity table as HTML."""
    # Build table headers
    headers = ["Tissue", "Conductivity"]
    if self.show_sources:
        headers.append("Source")

    header_cells = "".join(f"<th>{h}</th>" for h in headers)

    # Build table rows
    rows = []
    for tissue, data in self.conductivities.items():
        # Handle both string keys ("white_matter") and integer keys (1)
        if isinstance(tissue, int):
            # Integer key - use the 'name' field from data if available
            tissue_name = data.get("name", f"Tissue {tissue}")
        else:
            tissue_name = str(tissue).replace("_", " ").title()
        # Handle both 'value' and 'conductivity' field names
        value = data.get("value", data.get("conductivity", 0))
        unit = data.get("unit", "S/m")

        cells = [
            f"<td>{tissue_name}</td>",
            f"<td>{value:.4f} {unit}</td>",
        ]

        if self.show_sources:
            # Handle both 'source' and 'reference' field names
            source = data.get("source", data.get("reference", "—"))
            cells.append(f"<td class='source-cell'>{source}</td>")

        rows.append(f"<tr>{''.join(cells)}</tr>")

    title_html = f"<h3>{self._title}</h3>" if self._title else ""
    type_badge = (
        f'<span class="conductivity-type-badge">{self.conductivity_type}</span>'
    )

    return f"""
    <div class="reportlet conductivity-reportlet" id="{self.reportlet_id}">
        {title_html}
        {type_badge}
        <div class="table-wrapper">
            <table class="data-table conductivity-table striped">
                <thead>
                    <tr>{header_cells}</tr>
                </thead>
                <tbody>
                    {"".join(rows)}
                </tbody>
            </table>
        </div>
    </div>
    """

ProcessingStepReportlet

ProcessingStepReportlet(title: str | None = None, steps: list[dict[str, Any]] | None = None)

Bases: BaseReportlet

Reportlet for displaying processing pipeline steps.

Shows collapsible processing steps with status, duration, and optional details.

Initialize the processing step reportlet.

Parameters:

Name Type Description Default
title str | None

Title for the processing steps section

None
steps list[dict[str, Any]] | None

List of step dictionaries

None
Source code in tit/reporting/reportlets/metadata.py
def __init__(
    self,
    title: str | None = None,
    steps: list[dict[str, Any]] | None = None,
):
    """
    Initialize the processing step reportlet.

    Args:
        title: Title for the processing steps section
        steps: List of step dictionaries
    """
    super().__init__(title or "Processing Steps")
    self.steps: list[dict[str, Any]] = steps or []

add_step

add_step(name: str, description: str | None = None, status: StatusType | str = PENDING, duration: float | None = None, parameters: dict[str, Any] | None = None, output_files: list[str] | None = None, error_message: str | None = None) -> None

Add a processing step.

Parameters:

Name Type Description Default
name str

Step name

required
description str | None

Step description

None
status StatusType | str

Step status (pending, running, completed, failed, skipped)

PENDING
duration float | None

Duration in seconds

None
parameters dict[str, Any] | None

Step parameters

None
output_files list[str] | None

List of output file paths

None
error_message str | None

Error message if failed

None
Source code in tit/reporting/reportlets/metadata.py
def add_step(
    self,
    name: str,
    description: str | None = None,
    status: StatusType | str = StatusType.PENDING,
    duration: float | None = None,
    parameters: dict[str, Any] | None = None,
    output_files: list[str] | None = None,
    error_message: str | None = None,
) -> None:
    """
    Add a processing step.

    Args:
        name: Step name
        description: Step description
        status: Step status (pending, running, completed, failed, skipped)
        duration: Duration in seconds
        parameters: Step parameters
        output_files: List of output file paths
        error_message: Error message if failed
    """
    if isinstance(status, StatusType):
        status = status.value

    self.steps.append(
        {
            "name": name,
            "description": description,
            "status": status,
            "duration": duration,
            "parameters": parameters or {},
            "output_files": output_files or [],
            "error_message": error_message,
        }
    )

render_html

render_html() -> str

Render the processing steps as HTML.

Source code in tit/reporting/reportlets/metadata.py
def render_html(self) -> str:
    """Render the processing steps as HTML."""
    if not self.steps:
        return f"""
        <div class="reportlet processing-steps-reportlet" id="{self.reportlet_id}">
            <em>No processing steps recorded</em>
        </div>
        """

    step_items = []
    for i, step in enumerate(self.steps):
        step_id = f"{self.reportlet_id}-step-{i}"
        status = step.get("status", "pending")
        icon = self._get_status_icon(status)
        s = step.get("duration")
        if s is None:
            duration = "—"
        elif s < 60:
            duration = f"{s:.1f}s"
        elif s < 3600:
            duration = f"{s / 60:.1f}m"
        else:
            duration = f"{s / 3600:.1f}h"

        # Build parameters section
        params_html = ""
        if step.get("parameters"):
            param_rows = "".join(
                f"<tr><td>{k}</td><td>{v}</td></tr>"
                for k, v in step["parameters"].items()
            )
            params_html = f"""
            <div class="step-parameters">
                <strong>Parameters:</strong>
                <table class="data-table compact">
                    <tbody>{param_rows}</tbody>
                </table>
            </div>
            """

        # Build output files section
        outputs_html = ""
        if step.get("output_files"):
            file_list = "".join(f"<li>{f}</li>" for f in step["output_files"])
            outputs_html = f"""
            <div class="step-outputs">
                <strong>Output Files:</strong>
                <ul>{file_list}</ul>
            </div>
            """

        # Build error section
        error_html = ""
        if step.get("error_message"):
            error_html = f"""
            <div class="step-error">
                <strong>Error:</strong>
                <span class="error-text">{step["error_message"]}</span>
            </div>
            """

        description_html = ""
        if step.get("description"):
            description_html = (
                f'<p class="step-description">{step["description"]}</p>'
            )

        step_items.append(f"""
            <div class="processing-step" id="{step_id}">
                <div class="step-header" onclick="toggleStep('{step_id}')">
                    <span class="step-status {status}">{icon}</span>
                    <span class="step-name">{step["name"]}</span>
                    <span class="step-duration">{duration}</span>
                </div>
                <div class="step-content" id="{step_id}-content">
                    {description_html}
                    {params_html}
                    {outputs_html}
                    {error_html}
                </div>
            </div>
            """)

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

    # Summary counts
    completed = sum(1 for s in self.steps if s.get("status") == "completed")
    failed = sum(1 for s in self.steps if s.get("status") == "failed")
    total = len(self.steps)

    summary_html = f"""
    <div class="steps-summary">
        <span class="summary-item completed">{completed}/{total} completed</span>
        {f'<span class="summary-item failed">{failed} failed</span>' if failed > 0 else ''}
    </div>
    """

    return f"""
    <div class="reportlet processing-steps-reportlet" id="{self.reportlet_id}">
        {title_html}
        {summary_html}
        <div class="steps-list">
            {"".join(step_items)}
        </div>
    </div>
    """

SummaryCardsReportlet

SummaryCardsReportlet(title: str | None = None, cards: list[dict[str, Any]] | None = None, columns: int = 4)

Bases: BaseReportlet

Reportlet for displaying key summary metrics as cards.

Shows important values in a prominent card grid layout.

Initialize the summary cards reportlet.

Parameters:

Name Type Description Default
title str | None

Title for the summary section

None
cards list[dict[str, Any]] | None

List of card data dicts

None
columns int

Number of columns in grid

4
Source code in tit/reporting/reportlets/metadata.py
def __init__(
    self,
    title: str | None = None,
    cards: list[dict[str, Any]] | None = None,
    columns: int = 4,
):
    """
    Initialize the summary cards reportlet.

    Args:
        title: Title for the summary section
        cards: List of card data dicts
        columns: Number of columns in grid
    """
    super().__init__(title)
    self.cards: list[dict[str, Any]] = cards or []
    self.columns = columns

add_card

add_card(label: str, value: Any, icon: str | None = None, color: str | None = None, subtitle: str | None = None) -> None

Add a summary card.

Parameters:

Name Type Description Default
label str

Card label

required
value Any

Card value

required
icon str | None

Optional icon character

None
color str | None

Optional accent color

None
subtitle str | None

Optional subtitle text

None
Source code in tit/reporting/reportlets/metadata.py
def add_card(
    self,
    label: str,
    value: Any,
    icon: str | None = None,
    color: str | None = None,
    subtitle: str | None = None,
) -> None:
    """
    Add a summary card.

    Args:
        label: Card label
        value: Card value
        icon: Optional icon character
        color: Optional accent color
        subtitle: Optional subtitle text
    """
    self.cards.append(
        {
            "label": label,
            "value": value,
            "icon": icon,
            "color": color,
            "subtitle": subtitle,
        }
    )

render_html

render_html() -> str

Render the summary cards as HTML.

Source code in tit/reporting/reportlets/metadata.py
def render_html(self) -> str:
    """Render the summary cards as HTML."""
    if not self.cards:
        return ""

    card_items = []
    for card in self.cards:
        icon_html = (
            f'<span class="card-icon">{card["icon"]}</span>'
            if card.get("icon")
            else ""
        )
        subtitle_html = (
            f'<div class="card-subtitle">{card["subtitle"]}</div>'
            if card.get("subtitle")
            else ""
        )
        style = f'border-top-color: {card["color"]};' if card.get("color") else ""

        card_items.append(f"""
            <div class="summary-card" style="{style}">
                {icon_html}
                <div class="card-label">{card["label"]}</div>
                <div class="card-value">{card["value"]}</div>
                {subtitle_html}
            </div>
            """)

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

    return f"""
    <div class="reportlet summary-cards-reportlet" id="{self.reportlet_id}">
        {title_html}
        <div class="card-grid columns-{self.columns}">
            {"".join(card_items)}
        </div>
    </div>
    """

ParameterListReportlet

ParameterListReportlet(title: str | None = None, parameters: dict[str, dict[str, Any]] | None = None)

Bases: BaseReportlet

Reportlet for displaying a categorized list of parameters.

Organizes parameters into groups with clear visual hierarchy.

Initialize the parameter list reportlet.

Parameters:

Name Type Description Default
title str | None

Title for the parameters section

None
parameters dict[str, dict[str, Any]] | None

Dict of category -> {param_name: param_value}

None
Source code in tit/reporting/reportlets/metadata.py
def __init__(
    self,
    title: str | None = None,
    parameters: dict[str, dict[str, Any]] | None = None,
):
    """
    Initialize the parameter list reportlet.

    Args:
        title: Title for the parameters section
        parameters: Dict of category -> {param_name: param_value}
    """
    super().__init__(title or "Parameters")
    self.parameters: dict[str, dict[str, Any]] = parameters or {}

add_category

add_category(category: str, params: dict[str, Any]) -> None

Add a parameter category.

Parameters:

Name Type Description Default
category str

Category name

required
params dict[str, Any]

Dict of parameter name to value

required
Source code in tit/reporting/reportlets/metadata.py
def add_category(self, category: str, params: dict[str, Any]) -> None:
    """
    Add a parameter category.

    Args:
        category: Category name
        params: Dict of parameter name to value
    """
    self.parameters[category] = params

add_parameter

add_parameter(category: str, name: str, value: Any) -> None

Add a single parameter to a category.

Parameters:

Name Type Description Default
category str

Category name

required
name str

Parameter name

required
value Any

Parameter value

required
Source code in tit/reporting/reportlets/metadata.py
def add_parameter(self, category: str, name: str, value: Any) -> None:
    """
    Add a single parameter to a category.

    Args:
        category: Category name
        name: Parameter name
        value: Parameter value
    """
    if category not in self.parameters:
        self.parameters[category] = {}
    self.parameters[category][name] = value

render_html

render_html() -> str

Render the parameter list as HTML.

Source code in tit/reporting/reportlets/metadata.py
def render_html(self) -> str:
    """Render the parameter list as HTML."""
    if not self.parameters:
        return ""

    category_sections = []
    for category, params in self.parameters.items():
        rows = []
        for name, value in params.items():
            formatted_name = name.replace("_", " ").title()
            formatted_value = self._format_value(value)
            rows.append(f"""<tr>
                    <td class="param-name">{formatted_name}</td>
                    <td class="param-value">{formatted_value}</td>
                </tr>""")

        category_sections.append(f"""
            <div class="parameter-category">
                <h4>{category}</h4>
                <table class="data-table compact">
                    <tbody>{"".join(rows)}</tbody>
                </table>
            </div>
            """)

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

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