Arthur-Attili commited on
Commit
43ecffa
·
verified ·
1 Parent(s): ce33667

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -65
app.py CHANGED
@@ -61,99 +61,179 @@ def agrupar_por_administradora(dataframe):
61
  grupos[row['administradora']].append(row.to_dict())
62
  return grupos
63
 
64
- def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None):
65
- """Busca combinações de cotas dentro dos parâmetros especificados."""
66
- # Garante que o DataFrame está carregado
67
  if df is None:
68
- # Tenta carregar se ainda não foi carregado
69
  if not carregar_planilha():
70
- return "❌ Erro: Planilha não carregada. Por favor, tente novamente."
71
 
72
- # Filtra por tipo de bem e administradoras
73
  df_filtrado = df[df['tipo_do_bem'].str.lower() == 'imovel']
74
  if administradoras:
75
- # Certifica-se de que administradoras é uma lista (Gradio retorna tupla)
76
  if isinstance(administradoras, tuple):
77
  administradoras = list(administradoras)
78
  df_filtrado = df_filtrado[df_filtrado['administradora'].isin(administradoras)]
79
 
80
  if df_filtrado.empty:
81
- return "🚫 Nenhuma cota encontrada com os filtros aplicados. Tente ajustar os parâmetros."
82
 
83
- # Define faixas de tolerância
84
- credito_min, credito_max = credito_desejado * 0.93, credito_desejado * 1.07
85
- entrada_min, entrada_max = entrada_desejada * 0.93, entrada_desejada * 1.07
86
- parcela_min, parcela_max = parcela_desejada * 0.75, parcela_desejada * 1.25
87
 
88
  grupos = agrupar_por_administradora(df_filtrado)
89
  resultados = []
90
 
91
- # Configuração: mínimo 3 cotas, máximo 10 cotas por combinação
92
  MIN_COTAS = 3
93
  MAX_COTAS = 10
 
 
94
 
95
  for adm, cotas in grupos.items():
96
- # Filtra cotas que já estão dentro dos limites individualmente
97
- cotas_filtradas = [
98
- c for c in cotas
99
- if (credito_min <= c['valor_credito'] <= credito_max and
100
- parcela_min <= c['valor_parcela'] <= parcela_max)
101
- ]
102
-
103
- # Só busca se tiver pelo menos MIN_COTAS cotas
104
- if len(cotas_filtradas) < MIN_COTAS:
105
  continue
106
 
107
- # Busca combinações de MIN_COTAS a MAX_COTAS cotas
108
- for r in range(MIN_COTAS, min(MAX_COTAS, len(cotas_filtradas)) + 1):
109
- for combinacao in itertools.combinations(cotas_filtradas, r):
110
- total_credito = sum(c['valor_credito'] for c in combinacao)
111
- total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
112
- total_entrada = total_entrada_base + (total_credito * 0.085)
113
- total_parcelas = sum(c['valor_parcela'] for c in combinacao)
114
-
115
- # Verifica se a combinação está dentro dos limites
116
- if (credito_min <= total_credito <= credito_max and
117
- entrada_min <= total_entrada <= entrada_max and
118
- parcela_min <= total_parcelas <= parcela_max):
119
-
120
- total_saldo = sum(c['saldo_devedor'] for c in combinacao)
121
- valor_final = round(total_entrada + total_saldo, 2)
122
- diferenca = abs(valor_final - total_credito)
123
-
124
- resultados.append((adm, combinacao, diferenca))
125
-
126
- # Limita o número de resultados por administradora para evitar explosão
127
- if len(resultados) >= 1000: # Safety limit
128
- break
129
- if len(resultados) >= 1000:
130
  break
 
 
 
131
 
132
  if not resultados:
133
- # Tenta buscar com tolerâncias mais amplas se não encontrar resultados
134
- return buscar_combinacoes_com_tolerancia_ampliada(
135
- credito_desejado, entrada_desejada, parcela_desejada, administradoras
136
- )
137
-
138
- resultados.sort(key=lambda x: x[2]) # Ordena pela diferença
139
 
140
- return formatar_resultados(resultados)
 
 
 
141
 
142
- def buscar_combinacoes_com_tolerancia_ampliada(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
143
- """Busca combinações com tolerâncias mais amplas quando não encontra resultados."""
144
- # Tolerâncias ampliadas
145
- credito_min, credito_max = credito_desejado * 0.85, credito_desejado * 1.15
146
- entrada_min, entrada_max = entrada_desejada * 0.85, entrada_desejada * 1.15
147
- parcela_min, parcela_max = parcela_desejada * 0.65, parcela_desejada * 1.35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
- def formatar_moeda(valor):
150
- return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
 
 
 
 
 
 
 
 
 
 
 
151
 
 
152
  def formatar_resultados(resultados):
153
- """Formata os resultados para exibição."""
154
- resposta = ""
155
- # Limita a 15 resultados para evitar saídas muito longas
156
- for idx, (adm, combinacao, _) in enumerate(resultados[:15], 1):
 
 
 
157
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
158
  total_credito = sum(c['valor_credito'] for c in combinacao)
159
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
@@ -169,7 +249,7 @@ def formatar_resultados(resultados):
169
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
170
 
171
  resposta += f"""
172
- --- COMBINAÇÃO {idx} ---
173
  🏢 Administradora: {adm}
174
  🔢 Códigos: {', '.join(codigos)}
175
  💰 Crédito Total: {formatar_moeda(total_credito)}
 
61
  grupos[row['administradora']].append(row.to_dict())
62
  return grupos
63
 
64
+ def buscar_combinacoes_rapido(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None):
65
+ """Busca combinações de forma extremamente rápida com algoritmo inteligente."""
 
66
  if df is None:
 
67
  if not carregar_planilha():
68
+ return "❌ Erro: Planilha não carregada."
69
 
70
+ # Filtra as cotas
71
  df_filtrado = df[df['tipo_do_bem'].str.lower() == 'imovel']
72
  if administradoras:
 
73
  if isinstance(administradoras, tuple):
74
  administradoras = list(administradoras)
75
  df_filtrado = df_filtrado[df_filtrado['administradora'].isin(administradoras)]
76
 
77
  if df_filtrado.empty:
78
+ return "🚫 Nenhuma cota encontrada com os filtros aplicados."
79
 
80
+ # Tolerâncias mais amplas para melhor diversificação
81
+ credito_min, credito_max = credito_desejado * 0.85, credito_desejado * 1.15
82
+ entrada_min, entrada_max = entrada_desejada * 0.80, entrada_desejada * 1.20
83
+ parcela_min, parcela_max = parcela_desejada * 0.60, parcela_desejada * 1.40
84
 
85
  grupos = agrupar_por_administradora(df_filtrado)
86
  resultados = []
87
 
 
88
  MIN_COTAS = 3
89
  MAX_COTAS = 10
90
+ MAX_RESULTADOS_POR_ADM = 30
91
+ TOTAL_MAX_RESULTADOS = 100
92
 
93
  for adm, cotas in grupos.items():
94
+ if len(cotas) < MIN_COTAS:
 
 
 
 
 
 
 
 
95
  continue
96
 
97
+ # Ordena cotas por proximidade do valor desejado (otimização)
98
+ cotas_ordenadas = sorted(cotas, key=lambda x: abs(x['valor_credito'] - credito_desejado/MAX_COTAS))
99
+
100
+ # Busca usando algoritmo ganancioso com backtracking limitado
101
+ encontrados = buscar_combinacoes_inteligente(
102
+ cotas_ordenadas, credito_min, credito_max, entrada_min,
103
+ entrada_max, parcela_min, parcela_max, MIN_COTAS, MAX_COTAS,
104
+ max_resultados=MAX_RESULTADOS_POR_ADM
105
+ )
106
+
107
+ for combo in encontrados:
108
+ total_credito = sum(c['valor_credito'] for c in combo)
109
+ total_entrada_base = sum(c['valor_entrada'] for c in combo)
110
+ total_entrada = total_entrada_base + (total_credito * 0.085)
111
+ total_saldo = sum(c['saldo_devedor'] for c in combo)
112
+ valor_final = round(total_entrada + total_saldo, 2)
113
+ diferenca = abs(valor_final - total_credito)
114
+
115
+ resultados.append((adm, combo, diferenca))
116
+
117
+ if len(resultados) >= TOTAL_MAX_RESULTADOS:
 
 
118
  break
119
+
120
+ if len(resultados) >= TOTAL_MAX_RESULTADOS:
121
+ break
122
 
123
  if not resultados:
124
+ return "🚫 Nenhuma combinação adequada encontrada. Tente ampliar os filtros."
 
 
 
 
 
125
 
126
+ # Ordena por melhor match
127
+ resultados.sort(key=lambda x: x[2])
128
+
129
+ return formatar_resultados(resultados[:20])
130
 
131
+ def buscar_combinacoes_inteligente(cotas, credito_min, credito_max, entrada_min,
132
+ entrada_max, parcela_min, parcela_max,
133
+ min_cotas, max_cotas, max_resultados=30):
134
+ """Algoritmo inteligente para encontrar combinações rapidamente."""
135
+ resultados = []
136
+ n = len(cotas)
137
+
138
+ # Pré-calcula arrays para performance
139
+ creditos = np.array([c['valor_credito'] for c in cotas])
140
+ entradas = np.array([c['valor_entrada'] for c in cotas])
141
+ parcelas = np.array([c['valor_parcela'] for c in cotas])
142
+ saldos = np.array([c['saldo_devedor'] for c in cotas])
143
+
144
+ # Função de avaliação rápida
145
+ def avalia_combinacao(indices):
146
+ total_credito = creditos[indices].sum()
147
+ total_entrada_base = entradas[indices].sum()
148
+ total_entrada = total_entrada_base + (total_credito * 0.085)
149
+ total_parcelas = parcelas[indices].sum()
150
+
151
+ return (credito_min <= total_credito <= credito_max and
152
+ entrada_min <= total_entrada <= entrada_max and
153
+ parcela_min <= total_parcelas <= parcela_max)
154
+
155
+ # Estratégia 1: Combinações de tamanho fixo (3-6 cotas) - mais rápido
156
+ for tamanho in range(min_cotas, min(7, max_cotas + 1)):
157
+ if len(resultados) >= max_resultados:
158
+ break
159
+
160
+ # Amostra aleatória de combinações para evitar explosão combinatória
161
+ indices = list(range(n))
162
+ np.random.shuffle(indices) # Embaralha para diversidade
163
+
164
+ # Testa um número limitado de combinações
165
+ for combo_indices in islice(combinations(indices, tamanho), 1000):
166
+ if avalia_combinacao(combo_indices):
167
+ combo = [cotas[i] for i in combo_indices]
168
+ resultados.append(combo)
169
+
170
+ if len(resultados) >= max_resultados:
171
+ break
172
+
173
+ # Estratégia 2: Para combinações maiores (7-10 cotas), usa algoritmo ganancioso
174
+ if len(resultados) < max_resultados and max_cotas >= 7:
175
+ for _ in range(min(50, max_resultados - len(resultados))):
176
+ combo = []
177
+ indices_usados = set()
178
+
179
+ # Adiciona cotas até atingir o objetivo
180
+ for tentativa in range(max_cotas):
181
+ melhor_idx = None
182
+ melhor_score = float('inf')
183
+
184
+ # Encontra a melhor cota para adicionar
185
+ for idx in range(n):
186
+ if idx in indices_usados:
187
+ continue
188
+
189
+ combo_teste = combo + [cotas[idx]]
190
+ indices_teste = list(indices_usados) + [idx]
191
+
192
+ total_credito = sum(c['valor_credito'] for c in combo_teste)
193
+ score = abs(total_credito - (credito_max + credito_min)/2)
194
+
195
+ if score < melhor_score:
196
+ melhor_score = score
197
+ melhor_idx = idx
198
+
199
+ if melhor_idx is not None:
200
+ combo.append(cotas[melhor_idx])
201
+ indices_usados.add(melhor_idx)
202
+
203
+ # Verifica se a combinação atual é válida
204
+ if len(combo) >= min_cotas and avalia_combinacao(list(indices_usados)):
205
+ resultados.append(combo.copy())
206
+ if len(resultados) >= max_resultados:
207
+ break
208
+
209
+ if len(resultados) >= max_resultados:
210
+ break
211
+
212
+ return resultados[:max_resultados]
213
 
214
+ # Função principal ajustada
215
+ def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
216
+ entrada_calculada = credito_desejado * 0.085 + entrada_desejada
217
+
218
+ start_time = time.time()
219
+ resultado = buscar_combinacoes_rapido(
220
+ credito_desejado, entrada_calculada, parcela_desejada, administradoras
221
+ )
222
+
223
+ elapsed = time.time() - start_time
224
+ print(f"⏱️ Busca concluída em {elapsed:.2f} segundos")
225
+
226
+ return resultado
227
 
228
+ # Atualize a interface para mostrar estatísticas
229
  def formatar_resultados(resultados):
230
+ """Formata os resultados com informações de diversificação."""
231
+ if not resultados:
232
+ return "🚫 Nenhuma combinação encontrada."
233
+
234
+ resposta = f"📊 **Encontradas {len(resultados)} combinações**\n\n"
235
+
236
+ for idx, (adm, combinacao, diferenca) in enumerate(resultados, 1):
237
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
238
  total_credito = sum(c['valor_credito'] for c in combinacao)
239
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
 
249
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
250
 
251
  resposta += f"""
252
+ --- COMBINAÇÃO {idx} ({len(combinacao)} cotas) ---
253
  🏢 Administradora: {adm}
254
  🔢 Códigos: {', '.join(codigos)}
255
  💰 Crédito Total: {formatar_moeda(total_credito)}