Spaces:
Running
Running
| # 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 = "1Ieb7XCSLAPE8plQD2duVLnsBufjMSqjr" # ID entre /d/ e /edit | |
| 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(""" | |
| <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet"> | |
| <style> | |
| * { | |
| font-family: 'Poppins', sans-serif !important; | |
| } | |
| body { | |
| background-color: #ff8f00 !important; | |
| color: black !important; | |
| } | |
| /* Inputs Numéricos: Crédito, Entrada, Parcela */ | |
| input[type="number"] { | |
| background-color: white !important; | |
| border: 2px solid #000 !important; | |
| border-radius: 12px !important; | |
| color: black !important; | |
| padding: 10px !important; | |
| font-weight: 500; | |
| } | |
| /* Inputs de texto e áreas de texto (como a saída e código) */ | |
| input[type="text"], textarea { | |
| background-color: white !important; | |
| border: 2px solid black !important; | |
| border-radius: 12px !important; | |
| color: black !important; | |
| padding: 10px !important; | |
| } | |
| input::placeholder, textarea::placeholder { | |
| color: #333 !important; | |
| opacity: 0.7; | |
| } | |
| .gr-checkbox label { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-weight: 600; | |
| } | |
| input[type="checkbox"] { | |
| accent-color: black !important; | |
| width: 18px; | |
| height: 18px; | |
| } | |
| .gr-button { | |
| font-weight: bold; | |
| border-radius: 10px !important; | |
| background-color: white !important; | |
| color: black !important; | |
| border: 2px solid black !important; | |
| } | |
| .gr-button:hover { | |
| background-color: #fff3e0 !important; | |
| color: black !important; | |
| } | |
| label { | |
| font-weight: 600 !important; | |
| } | |
| </style> | |
| """) | |
| 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 |