CombinacoesAuto / app.py
Arthur-Attili's picture
Update app.py
858f2ba verified
Raw
History Blame Contribute Delete
12 kB
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()