Performance issue when loading module in Streamlit application

Hello everyone,

I’m developing an application in Streamlit that manages several inventory processes like Sales, Purchases, Inventory Replenishment, Warehouse Transfers, etc. However, when I select the “Warehouse Transfers” module, I notice the page takes a long time to load.

I would like to know what elements of Streamlit affect the efficiency of such applications and what I can optimize to reduce the loading time, I notice the page takes approximately 3 minutes to load

We have considered using multi threads, temporary tables, views, but we do not know which is the most optimal.

Steps to reproduce the issue:

  1. I enter the project folder using Anaconda with VS Code.
  2. Open the terminal.
  3. Run the command streamlit run Gesnas.py.
  4. Wait for the app to load.
  5. Select the “Warehouse Transfers” module.
  6. The page takes a long time to load.

What could be causing this delay and how can I improve performance? What best practices exist to optimize loading times in Streamlit?

This is the code I am using :

import streamlit as st  # Importa la librería Streamlit para crear aplicaciones web.
import pandas as pd  # Importa pandas para el manejo de datos.
from SystemResources.GesnasDashBoard.DashBoard import DashControlador as DashControlador  # Ajusta las rutas de importación si son necesarias
from SystemResources.GesnasDashBoard import TablaDatosControlador #maneja las funciones para graficar y para configuar las tablas de visor de datos
from SystemResources.GesnasDashBoard.Componentes import ComponenteTraslado
from SystemResources.GesnasDashBoard.ModuloTraslado import CalculadoraControlador as CalculadoraTraslado #controlador para realizar calculos de cantidades a trasladar

def main():
    if st.session_state.get('estado', False):
        st.experimental_rerun()
        return
    else: 
        startDate,endDate = ComponenteTraslado.obtenerFechas()
        #print(f'{startDate},{endDate}')
        almOrigen = ''
        almDestino = ''
        porcentaje_df = ComponenteTraslado.leer_porcentajes()
          # Reemplaza las claves
        porcentaje_df = {
                "PORCENTAJE_BELLO_MONTE" if k == "Bello Monte" else "PORCENTAJE_VALENCIA" if k == "Valencia" else k: v 
                for k, v in porcentaje_df.items()
         }
        porcentaje_df = pd.DataFrame.from_dict([porcentaje_df]).reset_index()
        porcentaje_df['PORCENTAJE_BELLO_MONTE'] = porcentaje_df['PORCENTAJE_BELLO_MONTE']/100
        porcentaje_df['PORCENTAJE_VALENCIA'] = porcentaje_df['PORCENTAJE_VALENCIA']/100
        reposicion = ['Productos Vendidos', f'% de traslado']
        
        if 'filtro' not in st.session_state:
            st.session_state['filtro'] = False
        
        if 'recalcular' not in st.session_state:
            st.session_state['recalcular'] = False 

        
        st.info(f'##### Módulo Traslado de Inventario')  # Muestra un mensaje de bienvenida en la página.
        
        
        listaAlmacenes= ComponenteTraslado.listar_almacenes() #carga la lista de los almacenes disponibles


        # Variables para manejar las selecciones dependientes
        almacen_valencia = "VALENCIA"
        almacen_bello_monte = "BELLO MONTE"
        porcentajeTraslado = 30

        # Crear columnas para los selectboxes
        col1, col2,col3 = st.columns(3)

        # Inicialmente, se definen los valores por defecto
        origen_default = "VALENCIA"
        destino_default = "BELLO MONTE"

        # Utilizamos el estado de sesión de Streamlit para manejar las selecciones
        if 'origen' not in st.session_state:
            st.session_state.origen = origen_default
        if 'destino' not in st.session_state:
            st.session_state.destino = destino_default
        if 'tipo' not in st.session_state:
            st.session_state.tipo = 'Seleccionar'
        
        with col1:
            #selector de tipos de reposición
            tipoReposicion = st.selectbox('Tipo de reposicion',key='tipo_reposicion',options=reposicion) 
            
            #permuta los selectores en base al almacén origen o almacé destino seleccionada
        with col2:
            def update_destino():
                #print("hola mundo")
                # Lógica para actualizar el destino cuando se selecciona un nuevo origen
                selected_origin = st.session_state['origen_select']
                
                #verifica el origen seleccionado si es VALENCIA el destino es BELLO MONTE, y viceversa
                if selected_origin == "BELLO MONTE":
                    st.session_state['seleccionado'] = reposicion[1]
                    st.session_state['tipo_reposicion'] = reposicion[1]
                    st.session_state['destino_select']= "VALENCIA"
                elif selected_origin == "VALENCIA":
                    st.session_state['destino_select'] = "BELLO MONTE"
  
                st.session_state['filtro'] = True

            almOrigen = st.selectbox('Almacen Origen', options=listaAlmacenes,key='origen_select',on_change=update_destino,index=0)


        with col3:   
            # Ajustar la lista de opciones para Almacen Destino dependiendo de la selección de Almacen Origen
            if almOrigen == almacen_valencia:
                opciones_destino = [almacen_bello_monte]
            elif almOrigen == almacen_bello_monte:
                opciones_destino = [almacen_valencia]
            else:
                opciones_destino = listaAlmacenes.copy()
                if almOrigen in opciones_destino:
                    opciones_destino.remove(almOrigen)

            def update_origen():
                # Lógica para actualizar el origen cuando se selecciona un nuevo destino
                selected_destino = st.session_state['destino_select']
                
                if selected_destino == "BELLO MONTE":
                    st.session_state['origen_select'] = "VALENCIA"
                elif selected_destino == "VALENCIA":
                    st.session_state['origen_select'] = "BELLO MONTE"
                

                st.session_state['filtro'] = True

        
            almDestino = st.selectbox('Almacen Destino', options=listaAlmacenes,index=1,key='destino_select',on_change=update_origen)

            #valida el tipo de reposicion 
        if tipoReposicion=='Productos Vendidos':

            # Añade un encabezado en la barra lateral para los filtros.
            #st.subheader('Historial de Productos vendidos')
            fechaCol1, fechaCol2 = st.columns((2))  # Crea dos columnas para entrada de fechas.
            with fechaCol1:
                fecha_desde = pd.to_datetime(st.date_input("###### Fecha Inicio:", startDate))  # Crea un selector de fecha de inicio.
                
                if 'fecha_desde' not in st.session_state:
                     st.session_state.fecha_desde = startDate

                if st.session_state.fecha_desde != fecha_desde:
                     print('entrando')
                     st.session_state['filtro'] = True
                     st.session_state['recalcular']=False
                
                st.session_state.fecha_desde = fecha_desde
                if ("FechaError" in st.session_state) and (st.session_state["FechaError"]):
                            st.error("Debe seleccionar una fecha de venta.")

            with fechaCol2:
                fecha_hasta = pd.to_datetime(st.date_input("###### Fecha Fin:", endDate))  # Crea un selector de fecha de finalización.
                
                if 'fecha_hasta' not in st.session_state:
                     st.session_state.fecha_hasta= endDate
                
                if st.session_state.fecha_hasta != fecha_hasta:
                    st.session_state['filtro'] = True
                    st.session_state['recalcular']=False
                
                st.session_state.fecha_hasta = fecha_hasta 

                if ("FechaError" in st.session_state) and (st.session_state["FechaError"]):
                    st.error("Debe seleccionar una fecha de venta.")
            
          #  vendidos_df = ComponenteTraslado.productos_vendidos(fecha_desde,fecha_hasta,st.session_state['destino_select'])
         #Despliega el slider de % de reposicion en caso de seleccionar % de reposicion 
            
          
            #convierte a dataframe
            #reemplaza las claves
           #porcentajeTraslado = st.slider("Seleccione el porcentaje de Traslado",min_value=0,max_value=100,value=30)  
           #porcentaje_df = ComponenteTraslado.obtener_rango_traslado(porcentajeTraslado)
        
        if tipoReposicion=='Productos Vendidos':
                    print('calculando A TRASLADAR en base a productos mas vendidos')
                    _callback_arg = {}
                    _callback_proc = CalculadoraTraslado.traslado_enFuncHistVentas #callback para definir la funcion de traslado en base a los productos más vendidos
                    _callback_arg = {  #parámetros necesarios
                            "origen" : almOrigen, #nombre del almacén origen
                            "destino" : almDestino, #nombre del almacén destino
                       #     "ventas" : vendidos_df, #data frame de los productos más vendidos
                            "porcentajeTraslado": porcentajeTraslado #porcentaje de existencia actual, en caso que el almacen origen esté por debajo de ese porcentaje
                        }
        else:
                    print('calculando A TRASLADAR en base al porcentaje de reposicion')    
                    _callback_arg = {}
                    _callback_proc = CalculadoraTraslado.traslado_enFuncPorcReposicion #callback para definir la funcion de traslado en base al % de reposición
                    _callback_arg = { #parámetros necesarios
                            "origen" : almOrigen, #nombre del almacén origen
                            "destino" : almDestino, #nombre del almacén destino
                            "porcentaje_VA" : porcentaje_df["PORCENTAJE_VALENCIA"].iloc[0], #porcentaje de existencia actual aceptable en Valencia(por ejemplo 70%)
                            "porcentaje_BM" : porcentaje_df["PORCENTAJE_BELLO_MONTE"].iloc[0] #porcentaje de existencia actual aceptable en Bello Monte(por ejemplo 30%)
                        }
        #realiza la funcion de calculo de traslacion de inventario y actualiza estos resultados
        # en la columna de Cantidad_Trasladar en el visor de existencia actual de productos por depósito  
        # 

        if st.session_state['recalcular'] == False:  
            existencias_df = ComponenteTraslado.existencia_actual_productos(fecha_desde,fecha_hasta,st.session_state['destino_select'])
           # existTabla_df = ComponenteTraslado.calcular(existencias_df,_callback_proc,_callback_arg)
            existTabla_df = existencias_df
        else:
            existTabla_df = st.session_state['df_existTabla']

        if tipoReposicion!=st.session_state['tipo']:
             st.session_state['recalcular'] =True 
             st.session_state['tipo']=tipoReposicion
        
      

        existTabla_df = existTabla_df[['CATEGORIA','CODIGO PRODUCTO','NOMBRE PRODUCTO','EXISTENCIA VA','EXISTENCIA BM','EXISTENCIA TOTAL','PESO','A TRASLADAR']]
        

        #agregar columna 'PESO(Kg.)'
        
        existTabla_df['PESO(Kg.)'] = existTabla_df['PESO'] * existTabla_df['A TRASLADAR'] 
        #solo filtra en existTabla todos los que tienen A TRASLADAR mayor que 0
        existTabla_df = existTabla_df[existTabla_df['A TRASLADAR'] > 0]

         # Inicializar el DataFrame en session_state si no existe
        st.session_state['df_existTabla'] = TablaDatosControlador.crearTablaExistencia(existTabla_df,'Existencia actual productos por deposito','Greens',filtrador="codigo_producto_existente",nombre_key="existencias") 
        
        TablaDatosControlador.exportarODB(st.session_state['df_existTabla'],arg={
                        "origen":almOrigen,
                        "destino":almDestino,
                        "porcentaje": porcentajeTraslado/100
                })        

Thank you in advance