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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -100
app.py CHANGED
@@ -3,82 +3,18 @@ import requests
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,10 +29,10 @@ def carregar_planilha():
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,14 +47,11 @@ def carregar_planilha():
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,7 +73,7 @@ def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, adm
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,13 +86,13 @@ def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, adm
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,7 +116,10 @@ def buscar_combinacoes(credito_desejado, entrada_desejada, parcela_desejada, adm
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,36 +131,60 @@ def 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,12 +200,14 @@ def formatar_resultados(resultados):
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,25 +220,24 @@ def formatar_resultados(resultados):
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,6 +252,7 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
291
  color: black !important;
292
  }
293
 
 
294
  input[type="number"] {
295
  background-color: white !important;
296
  border: 2px solid #000 !important;
@@ -300,6 +262,7 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
300
  font-weight: 500;
301
  }
302
 
 
303
  input[type="text"], textarea {
304
  background-color: white !important;
305
  border: 2px solid black !important;
@@ -357,7 +320,7 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
357
  administradoras_multiselect = gr.CheckboxGroup(
358
  label="Filtrar por Administradoras",
359
  choices=administradoras_options,
360
- value=administradoras_options,
361
  interactive=True
362
  )
363
 
@@ -365,14 +328,16 @@ with gr.Blocks(title="Sistema de Busca de Consórcio de Automóveis") as demo:
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()
 
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
  # 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
  # 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
  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
 
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
  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
  """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
  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
  """
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
  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
  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
  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
 
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