|
14 | 14 |
|
15 | 15 | from __future__ import annotations |
16 | 16 |
|
| 17 | +import dataclasses |
17 | 18 | from importlib import resources |
18 | 19 | import functools |
19 | 20 | import math |
20 | | -from typing import Any, Dict, Iterator, List, Optional, Tuple, Type |
| 21 | +from typing import Any, Dict, Iterator, List, Optional, Type |
21 | 22 | import uuid |
22 | 23 |
|
23 | 24 | import pandas as pd |
|
47 | 48 | WIDGET_BASE = object |
48 | 49 |
|
49 | 50 |
|
| 51 | +@dataclasses.dataclass(frozen=True) |
| 52 | +class _SortState: |
| 53 | + column: str |
| 54 | + ascending: bool |
| 55 | + |
| 56 | + |
50 | 57 | class TableWidget(WIDGET_BASE): |
51 | 58 | """An interactive, paginated table widget for BigFrames DataFrames. |
52 | 59 |
|
@@ -91,17 +98,18 @@ def __init__(self, dataframe: bigframes.dataframe.DataFrame): |
91 | 98 | self._all_data_loaded = False |
92 | 99 | self._batch_iter: Optional[Iterator[pd.DataFrame]] = None |
93 | 100 | self._cached_batches: List[pd.DataFrame] = [] |
94 | | - self._last_sort_state: Optional[Tuple[str, bool]] = None |
| 101 | + self._last_sort_state: Optional[_SortState] = None |
95 | 102 |
|
96 | 103 | # respect display options for initial page size |
97 | 104 | initial_page_size = bigframes.options.display.max_rows |
98 | 105 |
|
99 | 106 | # set traitlets properties that trigger observers |
| 107 | + # TODO(b/462525985): Investigate and improve TableWidget UX for DataFrames with a large number of columns. |
100 | 108 | self.page_size = initial_page_size |
101 | 109 | self.orderable_columns = [ |
102 | | - col |
103 | | - for col in dataframe.columns |
104 | | - if dtypes.is_orderable(dataframe.dtypes[col]) |
| 110 | + col_name |
| 111 | + for col_name, dtype in dataframe.dtypes.items() |
| 112 | + if dtypes.is_orderable(dtype) |
105 | 113 | ] |
106 | 114 |
|
107 | 115 | # obtain the row counts |
@@ -248,22 +256,17 @@ def _set_table_html(self) -> None: |
248 | 256 | # Apply sorting if a column is selected |
249 | 257 | df_to_display = self._dataframe |
250 | 258 | if self.sort_column: |
251 | | - try: |
252 | | - df_to_display = df_to_display.sort_values( |
253 | | - by=self.sort_column, ascending=self.sort_ascending |
254 | | - ) |
255 | | - except KeyError: |
256 | | - self._error_message = f"Column '{self.sort_column}' not found. Please select a valid column to sort by." |
257 | | - # Revert to unsorted state if sorting fails |
258 | | - self.sort_column = "" |
| 259 | + df_to_display = df_to_display.sort_values( |
| 260 | + by=self.sort_column, ascending=self.sort_ascending |
| 261 | + ) |
259 | 262 |
|
260 | 263 | # Reset batches when sorting changes |
261 | | - if self._last_sort_state != (self.sort_column, self.sort_ascending): |
| 264 | + if self._last_sort_state != _SortState(self.sort_column, self.sort_ascending): |
262 | 265 | self._batches = df_to_display._to_pandas_batches(page_size=self.page_size) |
263 | 266 | self._cached_batches = [] |
264 | 267 | self._batch_iter = None |
265 | 268 | self._all_data_loaded = False |
266 | | - self._last_sort_state = (self.sort_column, self.sort_ascending) |
| 269 | + self._last_sort_state = _SortState(self.sort_column, self.sort_ascending) |
267 | 270 | self.page = 0 # Reset to first page |
268 | 271 |
|
269 | 272 | start = self.page * self.page_size |
|
0 commit comments