Hello,
I’m currently building a dash app on AWS Elastic Beanstalk and I’m hoping to refresh the data by making a call to S3 every time the user reloads the page. This is fairly straight forward by placing my entire layout along with the call to S3 in a function called serve_layout()
then defining app.layout = serve_layout
.
However, things are a little more complicated because I also have a callback linked to a dropdown which filters the data, so I can’t place my callback function within serve_layout()
. I would prefer not to make a call to S3 every callback as this is expensive.
There must be an easy way to do this: reload data on page refresh AND filter data with dropdown (without reloading on every callback)
I have searched through this blog, which seems like my best bet: https://dash.plot.ly/sharing-data-between-callbacks,
but I wanted to check if there was a quick fix before I implement one of the solutions there.
Here’s my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px
import boto3
from datetime import datetime, timedelta
# Create Dash App
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
application = app.server
# Get data and metadata
full = pd.read_csv('s3://commcare/enriched_data.csv', engine='python')
full['timestamp'] = pd.to_datetime(full['timestamp']).dt.tz_localize(None)
s3 = boto3.client('s3', region_name='eu-west-1')
objects = s3.list_objects(Bucket='commcare', Prefix='enriched_data.csv')
last_update = str(objects['Contents'][0]['LastModified'].replace(tzinfo=None)) + ' UTC'
# Get districts and provinces
districts = [{'label':i,'value':i} for i in set(full['district'])]
provinces = [{'label':i,'value':i} for i in set(full['province'])]
# Define App layout
app.layout = html.Div(children=[
html.H2(children='amfAR | The Foundation for AIDs Research'),
html.H4(children='CommCare Survey Dashboard'),
html.Div(children='Filter by district(s):'),
dcc.Dropdown(
id='district-dropdown',
options=districts,
multi=True,
placeholder='All districts',
style={'width': '750px'}
),
html.Div([
html.Div(dcc.Graph(id='fig1'),
className="six columns"),
html.Div(dcc.Graph(id='fig2'),
className="six columns"),
], className="row"),
html.Div(children='Last updated: ' + last_update)
])
#app.layout = serve_layout
@app.callback([
dash.dependencies.Output('fig1', 'figure'),
dash.dependencies.Output('fig2', 'figure')],
[dash.dependencies.Input('district-dropdown', 'value')])
def update_graph(value):
if value==None or len(value)==0:
df = full
print('all districts')
else:
print('filtering by ' + str(value) + ':' + str(type(value)))
df = full[full['district'].isin(value)]
enough_staff = pd.get_dummies(df['enough_staff'].dropna()).sum()[['always','sometimes','never','dont_know']].fillna(0)
fig1 = go.Figure(data=[go.Pie(labels=enough_staff.index, values=enough_staff.values, hole=.5, sort=False)])
fig1.update_traces(hoverinfo='label+percent+value',marker=dict(colors=['lightgreen','lightblue','lightpink','lightgrey']))
fig1.update_layout(title_text="Are there enough staff in the clinic")
tmp = df[df['wait_time_hrs']>0]
today = pd.to_datetime(max(df['timestamp']).date())
avg_wt = tmp['wait_time_hrs'].mean()
ref = tmp[tmp['timestamp']<today]['wait_time_hrs'].mean()
wait_time = df[df['wait_time_hrs']>0][['timestamp','wait_time_hrs']].dropna().set_index('timestamp')
wait_time = wait_time.resample('D').mean().dropna()
colors = ['lightpink' if x>avg_wt else 'lightgreen' for x in wait_time['wait_time_hrs']]
fig2 = make_subplots(
rows=2, cols=1,
specs=[[{"type": "indicator"}],
[{"type": "bar"}]])
fig2.add_trace(go.Indicator(
mode = "number+delta",
value = avg_wt,
delta = {'reference': ref, 'decreasing':{'color':'lightgreen'},'increasing':{'color':'lightpink'}},
title = {'text': "Average Wait Time (hours)"}), row=1, col=1)
fig2.add_trace(go.Bar(x=wait_time.index, y=wait_time['wait_time_hrs'], marker_color=colors),
row=2, col=1)
fig2.update_layout(
yaxis_title="Wait Time (hours)",
plot_bgcolor = 'rgba(0,0,0,0)')
return fig1, fig2
if __name__ == '__main__':
application.run(debug=True, host='0.0.0.0', port=8050) # use this for local testing
#application.run(debug=True, port=8080) # use this for EB deployment