Невозможно соединить две функции обратного вызова в приложении Dash - PullRequest
2 голосов
/ 26 мая 2019

Здравствуйте, я впервые работаю над приложением для черточки и не могу решить проблему, с которой сталкиваюсь.У меня есть интерфейс, который принимает пользовательский ввод, выполняет некоторые вычисления, сохраняет результат в скрытом 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 Ответ

2 голосов
/ 26 мая 2019

Хорошо, сначала несколько мелких вещей.Здесь есть некоторые синтаксические ошибки, хотя они могут отсутствовать в вашем исходном файле.У вас отсутствует идентификатор, потому что id='Frequency', содержит опечатку.Легко исправить.Я получил множество предупреждений о неправильном реквизите для Dropdown, который я решил, удалив type='number', который вы передавали для каждой опции выпадающего меню.Я также изменил значение по умолчанию для update_graph на пустой словарь, что решило еще одну ошибку типа.

Теперь для больших вещей.Вы запускаете два обратных вызова, используя одну и ту же кнопку.Первый имеет дело с данными и хранит их в скрытом div.Второй спит достаточно долго, чтобы позволить первому закончить, а затем работает на том, что находится в скрытом div.Проблема в том, что второй обратный вызов извлечет содержимое этого скрытого div, как только он запустится, и тот факт, что он спит, не меняет значение, с которым он работает , даже если это значение обновляется перед сномтаймер заканчивается.

Исправление здесь будет заключаться в объединении обратных вызовов.Вы можете удалить скрытый элемент div и просто выполнить всю работу, начиная и заканчивая в одном обратном вызове.

...