Hi community! I stayed on country data this week, and it seems a pretty straightforward, so I tried with AG Grid just to practice working with tables. Just to remark, AG Grid is gigantic! So many features and attributes made it overwhelming at times, at least to me for now!
Code
'''Just some imports'''
from dash import Dash, dcc, callback, Input, Output, State, clientside_callback, _dash_renderer, Patch, ALL, html, no_update
from dash.exceptions import PreventUpdate
import dash_mantine_components as dmc
import plotly.express as px
import plotly.io as pio
import pandas as pd
import dash_ag_grid as dag
from dash_iconify import DashIconify
_dash_renderer._set_react_version("18.2.0")
dmc.add_figure_templates(default="mantine_dark")
df = pd.read_csv('https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2025/week-3/ODL-Export-Countries.csv')
# countries_no_program = (df
# .loc[df['RP Financing $'].isna() & df['FA Financing $'].isna()]
# .sort_values(by='Region'))
# # countries_no_program
# [{'label': val, 'value': val} for val in df['Region'].unique()]
# reg_val = 'Asia-Pacific'
# (df
# .query("Region in @reg_val")
# .sort_values(by='# RP', ascending=False)
# )
df10 = (df
.query('SIDS == True') # 'Yes' returns only countries classified as Small Island Developing States
.query('LDCs == True') # 'Yes' returns only countries classified as Least Developed Countries
).groupby('Region', as_index=False).agg({'RP Financing $':'sum',
'FA Financing $':'sum',
'# RP':'count',
'# FA':'count'})
df10_melted = (df10
.melt(id_vars='Region',
value_vars=['RP Financing $','FA Financing $'],
var_name='RP-FA',
value_name='$Financing'
)
)
fig1 = px.histogram(df10_melted,
x='Region',
y='$Financing',
color='RP-FA',
histfunc='sum',
barmode='group', # ['stack', 'group', 'overlay', 'relative']
template='simple_white'
)
# fig1
df11_melted = (df10
.melt(id_vars='Region',
value_vars=['# RP','# FA'],
var_name='RP_FA',
value_name='# Projects'
)
)
# df11_melted
fig2 = px.histogram(df11_melted,
x='Region',
y='# Projects',
color='RP_FA',
# histfunc='sum',
barmode='group', # ['stack', 'group', 'overlay', 'relative']
template='simple_white'
)
# fig2
## ****APP****
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
app = Dash(__name__, external_stylesheets=dmc.styles.ALL)
data = [["SIDS", '10'], ["LDCs", '01'], ["Both True", '11'], ["Both False", '00'], ["None Filter", 'all_rows']]
radio_group = dmc.RadioGroup(
dmc.Group([dmc.Radio(l, value=k) for l, k in data], my=10),
id="radiogroup_simple",
deselectable=False,
value='all_rows',
label="Add filter (SIDS and/or LDCs)",
size="md",
mb=10,
)
filter_function = {
'10': "params.data.SIDS == true && params.data.LDCs == false",
'01': "params.data.SIDS == false && params.data.LDCs == true",
'11': "params.data.SIDS == true && params.data.LDCs == true",
'00': "params.data.SIDS == false && params.data.LDCs == false",
'all_rows': "true"
}
table = dag.AgGrid(
id='table_1',
rowData=df.to_dict("records"),
columnDefs= [{"field": col} for col in df.columns.to_list()],
columnSize="autoSize",
# defaultColDef={"filter": True},
dashGridOptions={'pagination':True, # "animateRows": False, "rowSelection":'single'
'paginationPageSize':15,
"animateRows": False, #"rowSelection":'single'
},
# className="ag-theme-alpine-dark"
)
## ****Graph Cards****
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# def make_graph_card(fig, index):
# return dmc.GridCol(
# dmc.Card(dcc.Graph(figure=fig, id={"index": index}),
# withBorder=True,
# my='xs',
# shadow="sm",
# radius="md",),
# span={"base": 12, "md": 6}
# )
# graphs = dmc.Grid(
# [
# make_graph_card(fig1, "fig1"),
# make_graph_card(fig2, "fig2"),
# ],
# gutter="xl",
# style={"height": 800}
# )
graphs = dmc.Grid(
[
dmc.GridCol(
dmc.Card(
dcc.Graph(
id='fig1_id',#{"index": "fig1_id"},#
figure={},
),
withBorder=True,
my='xs',
shadow="sm",
radius="md"
),
span={"base": 12, "md": 6},
),
dmc.GridCol(
dmc.Card(
dcc.Graph(
id='fig2_id',#{"index": "fig2_id"},
figure={},
),
withBorder=True,
my='xs',
shadow="sm",
radius="md"
),
span={"base": 12, "md": 6},
)
],
gutter="xl",
style={"height": 800}
)
theme_toggle = dmc.Switch(
offLabel=DashIconify(icon="radix-icons:sun", width=15, color=dmc.DEFAULT_THEME["colors"]["yellow"][8]),
onLabel=DashIconify(icon="radix-icons:moon", width=15, color=dmc.DEFAULT_THEME["colors"]["yellow"][6]),
id="color-scheme-toggle",
persistence=True,
color="grey",
)
# ****LAYOUT****
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
layout = dmc.Container([
dmc.Group([
dmc.Title("Funded Activities Dashboard Navigator", mt="md", order=1),
theme_toggle,
], justify="space-between"),
dmc.Box([
dmc.Title("Money and projects delivered by Region", my='md', order=2),
]),
radio_group,
table,
dmc.Box([
dcc.Markdown("_**SIDS**, 'Yes' returns only readiness grants targeting Small Island Developing States_"),
dcc.Markdown("_**LDCs**, 'Yes' returns only readiness grants targeting Least Developed Countries_")
],mt='10px', ps='10px'), # bd="1px solid",
graphs,
], fluid=True)
app.layout = dmc.MantineProvider(layout)
# ****Switching Light to dark****
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
clientside_callback(
"""
(switchOn) => {
document.documentElement.setAttribute('data-mantine-color-scheme', switchOn ? 'dark' : 'light');
return window.dash_clientside.no_update
}
""",
Output("color-scheme-toggle", "id"),
Input("color-scheme-toggle", "checked"),
)
# ****Callback to switch AGGrid themes from light to dark mode****
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@callback(
Output("table_1", "className"),
Input("color-scheme-toggle", "checked"),
)
def update_grid_theme(switch_on):
if switch_on:
return "ag-theme-alpine-dark"
return "ag-theme-alpine"
# ****Callback to switching themes figures from light to dark mode****
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# @callback(
# Output({"index": ALL}, "figure")#, allow_duplicate=True),#Output({"type": "graph", "index": ALL}, "figure", allow_duplicate=True),
# Input("color-scheme-toggle", "checked"),
# State({"index": ALL}, "id"),#State({"type": "graph", "index": ALL}, "id"),
# prevent_initial_call=True
# )
# def update_figure_template(switch_on, ids):
# print(ids)
# template = pio.templates["mantine_dark"] if switch_on else pio.templates["mantine_light"]
# patched_figures = []
# for i in ids:
# patched_fig = Patch()
# patched_fig["layout"]["template"] = template
# patched_figures.append(patched_fig)
# return patched_figures
# Filter table AGG based on RadioButton
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@callback(
Output("table_1", "dashGridOptions", allow_duplicate=True),
Input("radiogroup_simple", "value"),
prevent_initial_call=True,
)
def update_external_filter(filter_value):
# print(filter_value)
return {
# if filter_value is not 'everyone', then we will start filtering
"isExternalFilterPresent": {"function": "true" if filter_value != 'all_rows' else "false"},
"doesExternalFilterPass": {"function": filter_function[filter_value]}
}
## Updating figures based on virtualRowData
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@callback(
Output('fig1_id', 'figure'),
Output('fig2_id', 'figure'),
Input('table_1', 'virtualRowData'),
Input("color-scheme-toggle", "checked"),
pervent_initial_call=True
)
def update_fig_based_AGG(vdata, switch_on):
template = pio.templates["mantine_dark"] if switch_on else pio.templates["mantine_light"]
if vdata == None:
raise PreventUpdate
else:
dff = pd.DataFrame(vdata)
dff0 = (dff
.groupby('Region', as_index=False)
.agg({'RP Financing $':'sum',
'FA Financing $':'sum',
'# RP':'count',
'# FA':'count'})
)
dff1 = (dff0
.melt(id_vars='Region',
value_vars=['RP Financing $','FA Financing $'],
var_name='RP-FA',
value_name='$Financing')
)
fig10 = px.histogram(dff1,
x='Region',
y='$Financing',
color='RP-FA',
histfunc='sum',
barmode='group', # ['stack', 'group', 'overlay', 'relative']
template=template,
title="Total <b>'USD$'</b> of Readiness Programmes and Funded Activities<br> by Region.",
labels={'Region': ''}
).update_layout(legend_title_text='').update_yaxes(title_text='')
dff2 = (dff0
.melt(id_vars='Region',
value_vars=['# RP','# FA'],
var_name='RP_FA',
value_name='# Projects'
)
)
fig20 = px.histogram(dff2,
x='Region',
y='# Projects',
color='RP_FA',
# histfunc='sum',
barmode='group', # ['stack', 'group', 'overlay', 'relative']
template='simple_white',
title="Number <b>'#'</b> of Readiness Programmes and Funded Activities<br> by Region.",
labels={'Region': ''}
).update_layout(legend_title_text='').update_yaxes(title_text='')
return fig10, fig20
if __name__ == "__main__":
app.run(debug=True, jupyter_mode='external', port=8085)