Open Coding-with-Adam opened 8 months ago
Hi @Coding-with-Adam, @JorgeMiguelGomes
This is what the data-insights page currently look like in my code. I modified the code a bit for the page to work as a standalone app. You can view this page along with the whole app on https://response-reporting-app.onrender.com/
from dash import Dash, html, dcc, callback, Output, Input, State
import dash
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template
import pandas as pd
import plotly.express as px
def create_graph_card(id, fig, className='p-2'):
height = "100%"
card = dbc.Card(
[dcc.Graph(id=id, figure=fig, style={'height': height}, config={'displayModeBar': False})],
style={'height': height},
className=className
)
return card
df = pd.read_csv("https://raw.githubusercontent.com/Coding-with-Adam/response-reporting-dashboard/main/dummy_data_100_wNan.csv")
df.columns = ['timestamp', 'reporting_entity', 'reporting_user', 'platform', 'url', 'report_type', 'screenshot_url', 'answer_date', 'platform_decision', 'policy', 'appeal']
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['answer_date'] = pd.to_datetime(df['answer_date'], errors='coerce')
unique_platforms = df['platform'].unique()
color_mapping = dict(zip(unique_platforms, px.colors.qualitative.G10))
df_pie = df.groupby('platform', as_index=False)['timestamp'].size().sort_values(by='size', ascending=False)
fig1 = px.pie(df_pie, values='size', names='platform', hole=.5, title='Total reports by platform',
color='platform', color_discrete_map=color_mapping)\
.update_traces(marker=dict(line=dict(color='#FFFFFF', width=2)), sort=True, direction='clockwise')\
.update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5), legend=dict(xanchor='right', x=1))
df_line = df.groupby(['timestamp', 'platform'], as_index=False)['platform'].size()
fig2 = px.line(df_line, x='timestamp', y='size', color='platform', color_discrete_map=color_mapping,
title='Daily reports by Platform', markers=True,
labels={'Date': 'timestamp', 'Count': 'size', 'Platform': 'platform'})\
.update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5))
df_average_response_time = df.copy().dropna(subset=['answer_date'])
df_average_response_time['response_time'] = (df_average_response_time['answer_date'] - df_average_response_time['timestamp']).dt.days
average_response_time = df_average_response_time.groupby('platform', as_index=False)['response_time'].mean().sort_values(by='response_time', ascending=False)
fig3 = px.bar(average_response_time, x='platform', y='response_time', title='Average responce time by platform (Days)',
).update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5),
showlegend=False)
fig4 = px.histogram(df, x='platform_decision', color='platform', color_discrete_map=color_mapping,
title='Decisions by platform', barmode='group').\
update_layout(margin=dict(l=0, r=0, t=30, b=0)).update_traces(marker=dict(line=dict(color='#FFFFFF', width=2))).update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5),
).update_yaxes(categoryorder='total descending')
fig1_card = create_graph_card('fig1', fig1)
fig2_card = create_graph_card('fig2', fig2)
fig3_card = create_graph_card('fig3', fig3)
fig4_card = create_graph_card('fig4', fig4)
load_figure_template("yeti")
app = dash.Dash(
suppress_callback_exceptions=True,
external_stylesheets=[dbc.themes.YETI, dbc.icons.BOOTSTRAP]
)
column_heigh_style = {"height": "100%"}
app.layout = dbc.Container(
[
dbc.Row([dbc.Col(fig1_card, width=6, style=column_heigh_style), dbc.Col(fig2_card, width=6, style=column_heigh_style)],
className='my-4',
style={"height": '45vh'},
justify='around'),
dbc.Row([dbc.Col(fig3_card, width=6, style=column_heigh_style), dbc.Col(fig4_card, width=6, style=column_heigh_style)],
className='my-4',
style={"height": '45vh'},
justify='around'),
],
fluid=True,
style={"height": '100vh', 'background-color': '#F4F6F7'},
)
if __name__ == '__main__':
app.run_server(debug=True)
Thank you @Moh-Ozzi Your data-insights page was added successfully.
Hello @Coding-with-Adam and @JorgeMiguelGomes These are my codes for the data insights page. I have updated the same in the data-insights.py file of the main branch. I require some assistance with the graph on the third tab. Otherwise kindly try out the full page and share your feedback.
import dash
from dash import Dash, html, dcc, callback, Output, Input, State, no_update
import dash_mantine_components as dmc
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
import pandas as pd
import plotly.express as px
df = pd.read_csv("https://raw.githubusercontent.com/Coding-with-Adam/response-reporting-dashboard/main/dummy_data_10000_wNan.csv")
df['Timestamp'] = pd.to_datetime(df['Timestamp'], format='%m/%d/%Y %H:%M')
df['Month'] = df['Timestamp'].dt.month
df['Year'] = df['Timestamp'].dt.year
df['Answer Date'] = pd.to_datetime(df['Answer Date'], format='%m/%d/%Y %H:%M')
mo = [{'label':m,'value':m} for m in sorted(df['Month'].unique())]
mo.insert(0, {'label': 'All months', 'value': 'all'})
yo = [{'label':y,'value':y} for y in sorted(df['Year'].unique())]
yo.insert(0, {'label': 'All years', 'value': 'all'})
df['Response Time'] = df['Answer Date'] - df['Timestamp']
df['Response Days'] = df['Response Time'].dt.days
# Report table
grid = dag.AgGrid(
id = "reports-table",
rowData = df.to_dict("records"),
columnDefs = [{"field": i} for i in df.columns],
columnSize = "sizeToFit",
defaultColDef = {"filter": True},
dashGridOptions = {"pagination": True, "paginationPageSize":13},
style = {"height": 650}
)
# Avg response time line chart
fig3 = px.line(df, x=df['Platform'].unique(), y=df.groupby(['Platform'])['Response Days'].mean(), text=df.groupby(['Platform'])['Response Days'].mean())
fig3.update_layout(xaxis_title="Platforms", yaxis_title="Avg Response Time (days)")
fig3.update_traces(textposition="top center", texttemplate='%{y:.2f}')
app = Dash(suppress_callback_exceptions=True)
app.layout = dmc.MantineProvider(
theme={"colorScheme": "dark"},
withGlobalStyles=True,
children=[
html.H1(children=["Transparency Reporting Data Insights"], style={"color": "white"}),
dmc.Tabs(
[
dmc.TabsList(
[
dmc.Tab(html.B("Reports Table"), value="1"),
dmc.Tab(html.B("Reports by Platform and Types"), value="2"),
dmc.Tab(html.B("Platform Decisions on Report Types"), value="3"),
dmc.Tab(html.B("Avg Response Time by Platform"), value="4")
]
),
],
id="tabs-example",
value="1",
),
html.Div(id="tabs-content", style={"paddingTop": 10}),
]
)
@callback(
Output("tabs-content", "children"),
Input("tabs-example", "value"))
def render_content(active):
if active == "1":
return [grid]
elif active == "2":
return [
html.P(children=["Choose the options from the dropdown menus and click on the 'Submit' button to get the desired results"], style={"color": "white"}),
html.P(children=["Hovering upon a particular Platform slice, will show the count of the Report Types of that particular Platform"], style={"color": "white"}),
html.Div([
html.Label(children=['Month:'], style={'color': 'white', 'font-weight': 'bold'}),
dcc.Dropdown(
id='month-variable',
options=mo,
value=['all'],
multi=True,
searchable=True
),
], style={'display': 'inline-block', 'margin-right': 20, 'width': 300}),
html.Div([
html.Label(children=['Year:'], style={'color': 'white', 'font-weight': 'bold'}),
dcc.Dropdown(
id='year-variable',
options=yo,
value=['all'],
multi=True,
searchable=True
),
], style={'display': 'inline-block', 'margin-right': 20, 'width': 300}),
html.Div([
dbc.Button(
id='pie-button',
children="Submit",
style={'height': 50, 'width': 300}
),
], style={'display': 'inline-block', 'margin-right': 20, 'height': 50, 'width': 300}),
dcc.Graph(id='graph1', clear_on_unhover=True, style={"height": 600}),
dcc.Tooltip(id="graph-tooltip")
]
elif active == "3":
return [
html.P(children=["Choose the options from the dropdown menus and click on the 'Submit' button to get the desired results"], style={"color": "white"}),
html.Div([
html.Label(children=['Month:'], style={'color': 'white', 'font-weight': 'bold'}),
dcc.Dropdown(
id='month-variable',
options=mo,
value=['all'],
multi=True,
searchable=True,
placeholder="Select a month"
),
], style={'display': 'inline-block', 'margin-right': 20, 'width': 300}),
html.Div([
html.Label(children=['Year:'], style={'color': 'white', 'font-weight': 'bold'}),
dcc.Dropdown(
id='year-variable',
options=yo,
value=['all'],
multi=True,
searchable=True,
placeholder="Select a year"
),
], style={'display': 'inline-block', 'margin-right': 20, 'width': 300}),
html.Div([
dbc.Button(
id='bar-button',
children="Submit",
style={'height': 50, 'width': 300}
),
], style={'display': 'inline-block', 'margin-right': 20, 'height': 50, 'width': 300}),
dcc.Graph(id='graph2')
]
else:
return [
dcc.Graph(id='graph3', figure=fig3, style={"height": 600})
]
@callback(
Output('graph1', 'figure'),
Input('pie-button', 'n_clicks'),
State('month-variable', 'value'),
State('year-variable', 'value'),
prevent_initial_call=True
)
def update_pie_chart(_, selected_month, selected_year):
if selected_month==['all'] and selected_year==['all']:
df_sub = df
else:
df_sub = df[(df['Month'].isin(selected_month)) & (df['Year'].isin(selected_year))]
fig1 = px.pie(df_sub, names='Platform', hole=0.8, color_discrete_sequence=px.colors.sequential.Agsunset)
fig1.update_traces(textposition='outside', textinfo='value+percent+label', rotation=50)
fig1.update_layout(margin=dict(t=50, b=35, l=0, r=0), showlegend=False,
plot_bgcolor='#fafafa', paper_bgcolor='#fafafa',
font=dict(size=17, color='#000000'),
hoverlabel=dict(bgcolor="#444", font_size=13, font_family="Lato, sans-serif"))
fig1.add_layout_image(
dict(
source="https://i.postimg.cc/zXr1NjnK/platforms.jpg",
xref="paper", yref="paper",
x=0.48, y=0.48,
sizex=0.47, sizey=0.47,
xanchor="center", yanchor="middle", sizing="contain",
)
)
return fig1
@callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph1", "hoverData"),
prevent_initial_call=True
)
def update_tooltip_content(hoverData):
if hoverData is None:
return no_update
pt = hoverData["points"][0]
bbox = pt["bbox"]
dff = df[df.Platform == pt["label"]]
prt_counts = dff['Report Type'].value_counts().sort_values(ascending=True)
fig_bar = px.bar(dff, y=prt_counts.index, x=prt_counts.values, title=f"Types of Reporting - {pt['label']}", text=prt_counts.values, orientation='h')
fig_bar.update_layout(yaxis_title="Report Types", xaxis_title="Count")
children = [dcc.Graph(figure=fig_bar, style={"height": 300, "width": 600})]
return True, bbox, children
@callback(
Output('graph2', 'figure'),
Input('bar-button', 'n_clicks'),
State('month-variable', 'value'),
State('year-variable', 'value'),
prevent_initial_call=True
)
def update_bar_chart(_, selected_month, selected_year):
if selected_month==['all'] and selected_year==['all']:
df_sub = df
else:
df_sub = df[(df['Month'].isin(selected_month)) & (df['Year'].isin(selected_year))]
rt_counts = df['Report Type'].value_counts().sort_values(ascending=False)
df['Platform Decision'].fillna("Unknown", inplace=True)
color_map = {"Removed": "green", "Demoted": "blue", "No Action": "red", "Unknown": "gray"}
fig2 = px.bar(df, x="Report Type", height=600, color="Platform Decision")
fig2.update_layout(xaxis_title="Report Types", yaxis_title="Count")
return fig2
if __name__ == '__main__':
app.run(debug=True)
Hi @jmanali1996 For now, I hashtagged out your code, because I can't get it to run.
I'm getting an error when I try to run your code, when the first tab is clicked. Instead of showing the grid, I get this error:
dash.exceptions.InvalidCallbackReturnValue: The callback for
<Output
tabs-content.children>
returned a value having typeAgGrid
which is not JSON serializable. The value in question is either the only value returned, or is in the top level of the returned list, and has string representation
Regarding the third tab, what kind of assistance do you need? Can you please be more specific. Is the year dropdown not working?
Hello @Coding-with-Adam @jmanali1996 @Moh-Ozzi for the records let me share here a little of my idea of insight page implementation I will add some info about what to expect in the project chat. IMPORTANT: this code implement some panel created on the fligth.. to avoid exceptions in the callback you should add at the end of the app.py the following params if name == "main": app.run_server(debug=True, dev_tools_ui=False, dev_tools_props_check=False)
import dash_bootstrap_components as dbc
from dash import html, dcc, dash_table, Input, Output, callback
import dash
import pandas as pd
import plotly.express as px
import numpy as np
#
# from datetime import datetime
dash.register_page(__name__, path="/data-insights", order=1)
################### data retrieval#############################
df = pd.read_csv(
"https://raw.githubusercontent.com/Coding-with-Adam/response-reporting-dashboard/main/dummy_data_1000_wNan.csv"
)
#################### data Prep section#########################
##add extra-cols to df -- for overall views
df["isOpen"] = np.where(df["Answer Date"].isna(), True, False)
df["reportAge"] = (
(pd.to_datetime(pd.Timestamp.today(), unit="s") - pd.to_datetime(df["Timestamp"]))
.dropna()
.astype(str)
.str.replace(" days.*$", "", regex=True)
).astype(int)
df["reportLifetime"] = (
(pd.to_datetime(df["Answer Date"]) - pd.to_datetime(df["Timestamp"]))
.dropna()
.astype(str)
.str.replace(" days", "")
.astype(int)
)
##data aggregation,subset for Report status view container "view1"
group1_1 = df.groupby(by=["Platform", "isOpen"]).count().reset_index()
df1_1 = (
df[df.isOpen == True]
.sort_values(by=["reportAge"], ascending=False)
.reset_index()
.loc[0:10][["Timestamp", "Platform", "Report Type"]]
)
##data aggregation, subset for Report status view container "view2" #########
group2_1 = df.groupby(by=["Platform", "Report Type"]).count().reset_index()
group2_2 = df.groupby(by=["Platform", "Report Type", "Appeal"]).count().reset_index()
group2_3 = df.groupby(by=["Report Type", "Appeal"]).count().reset_index()
####################selector setup##################################
viewOptions = {
a: b
for (a, b) in enumerate(
[
"Scorecard(PLACEHOLDER)",
"Report Status",
"Report Type",
"Users Activities(PLACEHOLDER)",
]
)
}
viewSelector = dcc.Dropdown(
options=viewOptions,
placeholder="Select a data view",
value=1,
clearable=False,
id="viewSelector-id",
className="ms-5 border-primary border-1 text-primary fw-normal rounded",
)
platformSelector = dcc.Dropdown(
options=df.Platform.unique().tolist(),
placeholder="Select Platforms",
clearable=True,
multi=True,
id="platformSelector-id",
className="ms-5 border-primary border-1 bg-dark text-primary fw-normal rounded",
)
timeRangeSelector = dcc.DatePickerRange(
id="my-date-picker-range",
min_date_allowed=pd.to_datetime(df["Timestamp"]).sort_values(ascending=True).loc[0],
max_date_allowed=pd.to_datetime(df["Timestamp"])
.sort_values(ascending=False)
.loc[df.shape[0] - 1],
initial_visible_month=pd.to_datetime(df["Timestamp"])
.sort_values(ascending=False)
.loc[0],
end_date=pd.to_datetime(df["Timestamp"])
.sort_values(ascending=False)
.loc[df.shape[0] - 1],
)
############# view1 --> Report status vizs ######################
fig1_1 = px.treemap(
group1_1,
path=[px.Constant("All Platforms"), "Platform", "isOpen"],
values="Timestamp",
color="isOpen",
# color_continuous_midpoint=np.average(group["isOpen"]),
color_discrete_map={"(?)": "#d0e0e3", True: "#0b5394", False: "lightgrey"},
hover_data=["Platform"],
# color_continuous_scale="Blues",
)
# fig1_1.update_layout(showlegend=True)
fig1_1.data[0].textinfo = "percent parent+value"
# color_continuous_midpoint=np.average(group['isOpen']))
fig1_1.update_layout(
title="<b>Reports Status ( % open/closed) by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_1.update_layout(margin=dict(t=50, l=25, r=25, b=25))
fig1_1.update_traces(
textposition="middle center",
textfont_size=14,
)
fig2 = px.bar(
group1_1,
x="Timestamp",
y="Platform",
color="isOpen",
orientation="h",
# hover_data=["tip", "size"],
# height=400,
color_discrete_sequence={"(?)": "#d0e0e3", True: "#0b5394", False: "lightgrey"},
title="<b>Reports Status ( open/closed count) by Platform</b>",
text="Timestamp",
)
fig2.update_layout(
title="<b>Reports Status ( open/closed count) by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_3 = px.bar(
df[df.isOpen == True]
.sort_values(by=["reportAge"], ascending=False)
.reset_index()
.loc[0:10],
x="reportAge",
y="Timestamp",
color="Platform",
orientation="h",
color_discrete_sequence=px.colors.qualitative.Pastel,
text="reportAge",
)
fig1_3.update_layout(
title="<b>Open Report - Oldest Top 10 [days]</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
table1_1 = dash_table.DataTable(
data=df1_1.to_dict("records"),
columns=[{"name": i, "id": i} for i in df1_1.columns],
style_cell={"textAlign": "left"},
style_header={"backgroundColor": "#446e9b", "fontWeight": "bold", "color": "white"},
style_data={"color": "grey", "backgroundColor": "white"},
)
fig1_2 = px.ecdf(
df[df.isOpen == 0],
x="reportLifetime",
color="Platform",
markers=True,
lines=False,
color_discrete_sequence=px.colors.qualitative.Pastel,
)
fig1_2.update_layout(
title="<b>Closed Report Lifetime Distribution by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_2.add_hline(
y=0.5, line_width=1, line_dash="dash", line_color="blue", annotation_text="median"
)
############# view2 --> Report type vizs ######################
fig2_1 = px.treemap(
group2_1,
path=[px.Constant("all"), "Platform", "Report Type"],
values="Timestamp",
color="Timestamp",
hover_data=["Platform"],
color_continuous_scale="RdBu_r",
)
fig2_1.update_layout(showlegend=True)
fig2_1.data[0].textinfo = "label+percent parent+value"
fig2_1.update_layout(
title="<b>Reports Type by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig2_1.update_layout(margin=dict(t=50, l=25, r=25, b=25))
fig2_1.update_traces(
textposition="middle center",
textfont_size=14,
)
fig2_2 = px.bar(
group2_2[group2_2["Report Type"] == df["Report Type"].unique().tolist()[0]],
x="Platform",
y="Timestamp",
color="Appeal",
facet_col="Report Type",
title="<b>Report Appeal by Platform, Report Type </b>",
text="Timestamp",
color_discrete_sequence=["#0b5394", "lightgrey"],
)
fig2_2.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1]))
fig2_2.update_layout(yaxis_title="Report Count")
############ view1 assembly ####################
view1 = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig1_1),
],
width=5,
),
dbc.Col(
[
dcc.Graph(figure=fig1_2),
],
width=5,
),
],
className="mb-2",
),
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig1_3),
],
width=5,
),
dbc.Col(
[
html.P(
"Open Reports - oldest Top 10 - details",
className="ps-5 pt-3 text-primary fw-bold",
),
table1_1,
],
width=5,
),
]
),
],
fluid=False,
id="view1-id",
className="ms-5 ",
)
############ view2 assembly ####################
view2 = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig2_1),
],
width=11,
),
],
className="mb-2 ",
),
dbc.Row(
[
dbc.Col(
[
html.P(
"Report Type Selector",
className="ps-5 pt-3 text-primary fw-bold",
),
dcc.Dropdown(
options=df["Report Type"].unique().tolist(),
clearable=False,
value=df["Report Type"].unique().tolist()[0],
id="ReportTypeSelector-id",
className="ms-5 border-primary border-1 bg-dark text-primary fw-normal rounded",
),
],
width=3,
),
dbc.Col(
[
dcc.Graph(figure=fig2_2, id="fig2_2-id"),
],
width=7,
),
]
),
],
fluid=False,
id="view2-id",
className="ms-5 vh-100 ",
style={"display": "flex", "flex-direction": "column"},
)
viewContent = dbc.Container([view1], id="viewContent-id")
# page layout
layout = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
html.P(
"Data View Selector",
className="ps-5 pt-3 text-primary fw-bold",
),
viewSelector,
],
width=3,
),
dbc.Col(
[
html.P(
"Time Range Selector (disabled)",
className="ps-5 pt-3 text-primary fw-bold",
),
timeRangeSelector,
],
width=3,
),
],
align="center",
),
dbc.Row(
[
dbc.Col(
[
html.Hr(),
],
className="pe-5",
),
],
align="center",
),
dbc.Row(
dbc.Col([viewContent]),
align="center",
),
],
className="",
)
@callback(
Output("viewContent-id", "children"),
Input("viewSelector-id", "value"),
prevent_initial_call=True,
suppress_callback_exceptions=True,
)
def updateView(selValue):
if selValue == 1:
return view1
else:
return view2
@callback(
Output("fig2_2-id", "figure"),
Input("ReportTypeSelector-id", "value"),
prevent_initial_call=True,
suppress_callback_exceptions=True,
)
def updateFig(selValue):
group2_2ff = group2_2[group2_2["Report Type"] == selValue]
fig2_2 = px.bar(
group2_2ff,
x="Platform",
y="Timestamp",
color="Appeal",
facet_col="Report Type",
title="<b>Report Appeal by Platform, Report Type </b>",
text="Timestamp",
color_discrete_sequence=["#0b5394", "lightgrey"],
)
fig2_2.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1]))
fig2_2.update_layout(yaxis_title="Report Count")
return fig2_2
hello @Coding-with-Adam, as agreed here below the code of the insights plots I showed last week (reports status and reports related plots) I turned the orginal structure into 2 tabs (I did not apply any style to the tabs --just a container to provide the samples). Please let me now for any questions. still missing the tab related to the users activities...hopefully my proposals coming soon :-)
mport dash_bootstrap_components as dbc
from dash import html, dcc, dash_table, Input, Output, callback
import dash
import pandas as pd
import plotly.express as px
import numpy as np
#
# from datetime import datetime
dash.register_page(__name__, path="/data-insights", order=1)
################### data retrieval#############################
df = pd.read_csv(
"https://raw.githubusercontent.com/Coding-with-Adam/response-reporting-dashboard/main/dummy_data_1000_wNan.csv"
)
#################### data Prep section#########################
##add extra-cols to df -- for overall views
df["isOpen"] = np.where(df["Answer Date"].isna(), True, False)
df["reportAge"] = (
(pd.to_datetime(pd.Timestamp.today(), unit="s") - pd.to_datetime(df["Timestamp"]))
.dropna()
.astype(str)
.str.replace(" days.*$", "", regex=True)
).astype(int)
df["reportLifetime"] = (
(pd.to_datetime(df["Answer Date"]) - pd.to_datetime(df["Timestamp"]))
.dropna()
.astype(str)
.str.replace(" days", "")
.astype(int)
)
##data aggregation,subset for Report status view container "view1"
group1_1 = df.groupby(by=["Platform", "isOpen"]).count().reset_index()
df1_1 = (
df[df.isOpen == True]
.sort_values(by=["reportAge"], ascending=False)
.reset_index()
.loc[0:10][["Timestamp", "Platform", "Report Type"]]
)
##data aggregation, subset for Report status view container "view2" #########
group2_1 = df.groupby(by=["Platform", "Report Type"]).count().reset_index()
group2_2 = df.groupby(by=["Platform", "Report Type", "Appeal"]).count().reset_index()
group2_3 = df.groupby(by=["Report Type", "Appeal"]).count().reset_index()
platformSelector = dcc.Dropdown(
options=df.Platform.unique().tolist(),
placeholder="Select Platforms",
clearable=True,
multi=True,
id="platformSelector-id",
className="ms-5 border-primary border-1 bg-dark text-primary fw-normal rounded",
)
############# view1 --> Report status vizs ######################
fig1_1 = px.treemap(
group1_1,
path=[px.Constant("All Platforms"), "Platform", "isOpen"],
values="Timestamp",
color="isOpen",
# color_continuous_midpoint=np.average(group["isOpen"]),
color_discrete_map={"(?)": "#d0e0e3", True: "#0b5394", False: "lightgrey"},
hover_data=["Platform"],
# color_continuous_scale="Blues",
)
# fig1_1.update_layout(showlegend=True)
fig1_1.data[0].textinfo = "percent parent+value"
# color_continuous_midpoint=np.average(group['isOpen']))
fig1_1.update_layout(
title="<b>Reports Status ( % open/closed) by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_1.update_layout(margin=dict(t=50, l=25, r=25, b=25))
fig1_1.update_traces(
textposition="middle center",
textfont_size=14,
)
fig2 = px.bar(
group1_1,
x="Timestamp",
y="Platform",
color="isOpen",
orientation="h",
# hover_data=["tip", "size"],
# height=400,
color_discrete_sequence={"(?)": "#d0e0e3", True: "#0b5394", False: "lightgrey"},
title="<b>Reports Status ( open/closed count) by Platform</b>",
text="Timestamp",
)
fig2.update_layout(
title="<b>Reports Status ( open/closed count) by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_3 = px.bar(
df[df.isOpen == True]
.sort_values(by=["reportAge"], ascending=False)
.reset_index()
.loc[0:10],
x="reportAge",
y="Timestamp",
color="Platform",
orientation="h",
color_discrete_sequence=px.colors.qualitative.Pastel,
text="reportAge",
)
fig1_3.update_layout(
title="<b>Open Report - Oldest Top 10 [days]</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
table1_1 = dash_table.DataTable(
data=df1_1.to_dict("records"),
columns=[{"name": i, "id": i} for i in df1_1.columns],
style_cell={"textAlign": "left"},
style_header={"backgroundColor": "#446e9b", "fontWeight": "bold", "color": "white"},
style_data={"color": "grey", "backgroundColor": "white"},
)
fig1_2 = px.ecdf(
df[df.isOpen == 0],
x="reportLifetime",
color="Platform",
markers=True,
lines=False,
color_discrete_sequence=px.colors.qualitative.Pastel,
)
fig1_2.update_layout(
title="<b>Closed Report Lifetime Distribution by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_2.add_hline(
y=0.5, line_width=1, line_dash="dash", line_color="blue", annotation_text="median"
)
############# view2 --> Report type vizs ######################
fig2_1 = px.treemap(
group2_1,
path=[px.Constant("all"), "Platform", "Report Type"],
values="Timestamp",
color="Timestamp",
hover_data=["Platform"],
color_continuous_scale="RdBu_r",
)
fig2_1.update_layout(showlegend=True)
fig2_1.data[0].textinfo = "label+percent parent+value"
fig2_1.update_layout(
title="<b>Reports Type by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig2_1.update_layout(margin=dict(t=50, l=25, r=25, b=25))
fig2_1.update_traces(
textposition="middle center",
textfont_size=14,
)
fig2_2 = px.bar(
group2_2[group2_2["Report Type"] == df["Report Type"].unique().tolist()[0]],
x="Platform",
y="Timestamp",
color="Appeal",
facet_col="Report Type",
title="<b>Report Appeal by Platform, Report Type </b>",
text="Timestamp",
color_discrete_sequence=["#0b5394", "lightgrey"],
)
fig2_2.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1]))
fig2_2.update_layout(yaxis_title="Report Count")
############ view1 assembly ####################
view1 = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig1_1),
],
width=5,
),
dbc.Col(
[
dcc.Graph(figure=fig1_2),
],
width=5,
),
],
className="mb-2",
),
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig1_3),
],
width=5,
),
dbc.Col(
[
html.P(
"Open Reports - oldest Top 10 - details",
className="ps-5 pt-3 text-primary fw-bold",
),
table1_1,
],
width=5,
),
]
),
],
fluid=False,
id="view1-id",
className="ms-5 vh-100 ",
)
############ view2 assembly ####################
view2 = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig2_1),
],
width=11,
),
],
className="mb-2 ",
),
dbc.Row(
[
dbc.Col(
[
html.P(
"Report Type Selector",
className="ps-5 pt-3 text-primary fw-bold",
),
dcc.Dropdown(
options=df["Report Type"].unique().tolist(),
clearable=False,
value=df["Report Type"].unique().tolist()[0],
id="ReportTypeSelector-id",
className="ms-5 border-primary border-1 bg-dark text-primary fw-normal rounded",
),
],
width=3,
),
dbc.Col(
[
dcc.Graph(figure=fig2_2, id="fig2_2-id"),
],
width=7,
),
]
),
],
fluid=False,
id="view2-id",
className="ms-5 vh-100 ",
style={"display": "flex", "flex-direction": "column"},
)
viewContent = dbc.Container([view1], id="viewContent-id")
# page layout
layout = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
html.Hr(),
],
className="pe-5",
),
],
align="center",
),
dbc.Row(
dbc.Col(
[
dbc.Tabs(
[
dbc.Tab(label="Report Status", tab_id="tab-1"),
dbc.Tab(label="Report Type", tab_id="tab-2"),
],
id="tabs",
active_tab="tab-1",
),
html.Br(),
viewContent,
]
),
align="center",
),
],
className="",
)
@callback(
Output("viewContent-id", "children"),
Input("tabs", "active_tab"),
prevent_initial_call=True,
suppress_callback_exceptions=True,
)
def updateView(selTab):
print(type(selTab))
if selTab == "tab-1":
print("here")
return view1
else:
return view2
@callback(
Output("fig2_2-id", "figure"),
Input("ReportTypeSelector-id", "value"),
prevent_initial_call=True,
suppress_callback_exceptions=True,
)
def updateFig(selValue):
group2_2ff = group2_2[group2_2["Report Type"] == selValue]
fig2_2 = px.bar(
group2_2ff,
x="Platform",
y="Timestamp",
color="Appeal",
facet_col="Report Type",
title="<b>Report Appeal by Platform, Report Type </b>",
text="Timestamp",
color_discrete_sequence=["#0b5394", "lightgrey"],
)
fig2_2.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1]))
fig2_2.update_layout(yaxis_title="Report Count")
return fig2_2
hello @Coding-with-Adam here below the most updated code of my proposal for the insights plots. Now I have three tabs. I added to the existing two tabs one new related to the users activities. Please let me now for any questions. Let's talk together today
import dash_bootstrap_components as dbc
from dash import html, dcc, dash_table, Input, Output, callback
import dash
import pandas as pd
import plotly.express as px
import numpy as np
#
# from datetime import datetime
dash.register_page(__name__, path="/data-insights", order=1)
#####################support functions########################
def create_insight_card(title, value, note=""):
card = dbc.CardGroup(
[
dbc.Card(
[
dbc.Row(
[
dbc.Col(
dbc.CardBody(
[
html.H4(title, className="card-title"),
html.H1(
value,
className="card-text",
),
html.Small(
note,
className="card-text text-muted",
),
]
),
className="col-md-8 ",
),
],
className="g-0 d-flex align-items-center",
)
],
className="mb-3 bg-opacity-10 mt-3 shadow my-2 bg-light text-primary rounded ",
),
dbc.Card(
className="mb-3 mt-3 bg-primary shadow my-2 bg-opacity-80 ",
style={"maxWidth": 75},
),
],
className="",
)
return card
################### data retrieval#############################
df = pd.read_csv(
"https://raw.githubusercontent.com/Coding-with-Adam/response-reporting-dashboard/main/dummy_data_10000_wNan.csv"
)
#################### data Prep section#########################
##add extra-cols to df -- for overall views
df["isOpen"] = np.where(df["Answer Date"].isna(), True, False)
df["reportAge"] = (
(pd.to_datetime(pd.Timestamp.today(), unit="s") - pd.to_datetime(df["Timestamp"]))
.dropna()
.astype(str)
.str.replace(" days.*$", "", regex=True)
).astype(int)
df["reportLifetime"] = (
(pd.to_datetime(df["Answer Date"]) - pd.to_datetime(df["Timestamp"]))
.dropna()
.astype(str)
.str.replace(" days", "")
.astype(int)
)
##data aggregation,subset for Report status view container "view1"
group1_1 = df.groupby(by=["Platform", "isOpen"]).count().reset_index()
df1_1 = (
df[df.isOpen == True]
.sort_values(by=["reportAge"], ascending=False)
.reset_index()
.loc[0:10][["Timestamp", "Platform", "Report Type"]]
)
##data aggregation, subset for Report status view container "view2" #########
group2_1 = df.groupby(by=["Platform", "Report Type"]).count().reset_index()
group2_2 = df.groupby(by=["Platform", "Report Type", "Appeal"]).count().reset_index()
group2_3 = df.groupby(by=["Report Type", "Appeal"]).count().reset_index()
##data aggregation view3
# group3_1 = df.groupby(by=[pd.to_datetime(df["Answer Date"]), "Platform"]).count()
df3_1 = df[["Timestamp", "Reporting User"]].drop_duplicates(subset=["Reporting User"])
df3_1["Timestamp"] = pd.to_datetime(df3_1.Timestamp).dt.strftime("%Y-%m")
group3_1 = df3_1.groupby(by="Timestamp").count()
group3_1.rename(columns={"Reporting User": "New Users"}, inplace=True)
# get total active users, last month new users for "view3" cards
totalActiveUsers = df["Reporting User"].nunique()
currentMonth = pd.to_datetime(pd.Timestamp.today(), unit="s").strftime("%Y-%m")
newActiveUsers_currentMonth = df3_1[df3_1["Timestamp"] == currentMonth].shape[0]
df3_2 = df[["Timestamp", "Platform"]]
df3_2["Timestamp"] = pd.to_datetime(df3_2.Timestamp).dt.strftime("%Y-%m-%d")
group3_2 = df3_2.groupby(by=["Timestamp"]).count().reset_index()
group3_2.set_index(pd.DatetimeIndex(group3_2["Timestamp"]), inplace=True)
group3_2.drop(columns="Timestamp", inplace=True)
group3_2_2 = group3_2.resample("W").sum()
group3_2_2.rename(columns={"Platform": "New Reports"}, inplace=True)
df3_3 = df.groupby(by="Reporting User")["Timestamp"].count().reset_index()
group3_3 = df3_3.groupby(by="Timestamp").count()
updateCardTime = (
f"Last Update on {pd.to_datetime(pd.Timestamp.today()).strftime('%Y-%m-%d')}"
)
###############################################################################
# print(group3_1.columns)
platformSelector = dcc.Dropdown(
options=df.Platform.unique().tolist(),
placeholder="Select Platforms",
clearable=True,
multi=True,
id="platformSelector-id",
className="ms-5 border-primary border-1 bg-dark text-primary fw-normal rounded",
)
############# view1 --> Report status vizs ######################
fig1_1 = px.treemap(
group1_1,
path=[px.Constant("All Platforms"), "Platform", "isOpen"],
values="Timestamp",
color="isOpen",
# color_continuous_midpoint=np.average(group["isOpen"]),
color_discrete_map={"(?)": "#d0e0e3", True: "#0b5394", False: "lightgrey"},
hover_data=["Platform"],
# color_continuous_scale="Blues",
)
# fig1_1.update_layout(showlegend=True)
fig1_1.data[0].textinfo = "percent parent+value"
# color_continuous_midpoint=np.average(group['isOpen']))
fig1_1.update_layout(
title="<b>Reports Status ( % open/closed) by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_1.update_layout(margin=dict(t=50, l=25, r=25, b=25))
fig1_1.update_traces(
textposition="middle center",
textfont_size=14,
)
fig2 = px.bar(
group1_1,
x="Timestamp",
y="Platform",
color="isOpen",
orientation="h",
# hover_data=["tip", "size"],
# height=400,
color_discrete_sequence={"(?)": "#d0e0e3", True: "#0b5394", False: "lightgrey"},
title="<b>Reports Status ( open/closed count) by Platform</b>",
text="Timestamp",
)
fig2.update_layout(
title="<b>Reports Status ( open/closed count) by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_3 = px.bar(
df[df.isOpen == True]
.sort_values(by=["reportAge"], ascending=False)
.reset_index()
.loc[0:10],
x="reportAge",
y="Timestamp",
color="Platform",
orientation="h",
color_discrete_sequence=px.colors.qualitative.Pastel,
text="reportAge",
)
fig1_3.update_layout(
title="<b>Open Report - Oldest Top 10 [days]</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
table1_1 = dash_table.DataTable(
data=df1_1.to_dict("records"),
columns=[{"name": i, "id": i} for i in df1_1.columns],
style_cell={"textAlign": "left"},
style_header={"backgroundColor": "#446e9b", "fontWeight": "bold", "color": "white"},
style_data={"color": "grey", "backgroundColor": "white"},
)
fig1_2 = px.ecdf(
df[df.isOpen == 0],
x="reportLifetime",
color="Platform",
markers=True,
lines=False,
color_discrete_sequence=px.colors.qualitative.Pastel,
)
fig1_2.update_layout(
title="<b>Closed Report Lifetime Distribution by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig1_2.add_hline(
y=0.5, line_width=1, line_dash="dash", line_color="blue", annotation_text="median"
)
############# view2 --> Report type vizs ######################
fig2_1 = px.treemap(
group2_1,
path=[px.Constant("all"), "Platform", "Report Type"],
values="Timestamp",
color="Timestamp",
hover_data=["Platform"],
color_continuous_scale="RdBu_r",
)
fig2_1.update_layout(showlegend=True)
fig2_1.data[0].textinfo = "label+percent parent+value"
fig2_1.update_layout(
title="<b>Reports Type by Platform</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
fig2_1.update_layout(margin=dict(t=50, l=25, r=25, b=25))
fig2_1.update_traces(
textposition="middle center",
textfont_size=14,
)
fig2_2 = px.bar(
group2_2[group2_2["Report Type"] == df["Report Type"].unique().tolist()[0]],
x="Platform",
y="Timestamp",
color="Appeal",
facet_col="Report Type",
title="<b>Report Appeal by Platform, Report Type </b>",
text="Timestamp",
color_discrete_sequence=["#0b5394", "lightgrey"],
)
fig2_2.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1]))
fig2_2.update_layout(yaxis_title="Report Count")
##############view3 vizs##########################
fig3_1 = px.bar(
group3_1,
x=group3_1.index,
y=group3_1["New Users"],
color="New Users",
color_continuous_scale="Blues",
color_continuous_midpoint=group3_1["New Users"].min(),
)
# fig3_1.update_traces(marker_color="#0b5394")
fig3_1.update_layout(yaxis_title="New Users Count")
fig3_1.update_layout(
title="<b>New Users - Montly Basis</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
xaxis=dict(
rangeslider=dict(visible=True, thickness=0.03), # , bgcolor="#636EFA"
type="date",
),
)
fig3_2 = px.bar(
group3_2_2,
x=group3_2_2.index,
y=group3_2_2["New Reports"],
color="New Reports",
color_continuous_scale="Blues",
)
# fig3_2.update_traces(marker_color="#0b5394")
fig3_2.update_layout(yaxis_title="New Reports")
fig3_2.update_layout(
title="<b>New Reports - Weekly Basis</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
xaxis=dict(
rangeslider=dict(visible=True, thickness=0.03), # , bgcolor="#636EFA"
type="date",
),
)
fig3_3 = px.pie(
group3_3,
values="Reporting User",
names=group3_3.index,
color=group3_3.index,
color_discrete_sequence=px.colors.sequential.Blues_r,
)
fig3_3.update_traces(textposition="inside", textinfo="percent")
fig3_3.update_layout(uniformtext_minsize=12, uniformtext_mode="hide")
fig3_3.update_layout(
title="<b>Report Count by User</b>",
title_font=dict(
size=16,
color="#446e9b",
family="Arial, Helvetica, sans-serif",
),
)
card3_1 = create_insight_card("Active Users", totalActiveUsers, updateCardTime)
card3_2 = create_insight_card(
"New Users", newActiveUsers_currentMonth, f"current Month:{currentMonth}"
)
############ view1 assembly ####################
view1 = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig1_1),
],
width=5,
),
dbc.Col(
[
dcc.Graph(figure=fig1_2),
],
width=5,
),
],
className="mb-2",
),
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig1_3),
],
width=5,
),
dbc.Col(
[
html.P(
"Open Reports - oldest Top 10 - details",
className="ps-5 pt-3 text-primary fw-bold",
),
table1_1,
],
width=5,
),
]
),
],
fluid=False,
id="view1-id",
className="ms-5 vh-100 ",
)
############ view2 assembly ####################
view2 = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig2_1),
],
width=11,
),
],
className="mb-2 ",
),
dbc.Row(
[
dbc.Col(
[
html.P(
"Report Type Selector",
className="ps-5 pt-3 text-primary fw-bold",
),
dcc.Dropdown(
options=df["Report Type"].unique().tolist(),
clearable=False,
value=df["Report Type"].unique().tolist()[0],
id="ReportTypeSelector-id",
className="ms-5 border-primary border-1 bg-dark text-primary fw-normal rounded",
),
],
width=3,
),
dbc.Col(
[
dcc.Graph(figure=fig2_2, id="fig2_2-id"),
],
width=7,
),
]
),
],
fluid=False,
id="view2-id",
className="ms-5 vh-100 ",
style={"display": "flex", "flex-direction": "column"},
)
#### view3 container
view3 = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
card3_1,
],
width=5,
),
dbc.Col(
[
card3_2,
],
width=5,
),
],
className="mb-2",
),
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig3_2),
],
width=10,
),
# dbc.Col(
# [
# dcc.Graph(figure=fig3_2),
# ],
# width=5,
# ),
],
className="mb-2",
),
dbc.Row(
[
dbc.Col(
[
dcc.Graph(figure=fig3_1),
],
width=5,
),
dbc.Col(
[
# html.P(
# "Open Reports - oldest Top 10 - details",
# className="ps-5 pt-3 text-primary fw-bold",
# ),
# # table1_1,
dcc.Graph(figure=fig3_3),
],
width=5,
),
]
),
],
fluid=False,
id="view3-id",
className="ms-5 vh-100 ",
)
######################################################################
viewContent = dbc.Container([view1], id="viewContent-id")
# page layout
layout = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[
html.Hr(),
],
className="pe-5",
),
],
align="center",
),
dbc.Row(
dbc.Col(
[
dbc.Tabs(
[
dbc.Tab(label="Report Status", tab_id="tab-1"),
dbc.Tab(label="Report Type", tab_id="tab-2"),
dbc.Tab(label="Users Activities", tab_id="tab-3"),
],
id="tabs",
active_tab="tab-1",
),
html.Br(),
viewContent,
]
),
align="center",
),
],
className="",
)
@callback(
Output("viewContent-id", "children"),
Input("tabs", "active_tab"),
prevent_initial_call=True,
suppress_callback_exceptions=True,
)
def updateView(selTab):
print(type(selTab))
if selTab == "tab-1":
print("here")
return view1
if selTab == "tab-2":
print("here")
return view2
else:
return view3
@callback(
Output("fig2_2-id", "figure"),
Input("ReportTypeSelector-id", "value"),
prevent_initial_call=True,
suppress_callback_exceptions=True,
)
def updateFig(selValue):
group2_2ff = group2_2[group2_2["Report Type"] == selValue]
fig2_2 = px.bar(
group2_2ff,
x="Platform",
y="Timestamp",
color="Appeal",
facet_col="Report Type",
title="<b>Report Appeal by Platform, Report Type </b>",
text="Timestamp",
color_discrete_sequence=["#0b5394", "lightgrey"],
)
fig2_2.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1]))
fig2_2.update_layout(yaxis_title="Report Count")
return fig2_2
Thank you @topaccina I just added your code to the main data insights page.
For record keeping, in case @JorgeMiguelGomes would like to incorporate Mohamed's original 4 visualizations, here's the code.
import dash
from dash import Dash, html, dcc, callback, Output, Input, State
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
import pandas as pd
import plotly.express as px
from dash_bootstrap_templates import load_figure_template
dash.register_page(__name__)
def create_graph_card(id, fig, className='p-2'):
height = "100%"
card = dbc.Card(
[dcc.Graph(id=id, figure=fig, style={'height': height}, config={'displayModeBar': False})],
style={'height': height},
className=className
)
return card
df = pd.read_csv("https://raw.githubusercontent.com/Coding-with-Adam/response-reporting-dashboard/main/dummy_data_100_wNan.csv")
df.columns = ['timestamp', 'reporting_entity', 'reporting_user', 'platform', 'url', 'report_type', 'screenshot_url', 'answer_date', 'platform_decision', 'policy', 'appeal']
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['answer_date'] = pd.to_datetime(df['answer_date'], errors='coerce')
unique_platforms = df['platform'].unique()
color_mapping = dict(zip(unique_platforms, px.colors.qualitative.G10))
df_pie = df.groupby('platform', as_index=False)['timestamp'].size().sort_values(by='size', ascending=False)
fig1 = px.pie(df_pie, values='size', names='platform', hole=.5, title='Total reports by platform',
color='platform', color_discrete_map=color_mapping)\
.update_traces(marker=dict(line=dict(color='#FFFFFF', width=2)), sort=True, direction='clockwise')\
.update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5), legend=dict(xanchor='right', x=1))
df_line = df.groupby(['timestamp', 'platform'], as_index=False)['platform'].size()
fig2 = px.line(df_line, x='timestamp', y='size', color='platform', color_discrete_map=color_mapping,
title='Daily reports by Platform', markers=True,
labels={'Date': 'timestamp', 'Count': 'size', 'Platform': 'platform'})\
.update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5))
df_average_response_time = df.copy().dropna(subset=['answer_date'])
df_average_response_time['response_time'] = (df_average_response_time['answer_date'] - df_average_response_time['timestamp']).dt.days
average_response_time = df_average_response_time.groupby('platform', as_index=False)['response_time'].mean().sort_values(by='response_time', ascending=False)
fig3 = px.bar(average_response_time, x='platform', y='response_time', title='Average responce time by platform (Days)',
).update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5),
showlegend=False)
fig4 = px.histogram(df, x='platform_decision', color='platform', color_discrete_map=color_mapping,
title='Decisions by platform', barmode='group').\
update_layout(margin=dict(l=0, r=0, t=30, b=0)).update_traces(marker=dict(line=dict(color='#FFFFFF', width=2))).update_layout(margin=dict(l=0, r=0, t=30, b=5), title=dict(font=dict(family='Arial', size=16), x=0.5),
).update_yaxes(categoryorder='total descending')
fig1_card = create_graph_card('fig1', fig1)
fig2_card = create_graph_card('fig2', fig2)
fig3_card = create_graph_card('fig3', fig3)
fig4_card = create_graph_card('fig4', fig4)
load_figure_template("yeti")
column_heigh_style = {"height": "100%"}
layout = dbc.Container(
[
dbc.Row([dbc.Col(fig1_card, width=6, style=column_heigh_style), dbc.Col(fig2_card, width=6, style=column_heigh_style)],
className='my-4',
style={"height": '45vh'},
justify='around'),
dbc.Row([dbc.Col(fig3_card, width=6, style=column_heigh_style), dbc.Col(fig4_card, width=6, style=column_heigh_style)],
className='my-4',
style={"height": '45vh'},
justify='around'),
],
fluid=True,
style={"height": '100vh', 'background-color': '#F4F6F7'},
)
For the data insights page we should have data visualizations that highlight, at minimum: