Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,122 +1,139 @@
|
|
| 1 |
import os
|
| 2 |
import subprocess
|
| 3 |
-
import sys
|
| 4 |
import pandas as pd
|
| 5 |
-
import
|
|
|
|
| 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
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
except: pass
|
| 16 |
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
-
#
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
| 62 |
|
| 63 |
-
#
|
| 64 |
-
|
| 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 |
-
|
| 70 |
-
|
|
|
|
|
|
|
| 71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
browser.close()
|
| 73 |
|
| 74 |
if not all_data:
|
| 75 |
-
return "
|
| 76 |
|
| 77 |
df = pd.DataFrame(all_data)
|
| 78 |
|
| 79 |
-
#
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
|
| 85 |
-
#
|
| 86 |
-
|
| 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 |
-
|
|
|
|
| 99 |
|
| 100 |
-
|
|
|
|
|
|
|
| 101 |
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
| 105 |
with gr.Row():
|
| 106 |
with gr.Column(scale=1):
|
| 107 |
-
zona = gr.Textbox(label="
|
| 108 |
-
area = gr.Number(label="Área M2
|
| 109 |
-
tipo = gr.Dropdown(["Apartamento", "Casa"
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
with gr.Column(scale=2):
|
| 112 |
msg = gr.Markdown()
|
| 113 |
-
plot = gr.Plot()
|
| 114 |
with gr.Tabs():
|
| 115 |
-
with gr.TabItem("
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
table = gr.Dataframe()
|
| 117 |
-
|
| 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()
|