jcalbornoz commited on
Commit
7b2e39a
·
verified ·
1 Parent(s): 98092ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -55
app.py CHANGED
@@ -13,7 +13,7 @@ try:
13
  subprocess.run(["playwright", "install", "chromium"], check=True)
14
  except: pass
15
 
16
- # --- 1. GENERADOR DE URLS (Tu lógica exacta) ---
17
  def construir_urls_final(zona, ciudad, tipo, hab, ban, park, antiguedad):
18
  # Mapeo Antigüedad
19
  mapa_ant = {
@@ -24,11 +24,7 @@ def construir_urls_final(zona, ciudad, tipo, hab, ban, park, antiguedad):
24
  "Más de 30 años": "mas-de-30-anos"
25
  }
26
  slug_ant = mapa_ant.get(antiguedad, "de-1-a-8-anios")
27
-
28
- # Lógica singular/plural
29
  slug_park = f"{int(park)}-parqueadero" if int(park) == 1 else f"{int(park)}-parqueaderos"
30
-
31
- # Searchstring
32
  query_geo = f"{zona.lower().replace(' ', '-')}-{ciudad.lower().replace(' ', '-')}"
33
 
34
  # URL FINCA RAÍZ
@@ -40,55 +36,71 @@ def construir_urls_final(zona, ciudad, tipo, hab, ban, park, antiguedad):
40
 
41
  return url_fr, url_mc
42
 
43
- # --- 2. GENERADOR PDF CON URLS VISIBLES ---
44
- def generar_pdf_con_fuentes(zona, df, url_fr, url_mc):
45
- pdf_path = f"Reporte_{int(time.time())}.pdf"
 
 
 
 
 
 
46
  pdf = FPDF()
47
  pdf.add_page()
48
 
49
- # Título
50
- pdf.set_font("Arial", 'B', 14)
51
- pdf.cell(0, 10, f"ESTUDIO DE MERCADO: {zona.upper()}", ln=True)
 
 
 
 
 
 
52
 
53
- # BLOQUE DE FUENTES (URLs Visibles)
 
 
 
 
 
 
 
 
 
 
 
 
54
  pdf.set_font("Arial", 'B', 8)
55
  pdf.set_text_color(100, 100, 100)
56
- pdf.cell(0, 5, "FUENTES DE BÚSQUEDA GENERADAS:", ln=True)
57
  pdf.set_font("Courier", '', 7)
58
- pdf.multi_cell(0, 4, f"FR: {url_fr}")
59
- pdf.multi_cell(0, 4, f"MC: {url_mc}")
60
  pdf.ln(5)
61
 
62
- # Tabla de Resultados
63
  pdf.set_text_color(0, 0, 0)
64
- pdf.set_font("Arial", '', 10)
 
65
 
 
66
  for _, r in df.iterrows():
67
- pdf.set_fill_color(240, 240, 240)
68
- # Título
69
- pdf.set_font("Arial", 'B', 10)
70
- pdf.cell(0, 8, f"{r['Portal']} - ${r['Precio']:,.0f}", ln=True, fill=True)
71
- # Detalle
72
- pdf.set_font("Arial", '', 9)
73
- pdf.multi_cell(0, 5, f"Desc: {r['Descripcion']}")
74
  pdf.set_font("Arial", 'U', 8)
75
  pdf.set_text_color(0, 0, 255)
76
- pdf.cell(0, 5, f"Link: {r['URL']}", ln=True)
77
  pdf.set_text_color(0, 0, 0)
78
  pdf.ln(2)
79
 
80
  pdf.output(pdf_path)
81
- return pdf_path
82
 
83
  # --- 3. MOTOR DE EXTRACCIÓN ---
84
- def motor_tramitia_debug(zona, ciudad, area, tipo, hab, ban, park, antiguedad):
85
  resultados = []
86
-
87
- # 1. Generamos URLs
88
  url_fr, url_mc = construir_urls_final(zona, ciudad, tipo, hab, ban, park, antiguedad)
89
-
90
- # INICIAMOS EL LOG VISIBLE
91
- log_visible = f"✅ URLs GENERADAS POR EL SISTEMA:\n\n🌍 FINCA RAÍZ:\n{url_fr}\n\n🌍 METROCUADRADO:\n{url_mc}\n\n--- PROCESANDO ---\n"
92
 
93
  with sync_playwright() as p:
94
  browser = p.chromium.launch(headless=True)
@@ -113,13 +125,11 @@ def motor_tramitia_debug(zona, ciudad, area, tipo, hab, ban, park, antiguedad):
113
  "Portal": "Finca Raiz",
114
  "Precio": max(precios),
115
  "Precio_M2": max(precios) / area,
116
- "Descripcion": txt.split('\n')[0][:60],
117
  "URL": href if href else url_fr
118
  })
119
  except: continue
120
  page.close()
121
- except Exception as e:
122
- log_visible += f"⚠️ Error FR: {str(e)}\n"
123
 
124
  # --- METROCUADRADO ---
125
  try:
@@ -127,7 +137,6 @@ def motor_tramitia_debug(zona, ciudad, area, tipo, hab, ban, park, antiguedad):
127
  page.goto(url_mc, wait_until="domcontentloaded", timeout=60000)
128
  for _ in range(3): page.mouse.wheel(0, 800); time.sleep(1)
129
 
130
- # Selector Detail específico que pediste
131
  details = page.query_selector_all("div.property-card__detail")
132
  for d in details[:5]:
133
  try:
@@ -135,7 +144,6 @@ def motor_tramitia_debug(zona, ciudad, area, tipo, hab, ban, park, antiguedad):
135
  precios = [int(s) for s in txt.replace('.', '').replace('$', '').split() if s.isdigit() and len(s) >= 6]
136
  if not precios: continue
137
 
138
- # Búsqueda de link hacia arriba
139
  href = d.evaluate("""el => {
140
  let card = el.closest('li') || el.closest('div[class*="card"]');
141
  return card && card.querySelector('a') ? card.querySelector('a').href : '';
@@ -145,35 +153,40 @@ def motor_tramitia_debug(zona, ciudad, area, tipo, hab, ban, park, antiguedad):
145
  "Portal": "Metrocuadrado",
146
  "Precio": max(precios),
147
  "Precio_M2": max(precios) / area,
148
- "Descripcion": txt.split('\n')[0][:60],
149
  "URL": href if href else url_mc
150
  })
151
  except: continue
152
  page.close()
153
- except Exception as e:
154
- log_visible += f"⚠️ Error MC: {str(e)}\n"
155
-
156
  browser.close()
157
 
158
  if not resultados:
159
- return f"{log_visible}\n❌ NO SE ENCONTRARON RESULTADOS.", None, None
160
 
161
  df = pd.DataFrame(resultados)
162
 
163
- # Generar PDF con las URLs
164
- pdf_path = generar_pdf_con_fuentes(zona, df, url_fr, url_mc)
165
 
166
- return f"{log_visible}\n✅ Éxito: {len(df)} inmuebles encontrados.", df, pdf_path
 
 
 
 
 
 
 
 
167
 
168
  # --- INTERFAZ ---
169
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
170
- gr.Markdown("## 🤖 TramitIA Pro: Auditoría de URLs")
171
 
172
  with gr.Row():
173
- with gr.Column():
174
  c = gr.Textbox(label="Ciudad", value="Bogota")
175
  z = gr.Textbox(label="Zona (Ej: Fontibon)", value="Fontibon")
176
- a = gr.Number(label="Área M2", value=60)
177
  t = gr.Dropdown(["Apartamento", "Casa"], label="Tipo", value="Apartamento")
178
 
179
  with gr.Row():
@@ -185,14 +198,20 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
185
  ["Menos de 1 año", "1 a 8 años", "9 a 15 años", "16 a 30 años", "Más de 30 años"],
186
  label="Antigüedad", value="1 a 8 años"
187
  )
188
- btn = gr.Button("BUSCAR Y MOSTRAR URLS", variant="primary")
 
 
 
 
189
 
190
- with gr.Column():
191
- # CAJA DE TEXTO GRANDE PARA VER LAS URLS
192
- msg = gr.Textbox(label="🔍 Log de URLs Generadas (Copia y pega para probar)", lines=8)
193
- out_df = gr.Dataframe(label="Resultados")
194
- out_pdf = gr.File(label="Reporte con Fuentes")
 
 
195
 
196
- btn.click(motor_tramitia_debug, [z, c, a, t, h, b, p, e], [msg, out_df, out_pdf])
197
 
198
  demo.launch()
 
13
  subprocess.run(["playwright", "install", "chromium"], check=True)
14
  except: pass
15
 
16
+ # --- 1. GENERADOR DE URLS ---
17
  def construir_urls_final(zona, ciudad, tipo, hab, ban, park, antiguedad):
18
  # Mapeo Antigüedad
19
  mapa_ant = {
 
24
  "Más de 30 años": "mas-de-30-anos"
25
  }
26
  slug_ant = mapa_ant.get(antiguedad, "de-1-a-8-anios")
 
 
27
  slug_park = f"{int(park)}-parqueadero" if int(park) == 1 else f"{int(park)}-parqueaderos"
 
 
28
  query_geo = f"{zona.lower().replace(' ', '-')}-{ciudad.lower().replace(' ', '-')}"
29
 
30
  # URL FINCA RAÍZ
 
36
 
37
  return url_fr, url_mc
38
 
39
+ # --- 2. GENERADOR PDF CON ESTIMACIÓN FINANCIERA ---
40
+ def generar_pdf_con_estimacion(zona, area, df, url_fr, url_mc):
41
+ # Cálculos Matemáticos
42
+ promedio_m2 = df['Precio_M2'].mean()
43
+ precio_sugerido = promedio_m2 * area
44
+ precio_min = df['Precio'].min()
45
+ precio_max = df['Precio'].max()
46
+
47
+ pdf_path = f"Estimacion_{int(time.time())}.pdf"
48
  pdf = FPDF()
49
  pdf.add_page()
50
 
51
+ # Encabezado
52
+ pdf.set_font("Arial", 'B', 16)
53
+ pdf.cell(0, 10, f"ESTIMACION DE CANON: {zona.upper()}", ln=True, align='C')
54
+ pdf.ln(5)
55
+
56
+ # --- BLOQUE DE VALORACIÓN (LO NUEVO) ---
57
+ pdf.set_fill_color(220, 230, 240)
58
+ pdf.rect(10, 30, 190, 40, 'F')
59
+ pdf.set_y(35)
60
 
61
+ pdf.set_font("Arial", 'B', 12)
62
+ pdf.cell(0, 8, f"CANON SUGERIDO (Area: {area}m2):", ln=True, align='C')
63
+
64
+ pdf.set_font("Arial", 'B', 20)
65
+ pdf.set_text_color(0, 100, 0)
66
+ pdf.cell(0, 10, f"${precio_sugerido:,.0f}", ln=True, align='C')
67
+
68
+ pdf.set_font("Arial", '', 10)
69
+ pdf.set_text_color(0, 0, 0)
70
+ pdf.cell(0, 8, f"Rango de Mercado: ${precio_min:,.0f} - ${precio_max:,.0f}", ln=True, align='C')
71
+ pdf.ln(15)
72
+
73
+ # Fuentes
74
  pdf.set_font("Arial", 'B', 8)
75
  pdf.set_text_color(100, 100, 100)
76
+ pdf.cell(0, 5, "URLS DE BÚSQUEDA (AUDITORÍA):", ln=True)
77
  pdf.set_font("Courier", '', 7)
78
+ pdf.multi_cell(0, 4, f"FR: {url_fr}\nMC: {url_mc}")
 
79
  pdf.ln(5)
80
 
81
+ # Detalle de Comparables
82
  pdf.set_text_color(0, 0, 0)
83
+ pdf.set_font("Arial", 'B', 12)
84
+ pdf.cell(0, 10, "COMPARABLES UTILIZADOS:", ln=True)
85
 
86
+ pdf.set_font("Arial", '', 10)
87
  for _, r in df.iterrows():
88
+ pdf.set_fill_color(245, 245, 245)
89
+ pdf.cell(0, 8, f"{r['Portal']} - ${r['Precio']:,.0f} (M2: ${r['Precio_M2']:,.0f})", ln=True, fill=True)
 
 
 
 
 
90
  pdf.set_font("Arial", 'U', 8)
91
  pdf.set_text_color(0, 0, 255)
92
+ pdf.cell(0, 5, f"{r['URL']}", ln=True)
93
  pdf.set_text_color(0, 0, 0)
94
  pdf.ln(2)
95
 
96
  pdf.output(pdf_path)
97
+ return pdf_path, precio_sugerido, precio_min, precio_max
98
 
99
  # --- 3. MOTOR DE EXTRACCIÓN ---
100
+ def motor_tramitia_estimador(zona, ciudad, area, tipo, hab, ban, park, antiguedad):
101
  resultados = []
 
 
102
  url_fr, url_mc = construir_urls_final(zona, ciudad, tipo, hab, ban, park, antiguedad)
103
+ log_visible = f"✅ URLs AUDITABLES:\nFR: {url_fr}\nMC: {url_mc}\n\n--- INICIANDO ESCANEO ---\n"
 
 
104
 
105
  with sync_playwright() as p:
106
  browser = p.chromium.launch(headless=True)
 
125
  "Portal": "Finca Raiz",
126
  "Precio": max(precios),
127
  "Precio_M2": max(precios) / area,
 
128
  "URL": href if href else url_fr
129
  })
130
  except: continue
131
  page.close()
132
+ except Exception as e: log_visible += f"⚠️ Error FR: {e}\n"
 
133
 
134
  # --- METROCUADRADO ---
135
  try:
 
137
  page.goto(url_mc, wait_until="domcontentloaded", timeout=60000)
138
  for _ in range(3): page.mouse.wheel(0, 800); time.sleep(1)
139
 
 
140
  details = page.query_selector_all("div.property-card__detail")
141
  for d in details[:5]:
142
  try:
 
144
  precios = [int(s) for s in txt.replace('.', '').replace('$', '').split() if s.isdigit() and len(s) >= 6]
145
  if not precios: continue
146
 
 
147
  href = d.evaluate("""el => {
148
  let card = el.closest('li') || el.closest('div[class*="card"]');
149
  return card && card.querySelector('a') ? card.querySelector('a').href : '';
 
153
  "Portal": "Metrocuadrado",
154
  "Precio": max(precios),
155
  "Precio_M2": max(precios) / area,
 
156
  "URL": href if href else url_mc
157
  })
158
  except: continue
159
  page.close()
160
+ except Exception as e: log_visible += f"⚠️ Error MC: {e}\n"
 
 
161
  browser.close()
162
 
163
  if not resultados:
164
+ return f"{log_visible}\n❌ NO HAY DATOS PARA CALCULAR.", None, None, "---"
165
 
166
  df = pd.DataFrame(resultados)
167
 
168
+ # Generar PDF y Cálculos
169
+ pdf_path, sug, min_p, max_p = generar_pdf_con_estimacion(zona, area, df, url_fr, url_mc)
170
 
171
+ resumen_financiero = (
172
+ f"💰 **ESTIMACIÓN DE RENTA**\n"
173
+ f"🔹 **Canon Sugerido:** ${sug:,.0f}\n"
174
+ f"📉 Mínimo Zona: ${min_p:,.0f}\n"
175
+ f"📈 Máximo Zona: ${max_p:,.0f}\n"
176
+ f"📊 Basado en {len(df)} comparables."
177
+ )
178
+
179
+ return f"{log_visible}\n✅ Cálculo Completado.", df, pdf_path, resumen_financiero
180
 
181
  # --- INTERFAZ ---
182
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
183
+ gr.Markdown("## 🤖 TramitIA Pro: Calculadora de Renta Justificada")
184
 
185
  with gr.Row():
186
+ with gr.Column(scale=1):
187
  c = gr.Textbox(label="Ciudad", value="Bogota")
188
  z = gr.Textbox(label="Zona (Ej: Fontibon)", value="Fontibon")
189
+ a = gr.Number(label="Área M2 (Tu Cliente)", value=60)
190
  t = gr.Dropdown(["Apartamento", "Casa"], label="Tipo", value="Apartamento")
191
 
192
  with gr.Row():
 
198
  ["Menos de 1 año", "1 a 8 años", "9 a 15 años", "16 a 30 años", "Más de 30 años"],
199
  label="Antigüedad", value="1 a 8 años"
200
  )
201
+ btn = gr.Button("CALCULAR CANON DE ARRENDAMIENTO", variant="primary")
202
+
203
+ with gr.Column(scale=2):
204
+ # RESULTADO DESTACADO
205
+ res_fin = gr.Markdown("### 💰 El resultado aparecerá aquí...")
206
 
207
+ with gr.Tabs():
208
+ with gr.TabItem("🔍 Log URLs"):
209
+ msg = gr.Textbox(lines=6, label="Auditoría de Enlaces")
210
+ with gr.TabItem("📋 Tabla de Comparables"):
211
+ out_df = gr.Dataframe()
212
+ with gr.TabItem("📄 Reporte PDF"):
213
+ out_pdf = gr.File()
214
 
215
+ btn.click(motor_tramitia_estimador, [z, c, a, t, h, b, p, e], [msg, out_df, out_pdf, res_fin])
216
 
217
  demo.launch()