Nested Acordion Callback

Im creating an accordion object which contains 3 levels, the date, the time (AM or PM) and a stock. There is a button within the last item which i would like to press in order for a plot to show up, however i am having no luck with the pattern matching callbacks. I am seeming to miss something with setting up the ids for my particular use case. Any help would be appreciated!

def nest_acc(date, edf):
    market_stocks = stocks_on_specific_date
    am_stocks = morning_stock_list_on_date
    pm_stocks = afternoon_stock_list_on_date
    
    fill_text = lambda x: dbc.Row([
        dbc.Col(dcc.Markdown(f'''### ${x.upper()} ''')),
        dbc.Col(f'''{info_file[x]['sector']}'''),
        dbc.Col(dbc.Button("Show Plot", id = f'show_chart_button-{x}', n_clicks = 0, value = x))
    ])
    text_plot = lambda x: [fill_text(x), html.Div(id = f'chart-{x}')]
    
    am_acc = dbc.Accordion(
        [dbc.AccordionItem(text_plot(stock), title=info_file[stock]['longName']) for stock in am_stocks],
        flush = True,
        start_collapsed = False
    )
    
    pm_acc = dbc.Accordion(
        [dbc.AccordionItem(text_plot(stock), title=info_file[stock]['longName']) for stock in pm_stocks],
        flush = True, 
        start_collapsed = True
    )
    
    out_acc = dbc.Accordion(
        [dbc.AccordionItem(am_acc, title="🌀️ AM"), dbc.AccordionItem(pm_acc, title="⛅️ PM")],
        start_collapsed=False,
        flush = True,
    )
    return out_acc
    
exps = sorted(list(edf['Date'].dt.date.unique()))
nest_acc = dbc.Accordion(
    [dbc.AccordionItem(nest_acc(d, edf), title= dt.datetime.strftime(d, format = "%a %b %d, %Y")) for d in exps],
    flush = True,
    start_collapsed = False
)

accordion = html.Div([nest_acc])
####################################################################################################
dash.register_page(__name__)
####################################################################################################

db_container= dbc.Container(
    [
    dbc.Row(
            [
                dbc.Col(accordion),
            ]
        )
    ],
    style=contain_style
)



chart_area = html.Div(id = 'em-chart')
layout = html.Div(
    [
        chart_area,  
        html.H1("🚨 Earnings ToolBox πŸ› οΈ" , style={'textAlign': 'center', 'marginTop': 40}),
        db_container,
    ]
)

@callback(
    Output('em-chart', 'children'),
    [Input(f'show_chart_button-{x}', 'n_clicks') for x in market_stocks],
)
def lc(*args):
    stock = ctx.triggered[0]['prop_id'].split('-')[1].split('.')[0]
# Do some plotting
    return dcc.Graph(figure = fig)

Hi @jach welcome to the forums!

You are actually not using pattern matching callbacks but a list comprehension of a bunch of Inputs.

What errors do you get if you run your app with debug=True?

Hi thanks for the response, im only able to get the desired output if i set a single Output Output('chart-pfe', 'children)' (here pfe is a stock name and would need be interchanged with each individual stock) , along with thelist of buttons for the input same as above. So I tried using list comprehension for the output as well. the resulting error was:

dash._grouping.SchemaTypeValidationError: Schema: [<Output `chart-aapl.children`>, <Output `chart-pfe.children`>, <Output `chart-pep.children`>, <Output `chart-dkng.children`>, <Output `chart-mdb.children`>, <Output `chart-ups.children`>, <Output `chart-sq.children`>, <Output `chart-lmt.children`>, <Output `chart-qcom.children`>, <Output `chart-ardx.children`>, <Output `chart-mos.children`>, <Output `chart-abbv.children`>, <Output `chart-rkt.children`>, <Output `chart-ebay.children`>, <Output `chart-coin.children`>, <Output `chart-rtx.children`>, <Output `chart-ctva.children`>, <Output `chart-cvs.children`>, <Output `chart-ko.children`>, <Output `chart-ctra.children`>, <Output `chart-fang.children`>, <Output `chart-spwr.children`>, <Output `chart-oxy.children`>, <Output `chart-eog.children`>, <Output `chart-k.children`>, <Output `chart-ual.children`>, <Output `chart-afl.children`>, <Output `chart-aig.children`>, <Output `chart-dash.children`>, <Output `chart-open.children`>, <Output `chart-expe.children`>] Path: () Expected type: (<class 'tuple'>, <class 'list'>) Received value of type <class 'dash.dcc.Graph.Graph'>: Graph(figure=Figure({ 'data': [{'mode': 'lines', 'name': 'Close Price', 'type': 'scatter', 'x': array([datetime.datetime(2024, 4, 2, 0, 0), datetime.datetime(2024, 4, 3, 0, 0), datetime.datetime(2024, 4, 4, 0, 0), datetime.datetime(2024, 4, 5, 0, 0), datetime.datetime(2024, 4, 8, 0, 0), datetime.datetime(2024, 4, 9, 0, 0), datetime.datetime(2024, 4, 10, 0, 0), datetime.datetime(2024, 4, 11, 0, 0), datetime.datetime(2024, 4, 12, 0, 0), datetime.datetime(2024, 4, 15, 0, 0), datetime.datetime(2024, 4, 16, 0, 0), datetime.datetime(2024, 4, 17, 0, 0), datetime.datetime(2024, 4, 18, 0, 0), datetime.datetime(2024, 4, 19, 0, 0), datetime.datetime(2024, 4, 22, 0, 0), datetime.datetime(2024, 4, 23, 0, 0), datetime.datetime(2024, 4, 24, 0, 0), datetime.datetime(2024, 4, 25, 0, 0), datetime.datetime(2024, 4, 26, 0, 0), datetime.datetime(2024, 4, 29, 0, 0), datetime.datetime(2024, 4, 30, 0, 0), datetime.datetime(2024, 5, 1, 0, 0), datetime.datetime(2024, 5, 2, 0, 0)], dtype=object), 'y': array([27.54999924, 27.21999931, 26.64999962, 26.65999985, 26.57999992, 26.78000069, 26.31999969, 26.34000015, 25.86000061, 25.90999985, 25.69000053, 25.42000008, 25.38999939, 26. , 26.26000023, 26.31999969, 26.27000046, 25.26000023, 25.39999962, 25.63999939, 25.62000084, 27.18000031, 27.70730019])}, {'line': {'color': 'green'}, 'name': 'Upper Band', 'text': [$27.76, $28.68, $29.15, $29.34, $28.94], 'textposition': 'top center', 'type': 'scatter', 'x': array([datetime.datetime(2024, 5, 2, 0, 0), datetime.datetime(2024, 5, 3, 0, 0), datetime.datetime(2024, 5, 10, 0, 0), datetime.datetime(2024, 5, 17, 0, 0), datetime.datetime(2024, 5, 24, 0, 0)], dtype=object), 'y': array([27.75730019, 28.67730019, 29.14730019, 29.33730019, 28.93730019])}, {'line': {'color': 'red'}, 'name': 'Lower Band', 'text': [$27.66, $26.74, $26.27, $26.08, $26.48], 'textposition': 'bottom center', 'type': 'scatter', 'x': array([datetime.datetime(2024, 5, 2, 0, 0), datetime.datetime(2024, 5, 3, 0, 0), datetime.datetime(2024, 5, 10, 0, 0), datetime.datetime(2024, 5, 17, 0, 0), datetime.datetime(2024, 5, 24, 0, 0)], dtype=object), 'y': array([27.65730019, 26.73730019, 26.26730019, 26.07730019, 26.47730019])}, {'fill': 'tonexty', 'fillcolor': 'rgba(0,100,80,0.2)', 'line': {'color': 'green'}, 'name': '', 'type': 'scatter', 'x': array([datetime.datetime(2024, 5, 2, 0, 0), datetime.datetime(2024, 5, 3, 0, 0), datetime.datetime(2024, 5, 10, 0, 0), datetime.datetime(2024, 5, 17, 0, 0), datetime.datetime(2024, 5, 24, 0, 0)], dtype=object), 'y': array([27.75730019, 28.67730019, 29.14730019, 29.33730019, 28.93730019])}], 'layout': {'template': '...', 'title': {'text': 'PFE'}, 'xaxis': {'title': {'text': 'Date'}}, 'yaxis': {'title': {'text': 'Price'}}} }))

When i Tried using Output({'type': 'chart', 'index': MATCH}, 'children'), with the same Input as above in the code without any errors, but the buttons are unable to β€œfind” the div or even print out the stock name to the terminal

Im able to match the buttons clicking by adding a dictionary instead of an individual id.

def nest_acc(date, edf):
    market_stocks = stocks_on_specific_date
    am_stocks = morning_stock_list_on_date
    pm_stocks = afternoon_stock_list_on_date
fill_text = lambda x: dbc.Row([
        dbc.Col(dcc.Markdown(f'''### ${x.upper()} ''')),
        dbc.Col(f'''{info_file[x]['sector']}'''),
        dbc.Col(dbc.Button("Show Expected Move", id = {'name':f'show_chart_button', 'value':x}, n_clicks = 0, value = x))
    ])
    text_plot = lambda x: [fill_text(x), html.Div(id = {'name':f'show_chart', 'value':x})]
    
    am_acc = dbc.Accordion(
        [dbc.AccordionItem(text_plot(stock), title=info_file[stock]['longName']) for stock in am_stocks],
        flush = True,
        start_collapsed = False
    )
    
    pm_acc = dbc.Accordion(
        [dbc.AccordionItem(text_plot(stock), title=info_file[stock]['longName']) for stock in pm_stocks],
        flush = True, 
        start_collapsed = True
    )
    
    out_acc = dbc.Accordion(
        [dbc.AccordionItem(am_acc, title="🌀️ AM"), dbc.AccordionItem(pm_acc, title="⛅️ PM")],
        start_collapsed=False,
        flush = True,
    )
    return out_acc

main_acc = dbc.Accordion(
    [dbc.AccordionItem(nest_acc(exps[date], edf), title = exps_show[date]) for date in range(len(exps))],
    start_collapsed = False,
    flush = True
)

from dash import MATCH, ALL, State, Input, Output
app = Dash(__name__, external_stylesheets = [dbc.themes.BOOTSTRAP])
app.layout = html.Div([main_acc])
app.run(jupyter_mode="external")

@app.callback(
    Output({'name': 'show_chart', 'value': MATCH}, 'children'),
    Input({'name': 'show_chart_button', 'value': MATCH}, 'n_clicks'),
    State({'name': 'show_chart_button', 'value': MATCH}, 'value')
)

def show_chart(*args):
    print(args)
n_clicks, stock = args
if n_clicks: return html.Div("stuff", className="bg-secondary h-100")

when the args are printed out we get (0, stock_name) so by adding if statement at the end i have reached the desired result.

1 Like