Spaces:
Sleeping
Sleeping
| 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.") | |