Skip to content

Commit 6124efc

Browse files
authored
Merge pull request #142 from posit-dev/feat-step-report-header-customize
feat: better customize header in step reports (through `.get_step_report(header=...)`
2 parents f09a8bb + 7fc14e3 commit 6124efc

File tree

2 files changed

+136
-79
lines changed

2 files changed

+136
-79
lines changed

pointblank/validate.py

Lines changed: 127 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8886,9 +8886,12 @@ def get_step_report(
88868886
column selector expressions don't resolve to any columns.
88878887
header
88888888
Options for customizing the header of the step report. The default is the `":default:"`
8889-
value which produces a generic header. Aside from this default, text can be provided for
8890-
the header. This will be interpreted as Markdown text and transformed internally to
8891-
HTML.
8889+
value which produces a header with a standard title and set of details underneath. Aside
8890+
from this default, free text can be provided for the header. This will be interpreted as
8891+
Markdown text and transformed internally to HTML. You can provide one of two templating
8892+
elements: `{title}` and `{details}`. The default header has the template
8893+
`"{title}{details}"` so you can easily start from that and modify as you see fit. If you
8894+
don't want a header at all, you can set `header=None` to remove it entirely.
88928895
limit
88938896
The number of rows to display for those validation steps that check values in rows (the
88948897
`col_vals_*()` validation steps). The default is `10` rows and the limit can be removed
@@ -10362,7 +10365,12 @@ def _step_report_row_based(
1036210365
else:
1036310366
step_report = tbl_preview
1036410367

10365-
if header == ":default:":
10368+
# TODO: localize all text fragments according to `lang=` parameter
10369+
10370+
if header is None:
10371+
pass
10372+
10373+
elif header == ":default:":
1036610374
step_report = step_report.tab_header(
1036710375
title=html(f"Report for Validation Step {i} {CHECK_MARK_SPAN}"),
1036810376
subtitle=html(
@@ -10435,27 +10443,41 @@ def _step_report_row_based(
1043510443
not_shown = " (NOT SHOWN)"
1043610444
shown_failures = ""
1043710445

10446+
title = f"Report for Validation Step {i}"
10447+
details = (
10448+
"<div style='font-size: 13.6px;'>"
10449+
"<div style='padding-top: 7px;'>"
10450+
"ASSERTION <span style='border-style: solid; border-width: thin; "
10451+
"border-color: lightblue; padding-left: 2px; padding-right: 2px;'>"
10452+
f"<code style='color: #303030;'>{text}</code></span>"
10453+
"</div>"
10454+
"<div style='padding-top: 7px;'>"
10455+
f"<strong>{n_failed}</strong> / "
10456+
f"<strong>{n}</strong> TEST UNIT FAILURES "
10457+
f"IN COLUMN <strong>{column_position}</strong>{not_shown}"
10458+
"</div>"
10459+
f"<div>EXTRACT OF {extract_of_x_rows} "
10460+
f"<strong>{extract_length_resolved}</strong> ROWS {shown_failures}:"
10461+
"</div>"
10462+
"</div>"
10463+
)
10464+
10465+
# If `header` is None then don't add a header and just return the step report
10466+
if header is None:
10467+
return step_report
10468+
10469+
# Generate the default template text for the header when `":default:"` is used
1043810470
if header == ":default:":
10439-
step_report = step_report.tab_header(
10440-
title=f"Report for Validation Step {i}",
10441-
subtitle=html(
10442-
"<div>"
10443-
"ASSERTION <span style='border-style: solid; border-width: thin; "
10444-
"border-color: lightblue; padding-left: 2px; padding-right: 2px;'>"
10445-
f"<code style='color: #303030;'>{text}</code></span><br>"
10446-
f"<div style='padding-top: 3px;'><strong>{n_failed}</strong> / "
10447-
f"<strong>{n}</strong> TEST UNIT FAILURES "
10448-
f"IN COLUMN <strong>{column_position}</strong>{not_shown}</div>"
10449-
f"<div style='padding-top: 10px;'>EXTRACT OF {extract_of_x_rows} "
10450-
f"<strong>{extract_length_resolved}</strong> ROWS {shown_failures}:"
10451-
"</div></div>"
10452-
),
10453-
)
10471+
header = "{title}{details}"
1045410472

10455-
else:
10456-
step_report = step_report.tab_header(
10457-
title=md(header),
10458-
)
10473+
# Use commonmark to convert the header text to HTML
10474+
header = commonmark.commonmark(header)
10475+
10476+
# Place any templated text in the header
10477+
header = header.format(title=title, details=details)
10478+
10479+
# Create the header with `header` string
10480+
step_report = step_report.tab_header(title=md(header))
1045910481

1046010482
return step_report
1046110483

@@ -10620,23 +10642,6 @@ def _step_report_schema_in_order(
1062010642
if debug_return_df:
1062110643
return schema_combined
1062210644

10623-
# Get the other parameters for the `col_schema_match()` function
10624-
case_sensitive_colnames = schema_info["params"]["case_sensitive_colnames"]
10625-
case_sensitive_dtypes = schema_info["params"]["case_sensitive_dtypes"]
10626-
full_match_dtypes = schema_info["params"]["full_match_dtypes"]
10627-
10628-
# Generate text for the `col_schema_match()` parameters
10629-
col_schema_match_params_html = _create_col_schema_match_params_html(
10630-
complete=complete,
10631-
in_order=True,
10632-
case_sensitive_colnames=case_sensitive_colnames,
10633-
case_sensitive_dtypes=case_sensitive_dtypes,
10634-
full_match_dtypes=full_match_dtypes,
10635-
)
10636-
10637-
# Get the passing symbol for the step
10638-
passing_symbol = CHECK_MARK_SPAN if all_passed else CROSS_MARK_SPAN
10639-
1064010645
step_report = (
1064110646
GT(schema_combined, id="pb_step_tbl")
1064210647
.fmt_markdown(columns=None)
@@ -10725,15 +10730,6 @@ def _step_report_schema_in_order(
1072510730
.tab_options(source_notes_font_size="12px")
1072610731
)
1072710732

10728-
if header == ":default:":
10729-
step_report = step_report.tab_header(
10730-
title=html(f"Report for Validation Step {step} {passing_symbol}"),
10731-
subtitle=html(col_schema_match_params_html),
10732-
)
10733-
10734-
else:
10735-
step_report = step_report.tab_header(title=md(header))
10736-
1073710733
if schema_length == "shorter":
1073810734
# Add background color to the missing column on the exp side
1073910735
step_report = step_report.tab_style(
@@ -10774,6 +10770,43 @@ def _step_report_schema_in_order(
1077410770
if version("great_tables") >= "0.17.0":
1077510771
step_report = step_report.tab_options(quarto_disable_processing=True)
1077610772

10773+
# If `header` is None then don't add a header and just return the step report
10774+
if header is None:
10775+
return step_report
10776+
10777+
# Get the other parameters for the `col_schema_match()` function
10778+
case_sensitive_colnames = schema_info["params"]["case_sensitive_colnames"]
10779+
case_sensitive_dtypes = schema_info["params"]["case_sensitive_dtypes"]
10780+
full_match_dtypes = schema_info["params"]["full_match_dtypes"]
10781+
10782+
# Get the passing symbol for the step
10783+
passing_symbol = CHECK_MARK_SPAN if all_passed else CROSS_MARK_SPAN
10784+
10785+
# Generate the title for the step report
10786+
title = f"Report for Validation Step {step} {passing_symbol}"
10787+
10788+
# Generate the details for the step report
10789+
details = _create_col_schema_match_params_html(
10790+
complete=complete,
10791+
in_order=True,
10792+
case_sensitive_colnames=case_sensitive_colnames,
10793+
case_sensitive_dtypes=case_sensitive_dtypes,
10794+
full_match_dtypes=full_match_dtypes,
10795+
)
10796+
10797+
# Generate the default template text for the header when `":default:"` is used
10798+
if header == ":default:":
10799+
header = "{title}{details}"
10800+
10801+
# Use commonmark to convert the header text to HTML
10802+
header = commonmark.commonmark(header)
10803+
10804+
# Place any templated text in the header
10805+
header = header.format(title=title, details=details)
10806+
10807+
# Create the header with `header` string
10808+
step_report = step_report.tab_header(title=md(header))
10809+
1077710810
return step_report
1077810811

1077910812

@@ -11039,23 +11072,6 @@ def _step_report_schema_any_order(
1103911072
if debug_return_df:
1104011073
return schema_combined
1104111074

11042-
# Get the other parameters for the `col_schema_match()` function
11043-
case_sensitive_colnames = schema_info["params"]["case_sensitive_colnames"]
11044-
case_sensitive_dtypes = schema_info["params"]["case_sensitive_dtypes"]
11045-
full_match_dtypes = schema_info["params"]["full_match_dtypes"]
11046-
11047-
# Generate text for the `col_schema_match()` parameters
11048-
col_schema_match_params_html = _create_col_schema_match_params_html(
11049-
complete=complete,
11050-
in_order=False,
11051-
case_sensitive_colnames=case_sensitive_colnames,
11052-
case_sensitive_dtypes=case_sensitive_dtypes,
11053-
full_match_dtypes=full_match_dtypes,
11054-
)
11055-
11056-
# Get the passing symbol for the step
11057-
passing_symbol = CHECK_MARK_SPAN if all_passed else CROSS_MARK_SPAN
11058-
1105911075
step_report = (
1106011076
GT(schema_combined, id="pb_step_tbl")
1106111077
.fmt_markdown(columns=None)
@@ -11145,15 +11161,6 @@ def _step_report_schema_any_order(
1114511161
.tab_options(source_notes_font_size="12px")
1114611162
)
1114711163

11148-
if header == ":default:":
11149-
step_report = step_report.tab_header(
11150-
title=html(f"Report for Validation Step {step} {passing_symbol}"),
11151-
subtitle=html(col_schema_match_params_html),
11152-
)
11153-
11154-
else:
11155-
step_report = step_report.tab_header(title=md(header))
11156-
1115711164
# Add background color to signify limits of target table schema (on LHS side)
1115811165
if len(colnames_exp_unmatched) > 0:
1115911166
step_report = step_report.tab_style(
@@ -11172,6 +11179,43 @@ def _step_report_schema_any_order(
1117211179
if version("great_tables") >= "0.17.0":
1117311180
step_report = step_report.tab_options(quarto_disable_processing=True)
1117411181

11182+
# If `header` is None then don't add a header and just return the step report
11183+
if header is None:
11184+
return step_report
11185+
11186+
# Get the other parameters for the `col_schema_match()` function
11187+
case_sensitive_colnames = schema_info["params"]["case_sensitive_colnames"]
11188+
case_sensitive_dtypes = schema_info["params"]["case_sensitive_dtypes"]
11189+
full_match_dtypes = schema_info["params"]["full_match_dtypes"]
11190+
11191+
# Get the passing symbol for the step
11192+
passing_symbol = CHECK_MARK_SPAN if all_passed else CROSS_MARK_SPAN
11193+
11194+
# Generate the title for the step report
11195+
title = f"Report for Validation Step {step} {passing_symbol}"
11196+
11197+
# Generate the details for the step report
11198+
details = _create_col_schema_match_params_html(
11199+
complete=complete,
11200+
in_order=False,
11201+
case_sensitive_colnames=case_sensitive_colnames,
11202+
case_sensitive_dtypes=case_sensitive_dtypes,
11203+
full_match_dtypes=full_match_dtypes,
11204+
)
11205+
11206+
# Generate the default template text for the header when `":default:"` is used
11207+
if header == ":default:":
11208+
header = "{title}{details}"
11209+
11210+
# Use commonmark to convert the header text to HTML
11211+
header = commonmark.commonmark(header)
11212+
11213+
# Place any templated text in the header
11214+
header = header.format(title=title, details=details)
11215+
11216+
# Create the header with `header` string
11217+
step_report = step_report.tab_header(title=md(header))
11218+
1117511219
return step_report
1117611220

1117711221

@@ -11247,7 +11291,12 @@ def _create_col_schema_match_params_html(
1124711291
)
1124811292

1124911293
return (
11250-
'<div style="display: flex;"><div style="margin-right: 5px;">COLUMN SCHEMA MATCH</div>'
11251-
f"{complete_text}{in_order_text}{case_sensitive_colnames_text}{case_sensitive_dtypes_text}"
11252-
f"{full_match_dtypes_text}</div>"
11294+
'<div style="display: flex; font-size: 13.7px; padding-top: 7px;">'
11295+
'<div style="margin-right: 5px;">COLUMN SCHEMA MATCH</div>'
11296+
f"{complete_text}"
11297+
f"{in_order_text}"
11298+
f"{case_sensitive_colnames_text}"
11299+
f"{case_sensitive_dtypes_text}"
11300+
f"{full_match_dtypes_text}"
11301+
"</div>"
1125311302
)

tests/test_validate.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6640,10 +6640,18 @@ def test_get_step_report_no_fail(tbl_type):
66406640
for i in range(1, 18):
66416641
assert isinstance(validation.get_step_report(i=i, limit=None), GT.GT)
66426642

6643-
# Test with a custom header
6643+
# Test with a custom header using static text
66446644
for i in range(1, 18):
66456645
assert isinstance(validation.get_step_report(i=i, header="Custom header"), GT.GT)
66466646

6647+
# Test with a custom header using templating elements
6648+
for i in range(1, 18):
6649+
assert isinstance(validation.get_step_report(i=i, header="Title {title} {details}"), GT.GT)
6650+
6651+
# Test with header removal
6652+
for i in range(1, 18):
6653+
assert isinstance(validation.get_step_report(i=i, header=None), GT.GT)
6654+
66476655
#
66486656
# Tests with a subset of columns
66496657
#

0 commit comments

Comments
 (0)