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"{landmark['name']}
{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"{municipality['name']}
Historia:
{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"{news_item['name']}
{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.")