Reputation: 560
Hello I am working on a dash app for the first time and I am unable to fix an issue that I am facing. I have a front-end that takes the user input, does some calculation, stores the result in a hidden div and then updates the graph.
The app loads fine however it runs into an error as soon as I hit the submit button. Below is the code I have written. It looks like both the callback functions triggers at the same time.
app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
html.Div([
html.Div([
html.Img(src='data:image/png;base64,{}'.format(encoded_image.decode()), className="nine columns")
], className="three columns"),
html.Div([
html.Div([
html.Div([
html.P("Select Month Range:"),
],
className="two columns"
),
html.Div([
dcc.RangeSlider(
id='month_slider',
# updatemode='drag',
# count=1,
min=1,
max=maxmarks,
step=1,
value=[maxmarks - 1, maxmarks],
marks=tags,
pushable=1
),
],
className="six columns",
style={})
],
className="twelve columns",
style={
'backgroundColor': '#EFEAEA',
'padding-top': '1.5em',
'padding-bottom': '1em'
}),
html.Div([
html.Div([
dcc.Dropdown(
id='demographics',
options=[
{'label': 'All 18-49', 'value': '18-49'},
{'label': 'Female 25-54', 'value': '25-54F'},
{'label': 'All 25-54', 'value': '25-54'},
],
placeholder="Select Demographics",
)
],
className="two columns",
style={}),
html.Div([
dcc.Dropdown(
id='ID',
options=[
{'label': '200', 'value': 200, 'type': 'number'},
{'label': '250', 'value': 250, 'type': 'number'},
{'label': '300', 'value': 300, 'type': 'number'},
{'label': '350', 'value': 350, 'type': 'number'},
{'label': '400', 'value': 400, 'type': 'number'},
],
placeholder="Select ID",
)
],
className="two columns",
style={}),
html.Div([
dcc.Dropdown(
id='Income',
options=[
{'label': '50,000', 'value': 50000, 'type': 'number'},
{'label': '100,000', 'value': 100000, 'type': 'number'},
{'label': '200,000', 'value': 200000, 'type': 'number'},
{'label': '350,000', 'value': 350000, 'type': 'number'},
{'label': '500,000', 'value': 500000, 'type': 'number'},
],
placeholder="Select Income",
)
],
className="two columns",
style={}),
html.Div([
dcc.Dropdown(
id='Frquency',
options=[
{'label': 'None per week, 'value': 0, 'type': 'number'},
{'label': 'Once per Week', 'value': 1, 'type': 'number'},
{'label': 'Thrice per Week', 'value': 3, 'type': 'number'},
],
placeholder="Select Frequency",
)
],
className="two columns",
style={}),
html.Div([
html.Button('Submit', id='submit_button', className='twelve columns',
style={'background-color': '#2D91C3', 'color': 'white', 'font-size': '1em'})
],
className="two columns",
style={}),
],
className="twelve columns",
style={
'backgroundColor': '#EFEAEA',
'padding-top': '1em',
'padding-bottom': '1.5em'
})
], className="nine columns", style={})
], className="twelve columns"),
# dcc.graph layout
html.Div([
html.Div([
dcc.Graph(id='example-graph', config={"displayModeBar": False, "scrollZoom": False})
],
className="six columns",
style={'border-right': 'thin grey solid', 'border-left': 'thin grey solid',
'border-top': 'thin grey solid'}),
html.Div(id='intermediate-value1', style={'display': 'none'})
])]
@app.callback(
Output("intermediate-value1", "children"),
[Input("submit_button", "n_clicks")],
[
State("month_slider", "value"),
State("demographics", "value"),
State("Income", "value"),
State("Frequency", "value"),
],
)
def clean_data(n_clicks, month_range, demo, inc, fre_cap):
if n_clicks is not None and n_clicks > 0:
employee_data_temp = employee_data.copy()
start_date = month_range[0]
end_date = month_range[1]
mask1 = employee_data_temp["total_months"] == int(end_date - start_date)
employee_data_temp = employee_data_temp.loc[mask1]
mask2 = (
(employee_data_temp["demographic"] == demo)
& (employee_data_temp["freq_cap"] == fre_cap)
& (employee_data_temp["total_incressions"] == inc)
)
employee_data_temp = employee_data_temp.loc[mask2]
employee_data_temp = employee_data_temp.sort_values(by=["reach"])
employee_data_temp["income_percent"] = (employee_data_temp["reach"] / 955000) * 100
employee_data_temp = employee_data_temp.reset_index(drop=True)
return employee_data_temp.to_json(date_format="iso", orient="split")
else:
return []
@app.callback(Output("example-graph", "figure"),
[Input("submit_button", "n_clicks")],
[State("intermediate-value1", "children"),
State("Income", "value")])
def update_graph(n_clicks, employee_data_temp, inc):
t.sleep(2)
if n_clicks is not None and n_clicks > 0:
dff = pd.read_json(employee_data_temp, orient="split")
max_income = dff["income_percent"].iloc[9]
max_income = max_income.round(2)
trace = Scatter(
y=dff["income_percent"], x=dff["inc"], line=plotly.graph_objs.scatter.Line(color="#1a2d46"), mode="lines"
)
layout1 = Layout(
plot_bgcolor="#FFFFFF",
paper_bgcolor="#FFFFFF",
height=450,
title="Digital Reach - " + str(max_income) + " " + "%",
xaxis=dict(showgrid=True, showline=True, zeroline=True, fixedrange=True, title="Total Income"),
yaxis=dict(showline=True, fixedrange=True, zeroline=True, title="Income (%)"),
margin=plotly.graph_objs.layout.Margin(t=45, l=50, r=50),
)
return Figure(data=[trace], layout=layout1)
else:
return []
Below is the error I get:
Traceback (most recent call last):
File "C:\Users\Tushar\Documents\django_projects\tvnz_dash_app\src\dash_app\incrementor\views.py", line 21, in dispatcher
response = server.full_dispatch_request()
File "C:\python37\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\python37\lib\site-packages\flask\app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\python37\lib\site-packages\flask\_compat.py", line 35, in reraise
raise value
File "C:\python37\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "C:\python37\lib\site-packages\flask\app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\python37\lib\site-packages\dash\dash.py", line 1073, in dispatch
response.set_data(self.callback_map[output]['callback'](*args))
File "C:\python37\lib\site-packages\dash\dash.py", line 969, in add_context
output_value = func(*args, **kwargs)
File "C:\Users\Tushar\Documents\django_projects\tvnz_dash_app\src\dash_app\incrementor\router.py", line 699, in update_graph
dff = pd.read_json(employee_data_temp, orient="split")
File "C:\python37\lib\site-packages\pandas\io\json\json.py", line 413, in read_json
path_or_buf, encoding=encoding, compression=compression,
File "C:\python37\lib\site-packages\pandas\io\common.py", line 232, in get_filepath_or_buffer
raise ValueError(msg.format(_type=type(filepath_or_buffer)))
ValueError: Invalid file path or buffer object type: <class 'NoneType'>
What I want is that the function should run when a user click a submit button and as the update graph function is dependent on the clean data function it should not run until and unless the clean data function is finished completing.
Please can some one help me to figure what am I missing here and help to resolve the issue.
Thanks a lot in advance !!
NEW UPDATED FUNCTIONS
@app.callback(
Output("intermediate-value1", "children"),
[Input("submit_button", "n_clicks")],
[
State("month_slider", "value"),
State("demographics", "value"),
State("digital_impressions", "value"),
State("digital_frequency", "value"),
],
)
def get_match(n_clicks, month_range, demo, tarps):
if n_clicks is not None and n_clicks > 0:
some long python script data calculatation
return match_data_temp.to_json(date_format="iso", orient="split")
@app.callback(
Output("intermediate-value2", "children"),
[Input("submit_button", "n_clicks")],
[
State("month_slider", "value"),
State("demographics", "value"),
State("digital_impressions", "value"),
State("digital_frequency", "value"),
],
)
def clean_data(n_clicks, month_range, demo, inc, fre_cap):
if n_clicks is not None and n_clicks > 0:
employee_data_temp = employee_data.copy()
start_date = month_range[0]
end_date = month_range[1]
mask1 = employee_data_temp["total_months"] == int(end_date - start_date)
employee_data_temp = employee_data_temp.loc[mask1]
mask2 = (
(employee_data_temp["demographic"] == demo)
& (employee_data_temp["freq_cap"] == fre_cap)
& (employee_data_temp["total_incressions"] == inc)
)
employee_data_temp = employee_data_temp.loc[mask2]
employee_data_temp = employee_data_temp.sort_values(by=["reach"])
employee_data_temp["income_percent"] = (employee_data_temp["reach"] / 955000) * 100
employee_data_temp = employee_data_temp.reset_index(drop=True)
return employee_data_temp.to_json(date_format="iso", orient="split")
@app.callback(
Output("intermediate-value3", "children"),
[
Input("month_slider", "value"),
Input("demographics", "value"),
Input("intermediate-value2", "children"),
Input("intermediate-value1", "children"),
],
)
def get_match_overlap(month_range, demo, match_data_temp, employee_data_temp):
overlap_match = pd.read_json(match_data_temp, orient="split")
overlap_employee = pd.read_json(employee_data_temp, orient="split")
some long python calculatation
return final.to_json(date_format="iso", orient="split")
@app.callback(
Output("example-graph2", "figure"),
[Input("intermediate-value3", "children"), Input("intermediate-value2", "children")],
)
def update_graph(final, match_data_temp):
if final is not None:
dff_overlap = pd.read_json(final, orient="split")
dff_match_data = pd.read_json(match_data_temp, orient="split")
overlap = Scatter(
y=dff_overlap["final"],
x=dff_overlap["percentage"],
name="tile",
line=plotly.graph_objs.scatter.Line(color="red"),
mode="lines",
)
layout1 = Layout(
plot_bgcolor="#FFFFFF",
paper_bgcolor="#FFFFFF",
title="title",
height=550,
xaxis=dict(showgrid=True, showline=True, zeroline=False, fixedrange=True, title="% Campaign Completion", range=[0, 100]),
yaxis=dict(showline=True, fixedrange=True, zeroline=False, title="Audience Reached (%)", range=[0, (reach_increment + 10)]),
margin=plotly.graph_objs.layout.Margin(t=45, l=50, r=50),
)
linear = Scatter(
y=dff_match_data["Final"],
x=dff_match_data["percentage"],
name="title",
line=plotly.graph_objs.scatter.Line(color="black"),
mode="lines",
)
return Figure(data=[overlap, linear], layout=layout1)
@app.callback(Output("example-graph", "figure"),
[Input("submit_button", "n_clicks")],
[State("intermediate-value2", "children"),
State("Income", "value")])
def update_graph(n_clicks, employee_data_temp, inc):
if employee_data_temp is not None:
dff = pd.read_json(employee_data_temp, orient="split")
trace = Scatter(
y=dff["income_percent"], x=dff["inc"], line=plotly.graph_objs.scatter.Line(color="#1a2d46"), mode="lines"
)
layout1 = Layout(
plot_bgcolor="#FFFFFF",
paper_bgcolor="#FFFFFF",
height=450,
title="Digital - " + str(max_income) + " " + "%",
xaxis=dict(showgrid=True, showline=True, zeroline=True, fixedrange=True, title="Total Income"),
yaxis=dict(showline=True, fixedrange=True, zeroline=True, title="Income (%)"),
margin=plotly.graph_objs.layout.Margin(t=45, l=50, r=50),
)
return Figure(data=[trace], layout=layout1)
Upvotes: 2
Views: 2666
Reputation: 6606
Ok, a few minor things first. There are some syntax errors here, though they may not be in your original file. You have a missing ID, because id='Frequency',
has a typo. Easy fix. I got numerous warnings about an incorrect prop for Dropdown
, which I solved by removing the type='number'
you were passing in for each dropdown option. I also changed the default return value for update_graph
to an empty dictionary, which resolved another type error.
Now for bigger things. You are triggering two callbacks using the same button. The first deals with the data, and stores it in a hidden div. The second sleeps long enough to allow the first one to finish, and then runs on what's in the hidden div. The problem is that the second callback will pull in the contents of that hidden div as soon as it runs, and the fact that it sleeps does not change the value it works with even if that value updates before the sleep timer ends.
The fix here would be to combine callbacks. You can remove the hidden div, and simply do all the work start to finish in the single callback.
Upvotes: 3