2
2
3
3
import warnings
4
4
5
+ # TODO-barret; Should `.input_cell_selection()` ever return None? Is that value even helpful? Empty lists would be much more user friendly.
6
+ # TODO-barret-render.data_frame; Add method `update_sort()`
7
+ # TODO-barret-render.data_frame; Add method `update_filter()`
5
8
# TODO-barret-render.data_frame; Docs
6
9
# TODO-barret-render.data_frame; Add examples!
7
10
from typing import (
58
61
from ._data_frame_utils ._datagridtable import DataFrameResult
59
62
60
63
61
- class SelectedIndices (TypedDict ):
62
- rows : tuple [int ] | None
63
- columns : tuple [int ] | None
64
-
65
-
66
64
class ColumnSort (TypedDict ):
67
65
id : str
68
66
desc : bool
@@ -78,6 +76,18 @@ class ColumnFilterNumber(TypedDict):
78
76
value : tuple [float , float ]
79
77
80
78
79
+ ColumnFilter = Union [ColumnFilterStr , ColumnFilterNumber ]
80
+
81
+
82
+ class DataViewInfo (TypedDict ):
83
+ sort : tuple [ColumnSort , ...]
84
+ filter : tuple [ColumnFilter , ...]
85
+
86
+ rows : tuple [int , ...] # sorted and filtered row number
87
+ selected_rows : tuple [int , ...] # selected and sorted and filtered row number
88
+ # selected_columns: tuple[int, ...] # selected and sorted and filtered row number
89
+
90
+
81
91
# # TODO-future; Use `dataframe-api-compat>=0.2.6` to injest dataframes and return standardized dataframe structures
82
92
# # TODO-future: Find this type definition: https://github.com/data-apis/dataframe-api-compat/blob/273c0be45962573985b3a420869d0505a3f9f55d/dataframe_api_compat/polars_standard/dataframe_object.py#L22
83
93
# # Related: https://data-apis.org/dataframe-api-compat/quick_start/
@@ -275,6 +285,18 @@ def data_view(self, *, selected: bool = False) -> pd.DataFrame:
275
285
else :
276
286
return self ._data_view_all ()
277
287
288
+ data_view_info : reactive .Calc_ [DataViewInfo ]
289
+ """
290
+ Reactive value of the data frame's view information.
291
+
292
+ This includes:
293
+ * `sort`: An array of `{"id": str, "desc": bool }` information. This is the output of `.input_column_sort()`.
294
+ * `filter`: An array of `{"id": str, "value": str | tuple[float, float]}` information. This is the output of `.input_column_filter()`.
295
+ * `rows`: The row numbers of the data frame that are currently being viewed in the browser after sorting and filtering has been applied.
296
+ * `selected_rows`: `rows` values that have been selected by the user. This value created from the `rows` key in `.input_cell_selection()`.
297
+
298
+ """
299
+
278
300
# TODO-barret-render.data_frame; Allow for DataTable and DataGrid to accept SelectionModes
279
301
selection_modes : reactive .Calc_ [SelectionModes ]
280
302
"""
@@ -285,7 +307,7 @@ def data_view(self, *, selected: bool = False) -> pd.DataFrame:
285
307
"""
286
308
Reactive value of selected cell information.
287
309
288
- This method is a wrapper around `input.<id>_selected_cells ()`, where `<id>` is
310
+ This method is a wrapper around `input.<id>_cell_selection ()`, where `<id>` is
289
311
the `id` of the data frame output. This method returns the selected rows and
290
312
will cause reactive updates as the selected rows change.
291
313
@@ -297,7 +319,7 @@ def data_view(self, *, selected: bool = False) -> pd.DataFrame:
297
319
selected cells.
298
320
"""
299
321
300
- _input_data_view_indices : reactive .Calc_ [list [int ]]
322
+ _input_data_view_rows : reactive .Calc_ [tuple [int , ... ]]
301
323
"""
302
324
Reactive value of the data frame's view indices.
303
325
@@ -311,6 +333,16 @@ def data_view(self, *, selected: bool = False) -> pd.DataFrame:
311
333
This is the data frame with all the user's edit patches applied to it.
312
334
"""
313
335
336
+ input_column_sort : reactive .Calc_ [tuple [ColumnSort , ...]]
337
+ """
338
+ Reactive value of the data frame's column sorting information.
339
+ """
340
+
341
+ input_column_filter : reactive .Calc_ [tuple [ColumnFilter , ...]]
342
+ """
343
+ Reactive value of the data frame's column filters.
344
+ """
345
+
314
346
def _reset_reactives (self ) -> None :
315
347
self ._value .set (None )
316
348
self ._cell_patch_map .set ({})
@@ -380,36 +412,53 @@ def self_input_cell_selection() -> CellSelection | None:
380
412
381
413
self .input_cell_selection = self_input_cell_selection
382
414
383
- # # Array of sorted column information
384
- # # TODO-barret-render.data_frame; Expose and update column sorting
385
- # # Do not expose until update methods are provided
386
- # @reactive.calc
387
- # def self__input_column_sort() -> list[ColumnSort]:
388
- # column_sort = self._get_session().input[f"{self.output_id}_column_sort"]()
389
- # return column_sort
415
+ @reactive .calc
416
+ def self_input_column_sort () -> tuple [ColumnSort , ...]:
417
+ column_sort = self ._get_session ().input [f"{ self .output_id } _column_sort" ]()
418
+ return tuple (column_sort )
390
419
391
- # self._input_column_sort = self__input_column_sort
420
+ self .input_column_sort = self_input_column_sort
392
421
393
- # # Array of column filters applied by user
394
- # # TODO-barret-render.data_frame; Expose and update column filters
395
- # # Do not expose until update methods are provided
396
- # @reactive.calc
397
- # def self__input_column_filter() -> list[ColumnFilterStr | ColumnFilterNumber]:
398
- # column_filter = self._get_session().input[
399
- # f"{self.output_id}_column_filter"
400
- # ]()
401
- # return column_filter
422
+ @reactive .calc
423
+ def self_input_column_filter () -> tuple [ColumnFilter , ...]:
424
+ column_filter = self ._get_session ().input [
425
+ f"{ self .output_id } _column_filter"
426
+ ]()
427
+ return tuple (column_filter )
428
+
429
+ self .input_column_filter = self_input_column_filter
430
+
431
+ @reactive .calc
432
+ def self_data_view_info () -> DataViewInfo :
433
+
434
+ cell_selection = self .input_cell_selection ()
435
+ selected_rows = tuple (
436
+ cell_selection ["rows" ]
437
+ if cell_selection is not None and "rows" in cell_selection
438
+ else tuple [int ]()
439
+ )
440
+
441
+ sort = self .input_column_sort ()
442
+ filter = self .input_column_filter ()
443
+ rows = self ._input_data_view_rows ()
444
+
445
+ return {
446
+ "sort" : sort ,
447
+ "filter" : filter ,
448
+ "rows" : rows ,
449
+ "selected_rows" : selected_rows ,
450
+ }
402
451
403
- # self._input_column_filter = self__input_column_filter
452
+ self .data_view_info = self_data_view_info
404
453
405
454
@reactive .calc
406
- def self__input_data_view_indices () -> list [int ]:
407
- data_view_indices = self ._get_session ().input [
408
- f"{ self .output_id } _data_view_indices "
455
+ def self__input_data_view_rows () -> tuple [int , ... ]:
456
+ data_view_rows = self ._get_session ().input [
457
+ f"{ self .output_id } _data_view_rows "
409
458
]()
410
- return data_view_indices
459
+ return tuple ( data_view_rows )
411
460
412
- self ._input_data_view_indices = self__input_data_view_indices
461
+ self ._input_data_view_rows = self__input_data_view_rows
413
462
414
463
# @reactive.calc
415
464
# def self__data_selected() -> pd.DataFrame:
@@ -485,23 +534,21 @@ def _subset_data_view(selected: bool) -> pd.DataFrame:
485
534
data = self ._data_patched ().copy (deep = False )
486
535
487
536
# Turn into list for pandas compatibility
488
- data_view_indices = list (self ._input_data_view_indices ())
537
+ data_view_rows = list (self ._input_data_view_rows ())
489
538
490
539
# Possibly subset the indices to selected rows
491
540
if selected :
492
541
cell_selection = self .input_cell_selection ()
493
542
if cell_selection is not None and cell_selection ["type" ] == "row" :
494
543
# Use a `set` for faster lookups
495
- selected_row_indices_set = set (cell_selection ["rows" ])
544
+ selected_row_set = set (cell_selection ["rows" ])
496
545
497
546
# Subset the data view indices to only include the selected rows
498
- data_view_indices = [
499
- index
500
- for index in data_view_indices
501
- if index in selected_row_indices_set
547
+ data_view_rows = [
548
+ row for row in data_view_rows if row in selected_row_set
502
549
]
503
550
504
- return data .iloc [data_view_indices ]
551
+ return data .iloc [data_view_rows ]
505
552
506
553
# Helper reactives so that internal calculations can be cached for use in other calculations
507
554
@reactive .calc
0 commit comments