import streamlit as st import pandas as pd import gspread from google.oauth2.service_account import Credentials import os import json # ========================================== # 1. PARTE SUPERIOR: LÓGICA DEL LOGIN # ========================================== # Configuramos la memoria if 'usuario_autenticado' not in st.session_state: st.session_state['usuario_autenticado'] = False # Función para verificar las credenciales def verificar_login(usuario, contrasena): usuario_correcto = os.environ.get("USUARIO") contrasena_correcta = os.environ.get("PASSWORD") if usuario == usuario_correcto and contrasena == contrasena_correcta: st.session_state['usuario_autenticado'] = True st.success("¡Inicio de sesión exitoso!") else: st.error("Usuario o contraseña incorrectos.") # ========================================== # 2. SISTEMA DE RUTAS (MOSTRAR LOGIN O TU APP) # ========================================== # Si el usuario NO está autenticado, mostramos el formulario if not st.session_state['usuario_autenticado']: st.title("🔒 Iniciar Sesión") usuario_input = st.text_input("Nombre de Usuario") contrasena_input = st.text_input("Contraseña", type="password") if st.button("Entrar"): verificar_login(usuario_input, contrasena_input) # Si el usuario SÍ está autenticado, mostramos TU APLICACIÓN else: # Botón opcional para cerrar sesión en la parte superior if st.button("Cerrar Sesión"): st.session_state['usuario_autenticado'] = False st.rerun() st.divider() st.title("💱 Gestor de Compra/Venta de Divisas") # --- 2. CONEXIÓN A GOOGLE SHEETS (VÍA SECRETO DE HUGGING FACE) --- @st.cache_resource def init_connection(): scopes = [ "https://www.googleapis.com/auth/spreadsheets", ] try: # 1. Traemos el texto desde los Secretos de Hugging Face secreto_hf = os.environ.get("GCP_CREDENTIALS") if secreto_hf is None: st.error("No se encontró el secreto 'GCP_CREDENTIALS'.") st.stop() # 2. Convertimos ese texto en un diccionario de Python credenciales_dict = json.loads(secreto_hf) # 3. ¡IMPORTANTE! Usamos info en lugar de file credentials = Credentials.from_service_account_info( credenciales_dict, scopes=scopes ) client = gspread.authorize(credentials) return client except Exception as e: st.error(f"Error de conexión: {e}") st.stop() # Conectamos con Google Sheets gc = init_connection() # 1. Pega aquí tu ID largo (el que está en la URL) ID_DEL_DOCUMENTO = "1vYsYvo4LJERddrXGi4y-ugp-6vVM4xOo-D_ZPaxuMdI" # 2. Escribe aquí el nombre exacto de la pestaña NOMBRE_DE_LA_HOJA = "Dol" try: documento_completo = gc.open_by_key(ID_DEL_DOCUMENTO) sheet = documento_completo.worksheet(NOMBRE_DE_LA_HOJA) except gspread.exceptions.WorksheetNotFound: st.error(f"Se encontró el documento, pero no existe una pestaña llamada '{NOMBRE_DE_LA_HOJA}'.") st.stop() except Exception as e: st.error(f"No se pudo abrir el documento. Verifica el ID y los permisos. Error: {e}") st.stop() # --- 3. INICIALIZAR VARIABLES DE SESIÓN --- if 'compras' not in st.session_state: st.session_state.compras = [] if 'ventas' not in st.session_state: st.session_state.ventas = [] # --- 4. INTERFAZ DE ENTRADA DE DATOS --- col1, col2 = st.columns(2) with col1: st.header("🛒 Compras ($)") with st.form("form_compra", clear_on_submit=True): tasa_c = st.number_input("Tasa Compra", min_value=0.0, format="%.2f") monto_c = st.number_input("Monto ($)", min_value=0.0, format="%.2f") submit_c = st.form_submit_button("Agregar Compra") if submit_c and tasa_c > 0 and monto_c > 0: total_bs_c = tasa_c * monto_c st.session_state.compras.append({ "Tipo": "Compra", "Tasa": tasa_c, "Monto ($)": monto_c, "Total (Bs)": total_bs_c }) st.success("¡Compra agregada temporalmente!") with col2: st.header("💸 Ventas (USDT)") with st.form("form_venta", clear_on_submit=True): tasa_v = st.number_input("Tasa Venta", min_value=0.0, format="%.4f") monto_v = st.number_input("Monto (USDT)", min_value=0.0, format="%.2f") submit_v = st.form_submit_button("Agregar Venta") if submit_v and tasa_v > 0 and monto_v > 0: total_bs_v = tasa_v * monto_v st.session_state.ventas.append({ "Tipo": "Venta", "Tasa": tasa_v, "Monto (USDT)": monto_v, "Total (Bs)": total_bs_v }) st.success("¡Venta agregada temporalmente!") # --- 5. VISUALIZACIÓN TEMPORAL --- st.divider() df_compras = pd.DataFrame(st.session_state.compras) df_ventas = pd.DataFrame(st.session_state.ventas) col_tabla1, col_tabla2 = st.columns(2) with col_tabla1: st.subheader("Registros de Compra (Sin guardar)") st.dataframe(df_compras, use_container_width=True) with col_tabla2: st.subheader("Registros de Venta (Sin guardar)") st.dataframe(df_ventas, use_container_width=True) # --- 6. PUSH A GOOGLE SHEETS --- if st.button("🚀 Hacer Push a Google Sheets", use_container_width=True): try: datos_a_subir = [] for row in st.session_state.compras: datos_a_subir.append(["Compra", row["Tasa"], row["Monto ($)"], row["Total (Bs)"]]) for row in st.session_state.ventas: datos_a_subir.append(["Venta", row["Tasa"], row["Monto (USDT)"], row["Total (Bs)"]]) if datos_a_subir: sheet.append_rows(datos_a_subir) st.success("¡Datos enviados exitosamente!") st.session_state.compras = [] st.session_state.ventas = [] st.rerun() else: st.warning("No hay datos nuevos para subir.") except Exception as e: st.error(f"Error al subir a Google Sheets: {e}") # --- 7. LECTURA DE GOOGLE SHEETS Y CÁLCULO HISTÓRICO --- st.divider() st.header("📈 Resumen Histórico y Ganancias") try: datos_historicos = sheet.get_all_values() if len(datos_historicos) > 0: df_hist = pd.DataFrame(datos_historicos, columns=["Tipo", "Tasa", "Monto", "Total (Bs)"]) df_hist["Tasa"] = pd.to_numeric(df_hist["Tasa"], errors='coerce') df_hist["Monto"] = pd.to_numeric(df_hist["Monto"], errors='coerce') df_hist["Total (Bs)"] = pd.to_numeric(df_hist["Total (Bs)"], errors='coerce') df_hist = df_hist.dropna(subset=['Total (Bs)']) hist_compras = df_hist[df_hist["Tipo"].str.contains("Compra", case=False, na=False)] hist_ventas = df_hist[df_hist["Tipo"].str.contains("Venta", case=False, na=False)] h_total_bs_comprados = hist_compras["Total (Bs)"].sum() h_total_usd_comprados = hist_compras["Monto"].sum() h_tasa_prom_compra = h_total_bs_comprados / h_total_usd_comprados if h_total_usd_comprados > 0 else 0 h_total_bs_vendidos = hist_ventas["Total (Bs)"].sum() h_total_usdt_vendidos = hist_ventas["Monto"].sum() h_tasa_prom_venta = h_total_bs_vendidos / h_total_usdt_vendidos if h_total_usdt_vendidos > 0 else 0 h_diferencia_bs = h_total_bs_vendidos - h_total_bs_comprados diferencia_divisas = h_total_usd_comprados - h_total_usdt_vendidos valor_bs_sobrantes = h_diferencia_bs / h_tasa_prom_venta if h_tasa_prom_venta > 0 else 0 ganancia_estimada_usd = diferencia_divisas + valor_bs_sobrantes col_m1, col_m2, col_m3, col_m4 = st.columns(4) col_m1.metric("Total Invertido ($)", f"{h_total_usd_comprados:,.2f}") col_m2.metric("Total Vendido (USDT)", f"{h_total_usdt_vendidos:,.2f}") col_m3.metric("Diferencia (Bs)", f"{h_diferencia_bs:,.2f}") col_m4.metric("Ganancia Neta Estimada (USD)", f"{ganancia_estimada_usd:,.2f}") st.write(f"**Tasa Promedio Compra:** {h_tasa_prom_compra:,.2f} Bs | **Tasa Promedio Venta:** {h_tasa_prom_venta:,.2f} Bs") else: st.info("La hoja de cálculo está vacía o aún no tiene registros válidos.") except Exception as e: st.error(f"Error al leer la hoja histórica: {e}")