Hi, I’m currently creating an excel like web app that can visualize and analyze my huge csv data.
I almost done with all the core features using dash AG Grid
I used rowModelType that allows grouping, thanks to the help I received from the link below. I have modified the extractRowFromData() function to enable Filtering and sorting as well.
serverSide rowModelType in Dash AgGrid - Dash Python - Plotly Community Forum
However, I’m having difficulty implementing some additional features as bellow.
I not used to React of javascript, plus since Im using serverSide rowModel, Im struggling to do it by my self.
Please give me help if these features are possilbe.
-
As shown in the blue box in the picture below, I want to display the counter number of data inside the group when I fold them using sidebar. If you run my code, row count is displayed in other columns(the red box below). And, I want the group to be sorted in order of high group count number when I click the Group column.
-
When I fold by grouping, I want to display the first row data of hidden data in other columns to the red box in the picture below ( my current version of code shows the row group count number).
- I want to apply Advanced Filter to my dash ag table.
JavaScript Data Grid: Advanced Filter (ag-grid.com)
- I want to apply Statusbar(or Footer) to show the number of rows. show the number of currently visible rows in grid, so folded rows count as 1 row.
JavaScript Data Grid: Row Grouping - Group Footers (ag-grid.com)
JavaScript Data Grid: Status Bar (ag-grid.com)
I am using dash (version=2.14.1) and dash_ag_grid(version = 2.4.0)
Here is my current code that Im working on:
app.py
import dash_ag_grid as dag
from dash import Dash, Input, Output, html, dcc, State
import requests, json
import flask
import pandas as pd
app = Dash(__name__)
server = app.server
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
rowData = df.to_dict("records")
columnDefs = [
# Row group by country and by year is enabled.
{"field": "country","sortable": True,"filter": True},
{"field": "year", "sortable": True, "filter": True,},
{"field": "athlete", "sortable": True, "filter": True},
{"field": "age", "sortable": True, "filter": True},
{"field": "date", "sortable": True, "filter": True},
{"field": "sport", "sortable": True, "filter": True},
{"field": "total", "sortable": True, "filter": True},
]
def filterDf(df, data, col):
operators = {
"greaterThanOrEqual": "ge",
"lessThanOrEqual": "le",
"lessThan": "lt",
"greaterThan": "gt",
"notEqual": "ne",
"equals": "eq",
}
if "filter" in data:
crit1 = data["filter"]
crit1 = pd.Series(crit1).astype(df[col].dtype)[0]
if "type" in data:
if data["type"] == "contains":
df = df.loc[df[col].str.contains(crit1, na=False)]
elif data["type"] == "notContains":
df = df.loc[~df[col].str.contains(crit1, na=False)]
elif data["type"] == "startsWith":
df = df.loc[df[col].str.startswith(crit1, na=False)]
elif data["type"] == "notStartsWith":
df = df.loc[~df[col].str.startswith(crit1, na=False)]
elif data["type"] == "endsWith":
df = df.loc[df[col].str.endswith(crit1, na=False)]
elif data["type"] == "notEndsWith":
df = df.loc[~df[col].str.endswith(crit1, na=False)]
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)]
elif data["filterType"] == "set":
df = df.loc[df[col].isin(data["values"])]
return df
def extractRowsFromData(request, df):
response = []
print(request)
dff = df.copy()
if request["filterModel"]:
fils = request["filterModel"]
for k in fils:
try:
if "operator" in fils[k]:
if fils[k]["operator"] == "AND":
for fil in fils[k]["conditions"]:
dff = filterDf(dff, fil, k
)
else:
dffs = []
for fil in fils[k]["conditions"]:
dffs.append(filterDf(dff, fil, k))
dff = pd.concat(dffs)
else:
dff = filterDf(
dff, fils[k], k
)
except:
pass
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
)
groupBy = []
if request["rowGroupCols"]:
groupBy = [i["id"] for i in request["rowGroupCols"]]
agg = {}
if request["valueCols"]:
agg = {i["id"]: i["aggFunc"] for i in request["valueCols"]}
if not request["groupKeys"]:
if groupBy:
if agg:
dff = dff.groupby(groupBy[0]).agg(agg).reset_index()
else:
dff = dff.groupby(groupBy[0]).agg("count").reset_index()
else:
for i in range(len(request["groupKeys"])):
dff = dff[dff[request["rowGroupCols"][i]["id"]] == request["groupKeys"][i]]
if len(request["groupKeys"]) != len(groupBy):
if agg:
dff = (
dff.groupby(groupBy[: len(request["groupKeys"]) + 1])
.agg(agg)
.reset_index()
)
else:
dff = (
dff.groupby(groupBy[: len(request["groupKeys"]) + 1])
.agg("count")
.reset_index()
)
dff = dff.sort_values(
by=[i["colId"] for i in request["sortModel"]],
ascending=[i["sort"] == "asc" for i in request["sortModel"]],
)
return {
"rowData": dff.to_dict("records")[request["startRow"] : request["endRow"]],
"rowCount": len(dff),
}
@server.route("/api/serverData", methods=["POST"])
def serverData():
response = extractRowsFromData(flask.request.json, df)
return json.dumps(response)
grid = html.Div(
[
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
dashGridOptions={
"rowSelection": "multiple",
"sideBar": True,
"rowGroupPanelShow": "always",
"suppressRowGroupHidesColumns": True,
# "groupIncludeFooter": True,
# "groupIncludeTotalFooter": True,
# "rowGroupPanelSuppressSort": True,
# "enableAdvancedFilter": True,
},
defaultColDef=dict(
resizable=True, enableRowGroup=True, enableValue=True, enablePivot=True
),
enableEnterpriseModules=True,
rowModelType="serverSide",
style={"overflow": "auto", "resize": "both", "height": "60vh"},
),
]
)
app.layout = html.Div(
[
dcc.Markdown("Example: Organisational Hierarchy using Tree Data "),
grid,
]
)
app.clientside_callback(
"""async function (id) {
const updateData = (grid) => {
var datasource = createServerSideDatasource();
grid.setServerSideDatasource(datasource);
};
var grid;
grid = await window.dash_ag_grid.getApiAsync(id)
if (grid) {
updateData(grid)
}
return window.dash_clientside.no_update
}""",
Output("grid", "id"),
Input("grid", "id"),
)
if __name__ == "__main__":
app.run(debug=True)
js.file:
async function getServerData(request) {
response = await fetch('./api/serverData', {'method': 'POST', 'body': JSON.stringify(request),
'headers': {'content-type': 'application/json'}})
return response.json()
}
function createServerSideDatasource() {
const dataSource = {
getRows: async (params) => {
console.log('ServerSideDatasource.getRows: params = ', params);
var result = await getServerData(params.request)
console.log('getRows: result = ', result);
setTimeout(function () {
params.success(result);
}, 200);
},
};
return dataSource;
}