无法在仪表板应用程序中将两个回调功能连接在一起

时间:2019-05-26 00:48:35

标签: django plotly loading plotly-dash

您好,我是第一次使用破折号应用程序,无法修复 我面临的一个问题。我有一个前端,需要用户输入,进行一些计算,将结果存储在隐藏的div中,然后更新图形。

该应用程序加载正常,但是一旦我按下“提交”按钮,它就会出错。以下是我编写的代码。看起来两个回调函数都同时触发。

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 []

以下是我得到的错误:

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'>

我想要的是该功能应该在用户单击提交按钮时运行,并且由于更新图功能取决于清洁数据功能,因此除非以及直到清洁数据功能完成,否则该功能才应运行。

请有人帮我弄清楚我在这里想念什么,并帮助解决问题。

非常感谢!

新的更新功能


@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)


1 个答案:

答案 0 :(得分:2)

好的,首先要注意一些小事情。这里有一些语法错误,尽管它们可能不在原始文件中。您的ID丢失,因为id='Frequency',有错字。轻松修复。我收到了许多关于Dropdown道具不正确的警告,我通过删除为每个下拉选项传递的type='number'解决了这个问题。我还将update_graph的默认返回值更改为一个空字典,这解决了另一个类型错误。

现在需要更大的东西。您正在使用同一按钮触发两个回调。第一个处理数据,并将其存储在隐藏的div中。第二个睡眠足够长的时间以允许第一个睡眠,然后在隐藏的div中运行。问题在于,第二个回调函数将在其运行后立即拉入该隐藏div的内容,并且它休眠的事实不会更改它与一起使用的值,即使该值在睡眠计时器结束。

此处的解决方法是合并回调。您可以删除隐藏的div,只需在单个回调中完成所有工作即可开始。