# app.py import pandas as pd import requests import io import itertools from collections import defaultdict import gradio as gr # Importação principal para Gradio import os # Para variáveis de ambiente, se necessário # 🔧 CONFIGURAÇÕES GLOBAIS # O ID da planilha para Consórcio de Automóveis # Certifique-se de que este FILE_ID esteja acessível publicamente ou para "Qualquer pessoa com o link" FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X" URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}" # Variável global para armazenar o DataFrame df = None def carregar_planilha(): """Carrega e prepara a planilha para análise de consórcios de automóveis.""" global df try: print("⏳ Carregando planilha...") response = requests.get(URL) response.raise_for_status() data = io.BytesIO(response.content) df = pd.read_excel(data) # Padroniza nomes das colunas df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") # Converte colunas numéricas numeric_cols = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas'] for col in numeric_cols: if col in df.columns: df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0) # Verifica colunas essenciais if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns: raise ValueError("Colunas essenciais não encontradas na planilha") # Remove duplicatas df.drop_duplicates(inplace=True) print("✅ Planilha carregada com sucesso!") return True except Exception as e: print(f"❌ Erro ao carregar planilha: {str(e)}") # Retorne uma mensagem de erro mais útil para o usuário return False def agrupar_por_administradora(dataframe): """Agrupa cotas por administradora.""" grupos = defaultdict(list) for _, row in dataframe.iterrows(): grupos[row['administradora']].append(row.to_dict()) return grupos def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None): """Busca combinações de cotas para automóveis dentro dos parâmetros especificados.""" # Garante que o DataFrame está carregado antes de cada busca # Isso é útil caso a sessão expire ou o DF seja limpo por algum motivo no servidor do HF. global df if df is None: if not carregar_planilha(): return "❌ Erro: Planilha não carregada. Verifique se o ID do arquivo está correto e público." # Filtra por tipo de bem (Auto) e administradoras selecionadas df_filtrado = df[df['tipo_do_bem'].str.lower() == 'auto'] if administradoras: # Garante que 'administradoras' seja uma lista para o isin() if isinstance(administradoras, tuple): # Gradio SelectMultiple retorna uma tupla administradoras = list(administradoras) df_filtrado = df_filtrado[df_filtrado['administradora'].isin(administradoras)] if df_filtrado.empty: return "🚫 Nenhuma cota de automóvel encontrada com os filtros aplicados." # Define faixas de tolerância credito_min, credito_max = credito_desejado * 0.93, credito_desejado * 1.07 entrada_min, entrada_max = entrada_desejada * 0.93, entrada_desejada * 1.07 parcela_min, parcela_max = parcela_desejada * 0.5, parcela_desejada * 1.5 grupos = agrupar_por_administradora(df_filtrado) resultados = [] for adm, cotas in grupos.items(): for r in range(1, min(3, len(cotas)) + 1): # Máximo 3 cotas por combinação for combinacao in itertools.combinations(cotas, r): total_credito = sum(c['valor_credito'] for c in combinacao) total_entrada = sum(c['valor_entrada'] for c in combinacao) total_parcelas = sum(c['valor_parcela'] for c in combinacao) if (credito_min <= total_credito <= credito_max and entrada_min <= total_entrada <= entrada_max and parcela_min <= total_parcelas <= parcela_max): total_saldo = sum(c['saldo_devedor'] for c in combinacao) valor_final = round(total_entrada + total_saldo, 2) diferenca = abs(valor_final - total_credito) resultados.append((adm, combinacao, diferenca)) if not resultados: return "Nenhuma combinação adequada encontrada para automóveis com os parâmetros fornecidos." resultados.sort(key=lambda x: x[2]) return formatar_resultados(resultados) def formatar_moeda(valor): return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".") def formatar_resultados(resultados): """Formata os resultados para exibição.""" resposta = "" # Limita a 15 resultados para evitar saídas muito longas for idx, (adm, combinacao, _) in enumerate(resultados[:15], 1): codigos = [str(c.get('codigo', 'N/A')) for c in combinacao] total_credito = sum(c['valor_credito'] for c in combinacao) total_entrada = sum(c['valor_entrada'] for c in combinacao) total_saldo = sum(c['saldo_devedor'] for c in combinacao) total_parcelas = sum(c['valor_parcela'] for c in combinacao) maior_prazo = max(int(c.get('qtd_parcelas', 0)) for c in combinacao) valor_final = round(total_entrada + total_saldo, 2) credito_real = round(total_credito - total_entrada, 2) taxa_total = total_saldo - credito_real taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0 taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0 resposta += f""" --- COMBINAÇÃO {idx} --- 🏢 Administradora: {adm} 🔢 Códigos: {', '.join(codigos)} 💰 Crédito Total: {formatar_moeda(total_credito)} 💵 Entrada (+comissão): {formatar_moeda(total_entrada)} 📉 Saldo Devedor: {formatar_moeda(total_saldo)} 💲 Parcela: {formatar_moeda(total_parcelas)} ⏳ Prazo: {maior_prazo} meses 🔚 Valor Final: {formatar_moeda(valor_final)} 💠 Crédito Líquido: {formatar_moeda(credito_real)} 📊 Taxa Total: {taxa_percentual:.2f}% ({formatar_moeda(taxa_total)}) 🗓️ Taxa Mensal: {taxa_mensal:.2f}% 🗓️ Taxa Anual: {taxa_mensal * 12:.2f}% """ return resposta # --- INICIALIZAÇÃO PARA GRADIO --- # Carrega a planilha UMA VEZ no início do aplicativo # Se der erro, a função de busca tentará carregar novamente. if not carregar_planilha(): print("⚠️ Aviso: Planilha não carregada na inicialização. Tentará carregar na primeira busca.") # Prepara as opções para o seletor de administradoras do Gradio def get_administradoras_options(): if df is not None: adm_list = df[df['tipo_do_bem'].str.lower() == 'auto']['administradora'].unique() return sorted(adm_list.tolist()) # Converter para lista, pois .unique() pode retornar um numpy array return [] # --- Interface Gradio --- with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo: gr.HTML(""" """) gr.Markdown("# 🚗 Sistema de Busca de Combinações de Consórcio (Automóveis)") gr.Markdown("Preencha os campos abaixo para encontrar as melhores combinações de cotas de consórcio de veículo.") with gr.Row(): credito_input = gr.Number(label="Crédito Desejado (R$)", value=30000, step=1000, interactive=True) entrada_input = gr.Number(label="Entrada Desejada (R$)", value=3000, step=100, interactive=True) parcela_input = gr.Number(label="Parcela Desejada (R$)", value=500, step=10, interactive=True) administradoras_options = get_administradoras_options() administradoras_multiselect = gr.CheckboxGroup( label="Filtrar por Administradoras", choices=administradoras_options, value=administradoras_options, # Seleciona todas por padrão interactive=True ) submit_btn = gr.Button("🔍 Buscar Combinações", variant="primary") output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...") # Conecta o botão à função de busca submit_btn.click( fn=buscar_combinacoes, inputs=[credito_input, entrada_input, parcela_input, administradoras_multiselect], outputs=output_text ) gr.Markdown("---") gr.Markdown("Ferramenta desenvolvida para otimizar a busca por consórcios de automóveis.") if __name__ == "__main__": demo.launch() # Você não precisa disso no Colab, mas sim no arquivo que você fará o deploy