From 92ec7b1e784092f57a51d198cecca843937c3b2e Mon Sep 17 00:00:00 2001 From: petar-qb Date: Fri, 7 Jun 2024 10:20:49 +0200 Subject: [PATCH 1/4] Infinite scroll AgGrid added --- vizro-core/examples/kpi/app.py | 29 ++++++++++++---- vizro-core/examples/kpi/utils/_charts.py | 43 ++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/vizro-core/examples/kpi/app.py b/vizro-core/examples/kpi/app.py index 8cfa06ee4..1befee1b7 100644 --- a/vizro-core/examples/kpi/app.py +++ b/vizro-core/examples/kpi/app.py @@ -2,11 +2,11 @@ import pandas as pd import vizro.models as vm -from utils._charts import COLUMN_DEFS, KPI, bar, choropleth, line, pie +from dash import Input, Output, callback, no_update +from utils._charts import COLUMN_DEFS, KPI, AgGridPage, bar, choropleth, infinite_scroll_ag_grid, line, pie from utils._helper import clean_data_and_add_columns from vizro import Vizro from vizro.actions import filter_interaction -from vizro.tables import dash_ag_grid # DATA -------------------------------------------------------------------------------------------- df_complaints = pd.read_csv("https://query.data.world/s/glbdstahsuw3hjgunz3zssggk7dsfu?dws=00000") @@ -217,14 +217,15 @@ ], ) -page_table = vm.Page( +page_table = AgGridPage( title="List of complaints", components=[ vm.AgGrid( - figure=dash_ag_grid( - data_frame=df_complaints, + figure=infinite_scroll_ag_grid( + id="kpi_grid", + data_frame=pd.DataFrame(), columnDefs=COLUMN_DEFS, - dashGridOptions={"pagination": True}, + getRowId="params.data.Complaint ID", ) ) ], @@ -244,5 +245,19 @@ ), ) + +# CALLBACKS ----------------------------------------------------------------------------------- +@callback( + Output("kpi_grid", "getRowsResponse"), + Input("kpi_grid", "getRowsRequest"), +) +def infinite_scroll(request): + """Infinite scroll callback mechanism for the AG Grid.""" + if request is None: + return no_update + partial = df_complaints.iloc[request["startRow"] : request["endRow"]] + return {"rowData": partial.to_dict("records"), "rowCount": len(df_complaints.index)} + + if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run(debug=False) diff --git a/vizro-core/examples/kpi/utils/_charts.py b/vizro-core/examples/kpi/utils/_charts.py index d6ffd5147..83979bca7 100644 --- a/vizro-core/examples/kpi/utils/_charts.py +++ b/vizro-core/examples/kpi/utils/_charts.py @@ -2,12 +2,15 @@ from typing import List, Literal, Optional +import dash_ag_grid as dag import dash_bootstrap_components as dbc import pandas as pd import vizro.models as vm import vizro.plotly.express as px from dash import html from vizro.models.types import capture +from vizro.tables._dash_ag_grid import _DATA_TYPE_DEFINITIONS +from vizro.tables._utils import _set_defaults_nested # CUSTOM COMPONENTS ------------------------------------------------------------- @@ -40,6 +43,13 @@ def build(self): ) +class AgGridPage(vm.Page): + """Page without the on_page_load mechanism.""" + + def pre_build(self): + pass + + # CUSTOM CHARTS ---------------------------------------------------------------- @capture("graph") def bar( @@ -191,3 +201,36 @@ def choropleth( }, {"field": "Timely response?", "cellRenderer": "markdown", "headerName": "On time?", "flex": 3}, ] + + +@capture("ag_grid") +def infinite_scroll_ag_grid(data_frame: pd.DataFrame, **kwargs) -> dag.AgGrid: + """Implementation of infinite scroll AgGrid with sensible defaults to be used in [`AgGrid`][vizro.models.AgGrid].""" + defaults = { + "className": "ag-theme-quartz-dark ag-theme-vizro", + "columnDefs": [{"field": col} for col in data_frame.columns], + "rowModelType": "infinite", + "defaultColDef": { + "resizable": True, + "sortable": False, + "filter": False, + "filterParams": { + "buttons": ["apply", "reset"], + "closeOnApply": True, + }, + }, + "dashGridOptions": { + "dataTypeDefinitions": _DATA_TYPE_DEFINITIONS, + "animateRows": False, + "rowSelection": "multiple", + "rowBuffer": 0, + "cacheBlockSize": 100, + "cacheOverflowSize": 2, + "maxConcurrentDatasourceRequests": 2, + "infiniteInitialRowCount": 100, + "maxBlocksInCache": 10, + }, + "style": {"height": "100%"}, + } + kwargs = _set_defaults_nested(kwargs, defaults) + return dag.AgGrid(**kwargs) From 146adecd5e1437d1a1d5eec37f42ffbf76b4ade8 Mon Sep 17 00:00:00 2001 From: petar-qb Date: Fri, 7 Jun 2024 10:57:53 +0200 Subject: [PATCH 2/4] Minor changes --- vizro-core/examples/kpi/app.py | 4 ++-- vizro-core/examples/kpi/utils/_charts.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vizro-core/examples/kpi/app.py b/vizro-core/examples/kpi/app.py index 1befee1b7..09ba8342c 100644 --- a/vizro-core/examples/kpi/app.py +++ b/vizro-core/examples/kpi/app.py @@ -223,7 +223,7 @@ vm.AgGrid( figure=infinite_scroll_ag_grid( id="kpi_grid", - data_frame=pd.DataFrame(), + data_frame=df_complaints, columnDefs=COLUMN_DEFS, getRowId="params.data.Complaint ID", ) @@ -260,4 +260,4 @@ def infinite_scroll(request): if __name__ == "__main__": - Vizro().build(dashboard).run(debug=False) + Vizro().build(dashboard).run() diff --git a/vizro-core/examples/kpi/utils/_charts.py b/vizro-core/examples/kpi/utils/_charts.py index 83979bca7..6b252d73b 100644 --- a/vizro-core/examples/kpi/utils/_charts.py +++ b/vizro-core/examples/kpi/utils/_charts.py @@ -44,7 +44,7 @@ def build(self): class AgGridPage(vm.Page): - """Page without the on_page_load mechanism.""" + """Page without the on page load mechanism.""" def pre_build(self): pass From 80687c0027a2db5b2eb26f12cc118f9a7f61e4b3 Mon Sep 17 00:00:00 2001 From: petar-qb Date: Tue, 11 Jun 2024 14:29:33 +0200 Subject: [PATCH 3/4] Enable filtering and sorting for infinite scroll ag_grid --- vizro-core/examples/kpi/app.py | 92 ++++++++++++++++++++++-- vizro-core/examples/kpi/utils/_charts.py | 4 +- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/vizro-core/examples/kpi/app.py b/vizro-core/examples/kpi/app.py index 09ba8342c..44b3e5e9d 100644 --- a/vizro-core/examples/kpi/app.py +++ b/vizro-core/examples/kpi/app.py @@ -247,16 +247,98 @@ # CALLBACKS ----------------------------------------------------------------------------------- +operators = { + "greaterThanOrEqual": "ge", + "lessThanOrEqual": "le", + "lessThan": "lt", + "greaterThan": "gt", + "notEqual": "ne", + "equals": "eq", +} + + +def filter_df(df, data, col): + if data["filterType"] == "date": + crit1 = data["dateFrom"] + crit1 = pd.Series(crit1).astype(df[col].dtype)[0] + if "dateTo" in data: + crit2 = data["dateTo"] + crit2 = pd.Series(crit2).astype(df[col].dtype)[0] + else: + crit1 = data["filter"] + crit1 = pd.Series(crit1).astype(df[col].dtype)[0] + if "filterTo" in data: + crit2 = data["filterTo"] + crit2 = pd.Series(crit2).astype(df[col].dtype)[0] + if data["type"] == "contains": + df = df.loc[df[col].str.contains(crit1)] + elif data["type"] == "notContains": + df = df.loc[~df[col].str.contains(crit1)] + elif data["type"] == "startsWith": + df = df.loc[df[col].str.startswith(crit1)] + elif data["type"] == "notStartsWith": + df = df.loc[~df[col].str.startswith(crit1)] + elif data["type"] == "endsWith": + df = df.loc[df[col].str.endswith(crit1)] + elif data["type"] == "notEndsWith": + df = df.loc[~df[col].str.endswith(crit1)] + elif data["type"] == "inRange": + if data["filterType"] == "date": + df = df.loc[df[col].astype("datetime64[ns]").between_time(crit1, crit2)] + else: + df = df.loc[df[col].between(crit1, crit2)] + elif data["type"] == "blank": + df = df.loc[df[col].isnull()] + elif data["type"] == "notBlank": + df = df.loc[~df[col].isnull()] + else: + df = df.loc[getattr(df[col], operators[data["type"]])(crit1)] + return df + + @callback( Output("kpi_grid", "getRowsResponse"), Input("kpi_grid", "getRowsRequest"), ) def infinite_scroll(request): - """Infinite scroll callback mechanism for the AG Grid.""" - if request is None: - return no_update - partial = df_complaints.iloc[request["startRow"] : request["endRow"]] - return {"rowData": partial.to_dict("records"), "rowCount": len(df_complaints.index)} + dff = df_complaints.copy() + + if request: + if request["filterModel"]: + fils = request["filterModel"] + for k in fils: + try: + if "operator" in fils[k]: + if fils[k]["operator"] == "AND": + dff = filter_df(dff, fils[k]["condition1"], k) + dff = filter_df(dff, fils[k]["condition2"], k) + else: + dff1 = filter_df(dff, fils[k]["condition1"], k) + dff2 = filter_df(dff, fils[k]["condition2"], k) + dff = pd.concat([dff1, dff2]) + else: + dff = filter_df(dff, fils[k], k) + except: + pass + dff = dff + + if request["sortModel"]: + sorting = [] + asc = [] + for sort in request["sortModel"]: + sorting.append(sort["colId"]) + if sort["sort"] == "asc": + asc.append(True) + else: + asc.append(False) + dff = dff.sort_values(by=sorting, ascending=asc) + + lines = len(dff.index) + if lines == 0: + lines = 1 + + partial = dff.iloc[request["startRow"] : request["endRow"]] + return {"rowData": partial.to_dict("records"), "rowCount": lines} if __name__ == "__main__": diff --git a/vizro-core/examples/kpi/utils/_charts.py b/vizro-core/examples/kpi/utils/_charts.py index 6b252d73b..91c9bce61 100644 --- a/vizro-core/examples/kpi/utils/_charts.py +++ b/vizro-core/examples/kpi/utils/_charts.py @@ -212,8 +212,8 @@ def infinite_scroll_ag_grid(data_frame: pd.DataFrame, **kwargs) -> dag.AgGrid: "rowModelType": "infinite", "defaultColDef": { "resizable": True, - "sortable": False, - "filter": False, + "sortable": True, + "filter": True, "filterParams": { "buttons": ["apply", "reset"], "closeOnApply": True, From 5d99ad03e0bac0016942042ddbec6c7120259dd2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:30:11 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- vizro-core/examples/kpi/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vizro-core/examples/kpi/app.py b/vizro-core/examples/kpi/app.py index 44b3e5e9d..95ef241ac 100644 --- a/vizro-core/examples/kpi/app.py +++ b/vizro-core/examples/kpi/app.py @@ -2,7 +2,7 @@ import pandas as pd import vizro.models as vm -from dash import Input, Output, callback, no_update +from dash import Input, Output, callback from utils._charts import COLUMN_DEFS, KPI, AgGridPage, bar, choropleth, infinite_scroll_ag_grid, line, pie from utils._helper import clean_data_and_add_columns from vizro import Vizro