Convert data only when it is downloaded

Hello you lovley people,
I build a small app for people to play around with their own data on a conditional generation task. It’s used only by internal trusted users so security is of no concern to me. The user will upload a file, get their generations and in the end they should be able to download the results as a csv file. I want to prevent the conversion to happen each time the app reloads. I am aware of @chach_data, but since the df changes each time a new result is appended the conversion happens. So in a very minimal example, how can I make it that convert_df is only run when the download button is clicked.

import pandas as pd
import streamlit as st

if 'df' not in st.session_state:
    st.session_state.df = pd.DataFrame()

@st.cache_data
def convert_df(df):
    print('happend')
    return df.to_csv().encode('utf-8')

def do():
    print('done')
    st.session_state.df = pd.concat([st.session_state.df, pd.Series([1,2,3]).T])
    print('done')


data = convert_df(st.session_state.df) 
st.button('Do', on_click=do)
st.download_button("Download", data=data, file_name='res.csv', mime='csv')

At the moment I’m using a workaround. I have two variables that I store in session state, one stores the data while the users are running the app and one which contains an empty df. I then added a second button that copies the df to the variable that will be exported. But I’m sure there is a better way, right?

if 'df' not in st.session_state:
    st.session_state.df = pd.DataFrame()

if 'df_export' not in st.session_state:
    st.session_state.df_export = pd.DataFrame()

@st.cache_data
def convert_df(df):
    return df.to_csv().encode('utf-8')

def do():
    st.session_state.df = pd.concat([st.session_state.df, pd.Series([1,2,3]).T])
    print('done')

def save():
    st.session_state.df_export = st.session_state.df

data = convert_df(st.session_state.df_export) 
st.button('Do', on_click=do)
st.button('Save', on_click=save)
st.download_button("Download", data=data, file_name='res.csv', mime='csv')

Hi @MaxB

I thought your solution works well in a multi-step manner, however it seems you’d like an approach that performs everything in 1 step.

A while back before the st.download_button was released, I would use something like the following:

def filedownload(df):
    csv = df.to_csv(index=False)
    b64 = base64.b64encode(csv.encode()).decode()  # strings <-> bytes conversions
    href = f'<a href="data:file/csv;base64,{b64}" download="SP500.csv">Download CSV File</a>'
    return href

st.markdown(filedownload(df_selected_sector), unsafe_allow_html=True)

Prior to the actual generation of the CSV file, you can add additional task to perform and package it into a single function, which when users click on would trigger all the task in the function.

Hope this helps.

1 Like

You may want to give a thumbs up to this open issue:

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.