I am working on a dashboard with dash-plotly and I have created two different callbacks to handle data storage. The first callback loads initial data based on a dropdown
@app.callback(Output('battery-data','data'),
[Input('battery-dropdown', 'value')],
[State('battery-data','data')]
)
def load_data(battery_name):
example_data = utils_new.load_dump_from_mongo(collection,battery_name)
example_pd = pd.DataFrame(example_data)
print(example_pd.head())
example_pd = example_pd.drop('_id',axis=1)
return(example_pd.to_json(date_format='iso', orient='split'))
While the second gets as input the initial data and then updates it recurrently
@app.callback(Output('battery-data', 'data'),
[Input('interval-component', 'n_intervals'),
Input('battery-data','data'),
Input('battery-dropdown', 'value']
) [State('battery-data', 'data')])
def update_data(n, battery_json, battery_name, time_range):
example_old_data = pd.read_json(battery_json, orient='split')
example_old_data['Time'] = pd.to_datetime(example_old_data['Time']).apply(lambda x: x.replace(tzinfo=None))
last_record_time = parser.parse(str(example_old_data['Time'].tail(1).item()))
# load last 10 seconds of data as a list of records
update_data = utils_new.update_data_from_mongo(collection,battery_name,last_record_time)
update_pd = pd.DataFrame(update_data)
print('SIZE OF THE UPDATE', len(update_pd))
update_pd.drop("_id", axis=1, inplace=True)
# add update data
len_update = len(update_pd)
example_new_data = example_old_data.append(update_pd,ignore_index=True)
example_new_data.drop(example_new_data.head(len_update).index,inplace=True)
example_new_data = example_new_data.reset_index(drop=True)
# filter for battery id
example_new_data_battery = example_new_data[example_new_data['Battery_ID'] == battery_name]
return(example_new_data.to_json(date_format='iso', orient='split'))
app.layout = dbc.Container([
dbc.Row([
html.H1(children='Battery monitoring'),
html.Div(children='''
This dashboard shows battery state
'''),
html.Br(style={'marginBottom': '10px'})
]),
dbc.Row([
dbc.Col([
dbc.Row([batteries_drop()]),
],width=6),
dbc.Col([
dbc.Row(id='card-anomaly'),
], width=6)]),
dcc.Store(id='battery-data')
dcc.Interval(id='interval-component',
interval=10*1000,
n_intervals=0)
])
However, in this way, it doesn’t work because I use the same ID for two different callbacks. Do you know if there is a way to make it work? Or if you know a strategy to address this? Initially, I would like to load only the data from a single battery to avoid crashing the dashboard and then I need to get the data updated every 10 secs.
Thanks for any suggestion or help!
Advertisement
Answer
You can fix this issue by using the great dash-extensions library.
This library has some modules that modify the Dash and allow us to extend it to do more than the standard Plotly does. By using a module called MultiplexerTransform
combined with DashProxy you will be able to use the same ID in different callback outputs;
Below is a code snippet which does exactly what you did, you just need to adjust it to your context.
from dash.exceptions import PreventUpdate
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, html
app = DashProxy(transforms=[MultiplexerTransform()])
app.layout = html.Div([
html.Button("Left", id="left"),
html.Button("Right", id="right"),
html.Div(id="log")
])
@app.callback(Output("log", "children"), Input("left", "n_clicks"))
def left(n_clicks):
if not n_clicks:
raise PreventUpdate()
return "left"
@app.callback(Output("log", "children"), Input("right", "n_clicks"))
def right(n_clicks):
if not n_clicks:
raise PreventUpdate()
return "right"
if __name__ == "__main__":
app.run_server()
To read more about this module and library you can read: dash-extensions
Regards,
Leonardo