首页 > 解决方案 > python dash 回调动态生成的搜索结果。一定有更好的方法

问题描述

我有一个破折号应用程序,它获取短文本列表和一些关于它们的元数据作为输入。我在一个填充了数据的无序列表中显示这些文本。现在我想为我的所有列表元素设置一个通用回调,以便在单击它们时填充详细视图并运行相似性算法。此外,用户应该能够添加和删除类别(然后将句子放入类别中,但这还没有实现)。这是到目前为止的代码。它使用破折号的模式匹配回调。首先,它非常慢,其次,我很确定必须有更好的方法来实现这种行为。

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, MATCH, ALL
from dash_html_components.Button import Button
import datasets
import html_generators
import re

app = dash.Dash(__name__)


sentences, embeddings = datasets.dataset_from_csv('sentences')
categories = set(sentence['category'] for sentence in sentences if sentence['category'])




app.layout = html.Div([
    html.Div(
        className="app-header",
        children=[
            html.Div('Plotly Dash', className="app-header--title")
        ]
    ),
    html.Div(
        className="arg-list-pane",
        children=[
            html.Div(className="arg-list-box", children=[
                html.Div('Header', className="arg-list-header"),
                html.Ul(children=[
                    html.Div(
                        sentence['sentence'],
                        id={'type': 'argument-container', 'index': sentence['id']},
                        className="arg-container") for sentence in sentences]
            )]
            ),
            html.Div('Argument Details', id="argument-detail-box")
        ]
    ),
    html.Div(
        className="category-pane",
        children=[
            html.Div(className="category-box", children=[
                html.Div(
                    className="category-header",
                    children=[
                        dcc.Input(id='cat-input', value='Add Category..', type='text'),
                        html.Button(id='submit-cat-button', n_clicks=0, children='Add')
                    ]),
                html.Ul(
                    id='cat_list',
                    children=[
                        html.Div(category, {'type': 'category-item', 'index': index},
                                 className="category-container")
                        for index, category in enumerate(categories)
                    ])
                ]
            ),
            html.Div("algorithm output here", id='algo-box')
        ]
    )
])


@app.callback(
    Output('cat_list', 'children'),
              Input('submit-cat-button', 'n_clicks'),
              Input("cat-input", "n_submit"),
              Input({'type': 'category-remove-btn', 'index': ALL}, 'n_clicks'),
              State('cat-input', 'value'),
              State('cat_list', 'children')
)
def add_category(n_clicks, n_submit, remove_click, cat_input, children):
    """Handle Category input.
    Data from the input box can be submitted with enter and button click. If
    that happens, a new category will be created if it doesn't exist yet.
    If the remove button of a category is presesd, the corresponding list
    element will be deleted.
    """
    if n_clicks or n_submit:
        trigger_id = dash.callback_context.triggered[0]['prop_id']
        if trigger_id.startswith('submit-cat-button') or trigger_id.startswith('cat-input'):
            already_there = False
            for category in children:
                if cat_input in category['props']['children']:
                    already_there = True
            if not already_there:
                new_cat = html.Li(
                    children= [
                        cat_input,
                        html.Button(
                            'remove',
                            id={'type': 'category-remove-btn', 'index': len(children)})
                    ],
                    id={'type': 'category-item', 'index': len(children)},
                    className="category-container")
                children.append(new_cat)
        else:
            button_id = int(re.findall('\d+', trigger_id)[0])
            for child in children:
                if child['props']['children'][1]['props']['id']['index'] == button_id:
                    children.remove(child)
    return children


@app.callback(
    Output('argument-detail-box', 'children'),
    Output('algo-box', 'children'),
    Input({'type': 'argument-container', 'index': ALL}, 'n_clicks')
)
def handle_arg_click(n_clicks):
    """handle click on a list item.
    When a list item is clicked, the n_clicks property will update, the callback
    will be triggered and the responsible list element will be stored in
    `dash.callable_context.triggered`. From that, the index/id of the sentence
    is known. With that information, the algorithm can be fed and the info box
    can be changed.
    """
    if not dash.callback_context.triggered[0]['value']:
        raise dash.exceptions.PreventUpdate


    sentence_id = int(re.findall('(\d+)',dash.callback_context.triggered[0]['prop_id'])[0])


    sentence_data = sentences[sentence_id]
    details_table = html_generators.create_details_table(sentence_data, header='argument details')
    return details_table, 'Similarity Score list here'




if __name__ == '__main__':
    app.run_server(debug=True)

从模式匹配回调中获取句子 ID 非常简单,但使用正则表达式查找正确的 ID 对我来说似乎有点不靠谱。此外,这就是应用程序运行缓慢的原因,因为我大约有 1200 个句子都在列表中,并且模式匹配回调要么返回所有数据,要么您必须指定匹配的输出。由于我有一个通用的输出视图,我必须根据 docs在模式匹配回调中匹配所有内容。
您还可以看到我使用删除按钮处理动态创建类别的方式,并查看嵌套的 dict/list 结构以找出在按下删除按钮时要删除的子元素。我无法想象这是最好的方法。

我是编写此类 Web 界面的新手,所以也许我错过了一个核心概念,它甚至没有在 dash 文档中详细说明。我只是有一种预感,这不是为用户或数据生成的 html 元素(如列表项)实现回调的正确方法。

您将如何实现这样的努力 - 用户单击(动态创建)列表元素和填充详细信息视图,所以非常基本的东西 - 在 python dash 中?

标签: pythonhtmlplotly-dash

解决方案


推荐阅读