Update app.py
Browse files
app.py
CHANGED
|
@@ -5,7 +5,7 @@ import itertools
|
|
| 5 |
from collections import defaultdict
|
| 6 |
import gradio as gr
|
| 7 |
|
| 8 |
-
# 🔧 CONFIGURAÇÕES GLOBAIS
|
| 9 |
FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X"
|
| 10 |
URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
|
| 11 |
|
|
@@ -18,22 +18,27 @@ def formatar_moeda(valor):
|
|
| 18 |
return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
|
| 19 |
|
| 20 |
def carregar_planilha():
|
|
|
|
| 21 |
global df_auto, grupos_auto, planilha_carregada
|
| 22 |
-
if planilha_carregada:
|
|
|
|
| 23 |
try:
|
| 24 |
response = requests.get(URL)
|
| 25 |
response.raise_for_status()
|
| 26 |
df = pd.read_excel(io.BytesIO(response.content))
|
|
|
|
|
|
|
| 27 |
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
|
| 28 |
|
| 29 |
-
|
| 30 |
-
for col in
|
| 31 |
if col in df.columns:
|
| 32 |
df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
|
| 33 |
|
| 34 |
-
#
|
| 35 |
df_auto = df[df['tipo_do_bem'].str.lower().str.contains('auto', na=False)].copy()
|
| 36 |
|
|
|
|
| 37 |
grupos_auto = defaultdict(list)
|
| 38 |
for _, row in df_auto.iterrows():
|
| 39 |
grupos_auto[row['administradora']].append(row.to_dict())
|
|
@@ -41,83 +46,85 @@ def carregar_planilha():
|
|
| 41 |
planilha_carregada = True
|
| 42 |
return True
|
| 43 |
except Exception as e:
|
| 44 |
-
print(f"Erro: {e}")
|
| 45 |
return False
|
| 46 |
|
| 47 |
def buscar_auto(credito_desejado, entrada_desejada, parcela_desejada, adms_selecionadas):
|
| 48 |
-
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
# Filtro de
|
| 51 |
-
if adms_selecionadas
|
| 52 |
-
grupos = {k: v for k, v in grupos_auto.items() if k in adms_selecionadas}
|
| 53 |
-
else:
|
| 54 |
-
grupos = grupos_auto
|
| 55 |
|
| 56 |
-
if not grupos:
|
|
|
|
| 57 |
|
| 58 |
-
# Tolerâncias (Baseadas na lógica do seu código de imóvel)
|
| 59 |
-
tol = 0.25
|
| 60 |
resultados = []
|
|
|
|
|
|
|
| 61 |
|
| 62 |
for adm, cotas in grupos.items():
|
| 63 |
-
#
|
| 64 |
for r in range(1, min(4, len(cotas) + 1)):
|
| 65 |
for combo in itertools.combinations(cotas, r):
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
| 69 |
|
| 70 |
-
if (credito_desejado*(1-tol) <=
|
| 71 |
-
|
| 72 |
-
|
| 73 |
|
| 74 |
-
dif = abs(
|
| 75 |
resultados.append((adm, combo, dif))
|
| 76 |
-
|
|
|
|
| 77 |
|
| 78 |
if not resultados:
|
| 79 |
-
return "🚫 Nenhuma combinação
|
| 80 |
|
|
|
|
| 81 |
resultados.sort(key=lambda x: x[2])
|
| 82 |
|
| 83 |
-
# Formatação de
|
| 84 |
-
|
| 85 |
-
for idx, (adm, combo, _) in enumerate(resultados[:
|
| 86 |
-
codigos = [str(c.get('codigo',
|
| 87 |
t_cred = sum(c['valor_credito'] for c in combo)
|
| 88 |
t_ent = sum(c['valor_entrada'] for c in combo) + (t_cred * 0.085)
|
| 89 |
t_parc = sum(c['valor_parcela'] for c in combo)
|
| 90 |
prazo = max(int(c['qtd_parcelas']) for c in combo)
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
"""
|
| 102 |
-
return resposta
|
| 103 |
|
| 104 |
-
# --- INTERFACE
|
| 105 |
carregar_planilha()
|
| 106 |
opcoes_adm = sorted(list(grupos_auto.keys())) if grupos_auto else []
|
| 107 |
|
| 108 |
-
with gr.Blocks(title="Buscador Auto") as demo:
|
| 109 |
-
gr.Markdown("# 🚗 Busca de
|
| 110 |
|
| 111 |
with gr.Row():
|
| 112 |
-
cred_in = gr.Number(label="Crédito Desejado (R$)", value=
|
| 113 |
-
ent_in = gr.Number(label="Entrada
|
| 114 |
-
parc_in = gr.Number(label="Parcela Máxima (R$)", value=
|
|
|
|
|
|
|
| 115 |
|
| 116 |
-
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
out_text = gr.Textbox(label="Resultados", lines=20, interactive=False)
|
| 121 |
|
| 122 |
btn.click(
|
| 123 |
fn=buscar_auto,
|
|
@@ -126,5 +133,5 @@ with gr.Blocks(title="Buscador Auto") as demo:
|
|
| 126 |
)
|
| 127 |
|
| 128 |
if __name__ == "__main__":
|
| 129 |
-
#
|
| 130 |
-
demo.launch(
|
|
|
|
| 5 |
from collections import defaultdict
|
| 6 |
import gradio as gr
|
| 7 |
|
| 8 |
+
# 🔧 CONFIGURAÇÕES GLOBAIS - Planilha de AUTO
|
| 9 |
FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X"
|
| 10 |
URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
|
| 11 |
|
|
|
|
| 18 |
return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
|
| 19 |
|
| 20 |
def carregar_planilha():
|
| 21 |
+
"""Carrega e prepara a planilha de Automóveis."""
|
| 22 |
global df_auto, grupos_auto, planilha_carregada
|
| 23 |
+
if planilha_carregada:
|
| 24 |
+
return True
|
| 25 |
try:
|
| 26 |
response = requests.get(URL)
|
| 27 |
response.raise_for_status()
|
| 28 |
df = pd.read_excel(io.BytesIO(response.content))
|
| 29 |
+
|
| 30 |
+
# Padronização de colunas
|
| 31 |
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
|
| 32 |
|
| 33 |
+
colunas_num = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
|
| 34 |
+
for col in colunas_num:
|
| 35 |
if col in df.columns:
|
| 36 |
df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
|
| 37 |
|
| 38 |
+
# Filtra apenas o que for AUTO (conforme sua planilha)
|
| 39 |
df_auto = df[df['tipo_do_bem'].str.lower().str.contains('auto', na=False)].copy()
|
| 40 |
|
| 41 |
+
# Agrupamento por Administradora
|
| 42 |
grupos_auto = defaultdict(list)
|
| 43 |
for _, row in df_auto.iterrows():
|
| 44 |
grupos_auto[row['administradora']].append(row.to_dict())
|
|
|
|
| 46 |
planilha_carregada = True
|
| 47 |
return True
|
| 48 |
except Exception as e:
|
| 49 |
+
print(f"❌ Erro ao carregar: {e}")
|
| 50 |
return False
|
| 51 |
|
| 52 |
def buscar_auto(credito_desejado, entrada_desejada, parcela_desejada, adms_selecionadas):
|
| 53 |
+
"""Lógica de busca otimizada para Automóveis."""
|
| 54 |
+
if not planilha_carregada:
|
| 55 |
+
carregar_planilha()
|
| 56 |
|
| 57 |
+
# Filtro de administradoras
|
| 58 |
+
grupos = {k: v for k, v in grupos_auto.items() if k in adms_selecionadas} if adms_selecionadas else grupos_auto
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
+
if not grupos:
|
| 61 |
+
return "🚫 Nenhuma administradora selecionada ou encontrada."
|
| 62 |
|
|
|
|
|
|
|
| 63 |
resultados = []
|
| 64 |
+
# Tolerância de 25% para facilitar o encontro de combinações
|
| 65 |
+
tol = 0.25
|
| 66 |
|
| 67 |
for adm, cotas in grupos.items():
|
| 68 |
+
# Limita a combinação de até 3 cotas para não travar a memória do servidor
|
| 69 |
for r in range(1, min(4, len(cotas) + 1)):
|
| 70 |
for combo in itertools.combinations(cotas, r):
|
| 71 |
+
t_cred = sum(c['valor_credito'] for c in combo)
|
| 72 |
+
# Cálculo da entrada: Entrada da cota + 8.5% do crédito (comissão)
|
| 73 |
+
t_ent = sum(c['valor_entrada'] for c in combo) + (t_cred * 0.085)
|
| 74 |
+
t_parc = sum(c['valor_parcela'] for c in combo)
|
| 75 |
|
| 76 |
+
if (credito_desejado*(1-tol) <= t_cred <= credito_desejado*(1+tol) and
|
| 77 |
+
t_ent <= entrada_desejada * 1.5 and
|
| 78 |
+
t_parc <= parcela_desejada * 1.5):
|
| 79 |
|
| 80 |
+
dif = abs(t_cred - credito_desejado)
|
| 81 |
resultados.append((adm, combo, dif))
|
| 82 |
+
|
| 83 |
+
if len(resultados) > 30: break
|
| 84 |
|
| 85 |
if not resultados:
|
| 86 |
+
return "🚫 Nenhuma combinação encontrada com esses valores. Tente aumentar a entrada ou a parcela."
|
| 87 |
|
| 88 |
+
# Ordena pela proximidade do crédito desejado
|
| 89 |
resultados.sort(key=lambda x: x[2])
|
| 90 |
|
| 91 |
+
# Formatação da String de Saída (Evita o erro 'unhashable type: dict')
|
| 92 |
+
saida = ""
|
| 93 |
+
for idx, (adm, combo, _) in enumerate(resultados[:15], 1):
|
| 94 |
+
codigos = [str(c.get('codigo', 'N/A')) for c in combo]
|
| 95 |
t_cred = sum(c['valor_credito'] for c in combo)
|
| 96 |
t_ent = sum(c['valor_entrada'] for c in combo) + (t_cred * 0.085)
|
| 97 |
t_parc = sum(c['valor_parcela'] for c in combo)
|
| 98 |
prazo = max(int(c['qtd_parcelas']) for c in combo)
|
| 99 |
|
| 100 |
+
saida += f"--- COMBINAÇÃO {idx} ---\n"
|
| 101 |
+
saida += f"🏢 Administradora: {adm}\n"
|
| 102 |
+
saida += f"🔢 Códigos: {', '.join(codigos)}\n"
|
| 103 |
+
saida += f"💰 Crédito Total: {formatar_moeda(t_cred)}\n"
|
| 104 |
+
saida += f"💵 Entrada (+comissão): {formatar_moeda(t_ent)}\n"
|
| 105 |
+
saida += f"💲 Parcela Total: {formatar_moeda(t_parc)}\n"
|
| 106 |
+
saida += f"⏳ Prazo: {prazo} meses\n\n"
|
| 107 |
+
|
| 108 |
+
return saida
|
|
|
|
|
|
|
| 109 |
|
| 110 |
+
# --- INTERFACE GRADIO ---
|
| 111 |
carregar_planilha()
|
| 112 |
opcoes_adm = sorted(list(grupos_auto.keys())) if grupos_auto else []
|
| 113 |
|
| 114 |
+
with gr.Blocks(title="Buscador Consórcio Auto") as demo:
|
| 115 |
+
gr.Markdown("# 🚗 Busca de Cotas de Automóvel")
|
| 116 |
|
| 117 |
with gr.Row():
|
| 118 |
+
cred_in = gr.Number(label="Crédito Desejado (R$)", value=80000)
|
| 119 |
+
ent_in = gr.Number(label="Entrada Disponível (R$)", value=15000)
|
| 120 |
+
parc_in = gr.Number(label="Parcela Máxima (R$)", value=1200)
|
| 121 |
+
|
| 122 |
+
adm_in = gr.CheckboxGroup(choices=opcoes_adm, label="Selecione as Administradoras", value=opcoes_adm)
|
| 123 |
|
| 124 |
+
btn = gr.Button("🔍 BUSCAR MELHORES OPÇÕES", variant="primary")
|
| 125 |
|
| 126 |
+
# Textbox para exibir o texto formatado (resolvendo o erro de dict)
|
| 127 |
+
out_text = gr.Textbox(label="Resultados Encontrados", lines=25, interactive=False)
|
|
|
|
| 128 |
|
| 129 |
btn.click(
|
| 130 |
fn=buscar_auto,
|
|
|
|
| 133 |
)
|
| 134 |
|
| 135 |
if __name__ == "__main__":
|
| 136 |
+
# server_name="0.0.0.0" é essencial para rodar em servidores (Hugging Face/Docker)
|
| 137 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|