Arthur-Attili commited on
Commit
e7f2c42
·
verified ·
1 Parent(s): ad44ff2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +235 -268
app.py CHANGED
@@ -2,235 +2,171 @@ import os
2
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
3
  os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
4
 
5
- import pandas as pd
6
  import requests
7
  import json
8
- from collections import defaultdict
9
- import gradio as gr
10
  from datetime import datetime
11
- import hashlib
12
 
13
  # ==================== CONFIGURAÇÕES ====================
14
  API_BASE_URL = "https://api.maxconconsorcios.com.br"
15
  WHATSAPP_NUMBER = "5516997865738"
16
 
17
- # Cache para evitar muitas requisições
18
  cache = {}
19
- CACHE_TTL = 300 # 5 minutos
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  # ==================== FUNÇÕES AUXILIARES ====================
22
  def formatar_moeda(valor):
23
  """Formata valor para moeda brasileira"""
24
- if valor is None:
25
  return "R$ 0,00"
26
  try:
27
- return f"R$ {float(valor):,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
 
28
  except:
29
  return "R$ 0,00"
30
 
31
- def get_cache_key(prefix, *args, **kwargs):
32
- """Gera chave de cache"""
33
- key_data = {'prefix': prefix, 'args': args, 'kwargs': kwargs}
34
- key_string = json.dumps(key_data, sort_keys=True)
35
- return f"{prefix}_{hashlib.md5(key_string.encode()).hexdigest()}"
36
-
37
- def is_cache_valid(key):
38
- """Verifica se cache é válido"""
39
- if key not in cache:
40
- return False
41
- timestamp, _ = cache[key]
42
- return (datetime.now() - timestamp).total_seconds() < CACHE_TTL
43
-
44
- def set_cache(key, value):
45
- """Armazena no cache"""
46
- cache[key] = (datetime.now(), value)
47
 
48
- def get_cache(key):
49
- """Recupera do cache"""
50
- if is_cache_valid(key):
51
- return cache[key][1]
52
- return None
 
53
 
54
- # ==================== BUSCA DE COTAS VIA SUPABASE ====================
55
- def buscar_cotas_supabase(filtros=None):
56
- """
57
- Busca cotas diretamente do Supabase através da API
58
- (mesma fonte do backend da Foto 1)
59
- """
60
- cache_key = get_cache_key('cotas', filtros)
61
- cached = get_cache(cache_key)
62
- if cached:
63
- print("📦 Cache HIT para cotas")
64
- return cached
65
-
66
  try:
67
- print(f"🔍 Buscando cotas na API: {API_BASE_URL}/api/cotas")
68
-
69
- # Prepara payload para a API
70
- payload = {}
71
- if filtros:
72
- if filtros.get('categoria'):
73
- payload['categoria'] = filtros['categoria']
74
- if filtros.get('valor_credito_min'):
75
- payload['valor_credito'] = filtros['valor_credito_min']
76
- if filtros.get('valor_parcela_max'):
77
- payload['valor_parcela'] = filtros['valor_parcela_max']
78
- if filtros.get('administradora_id'):
79
- payload['administradora_id'] = filtros['administradora_id']
80
-
81
  response = requests.post(
82
  f"{API_BASE_URL}/api/cotas",
83
- json=payload,
84
  headers={'Content-Type': 'application/json'},
85
  timeout=30
86
  )
87
 
88
  if response.status_code == 200:
89
  cotas = response.json()
90
- print(f"✅ {len(cotas)} cotas encontradas")
91
- set_cache(cache_key, cotas)
92
  return cotas
93
  else:
94
  print(f"❌ Erro na API: {response.status_code}")
95
  return []
96
 
 
 
 
 
 
 
97
  except Exception as e:
98
  print(f"❌ Erro ao buscar cotas: {str(e)}")
99
  return []
100
 
101
- def get_administradoras_list():
102
- """Obtém lista de administradoras disponíveis"""
103
- cache_key = get_cache_key('administradoras')
104
- cached = get_cache(cache_key)
105
- if cached:
106
- return cached
 
 
 
107
 
108
- cotas = buscar_cotas_supabase()
109
- administradoras = set()
 
 
110
 
111
- for cota in cotas:
112
- admin_id = cota.get('administradora_id')
113
- if admin_id:
114
- administradoras.add(get_administradora_nome(admin_id))
115
 
116
- resultado = sorted(list(administradoras))
117
- set_cache(cache_key, resultado)
118
- return resultado
119
-
120
- def get_administradora_nome(admin_id):
121
- """Mapeia ID da administradora para nome"""
122
- admin_map = {
123
- 1:'Itaú', 2:'Bradesco', 3:'Santander', 4:'Honda', 5:'Volkswagen',
124
- 6:'Serello', 7:'Ademicon', 8:'Remaza', 9:'Porto Seguro VP',
125
- 10:'Banrisul', 11:'Embracon', 12:'Serrana', 13:'Caixa XS5',
126
- 14:'Caixa CNP', 15:'Unifisa', 16:'HS Consorcios', 17:'Caixa Seguradora',
127
- 18:'Porto Seguro', 19:'Mycon', 20:'Rodobens', 21:'Ford',
128
- 22:'Sicoob Unicoob', 23:'Zema', 24:'Maggi', 25:'Ancora', 26:'Yamaha',
129
- 27:'Sponchiado', 28:'Scania', 29:'Disal', 30:'Servopa', 31:'Canopus',
130
- 32:'Bradesco', 33:'Banco do Brasil', 34:'Santander', 35:'Sicredi'
131
- }
132
- return admin_map.get(admin_id, 'Não especificado')
133
 
134
- def calcular_entrada_total(cota):
135
- """Calcula entrada total incluindo comissão de 8.5%"""
136
- credito = float(cota.get('valor_credito', 0))
137
- entrada = float(cota.get('entrada', 0))
138
- comissao = credito * 0.085
139
- return entrada + comissao
140
 
141
- def formatar_tempo_parcelas(cota):
142
- """Formata o tempo de parcelas com escalonamento (1-21x e 22-27x)"""
143
- parcelas = int(cota.get('parcelas', 0))
144
- valor_parcela = float(cota.get('valor_parcela', 0))
145
 
146
- if parcelas == 0:
147
- return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
- resultado = []
 
 
 
150
 
151
- # Primeiro período: 1x à 21x
152
- primeiro_periodo = min(21, parcelas)
153
- resultado.append(f"{1}x à {primeiro_periodo}x: {formatar_moeda(valor_parcela)}/mês")
 
154
 
155
- # Segundo período: se houver mais de 21 parcelas
156
- if parcelas > 21:
157
- # Valor reduzido (aproximadamente 44% do valor original)
158
- valor_reduzido = valor_parcela * 0.44
159
- resultado.append(f"{22}x à {parcelas}x: {formatar_moeda(valor_reduzido)}/mês")
160
 
161
- return "\\n".join(resultado)
 
162
 
163
- # ==================== FUNÇÃO PRINCIPAL DE BUSCA ====================
164
- def buscar_combinacoes(credito, entrada, parcela, adm_selecionadas):
165
- """
166
- Busca combinações de cotas usando a API real do Supabase
167
- """
168
- if not credito and not entrada and not parcela:
169
- return "❌ Informe pelo menos um valor (Crédito, Entrada ou Parcela)"
170
 
171
- try:
172
- # Converte valores para float
173
- credito_val = float(credito) if credito else 0
174
- entrada_val = float(entrada) if entrada else 0
175
- parcela_val = float(parcela) if parcela else 0
176
-
177
- print(f"🔍 Buscando - Crédito: R$ {credito_val:,.2f}, Entrada: R$ {entrada_val:,.2f}, Parcela: R$ {parcela_val:,.2f}")
178
-
179
- # Busca cotas na API
180
- cotas = buscar_cotas_supabase()
181
-
182
- if not cotas:
183
- return "❌ Erro ao carregar cotas. Tente novamente mais tarde."
184
-
185
- # Filtra cotas disponíveis
186
- cotas = [c for c in cotas if c.get('reserva') != 'reservado']
187
-
188
- # Filtra por administradora
189
- if adm_selecionadas:
190
- adm_ids = []
191
- for adm_nome in adm_selecionadas:
192
- for admin_id, nome in get_admin_map().items():
193
- if nome.lower() == adm_nome.lower():
194
- adm_ids.append(admin_id)
195
- break
196
-
197
- if adm_ids:
198
- cotas = [c for c in cotas if c.get('administradora_id') in adm_ids]
199
-
200
- # Filtra por crédito mínimo
201
- if credito_val > 0:
202
- cotas = [c for c in cotas if float(c.get('valor_credito', 0)) >= credito_val]
203
-
204
- # Filtra por parcela máxima
205
- if parcela_val > 0:
206
- cotas = [c for c in cotas if float(c.get('valor_parcela', 0)) <= parcela_val]
207
-
208
- # Filtra por entrada máxima
209
- if entrada_val > 0:
210
- cotas = [c for c in cotas if calcular_entrada_total(c) <= entrada_val]
211
-
212
- # Ordena por crédito (maior para menor)
213
- cotas.sort(key=lambda x: float(x.get('valor_credito', 0)), reverse=True)
214
-
215
- if not cotas:
216
- return "🚫 Nenhuma cota encontrada com os filtros selecionados."
217
-
218
- # Limita para exibição
219
- cotas = cotas[:15]
220
-
221
- # Formata resultados
222
- return formatar_resultados(cotas)
223
-
224
- except Exception as e:
225
- print(f"❌ Erro na busca: {str(e)}")
226
- return f"❌ Erro ao buscar combinações: {str(e)}"
227
 
228
  def formatar_resultados(cotas):
229
  """Formata os resultados para exibição"""
230
  if not cotas:
231
- return "🚫 Nenhuma combinação encontrada."
232
 
233
- resposta = ""
234
 
235
  for idx, cota in enumerate(cotas, 1):
236
  adm_nome = get_administradora_nome(cota.get('administradora_id'))
@@ -247,7 +183,7 @@ def formatar_resultados(cotas):
247
  taxa_total = saldo - credito_real
248
  taxa_percentual = (taxa_total / credito_real * 100) if credito_real > 0 else 0
249
 
250
- # Taxas mensal e anual
251
  if prazo > 0:
252
  taxa_mensal = taxa_percentual / prazo
253
  taxa_anual = taxa_mensal * 12
@@ -255,113 +191,144 @@ def formatar_resultados(cotas):
255
  taxa_mensal = 0
256
  taxa_anual = 0
257
 
258
- tempo_parcelas = formatar_tempo_parcelas(cota)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
- resposta += f"""
261
- --- COMBINAÇÃO {idx} ---
262
- 🏢 Administradora: {adm_nome}
263
- 🔢 Código: {codigo}
264
- 💰 Crédito Total: {formatar_moeda(credito)}
265
- 💵 Entrada Total: {formatar_moeda(entrada_total)}
266
- 📉 Saldo Devedor: {formatar_moeda(saldo)}
267
- 💲 Parcela: {formatar_moeda(parcela)}
268
- ⏳ Prazo: {prazo} meses
269
- 🔚 Valor Final: {formatar_moeda(valor_final)}
270
- 💠 Crédito Líquido: {formatar_moeda(credito_real)}
271
- 📊 Taxa Total: {taxa_percentual:.2f}% ({formatar_moeda(taxa_total)})
272
- 🗓️ Taxa Mensal: {taxa_mensal:.2f}%
273
- 🗓️ Taxa Anual: {taxa_anual:.2f}%
274
- ⏰ TEMPO DE PARCELAS:
275
- {tempo_parcelas if tempo_parcelas else "Não disponível"}
276
  """
277
 
278
- return resposta
 
 
279
 
280
- def get_admin_map():
281
- """Retorna mapeamento de administradoras"""
282
- return {
283
- 1: 'Itaú', 2: 'Bradesco', 3: 'Santander', 4: 'Honda', 5: 'Volkswagen',
284
- 6: 'Serello', 7: 'Ademicon', 8: 'Remaza', 9: 'Porto Seguro VP',
285
- 10: 'Banrisul', 11: 'Embracon', 12: 'Serrana', 13: 'Caixa XS5',
286
- 14: 'Caixa CNP', 15: 'Unifisa', 16: 'HS Consorcios', 17: 'Caixa Seguradora',
287
- 18: 'Porto Seguro', 19: 'Mycon', 20: 'Rodobens', 21: 'Ford',
288
- 22: 'Sicoob Unicoob', 23: 'Zema', 24: 'Maggi', 25: 'Ancora', 26: 'Yamaha',
289
- 27: 'Sponchiado', 28: 'Scania', 29: 'Disal', 30: 'Servopa', 31: 'Canopus',
290
- 32: 'Bradesco', 33: 'Banco do Brasil', 34: 'Santander', 35: 'Sicredi'
291
- }
292
 
293
- # ==================== INTERFACE GRADIO ====================
294
  def obter_administradoras():
295
- """Obtém lista de administradoras para o dropdown"""
296
  try:
297
- return get_administradoras_list()
 
 
 
 
 
 
298
  except:
299
- return ["Porto Seguro", "Itaú", "Bradesco", "Santander", "Banco do Brasil"]
 
300
 
301
- with gr.Blocks(title="🚗 MAXCON - Busca de Consórcio Auto", theme=gr.themes.Soft()) as demo_auto:
302
- gr.HTML("""
303
- <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #1a3a6c 0%, #2a5a9c 100%); border-radius: 12px; margin-bottom: 20px;">
304
- <h1 style="color: white; margin: 0;">🚗 MAXCON Consórcios</h1>
305
- <p style="color: #ffd166; margin: 10px 0 0 0;">Encontre as melhores cotas para seu veículo</p>
306
- </div>
307
- """)
308
-
309
- with gr.Row():
310
- with gr.Column(scale=1):
311
- credito_input = gr.Number(
312
- label="💰 Crédito Desejado (mínimo)",
313
- value=100000,
314
- step=1000,
315
- precision=2
316
- )
317
- entrada_input = gr.Number(
318
- label="💵 Entrada (máximo)",
319
- value=50000,
320
- step=1000,
321
- precision=2
322
- )
323
- parcela_input = gr.Number(
324
- label="💲 Parcela (máximo)",
325
- value=2000,
326
- step=100,
327
- precision=2
328
- )
329
-
330
- adm_input = gr.Dropdown(
331
- choices=obter_administradoras(),
332
- label="🏢 Administradoras",
333
- multiselect=True,
334
- interactive=True
335
- )
 
 
 
 
 
 
 
 
 
 
336
 
337
- buscar_btn = gr.Button("🔍 Buscar Combinações", variant="primary", size="lg")
 
 
 
 
338
 
339
- with gr.Column(scale=2):
340
- output_text = gr.Textbox(
341
- label="📋 Resultados",
342
- lines=25,
343
- max_lines=40,
344
- interactive=False,
345
- placeholder="Os resultados aparecerão aqui após a busca..."
346
- )
347
-
348
- buscar_btn.click(
349
- fn=buscar_combinacoes,
350
- inputs=[credito_input, entrada_input, parcela_input, adm_input],
351
- outputs=output_text
352
- )
353
-
354
- gr.Markdown("""
355
- ---
356
- ### 📌 Sobre a Busca
 
 
 
 
 
 
 
 
 
357
 
358
- - Os resultados são buscados **em tempo real** do nosso banco de dados
359
- - Valores atualizados diariamente
360
- - Entre em contato pelo WhatsApp para negociar
361
- """)
362
 
363
  # ==================== INICIALIZAÇÃO ====================
364
  if __name__ == "__main__":
365
  print("🚀 Iniciando servidor MAXCON no Hugging Face...")
366
  print(f"📡 API Base: {API_BASE_URL}")
367
- demo_auto.launch()
 
 
2
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
3
  os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
4
 
5
+ import gradio as gr
6
  import requests
7
  import json
 
 
8
  from datetime import datetime
9
+ import time
10
 
11
  # ==================== CONFIGURAÇÕES ====================
12
  API_BASE_URL = "https://api.maxconconsorcios.com.br"
13
  WHATSAPP_NUMBER = "5516997865738"
14
 
15
+ # Cache simples
16
  cache = {}
17
+
18
+ # ==================== MAPEAMENTO DE ADMINISTRADORAS ====================
19
+ ADMIN_MAP = {
20
+ 1: 'Itaú', 2: 'Bradesco', 3: 'Santander', 4: 'Honda', 5: 'Volkswagen',
21
+ 6: 'Serello', 7: 'Ademicon', 8: 'Remaza', 9: 'Porto Seguro VP',
22
+ 10: 'Banrisul', 11: 'Embracon', 12: 'Serrana', 13: 'Caixa XS5',
23
+ 14: 'Caixa CNP', 15: 'Unifisa', 16: 'HS Consorcios', 17: 'Caixa Seguradora',
24
+ 18: 'Porto Seguro', 19: 'Mycon', 20: 'Rodobens', 21: 'Ford',
25
+ 22: 'Sicoob Unicoob', 23: 'Zema', 24: 'Maggi', 25: 'Ancora', 26: 'Yamaha',
26
+ 27: 'Sponchiado', 28: 'Scania', 29: 'Disal', 30: 'Servopa', 31: 'Canopus',
27
+ 32: 'Bradesco', 33: 'Banco do Brasil', 34: 'Santander', 35: 'Sicredi'
28
+ }
29
 
30
  # ==================== FUNÇÕES AUXILIARES ====================
31
  def formatar_moeda(valor):
32
  """Formata valor para moeda brasileira"""
33
+ if valor is None or valor == '':
34
  return "R$ 0,00"
35
  try:
36
+ num = float(valor)
37
+ return f"R$ {num:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
38
  except:
39
  return "R$ 0,00"
40
 
41
+ def get_administradora_nome(admin_id):
42
+ """Retorna nome da administradora pelo ID"""
43
+ return ADMIN_MAP.get(admin_id, 'Não especificado')
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ def calcular_entrada_total(cota):
46
+ """Calcula entrada total incluindo comissão de 8.5%"""
47
+ credito = float(cota.get('valor_credito', 0))
48
+ entrada = float(cota.get('entrada', 0))
49
+ comissao = credito * 0.085
50
+ return entrada + comissao
51
 
52
+ # ==================== BUSCA DE COTAS ====================
53
+ def buscar_cotas_da_api():
54
+ """Busca cotas diretamente da API do Supabase"""
 
 
 
 
 
 
 
 
 
55
  try:
56
+ print("🔄 Buscando cotas na API...")
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  response = requests.post(
58
  f"{API_BASE_URL}/api/cotas",
59
+ json={},
60
  headers={'Content-Type': 'application/json'},
61
  timeout=30
62
  )
63
 
64
  if response.status_code == 200:
65
  cotas = response.json()
66
+ print(f"✅ {len(cotas)} cotas carregadas")
 
67
  return cotas
68
  else:
69
  print(f"❌ Erro na API: {response.status_code}")
70
  return []
71
 
72
+ except requests.exceptions.Timeout:
73
+ print("❌ Timeout na conexão com a API")
74
+ return []
75
+ except requests.exceptions.ConnectionError:
76
+ print("❌ Erro de conexão com a API")
77
+ return []
78
  except Exception as e:
79
  print(f"❌ Erro ao buscar cotas: {str(e)}")
80
  return []
81
 
82
+ def buscar_combinacoes(credito, entrada, parcela, admin_selecionadas):
83
+ """
84
+ Função principal de busca de combinações
85
+ """
86
+ # Validação dos inputs
87
+ try:
88
+ credito_val = float(credito) if credito else 0
89
+ except:
90
+ credito_val = 0
91
 
92
+ try:
93
+ entrada_val = float(entrada) if entrada else 0
94
+ except:
95
+ entrada_val = 0
96
 
97
+ try:
98
+ parcela_val = float(parcela) if parcela else 0
99
+ except:
100
+ parcela_val = 0
101
 
102
+ if credito_val == 0 and entrada_val == 0 and parcela_val == 0:
103
+ return "❌ Informe pelo menos um valor (Crédito, Entrada ou Parcela)"
104
+
105
+ # Busca cotas
106
+ cotas = buscar_cotas_da_api()
107
+
108
+ if not cotas:
109
+ return """❌ **Erro ao carregar dados**
 
 
 
 
 
 
 
 
 
110
 
111
+ Não foi possível conectar ao servidor. Por favor, tente novamente em alguns instantes.
 
 
 
 
 
112
 
113
+ Se o erro persistir, entre em contato pelo WhatsApp."""
 
 
 
114
 
115
+ # Filtra cotas disponíveis
116
+ cotas_disponiveis = [c for c in cotas if c.get('reserva') != 'reservado']
117
+ print(f"📊 Cotas disponíveis: {len(cotas_disponiveis)}")
118
+
119
+ # Filtra por administradora
120
+ if admin_selecionadas and len(admin_selecionadas) > 0:
121
+ adm_ids_filtro = []
122
+ for admin_nome in admin_selecionadas:
123
+ for admin_id, nome in ADMIN_MAP.items():
124
+ if nome.lower() == admin_nome.lower():
125
+ adm_ids_filtro.append(admin_id)
126
+ break
127
+
128
+ if adm_ids_filtro:
129
+ cotas_disponiveis = [c for c in cotas_disponiveis if c.get('administradora_id') in adm_ids_filtro]
130
+ print(f"📊 Após filtro de administradora: {len(cotas_disponiveis)}")
131
 
132
+ # Filtra por crédito mínimo
133
+ if credito_val > 0:
134
+ cotas_disponiveis = [c for c in cotas_disponiveis if float(c.get('valor_credito', 0)) >= credito_val]
135
+ print(f"📊 Após filtro de crédito (≥ R$ {credito_val:,.2f}): {len(cotas_disponiveis)}")
136
 
137
+ # Filtra por parcela máxima
138
+ if parcela_val > 0:
139
+ cotas_disponiveis = [c for c in cotas_disponiveis if float(c.get('valor_parcela', 0)) <= parcela_val]
140
+ print(f"📊 Após filtro de parcela (≤ R$ {parcela_val:,.2f}): {len(cotas_disponiveis)}")
141
 
142
+ # Filtra por entrada máxima
143
+ if entrada_val > 0:
144
+ cotas_disponiveis = [c for c in cotas_disponiveis if calcular_entrada_total(c) <= entrada_val]
145
+ print(f"📊 Após filtro de entrada (≤ R$ {entrada_val:,.2f}): {len(cotas_disponiveis)}")
 
146
 
147
+ if not cotas_disponiveis:
148
+ return """🚫 **Nenhuma cota encontrada**
149
 
150
+ Tente ajustar os filtros:
151
+ - Aumente o valor do crédito
152
+ - Diminua o valor da entrada
153
+ - Diminua o valor da parcela
154
+ - Selecione mais administradoras"""
 
 
155
 
156
+ # Ordena por crédito (do maior para o menor)
157
+ cotas_disponiveis.sort(key=lambda x: float(x.get('valor_credito', 0)), reverse=True)
158
+
159
+ # Limita para exibição (máximo 15 resultados)
160
+ resultados = cotas_disponiveis[:15]
161
+
162
+ return formatar_resultados(resultados)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  def formatar_resultados(cotas):
165
  """Formata os resultados para exibição"""
166
  if not cotas:
167
+ return "🚫 Nenhum resultado encontrado."
168
 
169
+ resultado = f"## 🎯 Resultados encontrados: {len(cotas)} cota(s)\n\n"
170
 
171
  for idx, cota in enumerate(cotas, 1):
172
  adm_nome = get_administradora_nome(cota.get('administradora_id'))
 
183
  taxa_total = saldo - credito_real
184
  taxa_percentual = (taxa_total / credito_real * 100) if credito_real > 0 else 0
185
 
186
+ # Taxas
187
  if prazo > 0:
188
  taxa_mensal = taxa_percentual / prazo
189
  taxa_anual = taxa_mensal * 12
 
191
  taxa_mensal = 0
192
  taxa_anual = 0
193
 
194
+ resultado += f"""
195
+ ### 📌 Combinação {idx}
196
+
197
+ | Campo | Valor |
198
+ |-------|-------|
199
+ | 🏢 **Administradora** | {adm_nome} |
200
+ | 🔢 **Código** | {codigo} |
201
+ | 💰 **Crédito** | {formatar_moeda(credito)} |
202
+ | 💵 **Entrada** | {formatar_moeda(entrada_total)} |
203
+ | 💲 **Parcela** | {formatar_moeda(parcela)} |
204
+ | ⏳ **Prazo** | {prazo} meses |
205
+ | 📉 **Saldo Devedor** | {formatar_moeda(saldo)} |
206
+ | 🔚 **Valor Final** | {formatar_moeda(valor_final)} |
207
+ | 💠 **Crédito Líquido** | {formatar_moeda(credito_real)} |
208
+ | 📊 **Taxa Total** | {taxa_percentual:.2f}% ({formatar_moeda(taxa_total)}) |
209
+ | 🗓️ **Taxa Mensal** | {taxa_mensal:.2f}% |
210
+ | 🗓️ **Taxa Anual** | {taxa_anual:.2f}% |
211
+
212
+ **⏰ Tempo de Parcelas:**
213
+ - 1x à {min(21, prazo)}x: {formatar_moeda(parcela)}/mês
214
+ """
215
+ if prazo > 21:
216
+ valor_reduzido = parcela * 0.44
217
+ resultado += f"- {22}x à {prazo}x: {formatar_moeda(valor_reduzido)}/mês\n"
218
 
219
+ resultado += f"""
220
+ ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  """
222
 
223
+ resultado += f"""
224
+ ### 📞 **Interessado?**
225
+ Entre em contato pelo WhatsApp para negociar esta cota:
226
 
227
+ [![WhatsApp](https://img.shields.io/badge/WhatsApp-25D366?style=for-the-badge&logo=whatsapp&logoColor=white)](https://wa.me/{WHATSAPP_NUMBER}?text=Olá! Tenho interesse nas cotas de consórcio)
228
+ """
229
+
230
+ return resultado
 
 
 
 
 
 
 
 
231
 
232
+ # ==================== FUNÇÃO PARA OBTER ADMINISTRADORAS ====================
233
  def obter_administradoras():
234
+ """Obtém lista de administradoras disponíveis"""
235
  try:
236
+ cotas = buscar_cotas_da_api()
237
+ adm_set = set()
238
+ for cota in cotas:
239
+ admin_id = cota.get('administradora_id')
240
+ if admin_id:
241
+ adm_set.add(get_administradora_nome(admin_id))
242
+ return sorted(list(adm_set))
243
  except:
244
+ # Lista fallback caso não consiga carregar
245
+ return ["Porto Seguro", "Itaú", "Bradesco", "Santander", "Banco do Brasil", "Honda", "Yamaha"]
246
 
247
+ # ==================== INTERFACE GRADIO ====================
248
+ def create_interface():
249
+ with gr.Blocks(title="MAXCON - Consórcio Auto", theme=gr.themes.Soft()) as demo:
250
+ gr.HTML("""
251
+ <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #1a3a6c 0%, #2a5a9c 100%); border-radius: 12px; margin-bottom: 20px;">
252
+ <h1 style="color: white; margin: 0;">🚗 MAXCON Consórcios</h1>
253
+ <p style="color: #ffd166; margin: 10px 0 0 0;">Encontre as melhores cotas para seu veículo</p>
254
+ </div>
255
+ """)
256
+
257
+ with gr.Row():
258
+ with gr.Column(scale=1):
259
+ credito_input = gr.Number(
260
+ label="💰 Crédito Desejado (mínimo)",
261
+ value=100000,
262
+ step=1000,
263
+ precision=2
264
+ )
265
+ entrada_input = gr.Number(
266
+ label="💵 Entrada (máximo)",
267
+ value=50000,
268
+ step=1000,
269
+ precision=2
270
+ )
271
+ parcela_input = gr.Number(
272
+ label="💲 Parcela (máximo)",
273
+ value=2000,
274
+ step=100,
275
+ precision=2
276
+ )
277
+
278
+ # Carrega administradoras dinamicamente
279
+ admin_choices = obter_administradoras()
280
+ adm_input = gr.Dropdown(
281
+ choices=admin_choices,
282
+ label="🏢 Administradoras",
283
+ multiselect=True,
284
+ value=admin_choices[:5] if len(admin_choices) > 5 else admin_choices,
285
+ interactive=True
286
+ )
287
+
288
+ buscar_btn = gr.Button("🔍 Buscar Combinações", variant="primary", size="lg")
289
+
290
+ # Botão de limpar
291
+ limpar_btn = gr.Button("🗑️ Limpar Filtros", variant="secondary", size="lg")
292
 
293
+ with gr.Column(scale=2):
294
+ output_text = gr.Markdown(
295
+ value="### 🔍 Aguardando busca...\n\nPreencha os filtros ao lado e clique em 'Buscar Combinações'",
296
+ label="Resultados"
297
+ )
298
 
299
+ # Eventos
300
+ buscar_btn.click(
301
+ fn=buscar_combinacoes,
302
+ inputs=[credito_input, entrada_input, parcela_input, adm_input],
303
+ outputs=output_text
304
+ )
305
+
306
+ def limpar_filtros():
307
+ return [0, 0, 0, []]
308
+
309
+ limpar_btn.click(
310
+ fn=limpar_filtros,
311
+ outputs=[credito_input, entrada_input, parcela_input, adm_input]
312
+ )
313
+
314
+ gr.Markdown("""
315
+ ---
316
+ ### 📌 Sobre a Busca
317
+
318
+ - Os resultados são buscados **em tempo real** do nosso banco de dados
319
+ - Valores atualizados diariamente
320
+ - **Comissão de 8.5%** já incluída no valor da entrada
321
+ - Entre em contato pelo WhatsApp para negociar
322
+
323
+ ---
324
+ **MAXCON Assessoria** - Realizando sonhos há mais de 5 anos
325
+ """)
326
 
327
+ return demo
 
 
 
328
 
329
  # ==================== INICIALIZAÇÃO ====================
330
  if __name__ == "__main__":
331
  print("🚀 Iniciando servidor MAXCON no Hugging Face...")
332
  print(f"📡 API Base: {API_BASE_URL}")
333
+ demo = create_interface()
334
+ demo.launch(server_name="0.0.0.0", server_port=7860)