Arthur-Attili commited on
Commit
3650fb4
·
verified ·
1 Parent(s): 04666e9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -122
app.py CHANGED
@@ -23,7 +23,7 @@ URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
23
  df = None
24
 
25
  def carregar_planilha():
26
- """Carrega e prepara a planilha para análise de consórcios de automóveis."""
27
  global df
28
  try:
29
  print("⏳ Carregando planilha...")
@@ -34,30 +34,27 @@ def carregar_planilha():
34
 
35
  # Padroniza nomes das colunas
36
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
37
-
38
- # Padroniza os nomes das administradoras
39
- if 'administradora' in df.columns:
40
- df['administradora'] = df['administradora'].astype(str).str.strip().str.lower()
41
 
42
 
43
  # Converte colunas numéricas
44
- numeric_cols = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
45
- for col in numeric_cols:
46
- if col in df.columns:
47
- df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
48
 
49
  # Verifica colunas essenciais
50
  if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns:
51
- raise ValueError("Colunas essenciais não encontradas na planilha")
52
 
53
  # Remove duplicatas
54
  df.drop_duplicates(inplace=True)
55
- print("✅ Planilha carregada com sucesso!")
 
56
  return True
57
 
58
  except Exception as e:
59
  print(f"❌ Erro ao carregar planilha: {str(e)}")
60
- # Retorne uma mensagem de erro mais útil para o usuário
61
  return False
62
 
63
  def agrupar_por_administradora(dataframe):
@@ -68,36 +65,35 @@ def agrupar_por_administradora(dataframe):
68
  return grupos
69
 
70
  def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None):
71
- """Busca combinações de cotas para automóveis dentro dos parâmetros especificados."""
72
- # Garante que o DataFrame está carregado antes de cada busca
73
- # Isso é útil caso a sessão expire ou o DF seja limpo por algum motivo no servidor do HF.
74
- global df
75
  if df is None:
 
76
  if not carregar_planilha():
77
- return "❌ Erro: Planilha não carregada. Verifique se o ID do arquivo está correto e público."
78
 
79
- # Filtra por tipo de bem (Auto) e administradoras selecionadas
80
- df_filtrado = df[df['tipo_do_bem'].str.lower() == 'auto']
81
  if administradoras:
82
- if isinstance(administradoras, tuple): # Gradio SelectMultiple retorna uma tupla
 
83
  administradoras = list(administradoras)
84
- administradoras = [a.strip().lower() for a in administradoras] # Normaliza os nomes
85
  df_filtrado = df_filtrado[df_filtrado['administradora'].isin(administradoras)]
86
 
87
-
88
  if df_filtrado.empty:
89
- return "🚫 Nenhuma cota de automóvel encontrada com os filtros aplicados."
90
 
91
  # Define faixas de tolerância
92
  credito_min, credito_max = credito_desejado * 0.93, credito_desejado * 1.07
93
  entrada_min, entrada_max = entrada_desejada * 0.93, entrada_desejada * 1.07
94
- parcela_min, parcela_max = parcela_desejada * 0.5, parcela_desejada * 1.5
95
 
96
  grupos = agrupar_por_administradora(df_filtrado)
97
  resultados = []
98
 
99
  for adm, cotas in grupos.items():
100
- for r in range(1, min(3, len(cotas)) + 1): # Máximo 3 cotas por combinação
101
  for combinacao in itertools.combinations(cotas, r):
102
  total_credito = sum(c['valor_credito'] for c in combinacao)
103
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
@@ -110,14 +106,15 @@ def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, adm
110
 
111
  total_saldo = sum(c['saldo_devedor'] for c in combinacao)
112
  valor_final = round(total_entrada + total_saldo, 2)
113
- diferenca = abs(valor_final - total_credito)
114
 
115
  resultados.append((adm, combinacao, diferenca))
116
 
117
  if not resultados:
118
- return "Nenhuma combinação adequada encontrada para automóveis com os parâmetros fornecidos."
 
 
119
 
120
- resultados.sort(key=lambda x: x[2])
121
  return formatar_resultados(resultados)
122
 
123
  def formatar_moeda(valor):
@@ -126,62 +123,8 @@ def formatar_moeda(valor):
126
  def formatar_resultados(resultados):
127
  """Formata os resultados para exibição."""
128
  resposta = ""
129
-
130
- # 1. Agrupar os resultados por administradora, mantendo a diferença para ordenação interna
131
- # grouped_by_adm = {'Administradora X': [(combinacao_A, 100), (combinacao_C, 180), ...],
132
- # 'Administradora Y': [(combinacao_B, 150), ...]}
133
- grouped_by_adm = defaultdict(list)
134
- for adm, combinacao, diferenca in resultados:
135
- grouped_by_adm[adm].append((combinacao, diferenca))
136
-
137
- # 2. Ordenar as combinações dentro de cada administradora pela diferença (do menor para o maior)
138
- for adm in grouped_by_adm:
139
- grouped_by_adm[adm].sort(key=lambda x: x[1]) # x[1] é a diferença
140
-
141
- final_resultados_diversificados = []
142
-
143
- # Dicionário para acompanhar o índice da próxima combinação a ser pega de cada administradora
144
- # Ex: {'Itau': 0, 'Bradesco': 0, 'Caixa': 0}
145
- adm_indices = {adm: 0 for adm in grouped_by_adm.keys()}
146
-
147
- # Lista de administradoras ativas (que ainda têm combinações disponíveis)
148
- active_adms = list(grouped_by_adm.keys())
149
-
150
- current_adm_idx = 0 # Índice para ciclar entre as administradoras ativas
151
-
152
- # Loop para selecionar até 15 combinações, garantindo diversidade
153
- while len(final_resultados_diversificados) < 15 and active_adms:
154
- # Pega a administradora da vez para selecionar uma combinação
155
- adm_da_vez = active_adms[current_adm_idx]
156
-
157
- # Pega o índice da próxima combinação para essa administradora
158
- idx_na_adm = adm_indices[adm_da_vez]
159
-
160
- # Verifica se essa administradora ainda tem combinações disponíveis
161
- if idx_na_adm < len(grouped_by_adm[adm_da_vez]):
162
- # Pega a combinação (ignorando a diferença, que já foi usada para ordenar internamente)
163
- combinacao_data, _ = grouped_by_adm[adm_da_vez][idx_na_adm]
164
-
165
- # Adiciona à lista de resultados final (com a administradora e a combinação)
166
- final_resultados_diversificados.append((adm_da_vez, combinacao_data))
167
-
168
- # Incrementa o índice para a próxima vez que pegarmos dessa administradora
169
- adm_indices[adm_da_vez] += 1
170
- else:
171
- # Se essa administradora não tem mais combinações, remove ela da lista de ativas
172
- active_adms.pop(current_adm_idx)
173
- # Ajusta o current_adm_idx se a lista diminuiu e estamos no limite
174
- if current_adm_idx >= len(active_adms) and active_adms:
175
- current_adm_idx = 0 # Volta para o início da lista se removemos o último
176
-
177
- # Move para a próxima administradora no ciclo (garantindo que não estouramos o índice da lista active_adms)
178
- if active_adms: # Só avança se ainda houver administradoras ativas
179
- current_adm_idx = (current_adm_idx + 1) % len(active_adms)
180
- else:
181
- break # Não há mais administradoras ativas, sai do loop
182
-
183
- # Agora, itera sobre os resultados diversificados para formatar a resposta
184
- for idx, (adm, combinacao) in enumerate(final_resultados_diversificados, 1):
185
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
186
  total_credito = sum(c['valor_credito'] for c in combinacao)
187
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
@@ -196,14 +139,12 @@ def formatar_resultados(resultados):
196
  taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0
197
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
198
 
199
- #comissao = total_entrada * (8.5 / 108.5)
200
-
201
  resposta += f"""
202
  --- COMBINAÇÃO {idx} ---
203
- �� Administradora: {adm}
204
  🔢 Códigos: {', '.join(codigos)}
205
  💰 Crédito Total: {formatar_moeda(total_credito)}
206
- �� Entrada (+comissão): {formatar_moeda(total_entrada)}
207
  📉 Saldo Devedor: {formatar_moeda(total_saldo)}
208
  💲 Parcela: {formatar_moeda(total_parcelas)}
209
  ⏳ Prazo: {maior_prazo} meses
@@ -215,38 +156,34 @@ def formatar_resultados(resultados):
215
  """
216
  return resposta
217
 
218
- # --- INICIALIZAÇÃO PARA GRADIO ---
219
- # Carrega a planilha UMA VEZ no início do aplicativo
220
- # Se der erro, a função de busca tentará carregar novamente.
221
- if not carregar_planilha():
222
- print("⚠️ Aviso: Planilha não carregada na inicialização. Tentará carregar na primeira busca.")
223
 
224
- # Calcula a entrada adicionando 8,5% ao valor final da entrada.
225
  def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
226
  entrada_calculada = credito_desejado * 0.085 + entrada_desejada
227
  return buscar_combinacoes(credito_desejado, entrada_calculada, parcela_desejada, administradoras)
228
 
229
- # Prepara as opções para o seletor de administradoras do Gradio
230
  def get_administradoras_options():
231
  if df is not None:
232
- adm_list = df[df['tipo_do_bem'].str.lower() == 'auto']['administradora'].unique()
233
- return sorted(adm_list.tolist()) # Converter para lista, pois .unique() pode retornar um numpy array
234
  return []
235
 
236
- # --- Interface Gradio ---
237
- with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
238
  gr.HTML("""
239
  <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
240
  <style>
241
  * {
242
  font-family: 'Poppins', sans-serif !important;
243
  }
244
-
245
  body {
246
  background-color: #ff8f00 !important;
247
  color: black !important;
248
  }
249
-
250
  /* Inputs Numéricos: Crédito, Entrada, Parcela */
251
  input[type="number"] {
252
  background-color: white !important;
@@ -256,7 +193,7 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
256
  padding: 10px !important;
257
  font-weight: 500;
258
  }
259
-
260
  /* Inputs de texto e áreas de texto (como a saída e código) */
261
  input[type="text"], textarea {
262
  background-color: white !important;
@@ -265,25 +202,25 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
265
  color: black !important;
266
  padding: 10px !important;
267
  }
268
-
269
  input::placeholder, textarea::placeholder {
270
  color: #333 !important;
271
  opacity: 0.7;
272
  }
273
-
274
  .gr-checkbox label {
275
  display: flex;
276
  align-items: center;
277
  gap: 8px;
278
  font-weight: 600;
279
  }
280
-
281
  input[type="checkbox"] {
282
  accent-color: black !important;
283
  width: 18px;
284
  height: 18px;
285
  }
286
-
287
  .gr-button {
288
  font-weight: bold;
289
  border-radius: 10px !important;
@@ -291,48 +228,46 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
291
  color: black !important;
292
  border: 2px solid black !important;
293
  }
294
-
295
  .gr-button:hover {
296
  background-color: #fff3e0 !important;
297
  color: black !important;
298
  }
299
-
300
  label {
301
  font-weight: 600 !important;
302
  }
303
  </style>
304
  """)
305
-
306
- gr.Markdown("# 🚗 Sistema de Busca de Combinações de Consórcio (Automóveis)")
307
- gr.Markdown("Preencha os campos abaixo para encontrar as melhores combinações de cotas de consórcio de veículo.")
 
308
 
309
  with gr.Row():
310
- credito_input = gr.Number(label="Crédito Desejado (R$)", value=30000, step=1000, interactive=True)
311
- entrada_input = gr.Number(label="Entrada Desejada (R$)", value=3000, step=100, interactive=True)
312
- parcela_input = gr.Number(label="Parcela Desejada (R$)", value=500, step=10, interactive=True)
313
 
314
- administradoras_options = get_administradoras_options()
315
  administradoras_multiselect = gr.CheckboxGroup(
316
  label="Filtrar por Administradoras",
317
- choices=administradoras_options,
318
- value=administradoras_options, # Seleciona todas por padrão
319
- interactive=True
320
  )
321
 
322
  submit_btn = gr.Button("🔍 Buscar Combinações", variant="primary")
323
 
324
  output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...")
325
 
326
- # Conecta o botão à função de busca
327
  submit_btn.click(
328
  fn=ajustar_e_buscar,
329
  inputs=[credito_input, entrada_input, parcela_input, administradoras_multiselect],
330
  outputs=output_text
331
  )
332
 
333
-
334
  gr.Markdown("---")
335
- gr.Markdown("Ferramenta desenvolvida para otimizar a busca por consórcios de automóveis.")
336
 
 
337
  if __name__ == "__main__":
338
- demo.launch() # Você não precisa disso no Colab, mas sim no arquivo que você fará o deploy
 
23
  df = None
24
 
25
  def carregar_planilha():
26
+ """Carrega e prepara a planilha para análise."""
27
  global df
28
  try:
29
  print("⏳ Carregando planilha...")
 
34
 
35
  # Padroniza nomes das colunas
36
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
37
+ df['administradora'] = df['administradora'].str.strip().str.lower()
 
 
 
38
 
39
 
40
  # Converte colunas numéricas
41
+ colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
42
+ for coluna in colunas_numericas:
43
+ if coluna in df.columns:
44
+ df[coluna] = pd.to_numeric(df[coluna], errors='coerce').fillna(0)
45
 
46
  # Verifica colunas essenciais
47
  if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns:
48
+ raise ValueError("Colunas essenciais não encontradas")
49
 
50
  # Remove duplicatas
51
  df.drop_duplicates(inplace=True)
52
+
53
+ print("✅ Planilha carregada com sucesso")
54
  return True
55
 
56
  except Exception as e:
57
  print(f"❌ Erro ao carregar planilha: {str(e)}")
 
58
  return False
59
 
60
  def agrupar_por_administradora(dataframe):
 
65
  return grupos
66
 
67
  def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None):
68
+ """Busca combinações de cotas dentro dos parâmetros especificados."""
69
+ # Garante que o DataFrame está carregado
 
 
70
  if df is None:
71
+ # Tenta carregar se ainda não foi carregado
72
  if not carregar_planilha():
73
+ return "❌ Erro: Planilha não carregada. Por favor, tente novamente."
74
 
75
+ # Filtra por tipo de bem e administradoras
76
+ df_filtrado = df[df['tipo_do_bem'].str.lower() == 'imovel']
77
  if administradoras:
78
+ # Certifica-se de que administradoras é uma lista (Gradio retorna tupla)
79
+ if isinstance(administradoras, tuple):
80
  administradoras = list(administradoras)
81
+ administradoras = [a.strip().lower() for a in administradoras]
82
  df_filtrado = df_filtrado[df_filtrado['administradora'].isin(administradoras)]
83
 
 
84
  if df_filtrado.empty:
85
+ return "🚫 Nenhuma cota encontrada com os filtros aplicados. Tente ajustar os parâmetros."
86
 
87
  # Define faixas de tolerância
88
  credito_min, credito_max = credito_desejado * 0.93, credito_desejado * 1.07
89
  entrada_min, entrada_max = entrada_desejada * 0.93, entrada_desejada * 1.07
90
+ parcela_min, parcela_max = parcela_desejada * 0.75, parcela_desejada * 1.25
91
 
92
  grupos = agrupar_por_administradora(df_filtrado)
93
  resultados = []
94
 
95
  for adm, cotas in grupos.items():
96
+ for r in range(1, min(3, len(cotas)) + 1): # Máximo 3 cotas por combinação
97
  for combinacao in itertools.combinations(cotas, r):
98
  total_credito = sum(c['valor_credito'] for c in combinacao)
99
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
 
106
 
107
  total_saldo = sum(c['saldo_devedor'] for c in combinacao)
108
  valor_final = round(total_entrada + total_saldo, 2)
109
+ diferenca = abs(valor_final - total_credito) # Diferença do valor final pelo crédito (para ordenação)
110
 
111
  resultados.append((adm, combinacao, diferenca))
112
 
113
  if not resultados:
114
+ return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
115
+
116
+ resultados.sort(key=lambda x: x[2]) # Ordena pela diferença
117
 
 
118
  return formatar_resultados(resultados)
119
 
120
  def formatar_moeda(valor):
 
123
  def formatar_resultados(resultados):
124
  """Formata os resultados para exibição."""
125
  resposta = ""
126
+ # Limita a 15 resultados para evitar saídas muito longas
127
+ for idx, (adm, combinacao, _) in enumerate(resultados[:15], 1):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
129
  total_credito = sum(c['valor_credito'] for c in combinacao)
130
  total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
 
139
  taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0
140
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
141
 
 
 
142
  resposta += f"""
143
  --- COMBINAÇÃO {idx} ---
144
+ 🏢 Administradora: {adm}
145
  🔢 Códigos: {', '.join(codigos)}
146
  💰 Crédito Total: {formatar_moeda(total_credito)}
147
+ 💵 Entrada (+comissão): {formatar_moeda(total_entrada)}
148
  📉 Saldo Devedor: {formatar_moeda(total_saldo)}
149
  💲 Parcela: {formatar_moeda(total_parcelas)}
150
  ⏳ Prazo: {maior_prazo} meses
 
156
  """
157
  return resposta
158
 
159
+ # Carrega a planilha uma vez quando o aplicativo inicia
160
+ carregar_planilha()
 
 
 
161
 
 
162
  def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
163
  entrada_calculada = credito_desejado * 0.085 + entrada_desejada
164
  return buscar_combinacoes(credito_desejado, entrada_calculada, parcela_desejada, administradoras)
165
 
166
+ # Prepara a lista de administradoras para o Gradio
167
  def get_administradoras_options():
168
  if df is not None:
169
+ adm_list = df[df['tipo_do_bem'].str.lower() == 'imovel']['administradora'].unique()
170
+ return sorted(adm_list)
171
  return []
172
 
173
+ # Criando a interface Gradio
174
+ with gr.Blocks(title="Sistema de Busca de Consórcio Imóvel") as demo:
175
  gr.HTML("""
176
  <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
177
  <style>
178
  * {
179
  font-family: 'Poppins', sans-serif !important;
180
  }
181
+
182
  body {
183
  background-color: #ff8f00 !important;
184
  color: black !important;
185
  }
186
+
187
  /* Inputs Numéricos: Crédito, Entrada, Parcela */
188
  input[type="number"] {
189
  background-color: white !important;
 
193
  padding: 10px !important;
194
  font-weight: 500;
195
  }
196
+
197
  /* Inputs de texto e áreas de texto (como a saída e código) */
198
  input[type="text"], textarea {
199
  background-color: white !important;
 
202
  color: black !important;
203
  padding: 10px !important;
204
  }
205
+
206
  input::placeholder, textarea::placeholder {
207
  color: #333 !important;
208
  opacity: 0.7;
209
  }
210
+
211
  .gr-checkbox label {
212
  display: flex;
213
  align-items: center;
214
  gap: 8px;
215
  font-weight: 600;
216
  }
217
+
218
  input[type="checkbox"] {
219
  accent-color: black !important;
220
  width: 18px;
221
  height: 18px;
222
  }
223
+
224
  .gr-button {
225
  font-weight: bold;
226
  border-radius: 10px !important;
 
228
  color: black !important;
229
  border: 2px solid black !important;
230
  }
231
+
232
  .gr-button:hover {
233
  background-color: #fff3e0 !important;
234
  color: black !important;
235
  }
236
+
237
  label {
238
  font-weight: 600 !important;
239
  }
240
  </style>
241
  """)
242
+
243
+
244
+ gr.Markdown("# 🏠 Busca de Combinações de Consórcio Imóvel")
245
+ gr.Markdown("Insira os valores desejados para crédito, entrada e parcela, e filtre por administradora.")
246
 
247
  with gr.Row():
248
+ credito_input = gr.Number(label="Crédito Desejado (R$)", value=300000, step=1000)
249
+ entrada_input = gr.Number(label="Entrada Desejada (R$)", value=30000, step=1000)
250
+ parcela_input = gr.Number(label="Parcela Desejada (R$)", value=1500, step=100)
251
 
 
252
  administradoras_multiselect = gr.CheckboxGroup(
253
  label="Filtrar por Administradoras",
254
+ choices=get_administradoras_options(),
255
+ value=get_administradoras_options() # Seleciona todas por padrão
 
256
  )
257
 
258
  submit_btn = gr.Button("🔍 Buscar Combinações", variant="primary")
259
 
260
  output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...")
261
 
 
262
  submit_btn.click(
263
  fn=ajustar_e_buscar,
264
  inputs=[credito_input, entrada_input, parcela_input, administradoras_multiselect],
265
  outputs=output_text
266
  )
267
 
 
268
  gr.Markdown("---")
269
+ gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio ideais.")
270
 
271
+ # Inicia o aplicativo Gradio
272
  if __name__ == "__main__":
273
+ demo.launch() # Você não precisa disso no Colab, mas sim no arquivo que você fará o deploy