I have a simple reproducible example (the full code will be at the bottom) to illustrate the problem.
First I have 4 dataframes that are stored in a list. The dataframes are simplified versions, so there is only 1 column called col
. In each dataframe, the first entry is the condition
, which in this example the condition
is either A, B or C. The purpose of the app is to handle each dataframe one by one while extracting information based on the condition
.
# 4 dataframes that are stored in a list
data1 = {
"col": ["A", "random1", "random2", "S1", "random3", "P123"]
}
data2 = {
"col": ["A", "random3", "random4", "S1", "random10", "P124"]
}
data3 = {
"col": ["B", "random99", "S2", "P125"]
}
data4 = {
"col": ["C", "g4rr", "dsf31", "dsfjjb", "sdfdfsd", "123jbds", "01bdwe"]
}
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
df3 = pd.DataFrame(data3)
df4 = pd.DataFrame(data4)
list1 = [df1, df2, df3, df4]
For example, if the condition is A, the supplier_name_value can be extracted in the [3] index of the column.
If the condition is B, the supplier_name_value can be extracted in the [2] index.
If the condition is C, supplier_name_value is empty.
# supplier name value and po number value differs depending on the condition
if list1[st.session_state.page]['col'][0] == "A":
supplier_name_value = list1[st.session_state.page]['col'][3]
po_number_value = list1[st.session_state.page]['col'][5]
elif list1[st.session_state.page]['col'][0] == "B":
supplier_name_value = list1[st.session_state.page]['col'][2]
po_number_value = list1[st.session_state.page]['col'][3]
else:
supplier_name_value = ""
po_number_value = ""
If the supplier_name_value is not empty, then the dataframe is relevant, and selectbox is chosen as Yes. Otherwise, it’s chosen as No.
if supplier_name_value:
choice = st.selectbox(label="Relevant?", options=["Yes", "No"], index=0)
else:
choice = st.selectbox(label="Relevant?", options=["Yes", "No"], index=1)
If the selectbox is Yes, the supplier_name_value and po_number_value will be extracted into text_input which are in a form.
Else, no text_input
When the user clicks Next, it saves those information to a spreadsheet (not included in the code) and then goes to the next dataframe in the list, and relevant information will be extracted. It works great when user don’t need to change the information, but there is problem when the user needs to change things.
Problem
- For example, on the first dataframe where the selectbox is Yes because the condition is A. If the user overrides the selectbox to No, the text_input will disappear (as expected) and user can click Next. But for the second dataframe where the selectbox should be Yes again, it somehow won’t update the selectbox, and it stays as No.
- The reason to use st.form is because it has an option to clear_on_submit, so everytime I go to the next dataframe, those fields will be refreshed which allows user to override the original fields without worrying about the fields not updating for the next dataframe.
- I can’t put selectbox in the st.form, because it won’t allow user override to dynamically change the form, because the form waits for user input first before the submit button is clicked.
Expected Behaviour
For example, what’s displayed below is the first dataframe. Because it has condition A, thus the supplier_name and po_number is pulled using their relevant positions and the information is extracted to the st.form. The user checks if the pulled information is correct, then click next. The user is not trying to correct the dataframe, but simply checking if the extracted info is correct, if it’s correct, the user clicks next, if it’s not right, the user changes the text inside the form, then click next. The information will be saved elsewhere (this saving process is not included in the demo app), but just think about everytime the user clicks next, the info is saved to a spreadsheet.
After the user clicks next, it goes to the next dataframe in the list, the user checks again, then clicks next. Then follow the same procedure over and over.
Now back to the first dataframe, when the user examines the pulled information and the dataframe. The user realised the algorithm made a mistake, and this dataframe should not be relevant, and thus don’t require the information to be extracted. So the user selects No in the selectbox, then clicks next.
What I’m hoping to see is it goes to the next dataframe and the below is presented.
But what actually happened is, the
Relevant
selectbox is still No because it didn’t get refreshed.
My assumption is that streamlit sees because the condition is the same with dataframe2 and dataframe1, both have condition A. So when it advances to dataframe2, it decides not to run the script from top to bottom again.
Summary: what I want the app to do is, after the user clicks next, it goes to the next dataframe, while running the python script from top to bottom again (for example, to reassess if the dataframe is relevant or not
if supplier_name_value:
choice = st.selectbox(label="Relevant?", options=["Yes", "No"], index=0)
else:
choice = st.selectbox(label="Relevant?", options=["Yes", "No"], index=1)
Full Code
import streamlit as st
import pandas as pd
if 'page' not in st.session_state:
st.session_state.page = 0
# 4 dataframes that are stored in a list
data1 = {
"col": ["A", "random1", "random2", "S1", "random3", "P123"]
}
data2 = {
"col": ["A", "random3", "random4", "S1", "random10", "P124"]
}
data3 = {
"col": ["B", "random99", "S2", "P125"]
}
data4 = {
"col": ["C", "g4rr", "dsf31", "dsfjjb", "sdfdfsd", "123jbds", "01bdwe"]
}
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
df3 = pd.DataFrame(data3)
df4 = pd.DataFrame(data4)
list1 = [df1, df2, df3, df4]
# display the list
st.write(list1[st.session_state.page])
# supplier name value and po number value differs depending on the condition
if list1[st.session_state.page]['col'][0] == "A":
supplier_name_value = list1[st.session_state.page]['col'][3]
po_number_value = list1[st.session_state.page]['col'][5]
elif list1[st.session_state.page]['col'][0] == "B":
supplier_name_value = list1[st.session_state.page]['col'][2]
po_number_value = list1[st.session_state.page]['col'][3]
else:
supplier_name_value = ""
po_number_value = ""
# if the supplier name value is not empty, then the selectbox is Yes, otherwise No
if supplier_name_value:
choice = st.selectbox(label="Relevant?", options=["Yes", "No"], index=0)
else:
choice = st.selectbox(label="Relevant?", options=["Yes", "No"], index=1)
# if the selectbox is yes, then there are fields to be populated, otherwise No
if choice == "Yes":
with st.form(key="my_form", clear_on_submit=True):
supplier_name = st.text_input(label="Supplier Name", value=supplier_name_value)
po_number = st.text_input(label="PO Number", value=po_number_value)
next_button = st.form_submit_button(label="Next")
if next_button:
if st.session_state.page + 1 < len(list1):
st.session_state.page += 1
st.experimental_rerun()
else:
st.markdown("It's already the last one")
else:
next_button2 = st.button(label="Next")
if next_button2:
if st.session_state.page + 1 < len(list1):
st.session_state.page += 1
st.experimental_rerun()
else:
st.markdown("It's already the last one")