demo-kpi / utils /_charts.py
maxschulz-COL's picture
Add KPI dashboard (#1)
7002c2f verified
raw
history blame
5.78 kB
"""Contains custom components and charts used inside the dashboard."""
from typing import List, Literal, Optional
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
# CUSTOM COMPONENTS -------------------------------------------------------------
# Note: This is a static KPI Card only (it will not be reactive to controls). A new dynamic KPI Card component
# is currently in development.
class KPI(vm.VizroBaseModel):
"""Static custom `KPI` Card."""
type: Literal["kpi"] = "kpi"
title: str
value: str
icon: str
sign: Literal["delta-pos", "delta-neg"]
ref_value: str
def build(self):
return dbc.Card(
[
html.H4(self.title),
html.P(self.value),
html.Span(
[
html.Span(self.icon, className="material-symbols-outlined"),
html.Span(self.ref_value),
],
className=self.sign,
),
],
className="kpi-card-ref",
)
# CUSTOM CHARTS ----------------------------------------------------------------
@capture("graph")
def bar(
x: str,
y: str,
data_frame: pd.DataFrame,
top_n: int = 15,
custom_data: Optional[List[str]] = None,
):
df_agg = data_frame.groupby(y).agg({x: "count"}).sort_values(by=x, ascending=False).reset_index()
fig = px.bar(
data_frame=df_agg.head(top_n),
x=x,
y=y,
orientation="h",
text=x,
color_discrete_sequence=["#1A85FF"],
custom_data=custom_data,
)
fig.update_layout(xaxis_title="# of Complaints", yaxis=dict(title="", autorange="reversed")) # noqa: C408
return fig
@capture("graph")
def line(x: str, y: str, data_frame: pd.DataFrame):
df_agg = data_frame.groupby(x).agg({y: "count"}).reset_index()
fig = px.area(
data_frame=df_agg,
x=x,
y=y,
color_discrete_sequence=["#1A85FF"],
title="Complaints over time",
)
fig.update_layout(xaxis_title="Date Received", yaxis_title="# of Complaints", title_pad_t=4)
return fig
@capture("graph")
def pie(
names: str,
values: str,
data_frame: pd.DataFrame = None,
title: Optional[str] = None,
custom_order: Optional[List[str]] = None,
):
df_agg = data_frame.groupby(names).agg({values: "count"}).reset_index()
# Apply custom order so colors are applied correctly to the pie chart
order_mapping = {category: index for index, category in enumerate(custom_order)}
df_sorted = df_agg.sort_values(by=names, key=lambda names: names.map(order_mapping))
fig = px.pie(
data_frame=df_sorted,
names=names,
values=values,
color_discrete_sequence=["#1a85ff", "#7ea1ee", "#adbedc", "#df658c", "#d41159"],
title=title,
hole=0.4,
)
fig.update_layout(legend_x=1, legend_y=1, title_pad_t=2, margin=dict(l=0, r=0, t=60, b=0)) # noqa: C408
fig.update_traces(sort=False)
return fig
@capture("graph")
def choropleth(
locations: str,
color: str,
data_frame: pd.DataFrame = None,
title: Optional[str] = None,
custom_data: Optional[List[str]] = None,
):
df_agg = data_frame.groupby(locations).agg({color: "count"}).reset_index()
fig = px.choropleth(
data_frame=df_agg,
locations=locations,
color=color,
color_continuous_scale=[
"#ded6d8",
"#f3bdcb",
"#f7a9be",
"#f894b1",
"#f780a3",
"#f46b94",
"#ee517f",
"#e94777",
"#e43d70",
"#df3168",
"#d92460",
"#d41159",
],
scope="usa",
locationmode="USA-states",
title=title,
custom_data=custom_data,
)
fig.update_coloraxes(colorbar={"thickness": 10, "title": {"side": "right"}})
return fig
# TABLE CONFIGURATIONS ---------------------------------------------------------
CELL_STYLE = {
"styleConditions": [
{
"condition": "params.value == 'Closed with explanation'",
"style": {"backgroundColor": "#1a85ff"},
},
{
"condition": "params.value == 'Closed with monetary relief'",
"style": {"backgroundColor": "#d41159"},
},
{
"condition": "params.value == 'Closed with non-monetary relief'",
"style": {"backgroundColor": "#adbedc"},
},
{
"condition": "params.value == 'Closed without relief'",
"style": {"backgroundColor": "#7ea1ee"},
},
{
"condition": "params.value == 'Closed with relief'",
"style": {"backgroundColor": "#df658c"},
},
{
"condition": "params.value == 'Closed'",
"style": {"backgroundColor": "#1a85ff"},
},
]
}
COLUMN_DEFS = [
{"field": "Complaint ID", "cellDataType": "text", "headerName": "ID", "flex": 3},
{"field": "Date Received", "cellDataType": "text", "headerName": "Date", "flex": 3},
{"field": "Channel", "cellDataType": "text", "flex": 3},
{"field": "State", "cellDataType": "text", "flex": 2},
{"field": "Product", "cellDataType": "text", "flex": 5},
{"field": "Issue", "cellDataType": "text", "flex": 5},
{
"field": "Company response - detailed",
"cellDataType": "text",
"cellStyle": CELL_STYLE,
"headerName": "Company response",
"flex": 6,
},
{"field": "Timely response?", "cellRenderer": "markdown", "headerName": "On time?", "flex": 3},
]