"""
VUTIA - Sistema de Análisis de Viviendas de Uso Turístico
Ayuntamiento de Dénia - Plataforma de Análisis Institucional
"""
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
from datetime import datetime
from io import BytesIO
import os
# Configuración de la página
st.set_page_config(
page_title="VUTIA - Sistema de Análisis VUT",
page_icon="📊",
layout="wide",
initial_sidebar_state="expanded"
)
# CSS Profesional Institucional (copiado del original)
st.markdown("""
""", unsafe_allow_html=True)
# Colores corporativos
COLORS = {
'VUT_CONFIRMADA': '#006AA7',
'VUT_POSIBLE': '#017CB5',
'VUT_BAJACONFIANZA_NOREGLADA': '#009992',
'HABITUAL': '#00AA98',
'SEGUNDA_RESIDENCIA': '#43C179',
'VACIA': '#8BB640'
}
def get_chart_layout():
return {
'paper_bgcolor': 'rgba(20, 24, 50, 0.5)',
'plot_bgcolor': 'rgba(26, 31, 58, 0.5)',
'font': {'family': 'Inter', 'size': 12, 'color': 'white'},
'margin': {'t': 40, 'r': 20, 'b': 40, 'l': 50},
'xaxis': {'gridcolor': 'rgba(67, 193, 121, 0.1)', 'color': 'white'},
'yaxis': {'gridcolor': 'rgba(67, 193, 121, 0.1)', 'color': 'white'}
}
@st.cache_data
def cargar_datos(uploaded_file=None):
"""Carga datos desde archivo subido o usa datos de demostración"""
if uploaded_file is not None:
try:
df = pd.read_excel(uploaded_file)
return df
except Exception as e:
st.error(f"Error al cargar el archivo: {e}")
return None
else:
# Datos de demostración
st.info("📊 Usando datos de demostración. Sube tu archivo Excel para ver tus datos reales.")
return crear_datos_demo()
def crear_datos_demo():
"""Crea datos de demostración para pruebas"""
np.random.seed(42)
n_samples = 1000
categorias = ['VUT_CONFIRMADA', 'VUT_POSIBLE', 'VUT_BAJACONFIANZA_NOREGLADA',
'HABITUAL', 'SEGUNDA_RESIDENCIA', 'VACIA']
barrios = ['Centro', 'Les Rotes', 'Les Marines', 'La Xara', 'Jesus Pobre',
'Els Poblets', 'Dénia Nord', 'Dénia Sud']
data = {
'Direccion': [f'Calle Demo {i}' for i in range(n_samples)],
'Barrio': np.random.choice(barrios, n_samples),
'Categoria': np.random.choice(categorias, n_samples,
p=[0.15, 0.20, 0.15, 0.25, 0.15, 0.10]),
'Confianza': np.random.uniform(0.3, 1.0, n_samples),
'Consumo_Total_Periodo_m3': np.random.uniform(50, 500, n_samples),
'Ratio_Verano_Invierno': np.random.uniform(0.5, 3.0, n_samples)
}
return pd.DataFrame(data)
# Header institucional
st.markdown("""
""", unsafe_allow_html=True)
# Subida de archivo
with st.sidebar:
st.markdown("### 📁 CARGAR DATOS")
uploaded_file = st.file_uploader(
"Sube tu archivo Excel",
type=['xlsx', 'xls'],
help="Archivo con datos de clasificación VUT"
)
st.markdown("---")
# Cargar datos
df = cargar_datos(uploaded_file)
if df is None:
st.error("No se pudieron cargar los datos")
st.stop()
# SIDEBAR DE FILTROS
with st.sidebar:
st.markdown("### 🔍 FILTROS")
categorias = df['Categoria'].unique().tolist()
cat_sel = st.multiselect("Categorías", categorias, default=categorias)
barrios = ['Todos'] + sorted(df['Barrio'].unique().tolist())
barrio_sel = st.selectbox("Barrio", barrios)
confianza_min = st.slider("Confianza Mínima (%)", 0, 100, 0, 5)
# Aplicar filtros
df_filtrado = df.copy()
if cat_sel:
df_filtrado = df_filtrado[df_filtrado['Categoria'].isin(cat_sel)]
if barrio_sel != 'Todos':
df_filtrado = df_filtrado[df_filtrado['Barrio'] == barrio_sel]
df_filtrado = df_filtrado[df_filtrado['Confianza'] >= confianza_min / 100]
# Estadísticas
st.markdown("### ESTADÍSTICAS")
col1, col2 = st.columns(2)
with col1:
st.metric("Filtradas", f"{len(df_filtrado):,}")
with col2:
st.metric("Total", f"{len(df):,}")
if len(df_filtrado) < len(df):
pct = (len(df_filtrado) / len(df)) * 100
st.caption(f"Mostrando {pct:.1f}% del total")
st.markdown("---")
st.caption("**VUTIA v3.0**")
st.caption("© 2026 Ayuntamiento de Dénia")
# TABS
tab1, tab2, tab3, tab4 = st.tabs([
"Panel General",
"Análisis VUT",
"Datos",
"Exportación"
])
# TAB 1: PANEL GENERAL
with tab1:
st.markdown("## Panel General")
st.markdown("")
# Métricas principales
col1, col2, col3, col4, col5, col6 = st.columns(6)
with col1:
n = len(df_filtrado[df_filtrado['Categoria'] == 'VUT_CONFIRMADA'])
st.metric("VUT Confirmada", f"{n:,}",
f"{(n/len(df_filtrado)*100):.1f}%" if len(df_filtrado) > 0 else "0%")
with col2:
n = len(df_filtrado[df_filtrado['Categoria'] == 'VUT_POSIBLE'])
st.metric("VUT Posible", f"{n:,}",
f"{(n/len(df_filtrado)*100):.1f}%" if len(df_filtrado) > 0 else "0%")
with col3:
n = len(df_filtrado[df_filtrado['Categoria'] == 'VUT_BAJACONFIANZA_NOREGLADA'])
st.metric("VUT Baja Conf.", f"{n:,}",
f"{(n/len(df_filtrado)*100):.1f}%" if len(df_filtrado) > 0 else "0%")
with col4:
n = len(df_filtrado[df_filtrado['Categoria'] == 'HABITUAL'])
st.metric("Habitual", f"{n:,}",
f"{(n/len(df_filtrado)*100):.1f}%" if len(df_filtrado) > 0 else "0%")
with col5:
n = len(df_filtrado[df_filtrado['Categoria'] == 'SEGUNDA_RESIDENCIA'])
st.metric("Segunda Res.", f"{n:,}",
f"{(n/len(df_filtrado)*100):.1f}%" if len(df_filtrado) > 0 else "0%")
with col6:
n = len(df_filtrado[df_filtrado['Categoria'] == 'VACIA'])
st.metric("Vacía", f"{n:,}",
f"{(n/len(df_filtrado)*100):.1f}%" if len(df_filtrado) > 0 else "0%")
st.markdown("---")
# Gráfico de distribución
st.markdown("### Distribución por Categorías")
dist = df_filtrado['Categoria'].value_counts()
fig = go.Figure(data=[go.Pie(
labels=dist.index,
values=dist.values,
hole=.45,
marker=dict(
colors=[COLORS.get(c, '#017CB5') for c in dist.index],
line=dict(color='rgba(10, 14, 39, 0.8)', width=3)
),
textfont=dict(size=12, family='Inter', color='white'),
hovertemplate='%{label}
%{value:,} viviendas
%{percent}'
)])
layout = get_chart_layout()
layout['height'] = 400
fig.update_layout(layout)
st.plotly_chart(fig, use_container_width=True)
# TAB 2: ANÁLISIS VUT
with tab2:
st.markdown("## Análisis Detallado de VUT")
vut_categories = ['VUT_CONFIRMADA', 'VUT_POSIBLE', 'VUT_BAJACONFIANZA_NOREGLADA']
df_vut = df_filtrado[df_filtrado['Categoria'].isin(vut_categories)]
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total VUT", f"{len(df_vut):,}")
with col2:
st.metric("Confirmadas", f"{len(df_vut[df_vut['Categoria']=='VUT_CONFIRMADA']):,}")
with col3:
avg_conf = df_vut['Confianza'].mean() * 100 if len(df_vut) > 0 else 0
st.metric("Confianza Media", f"{avg_conf:.1f}%")
st.markdown("---")
# Top 20 VUT
st.markdown("### Top 20 VUT por Confianza")
top20 = df_vut.nlargest(20, 'Confianza')
for idx, row in top20.iterrows():
with st.container():
col1, col2, col3, col4 = st.columns([3, 2, 2, 2])
with col1:
st.write(f"**{row['Direccion']}**")
with col2:
st.write(f"Barrio: {row['Barrio']}")
with col3:
st.write(f"Categoría: {row['Categoria']}")
with col4:
st.write(f"Confianza: {row['Confianza']*100:.1f}%")
st.markdown("---")
# TAB 3: DATOS
with tab3:
st.markdown("## Datos Detallados")
# Búsqueda
busqueda = st.text_input("🔍 Buscar por dirección o barrio")
df_mostrar = df_filtrado.copy()
if busqueda:
mask = (df_mostrar['Direccion'].str.contains(busqueda, case=False, na=False) |
df_mostrar['Barrio'].str.contains(busqueda, case=False, na=False))
df_mostrar = df_mostrar[mask]
st.dataframe(
df_mostrar[['Direccion', 'Barrio', 'Categoria', 'Confianza', 'Consumo_Total_Periodo_m3']],
use_container_width=True,
height=600
)
st.caption(f"Mostrando {len(df_mostrar):,} de {len(df_filtrado):,} viviendas")
# TAB 4: EXPORTACIÓN
with tab4:
st.markdown("## Exportación de Datos")
st.markdown("### Descargar Datos Filtrados")
# Convertir a Excel
buffer = BytesIO()
with pd.ExcelWriter(buffer, engine='openpyxl') as writer:
df_filtrado.to_excel(writer, index=False, sheet_name='Datos')
buffer.seek(0)
st.download_button(
label="📊 Descargar Excel",
data=buffer,
file_name=f"vutia_datos_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
# Convertir a CSV
csv = df_filtrado.to_csv(index=False).encode('utf-8')
st.download_button(
label="📄 Descargar CSV",
data=csv,
file_name=f"vutia_datos_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
mime="text/csv"
)