VicGerardoPR's picture
Update app.py
353ef89 verified
import os
import re
import streamlit as st
import folium
from streamlit_folium import st_folium
import openai
from bs4 import BeautifulSoup
# Configuraci贸n de la p谩gina
st.set_page_config(page_title="Mapa Interactivo de Puerto Rico", layout="wide")
st.title("Mapa Interactivo de Puerto Rico y Chatbot de Recomendaciones")
# =============================================================================
# Funciones para parsear archivos
# =============================================================================
def parse_text_file(filepath):
"""
Parseo para archivos de texto plano.
Busca l铆neas con 'Name:', 'Latitude:' y 'Longitude:'.
"""
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
name_match = re.search(r'Name:\s*(.*)', content, re.IGNORECASE)
name = name_match.group(1).strip() if name_match else os.path.splitext(os.path.basename(filepath))[0]
lat_match = re.search(r'Latitude:\s*([-+]?[0-9]*\.?[0-9]+)', content, re.IGNORECASE)
lon_match = re.search(r'Longitude:\s*([-+]?[0-9]*\.?[0-9]+)', content, re.IGNORECASE)
if lat_match and lon_match:
try:
lat = float(lat_match.group(1))
except ValueError:
lat = None
try:
lon = float(lon_match.group(1))
except ValueError:
lon = None
else:
lat, lon = None, None
return {
"name": name,
"lat": lat,
"lon": lon,
"content": content
}
def parse_html_file(filepath):
"""
Parseo para archivos HTML (por ejemplo, de Wikipedia).
Extrae el t铆tulo, las coordenadas desde el bloque "wgCoordinates" y, si es posible,
la secci贸n hist贸rica (buscando un encabezado que contenga "History" o "Historia").
"""
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# Parsear con BeautifulSoup
soup = BeautifulSoup(content, "html.parser")
title = soup.title.string.strip() if soup.title else os.path.splitext(os.path.basename(filepath))[0]
# Extraer coordenadas (pueden venir en el bloque "wgCoordinates")
coord_match = re.search(r'"wgCoordinates":\s*{\s*"lat":\s*([0-9\.-]+),\s*"lon":\s*([0-9\.-]+)', content)
if coord_match:
lat_str = coord_match.group(1)
lon_str = coord_match.group(2)
try:
lat = float(lat_str) if lat_str not in ['-', ''] else None
except ValueError:
lat = None
try:
lon = float(lon_str) if lon_str not in ['-', ''] else None
except ValueError:
lon = None
else:
lat, lon = None, None
# Intentar extraer datos hist贸ricos: se busca un encabezado que contenga "History" o "Historia"
history = ""
history_header = soup.find(lambda tag: tag.name in ["h2", "h3"] and ("History" in tag.get_text() or "Historia" in tag.get_text()))
if history_header:
history_parts = []
# Recorrer los hermanos hasta llegar a otro encabezado del mismo nivel (o superior)
for sibling in history_header.find_next_siblings():
if sibling.name and sibling.name.startswith("h"):
break
history_parts.append(sibling.get_text(strip=True))
history = "\n".join(history_parts)
return {
"name": title,
"lat": lat,
"lon": lon,
"content": content,
"history": history
}
def load_data_from_directory(directory, parser_func):
"""
Carga todos los archivos .txt de un directorio usando la funci贸n de parseo indicada.
"""
data_points = []
if os.path.exists(directory):
for filename in os.listdir(directory):
if filename.endswith(".txt"):
filepath = os.path.join(directory, filename)
data_points.append(parser_func(filepath))
return data_points
# =============================================================================
# Carga de los datos desde las carpetas correspondientes
# =============================================================================
landmarks_dir = os.path.join("data", "landmarks")
municipalities_dir = os.path.join("data", "municipalities")
news_dir = os.path.join("data", "news")
# Para landmarks y news se asume que los archivos son de texto plano.
landmarks = load_data_from_directory(landmarks_dir, parse_text_file)
news_data = load_data_from_directory(news_dir, parse_text_file)
# Para municipalities, se usa el parseo de HTML (que ahora incluye datos hist贸ricos)
municipalities = load_data_from_directory(municipalities_dir, parse_html_file)
# =============================================================================
# Creaci贸n del mapa interactivo usando Folium
# =============================================================================
# Coordenadas centrales aproximadas de Puerto Rico
map_center = [18.2208, -66.5901]
m = folium.Map(location=map_center, zoom_start=10)
# Agregar marcadores para Landmarks
for landmark in landmarks:
if landmark["lat"] is not None and landmark["lon"] is not None:
folium.Marker(
location=[landmark["lat"], landmark["lon"]],
popup=folium.Popup(f"<b>{landmark['name']}</b><br>{landmark['content'][:200]}...", max_width=300),
icon=folium.Icon(color='blue', icon='info-sign')
).add_to(m)
# Agregar marcadores para Municipios, incluyendo datos hist贸ricos si est谩n disponibles
for municipality in municipalities:
if municipality["lat"] is not None and municipality["lon"] is not None:
history_text = municipality.get("history", "")
history_excerpt = history_text[:300] + "..." if len(history_text) > 300 else history_text
popup_content = f"<b>{municipality['name']}</b><br><b>Historia:</b><br>{history_excerpt}"
folium.Marker(
location=[municipality["lat"], municipality["lon"]],
popup=folium.Popup(popup_content, max_width=300),
icon=folium.Icon(color='green', icon='home')
).add_to(m)
# Agregar marcadores para Noticias (si se disponen de coordenadas)
for news_item in news_data:
if news_item["lat"] is not None and news_item["lon"] is not None:
folium.Marker(
location=[news_item["lat"], news_item["lon"]],
popup=folium.Popup(f"<b>{news_item['name']}</b><br>{news_item['content'][:200]}...", max_width=300),
icon=folium.Icon(color='red', icon='newspaper')
).add_to(m)
st.subheader("Mapa Interactivo")
st_data = st_folium(m, width=700, height=500)
# =============================================================================
# Secci贸n del Chatbot con API de OpenAI
# =============================================================================
st.markdown("---")
st.subheader("Chatbot de Recomendaciones")
st.write("Consulta recomendaciones sobre Puerto Rico.")
# Entrada de usuario para la consulta
user_input = st.text_input("Escribe tu consulta:")
if st.button("Enviar consulta"):
if user_input:
# Configura tu clave API de OpenAI (def铆nela en st.secrets)
openai.api_key = st.secrets[""]
with st.spinner("Consultando a OpenAI..."):
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "Eres un asistente experto en recomendaciones tur铆sticas y culturales de Puerto Rico. Ayudas a los usuarios a descubrir lugares de inter茅s y actividades en la isla."},
{"role": "user", "content": user_input}
]
)
answer = response.choices[0].message.content.strip()
st.markdown("**Respuesta:**")
st.write(answer)
except Exception as e:
st.error(f"Error al conectarse con la API de OpenAI: {e}")
else:
st.warning("Por favor, ingresa una consulta.")