How to save and load Dash Callback Context Inputs / Inputs_list

Dear friends,
I’m fighting unsuccessfully with following issue:
My goal is to fill in the form with values and then store all information (into the csv file?) to be able to load all values back into the form when both switching between tabs or when re-opening the app.

I’m new in dash Plotly, so I apologise if my question is trivial.
I would very very appreciate your help. Thanks in advance.

In general I need following functionality:
Step 1. Button ‘Add Row’ + fill in values
Step 2. Button ‘Save Form’ to save the form e.g. to csv file
Step 3. Button ‘Clear Form’
Step 4. Button ‘Load Form’ with the goal to get exactly the same status as after Step 1

I need a help with Step 2 and especially with Step 4

Here is the code…I work in Google Colab

from google.colab import drive
drive.mount('/drive/')

!pip install jupyter-dash
!pip install dash-bootstrap-components

from jupyter_dash import JupyterDash
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State, ALL
import pandas as pd


add_button = dbc.Button(
    "Add Row",
    id="add-idea",
    n_clicks=0,
    outline=True,
    size="sm",
    color="primary"
)

save_button = dbc.Button(
    "Save Form",
    id="save-form",
    n_clicks=0,
    outline=True,
    size="sm",
    color="primary"
)

clear_button = dbc.Button(
    "Clear Form",
    id="clear-form",
    n_clicks=0,
    outline=True,
    size="sm",
    color="primary"
)

load_button = dbc.Button(
    "Load Form",
    id="load-form",
    n_clicks=0,
    outline=True,
    size="sm",
    color="primary"
)

owner_tabs = html.Div(
    [
        dbc.Tabs(
            [
                dbc.Tab(label="Owner1", tab_id="O1"),
                dbc.Tab(label="Owner2", tab_id="O2"),
            ],
            id="tabs-owner",
            active_tab="O1",
        ),
        save_button,
        clear_button,
        load_button,
        html.Div(id='idea-container', children=[]),
        add_button,
    ]
)


strike_input = dcc.Input(
    id={
        'type': 'input-strike',
        'index': 'n_clicks'
        },
    type="number", 
    min=2500, 
    max=7000, 
    step=50, 
    placeholder = 'Strike',
)

quantity_input = dcc.Input(
    id={
        'type': 'input-quantity',
        'index': 'n_clicks'
        },
    type="number", 
    min=1, 
    max=1000, 
    step=1, 
    placeholder = 'Quantity',
)


action_dropdown = dcc.Dropdown(
    id={
        'type': 'dropdown-action',
        'index': 'n_clicks'
        },
    options=[{'label': i, 'value': i} for i in ['BTO', 'STO', 'BTC', 'STC']],
    placeholder="Act",
    clearable=False,
)

remove_button = html.Button(
    'Remove',
    id={
        'type': 'button-remove',
        'index': 'n_clicks'
        },
)


app = JupyterDash(__name__, external_stylesheets=[dbc.themes.CYBORG])
app.config.suppress_callback_exceptions = True


app.layout = dbc.Container(
    [
     dbc.Row([dbc.Col([owner_tabs])]),
    ],
)

@app.callback(
    Output('idea-container', 'children'),
    Input("tabs-owner", "active_tab"),
    Input('save-form', 'n_clicks'),
    Input('clear-form', 'n_clicks'),
    Input('load-form', 'n_clicks'),
    Input('add-idea', 'n_clicks'),
    Input({'type': 'input-quantity', 'index': ALL}, 'value'),
    Input({'type': 'input-strike', 'index': ALL}, 'value'),
    Input({'type': 'dropdown-action', 'index': ALL}, 'value'),
    Input({'type': 'button-remove', 'index': ALL}, 'n_clicks'),
    State('idea-container', 'children')
    )
def update_ideas(owner, save, clear, load, add, quantity, strike, action, remove, ideas):
  new_dropdown = dbc.FormGroup([quantity_input, strike_input, action_dropdown, remove_button], row=True)
  triggered = [t["prop_id"] for t in dash.callback_context.triggered]
  if 'add-idea' in triggered[0]:
    ideas.append(new_dropdown)
  if 'button-remove' in triggered[0]:
    for (i, value) in enumerate(remove):
      if value==1:
        del ideas[i]
        break
  if 'save-form' in triggered[0]:
    # Option 1: save separate values to DataFrame
    dfval = pd.DataFrame(columns=['owner', 'quantity', 'strike', 'action'])
    for i in range(len(ideas)):
      dfval.loc[len(dfval.index)] = [owner, quantity[i], strike[i], action[i]]
    dfval.to_csv('/drive/MyDrive/dfval.csv', index=False)
    # Option 2: save whole dictionary to DataFrame - not working
    # dfdict=pd.DataFrame(ideas,index=[0])
    # dfdict.to_csv('/drive/MyDrive/dfdict.csv', index=False)
  if 'clear-form' in triggered[0]:
    ideas = []
  if 'load-form' in triggered[0]:
    #Option 1: load separate values
    dfval = pd.read_csv('/drive/MyDrive/dfval.csv')
    for i in range(len(dfval)):
      ideas.append(new_dropdown)
      # How to fill in values to i'deas' instance? 
      # quantity[i]=dfval['quantity'].values[i]
      # strike[i]=dfval['strike'].values[i]
      # action[i]=dfval['action'].values[i]
    #Option 2: load whole dictionary - not working
    # ideas = pd.read_csv('/drive/MyDrive/dfdict.csv').to_dict('records')

  return ideas

if __name__ == "__main__":
  app.run_server(mode='inline', debug=True, use_reloader=False)

Hi @Mike12

  1. you can use the menu option to copy code like this example:

image

  1. For write and read data use df.to_csv(‘data.csv’) and pd.read_csv(‘data.csv’) inside the callbacks.
  1. Thanks for pointing it out - my original post edited
  2. Could you please specify it a bit more? Instance “ideas” in my case is not dataframe but stringified dictionary. https://dash.plotly.com/pattern-matching-callbacks

Sorry is not clear to me what you want to store.

Whatever you want to store, transform it into a DataFrame and write in a csv file. then read the scv file and bild what you want to show.

This is exactly the point where I failed. I’m simply not able to load the data back into the form.

If you want help, be more specific, all the information can be store, then read and also use to rewrite a form. But if you don’t mention what is the information you want to store, in wich dash element is entered by the user and in which form do you want to rewrite later, it is a little bit difficult to get help.

I have added more details into my original post, sorry for not being more precise before. I hope I explained it better now.

Hi @Mike12

I read your code and its to difficult to follow, because you are bilding the entire layout app in your callback.

Consider changing your method because is very difficult to rebild all the app.

First bild the layout with the tabs and buttons, adding an id for each element, then create a callback for each user interaction (for example one for adding rows, one for save form, another for clear, etc).

In that way it will be easy just to save what the user enters in the dropdown.

working with dynamicaly elements and then try to rebild it is not so easy.

Hope it helps.

Hi @Eduardo,
I have chosen my ‘complicated’ approach to be able to edit the values filled in to the form. I’m not completely sure how I could elegantly do it using your proposal. Some example might help.
Anyway thank you really a lot for your effort, you are the only one who answered.

I would do the following,

  1. Define a data structure X that describes the form state, e.g. a dataframe
  2. Write a function build_form that takes X as input and renders the form components
  3. Bind a callback to the save button that writes X to some destination, e.g. to disk as pickle or json
  4. Bind a callback to the load button that loads X and calls build_form(X)
  5. Bind a callback to the clear button that calls build_form(None)

Similarly, do the initial render via build_form(None). Note that you will probably have to merge the callbacks (4,5) as they target the same output.

1 Like

Now I know…my approach when using Pattern-Matching Clawbacks was wrong…all what I need can be done using Dash DataTable.

Thanks for the support anyway