Arthur-Attili's picture
Update app.py
131e309 verified
Raw
History Blame Contribute Delete
13.4 kB
# app.py - VERSÃO COM MARGEM ABSOLUTA FIXA
import pandas as pd
import requests
import io
import itertools
from collections import defaultdict
import gradio as gr
# 🔧 CONFIGURAÇÕES GLOBAIS
FILE_ID = "1XvNz7DlWioT64uNdcfBSL8Ui2nRSi9gT"
URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
# Variáveis globais para armazenamento
df = None
df_imovel = None
grupos_imovel = None
planilha_carregada = False
def carregar_planilha():
"""Carrega e prepara a planilha para análise."""
global df, df_imovel, grupos_imovel, planilha_carregada
if planilha_carregada:
return True
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(" ", "_")
# Colunas numéricas
colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
for coluna in colunas_numericas:
if coluna in df.columns:
df[coluna] = pd.to_numeric(df[coluna], 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")
df.drop_duplicates(inplace=True)
# Pré‑processa imóvel
df_imovel = df[df['tipo_do_bem'].str.lower() == 'imovel'].copy()
grupos_imovel = agrupar_por_administradora(df_imovel)
planilha_carregada = True
print("✅ Planilha carregada e pré-processada com sucesso")
print(f"✅ Total de cotas de imóvel: {len(df_imovel)}")
print(f"✅ Administradoras disponíveis: {list(grupos_imovel.keys())}")
return True
except Exception as e:
print(f"❌ Erro ao carregar planilha: {str(e)}")
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 formatar_moeda(valor):
return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
def calcular_tempo_parcelas(combinacao):
"""Calcula o tempo de parcelas baseado nas cotas da combinação."""
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 ao calcular tempo de parcelas: {str(e)}")
return ""
def definir_margens_absolutas(credito_desejado, entrada_desejada, parcela_desejada):
"""Define margens fixas em R$ para crédito, entrada e parcela."""
# Margens em R$ (ex.: 500.000 → 480.000–520.000)
margem_credito = 40_000
margem_entrada = 10_000
margem_parcela = 1_000
credito_min = max(0, credito_desejado - margem_credito)
credito_max = credito_desejado + margem_credito
entrada_min = max(0, entrada_desejada - margem_entrada)
entrada_max = entrada_desejada + margem_entrada
parcela_min = max(0, parcela_desejada - margem_parcela)
parcela_max = parcela_desejada + margem_parcela
return credito_min, credito_max, entrada_min, entrada_max, parcela_min, parcela_max
def filtrar_cotas_por_faixa(cotas, credito_min, credito_max):
"""Filtra cotas que pelo menos têm chance de entrar na faixa de crédito."""
return [c for c in cotas if c['valor_credito'] < credito_max * 2]
def buscar_combinacoes_otimizado(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None, max_combinacoes=15):
"""Busca combinações de cotas dentro de margens fixas absolutas."""
global df_imovel, grupos_imovel
if not planilha_carregada:
if not carregar_planilha():
return "❌ Erro: Planilha não carregada. Por favor, tente novamente."
# Filtra administradoras
if administradoras and len(administradoras) > 0:
grupos_filtrados = {adm: cotas for adm, cotas in grupos_imovel.items() if adm in administradoras}
else:
grupos_filtrados = grupos_imovel
if not grupos_filtrados:
return "🚫 Nenhuma cota encontrada com os filtros aplicados. Tente ajustar os parâmetros."
print(f"🔍 Total de administradoras no filtro: {len(grupos_filtrados)}")
# Usa margens fixas em R$
credito_min, credito_max, entrada_min, entrada_max, parcela_min, parcela_max = definir_margens_absolutas(
credito_desejado, entrada_desejada, parcela_desejada
)
print(f"💰 Valores desejados: Crédito: R$ {credito_desejado:,.2f}, Entrada: R$ {entrada_desejada:,.2f}, Parcela: R$ {parcela_desejada:,.2f}")
print(f"🔍 Margem de crédito: de R$ {credito_min:,.2f} até R$ {credito_max:,.2f}")
# Limites de busca
max_cotas_combinacao = 15
max_combinacoes_por_adm = 4000 # para evitar explosão de combinações [web:12][web:18]
resultados = []
total_combinacoes_testadas = 0
for adm, cotas in grupos_filtrados.items():
print(f"🔍 Analisando administradora: {adm} ({len(cotas)} cotas)")
cotas_filtradas = filtrar_cotas_por_faixa(cotas, credito_min, credito_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_credito = sum(c['valor_credito'] for c in combinacao)
total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
total_entrada = total_entrada_base + (total_credito * 0.085)
total_parcelas = sum(c['valor_parcela'] for c in combinacao)
# Verifica margens fixas
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 len(resultados) >= max_combinacoes * 2:
break
if len(resultados) >= max_combinacoes * 2:
break
if len(resultados) >= max_combinacoes * 2:
break
print(f"🔍 Total de combinações testadas: {total_combinacoes_testadas}")
print(f"🔍 Combinações válidas encontradas: {len(resultados)}")
if not resultados:
return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
resultados.sort(key=lambda x: x[2]) # ordena pela menor diferença
return formatar_resultados(resultados[:max_combinacoes])
def formatar_resultados(resultados):
"""Formata os resultados para exibição incluindo tempo de parcelas."""
if not resultados:
return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
resposta = ""
for idx, (adm, combinacao, _) in enumerate(resultados, 1):
codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
total_credito = sum(c['valor_credito'] for c in combinacao)
total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
total_entrada = total_entrada_base + (total_credito * 0.085)
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
tempo_parcelas = calcular_tempo_parcelas(combinacao)
resposta += f"""
--- COMBINAÇÃO {idx} ---
🏢 Administradora: {adm}
🔢 Códigos: {', '.join(codigos)} ({len(combinacao)} cotas)
💰 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}%
⏰ TEMPO DE PARCELAS:
{tempo_parcelas if tempo_parcelas else "Não foi possível calcular o tempo de parcelas"}
"""
return resposta
# Carrega a planilha uma vez quando o aplicativo inicia
carregar_planilha()
def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
return buscar_combinacoes_otimizado(
float(credito_desejado or 0),
float(entrada_desejada or 0),
float(parcela_desejada or 0),
administradoras,
max_combinacoes=15
)
def get_administradoras_options():
if df is not None and planilha_carregada:
adm_list = df[df['tipo_do_bem'].str.lower() == 'imovel']['administradora'].unique()
return sorted(adm_list)
return []
# Interface Gradio
with gr.Blocks(title="Sistema de Busca de Consórcio Imóvel") 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; }
input[type="number"], input[type="text"], 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;
}
.gr-button:hover { background-color: #fff3e0 !important; }
</style>
""")
gr.Markdown("# 🏠 Busca de Combinações de Consórcio Imóvel")
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=300000, step=1000)
entrada_input = gr.Number(label="Entrada Desejada (R$)", value=30000, step=1000)
parcela_input = gr.Number(label="Parcela Desejada (R$)", value=1500, step=100)
administradoras_multiselect = gr.CheckboxGroup(
label="Filtrar por Administradoras",
choices=get_administradoras_options(),
value=get_administradoras_options()
)
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...")
submit_btn.click(
fn=ajustar_e_buscar,
inputs=[credito_input, entrada_input, parcela_input, administradoras_multiselect],
outputs=output_text
)
gr.Markdown("---")
gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio ideais.")
if __name__ == "__main__":
demo.launch()
#