Dash_table update on click not working

Hello,

I am trying to update a dash_table based on data from an external data source when a button has been clicked. I have managed to get the selected rows working it highlights the rows but the function for on click isn’t working. The current code is:

def account_data(db_reference):
  ref = db.reference(db_reference)
  users = ref.get()
  users = pd.DataFrame.from_dict(users, orient = "index")
  users.reset_index(inplace=True)
  users.rename(columns = {"index":"uid"}, inplace=True)
  #clean the data
  users = users.replace(r'^\s*$', np.nan, regex=True)
  users["city_of_residence"] = users["city_of_residence"].str.split(",").str[0]
  users["city_of_residence"] = users["city_of_residence"].str.replace('"', "")
  users["city_of_residence"] = users["city_of_residence"].str.rstrip()

  return users

def auth_data():

  user_dict = {}

  for user in fb_auth.list_users().iterate_all():
    user_dict[user.uid] = {"Email":user.email}

  auth_users = pd.DataFrame.from_dict(user_dict, orient = "index")
  auth_users.reset_index(inplace=True)
  auth_users.rename(columns = {"index":"uid"}, inplace=True)

  return auth_users

def unathorised_users(accounts, users):

  uids_no_account = list(set(accounts["uid"]) - set(users["uid"]))

  uids_not_verified = users[(users["Verified"] == False)
                              | (users["Verified"] == "False")
                              | (users["Verified"] == "false")
                              | (users["Verified"].isna())]
  uids_not_verified = list(uids_not_verified["uid"])

  #merge_list 
  uids_to_verify = uids_no_account + uids_not_verified

  return uids_to_verify

def data_to_verify(users, accounts, uids):

  users_to_verify = users.merge(accounts,
                                on = "uid",
                                how = "outer")

  users_to_verify = users_to_verify[users_to_verify["uid"].isin(uids)]

  users_to_verify["name"] = users_to_verify["name"].replace(np.nan, "Name not given")
  users_to_verify["Email"] = users_to_verify["Email"].replace(np.nan, "Email not given")
  users_to_verify.rename(columns = {"uid":"User id",
                                  "name":"Name"}, inplace=True)

  return users_to_verify


users = account_data("/users/") 

auth_users = auth_data()

uids_to_verify = unathorised_users(auth_users, users)

users_to_verify = data_to_verify(users, auth_users, uids_to_verify)

columns_to_verify = ["User id", "Name", "Email"]

app.layout = html.Div([
html.Div([html.H3("Table of unverfied users", style = {"color":"grey"}),
                 dash_table.DataTable(id = "email-auth",
                                 columns = [{"name":i, "id":i} for i in columns_to_verify],
                                 data = users_to_verify.to_dict("records"),
                                #for page size i.e. pages page_size = 10
                                  page_size = 50,
                                 page_action = "none",
                                 #add row selectivity
                                 row_selectable = "multi",
                                 #store selected rows
                                 selected_rows = [],
                                  fixed_rows = {"headers":True},
                                  style_header = {"backgroundColor":"blue",
                                                  "color": "white",
                                                  "border":"1px solid black"},
                                  style_cell = {"textAlign":"left",
                                                "border":"1px solid black"},
                                 style_table={ "overflowY":"auto",
                                                "overflowX":"auto"})],
                style = {"display":"block",
                        "width":"80%",
                        "marginTop":10,
                        "marginBottom":20,
                        "margin-left":"auto",
                        "margin-right":"auto",
                        "padding":"10px"}),

    html.Div([
    
    dcc.ConfirmDialogProvider(
      children = html.Button("Verify Users", id = "but-verify", n_clicks = 0),
      id = "check_verification",
      message = "Are you sure you want to verify selected users"
    ),

    dcc.ConfirmDialogProvider(
      children = html.Button("Remove Users", id =  "but-remove", n_clicks = 0),
      id = "check_removal",
      message = "Are you sure you want to permanently remove selected users"
    )
    ])
])

@app.callback(
  Output("email-auth", "style_data_conditional"),
  Input("email-auth", "selected_rows")
 # prevent_initial_call = True
)
def update_styles(selected_rows):
  print("I am called")
  print(selected_rows)
  table_style = [{"if": {"row_index":i}, "background_color":"#F0F8FF"} for i in selected_rows]

  return table_style  

@app.callback(
  Output("email-auth", "data"),
  [Input("but-verify", "n_clicks")],
  [State("email-auth", "selected_rows")],
  prevent_initial_call=True
)
def verify_data(verify_clicks, selected_rows):

  if verify_clicks is None:
    raise dash.exceptions.PreventUpdate
  else:

    users = account_data("/users/")
    auth_users = auth_data()
    uids_to_verify = unathorised_users(auth_users, users)
    users_to_verify = data_to_verify(users, auth_users, uids_to_verify)

    user_ids = users_to_verify.iloc[selected_rows]
    user_ids = list(user_ids["User id"])

    for user_id in user_ids:
      try:
        ref.child(user_id).child("Verified").set(True)
      except:
        print(user_id, " cannot be verified")   

    users = account_data("/users/")
    auth_users = auth_data()
    uids_to_verify = unathorised_users(auth_users, users)
    users_to_verify = data_to_verify(users, auth_users, uids_to_verify)

  return users_to_verify.to_dict("records")

The account data(), auth_data(), unauthorised_users() and data to verify() functions all work and extract data from the firebase database when the app is initially loaded. I just want to update the realtime database, then the same calls are made to get the updated data. The function doesn’t seem to want to work at all for updating the dash table, or the firebase realtime database and no errors are thrown.

Any advice appreciated!

Thanks!

I am not at all familiar with the Firebase SDK for Python, so I hope somebody else can jump in and help.

I wonder if the problems isn’t in this line:

ref seems to be defined only in the body of account_data, or maybe it is your original code somewhere else. You say that no error appears, but does the callback get trigger? Does it enter your for loop?

1 Like

Hi @jlfsjunior

Thanks once again for your help!

Sorry, that line is specified earlier in the code.

I believe I managed to figure out why it didn’t work. Since the button is wrapped in a dcc.ConfirmDialogProvider the button wasn’t clicking i.e. n_clicks didn’t seem to be going up. So instead of using:

@app.callback(
  Output("email-auth", "data"),
  [Input("but-verify", "n_clicks")],
  [State("email-auth", "selected_rows")],
  prevent_initial_call=True
)
def verify_data(verify_clicks, selected_rows):

  if verify_clicks is None:
    raise dash.exceptions.PreventUpdate
  else:

    users = account_data("/users/")
    auth_users = auth_data()
    uids_to_verify = unathorised_users(auth_users, users)
    users_to_verify = data_to_verify(users, auth_users, uids_to_verify)

    user_ids = users_to_verify.iloc[selected_rows]
    user_ids = list(user_ids["User id"])

    for user_id in user_ids:
      try:
        ref.child(user_id).child("Verified").set(True)
      except:
        print(user_id, " cannot be verified")   

    users = account_data("/users/")
    auth_users = auth_data()
    uids_to_verify = unathorised_users(auth_users, users)
    users_to_verify = data_to_verify(users, auth_users, uids_to_verify)

  return users_to_verify.to_dict("records")

The following code appeared to work instead:

@app.callback(
  Output("email-auth", "data"),
  Input("check_verification", "submit_n_clicks"),
  [State("email-auth", "selected_rows")],
  prevent_initial_call=True
)
def verify_data(verify_clicks, selected_rows):

  changed_id = [p["prop_id"] for p in dash.callback_context.triggered][0]

  print(changed_id)

  if changed_id is None:

    print("No update needed")
    raise dash.exceptions.PreventUpdate

elif "check_verification" in changed_id:

...

So I believe that although the button was being clicked, the last thing being triggered was the confirm dialog provider, and while it would submit_n_clicks it wouldn’t send them to the number of clicks of the button (at least not how I set it up). So the code would set to run when the confirm dialog button was confirmed rather than the button itself.

For the previous code, on click it wouldn’t enter the for if conditions, so I thought something was going wrong with n_clicks variable somewhere. I then printed out the changed_id from the callback_context and found that it was the dialog provider that was being triggered.

I am still not entirely sure of how the ConfirmDialogProvider interacts with the button clicks, but the final piece of code managed to work for the purpose I wanted it to at least (or has done so far I should say anyway… always chance for that to change…)

1 Like

I see… Sorry, I think I guided you in the wrong direction in the other thread when you asked about ConfirmDialogProvider. I used a different system for modals, not this one…

I believe the adjustment you made is right, and the docs are pretty straightforward for this component. Only noticeable distinction is that submit_n_clicks defaults to zero.

1 Like

No worries! I managed to get there in the end and you answered my initial question on the last thread anyway! :slight_smile:

I think so yes, although just getting the knowledge of how these components interact is the challenge when this is my first time using dash. Yes, that’s why I am using the callback_context so it takes in the number of clicks and when they are changed, but uses the callback_context to update which button was pressed and then perform the action from there.

Thanks though!