Core infrastructure for the TI-Toolbox reporting system.
Provides the foundational components for building modular, self-contained
HTML reports: protocol definitions, base reportlet classes, the report
assembler, and HTML/CSS/JS templates.
Public API
Protocols and types
ReportletType, ReportMetadata, ReportSection, Reportlet,
SeverityLevel, StatusType
Base reportlets
BaseReportlet, MetadataReportlet, ImageReportlet, TableReportlet,
TextReportlet, ErrorReportlet, ReferencesReportlet
Assembler
ReportAssembler
Templates
DEFAULT_CSS_STYLES, DEFAULT_JS_SCRIPTS, get_html_template
See Also
tit.reporting.reportlets : Domain-specific reportlet subclasses.
tit.reporting.generators : Report generator classes that build full reports.
ReportletType
Bases: Enum
Enumeration of reportlet types.
Each member corresponds to a rendering strategy in the base reportlet
classes.
ReportMetadata(title: str, subject_id: str | None = None, session_id: str | None = None, report_type: str = 'general', generation_time: datetime = now(), software_versions: dict[str, str] = dict(), project_dir: str | None = None, bids_version: str = '1.8.0', dataset_type: str = 'derivative')
Metadata for a generated report.
title : str
Report title shown in the header.
subject_id : str or None
BIDS subject identifier (without sub- prefix).
session_id : str or None
Session or run identifier.
report_type : str
Type tag (e.g. "simulation", "preprocessing").
generation_time : datetime.datetime
Timestamp when the report was generated.
software_versions : dict[str, str]
Mapping of tool name to version string.
project_dir : str or None
BIDS project root directory.
bids_version : str
BIDS specification version.
dataset_type : str
BIDS dataset type (always "derivative").
Convert metadata to a JSON-serialisable dictionary.
dict
All metadata fields with generation_time as ISO-8601 string.
Source code in tit/reporting/core/protocols.py
| def to_dict(self) -> dict[str, Any]:
"""Convert metadata to a JSON-serialisable dictionary.
Returns
-------
dict
All metadata fields with ``generation_time`` as ISO-8601 string.
"""
return {
"title": self.title,
"subject_id": self.subject_id,
"session_id": self.session_id,
"report_type": self.report_type,
"generation_time": self.generation_time.isoformat(),
"software_versions": self.software_versions,
"project_dir": self.project_dir,
"bids_version": self.bids_version,
"dataset_type": self.dataset_type,
}
|
ReportSection
dataclass
ReportSection(section_id: str, title: str, reportlets: list[Any] = list(), description: str | None = None, collapsed: bool = False, order: int = 0)
A section within a report containing multiple reportlets.
Attributes
section_id : str
Unique identifier used as the HTML id attribute.
title : str
Human-readable section title.
reportlets : list
Ordered list of reportlet instances.
description : str or None
Optional description displayed below the title.
collapsed : bool
Whether the section is initially collapsed.
order : int
Sort order (lower values appear first).
add_reportlet
add_reportlet(reportlet: Any) -> None
Add a reportlet to this section.
Source code in tit/reporting/core/protocols.py
| def add_reportlet(self, reportlet: Any) -> None:
"""Add a reportlet to this section."""
self.reportlets.append(reportlet)
|
render_html
Render the section and all its reportlets as HTML.
Returns
str
HTML fragment for the complete section.
Source code in tit/reporting/core/protocols.py
| def render_html(self) -> str:
"""Render the section and all its reportlets as HTML.
Returns
-------
str
HTML fragment for the complete section.
"""
collapse_class = "collapsible" if self.collapsed else ""
content_parts = []
for reportlet in self.reportlets:
content_parts.append(reportlet.render_html())
content = "\n".join(content_parts)
description_html = ""
if self.description:
description_html = f'<p class="section-description">{self.description}</p>'
return f"""
<section id="{self.section_id}" class="report-section {collapse_class}">
<h2 class="section-title">{self.title}</h2>
{description_html}
<div class="section-content">
{content}
</div>
</section>
"""
|
to_dict
Convert section to a JSON-serialisable dictionary.
Returns
dict
Section metadata plus serialised reportlets.
Source code in tit/reporting/core/protocols.py
| def to_dict(self) -> dict[str, Any]:
"""Convert section to a JSON-serialisable dictionary.
Returns
-------
dict
Section metadata plus serialised reportlets.
"""
return {
"section_id": self.section_id,
"title": self.title,
"description": self.description,
"collapsed": self.collapsed,
"order": self.order,
"reportlets": [r.to_dict() for r in self.reportlets],
}
|
Reportlet
Bases: Protocol
Protocol defining the interface for all reportlets.
Any object satisfying this protocol can be added to a
ReportSection.
reportlet_type
property
Return the type of this reportlet.
reportlet_id
property
Return a unique identifier for this reportlet.
render_html
Render the reportlet as an HTML fragment.
Source code in tit/reporting/core/protocols.py
| def render_html(self) -> str:
"""Render the reportlet as an HTML fragment."""
...
|
to_dict
Convert the reportlet to a dictionary representation.
Source code in tit/reporting/core/protocols.py
| def to_dict(self) -> dict[str, Any]:
"""Convert the reportlet to a dictionary representation."""
...
|
SeverityLevel
Bases: Enum
Severity levels for errors and warnings.
Used by ErrorReportlet to control icon and colour styling.
StatusType
Bases: Enum
Status types for processing steps.
Used by ProcessingStepReportlet to display step progress.
BaseReportlet
BaseReportlet(title: str | None = None)
Bases: ABC
Abstract base class for all reportlets.
Subclasses must implement reportlet_type, render_html, and
to_dict.
Parameters
title : str or None, optional
Optional title displayed above the reportlet content.
See Also
MetadataReportlet : Key-value pair display.
ImageReportlet : Inline image display.
TableReportlet : Tabular data display.
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
Return the type of this reportlet.
reportlet_id
property
Return a unique identifier for this reportlet.
title
property
Return the title of this reportlet.
render_html
abstractmethod
Render the reportlet as an HTML fragment.
Source code in tit/reporting/core/base.py
| @abstractmethod
def render_html(self) -> str:
"""Render the reportlet as an HTML fragment."""
pass
|
to_dict
abstractmethod
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(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 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 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
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 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 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 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 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>
"""
|
ReportAssembler
Assemble reportlets into a complete HTML report.
Manages sections, handles ordering, generates the table of contents,
and renders the final HTML document.
Parameters
metadata : ReportMetadata or None, optional
Report metadata (title, subject, etc.). A default is created
when None.
title : str or None, optional
Report title. Overrides metadata.title when provided.
See Also
ReportMetadata : Dataclass holding report-level metadata.
ReportSection : Container for reportlets within a section.
BaseReportGenerator : Abstract generator that wraps ReportAssembler.
Source code in tit/reporting/core/assembler.py
| def __init__(
self,
metadata: ReportMetadata | None = None,
title: str | None = None,
):
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 a new section to the report.
Parameters
section_id : str
Unique identifier for the section.
title : str
Section title displayed in the report.
description : str or None, optional
Optional section description shown below the title.
collapsed : bool, optional
Whether the section starts collapsed (default False).
order : int or None, optional
Sort order (lower = earlier in report). Defaults to the
current number of sections.
Returns
ReportSection
The newly created section 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.
Parameters
----------
section_id : str
Unique identifier for the section.
title : str
Section title displayed in the report.
description : str or None, optional
Optional section description shown below the title.
collapsed : bool, optional
Whether the section starts collapsed (default *False*).
order : int or None, optional
Sort order (lower = earlier in report). Defaults to the
current number of sections.
Returns
-------
ReportSection
The newly created section 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 a section by its ID.
Parameters
section_id : str
The section identifier.
Returns
ReportSection or None
The matching section, 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.
Parameters
----------
section_id : str
The section identifier.
Returns
-------
ReportSection or None
The matching section, 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
section_id : str
The section identifier.
reportlet : Reportlet
The reportlet instance to add.
create_if_missing : bool, optional
Create the section if it does not exist (default True).
section_title : str or None, optional
Title for the new section when it is auto-created.
Raises
ValueError
If the section is not found and create_if_missing is False.
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.
Parameters
----------
section_id : str
The section identifier.
reportlet : Reportlet
The reportlet instance to add.
create_if_missing : bool, optional
Create the section if it does not exist (default *True*).
section_title : str or None, optional
Title for the new section when it is auto-created.
Raises
------
ValueError
If the section is not found and *create_if_missing* is *False*.
"""
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 the table of contents as HTML.
Returns
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
-------
str
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 the header metadata as HTML.
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
-------
str
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 all sections as HTML.
Returns
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
-------
str
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 the complete report as HTML.
Returns
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
-------
str
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 the report to a file.
Parameters
output_path : str or pathlib.Path
Path to save the HTML file.
create_dirs : bool, optional
Create parent directories if needed (default True).
Returns
pathlib.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.
Parameters
----------
output_path : str or pathlib.Path
Path to save the HTML file.
create_dirs : bool, optional
Create parent directories if needed (default *True*).
Returns
-------
pathlib.Path
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
Convert the report to a dictionary representation.
Returns
dict
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
-------
dict
Dictionary containing all report data.
"""
return {
"metadata": self.metadata.to_dict(),
"sections": [s.to_dict() for s in self.sections],
}
|
from_dict
classmethod
Create a ReportAssembler from a dictionary.
Reconstructs the structure but not the reportlet instances.
Use this for loading report metadata, not for full reconstruction.
Parameters
data : dict
Dictionary containing report data (as produced by to_dict).
Returns
ReportAssembler
Reconstructed instance.
Source code in tit/reporting/core/assembler.py
| @classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
"""Create a ReportAssembler from a dictionary.
Reconstructs the structure but **not** the reportlet instances.
Use this for loading report metadata, not for full reconstruction.
Parameters
----------
data : dict
Dictionary containing report data (as produced by ``to_dict``).
Returns
-------
ReportAssembler
Reconstructed instance.
"""
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
|
get_html_template
get_html_template(title: str, content: str, toc_html: str = '', metadata_html: str = '', footer_html: str = '', custom_css: str = '', custom_js: str = '') -> str
Generate a complete HTML document with the report content.
Parameters
title : str
The report title (used in <title> and the header <h1>).
content : str
The main HTML content (rendered sections).
toc_html : str, optional
Table of contents HTML fragment.
metadata_html : str, optional
Header metadata HTML fragment.
footer_html : str, optional
Footer HTML. Defaults to a TI-Toolbox credit line.
custom_css : str, optional
Additional CSS appended after the default styles.
custom_js : str, optional
Additional JavaScript appended after the default scripts.
Returns
str
Complete HTML document as a string.
Source code in tit/reporting/core/templates.py
| def get_html_template(
title: str,
content: str,
toc_html: str = "",
metadata_html: str = "",
footer_html: str = "",
custom_css: str = "",
custom_js: str = "",
) -> str:
"""Generate a complete HTML document with the report content.
Parameters
----------
title : str
The report title (used in ``<title>`` and the header ``<h1>``).
content : str
The main HTML content (rendered sections).
toc_html : str, optional
Table of contents HTML fragment.
metadata_html : str, optional
Header metadata HTML fragment.
footer_html : str, optional
Footer HTML. Defaults to a TI-Toolbox credit line.
custom_css : str, optional
Additional CSS appended after the default styles.
custom_js : str, optional
Additional JavaScript appended after the default scripts.
Returns
-------
str
Complete HTML document as a string.
"""
css = DEFAULT_CSS_STYLES
if custom_css:
css += f"\n/* Custom Styles */\n{custom_css}"
js = DEFAULT_JS_SCRIPTS
if custom_js:
js += f"\n// Custom Scripts\n{custom_js}"
nav_html = ""
if toc_html:
nav_html = f"""
<nav class="report-nav">
<h2>Contents</h2>
{toc_html}
</nav>
"""
footer = footer_html or """
<footer class="report-footer">
<p>Generated by <a href="https://github.com/idossha/TI-toolbox" target="_blank">TI-Toolbox</a></p>
</footer>
"""
return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title}</title>
<style>
{css}
</style>
</head>
<body>
<div class="report-container">
<header class="report-header">
<h1>{title}</h1>
{metadata_html}
</header>
{nav_html}
<main class="report-main">
{content}
</main>
{footer}
</div>
<script>
{js}
</script>
</body>
</html>
"""
|