Spaces:
Running
Running
| import os | |
| os.environ["GRADIO_ANALYTICS_ENABLED"] = "False" | |
| os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1" | |
| import pandas as pd | |
| import requests | |
| import io | |
| import itertools | |
| from collections import defaultdict | |
| import gradio as gr | |
| # 🔥 NOVO: import do middleware CORS do FastAPI (usado internamente pelo Gradio) | |
| from fastapi.middleware.cors import CORSMiddleware | |
| # 🔧 CONFIGURAÇÕES DO ARQUIVO | |
| FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X" | |
| URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}" | |
| # Variáveis globais | |
| df_auto = None | |
| grupos_auto = None | |
| planilha_carregada = False | |
| ADMIN_OPCOES = [] | |
| def carregar_planilha(): | |
| global df_auto, grupos_auto, planilha_carregada, ADMIN_OPCOES | |
| if planilha_carregada: | |
| return True | |
| try: | |
| print("⏳ Carregando planilha de automóveis...") | |
| response = requests.get(URL) | |
| response.raise_for_status() | |
| data = io.BytesIO(response.content) | |
| df = pd.read_excel(data) | |
| df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") | |
| colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas'] | |
| for col in colunas_numericas: | |
| if col in df.columns: | |
| df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0) | |
| if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns: | |
| raise ValueError("Colunas essenciais não encontradas") | |
| df.drop_duplicates(inplace=True) | |
| df_auto = df[df['tipo_do_bem'].str.lower() == 'auto'].copy() | |
| df_auto['administradora'] = df_auto['administradora'].astype(str).str.strip().str.lower() | |
| df_auto = df_auto[df_auto['administradora'].notna() & (df_auto['administradora'] != '')] | |
| grupos_auto = defaultdict(list) | |
| for _, row in df_auto.iterrows(): | |
| grupos_auto[row['administradora']].append(row.to_dict()) | |
| ADMIN_OPCOES = sorted(df_auto['administradora'].unique()) | |
| planilha_carregada = True | |
| print(f"✅ Total de cotas AUTO: {len(df_auto)}") | |
| print(f"✅ Administradoras AUTO: {ADMIN_OPCOES}") | |
| return True | |
| except Exception as e: | |
| print(f"❌ Erro ao carregar planilha AUTO: {e}") | |
| return False | |
| def formatar_moeda(valor): | |
| return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".") | |
| def definir_margens_absolutas(credito, entrada, parcela): | |
| margem_credito = 20_000 # ex.: 30.000 → 10.000–50.000 | |
| margem_entrada = 2_000 | |
| margem_parcela = 100 | |
| cred_min = max(0, credito - margem_credito) | |
| cred_max = credito + margem_credito | |
| ent_min = max(0, entrada - margem_entrada) | |
| ent_max = entrada + margem_entrada | |
| parc_min = max(0, parcela - margem_parcela) | |
| parc_max = parcela + margem_parcela | |
| return cred_min, cred_max, ent_min, ent_max, parc_min, parc_max | |
| def filtrar_cotas_por_faixa(cotas, cred_min, cred_max): | |
| return [c for c in cotas if c['valor_credito'] < cred_max * 2] | |
| def calcular_tempo_parcelas(combinacao): | |
| if not combinacao: | |
| return "" | |
| try: | |
| cotas_ordenadas = sorted(combinacao, key=lambda x: int(x.get('qtd_parcelas', 0))) | |
| prazos = sorted(set(int(c.get('qtd_parcelas', 0)) for c in combinacao)) | |
| if not prazos or prazos[0] <= 0: | |
| return "" | |
| resultado = [] | |
| prazo_anterior = 0 | |
| for prazo_atual in prazos: | |
| cotas_ativas = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) >= prazo_anterior + 1] | |
| valor_parcela_total = sum(c.get('valor_parcela', 0) for c in cotas_ativas) | |
| inicio = prazo_anterior + 1 | |
| fim = prazo_atual | |
| if inicio <= fim and valor_parcela_total > 0: | |
| resultado.append(f"{inicio}x à {fim}x: {formatar_moeda(valor_parcela_total)}/mês") | |
| prazo_anterior = prazo_atual | |
| if prazos and prazo_anterior < max(prazos): | |
| cotas_ativas_finais = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) > prazo_anterior] | |
| if cotas_ativas_finais: | |
| valor_parcela_final = sum(c.get('valor_parcela', 0) for c in cotas_ativas_finais) | |
| inicio_final = prazo_anterior + 1 | |
| fim_final = max(prazos) | |
| if valor_parcela_final > 0: | |
| resultado.append(f"{inicio_final}x à {fim_final}x: {formatar_moeda(valor_parcela_final)}/mês") | |
| return "\\n".join(resultado) if resultado else "" | |
| except Exception as e: | |
| print(f"❌ Erro calcular tempo de parcelas AUTO: {e}") | |
| return "" | |
| def buscar_combinacoes(credito, entrada, parcela, adm_selecionadas): | |
| if not planilha_carregada: | |
| if not carregar_planilha(): | |
| return "❌ Erro: planilha de automóveis não carregada." | |
| # Filtra administradoras | |
| if adm_selecionadas: | |
| grupos = {adm: cotas for adm, cotas in grupos_auto.items() if adm in adm_selecionadas} | |
| else: | |
| grupos = grupos_auto | |
| if not grupos: | |
| return "🚫 Nenhuma cota de automóvel encontrada com os filtros." | |
| # Margens absolutas | |
| cred_min, cred_max, ent_min, ent_max, parc_min, parc_max = definir_margens_absolutas( | |
| float(credito or 0), | |
| float(entrada or 0), | |
| float(parcela or 0) | |
| ) | |
| print(f"💰 AUTO - Crédito: R$ {credito:,.2f} → [{cred_min:,.2f}, {cred_max:,.2f}]") | |
| # Limites de busca | |
| max_cotas_combinacao = 15 | |
| max_combinacoes_por_adm = 4000 | |
| resultados = [] | |
| total_combinacoes_testadas = 0 | |
| for adm, cotas in grupos.items(): | |
| print(f"🔍 AUTO - Administradora: {adm} ({len(cotas)} cotas)") | |
| cotas_filtradas = filtrar_cotas_por_faixa(cotas, cred_min, cred_max) | |
| for r in range(1, min(max_cotas_combinacao + 1, len(cotas_filtradas) + 1)): | |
| combinacoes_geradas = 0 | |
| for combinacao in itertools.combinations(cotas_filtradas, r): | |
| if combinacoes_geradas >= max_combinacoes_por_adm: | |
| break | |
| total_combinacoes_testadas += 1 | |
| combinacoes_geradas += 1 | |
| total_cred = sum(c['valor_credito'] for c in combinacao) | |
| total_ent_base = sum(c['valor_entrada'] for c in combinacao) | |
| total_ent = total_ent_base + total_cred * 0.085 | |
| total_parc = sum(c['valor_parcela'] for c in combinacao) | |
| if (cred_min <= total_cred <= cred_max and | |
| ent_min <= total_ent <= ent_max and | |
| parc_min <= total_parc <= parc_max): | |
| total_saldo = sum(c['saldo_devedor'] for c in combinacao) | |
| valor_final = round(total_ent + total_saldo, 2) | |
| diff = abs(valor_final - total_cred) | |
| resultados.append((adm, combinacao, diff)) | |
| if len(resultados) >= 45: | |
| break | |
| if len(resultados) >= 45: | |
| break | |
| if len(resultados) >= 45: | |
| break | |
| print(f"🔍 AUTO - Total de combinações testadas: {total_combinacoes_testadas}") | |
| if not resultados: | |
| return "🚫 Nenhuma combinação de auto encontrada com os parâmetros fornecidos." | |
| resultados.sort(key=lambda x: x[2]) | |
| return formatar_resultados(resultados[:15]) | |
| def formatar_resultados(resultados): | |
| if not resultados: | |
| return "🚫 Nenhuma combinação de auto adequada encontrada." | |
| resposta = "" | |
| for idx, (adm, comb, _) in enumerate(resultados, 1): | |
| codigos = [str(c.get('codigo', 'N/A')) for c in comb] | |
| total_cred = sum(c['valor_credito'] for c in comb) | |
| total_ent_base = sum(c['valor_entrada'] for c in comb) | |
| total_ent = total_ent_base + total_cred * 0.085 | |
| total_saldo = sum(c['saldo_devedor'] for c in comb) | |
| total_parc = sum(c['valor_parcela'] for c in comb) | |
| maior_prazo = max(int(c.get('qtd_parcelas', 0)) for c in comb) | |
| valor_final = round(total_ent + total_saldo, 2) | |
| cred_real = total_cred - total_ent | |
| taxa_total = total_saldo - cred_real | |
| taxa_perc = (taxa_total / cred_real) * 100 if cred_real > 0 else 0 | |
| taxa_mensal = taxa_perc / maior_prazo if maior_prazo > 0 else 0 | |
| tempo = calcular_tempo_parcelas(comb) | |
| resposta += f""" | |
| --- COMBINAÇÃO {idx} AUTO --- | |
| 🏢 Administradora: {adm} | |
| 🔢 Códigos: {', '.join(codigos)} ({len(comb)} cotas) | |
| 💰 Crédito Total: {formatar_moeda(total_cred)} | |
| 💵 Entrada (+comissão): {formatar_moeda(total_ent)} | |
| 📉 Saldo Devedor: {formatar_moeda(total_saldo)} | |
| 💲 Parcela: {formatar_moeda(total_parc)} | |
| ⏳ Prazo: {maior_prazo} meses | |
| 🔚 Valor Final: {formatar_moeda(valor_final)} | |
| 💠 Crédito Líquido: {formatar_moeda(cred_real)} | |
| 📊 Taxa Total: {taxa_perc:.2f}% ({formatar_moeda(taxa_total)}) | |
| 🗓️ Taxa Mensal: {taxa_mensal:.2f}% | |
| 🗓️ Taxa Anual: {taxa_mensal * 12:.2f}% | |
| ⏰ TEMPO DE PARCELAS: | |
| {tempo if tempo else "Não foi possível calcular"} | |
| """ | |
| return resposta | |
| # 🔁 Carregar a planilha uma vez, no início do programa | |
| carregar_planilha() | |
| # Interface Gradio (exatamente igual ao imóvel, com Blocks) | |
| with gr.Blocks(title="🚗 Sistema de Busca de Consórcio Auto") as demo_auto: | |
| 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; } | |
| input[type="number"], textarea { | |
| background-color: white !important; | |
| border: 2px solid black !important; | |
| border-radius: 12px !important; | |
| color: black !important; | |
| padding: 10px !important; | |
| } | |
| .gr-button { | |
| font-weight: bold; | |
| border-radius: 10px !important; | |
| background-color: white !important; | |
| color: black !important; | |
| border: 2px solid black !important; | |
| } | |
| </style> | |
| """) | |
| gr.Markdown("# 🚗 Busca de Combinações de Consórcio Auto") | |
| gr.Markdown("Insira os valores desejados para crédito, entrada e parcela, e filtre por administradora.") | |
| with gr.Row(): | |
| credito_input = gr.Number(label="Crédito Desejado (R$)", value=30000, step=1000) | |
| entrada_input = gr.Number(label="Entrada Desejada (R$)", value=3000, step=100) | |
| parcela_input = gr.Number(label="Parcela Desejada (R$)", value=500, step=10) | |
| adm_input = gr.Dropdown( | |
| choices=ADMIN_OPCOES, | |
| label="Filtrar por Administradoras", | |
| multiselect=True, | |
| value=ADMIN_OPCOES, | |
| interactive=True | |
| ) | |
| submit_btn = gr.Button("🔍 Buscar Combinações AUTO", variant="primary") | |
| output_text = gr.Textbox( | |
| label="Resultados AUTO", | |
| lines=20, | |
| max_lines=40, | |
| interactive=False, | |
| placeholder="Os resultados aparecerão aqui..." | |
| ) | |
| submit_btn.click( | |
| fn=buscar_combinacoes, | |
| inputs=[credito_input, entrada_input, parcela_input, adm_input], | |
| outputs=output_text | |
| ) | |
| gr.Markdown("---") | |
| gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio automotivo.") | |
| # ============================================================ | |
| # 🔥 CORREÇÃO CORS – ADICIONADO AQUI | |
| # Obtém o app FastAPI interno e adiciona o middleware | |
| # ============================================================ | |
| app = demo_auto.app | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=[ | |
| "https://www.maxconconsorcios.com.br", | |
| "https://maxconconsorcios.com.br", | |
| # Se precisar testar localmente, adicione: | |
| # "http://localhost:8000", | |
| # "http://127.0.0.1:8000" | |
| ], | |
| allow_credentials=True, # ESSENCIAL para aceitar credenciais | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Executar o app | |
| if __name__ == "__main__": | |
| demo_auto.launch() |