diff --git a/src/dt_browser/browser.py b/src/dt_browser/browser.py index 4574d7d..46947d5 100644 --- a/src/dt_browser/browser.py +++ b/src/dt_browser/browser.py @@ -28,6 +28,7 @@ SelectFromTable, ) from dt_browser.bookmarks import Bookmarks +from dt_browser.column_metadata import ColumnMetadata from dt_browser.column_selector import ColumnSelector from dt_browser.custom_table import CustomTable, _color_name, polars_list_to_string from dt_browser.expression_box import ExpressionBox @@ -248,9 +249,8 @@ def compute_active_search_idx_display(self): class RowDetail(Widget, can_focus=False, can_focus_children=False): DEFAULT_CSS = """ RowDetail { - width: auto; - max_width: 50%; - min_width: 30%; + width: 100%; + height: 1fr; padding: 0 1; border: tall $primary; } @@ -288,14 +288,43 @@ def watch_row_df(self): assert self._schema is not None display_df = display_df.join(self._schema, on=["Field"]).select(["Field", "dtype", "Value"]) self._dt.set_dt(display_df, display_df.with_row_index(name=INDEX_COL).select([INDEX_COL])) - self.styles.width = self._dt.virtual_size.width + self.gutter.width + 1 self._dt.refresh() + if isinstance(self.parent, DetailPanel): + self.parent.update_width() # self._dt.go_to_cell(coord) + @property + def content_width(self) -> int: + return self._dt.virtual_size.width + self.gutter.width + 1 + def compose(self): yield self._dt +class DetailPanel(Widget, can_focus=False): + DEFAULT_CSS = """ +DetailPanel { + max-width: 50%; + min-width: 30%; + layout: vertical; +} +""" + + def __init__(self, row_detail: RowDetail, column_metadata: ColumnMetadata, *args, **kwargs): + super().__init__(*args, **kwargs) + self._row_detail = row_detail + self._column_metadata = column_metadata + + def update_width(self) -> None: + row_detail_width = self._row_detail.content_width if not self._row_detail.row_df.is_empty() else 0 + meta_width = self._column_metadata.content_size.width + self._column_metadata.gutter.width + self.styles.width = max(row_detail_width, meta_width) + + def compose(self): + yield self._row_detail + yield self._column_metadata + + def from_file_path(path: pathlib.Path, has_header: bool = True) -> pl.DataFrame: if path.suffix in [".arrow", ".feather"]: @@ -342,6 +371,7 @@ class DtBrowser(Widget): # pylint: disable=too-many-public-methods,too-many-ins current_filter = reactive[str | None](None) cur_row = reactive(0) + cur_col = reactive(0) cur_total_rows = reactive(0) total_rows = reactive(0) @@ -400,6 +430,9 @@ def __init__( self._ts_col_selector.styles.width = 1 self._row_detail = RowDetail() + self._column_metadata = ColumnMetadata() + self._column_metadata.set_source_df(self._filtered_dt) + self._detail_panel = DetailPanel(self._row_detail, self._column_metadata) self._color_by_cache: LRUCache[tuple[str, ...], pl.Series] = LRUCache(5) self._last_message_ts = time.time() @@ -623,10 +656,10 @@ async def action_show_save(self): async def watch_show_row_detail(self): if not self.show_row_detail: - if existing := self.query(RowDetail): + if existing := self.query(DetailPanel): existing.remove() elif not self._display_dt.is_empty(): - await self.query_one("#main_hori", Horizontal).mount(self._row_detail) + await self.query_one("#main_hori", Horizontal).mount(self._detail_panel) async def action_show_bookmarks(self): await self.mount(self._bookmarks, before=self.query_one(TableFooter)) @@ -644,6 +677,8 @@ async def action_timestamp_selector(self): def _set_filtered_dt(self, filtered_dt: pl.DataFrame, filtered_meta: pl.DataFrame, **kwargs): self._filtered_dt = filtered_dt self._meta_dt = filtered_meta + self._column_metadata.set_source_df(self._filtered_dt) + self._column_metadata.invalidate_cache() self._set_active_dt(self._filtered_dt, **kwargs) def _set_active_dt(self, active_dt: pl.DataFrame, new_row: int | None = None): @@ -714,10 +749,20 @@ def enable_select_from_table(self, event: SelectFromTable): @on(CustomTable.CellHighlighted, selector="#main_table") async def handle_cell_highlight(self, event: CustomTable.CellHighlighted): self.cur_row = event.coordinate.row + col = event.coordinate.column + if col != self.cur_col: + self.cur_col = col def watch_cur_row(self): self._row_detail.row_df = self._display_dt[self.cur_row] + def watch_cur_col(self): + if self._display_dt.is_empty() or self.cur_col >= len(self._display_dt.columns): + return + col_name = self._display_dt.columns[self.cur_col] + dtype = self._display_dt.schema[col_name] + self._column_metadata.column_info = (col_name, dtype) + @on(CustomTable.CellSelected, selector="#main_table") def handle_cell_select(self, event: CustomTable.CellSelected): if self._select_interest: @@ -857,6 +902,9 @@ def on_mount(self): self.cur_total_rows = len(self._display_dt) self.total_rows = len(self._original_dt) self._row_detail.row_df = self._display_dt[0] + if not self._display_dt.is_empty(): + col_name = self._display_dt.columns[0] + self._column_metadata.column_info = (col_name, self._display_dt.schema[col_name]) if self.removed_cols: err_str = ", ".join(f"{k}: {v}" for k, v in self.removed_cols.items()) self.notify( diff --git a/src/dt_browser/column_metadata.py b/src/dt_browser/column_metadata.py new file mode 100644 index 0000000..8d10f95 --- /dev/null +++ b/src/dt_browser/column_metadata.py @@ -0,0 +1,134 @@ +import polars as pl +from rich.table import Table as RichTable +from textual import work +from textual.reactive import reactive +from textual.widget import Widget +from textual.widgets import Static + + +def _categorical_stats(series: pl.Series) -> list[tuple[str, str]]: + n_unique = series.n_unique() + stats: list[tuple[str, str]] = [("Unique values", str(n_unique))] + val_col = series.name + vc = series.value_counts().sort(["count", val_col], descending=[True, False]).head(10) + for row in vc.iter_rows(named=True): + stats.append((f" {row[val_col]}", str(row["count"]))) + return stats + + +def _numeric_stats(series: pl.Series) -> list[tuple[str, str]]: + s = series.drop_nulls() + if s.is_empty(): + return [("", "No data")] + stats = [ + ("Min", str(s.min())), + ("Q1", str(s.quantile(0.25))), + ("Median", str(s.median())), + ("Q3", str(s.quantile(0.75))), + ("Max", str(s.max())), + ] + if s.dtype.is_float(): + nan_count = s.is_nan().sum() + if nan_count > 0: + stats.append(("NaN", str(nan_count))) + return stats + + +def _temporal_stats(series: pl.Series) -> list[tuple[str, str]]: + s = series.drop_nulls() + if s.is_empty(): + return [("", "No data")] + return [ + ("Min", str(s.min())), + ("Max", str(s.max())), + ] + + +def _boolean_stats(series: pl.Series) -> list[tuple[str, str]]: + true_count = series.sum() + null_count = series.null_count() + false_count = len(series) - (true_count or 0) - null_count + return [ + ("True", str(true_count)), + ("False", str(false_count)), + ] + + +def compute_column_stats(series: pl.Series) -> list[tuple[str, str]]: + dtype = series.dtype + if dtype == pl.Categorical: + stats = _categorical_stats(series) + elif dtype.is_numeric(): + stats = _numeric_stats(series) + elif dtype.is_temporal(): + stats = _temporal_stats(series) + elif dtype.is_(pl.Boolean): + stats = _boolean_stats(series) + else: + return [] + null_count = series.null_count() + if null_count > 0: + stats.append(("Null", str(null_count))) + return stats + + +class ColumnMetadata(Widget, can_focus=False, can_focus_children=False): + DEFAULT_CSS = """ +ColumnMetadata { + width: 100%; + height: auto; + padding: 0 1; + border: tall $primary; +} +""" + column_info: reactive[tuple[str, pl.DataType] | None] = reactive(None) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.border_title = "Column Metadata" + self._source_df: pl.DataFrame = pl.DataFrame() + self._cache: dict[str, list[tuple[str, str]]] = {} + self._static = Static("") + + def set_source_df(self, df: pl.DataFrame) -> None: + self._source_df = df + + def invalidate_cache(self) -> None: + self._cache.clear() + + def _render_stats(self, col_name: str, stats: list[tuple[str, str]]) -> None: + self.border_title = f"Column: {col_name}" + if not stats: + self._static.update("") + return + table = RichTable(show_header=False, box=None, padding=(0, 1), expand=True) + table.add_column("Stat", no_wrap=True) + table.add_column("Value", no_wrap=True, justify="right") + for label, value in stats: + table.add_row(label, value) + self._static.update(table) + if self.parent is not None and hasattr(self.parent, "update_width"): + self.parent.update_width() + + def watch_column_info(self) -> None: + if self.column_info is None or self._source_df.is_empty(): + return + col_name, _ = self.column_info + if col_name not in self._source_df.columns: + return + if col_name in self._cache: + self._render_stats(col_name, self._cache[col_name]) + else: + self.border_title = f"Column: {col_name}" + self._static.update("Computing...") + self._compute_stats(col_name) + + @work(exclusive=True) + async def _compute_stats(self, col_name: str) -> None: + series = self._source_df[col_name] + stats = compute_column_stats(series) + self._cache[col_name] = stats + self._render_stats(col_name, stats) + + def compose(self): + yield self._static diff --git a/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_boolean.svg b/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_boolean.svg new file mode 100644 index 0000000..2fba790 --- /dev/null +++ b/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_boolean.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DtBrowserApp + + + + + + + + + +  Row # id score category active  name                                             ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +     1  0   0.0 cat_0    true    item_0                                            Field    dtype       Value      +     2  1   1.5 cat_1    false   item_1                                            Row #    UInt32      1          +     3  2   3.0 cat_2    false   item_2                                            id       Int64       0          +     4  3   4.5 cat_3    true    item_3                                            score    Float64     0.0        +     5  4   6.0 cat_4    false   item_4                                            category Categorical cat_0      +     6  5   7.5 cat_0    false   item_5                                            active   Boolean     true       +     7  6   9.0 cat_1    true    item_6                                            name     String      item_0     +     8  7  10.5 cat_2    false   item_7                                            +     9  8  12.0 cat_3    false   item_8                                            +    10  9  13.5 cat_4    true    item_9                                            +    11 10  15.0 cat_0    false   item_10                                           +    12 11  16.5 cat_1    false   item_11                                           +    13 12  18.0 cat_2    true    item_12                                           +    14 13  19.5 cat_3    false   item_13                                           +    15 14  21.0 cat_4    false   item_14                                           +    16 15  22.5 cat_0    true    item_15                                           +    17 16  24.0 cat_1    false   item_16                                           +    18 17  25.5 cat_2    false   item_17                                           +    19 18  27.0 cat_3    true    item_18                                           +    20 19  28.5 cat_4    false   item_19                                           +    21 20  30.0 cat_0    false   item_20                                           +    22 21  31.5 cat_1    true    item_21                                           +    23 22  33.0 cat_2    false   item_22                                           +    24 23  34.5 cat_3    false   item_23                                          ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +    25 24  36.0 cat_4    true    item_24                                          ▔ Column: active ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    26 25  37.5 cat_0    false   item_25                                           True                        10  +    27 26  39.0 cat_1    false   item_26                                          ▆▆ False                       20  +    28 27  40.5 cat_2    true    item_27                                          ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette + + + diff --git a/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_categorical.svg b/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_categorical.svg new file mode 100644 index 0000000..06ad954 --- /dev/null +++ b/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_categorical.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DtBrowserApp + + + + + + + + + +  Row # id score category active  name                                             ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +     1  0   0.0 cat_0    true    item_0                                            Field    dtype       Value      +     2  1   1.5 cat_1    false   item_1                                            Row #    UInt32      1          +     3  2   3.0 cat_2    false   item_2                                            id       Int64       0          +     4  3   4.5 cat_3    true    item_3                                            score    Float64     0.0        +     5  4   6.0 cat_4    false   item_4                                            category Categorical cat_0      +     6  5   7.5 cat_0    false   item_5                                            active   Boolean     true       +     7  6   9.0 cat_1    true    item_6                                            name     String      item_0     +     8  7  10.5 cat_2    false   item_7                                            +     9  8  12.0 cat_3    false   item_8                                            +    10  9  13.5 cat_4    true    item_9                                            +    11 10  15.0 cat_0    false   item_10                                           +    12 11  16.5 cat_1    false   item_11                                           +    13 12  18.0 cat_2    true    item_12                                           +    14 13  19.5 cat_3    false   item_13                                           +    15 14  21.0 cat_4    false   item_14                                           +    16 15  22.5 cat_0    true    item_15                                           +    17 16  24.0 cat_1    false   item_16                                           +    18 17  25.5 cat_2    false   item_17                                           +    19 18  27.0 cat_3    true    item_18                                           +    20 19  28.5 cat_4    false   item_19                                          ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +    21 20  30.0 cat_0    false   item_20                                          ▔ Column: category ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    22 21  31.5 cat_1    true    item_21                                           Unique values                5  +    23 22  33.0 cat_2    false   item_22                                             cat_0                      6  +    24 23  34.5 cat_3    false   item_23                                             cat_1                      6  +    25 24  36.0 cat_4    true    item_24                                             cat_2                      6  +    26 25  37.5 cat_0    false   item_25                                             cat_3                      6  +    27 26  39.0 cat_1    false   item_26                                          ▆▆   cat_4                      6  +    28 27  40.5 cat_2    true    item_27                                          ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette + + + diff --git a/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_numeric.svg b/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_numeric.svg new file mode 100644 index 0000000..9710c26 --- /dev/null +++ b/tests/__snapshots__/test_column_metadata/test_snap_column_metadata_numeric.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DtBrowserApp + + + + + + + + + +  Row # id score category active  name                     ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +     1  0   0.0 cat_0    true    item_0                    Field    dtype       Value                              +     2  1   1.5 cat_1    false   item_1                    Row #    UInt32      1                                  +     3  2   3.0 cat_2    false   item_2                    id       Int64       0                                  +     4  3   4.5 cat_3    true    item_3                    score    Float64     0.0                                +     5  4   6.0 cat_4    false   item_4                    category Categorical cat_0                              +     6  5   7.5 cat_0    false   item_5                    active   Boolean     true                               +     7  6   9.0 cat_1    true    item_6                    name     String      item_0                             +     8  7  10.5 cat_2    false   item_7                    +     9  8  12.0 cat_3    false   item_8                    +    10  9  13.5 cat_4    true    item_9                    +    11 10  15.0 cat_0    false   item_10                   +    12 11  16.5 cat_1    false   item_11                   +    13 12  18.0 cat_2    true    item_12                   +    14 13  19.5 cat_3    false   item_13                   +    15 14  21.0 cat_4    false   item_14                   +    16 15  22.5 cat_0    true    item_15                   +    17 16  24.0 cat_1    false   item_16                   +    18 17  25.5 cat_2    false   item_17                   +    19 18  27.0 cat_3    true    item_18                   +    20 19  28.5 cat_4    false   item_19                   +    21 20  30.0 cat_0    false   item_20                  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +    22 21  31.5 cat_1    true    item_21                  ▔ Column: id ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    23 22  33.0 cat_2    false   item_22                   Min                                                  0  +    24 23  34.5 cat_3    false   item_23                   Q1                                                 7.0  +    25 24  36.0 cat_4    true    item_24                   Median                                            14.5  +    26 25  37.5 cat_0    false   item_25                   Q3                                                22.0  +    27 26  39.0 cat_1    false   item_26                  ▆▆ Max                                                 29  +    28 27  40.5 cat_2    true    item_27                  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette + + + diff --git a/tests/__snapshots__/test_column_metadata/test_snap_detail_panel_layout.svg b/tests/__snapshots__/test_column_metadata/test_snap_detail_panel_layout.svg new file mode 100644 index 0000000..5791943 --- /dev/null +++ b/tests/__snapshots__/test_column_metadata/test_snap_detail_panel_layout.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DtBrowserApp + + + + + + + + + +  Row # id score category active  name                     ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    1  0   0.0 cat_0    true    item_0                    Field    dtype       Value                              +    2  1   1.5 cat_1    false   item_1                    Row #    UInt32      3                                  +    3  2   3.0 cat_2    false   item_2                    id       Int64       2                                  +    4  3   4.5 cat_3    true    item_3                    score    Float64     3.0                                +    5  4   6.0 cat_4    false   item_4                    category Categorical cat_2                              +    6  5   7.5 cat_0    false   item_5                    active   Boolean     false                              +    7  6   9.0 cat_1    true    item_6                    name     String      item_2                             +    8  7  10.5 cat_2    false   item_7                    +    9  8  12.0 cat_3    false   item_8                    +   10  9  13.5 cat_4    true    item_9                    +   11 10  15.0 cat_0    false   item_10                   +   12 11  16.5 cat_1    false   item_11                   +   13 12  18.0 cat_2    true    item_12                   +   14 13  19.5 cat_3    false   item_13                   +   15 14  21.0 cat_4    false   item_14                   +   16 15  22.5 cat_0    true    item_15                   +   17 16  24.0 cat_1    false   item_16                   +   18 17  25.5 cat_2    false   item_17                   +   19 18  27.0 cat_3    true    item_18                   +   20 19  28.5 cat_4    false   item_19                   +   21 20  30.0 cat_0    false   item_20                  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +   22 21  31.5 cat_1    true    item_21                  ▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +   23 22  33.0 cat_2    false   item_22                   Min                                                  1  +   24 23  34.5 cat_3    false   item_23                   Q1                                                 8.0  +   25 24  36.0 cat_4    true    item_24                   Median                                            15.5  +   26 25  37.5 cat_0    false   item_25                   Q3                                                23.0  +   27 26  39.0 cat_1    false   item_26                  ▆▆ Max                                                 30  +   28 27  40.5 cat_2    true    item_27                  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette + + + diff --git a/tests/__snapshots__/test_snapshots/test_snap_column_hidden.svg b/tests/__snapshots__/test_snapshots/test_snap_column_hidden.svg index 0e96097..dcfa102 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_column_hidden.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_column_hidden.svg @@ -43,9 +43,8 @@ .terminal-r9 { fill: #242f38 } .terminal-r10 { fill: #8ad4a1 } .terminal-r11 { fill: #000f18 } -.terminal-r12 { fill: #003054 } -.terminal-r13 { fill: #ffa62b;font-weight: bold } -.terminal-r14 { fill: #495259 } +.terminal-r12 { fill: #ffa62b;font-weight: bold } +.terminal-r13 { fill: #495259 } @@ -149,38 +148,38 @@ - + -  Row # name    value category                               ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▎▔ Show/Hide/Reorder Columns ▔▔▔▔▔▎ -    1 item_0      0 cat_0                                   Field    dtype      ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -    2 item_1      1 cat_1                                   Row #    UInt32     Type to filter columns  -    3 item_2      2 cat_2                                   name     String     ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ -    4 item_3      3 cat_3                                   value    Int64      ▊▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎▎ -    5 item_4      4 cat_4                                   score    Float64    ▊▊X Row #▎▎ -    6 item_5      5 cat_0                                   category String     ▊▊X name▎▎ -    7 item_6      6 cat_1                                  ▊▊X value▎▎ -    8 item_7      7 cat_2                                  ▊▊X score▎▎ -    9 item_8      8 cat_3                                  ▊▊X category▎▎ -   10 item_9      9 cat_4                                  ▊▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎▎ -   11 item_10    10 cat_0                                  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ -   12 item_11    11 cat_1                                   -   13 item_12    12 cat_2                                   -   14 item_13    13 cat_3                                   -   15 item_14    14 cat_4                                   -   16 item_15    15 cat_0                                   -   17 item_16    16 cat_1                                   -   18 item_17    17 cat_2                                   -   19 item_18    18 cat_3                                   -   20 item_19    19 cat_4                                   - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - esc Close and Apply  ctrl+a Apply  ctrl+s Select All  ctrl+x Deselect All 1 / 20^p palette +  Row # name    value category              ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎▔ Show/Hide/Reorder Columns ▔▔▔▔▔▎ +    1 item_0      0 cat_0                  Field    dtype   Value               ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +    2 item_1      1 cat_1                  Row #    UInt32  1                   Type to filter columns  +    3 item_2      2 cat_2                  name     String  item_0              ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ +    4 item_3      3 cat_3                  value    Int64   0                   ▊▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎▎ +    5 item_4      4 cat_4                  score    Float64 0.0                 ▊▊X Row #▎▎ +    6 item_5      5 cat_0                  category String  cat_0               ▊▊X name▎▎ +    7 item_6      6 cat_1                 ▊▊X value▎▎ +    8 item_7      7 cat_2                 ▊▊X score▎▎ +    9 item_8      8 cat_3                 ▊▊X category▎▎ +   10 item_9      9 cat_4                 ▊▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎▎ +   11 item_10    10 cat_0                 ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +   12 item_11    11 cat_1                  +   13 item_12    12 cat_2                  +   14 item_13    13 cat_3                  +   15 item_14    14 cat_4                  +   16 item_15    15 cat_0                  +   17 item_16    16 cat_1                  +   18 item_17    17 cat_2                  +   19 item_18    18 cat_3                  +   20 item_19    19 cat_4                  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ + Min                                1  + Q1                               6.0  + Median                          10.5  + Q3                              15.0  + Max                               20  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + esc Close and Apply  ctrl+a Apply  ctrl+s Select All  ctrl+x Deselect All 1 / 20^p palette diff --git a/tests/__snapshots__/test_snapshots/test_snap_column_selector_open.svg b/tests/__snapshots__/test_snapshots/test_snap_column_selector_open.svg index cfcf083..7f3aad8 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_column_selector_open.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_column_selector_open.svg @@ -42,9 +42,8 @@ .terminal-r8 { fill: #e0e0e0 } .terminal-r9 { fill: #242f38 } .terminal-r10 { fill: #8ad4a1 } -.terminal-r11 { fill: #003054 } -.terminal-r12 { fill: #ffa62b;font-weight: bold } -.terminal-r13 { fill: #495259 } +.terminal-r11 { fill: #ffa62b;font-weight: bold } +.terminal-r12 { fill: #495259 } @@ -148,38 +147,38 @@ - + -  Row # name    value score category                         ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▎▔ Show/Hide/Reorder Columns ▔▔▔▔▔▎ -    1 item_0      0   0.0 cat_0                             Field    dtype      ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -    2 item_1      1   1.5 cat_1                             Row #    UInt32     Type to filter columns  -    3 item_2      2   3.0 cat_2                             name     String     ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ -    4 item_3      3   4.5 cat_3                             value    Int64      ▊▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎▎ -    5 item_4      4   6.0 cat_4                             score    Float64    ▊▊X Row #▎▎ -    6 item_5      5   7.5 cat_0                             category String     ▊▊X name▎▎ -    7 item_6      6   9.0 cat_1                            ▊▊X value▎▎ -    8 item_7      7  10.5 cat_2                            ▊▊X score▎▎ -    9 item_8      8  12.0 cat_3                            ▊▊X category▎▎ -   10 item_9      9  13.5 cat_4                            ▊▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎▎ -   11 item_10    10  15.0 cat_0                            ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ -   12 item_11    11  16.5 cat_1                             -   13 item_12    12  18.0 cat_2                             -   14 item_13    13  19.5 cat_3                             -   15 item_14    14  21.0 cat_4                             -   16 item_15    15  22.5 cat_0                             -   17 item_16    16  24.0 cat_1                             -   18 item_17    17  25.5 cat_2                             -   19 item_18    18  27.0 cat_3                             -   20 item_19    19  28.5 cat_4                             - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - esc Close and Apply  ctrl+a Apply  ctrl+s Select All  ctrl+x Deselect All 1 / 20^p palette +  Row # name    value score category        ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎▔ Show/Hide/Reorder Columns ▔▔▔▔▔▎ +    1 item_0      0   0.0 cat_0            Field    dtype   Value               ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +    2 item_1      1   1.5 cat_1            Row #    UInt32  1                   Type to filter columns  +    3 item_2      2   3.0 cat_2            name     String  item_0              ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ +    4 item_3      3   4.5 cat_3            value    Int64   0                   ▊▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎▎ +    5 item_4      4   6.0 cat_4            score    Float64 0.0                 ▊▊X Row #▎▎ +    6 item_5      5   7.5 cat_0            category String  cat_0               ▊▊X name▎▎ +    7 item_6      6   9.0 cat_1           ▊▊X value▎▎ +    8 item_7      7  10.5 cat_2           ▊▊X score▎▎ +    9 item_8      8  12.0 cat_3           ▊▊X category▎▎ +   10 item_9      9  13.5 cat_4           ▊▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎▎ +   11 item_10    10  15.0 cat_0           ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +   12 item_11    11  16.5 cat_1            +   13 item_12    12  18.0 cat_2            +   14 item_13    13  19.5 cat_3            +   15 item_14    14  21.0 cat_4            +   16 item_15    15  22.5 cat_0            +   17 item_16    16  24.0 cat_1            +   18 item_17    17  25.5 cat_2            +   19 item_18    18  27.0 cat_3            +   20 item_19    19  28.5 cat_4            +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ + Min                                1  + Q1                               6.0  + Median                          10.5  + Q3                              15.0  + Max                               20  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + esc Close and Apply  ctrl+a Apply  ctrl+s Select All  ctrl+x Deselect All 1 / 20^p palette diff --git a/tests/__snapshots__/test_snapshots/test_snap_cursor_moved.svg b/tests/__snapshots__/test_snapshots/test_snap_cursor_moved.svg index bc4fb13..5f0e508 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_cursor_moved.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_cursor_moved.svg @@ -37,8 +37,8 @@ .terminal-r3 { fill: #0178d4 } .terminal-r4 { fill: #c5c8c6 } .terminal-r5 { fill: #dde6ed } -.terminal-r6 { fill: #ffa62b;font-weight: bold } -.terminal-r7 { fill: #e0e0e0 } +.terminal-r6 { fill: #e0e0e0 } +.terminal-r7 { fill: #ffa62b;font-weight: bold } .terminal-r8 { fill: #495259 } @@ -143,38 +143,38 @@ - + -  Row # name    value score category                                                 ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ -     1 item_0      0   0.0 cat_0                                                     Field    dtype   Value          -     2 item_1      1   1.5 cat_1                                                     Row #    UInt32  4              -     3 item_2      2   3.0 cat_2                                                     name     String  item_3         -     4 item_3      3   4.5 cat_3                                                     value    Int64   3              -     5 item_4      4   6.0 cat_4                                                     score    Float64 4.5            -     6 item_5      5   7.5 cat_0                                                     category String  cat_3          -     7 item_6      6   9.0 cat_1                                                     -     8 item_7      7  10.5 cat_2                                                     -     9 item_8      8  12.0 cat_3                                                     -    10 item_9      9  13.5 cat_4                                                     -    11 item_10    10  15.0 cat_0                                                     -    12 item_11    11  16.5 cat_1                                                     -    13 item_12    12  18.0 cat_2                                                     -    14 item_13    13  19.5 cat_3                                                     -    15 item_14    14  21.0 cat_4                                                     -    16 item_15    15  22.5 cat_0                                                     -    17 item_16    16  24.0 cat_1                                                     -    18 item_17    17  25.5 cat_2                                                     -    19 item_18    18  27.0 cat_3                                                     -    20 item_19    19  28.5 cat_4                                                     - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette +  Row # name    value score category                         ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +     1 item_0      0   0.0 cat_0                             Field    dtype   Value                                  +     2 item_1      1   1.5 cat_1                             Row #    UInt32  4                                      +     3 item_2      2   3.0 cat_2                             name     String  item_3                                 +     4 item_3      3   4.5 cat_3                             value    Int64   3                                      +     5 item_4      4   6.0 cat_4                             score    Float64 4.5                                    +     6 item_5      5   7.5 cat_0                             category String  cat_3                                  +     7 item_6      6   9.0 cat_1                             +     8 item_7      7  10.5 cat_2                             +     9 item_8      8  12.0 cat_3                             +    10 item_9      9  13.5 cat_4                             +    11 item_10    10  15.0 cat_0                             +    12 item_11    11  16.5 cat_1                             +    13 item_12    12  18.0 cat_2                             +    14 item_13    13  19.5 cat_3                             +    15 item_14    14  21.0 cat_4                             +    16 item_15    15  22.5 cat_0                             +    17 item_16    16  24.0 cat_1                             +    18 item_17    17  25.5 cat_2                             +    19 item_18    18  27.0 cat_3                             +    20 item_19    19  28.5 cat_4                             +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Column: value ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ + Min                                                  0  + Q1                                                 5.0  + Median                                             9.5  + Q3                                                14.0  + Max                                                 19  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette diff --git a/tests/__snapshots__/test_snapshots/test_snap_filter_applied.svg b/tests/__snapshots__/test_snapshots/test_snap_filter_applied.svg index f877bb8..73dd962 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_filter_applied.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_filter_applied.svg @@ -37,11 +37,12 @@ .terminal-r3 { fill: #0178d4 } .terminal-r4 { fill: #c5c8c6 } .terminal-r5 { fill: #dde6ed } -.terminal-r6 { fill: #ffffff } +.terminal-r6 { fill: #000000 } .terminal-r7 { fill: #e0e0e0 } -.terminal-r8 { fill: #004578 } -.terminal-r9 { fill: #ffa62b;font-weight: bold } -.terminal-r10 { fill: #495259 } +.terminal-r8 { fill: #ffffff } +.terminal-r9 { fill: #004578 } +.terminal-r10 { fill: #ffa62b;font-weight: bold } +.terminal-r11 { fill: #495259 } @@ -145,38 +146,38 @@ - + -  Row # name    value score category                                                 ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ -   12 item_11    11  16.5 cat_1                                                     Field    dtype   Value          -   13 item_12    12  18.0 cat_2                                                     Row #    UInt32  1              -   14 item_13    13  19.5 cat_3                                                     name     String  item_0         -   15 item_14    14  21.0 cat_4                                                     value    Int64   0              -   16 item_15    15  22.5 cat_0                                                     score    Float64 0.0            -   17 item_16    16  24.0 cat_1                                                     category String  cat_0          -   18 item_17    17  25.5 cat_2                                                     -   19 item_18    18  27.0 cat_3                                                     -   20 item_19    19  28.5 cat_4                                                     - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ -▔ Filter dataframe ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -value > 10 -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - -────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - -value > 10 - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - ^t Select/copy value from table  esc Close 1 / 9 (Filtered from 20)^p palette +  Row # name    value score category                         ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +   12 item_11    11  16.5 cat_1                             Field    dtype   Value                                +   13 item_12    12  18.0 cat_2                             Row #    UInt32  1                                    +   14 item_13    13  19.5 cat_3                             name     String  item_0                               +   15 item_14    14  21.0 cat_4                             value    Int64   0                                    +   16 item_15    15  22.5 cat_0                             score    Float64 0.0                                 ▆▆ +   17 item_16    16  24.0 cat_1                            ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +   18 item_17    17  25.5 cat_2                            ▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +   19 item_18    18  27.0 cat_3                             Min                                                  1  +   20 item_19    19  28.5 cat_4                             Q1                                                 6.0  + Median                                            10.5  + Q3                                                15.0  + Max                                                 20  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Filter dataframe ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +value > 10 +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + +────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +value > 10 + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + ^t Select/copy value from table  esc Close 1 / 9 (Filtered from 20)^p palette diff --git a/tests/__snapshots__/test_snapshots/test_snap_initial_view.svg b/tests/__snapshots__/test_snapshots/test_snap_initial_view.svg index bb68455..2db7562 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_initial_view.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_initial_view.svg @@ -37,8 +37,8 @@ .terminal-r3 { fill: #0178d4 } .terminal-r4 { fill: #c5c8c6 } .terminal-r5 { fill: #dde6ed } -.terminal-r6 { fill: #ffa62b;font-weight: bold } -.terminal-r7 { fill: #e0e0e0 } +.terminal-r6 { fill: #e0e0e0 } +.terminal-r7 { fill: #ffa62b;font-weight: bold } .terminal-r8 { fill: #495259 } @@ -143,38 +143,38 @@ - + -  Row # name    value score category                                                 ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ -    1 item_0      0   0.0 cat_0                                                     Field    dtype   Value          -    2 item_1      1   1.5 cat_1                                                     Row #    UInt32  1              -    3 item_2      2   3.0 cat_2                                                     name     String  item_0         -    4 item_3      3   4.5 cat_3                                                     value    Int64   0              -    5 item_4      4   6.0 cat_4                                                     score    Float64 0.0            -    6 item_5      5   7.5 cat_0                                                     category String  cat_0          -    7 item_6      6   9.0 cat_1                                                     -    8 item_7      7  10.5 cat_2                                                     -    9 item_8      8  12.0 cat_3                                                     -   10 item_9      9  13.5 cat_4                                                     -   11 item_10    10  15.0 cat_0                                                     -   12 item_11    11  16.5 cat_1                                                     -   13 item_12    12  18.0 cat_2                                                     -   14 item_13    13  19.5 cat_3                                                     -   15 item_14    14  21.0 cat_4                                                     -   16 item_15    15  22.5 cat_0                                                     -   17 item_16    16  24.0 cat_1                                                     -   18 item_17    17  25.5 cat_2                                                     -   19 item_18    18  27.0 cat_3                                                     -   20 item_19    19  28.5 cat_4                                                     - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette +  Row # name    value score category                         ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    1 item_0      0   0.0 cat_0                             Field    dtype   Value                                  +    2 item_1      1   1.5 cat_1                             Row #    UInt32  1                                      +    3 item_2      2   3.0 cat_2                             name     String  item_0                                 +    4 item_3      3   4.5 cat_3                             value    Int64   0                                      +    5 item_4      4   6.0 cat_4                             score    Float64 0.0                                    +    6 item_5      5   7.5 cat_0                             category String  cat_0                                  +    7 item_6      6   9.0 cat_1                             +    8 item_7      7  10.5 cat_2                             +    9 item_8      8  12.0 cat_3                             +   10 item_9      9  13.5 cat_4                             +   11 item_10    10  15.0 cat_0                             +   12 item_11    11  16.5 cat_1                             +   13 item_12    12  18.0 cat_2                             +   14 item_13    13  19.5 cat_3                             +   15 item_14    14  21.0 cat_4                             +   16 item_15    15  22.5 cat_0                             +   17 item_16    16  24.0 cat_1                             +   18 item_17    17  25.5 cat_2                             +   19 item_18    18  27.0 cat_3                             +   20 item_19    19  28.5 cat_4                             +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ + Min                                                  1  + Q1                                                 6.0  + Median                                            10.5  + Q3                                                15.0  + Max                                                 20  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette diff --git a/tests/__snapshots__/test_snapshots/test_snap_narrow_terminal.svg b/tests/__snapshots__/test_snapshots/test_snap_narrow_terminal.svg index c6b2a22..d2db595 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_narrow_terminal.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_narrow_terminal.svg @@ -37,10 +37,9 @@ .terminal-r3 { fill: #0178d4 } .terminal-r4 { fill: #c5c8c6 } .terminal-r5 { fill: #dde6ed } -.terminal-r6 { fill: #003054 } -.terminal-r7 { fill: #e0e0e0 } -.terminal-r8 { fill: #ffa62b;font-weight: bold } -.terminal-r9 { fill: #495259 } +.terminal-r6 { fill: #e0e0e0 } +.terminal-r7 { fill: #ffa62b;font-weight: bold } +.terminal-r8 { fill: #495259 } @@ -144,38 +143,38 @@ - + -  Row # name    value score category▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▎ -    1 item_0      0   0.0 cat_0     Field    dtype       -    2 item_1      1   1.5 cat_1     Row #    UInt32      -    3 item_2      2   3.0 cat_2     name     String      -    4 item_3      3   4.5 cat_3     value    Int64       -    5 item_4      4   6.0 cat_4     score    Float64     -    6 item_5      5   7.5 cat_0     category String      -    7 item_6      6   9.0 cat_1     -    8 item_7      7  10.5 cat_2     -    9 item_8      8  12.0 cat_3     -   10 item_9      9  13.5 cat_4     -   11 item_10    10  15.0 cat_0     -   12 item_11    11  16.5 cat_1     -   13 item_12    12  18.0 cat_2     -   14 item_13    13  19.5 cat_3     -   15 item_14    14  21.0 cat_4     -   16 item_15    15  22.5 cat_0     -   17 item_16    16  24.0 cat_1     -   18 item_17    17  25.5 cat_2     -   19 item_18    18  27.0 cat_3     -   20 item_19    19  28.5 cat_4     - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - f Filter rows  / Search  b Add/Del Bookmark  x ^p palette +  Row # name    value score    ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    1 item_0      0   0.0     Field    dtype   Value    +    2 item_1      1   1.5     Row #    UInt32  1        +    3 item_2      2   3.0     name     String  item_0   +    4 item_3      3   4.5     value    Int64   0        +    5 item_4      4   6.0     score    Float64 0.0      +    6 item_5      5   7.5     category String  cat_0    +    7 item_6      6   9.0     +    8 item_7      7  10.5     +    9 item_8      8  12.0     +   10 item_9      9  13.5     +   11 item_10    10  15.0     +   12 item_11    11  16.5     +   13 item_12    12  18.0     +   14 item_13    13  19.5     +   15 item_14    14  21.0     +   16 item_15    15  22.5     +   17 item_16    16  24.0     +   18 item_17    17  25.5     +   19 item_18    18  27.0     +   20 item_19    19  28.5     +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▎ + Min                    1  + Q1                   6.0  + Median              10.5  + Q3                  15.0  + Max                   20  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x ^p palette diff --git a/tests/__snapshots__/test_snapshots/test_snap_row_detail.svg b/tests/__snapshots__/test_snapshots/test_snap_row_detail.svg index 65e86e7..d06f2a3 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_row_detail.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_row_detail.svg @@ -37,8 +37,8 @@ .terminal-r3 { fill: #0178d4 } .terminal-r4 { fill: #c5c8c6 } .terminal-r5 { fill: #dde6ed } -.terminal-r6 { fill: #ffa62b;font-weight: bold } -.terminal-r7 { fill: #e0e0e0 } +.terminal-r6 { fill: #e0e0e0 } +.terminal-r7 { fill: #ffa62b;font-weight: bold } .terminal-r8 { fill: #495259 } @@ -143,38 +143,38 @@ - + -  Row # name    value score category                                                 ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ -    1 item_0      0   0.0 cat_0                                                     Field    dtype   Value          -    2 item_1      1   1.5 cat_1                                                     Row #    UInt32  3              -    3 item_2      2   3.0 cat_2                                                     name     String  item_2         -    4 item_3      3   4.5 cat_3                                                     value    Int64   2              -    5 item_4      4   6.0 cat_4                                                     score    Float64 3.0            -    6 item_5      5   7.5 cat_0                                                     category String  cat_2          -    7 item_6      6   9.0 cat_1                                                     -    8 item_7      7  10.5 cat_2                                                     -    9 item_8      8  12.0 cat_3                                                     -   10 item_9      9  13.5 cat_4                                                     -   11 item_10    10  15.0 cat_0                                                     -   12 item_11    11  16.5 cat_1                                                     -   13 item_12    12  18.0 cat_2                                                     -   14 item_13    13  19.5 cat_3                                                     -   15 item_14    14  21.0 cat_4                                                     -   16 item_15    15  22.5 cat_0                                                     -   17 item_16    16  24.0 cat_1                                                     -   18 item_17    17  25.5 cat_2                                                     -   19 item_18    18  27.0 cat_3                                                     -   20 item_19    19  28.5 cat_4                                                     - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette +  Row # name    value score category                         ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    1 item_0      0   0.0 cat_0                             Field    dtype   Value                                  +    2 item_1      1   1.5 cat_1                             Row #    UInt32  3                                      +    3 item_2      2   3.0 cat_2                             name     String  item_2                                 +    4 item_3      3   4.5 cat_3                             value    Int64   2                                      +    5 item_4      4   6.0 cat_4                             score    Float64 3.0                                    +    6 item_5      5   7.5 cat_0                             category String  cat_2                                  +    7 item_6      6   9.0 cat_1                             +    8 item_7      7  10.5 cat_2                             +    9 item_8      8  12.0 cat_3                             +   10 item_9      9  13.5 cat_4                             +   11 item_10    10  15.0 cat_0                             +   12 item_11    11  16.5 cat_1                             +   13 item_12    12  18.0 cat_2                             +   14 item_13    13  19.5 cat_3                             +   15 item_14    14  21.0 cat_4                             +   16 item_15    15  22.5 cat_0                             +   17 item_16    16  24.0 cat_1                             +   18 item_17    17  25.5 cat_2                             +   19 item_18    18  27.0 cat_3                             +   20 item_19    19  28.5 cat_4                             +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ + Min                                                  1  + Q1                                                 6.0  + Median                                            10.5  + Q3                                                15.0  + Max                                                 20  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row Detail  s^p palette diff --git a/tests/__snapshots__/test_snapshots/test_snap_search_results.svg b/tests/__snapshots__/test_snapshots/test_snap_search_results.svg index 46098c3..b5c2068 100644 --- a/tests/__snapshots__/test_snapshots/test_snap_search_results.svg +++ b/tests/__snapshots__/test_snapshots/test_snap_search_results.svg @@ -37,8 +37,8 @@ .terminal-r3 { fill: #0178d4 } .terminal-r4 { fill: #c5c8c6 } .terminal-r5 { fill: #dde6ed } -.terminal-r6 { fill: #ffa62b;font-weight: bold } -.terminal-r7 { fill: #e0e0e0 } +.terminal-r6 { fill: #e0e0e0 } +.terminal-r7 { fill: #ffa62b;font-weight: bold } .terminal-r8 { fill: #495259 } @@ -143,38 +143,38 @@ - + -  Row # name    value score category                                                 ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ -    1 item_0      0   0.0 cat_0                                                     Field    dtype   Value          -    2 item_1      1   1.5 cat_1                                                     Row #    UInt32  7              -    3 item_2      2   3.0 cat_2                                                     name     String  item_6         -    4 item_3      3   4.5 cat_3                                                     value    Int64   6              -    5 item_4      4   6.0 cat_4                                                     score    Float64 9.0            -    6 item_5      5   7.5 cat_0                                                     category String  cat_1          -    7 item_6      6   9.0 cat_1                                                     -     8 item_7      7  10.5 cat_2                                                     -     9 item_8      8  12.0 cat_3                                                     -    10 item_9      9  13.5 cat_4                                                     -    11 item_10    10  15.0 cat_0                                                     -    12 item_11    11  16.5 cat_1                                                     -    13 item_12    12  18.0 cat_2                                                     -    14 item_13    13  19.5 cat_3                                                     -    15 item_14    14  21.0 cat_4                                                     -    16 item_15    15  22.5 cat_0                                                     -    17 item_16    16  24.0 cat_1                                                     -    18 item_17    17  25.5 cat_2                                                     -    19 item_18    18  27.0 cat_3                                                     -    20 item_19    19  28.5 cat_4                                                     - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ - f Filter rows  / Search  n Next  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row D^p palette +  Row # name    value score category                         ▔ Row Detail ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +    1 item_0      0   0.0 cat_0                             Field    dtype   Value                                  +    2 item_1      1   1.5 cat_1                             Row #    UInt32  7                                      +    3 item_2      2   3.0 cat_2                             name     String  item_6                                 +    4 item_3      3   4.5 cat_3                             value    Int64   6                                      +    5 item_4      4   6.0 cat_4                             score    Float64 9.0                                    +    6 item_5      5   7.5 cat_0                             category String  cat_1                                  +    7 item_6      6   9.0 cat_1                             +     8 item_7      7  10.5 cat_2                             +     9 item_8      8  12.0 cat_3                             +    10 item_9      9  13.5 cat_4                             +    11 item_10    10  15.0 cat_0                             +    12 item_11    11  16.5 cat_1                             +    13 item_12    12  18.0 cat_2                             +    14 item_13    13  19.5 cat_3                             +    15 item_14    14  21.0 cat_4                             +    16 item_15    15  22.5 cat_0                             +    17 item_16    16  24.0 cat_1                             +    18 item_17    17  25.5 cat_2                             +    19 item_18    18  27.0 cat_3                             +    20 item_19    19  28.5 cat_4                             +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ +▔ Column: Row # ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ + Min                                                  1  + Q1                                                 6.0  + Median                                            10.5  + Q3                                                15.0  + Max                                                 20  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + f Filter rows  / Search  n Next  b Add/Del Bookmark  x Compute Expressions...  c Columns...  r Toggle Row D^p palette diff --git a/tests/test_column_metadata.py b/tests/test_column_metadata.py new file mode 100644 index 0000000..23b5a78 --- /dev/null +++ b/tests/test_column_metadata.py @@ -0,0 +1,352 @@ +import datetime + +import polars as pl +from textual.pilot import Pilot + +from dt_browser.browser import DtBrowserApp +from dt_browser.column_metadata import ColumnMetadata, compute_column_stats +from dt_browser.custom_table import CustomTable + + +# --- Unit tests for stats computation --- + + +def test_numeric_stats(): + series = pl.Series("val", [1.0, 2.0, 3.0, 4.0, 5.0]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert labels == ["Min", "Q1", "Median", "Q3", "Max"] + assert stats[0][1] == "1.0" + assert stats[4][1] == "5.0" + + +def test_numeric_stats_integers(): + series = pl.Series("val", [10, 20, 30, 40, 50]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert labels == ["Min", "Q1", "Median", "Q3", "Max"] + assert stats[0][1] == "10" + assert stats[4][1] == "50" + + +def test_numeric_stats_empty(): + series = pl.Series("val", [], dtype=pl.Float64) + stats = compute_column_stats(series) + assert stats == [("", "No data")] + + +def test_categorical_stats(): + series = pl.Series("cat", ["a", "b", "a", "c", "b", "a"]).cast(pl.Categorical) + stats = compute_column_stats(series) + assert stats[0] == ("Unique values", "3") + # Top entries should be sorted by count descending + assert stats[1] == (" a", "3") + assert stats[2] == (" b", "2") + assert stats[3] == (" c", "1") + + +def test_categorical_stats_top_10(): + values = [f"cat_{i}" for i in range(20)] * 2 + series = pl.Series("cat", values).cast(pl.Categorical) + stats = compute_column_stats(series) + assert stats[0][0] == "Unique values" + # Should have at most 10 variant rows + 1 header + assert len(stats) <= 11 + + +def test_temporal_stats(): + series = pl.Series("ts", [datetime.datetime(2024, 1, 1), datetime.datetime(2024, 6, 15), datetime.datetime(2024, 12, 31)]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert labels == ["Min", "Max"] + assert "2024-01-01" in stats[0][1] + assert "2024-12-31" in stats[1][1] + + +def test_temporal_stats_empty(): + series = pl.Series("ts", [], dtype=pl.Datetime) + stats = compute_column_stats(series) + assert stats == [("", "No data")] + + +def test_boolean_stats(): + series = pl.Series("flag", [True, True, False, True, False]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert "True" in labels + assert "False" in labels + true_val = next(s[1] for s in stats if s[0] == "True") + false_val = next(s[1] for s in stats if s[0] == "False") + assert true_val == "3" + assert false_val == "2" + + +def test_boolean_stats_with_nulls(): + series = pl.Series("flag", [True, None, False, None]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert "Null" in labels + null_val = next(s[1] for s in stats if s[0] == "Null") + assert null_val == "2" + + +def test_numeric_stats_with_nulls(): + series = pl.Series("val", [1.0, None, 3.0, None, 5.0]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert "Null" in labels + null_val = next(s[1] for s in stats if s[0] == "Null") + assert null_val == "2" + + +def test_numeric_stats_with_nans(): + series = pl.Series("val", [1.0, float("nan"), 3.0, float("nan"), 5.0]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert "NaN" in labels + nan_val = next(s[1] for s in stats if s[0] == "NaN") + assert nan_val == "2" + + +def test_numeric_integer_no_nan_row(): + series = pl.Series("val", [1, 2, 3]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert "NaN" not in labels + + +def test_temporal_stats_with_nulls(): + series = pl.Series("ts", [datetime.datetime(2024, 1, 1), None, datetime.datetime(2024, 12, 31)]) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert "Null" in labels + null_val = next(s[1] for s in stats if s[0] == "Null") + assert null_val == "1" + + +def test_categorical_stats_with_nulls(): + series = pl.Series("cat", ["a", None, "b", None]).cast(pl.Categorical) + stats = compute_column_stats(series) + labels = [s[0] for s in stats] + assert "Null" in labels + null_val = next(s[1] for s in stats if s[0] == "Null") + assert null_val == "2" + + +def test_unsupported_dtype_returns_empty(): + series = pl.Series("text", ["hello", "world"]) + stats = compute_column_stats(series) + assert stats == [] + + +# --- Integration tests --- + + +def _make_mixed_app(num_rows: int = 30) -> DtBrowserApp: + df = pl.DataFrame( + { + "id": list(range(num_rows)), + "score": [round(i * 1.5, 1) for i in range(num_rows)], + "category": pl.Series([f"cat_{i % 5}" for i in range(num_rows)]).cast(pl.Categorical), + "active": [i % 3 == 0 for i in range(num_rows)], + "name": [f"item_{i}" for i in range(num_rows)], + } + ) + return DtBrowserApp("test", df) + + +async def test_column_metadata_visible_on_start(): + """Column metadata panel should be visible on app start and within viewport.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + metadata = app.query_one(ColumnMetadata) + assert metadata is not None + # Should show stats for the first column (Row # which is integer) + assert metadata.column_info is not None + # Must be within the visible area + assert metadata.region.y + metadata.region.height <= 30, ( + f"ColumnMetadata at y={metadata.region.y} h={metadata.region.height} is off-screen" + ) + assert metadata.region.height > 0 + + +async def test_column_metadata_displays_numeric_stats(): + """Verify numeric column stats are actually rendered in the widget.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + metadata = app.query_one(ColumnMetadata) + # First column is Row # (numeric) — stats should be cached + col_name = metadata.column_info[0] + assert col_name in metadata._cache + stats = metadata._cache[col_name] + labels = [s[0] for s in stats] + assert "Min" in labels + assert "Median" in labels + assert "Max" in labels + # Border title should show column name + assert col_name in metadata.border_title + + +async def test_column_metadata_displays_categorical_stats(): + """Verify categorical column stats are rendered with value counts.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + metadata = app.query_one(ColumnMetadata) + # Navigate to 'category' column (Row#, id, score, category) + for _ in range(3): + await pilot.press("right") + await pilot.pause() + assert metadata.column_info[0] == "category" + stats = metadata._cache["category"] + assert stats[0] == ("Unique values", "5") + # Should have value count entries + assert len(stats) == 6 # 1 header + 5 categories + assert "category" in metadata.border_title + + +async def test_column_metadata_displays_boolean_stats(): + """Verify boolean column stats show True/False counts.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + metadata = app.query_one(ColumnMetadata) + # Navigate to 'active' column (Row#, id, score, category, active) + for _ in range(4): + await pilot.press("right") + await pilot.pause() + assert metadata.column_info[0] == "active" + stats = metadata._cache["active"] + labels = [s[0] for s in stats] + assert "True" in labels + assert "False" in labels + + +async def test_column_metadata_updates_on_cursor_move(): + """Moving cursor to a different column updates column_info.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + metadata = app.query_one(ColumnMetadata) + initial_info = metadata.column_info + + await pilot.press("right") + await pilot.pause() + + assert metadata.column_info != initial_info + + +async def test_column_metadata_cache_works(): + """Moving to a column, away, and back should use cached stats.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + metadata = app.query_one(ColumnMetadata) + + # Move to column 1 + await pilot.press("right") + await pilot.pause() + col_name_1 = metadata.column_info[0] + assert col_name_1 in metadata._cache + + # Move to column 2 + await pilot.press("right") + await pilot.pause() + + # Move back to column 1 + await pilot.press("left") + await pilot.pause() + # Cache should still have the entry + assert col_name_1 in metadata._cache + + +async def test_column_metadata_cache_invalidated_on_filter(): + """Applying a filter should clear the column metadata cache.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + metadata = app.query_one(ColumnMetadata) + + # Move to trigger cache population + await pilot.press("right") + await pilot.pause() + assert len(metadata._cache) > 0 + + # Apply a filter + await pilot.press("f") + await pilot.pause() + await pilot.press(*list("id > 10")) + await pilot.press("enter") + await pilot.app.workers.wait_for_complete() + await pilot.pause() + + assert len(metadata._cache) == 0 + + +async def test_column_metadata_hidden_with_row_detail(): + """Toggling row detail off should also hide column metadata.""" + app = _make_mixed_app() + async with app.run_test(size=(120, 30)) as pilot: + await pilot.pause() + assert len(app.query(ColumnMetadata)) == 1 + + await pilot.press("r") + await pilot.pause() + + # Both should be gone (inside DetailPanel) + assert len(app.query(ColumnMetadata)) == 0 + + +# --- Snapshot tests --- + + +def test_snap_column_metadata_numeric(snap_compare): + """Snapshot showing numeric column stats (id column).""" + assert snap_compare( + _make_mixed_app(), + press=["right"], + terminal_size=(120, 30), + ) + + +def test_snap_column_metadata_categorical(snap_compare): + """Snapshot showing categorical column stats.""" + + async def run_before(pilot: Pilot) -> None: + # Navigate to the 'category' column (index 3: Row#, id, score, category) + for _ in range(3): + await pilot.press("right") + await pilot.pause() + + assert snap_compare( + _make_mixed_app(), + run_before=run_before, + terminal_size=(120, 30), + ) + + +def test_snap_column_metadata_boolean(snap_compare): + """Snapshot showing boolean column stats.""" + + async def run_before(pilot: Pilot) -> None: + # Navigate to the 'active' column (index 4: Row#, id, score, category, active) + for _ in range(4): + await pilot.press("right") + await pilot.pause() + + assert snap_compare( + _make_mixed_app(), + run_before=run_before, + terminal_size=(120, 30), + ) + + +def test_snap_detail_panel_layout(snap_compare): + """Snapshot showing full detail panel with both row detail and column metadata.""" + assert snap_compare( + _make_mixed_app(), + press=["down", "down"], + terminal_size=(120, 30), + )