Arthur-Attili commited on
Commit
1e830d0
·
verified ·
1 Parent(s): 870011b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +100 -65
app.py CHANGED
@@ -3,18 +3,82 @@ import requests
3
  import io
4
  import itertools
5
  from collections import defaultdict
6
- import gradio as gr # Importação principal para Gradio
7
- import os # Para variáveis de ambiente, se necessário
 
8
 
9
  # 🔧 CONFIGURAÇÕES GLOBAIS
10
- # O ID da planilha para Consórcio de Automóveis
11
- # Certifique-se de que este FILE_ID esteja acessível publicamente ou para "Qualquer pessoa com o link"
12
  FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X"
13
  URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
14
 
15
  # Variável global para armazenar o DataFrame
16
  df = None
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  def carregar_planilha():
20
  """Carrega e prepara a planilha para análise de consórcios de automóveis."""
@@ -29,10 +93,10 @@ def carregar_planilha():
29
  # Padroniza nomes das colunas
30
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
31
 
32
- # Padroniza os nomes das administradoras
33
  if 'administradora' in df.columns:
34
- df['administradora'] = df['administradora'].astype(str).str.strip().str.lower()
35
-
36
 
37
  # Converte colunas numéricas
38
  numeric_cols = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
@@ -47,11 +111,14 @@ def carregar_planilha():
47
  # Remove duplicatas
48
  df.drop_duplicates(inplace=True)
49
  print("✅ Planilha carregada com sucesso!")
 
 
 
 
50
  return True
51
 
52
  except Exception as e:
53
  print(f"❌ Erro ao carregar planilha: {str(e)}")
54
- # Retorne uma mensagem de erro mais útil para o usuário
55
  return False
56
 
57
  def agrupar_por_administradora(dataframe):
@@ -73,7 +140,7 @@ def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, adm
73
  if administradoras:
74
  if isinstance(administradoras, tuple):
75
  administradoras = list(administradoras)
76
- administradoras = [a.strip().lower() for a in administradoras]
77
  df_filtrado = df_filtrado[df_filtrado['administradora'].isin(administradoras)]
78
 
79
  if df_filtrado.empty:
@@ -86,13 +153,13 @@ def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, adm
86
 
87
  grupos = agrupar_por_administradora(df_filtrado)
88
  resultados = []
89
- MAX_COMBINACOES_POR_ADM = 50 # Limite para evitar processamento excessivo
90
 
91
  for adm, cotas in grupos.items():
92
- cotas = cotas[:25] # Limita a 25 cotas por administradora para evitar combinações explosivas
93
  combinacoes_validas = 0
94
 
95
- for r in range(1, min(6, len(cotas)) + 1): # Agora até 5 cotas
96
  for combinacao in itertools.combinations(cotas, r):
97
  if combinacoes_validas >= MAX_COMBINACOES_POR_ADM:
98
  break
@@ -116,10 +183,7 @@ def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, adm
116
  if not resultados:
117
  return "Nenhuma combinação adequada encontrada para automóveis com os parâmetros fornecidos."
118
 
119
- # Ordena por diferença (melhores combinações primeiro)
120
  resultados.sort(key=lambda x: x[2])
121
-
122
- # Limita o número total de resultados para evitar sobrecarga
123
  resultados = resultados[:250]
124
 
125
  return formatar_resultados(resultados)
@@ -131,60 +195,36 @@ def formatar_resultados(resultados):
131
  """Formata os resultados para exibição."""
132
  resposta = ""
133
 
134
- # 1. Agrupar os resultados por administradora, mantendo a diferença para ordenação interna
135
- # grouped_by_adm = {'Administradora X': [(combinacao_A, 100), (combinacao_C, 180), ...],
136
- # 'Administradora Y': [(combinacao_B, 150), ...]}
137
  grouped_by_adm = defaultdict(list)
138
  for adm, combinacao, diferenca in resultados:
139
  grouped_by_adm[adm].append((combinacao, diferenca))
140
 
141
- # 2. Ordenar as combinações dentro de cada administradora pela diferença (do menor para o maior)
142
  for adm in grouped_by_adm:
143
- grouped_by_adm[adm].sort(key=lambda x: x[1]) # x[1] é a diferença
144
 
145
  final_resultados_diversificados = []
146
-
147
- # Dicionário para acompanhar o índice da próxima combinação a ser pega de cada administradora
148
- # Ex: {'Itau': 0, 'Bradesco': 0, 'Caixa': 0}
149
  adm_indices = {adm: 0 for adm in grouped_by_adm.keys()}
150
-
151
- # Lista de administradoras ativas (que ainda têm combinações disponíveis)
152
  active_adms = list(grouped_by_adm.keys())
 
153
 
154
- current_adm_idx = 0 # Índice para ciclar entre as administradoras ativas
155
-
156
- # Loop para selecionar até 15 combinações, garantindo diversidade
157
  while len(final_resultados_diversificados) < 15 and active_adms:
158
- # Pega a administradora da vez para selecionar uma combinação
159
  adm_da_vez = active_adms[current_adm_idx]
160
-
161
- # Pega o índice da próxima combinação para essa administradora
162
  idx_na_adm = adm_indices[adm_da_vez]
163
 
164
- # Verifica se essa administradora ainda tem combinações disponíveis
165
  if idx_na_adm < len(grouped_by_adm[adm_da_vez]):
166
- # Pega a combinação (ignorando a diferença, que já foi usada para ordenar internamente)
167
  combinacao_data, _ = grouped_by_adm[adm_da_vez][idx_na_adm]
168
-
169
- # Adiciona à lista de resultados final (com a administradora e a combinação)
170
  final_resultados_diversificados.append((adm_da_vez, combinacao_data))
171
-
172
- # Incrementa o índice para a próxima vez que pegarmos dessa administradora
173
  adm_indices[adm_da_vez] += 1
174
  else:
175
- # Se essa administradora não tem mais combinações, remove ela da lista de ativas
176
  active_adms.pop(current_adm_idx)
177
- # Ajusta o current_adm_idx se a lista diminuiu e estamos no limite
178
  if current_adm_idx >= len(active_adms) and active_adms:
179
- current_adm_idx = 0 # Volta para o início da lista se removemos o último
180
 
181
- # Move para a próxima administradora no ciclo (garantindo que não estouramos o índice da lista active_adms)
182
- if active_adms: # Só avança se ainda houver administradoras ativas
183
  current_adm_idx = (current_adm_idx + 1) % len(active_adms)
184
  else:
185
- break # Não há mais administradoras ativas, sai do loop
186
 
187
- # Agora, itera sobre os resultados diversificados para formatar a resposta
188
  for idx, (adm, combinacao) in enumerate(final_resultados_diversificados, 1):
189
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
190
  total_credito = sum(c['valor_credito'] for c in combinacao)
@@ -200,14 +240,12 @@ def formatar_resultados(resultados):
200
  taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0
201
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
202
 
203
- #comissao = total_entrada * (8.5 / 108.5)
204
-
205
  resposta += f"""
206
  --- COMBINAÇÃO {idx} ---
207
- �� Administradora: {adm}
208
  🔢 Códigos: {', '.join(codigos)}
209
  💰 Crédito Total: {formatar_moeda(total_credito)}
210
- �� Entrada (+comissão): {formatar_moeda(total_entrada)}
211
  📉 Saldo Devedor: {formatar_moeda(total_saldo)}
212
  💲 Parcela: {formatar_moeda(total_parcelas)}
213
  ⏳ Prazo: {maior_prazo} meses
@@ -220,24 +258,25 @@ def formatar_resultados(resultados):
220
  """
221
  return resposta
222
 
223
- # --- INICIALIZAÇÃO PARA GRADIO ---
224
- # Carrega a planilha UMA VEZ no início do aplicativo
225
- # Se der erro, a função de busca tentará carregar novamente.
226
- if not carregar_planilha():
227
- print("⚠️ Aviso: Planilha não carregada na inicialização. Tentará carregar na primeira busca.")
228
-
229
- # Calcula a entrada adicionando 8,5% ao valor final da entrada.
230
  def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
231
  entrada_calculada = credito_desejado * 0.085 + entrada_desejada
232
  return buscar_combinacoes(credito_desejado, entrada_calculada, parcela_desejada, administradoras)
233
 
234
- # Prepara as opções para o seletor de administradoras do Gradio
235
  def get_administradoras_options():
236
- if df is not None:
237
- adm_list = df[df['tipo_do_bem'].str.lower() == 'auto']['administradora'].unique()
238
- return sorted(adm_list.tolist()) # Converter para lista, pois .unique() pode retornar um numpy array
 
 
 
 
 
239
  return []
240
 
 
 
 
 
241
  # --- Interface Gradio ---
242
  with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
243
  gr.HTML("""
@@ -252,7 +291,6 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
252
  color: black !important;
253
  }
254
 
255
- /* Inputs Numéricos: Crédito, Entrada, Parcela */
256
  input[type="number"] {
257
  background-color: white !important;
258
  border: 2px solid #000 !important;
@@ -262,7 +300,6 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
262
  font-weight: 500;
263
  }
264
 
265
- /* Inputs de texto e áreas de texto (como a saída e código) */
266
  input[type="text"], textarea {
267
  background-color: white !important;
268
  border: 2px solid black !important;
@@ -320,7 +357,7 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
320
  administradoras_multiselect = gr.CheckboxGroup(
321
  label="Filtrar por Administradoras",
322
  choices=administradoras_options,
323
- value=administradoras_options, # Seleciona todas por padrão
324
  interactive=True
325
  )
326
 
@@ -328,16 +365,14 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
328
 
329
  output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...")
330
 
331
- # Conecta o botão à função de busca
332
  submit_btn.click(
333
  fn=ajustar_e_buscar,
334
  inputs=[credito_input, entrada_input, parcela_input, administradoras_multiselect],
335
  outputs=output_text
336
  )
337
 
338
-
339
  gr.Markdown("---")
340
  gr.Markdown("Ferramenta desenvolvida para otimizar a busca por consórcios de automóveis.")
341
 
342
  if __name__ == "__main__":
343
- demo.launch() # Você não precisa disso no Colab, mas sim no arquivo que você fará o deploy
 
3
  import io
4
  import itertools
5
  from collections import defaultdict
6
+ import gradio as gr
7
+ import os
8
+ from unicodedata import normalize
9
 
10
  # 🔧 CONFIGURAÇÕES GLOBAIS
 
 
11
  FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X"
12
  URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
13
 
14
  # Variável global para armazenar o DataFrame
15
  df = None
16
 
17
+ def padronizar_nomes_administradoras(nome):
18
+ """Padroniza nomes de administradoras com mapeamento específico."""
19
+ if pd.isna(nome):
20
+ return nome
21
+
22
+ # Limpeza básica
23
+ nome_limpo = (
24
+ str(nome)
25
+ .strip()
26
+ .lower()
27
+ .replace('$', 's')
28
+ .replace(' ', ' ')
29
+ .strip()
30
+ )
31
+
32
+ # Remove acentos
33
+ nome_limpo = normalize('NFKD', nome_limpo).encode('ASCII', 'ignore').decode('ASCII')
34
+
35
+ # 👇 MAPEAMENTO COMPLETO com suas administradoras corretas
36
+ mapeamento = {
37
+ # Correções de nomes errados → nomes corretos
38
+ 'ademicon': 'Ademicon',
39
+ 'ancora': 'Ancora',
40
+ 'bb': 'Banco do Brasil',
41
+ 'banrisul': 'Banrisul',
42
+ 'bradesco': 'Bradesco',
43
+ 'caixa': 'Caixa XS5',
44
+ 'xs5': 'Caixa XS5',
45
+ 'cnp': 'Caixa CNP',
46
+ 'canopus': 'Canopus',
47
+ 'disal': 'Disal',
48
+ 'embracon': 'Embracon',
49
+ 'ford': 'Ford',
50
+ 'honda': 'Honda',
51
+ 'hs': 'HS',
52
+ 'magalu': 'Magazine Luiza',
53
+ 'maggi': 'Maggi',
54
+ 'mycon': 'Mycon',
55
+ 'porto': 'Porto Seguro',
56
+ 'porto-vp': 'Porto Seguro VP',
57
+ 'remaza': 'Remaza',
58
+ 'rodobens': 'Rodobens',
59
+ 'santander': 'Santander',
60
+ 'scania': 'Scania',
61
+ 'serello': 'Serello',
62
+ 'servopa': 'Servopa',
63
+ 'sicoobu': 'Sicoob Unicoob',
64
+ 'sicredi': 'Sicredi',
65
+ 'sponchiado': 'Sponchiado',
66
+ 'unifisa': 'Unifisa',
67
+ 'volkswagen': 'Volkswagen',
68
+ 'yamaha': 'Yamaha',
69
+ 'zema': 'Zema'
70
+ }
71
+
72
+ return mapeamento.get(nome_limpo, nome_limpo)
73
+
74
+ def verificar_administradoras():
75
+ """Mostra como ficaram as administradoras após a padronização."""
76
+ if df is not None and 'administradora' in df.columns:
77
+ print("🔍 Administradoras únicas após padronização:")
78
+ administradoras_unicas = df[df['tipo_do_bem'].str.lower() == 'auto']['administradora'].unique()
79
+ for i, adm in enumerate(sorted(administradoras_unicas), 1):
80
+ print(f"{i:2d}. {adm}")
81
+ print(f"Total: {len(administradoras_unicas)} administradoras")
82
 
83
  def carregar_planilha():
84
  """Carrega e prepara a planilha para análise de consórcios de automóveis."""
 
93
  # Padroniza nomes das colunas
94
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
95
 
96
+ # 👇 NOVA PADRONIZAÇÃO DAS ADMINISTRADORAS
97
  if 'administradora' in df.columns:
98
+ # Aplica a padronização personalizada
99
+ df['administradora'] = df['administradora'].apply(padronizar_nomes_administradoras)
100
 
101
  # Converte colunas numéricas
102
  numeric_cols = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
 
111
  # Remove duplicatas
112
  df.drop_duplicates(inplace=True)
113
  print("✅ Planilha carregada com sucesso!")
114
+
115
+ # Mostra as administradoras padronizadas
116
+ verificar_administradoras()
117
+
118
  return True
119
 
120
  except Exception as e:
121
  print(f"❌ Erro ao carregar planilha: {str(e)}")
 
122
  return False
123
 
124
  def agrupar_por_administradora(dataframe):
 
140
  if administradoras:
141
  if isinstance(administradoras, tuple):
142
  administradoras = list(administradoras)
143
+ administradoras = [a.strip() for a in administradoras]
144
  df_filtrado = df_filtrado[df_filtrado['administradora'].isin(administradoras)]
145
 
146
  if df_filtrado.empty:
 
153
 
154
  grupos = agrupar_por_administradora(df_filtrado)
155
  resultados = []
156
+ MAX_COMBINACOES_POR_ADM = 50
157
 
158
  for adm, cotas in grupos.items():
159
+ cotas = cotas[:25]
160
  combinacoes_validas = 0
161
 
162
+ for r in range(1, min(6, len(cotas)) + 1):
163
  for combinacao in itertools.combinations(cotas, r):
164
  if combinacoes_validas >= MAX_COMBINACOES_POR_ADM:
165
  break
 
183
  if not resultados:
184
  return "Nenhuma combinação adequada encontrada para automóveis com os parâmetros fornecidos."
185
 
 
186
  resultados.sort(key=lambda x: x[2])
 
 
187
  resultados = resultados[:250]
188
 
189
  return formatar_resultados(resultados)
 
195
  """Formata os resultados para exibição."""
196
  resposta = ""
197
 
 
 
 
198
  grouped_by_adm = defaultdict(list)
199
  for adm, combinacao, diferenca in resultados:
200
  grouped_by_adm[adm].append((combinacao, diferenca))
201
 
 
202
  for adm in grouped_by_adm:
203
+ grouped_by_adm[adm].sort(key=lambda x: x[1])
204
 
205
  final_resultados_diversificados = []
 
 
 
206
  adm_indices = {adm: 0 for adm in grouped_by_adm.keys()}
 
 
207
  active_adms = list(grouped_by_adm.keys())
208
+ current_adm_idx = 0
209
 
 
 
 
210
  while len(final_resultados_diversificados) < 15 and active_adms:
 
211
  adm_da_vez = active_adms[current_adm_idx]
 
 
212
  idx_na_adm = adm_indices[adm_da_vez]
213
 
 
214
  if idx_na_adm < len(grouped_by_adm[adm_da_vez]):
 
215
  combinacao_data, _ = grouped_by_adm[adm_da_vez][idx_na_adm]
 
 
216
  final_resultados_diversificados.append((adm_da_vez, combinacao_data))
 
 
217
  adm_indices[adm_da_vez] += 1
218
  else:
 
219
  active_adms.pop(current_adm_idx)
 
220
  if current_adm_idx >= len(active_adms) and active_adms:
221
+ current_adm_idx = 0
222
 
223
+ if active_adms:
 
224
  current_adm_idx = (current_adm_idx + 1) % len(active_adms)
225
  else:
226
+ break
227
 
 
228
  for idx, (adm, combinacao) in enumerate(final_resultados_diversificados, 1):
229
  codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
230
  total_credito = sum(c['valor_credito'] for c in combinacao)
 
240
  taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0
241
  taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
242
 
 
 
243
  resposta += f"""
244
  --- COMBINAÇÃO {idx} ---
245
+ 🏢 Administradora: {adm.upper()}
246
  🔢 Códigos: {', '.join(codigos)}
247
  💰 Crédito Total: {formatar_moeda(total_credito)}
248
+ 💵 Entrada (+comissão): {formatar_moeda(total_entrada)}
249
  📉 Saldo Devedor: {formatar_moeda(total_saldo)}
250
  💲 Parcela: {formatar_moeda(total_parcelas)}
251
  ⏳ Prazo: {maior_prazo} meses
 
258
  """
259
  return resposta
260
 
 
 
 
 
 
 
 
261
  def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
262
  entrada_calculada = credito_desejado * 0.085 + entrada_desejada
263
  return buscar_combinacoes(credito_desejado, entrada_calculada, parcela_desejada, administradoras)
264
 
 
265
  def get_administradoras_options():
266
+ """Obtém lista de administradoras únicas e padronizadas para o frontend."""
267
+ if df is not None and 'administradora' in df.columns:
268
+ adm_list = (
269
+ df[df['tipo_do_bem'].str.lower() == 'auto']['administradora']
270
+ .dropna()
271
+ .unique()
272
+ )
273
+ return sorted(adm_list.tolist())
274
  return []
275
 
276
+ # --- INICIALIZAÇÃO PARA GRADIO ---
277
+ if not carregar_planilha():
278
+ print("⚠️ Aviso: Planilha não carregada na inicialização. Tentará carregar na primeira busca.")
279
+
280
  # --- Interface Gradio ---
281
  with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
282
  gr.HTML("""
 
291
  color: black !important;
292
  }
293
 
 
294
  input[type="number"] {
295
  background-color: white !important;
296
  border: 2px solid #000 !important;
 
300
  font-weight: 500;
301
  }
302
 
 
303
  input[type="text"], textarea {
304
  background-color: white !important;
305
  border: 2px solid black !important;
 
357
  administradoras_multiselect = gr.CheckboxGroup(
358
  label="Filtrar por Administradoras",
359
  choices=administradoras_options,
360
+ value=administradoras_options,
361
  interactive=True
362
  )
363
 
 
365
 
366
  output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...")
367
 
 
368
  submit_btn.click(
369
  fn=ajustar_e_buscar,
370
  inputs=[credito_input, entrada_input, parcela_input, administradoras_multiselect],
371
  outputs=output_text
372
  )
373
 
 
374
  gr.Markdown("---")
375
  gr.Markdown("Ferramenta desenvolvida para otimizar a busca por consórcios de automóveis.")
376
 
377
  if __name__ == "__main__":
378
+ demo.launch()