Commit ·
391b4b4
0
Parent(s):
Final clean initial commit for deployment
Browse files- .gitattributes +1 -0
- Dockerfile +46 -0
- README.md +20 -0
- app_00.py +54 -0
- data/censo_manzanas_optimizado.gpq +3 -0
- requirements.txt +7 -0
.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*.gpq filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Usar una imagen base de Python basada en Debian estable (slim-buster es una buena opción)
|
| 2 |
+
FROM python:3.10-slim-buster
|
| 3 |
+
|
| 4 |
+
# Evitar que 'apt' solicite entradas interactivas durante la construcción
|
| 5 |
+
ENV DEBIAN_FRONTEND=noninteractive
|
| 6 |
+
|
| 7 |
+
# 1. Instalar dependencias del sistema (El "Paso GDAL" crítico)
|
| 8 |
+
# - libgdal-dev: Proporciona los encabezados de C++ y la herramienta gdal-config
|
| 9 |
+
# que 'pip' necesita para compilar los bindings de geopandas/fiona.
|
| 10 |
+
# - build-essential: Compiladores C/C++ básicos.
|
| 11 |
+
RUN apt-get update && \
|
| 12 |
+
apt-get install -y --no-install-recommends \
|
| 13 |
+
gdal-bin \
|
| 14 |
+
libgdal-dev \
|
| 15 |
+
build-essential \
|
| 16 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 17 |
+
|
| 18 |
+
# 2. Configurar el entorno de la aplicación
|
| 19 |
+
WORKDIR /app
|
| 20 |
+
|
| 21 |
+
# Crear un usuario no-root por seguridad, una buena práctica en contenedores
|
| 22 |
+
RUN useradd -m -u 1000 user
|
| 23 |
+
USER user
|
| 24 |
+
ENV HOME=/home/user \
|
| 25 |
+
PATH=/home/user/.local/bin:$PATH
|
| 26 |
+
|
| 27 |
+
# 3. Instalar dependencias de Python
|
| 28 |
+
# Copiar solo requirements.txt primero para aprovechar el caché de capas de Docker.
|
| 29 |
+
# Si requirements.txt no cambia, Docker no volverá a ejecutar este paso.
|
| 30 |
+
COPY --chown=user requirements.txt .
|
| 31 |
+
|
| 32 |
+
# Instalar usando pip en el directorio del usuario, sin guardar caché para mantener la imagen ligera.
|
| 33 |
+
RUN pip install --no-cache-dir --user -r requirements.txt
|
| 34 |
+
|
| 35 |
+
# 4. Copiar el resto del código de la aplicación
|
| 36 |
+
# El 'chown' asegura que el usuario 'user' sea el propietario de los archivos.
|
| 37 |
+
COPY --chown=user . .
|
| 38 |
+
|
| 39 |
+
# 5. Exponer el puerto que Hugging Face Spaces espera
|
| 40 |
+
EXPOSE 7860
|
| 41 |
+
|
| 42 |
+
# 6. Comando para ejecutar la aplicación
|
| 43 |
+
# Usar Gunicorn como un servidor WSGI de producción, no el servidor de desarrollo de Dash.
|
| 44 |
+
# app_00:server -> Busca la variable 'server' en el archivo 'app_00.py'.
|
| 45 |
+
# --bind 0.0.0.0:7860 -> Escucha en todas las interfaces de red en el puerto 7860.
|
| 46 |
+
CMD ["gunicorn", "app_00:server", "--workers", "4", "--bind", "0.0.0.0:7860"]
|
README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Dashboard Geoespacial DuckDB
|
| 3 |
+
emoji: 🗺️
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Dashboard Geoespacial con DuckDB y GeoParquet
|
| 11 |
+
|
| 12 |
+
Este Space demuestra la capacidad de realizar análisis geoespaciales de alto rendimiento
|
| 13 |
+
leyendo archivos GeoParquet locales con DuckDB dentro de un entorno contenedorizado.
|
| 14 |
+
|
| 15 |
+
**Tecnologías:**
|
| 16 |
+
- Dash
|
| 17 |
+
- DuckDB (con extensión `spatial`)
|
| 18 |
+
- GeoPandas
|
| 19 |
+
- Docker
|
| 20 |
+
- Git LFS
|
app_00.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dash
|
| 2 |
+
from dash import html
|
| 3 |
+
import duckdb
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
# --- Configuración ---
|
| 7 |
+
CENSO_PATH = "./data/censo_manzanas_optimizado.gpq"
|
| 8 |
+
|
| 9 |
+
# --- Inicialización de la App ---
|
| 10 |
+
# Inicializa la aplicación Dash
|
| 11 |
+
app = dash.Dash(__name__)
|
| 12 |
+
# Variable 'server' que Gunicorn usará para iniciar la aplicación
|
| 13 |
+
server = app.server
|
| 14 |
+
|
| 15 |
+
# --- Lógica de la Consulta ---
|
| 16 |
+
# Esta función se ejecuta UNA VEZ cuando la aplicación se inicia.
|
| 17 |
+
def get_total_manzanas():
|
| 18 |
+
"""
|
| 19 |
+
Realiza una consulta simple para contar el total de filas en el archivo GeoParquet.
|
| 20 |
+
"""
|
| 21 |
+
if not os.path.exists(CENSO_PATH):
|
| 22 |
+
return "Error: No se encontró el archivo de datos. Asegúrate de que exista en ./data/"
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
con = duckdb.connect(database=':memory:')
|
| 26 |
+
# La extensión espacial es necesaria para que DuckDB entienda el tipo GEOMETRY del GeoParquet
|
| 27 |
+
con.install_extension("spatial")
|
| 28 |
+
con.load_extension("spatial")
|
| 29 |
+
|
| 30 |
+
query = f"SELECT COUNT(*) FROM read_parquet('{CENSO_PATH}');"
|
| 31 |
+
result = con.execute(query).fetchdf().iloc[0, 0]
|
| 32 |
+
con.close()
|
| 33 |
+
return f"¡Éxito! El contenedor Docker y DuckDB funcionan. Total de manzanas leídas: {result}"
|
| 34 |
+
except Exception as e:
|
| 35 |
+
return f"Error durante la consulta de DuckDB: {e}"
|
| 36 |
+
|
| 37 |
+
# --- Layout de la Aplicación ---
|
| 38 |
+
# Define la estructura visual de la página.
|
| 39 |
+
app.layout = html.Div(style={'textAlign': 'center', 'fontFamily': 'Arial'}, children=[
|
| 40 |
+
html.H1("Prueba de Despliegue: Dash + DuckDB en Hugging Face Spaces"),
|
| 41 |
+
html.Hr(),
|
| 42 |
+
html.H2("Resultado de la consulta de prueba:"),
|
| 43 |
+
html.P(
|
| 44 |
+
id='query-result',
|
| 45 |
+
children=get_total_manzanas(), # Llama a la función para obtener el resultado
|
| 46 |
+
style={'fontSize': '20px', 'color': 'green' if 'Éxito' in get_total_manzanas() else 'red'}
|
| 47 |
+
)
|
| 48 |
+
])
|
| 49 |
+
|
| 50 |
+
# --- Punto de Entrada (para desarrollo local) ---
|
| 51 |
+
if __name__ == '__main__':
|
| 52 |
+
# Este bloque solo se ejecuta si corres 'python app_00.py' localmente.
|
| 53 |
+
# Gunicorn no lo usa.
|
| 54 |
+
app.run_server(debug=True)
|
data/censo_manzanas_optimizado.gpq
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:340b4c8f89ee22b905280f8c594ecc796756bd85780404e1d9867ca61a2af4ce
|
| 3 |
+
size 287211917
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dash
|
| 2 |
+
dash-bootstrap-components
|
| 3 |
+
dash-leaflet
|
| 4 |
+
plotly
|
| 5 |
+
gunicorn
|
| 6 |
+
duckdb>=0.10.0
|
| 7 |
+
geopandas
|