Skip to content

Commit b3fb756

Browse files
authored
Merge pull request #22 from rich-iannone/feat-preview-tbl-extras
feat: add option for info header in `preview()` table
2 parents 96b7680 + 0285bb1 commit b3fb756

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

pointblank/preview.py

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from pointblank.column import Column
99
from pointblank.schema import Schema
10+
from pointblank.validate import _create_table_type_html, _create_table_dims_html
1011
from pointblank._utils import _get_tbl_type, _check_any_df_lib, _select_df_lib
1112

1213
__all__ = ["preview"]
@@ -19,6 +20,7 @@ def preview(
1920
n_tail: int = 5,
2021
limit: int | None = 50,
2122
show_row_numbers: bool = True,
23+
incl_header: bool = True,
2224
max_col_width: int | None = 250,
2325
) -> GT:
2426
"""
@@ -59,6 +61,9 @@ def preview(
5961
show_row_numbers
6062
Should row numbers be shown? The numbers shown reflect the row numbers of the head and tail
6163
in the full table.
64+
incl_header
65+
Should the table include a header with the table type and table dimensions? Set to `True` by
66+
default.
6267
max_col_width
6368
The maximum width of the columns in pixels. This is `250` (`"250px"`) by default.
6469
@@ -176,6 +181,12 @@ def preview(
176181
if pl_pb_tbl:
177182
df_lib_name_gt = "polars" if "polars" in tbl_type else "pandas"
178183

184+
# Handle imports of Polars or Pandas here
185+
if df_lib_name_gt == "polars":
186+
import polars as pl
187+
else:
188+
import pandas as pd
189+
179190
# If `columns_subset=` is not None, resolve the columns to display
180191
if columns_subset is not None:
181192

@@ -240,8 +251,6 @@ def preview(
240251

241252
if tbl_type == "polars":
242253

243-
import polars as pl
244-
245254
n_rows = int(data.height)
246255

247256
# If n_head + n_tail is greater than the row count, display the entire table
@@ -257,8 +266,6 @@ def preview(
257266

258267
if tbl_type == "pandas":
259268

260-
import pandas as pd
261-
262269
n_rows = data.shape[0]
263270

264271
# If n_head + n_tail is greater than the row count, display the entire table
@@ -289,6 +296,16 @@ def preview(
289296
k: v.split("(")[0] if "(" in v else v for k, v in col_dtype_dict.items()
290297
}
291298

299+
# Create a dictionary of column and row positions where the value is None/NA/NULL
300+
# This is used to highlight these values in the table
301+
if df_lib_name_gt == "polars":
302+
none_values = {k: data[k].is_null().to_list() for k in col_names}
303+
else:
304+
none_values = {k: data[k].isnull() for k in col_names}
305+
306+
none_values = [(k, i) for k, v in none_values.items() for i, val in enumerate(v) if val]
307+
308+
# Import Great Tables to get preliminary renders of the columns
292309
import great_tables as gt
293310

294311
# For each of the columns get the average number of characters printed for each of the values
@@ -342,8 +359,6 @@ def preview(
342359

343360
if df_lib_name_gt == "pandas":
344361

345-
import pandas as pd
346-
347362
data.insert(0, "_row_num_", row_number_list)
348363

349364
# Get the highest number in the `row_number_list` and calculate a width that will
@@ -356,9 +371,29 @@ def preview(
356371
# Update the col_dtype_labels_dict to include the row number column (use empty string)
357372
col_dtype_labels_dict = {"_row_num_": ""} | col_dtype_labels_dict
358373

374+
# Create the label, table type, and thresholds HTML fragments
375+
table_type_html = _create_table_type_html(
376+
tbl_type=tbl_type, tbl_name=None, font_size="10px"
377+
)
378+
379+
tbl_dims_html = _create_table_dims_html(
380+
columns=len(col_names), rows=n_rows, font_size="10px"
381+
)
382+
383+
# Compose the subtitle HTML fragment
384+
combined_subtitle = (
385+
"<div>"
386+
'<div style="padding-top: 0; padding-bottom: 7px;">'
387+
f"{table_type_html}"
388+
f"{tbl_dims_html}"
389+
"</div>"
390+
"</div>"
391+
)
392+
359393
gt_tbl = (
360394
GT(data=data, id="pb_preview_tbl")
361395
.opt_table_font(font=google_font(name="IBM Plex Sans"))
396+
.opt_align_table_header(align="left")
362397
.fmt_markdown(columns=col_names)
363398
.tab_style(
364399
style=style.css(
@@ -393,6 +428,23 @@ def preview(
393428
.cols_width(cases=col_width_dict)
394429
)
395430

431+
if incl_header:
432+
gt_tbl = gt_tbl.tab_header(title=html(combined_subtitle))
433+
gt_tbl = gt_tbl.tab_options(heading_subtitle_font_size="12px")
434+
435+
if none_values:
436+
for column, none_index in none_values:
437+
gt_tbl = gt_tbl.tab_style(
438+
style=[style.text(color="#B22222"), style.fill(color="#FFC1C159")],
439+
locations=loc.body(rows=none_index, columns=column),
440+
)
441+
442+
if tbl_type == "pandas":
443+
gt_tbl = gt_tbl.sub_missing(missing_text="NA")
444+
445+
if ibis_tbl:
446+
gt_tbl = gt_tbl.sub_missing(missing_text="NULL")
447+
396448
if not full_dataset:
397449

398450
gt_tbl = gt_tbl.tab_style(

pointblank/validate.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5651,7 +5651,9 @@ def _create_label_html(label: str | None, start_time: str) -> str:
56515651
)
56525652

56535653

5654-
def _create_table_type_html(tbl_type: str | None, tbl_name: str | None) -> str:
5654+
def _create_table_type_html(
5655+
tbl_type: str | None, tbl_name: str | None, font_size: str = "smaller"
5656+
) -> str:
56555657

56565658
if tbl_type is None:
56575659
return ""
@@ -5665,7 +5667,7 @@ def _create_table_type_html(tbl_type: str | None, tbl_name: str | None) -> str:
56655667
return (
56665668
f"<span style='background-color: {style['background']}; color: {style['text']}; padding: 0.5em 0.5em; "
56675669
f"position: inherit; text-transform: uppercase; margin: 5px 10px 5px 0px; border: solid 1px {style['background']}; "
5668-
f"font-weight: bold; padding: 2px 10px 2px 10px; font-size: smaller;'>{style['label']}</span>"
5670+
f"font-weight: bold; padding: 2px 10px 2px 10px; font-size: {font_size};'>{style['label']}</span>"
56695671
)
56705672

56715673
return (
@@ -5678,6 +5680,26 @@ def _create_table_type_html(tbl_type: str | None, tbl_name: str | None) -> str:
56785680
)
56795681

56805682

5683+
def _create_table_dims_html(columns: int, rows: int, font_size: str = "10px") -> str:
5684+
5685+
return (
5686+
f"<span style='background-color: #eecbff; color: #333333; padding: 0.5em 0.5em; "
5687+
f"position: inherit; text-transform: uppercase; margin: 5px 0px 5px 5px; "
5688+
f"font-weight: bold; border: solid 1px #eecbff; padding: 2px 15px 2px 15px; "
5689+
f"font-size: {font_size};'>Rows</span>"
5690+
f"<span style='background-color: none; color: #333333; padding: 0.5em 0.5em; "
5691+
f"position: inherit; margin: 5px 0px 5px -4px; font-weight: bold; "
5692+
f"border: solid 1px #eecbff; padding: 2px 15px 2px 15px; font-size: {font_size};'>{rows}</span>"
5693+
f"<span style='background-color: #BDE7B4; color: #333333; padding: 0.5em 0.5em; "
5694+
f"position: inherit; text-transform: uppercase; margin: 5px 0px 5px 3px; "
5695+
f"font-weight: bold; border: solid 1px #BDE7B4; padding: 2px 15px 2px 15px; "
5696+
f"font-size: {font_size};'>Columns</span>"
5697+
f"<span style='background-color: none; color: #333333; padding: 0.5em 0.5em; "
5698+
f"position: inherit; margin: 5px 0px 5px -4px; font-weight: bold; "
5699+
f"border: solid 1px #BDE7B4; padding: 2px 15px 2px 15px; font-size: {font_size};'>{columns}</span>"
5700+
)
5701+
5702+
56815703
def _create_thresholds_html(thresholds: Thresholds) -> str:
56825704

56835705
if thresholds == Thresholds():

0 commit comments

Comments
 (0)