Arthur-Attili commited on
Commit
7ecb4f3
·
verified ·
1 Parent(s): 5476757

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +95 -219
app.py CHANGED
@@ -1,12 +1,10 @@
1
- # app.py - VERSÃO COM TEMPO DE PARCELAS
2
  import pandas as pd
3
  import requests
4
  import io
5
  import itertools
6
  from collections import defaultdict
7
  import gradio as gr
8
- import time
9
- import math
10
 
11
  # 🔧 CONFIGURAÇÕES GLOBAIS
12
  FILE_ID = "1XvNz7DlWioT64uNdcfBSL8Ui2nRSi9gT"
@@ -21,11 +19,10 @@ planilha_carregada = False
21
  def carregar_planilha():
22
  """Carrega e prepara a planilha para análise."""
23
  global df, df_imovel, grupos_imovel, planilha_carregada
24
-
25
- # Se já carregou, não precisa carregar novamente
26
  if planilha_carregada:
27
  return True
28
-
29
  try:
30
  print("⏳ Carregando planilha...")
31
  response = requests.get(URL)
@@ -36,7 +33,7 @@ def carregar_planilha():
36
  # Padroniza nomes das colunas
37
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
38
 
39
- # Converte colunas numéricas
40
  colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
41
  for coluna in colunas_numericas:
42
  if coluna in df.columns:
@@ -46,10 +43,9 @@ def carregar_planilha():
46
  if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns:
47
  raise ValueError("Colunas essenciais não encontradas")
48
 
49
- # Remove duplicatas
50
  df.drop_duplicates(inplace=True)
51
-
52
- # Pré-filtra e pré-processa dados de imóvel
53
  df_imovel = df[df['tipo_do_bem'].str.lower() == 'imovel'].copy()
54
  grupos_imovel = agrupar_por_administradora(df_imovel)
55
 
@@ -63,6 +59,7 @@ def carregar_planilha():
63
  print(f"❌ Erro ao carregar planilha: {str(e)}")
64
  return False
65
 
 
66
  def agrupar_por_administradora(dataframe):
67
  """Agrupa cotas por administradora."""
68
  grupos = defaultdict(list)
@@ -70,38 +67,38 @@ def agrupar_por_administradora(dataframe):
70
  grupos[row['administradora']].append(row.to_dict())
71
  return grupos
72
 
 
 
 
 
 
73
  def calcular_tempo_parcelas(combinacao):
74
- """Calcula o tempo de parcelas baseado nas cotas da combinação - VERSÃO CORRIGIDA."""
75
  if not combinacao:
76
  return ""
77
-
78
  try:
79
- # Ordena as cotas pelo prazo (menor prazo primeiro)
80
  cotas_ordenadas = sorted(combinacao, key=lambda x: int(x.get('qtd_parcelas', 0)))
81
-
82
- # Encontra todos os prazos únicos e ordena
83
  prazos = sorted(set(int(c.get('qtd_parcelas', 0)) for c in combinacao))
84
-
85
  if not prazos or prazos[0] <= 0:
86
  return ""
87
-
88
  resultado = []
89
  prazo_anterior = 0
90
-
91
- for i, prazo_atual in enumerate(prazos):
92
- # Calcula a soma das parcelas das cotas que estão ativas neste intervalo
93
  cotas_ativas = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) >= prazo_anterior + 1]
94
  valor_parcela_total = sum(c.get('valor_parcela', 0) for c in cotas_ativas)
95
-
96
  inicio = prazo_anterior + 1
97
  fim = prazo_atual
98
-
99
  if inicio <= fim and valor_parcela_total > 0:
100
  resultado.append(f"{inicio}x à {fim}x: {formatar_moeda(valor_parcela_total)}/mês")
101
-
102
  prazo_anterior = prazo_atual
103
-
104
- # Adiciona faixa final se houver cotas com prazos além do último ponto de quebra
105
  if prazos and prazo_anterior < max(prazos):
106
  cotas_ativas_finais = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) > prazo_anterior]
107
  if cotas_ativas_finais:
@@ -110,170 +107,92 @@ def calcular_tempo_parcelas(combinacao):
110
  fim_final = max(prazos)
111
  if valor_parcela_final > 0:
112
  resultado.append(f"{inicio_final}x à {fim_final}x: {formatar_moeda(valor_parcela_final)}/mês")
113
-
114
- return "\n".join(resultado) if resultado else ""
115
-
116
  except Exception as e:
117
  print(f"❌ Erro ao calcular tempo de parcelas: {str(e)}")
118
  return ""
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  def buscar_combinacoes_otimizado(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None, max_combinacoes=15):
121
- """Busca combinações de cotas dentro dos parâmetros especificados de forma otimizada."""
122
  global df_imovel, grupos_imovel
123
-
124
- # Garante que o DataFrame está carregado
125
  if not planilha_carregada:
126
  if not carregar_planilha():
127
  return "❌ Erro: Planilha não carregada. Por favor, tente novamente."
128
 
129
- # Filtra administradoras se especificado
130
  if administradoras and len(administradoras) > 0:
131
  grupos_filtrados = {adm: cotas for adm, cotas in grupos_imovel.items() if adm in administradoras}
132
- print(f"🔍 Filtrando por administradoras: {administradoras}")
133
  else:
134
  grupos_filtrados = grupos_imovel
135
- print("🔍 Buscando em todas as administradoras")
136
 
137
  if not grupos_filtrados:
138
  return "🚫 Nenhuma cota encontrada com os filtros aplicados. Tente ajustar os parâmetros."
139
 
140
  print(f"🔍 Total de administradoras no filtro: {len(grupos_filtrados)}")
141
-
142
- # TOLERÂNCIAS - Ajustadas para valores altos
143
- if credito_desejado > 10000000:
144
- tolerancia_credito = 0.50
145
- tolerancia_entrada = 0.50
146
- tolerancia_parcela = 1.00
147
- max_cotas_combinacao = 25
148
- else:
149
- tolerancia_credito = 0.25
150
- tolerancia_entrada = 0.25
151
- tolerancia_parcela = 0.50
152
- max_cotas_combinacao = 15
153
-
154
- # Define faixas de tolerância
155
- credito_min = credito_desejado * (1 - tolerancia_credito)
156
- credito_max = credito_desejado * (1 + tolerancia_credito)
157
- entrada_min = entrada_desejada * (1 - tolerancia_entrada)
158
- entrada_max = entrada_desejada * (1 + tolerancia_entrada)
159
- parcela_min = parcela_desejada * (1 - tolerancia_parcela)
160
- parcela_max = parcela_desejada * (1 + tolerancia_parcela)
161
 
162
  print(f"💰 Valores desejados: Crédito: R$ {credito_desejado:,.2f}, Entrada: R$ {entrada_desejada:,.2f}, Parcela: R$ {parcela_desejada:,.2f}")
 
 
 
 
 
163
 
164
  resultados = []
165
  total_combinacoes_testadas = 0
166
-
167
- # Para cada administradora, busca combinações
168
  for adm, cotas in grupos_filtrados.items():
169
  print(f"🔍 Analisando administradora: {adm} ({len(cotas)} cotas)")
170
-
171
- cotas_para_combinar = cotas
172
-
173
- # Gera combinações
174
- for r in range(1, min(max_cotas_combinacao + 1, len(cotas_para_combinar) + 1)):
175
- if credito_desejado > 10000000:
176
- max_combinacoes_por_adm = 7000
177
- else:
178
- max_combinacoes_por_adm = 4000
179
- combinacoes_geradas = 0
180
-
181
- for combinacao in itertools.combinations(cotas_para_combinar, r):
182
- if combinacoes_geradas >= max_combinacoes_por_adm:
183
- break
184
-
185
- total_combinacoes_testadas += 1
186
- combinacoes_geradas += 1
187
-
188
- total_credito = sum(c['valor_credito'] for c in combinacao)
189
- total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
190
- total_entrada = total_entrada_base + (total_credito * 0.085)
191
- total_parcelas = sum(c['valor_parcela'] for c in combinacao)
192
 
193
- # Verifica se a combinação está dentro das tolerâncias
194
- if (credito_min <= total_credito <= credito_max and
195
- entrada_min <= total_entrada <= entrada_max and
196
- parcela_min <= total_parcelas <= parcela_max):
197
 
198
- total_saldo = sum(c['saldo_devedor'] for c in combinacao)
199
- valor_final = round(total_entrada + total_saldo, 2)
200
- diferenca = abs(valor_final - total_credito)
201
-
202
- resultados.append((adm, combinacao, diferenca))
203
-
204
- if len(resultados) >= max_combinacoes * 3:
205
- break
206
-
207
- if len(resultados) >= max_combinacoes * 3:
208
- break
209
-
210
- if len(resultados) >= max_combinacoes * 3:
211
- break
212
-
213
- print(f"🔍 Total de combinações testadas: {total_combinacoes_testadas}")
214
- print(f"🔍 Combinações válidas encontradas: {len(resultados)}")
215
-
216
- if not resultados:
217
- # Tenta uma busca mais ampla se não encontrou resultados
218
- print("🔍 Nenhuma combinação encontrada. Tentando busca ampla...")
219
- return buscar_combinacoes_amplo(credito_desejado, entrada_desejada, parcela_desejada, administradoras, max_combinacoes)
220
-
221
- # Ordena pela diferença e pega as melhores
222
- resultados.sort(key=lambda x: x[2])
223
- return formatar_resultados(resultados[:max_combinacoes])
224
-
225
- def buscar_combinacoes_amplo(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None, max_combinacoes=15):
226
- """Busca combinações com tolerâncias mais amplas para valores altos."""
227
- global grupos_imovel
228
-
229
- # Filtra administradoras se especificado
230
- if administradoras and len(administradoras) > 0:
231
- grupos_filtrados = {adm: cotas for adm, cotas in grupos_imovel.items() if adm in administradoras}
232
- else:
233
- grupos_filtrados = grupos_imovel
234
-
235
- # Tolerâncias mais amplas para valores altos
236
- if credito_desejado > 10000000:
237
- tolerancia_credito = 0.50
238
- tolerancia_entrada = 0.50
239
- tolerancia_parcela = 1.00
240
- max_cotas_combinacao = 25
241
- else:
242
- tolerancia_credito = 0.25
243
- tolerancia_entrada = 0.25
244
- tolerancia_parcela = 0.50
245
- max_cotas_combinacao = 15
246
-
247
- credito_min = credito_desejado * (1 - tolerancia_credito)
248
- credito_max = credito_desejado * (1 + tolerancia_credito)
249
- entrada_min = entrada_desejada * (1 - tolerancia_entrada)
250
- entrada_max = entrada_desejada * (1 + tolerancia_entrada)
251
- parcela_min = parcela_desejada * (1 - tolerancia_parcela)
252
- parcela_max = parcela_desejada * (1 + tolerancia_parcela)
253
-
254
- resultados = []
255
- total_combinacoes_testadas = 0
256
-
257
- for adm, cotas in grupos_filtrados.items():
258
- print(f"🔍 BUSCA AMPLA - Analisando administradora: {adm}")
259
-
260
- # Gera combinações
261
- for r in range(1, min(max_cotas_combinacao + 1, len(cotas) + 1)):
262
  combinacoes_geradas = 0
263
- max_combinacoes_por_adm = 7000
264
-
265
- for combinacao in itertools.combinations(cotas, r):
266
  if combinacoes_geradas >= max_combinacoes_por_adm:
267
  break
268
-
269
  total_combinacoes_testadas += 1
270
  combinacoes_geradas += 1
271
-
272
  total_credito = sum(c['valor_credito'] for c in combinacao)
273
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
274
  total_entrada = total_entrada_base + (total_credito * 0.085)
275
  total_parcelas = sum(c['valor_parcela'] for c in combinacao)
276
 
 
277
  if (credito_min <= total_credito <= credito_max and
278
  entrada_min <= total_entrada <= entrada_max and
279
  parcela_min <= total_parcelas <= parcela_max):
@@ -283,32 +202,31 @@ def buscar_combinacoes_amplo(credito_desejado, entrada_desejada, parcela_desejad
283
  diferenca = abs(valor_final - total_credito)
284
 
285
  resultados.append((adm, combinacao, diferenca))
286
-
287
  if len(resultados) >= max_combinacoes * 2:
288
  break
289
-
290
  if len(resultados) >= max_combinacoes * 2:
291
  break
292
-
293
  if len(resultados) >= max_combinacoes * 2:
294
  break
295
 
296
- print(f"🔍 BUSCA AMPLA: Combinações válidas encontradas: {len(resultados)}")
 
297
 
298
  if not resultados:
299
  return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
300
 
301
- resultados.sort(key=lambda x: x[2])
302
  return formatar_resultados(resultados[:max_combinacoes])
303
 
304
- def formatar_moeda(valor):
305
- return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
306
 
307
  def formatar_resultados(resultados):
308
- """Formata os resultados para exibição INCLUINDO TEMPO DE PARCELAS CORRETO."""
309
  if not resultados:
310
  return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
311
-
312
  resposta = ""
313
  for idx, (adm, combinacao, _) in enumerate(resultados, 1):
314
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
@@ -324,8 +242,7 @@ def formatar_resultados(resultados):
324
  taxa_total = total_saldo - credito_real
325
  taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0
326
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
327
-
328
- # CHAMADA CORRIGIDA para calcular tempo de parcelas
329
  tempo_parcelas = calcular_tempo_parcelas(combinacao)
330
 
331
  resposta += f"""
@@ -347,72 +264,42 @@ def formatar_resultados(resultados):
347
  """
348
  return resposta
349
 
 
350
  # Carrega a planilha uma vez quando o aplicativo inicia
351
  carregar_planilha()
352
 
 
353
  def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
354
  return buscar_combinacoes_otimizado(
355
- credito_desejado, entrada_desejada, parcela_desejada, administradoras, max_combinacoes=15
 
 
 
 
356
  )
357
 
358
- # Prepara a lista de administradoras para o Gradio
359
  def get_administradoras_options():
360
  if df is not None and planilha_carregada:
361
  adm_list = df[df['tipo_do_bem'].str.lower() == 'imovel']['administradora'].unique()
362
  return sorted(adm_list)
363
  return []
364
 
365
- # Criando a interface Gradio (mantenha igual)
 
366
  with gr.Blocks(title="Sistema de Busca de Consórcio Imóvel") as demo:
367
  gr.HTML("""
368
  <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
369
  <style>
370
- * {
371
- font-family: 'Poppins', sans-serif !important;
372
- }
373
-
374
- body {
375
- background-color: #ff8f00 !important;
376
- color: black !important;
377
- }
378
-
379
- /* Inputs Numéricos: Crédito, Entrada, Parcela */
380
- input[type="number"] {
381
- background-color: white !important;
382
- border: 2px solid #000 !important;
383
- border-radius: 12px !important;
384
- color: black !important;
385
- padding: 10px !important;
386
- font-weight: 500;
387
- }
388
-
389
- /* Inputs de texto e áreas de texto (como a saída e código) */
390
- input[type="text"], textarea {
391
  background-color: white !important;
392
  border: 2px solid black !important;
393
  border-radius: 12px !important;
394
  color: black !important;
395
  padding: 10px !important;
396
  }
397
-
398
- input::placeholder, textarea::placeholder {
399
- color: #333 !important;
400
- opacity: 0.7;
401
- }
402
-
403
- .gr-checkbox label {
404
- display: flex;
405
- align-items: center;
406
- gap: 8px;
407
- font-weight: 600;
408
- }
409
-
410
- input[type="checkbox"] {
411
- accent-color: black !important;
412
- width: 18px;
413
- height: 18px;
414
- }
415
-
416
  .gr-button {
417
  font-weight: bold;
418
  border-radius: 10px !important;
@@ -420,19 +307,10 @@ with gr.Blocks(title="Sistema de Busca de Consórcio Imóvel") as demo:
420
  color: black !important;
421
  border: 2px solid black !important;
422
  }
423
-
424
- .gr-button:hover {
425
- background-color: #fff3e0 !important;
426
- color: black !important;
427
- }
428
-
429
- label {
430
- font-weight: 600 !important;
431
- }
432
  </style>
433
  """)
434
 
435
-
436
  gr.Markdown("# 🏠 Busca de Combinações de Consórcio Imóvel")
437
  gr.Markdown("Insira os valores desejados para crédito, entrada e parcela, e filtre por administradora.")
438
 
@@ -444,11 +322,10 @@ with gr.Blocks(title="Sistema de Busca de Consórcio Imóvel") as demo:
444
  administradoras_multiselect = gr.CheckboxGroup(
445
  label="Filtrar por Administradoras",
446
  choices=get_administradoras_options(),
447
- value=get_administradoras_options() # Seleciona todas por padrão
448
  )
449
 
450
  submit_btn = gr.Button("🔍 Buscar Combinações", variant="primary")
451
-
452
  output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...")
453
 
454
  submit_btn.click(
@@ -460,6 +337,5 @@ with gr.Blocks(title="Sistema de Busca de Consórcio Imóvel") as demo:
460
  gr.Markdown("---")
461
  gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio ideais.")
462
 
463
- # Inicia o aplicativo Gradio
464
  if __name__ == "__main__":
465
- demo.launch()
 
1
+ # app.py - VERSÃO COM MARGEM ABSOLUTA FIXA
2
  import pandas as pd
3
  import requests
4
  import io
5
  import itertools
6
  from collections import defaultdict
7
  import gradio as gr
 
 
8
 
9
  # 🔧 CONFIGURAÇÕES GLOBAIS
10
  FILE_ID = "1XvNz7DlWioT64uNdcfBSL8Ui2nRSi9gT"
 
19
  def carregar_planilha():
20
  """Carrega e prepara a planilha para análise."""
21
  global df, df_imovel, grupos_imovel, planilha_carregada
22
+
 
23
  if planilha_carregada:
24
  return True
25
+
26
  try:
27
  print("⏳ Carregando planilha...")
28
  response = requests.get(URL)
 
33
  # Padroniza nomes das colunas
34
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
35
 
36
+ # Colunas numéricas
37
  colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
38
  for coluna in colunas_numericas:
39
  if coluna in df.columns:
 
43
  if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns:
44
  raise ValueError("Colunas essenciais não encontradas")
45
 
 
46
  df.drop_duplicates(inplace=True)
47
+
48
+ # Préprocessa imóvel
49
  df_imovel = df[df['tipo_do_bem'].str.lower() == 'imovel'].copy()
50
  grupos_imovel = agrupar_por_administradora(df_imovel)
51
 
 
59
  print(f"❌ Erro ao carregar planilha: {str(e)}")
60
  return False
61
 
62
+
63
  def agrupar_por_administradora(dataframe):
64
  """Agrupa cotas por administradora."""
65
  grupos = defaultdict(list)
 
67
  grupos[row['administradora']].append(row.to_dict())
68
  return grupos
69
 
70
+
71
+ def formatar_moeda(valor):
72
+ return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
73
+
74
+
75
  def calcular_tempo_parcelas(combinacao):
76
+ """Calcula o tempo de parcelas baseado nas cotas da combinação."""
77
  if not combinacao:
78
  return ""
79
+
80
  try:
 
81
  cotas_ordenadas = sorted(combinacao, key=lambda x: int(x.get('qtd_parcelas', 0)))
 
 
82
  prazos = sorted(set(int(c.get('qtd_parcelas', 0)) for c in combinacao))
83
+
84
  if not prazos or prazos[0] <= 0:
85
  return ""
86
+
87
  resultado = []
88
  prazo_anterior = 0
89
+
90
+ for prazo_atual in prazos:
 
91
  cotas_ativas = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) >= prazo_anterior + 1]
92
  valor_parcela_total = sum(c.get('valor_parcela', 0) for c in cotas_ativas)
93
+
94
  inicio = prazo_anterior + 1
95
  fim = prazo_atual
96
+
97
  if inicio <= fim and valor_parcela_total > 0:
98
  resultado.append(f"{inicio}x à {fim}x: {formatar_moeda(valor_parcela_total)}/mês")
99
+
100
  prazo_anterior = prazo_atual
101
+
 
102
  if prazos and prazo_anterior < max(prazos):
103
  cotas_ativas_finais = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) > prazo_anterior]
104
  if cotas_ativas_finais:
 
107
  fim_final = max(prazos)
108
  if valor_parcela_final > 0:
109
  resultado.append(f"{inicio_final}x à {fim_final}x: {formatar_moeda(valor_parcela_final)}/mês")
110
+
111
+ return "\\n".join(resultado) if resultado else ""
112
+
113
  except Exception as e:
114
  print(f"❌ Erro ao calcular tempo de parcelas: {str(e)}")
115
  return ""
116
 
117
+
118
+ def definir_margens_absolutas(credito_desejado, entrada_desejada, parcela_desejada):
119
+ """Define margens fixas em R$ para crédito, entrada e parcela."""
120
+ # Margens em R$ (ex.: 500.000 → 480.000–520.000)
121
+ margem_credito = 40_000
122
+ margem_entrada = 10_000
123
+ margem_parcela = 1_000
124
+
125
+ credito_min = max(0, credito_desejado - margem_credito)
126
+ credito_max = credito_desejado + margem_credito
127
+
128
+ entrada_min = max(0, entrada_desejada - margem_entrada)
129
+ entrada_max = entrada_desejada + margem_entrada
130
+
131
+ parcela_min = max(0, parcela_desejada - margem_parcela)
132
+ parcela_max = parcela_desejada + margem_parcela
133
+
134
+ return credito_min, credito_max, entrada_min, entrada_max, parcela_min, parcela_max
135
+
136
+
137
+ def filtrar_cotas_por_faixa(cotas, credito_min, credito_max):
138
+ """Filtra cotas que pelo menos têm chance de entrar na faixa de crédito."""
139
+ return [c for c in cotas if c['valor_credito'] < credito_max * 2]
140
+
141
+
142
  def buscar_combinacoes_otimizado(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None, max_combinacoes=15):
143
+ """Busca combinações de cotas dentro de margens fixas absolutas."""
144
  global df_imovel, grupos_imovel
145
+
 
146
  if not planilha_carregada:
147
  if not carregar_planilha():
148
  return "❌ Erro: Planilha não carregada. Por favor, tente novamente."
149
 
150
+ # Filtra administradoras
151
  if administradoras and len(administradoras) > 0:
152
  grupos_filtrados = {adm: cotas for adm, cotas in grupos_imovel.items() if adm in administradoras}
 
153
  else:
154
  grupos_filtrados = grupos_imovel
 
155
 
156
  if not grupos_filtrados:
157
  return "🚫 Nenhuma cota encontrada com os filtros aplicados. Tente ajustar os parâmetros."
158
 
159
  print(f"🔍 Total de administradoras no filtro: {len(grupos_filtrados)}")
160
+
161
+ # Usa margens fixas em R$
162
+ credito_min, credito_max, entrada_min, entrada_max, parcela_min, parcela_max = definir_margens_absolutas(
163
+ credito_desejado, entrada_desejada, parcela_desejada
164
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  print(f"💰 Valores desejados: Crédito: R$ {credito_desejado:,.2f}, Entrada: R$ {entrada_desejada:,.2f}, Parcela: R$ {parcela_desejada:,.2f}")
167
+ print(f"🔍 Margem de crédito: de R$ {credito_min:,.2f} até R$ {credito_max:,.2f}")
168
+
169
+ # Limites de busca
170
+ max_cotas_combinacao = 15
171
+ max_combinacoes_por_adm = 4000 # para evitar explosão de combinações [web:12][web:18]
172
 
173
  resultados = []
174
  total_combinacoes_testadas = 0
175
+
 
176
  for adm, cotas in grupos_filtrados.items():
177
  print(f"🔍 Analisando administradora: {adm} ({len(cotas)} cotas)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
+ cotas_filtradas = filtrar_cotas_por_faixa(cotas, credito_min, credito_max)
 
 
 
180
 
181
+ for r in range(1, min(max_cotas_combinacao + 1, len(cotas_filtradas) + 1)):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  combinacoes_geradas = 0
183
+ for combinacao in itertools.combinations(cotas_filtradas, r):
 
 
184
  if combinacoes_geradas >= max_combinacoes_por_adm:
185
  break
186
+
187
  total_combinacoes_testadas += 1
188
  combinacoes_geradas += 1
189
+
190
  total_credito = sum(c['valor_credito'] for c in combinacao)
191
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
192
  total_entrada = total_entrada_base + (total_credito * 0.085)
193
  total_parcelas = sum(c['valor_parcela'] for c in combinacao)
194
 
195
+ # Verifica margens fixas
196
  if (credito_min <= total_credito <= credito_max and
197
  entrada_min <= total_entrada <= entrada_max and
198
  parcela_min <= total_parcelas <= parcela_max):
 
202
  diferenca = abs(valor_final - total_credito)
203
 
204
  resultados.append((adm, combinacao, diferenca))
205
+
206
  if len(resultados) >= max_combinacoes * 2:
207
  break
208
+
209
  if len(resultados) >= max_combinacoes * 2:
210
  break
211
+
212
  if len(resultados) >= max_combinacoes * 2:
213
  break
214
 
215
+ print(f"🔍 Total de combinações testadas: {total_combinacoes_testadas}")
216
+ print(f"🔍 Combinações válidas encontradas: {len(resultados)}")
217
 
218
  if not resultados:
219
  return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
220
 
221
+ resultados.sort(key=lambda x: x[2]) # ordena pela menor diferença
222
  return formatar_resultados(resultados[:max_combinacoes])
223
 
 
 
224
 
225
  def formatar_resultados(resultados):
226
+ """Formata os resultados para exibição incluindo tempo de parcelas."""
227
  if not resultados:
228
  return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
229
+
230
  resposta = ""
231
  for idx, (adm, combinacao, _) in enumerate(resultados, 1):
232
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
 
242
  taxa_total = total_saldo - credito_real
243
  taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0
244
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
245
+
 
246
  tempo_parcelas = calcular_tempo_parcelas(combinacao)
247
 
248
  resposta += f"""
 
264
  """
265
  return resposta
266
 
267
+
268
  # Carrega a planilha uma vez quando o aplicativo inicia
269
  carregar_planilha()
270
 
271
+
272
  def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
273
  return buscar_combinacoes_otimizado(
274
+ float(credito_desejado or 0),
275
+ float(entrada_desejada or 0),
276
+ float(parcela_desejada or 0),
277
+ administradoras,
278
+ max_combinacoes=15
279
  )
280
 
281
+
282
  def get_administradoras_options():
283
  if df is not None and planilha_carregada:
284
  adm_list = df[df['tipo_do_bem'].str.lower() == 'imovel']['administradora'].unique()
285
  return sorted(adm_list)
286
  return []
287
 
288
+
289
+ # Interface Gradio
290
  with gr.Blocks(title="Sistema de Busca de Consórcio Imóvel") as demo:
291
  gr.HTML("""
292
  <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
293
  <style>
294
+ * { font-family: 'Poppins', sans-serif !important; }
295
+ body { background-color: #ff8f00 !important; color: black !important; }
296
+ input[type="number"], input[type="text"], textarea {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  background-color: white !important;
298
  border: 2px solid black !important;
299
  border-radius: 12px !important;
300
  color: black !important;
301
  padding: 10px !important;
302
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  .gr-button {
304
  font-weight: bold;
305
  border-radius: 10px !important;
 
307
  color: black !important;
308
  border: 2px solid black !important;
309
  }
310
+ .gr-button:hover { background-color: #fff3e0 !important; }
 
 
 
 
 
 
 
 
311
  </style>
312
  """)
313
 
 
314
  gr.Markdown("# 🏠 Busca de Combinações de Consórcio Imóvel")
315
  gr.Markdown("Insira os valores desejados para crédito, entrada e parcela, e filtre por administradora.")
316
 
 
322
  administradoras_multiselect = gr.CheckboxGroup(
323
  label="Filtrar por Administradoras",
324
  choices=get_administradoras_options(),
325
+ value=get_administradoras_options()
326
  )
327
 
328
  submit_btn = gr.Button("🔍 Buscar Combinações", variant="primary")
 
329
  output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...")
330
 
331
  submit_btn.click(
 
337
  gr.Markdown("---")
338
  gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio ideais.")
339
 
 
340
  if __name__ == "__main__":
341
+ demo.launch()