Sunday 18 October 2020

Preventing chained callbacks from switching dropdown to original value dash

I have a lengthy section of code for several chained callbacks that stem from a multiple nested dictionary. I have all of the necessary dropdowns and options that I would like to provide. However, whenever I change the 'crop' dropdown in this example to something other than the original option (which is corn) it resets the 'Weighting' dropdown below. Similarly, if I change the 'Weighting' dropdown, it resets the 'Forecast Variable' dropdown to the original option. How can I prevent this? The point of the chained callbacks was so that changing one option would change the data that is plotted, as they are all linked.

I don't think the data is important here? But it functions like this:

final_dict[init_date][model][weight][crop]

The above exact dictionary then would output a dataframe. The columns in the dataframe then would be the 'forecast variable' which will eventually be plotted. If I do need to add data I can try and do that but the dict is VERY big.

Here is the code I have so far. Notice that the graph is empty because I haven't gotten that far yet.

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from pandas import Timestamp
import plotly.graph_objs as go
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
import pandas as pd
import numpy as np
from datetime import timedelta
import glob
import datetime as dt

import xarray as xr
import os 
from PIL import Image
import time
import random

my_dict={}

for i in np.arange(1,17,1):
    n=random.randint(1,10)
    m=random.randint(1,10)
    data=[[pd.Timestamp('2020-10-06'),n,m],[pd.Timestamp('2020-10-07'),m,n],[pd.Timestamp('2020-10-08'),n,m],[pd.Timestamp('2020-10-09'),m,n]]
    my_dict[i]=pd.DataFrame(data=data, columns=['time', 'Temp','Precip'])

final_dict={'day1':{'model1':{'weight1':{'crop1':my_dict[1], 'crop2':my_dict[2]},
                           'weight2':{'crop1':my_dict[3], 'crop2':my_dict[4]}},
                 
                 'model2':{'weight1':{'crop1':my_dict[5], 'crop2':my_dict[6]},
                           'weight2':{'crop1':my_dict[7], 'crop2':my_dict[8]}}},
         
         'day2':{'model1':{'weight1':{'crop1':my_dict[9], 'crop2':my_dict[10]},
                           'weight2':{'crop1':my_dict[11], 'crop2':my_dict[12]}},
                 
                 'model2':{'weight1':{'crop1':my_dict[13], 'crop2':my_dict[14]},
                           'weight2':{'crop1':my_dict[15], 'crop2':my_dict[16]}}}}

app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])

controls = dbc.Card(
    [   dbc.FormGroup(
            [dbc.Label("Init Date"),
                dcc.Dropdown(
                    id='init_dd',
                    options=[{'label': k, 'value': k} for k in final_dict.keys()],
                    value=list(final_dict.keys())[0],
                    clearable=False,
                ),
            ]
        ),
        dbc.FormGroup(
            [dbc.Label("Model"),
                dcc.Dropdown(
                    id='model_dd',
                    clearable=False,
                ),
            ]
        ), 
        dbc.FormGroup(
            [dbc.Label("Crop"),
                dcc.Dropdown(
                    id='crop_dd',
                    clearable=False,
                ),
            ]
        ),           
        dbc.FormGroup(
            [dbc.Label("Weighting"),
                dcc.Dropdown(
                    id='weight_dd',
                    clearable=False,
                ),
            ]
        ),
        dbc.FormGroup(
            [dbc.Label("Forecast Variable"),
                dcc.Dropdown(
                    id='columns_dd',
                    clearable=False,
                ),
            ]
        ),

    ],
    body=True,
)


app.layout = dbc.Container(
    [
        html.Hr(),
        dbc.Row([
            dbc.Col([
                dbc.Row([
                    dbc.Col(controls)
                ],  align="start"), 
            ],xs = 2)
            ,
            dbc.Col([
                dbc.Row([
                    dbc.Col([html.Div(id = 'plot_title')],)
                ]),
                dbc.Row([
                    dbc.Col(dcc.Graph(id="crop-graph")),
                ])
            ])
        ],), 
    ],
    fluid=True,
)
    
# Callbacks #####################################################################
#set the model
@app.callback(
    Output('model_dd', 'options'),
    [Input('init_dd', 'value')])
def set_model_options(model):
    return [{'label': i.replace('_',' '), 'value': i} for i in final_dict[model]]
 
@app.callback(
    Output('model_dd', 'value'),
    [Input('model_dd', 'options')])
def set_model_options_value(available_model):
    return available_model[0]['value']

#set the weight
@app.callback(
    Output('weight_dd', 'options'),
    [Input('init_dd', 'value'),
     Input('model_dd', 'value')])
def set_weight_options(selected_init, selected_model):
    return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model]]
 
@app.callback(
    Output('weight_dd', 'value'),
    [Input('weight_dd', 'options')])
def set_weight_value(available_weight):
    return available_weight[0]['value']

#set the crop
@app.callback(
    Output('crop_dd', 'options'),
    [Input('init_dd', 'value'),
     Input('model_dd', 'value'),
     Input('weight_dd', 'value')])
def set_crop_options(selected_init, selected_model, selected_weight):
    return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model][selected_weight]]
 
@app.callback(
    Output('crop_dd', 'value'),
    [Input('crop_dd', 'options')])
def set_crop_value(available_crop):
    return available_crop[0]['value']

#set the variable
@app.callback(
    Output('columns_dd', 'options'),
    [Input('init_dd', 'value'),
     Input('model_dd', 'value'),
     Input('weight_dd', 'value'),
     Input('crop_dd', 'value')])
def set_column_options(selected_init, selected_model, selected_weight, selected_crop):
    return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model][selected_weight][selected_crop].columns[1:]]
 
@app.callback(
    Output('columns_dd', 'value'),
    [Input('columns_dd', 'options')])
def set_column_value(available_column):
    return available_column[1]['value']

app.run_server(mode='external', port = 8099)   

Edit: Added in sample dummy data. Notice how when changing certain combinations of options, other options switch back to the original value. Would like to prevent that from happening.



from Preventing chained callbacks from switching dropdown to original value dash

No comments:

Post a Comment