HTML & Markdown Reporters¶
Reporters that generate validation reports in HTML and Markdown formats.
Profile and Validation render the supplied Core results without a separate public-report statistical filter. Hosting applications enforce access permissions outside the renderer; visibility must not change report content. All registered Core report themes are available through the corresponding renderer. Since 3.1.14, the SVG Profile renderer uses the selected theme's foreground for table/chart text and displays top-value frequencies as counts, not percentages. Profile column names, chart labels, patterns, recommendations and alerts are escaped as text. Interactive-chart JSON cannot close its surrounding script tag. Escaping preserves the input and displayed text; it does not redact report data.
The 3.1.14 renderer also preserves actual check names and execution retry counts in tables, escaping names as text rather than interpreting them as HTML. The 3.1.14 result-model correction preserves explicitly failed checks even when issue details are absent or redacted, without fabricating issues. Consumers must verify the installed package is at least 3.1.14. Profile and Validation consume different result types: equal themes do not imply equal sections or information. Truly empty results keep their existing semantics.
Profile tables, charts and cards share the same type-label precedence: the first
nonempty string among inferred_type, physical_type and legacy dtype, or
unknown when none is supplied. An explicitly supplied unknown is preserved.
This is presentation compatibility, not type inference or a change to input or
quality calculations; labels are still escaped as text.
In PDF output, long sections and tables may continue on another page. Chapter and section headings stay with following content; column cards that fit on a page stay together. Long column names wrap within table cells. These print-only layout rules preserve the screen theme, source values and report calculations.
For constant or singleton inputs, undefined skewness and kurtosis serialize as
JSON null, not a measured zero. Finite moments, other statistics and source
values remain unchanged; this is not a general non-finite-value sanitizer.
HTML Reporter¶
Basic Usage¶
from truthound.reporters import get_reporter
reporter = get_reporter("html")
html_output = reporter.render(run_result)
Configuration Options¶
HTMLReporterConfig provides the following options:
| Option | Type | Default | Description |
|---|---|---|---|
template |
str \| None |
None |
Custom Jinja2 template |
template_path |
str \| None |
None |
Template file path |
inline_css |
bool |
True |
Inline CSS in HTML |
include_js |
bool |
True |
Include JavaScript |
theme |
str |
"light" |
Theme ("light", "dark") |
include_charts |
bool |
True |
Include charts |
responsive |
bool |
True |
Responsive design |
Usage Examples¶
Basic HTML Report¶
from truthound.reporters import get_reporter
reporter = get_reporter("html")
html_output = reporter.render(result)
# Save to file
reporter.write(result, "validation_report.html")
Dark Theme¶
Custom Template¶
custom_template = """
<!DOCTYPE html>
<html>
<head><title>{{ title }}</title></head>
<body>
<h1>{{ result.data_asset }} Validation</h1>
<p>Status: {{ result.status }}</p>
<ul>
{% for issue in result.issues %}
<li>{{ issue.validator_name }}: {{ issue.message }}</li>
{% endfor %}
</ul>
</body>
</html>
"""
reporter = get_reporter("html", template=custom_template)
html_output = reporter.render(result)
Using Template File¶
reporter = get_reporter("html", template_path="templates/my_report.html")
html_output = reporter.render(result)
Default Template Features¶
The default HTML template includes:
- Summary Section: Overall statistics and status
- Severity Analysis: Donut chart (ApexCharts)
- Issues Table: Sortable/filterable table
- Detail Information: Detailed info for each validator result
- Responsive Design: Mobile optimized
Template Context Variables¶
Variables available in custom templates:
| Variable | Type | Description |
|---|---|---|
result |
RunPresentation |
Shared reporter presentation model |
title |
str |
Report title |
timestamp |
str |
Generation time |
config |
HTMLReporterConfig |
Reporter configuration |
statistics |
dict |
Statistics information |
Dependencies¶
HTML Reporter uses the Jinja2 template engine:
Markdown Reporter¶
Basic Usage¶
from truthound.reporters import get_reporter
reporter = get_reporter("markdown")
md_output = reporter.render(run_result)
Configuration Options¶
MarkdownReporterConfig provides the following options:
| Option | Type | Default | Description |
|---|---|---|---|
include_toc |
bool |
True |
Include table of contents |
heading_level |
int |
1 |
Starting heading level (1-6) |
include_badges |
bool |
True |
Include shields.io badges |
table_style |
str |
"github" |
Table style |
include_details |
bool |
True |
Include details section |
include_statistics |
bool |
True |
Include statistics section |
Usage Examples¶
Basic Markdown¶
from truthound.reporters import get_reporter
reporter = get_reporter("markdown")
md_output = reporter.render(result)
Output example:
# Validation Report


## Summary
| Metric | Value |
|--------|-------|
| Data Asset | customer_data.csv |
| Run ID | abc123-def456 |
| Status | FAILED |
| Total Validators | 10 |
| Passed | 8 |
| Failed | 2 |
| Pass Rate | 80.0% |
## Issues by Severity
| Severity | Count |
|----------|-------|
| Critical | 1 |
| High | 1 |
| Medium | 0 |
| Low | 0 |
## Failed Validations
### NullValidator - email
- **Severity**: critical
- **Count**: 5
- **Message**: Found 5 null values
### RangeValidator - age
- **Severity**: high
- **Count**: 3
- **Message**: 3 values out of range
Disable Badges¶
Disable Table of Contents¶
Adjust Heading Level¶
Heading level can be adjusted when embedding in documents:
shields.io Badges¶
When include_badges=True, the following badges are generated:
- Status Badge: Pass (green) / Fail (red)
- Pass Rate Badge: Color based on rate
- Issues Badge: Shows issue count



Table Reporter (SDK)¶
An ASCII/Unicode table reporter provided by the SDK.
Basic Usage¶
from truthound.reporters.sdk import TableReporter
reporter = TableReporter()
table_output = reporter.render(run_result)
Configuration Options¶
TableReporterConfig provides the following options:
| Option | Type | Default | Description |
|---|---|---|---|
style |
str |
"ascii" |
Table style |
include_passed |
bool |
False |
Include passed validators |
max_column_width |
int |
50 |
Maximum column width |
columns |
list[str] |
["validator", "column", "severity", "message"] |
Columns to display |
sort_by |
str \| None |
"severity" |
Sort by |
sort_ascending |
bool |
False |
Ascending sort |
Table Styles¶
ASCII (Default)¶
+------------+--------+----------+---------------------------+
| validator | column | severity | message |
+------------+--------+----------+---------------------------+
| NullValidator | email | critical | Found 5 null values |
| RangeValidator | age | high | 3 values out of range |
+------------+--------+----------+---------------------------+
Markdown¶
| validator | column | severity | message |
|------------|--------|----------|---------------------------|
| NullValidator | email | critical | Found 5 null values |
| RangeValidator | age | high | 3 values out of range |
Grid (Unicode)¶
╔════════════╤════════╤══════════╤═══════════════════════════╗
║ validator │ column │ severity │ message ║
╠════════════╪════════╪══════════╪═══════════════════════════╣
║ NullValidator │ email │ critical │ Found 5 null values ║
║ RangeValidator │ age │ high │ 3 values out of range ║
╚════════════╧════════╧══════════╧═══════════════════════════╝
Simple¶
validator column severity message
----------- ------ -------- ---------------------------
NullValidator email critical Found 5 null values
RangeValidator age high 3 values out of range
File Output¶
# Save HTML file
html_reporter = get_reporter("html")
html_reporter.write(result, "report.html")
# Save Markdown file
md_reporter = get_reporter("markdown")
md_reporter.write(result, "VALIDATION_REPORT.md")
API Reference¶
HTMLReporter¶
class HTMLReporter(ValidationReporter[HTMLReporterConfig]):
"""HTML format reporter (Jinja2-based)."""
name = "html"
file_extension = ".html"
content_type = "text/html"
def render(self, data: ValidationRunResult) -> str:
"""Render validation result as HTML."""
...
MarkdownReporter¶
class MarkdownReporter(ValidationReporter[MarkdownReporterConfig]):
"""Markdown format reporter."""
name = "markdown"
file_extension = ".md"
content_type = "text/markdown"
def render(self, data: ValidationRunResult) -> str:
"""Render validation result as Markdown."""
...