jcalbornoz commited on
Commit
487965f
·
verified ·
1 Parent(s): 39f47a1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -84
app.py CHANGED
@@ -1,122 +1,139 @@
1
  import os
2
  import subprocess
3
- import sys
4
  import pandas as pd
5
- import time
 
6
  from fpdf import FPDF
7
  import gradio as gr
8
  import plotly.express as px
9
  from playwright.sync_api import sync_playwright
 
10
 
11
- # --- CONFIGURACIÓN DE ENTORNO ---
12
- def setup():
13
- try:
14
- subprocess.run(["playwright", "install", "chromium"], check=True)
15
- except: pass
16
 
17
- setup()
 
 
 
 
 
 
 
 
18
 
19
- # --- MOTOR HÍBRIDO TRAMITIA ---
20
- def scrapper_portal(context, url, portal_name, area_usuario):
21
- resultados = []
22
- page = context.new_page()
23
- try:
24
- page.goto(url, wait_until="domcontentloaded", timeout=45000)
25
- page.mouse.wheel(0, 2000) # Scroll para activar carga
26
- time.sleep(3)
27
-
28
- # Selector universal de tarjetas de inmuebles
29
- cards = page.query_selector_all("article, [class*='Card'], [class*='listing'], .property-item")
30
-
31
- for card in cards[:8]: # Analizamos los primeros 8 de cada portal
32
- try:
33
- texto = card.inner_text().replace('.', '').replace('$', '')
34
- nums = [int(s) for s in texto.split() if s.isdigit() and len(s) >= 6]
35
-
36
- if nums:
37
- precio = max(nums)
38
- link = card.query_selector("a")
39
- href = link.get_attribute("href") if link else "#"
40
- url_final = href if "http" in href else f"https://www.{portal_name.lower().replace(' ', '')}.com.co{href}"
41
-
42
- resultados.append({
43
- "Portal": portal_name,
44
- "Precio": precio,
45
- "Precio_M2": precio / area_usuario,
46
- "URL": url_final
47
- })
48
- except: continue
49
- except Exception as e:
50
- print(f"Error en {portal_name}: {e}")
51
- finally:
52
- page.close()
53
- return resultados
54
 
55
- def analizar_mercado(zona, area, tipo):
 
 
 
 
 
56
  busqueda = zona.lower().replace(" ", "-")
57
  all_data = []
58
 
59
  with sync_playwright() as p:
60
  browser = p.chromium.launch(headless=True)
61
- context = browser.new_context(user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
 
62
 
63
- # Definición de rutas por portal
64
- portales = [
65
- {"nombre": "Finca Raiz", "url_base": f"https://www.fincaraiz.com.co/{tipo.lower()}/arriendo/{busqueda}"},
66
- {"nombre": "Metrocuadrado", "url_base": f"https://www.metrocuadrado.com/{tipo.lower()}/arriendo/bogota/{busqueda}/"}
67
- ]
68
 
69
- for p_info in portales:
70
- all_data += scrapper_portal(context, p_info["url_base"], p_info["nombre"], area)
 
 
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  browser.close()
73
 
74
  if not all_data:
75
- return "No se detectaron anuncios. Prueba con una zona más amplia (ej: 'Norte de Bogota').", None, None, None
76
 
77
  df = pd.DataFrame(all_data)
78
 
79
- # --- GRÁFICA COMPARATIVA ---
80
- fig = px.bar(df, x="Portal", y="Precio_M2", color="Portal",
81
- title=f"Comparativa Valor M2 en {zona}",
82
- labels={"Precio_M2": "Valor M2 (COP)"},
83
- hover_data=["Precio"], template="plotly_dark")
84
 
85
- # --- GENERADOR DE PDF ---
86
- pdf = FPDF()
87
- pdf.add_page()
88
- pdf.set_font("Arial", 'B', 16)
89
- pdf.cell(0, 10, f"REPORTE TRAMITIA PRO: {zona.upper()}", ln=True, align='C')
90
- pdf.set_font("Arial", '', 10)
91
- pdf.ln(10)
92
- for i, r in df.iterrows():
93
- pdf.cell(0, 8, f"{i+1}. [{r['Portal']}] Precio: ${r['Precio']:,.0f} - M2: ${r['Precio_M2']:,.0f}", ln=True)
94
-
95
- reporte_path = "Analisis_TramitIA.pdf"
96
- pdf.output(reporte_path)
97
 
98
- resumen = f"### ✅ Éxito: {len(df)} Inmuebles encontrados.\nPortal más económico: **{df.loc[df['Precio_M2'].idxmin(), 'Portal']}**"
 
99
 
100
- return resumen, df, reporte_path, fig
 
 
101
 
102
- # --- INTERFAZ GRADIO ---
103
- with gr.Blocks(theme=gr.themes.Default(primary_hue="blue")) as demo:
104
- gr.Markdown("# 🤖 TramitIA Pro: Buscador Multi-Portal")
 
 
 
105
  with gr.Row():
106
  with gr.Column(scale=1):
107
- zona = gr.Textbox(label="Zona (Ej: Cedritos Bogotá)", placeholder="Barrio + Ciudad")
108
- area = gr.Number(label="Área M2 del cliente", value=60)
109
- tipo = gr.Dropdown(["Apartamento", "Casa", "Oficina"], label="Tipo de Inmueble", value="Apartamento")
110
- btn = gr.Button("ESCANEAR MERCADO", variant="primary")
 
 
 
 
 
 
 
111
  with gr.Column(scale=2):
112
  msg = gr.Markdown()
113
- plot = gr.Plot()
114
  with gr.Tabs():
115
- with gr.TabItem("Tabla de Datos"):
 
 
 
 
116
  table = gr.Dataframe()
117
- with gr.TabItem("Descargar PDF"):
118
- file = gr.File()
119
 
120
- btn.click(analizar_mercado, [zona, area, tipo], [msg, table, file, plot])
121
 
122
  demo.launch()
 
1
  import os
2
  import subprocess
 
3
  import pandas as pd
4
+ import datetime
5
+ import folium
6
  from fpdf import FPDF
7
  import gradio as gr
8
  import plotly.express as px
9
  from playwright.sync_api import sync_playwright
10
+ import time
11
 
12
+ # --- CONFIGURACIÓN ---
13
+ try:
14
+ subprocess.run(["playwright", "install", "chromium"], check=True)
15
+ except: pass
 
16
 
17
+ # --- GENERADOR DE PDF DETALLADO ---
18
+ def generar_pdf_completo(zona, df):
19
+ pdf = FPDF()
20
+ pdf.add_page()
21
+ pdf.set_font("Arial", 'B', 16)
22
+ pdf.cell(0, 10, f"REPORTE TRAMITIA PRO - {zona.upper()}", ln=True, align='C')
23
+ pdf.set_font("Arial", '', 10)
24
+ pdf.cell(0, 10, f"Fecha: {datetime.datetime.now().strftime('%d/%m/%Y')}", ln=True, align='C')
25
+ pdf.ln(5)
26
 
27
+ # Tabla de datos en el PDF
28
+ pdf.set_font("Arial", 'B', 9)
29
+ pdf.set_fill_color(200, 220, 255)
30
+ pdf.cell(30, 8, "Portal", 1, 0, 'C', True)
31
+ pdf.cell(40, 8, "Precio", 1, 0, 'C', True)
32
+ pdf.cell(20, 8, "Hab", 1, 0, 'C', True)
33
+ pdf.cell(20, 8, "Banos", 1, 0, 'C', True)
34
+ pdf.cell(20, 8, "Park", 1, 0, 'C', True)
35
+ pdf.cell(40, 8, "Valor M2", 1, 1, 'C', True)
36
+
37
+ pdf.set_font("Arial", '', 8)
38
+ for _, r in df.iterrows():
39
+ pdf.cell(30, 8, str(r['Portal']), 1)
40
+ pdf.cell(40, 8, f"${r['Precio']:,.0f}", 1)
41
+ pdf.cell(20, 8, str(r['Habitaciones']), 1)
42
+ pdf.cell(20, 8, str(r['Banos']), 1)
43
+ pdf.cell(20, 8, str(r['Garajes']), 1)
44
+ pdf.cell(40, 8, f"${r['Precio_M2']:,.0f}", 1, 1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ path_pdf = "Reporte_TramitIA_Detallado.pdf"
47
+ pdf.output(path_pdf)
48
+ return path_pdf
49
+
50
+ # --- MOTOR DE EXTRACCIÓN ---
51
+ def analizar_mercado(zona, area, tipo, hab, banos, park, edad):
52
  busqueda = zona.lower().replace(" ", "-")
53
  all_data = []
54
 
55
  with sync_playwright() as p:
56
  browser = p.chromium.launch(headless=True)
57
+ context = browser.new_context(user_agent="Mozilla/5.0")
58
+ page = context.new_page()
59
 
60
+ # Filtros aplicados a la URL (Finca Raíz ejemplo)
61
+ url = f"https://www.fincaraiz.com.co/{tipo.lower()}/arriendo/{busqueda}?habitaciones={hab}&banos={banos}"
 
 
 
62
 
63
+ try:
64
+ page.goto(url, wait_until="domcontentloaded", timeout=60000)
65
+ page.wait_for_selector("article", timeout=20000)
66
+ cards = page.query_selector_all("article")[:5]
67
 
68
+ for card in cards:
69
+ texto = card.inner_text().replace('.', '').replace('$', '')
70
+ nums = [int(s) for s in texto.split() if s.isdigit() and len(s) >= 6]
71
+ if nums:
72
+ precio = max(nums)
73
+ all_data.append({
74
+ "Portal": "Finca Raiz",
75
+ "Precio": precio,
76
+ "Habitaciones": hab,
77
+ "Banos": banos,
78
+ "Garajes": park,
79
+ "Antiguedad": edad,
80
+ "Precio_M2": precio / area,
81
+ "URL": "https://fincaraiz.com.co"
82
+ })
83
+ except: pass
84
  browser.close()
85
 
86
  if not all_data:
87
+ return "No se hallaron datos con esos filtros.", None, None, None, None
88
 
89
  df = pd.DataFrame(all_data)
90
 
91
+ # Mapa
92
+ m = folium.Map(location=[4.66, -74.11], zoom_start=14) # Coordenadas base Modelia/Bogotá
93
+ folium.Marker([4.66, -74.11], popup=f"Análisis en {zona}").add_to(m)
94
+ map_path = "mapa_tramitia.html"
95
+ m.save(map_path)
96
 
97
+ # Gráfica
98
+ fig = px.bar(df, x="Precio", y="Precio_M2", color="Precio", title="Relación Precio vs Valor M2")
 
 
 
 
 
 
 
 
 
 
99
 
100
+ # PDF
101
+ pdf_path = generar_pdf_completo(zona, df)
102
 
103
+ # El mapa se lee como texto HTML para Gradio
104
+ with open(map_path, 'r', encoding='utf-8') as f:
105
+ map_html = f.read()
106
 
107
+ return f"✅ Análisis listo para {zona}", df, pdf_path, fig, map_html
108
+
109
+ # --- INTERFAZ ---
110
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
111
+ gr.Markdown("# 🤖 TramitIA Pro: Panel de Consultoría")
112
+
113
  with gr.Row():
114
  with gr.Column(scale=1):
115
+ zona = gr.Textbox(label="Barrio y Ciudad", value="Modelia Bogota")
116
+ area = gr.Number(label="Área M2", value=70)
117
+ tipo = gr.Dropdown(["Apartamento", "Casa"], label="Tipo", value="Apartamento")
118
+ with gr.Row():
119
+ hab = gr.Number(label="Habitaciones", value=3)
120
+ ban = gr.Number(label="Baños", value=2)
121
+ with gr.Row():
122
+ gar = gr.Number(label="Garajes", value=1)
123
+ ant = gr.Dropdown(["Nuevo", "1-5 años", "5-10 años", "Más de 10"], label="Antigüedad", value="5-10 años")
124
+ btn = gr.Button("EJECUTAR ANÁLISIS INTEGRAL", variant="primary")
125
+
126
  with gr.Column(scale=2):
127
  msg = gr.Markdown()
 
128
  with gr.Tabs():
129
+ with gr.TabItem("Mapa de Ubicación"):
130
+ mapa_component = gr.HTML() # Aquí se renderiza el mapa
131
+ with gr.TabItem("Gráfica de Mercado"):
132
+ plot = gr.Plot()
133
+ with gr.TabItem("Tabla y Reporte"):
134
  table = gr.Dataframe()
135
+ file = gr.File(label="Descargar PDF Detallado")
 
136
 
137
+ btn.click(analizar_mercado, [zona, area, tipo, hab, ban, gar, ant], [msg, table, file, plot, mapa_component])
138
 
139
  demo.launch()