Este proyecto documenta el diseño e implementación de una plataforma de datos full-stack y de código abierto, desarrollada para reemplazar una infraestructura analítica comercial costosa y rígida. A través de la migración de la base de datos relacional de AdventureWorks desde SQL Server hacia PostgreSQL, el desarrollo de pipelines ETL modulares en Python y la integración de modelos predictivos de Machine Learning, se transformó un sistema de reportes tradicional en un motor independiente y escalable, centralizado en una aplicación web interactiva con Flask y Docker.
El Desafío del Negocio (The Problem Statement)
AdventureWorks Inc. gestionaba su inteligencia de negocio y reportería a través de una suite de analítica comercial propietaria y tradicional basada en licencias de software cerrado. Al escalar la organización, el equipo se enfrentó a tres bloqueos críticos:
Escala Exponencial de Costos por Licenciamiento: El modelo de cobro por usuario o por núcleo se volvió financieramente insostenible ante la creciente demanda de acceso concurrente por parte de Ventas, RRHH, Producción y Compras.
Limitaciones de Integración con Data Science y ML: El ecosistema cerrado dificultaba acoplar modelos avanzados de Machine Learning (como forecasting de series temporales con Prophet o clasificación con XGBoost) en los flujos de datos sin recurrir a costosos módulos de IA adicionales.
Dependencia del Proveedor (Vendor Lock-in): La falta de control sobre el procesamiento impedía personalizar la interfaz a nivel corporativo y flexibilizar el despliegue en infraestructura propia.
Nuestra Solución:
Arquitectura Full-Stack Data de Código Abierto. Diseñamos y desarrollamos una plataforma web propietaria, independiente y end-to-end que migró la infraestructura y transformó los reportes rígidos del pasado en un motor analítico y predictivo centralizado:
Pipeline del proyecto
Fase 1: Ingesta, Auditoría y Exploración Profunda (OLTP)
Objetivo: Comprender las reglas y la estructura exacta del negocio para mitigar sesgos en las etapas de transformación y modelado.
Aprovisionamiento: Extracción y restauración del backup relacional nativo (.bak) de AdventureWorks 2022 en SQL Server.
Data Quality & Perfilado Inicial: Auditoría técnica mediante scripts SQL de exploración individual para cada una de las 18 tablas relevantes, documentando rangos de fechas, valores únicos y patrones de columnas clave.
Garantía de Integridad: Identificación exhaustiva de claves primarias y foráneas reales, consolidando las dinámicas operacionales en un Diccionario de Datos maestro y un diagrama relacional completo.
Fase 2: Diseño del Modelo Analítico Relacional y Requerimientos
Objetivo: Traducir los objetivos corporativos en una arquitectura de datos técnica limpia y estructurada.
Definición de Granularidad: Establecimiento preciso del nivel de detalle de las entidades operativas (ej. Ventas por línea de pedido, Inventarios por snapshots de producto/almacén y Finanzas por periodo contable).
Mapeo de KPIs: Selección de indicadores estratégicos para una visión de 360° (Sales Amount, Total Product Cost, Profit, Margen %, Tendencias YoY/MoM, Ticket Promedio y Alertas de Stock).
Gobernanza Técnica: Generación de un Documento de Requerimientos Funcionales como base para validar formalmente las necesidades lógicas con cada área antes de iniciar el código.
Fase 3: Extracción y Preparación mediante Vistas SQL
Objetivo: Proveer una capa de datos purificada, segura y optimizada desde la base de origen.
Desacoplamiento Estructural: Creación de vistas en SQL Server diseñadas según los requerimientos funcionales para pre-estructurar el set de datos.
Seguridad y Anonimización: Selección rigurosa de atributos descriptivos y demográficos para segmentación, aplicando políticas de privacidad al anonimizar o remover datos sensibles de clientes.
Cómputo en Base de Datos: Implementación de transformaciones básicas directamente en el motor de origen (como cálculo dinámico de edad y unificación de jerarquías de productos) para aligerar las fases posteriores de procesamiento en Python.
Fase 4: Migración Heterogénea de Bases de Datos
Objetivo: Romper la dependencia con el proveedor tradicional migrando el núcleo de la compañía a un motor Open-Source (PostgreSQL) sin alterar su estructura relacional original.
Schema Mapping: Traducción de tipos de datos, restricciones e incompatibilidades entre SQL Server y PostgreSQL mediante el uso de Python.
Pipelines de Ingesta: Diseño de scripts de migración progresiva y por lotes para mover de manera controlada el modelo relacional puro, asegurando consistencia matemática absoluta entre ambos motores.
Stack Principal: SQL Server, PostgreSQL, SQLAlchemy, PyODBC, Psycopg2 y Pandas.
Fase 5: Pipeline ETL y Automatización con Python
Objetivo: Construir un pipeline reproducible con un solo comando que extraiga, limpie y guarde datos curados listos para consumo concurrente.
Pipeline Modular (Jupyter/Scripts): Desarrollo de notebooks de experimentación consolidados posteriormente en un script ejecutable (etl_pipeline.py) alimentado por un archivo central de configuración (config.py).
Feature Engineering & Limpieza: Tratamiento automatizado de valores nulos mediante reglas específicas según la columna, conversión estructurada de tipos/fechas y eliminación de duplicados mediante funciones reutilizables (CheckData).
Persistencia Eficiente (Formatos de Alto Rendimiento): Exportación de datasets limpios y validados a disco en formato Parquet (para optimizar espacio y velocidad de lectura).
Trazabilidad y Auditoría: Integración de logs detallados de seguimiento en consola para registrar el éxito/fallo del pipeline y mantenimiento de un registro maestro de control (datasets_control.xlsx) con métricas de filas y columnas procesadas.
Fase 6: Optimización de la Capa Analítica en PostgreSQL
Objetivo: Centralizar la lógica analítica pesada dentro de PostgreSQL para mitigar la carga computacional en el backend de la aplicación.
Modelado Lógico: Configuración de Vistas (Views) enriquecidas y almacenamiento indexado en PostgreSQL directamente sobre las tablas migradas.
Agregación Avanzada: Construcción de consultas complejas diseñadas para unificar la información dispersa de las áreas de Sales, HR, Product Performance, Supply Chain y Customer Analytics en estructuras consolidadas.
Fase 7: Backend Analítico y Arquitectura de Software
Objetivo: Construir la capa lógica del servidor encargada de procesar peticiones y unificar los datos analíticos de la empresa.
Backend Engineering: Desarrollo de una aplicación web robusta en Python utilizando Flask, estructurada de forma modular mediante Blueprints para segmentar los servicios de cada departamento de la compañía.
Persistencia Dinámica y APIs: Conexión interactiva a PostgreSQL para leer de forma nativa los archivos estructurados y las vistas optimizadas, garantizando tiempos mínimos de respuesta del servidor (latencia en milisegundos).
Fase 8: Pipeline de Machine Learning y Analítica Predictiva
Objetivo: Escalar la plataforma hacia la analítica predictiva, dotando al sistema de la capacidad de anticipar eventos críticos de negocio.
Modelado de Inteligencia Artificial: Diseño e implementación de modelos supervisados y de análisis temporal utilizando Scikit-Learn, XGBoost y Prophet.
Casos de Uso Operacionales:
Forecasting de Ventas: Modelos de series de tiempo para proyectar la demanda mensual e ingresos.
Clasificación de Churn: Modelos predictivos para calcular la probabilidad de abandono de clientes recurrentes.
Optimización de Cadena de Suministro: Predicción de quiebres de stock basándose en flujos históricos de inventario.
Inferencia en Producción: Creación de un script automatizado para la extracción de features, reentrenamiento de modelos y serialización de artefactos (.pkl / ONNX), consumidos directamente por la API de Flask para generar predicciones en tiempo real. [1]
Fase 9: Visualización de Datos e Interfaces de Usuario (Frontend)
Objetivo: Democratizar los insights corporativos permitiendo a la mesa directiva evaluar el rendimiento histórico y forecast predictivos en una sola pantalla unificada.
Frontend Interactivo: Creación de una interfaz web multipágina y responsive codificada con Plotly y Flask, reemplazando por completo los visualizadores de la suite comercial rígida.
Capa Semántica Unificada: Ocultamiento de columnas técnicas y exposición directa al usuario de medidas avanzadas de KPIs históricos calculados en base de datos junto a las proyecciones dinámicas estimadas por los modelos de Machine Learning.
Fase 10: Despliegue en Producción y DevOps (MLOps)
Objetivo: Publicar el sistema bajo una infraestructura escalable, segura y con costos de mantenimiento fijos, logrando la independencia total del negocio.
Infraestructura Cloud: Aprovisionamiento, hardening y configuración de políticas de seguridad en un Servidor Virtual Privado (VPS) con Ubuntu Server.
Contenerización Completa: Aislamiento de entornos y servicios (PostgreSQL, la aplicación Flask con sus pipelines de ML, las dependencias de Python y los servidores web) mediante Docker y Docker Compose.
Arquitectura de Producción: Implementación del servidor WSGI Gunicorn acoplado a Nginx como proxy inverso, garantizando la gestión eficiente de peticiones, compresión de assets, balanceo de carga básico y cifrado SSL.
En este proyecto desarrollaremos una aplicación web con Flask que funcionará como el portal principal de nuestro VPS. Su propósito es servir como punto central de acceso a todos los proyectos, aplicaciones y servicios que vayamos desplegando a lo largo del tiempo.
La página estará disponible en el subdominio projects.fernandorioseco.es y actuará como un auténtico escaparate técnico y profesional. Desde ella será posible entrar a las aplicaciones de proyectos realizados. También cada proyecto contará con su propio subdominio dentro del VPS.
En ese subdominio se desplegará la aplicación funcional del proyecto, es decir, la herramienta o servicio que el usuario podrá utilizar directamente, ya sea una aplicación desarrollada con Flask, Streamlit, FastAPI u otra tecnología.
La explicación detallada del proyecto —objetivos, arquitectura, tecnologías empleadas, proceso de desarrollo y aprendizajes obtenidos— no se mostrará dentro de la aplicación, sino en el sitio principal del portfolio, donde cada proyecto tendrá su propia ficha o artículo descriptivo.
De esta forma se separan claramente dos elementos:
La documentación del proyecto, alojada en el sitio principal del portfolio.
La aplicación desplegada, accesible mediante su subdominio correspondiente.
Este enfoque permite mantener el portfolio organizado y profesional, ofreciendo por un lado la explicación técnica del trabajo realizado y, por otro, el acceso directo a la aplicación en funcionamiento.
Estructura general del proyecto
El propósito del proyecto es construir una aplicación web en Flask desplegada en projects.fernandorioseco.es que actuará como portal central del VPS.
Objetivos
Centralizar el acceso a proyectos.
Mostrar información sobre el VPS.
Presentar el stack tecnológico.
Servir como portfolio profesional.
Para desarrollar el Projects Portal seguiremos una metodología organizada que abarcará desde el diseño de la aplicación en local hasta su despliegue definitivo en el VPS. El proyecto no solo consistirá en construir una aplicación web con Flask, sino también en integrarla dentro de la infraestructura del servidor y conectarla con el resto de aplicaciones del portfolio.
La estructura general del proyecto se dividirá en las siguientes fases:
1 – Planificación y Diseño: En primer lugar definiremos el objetivo del portal, las secciones que incluirá y la experiencia de usuario que queremos ofrecer. En esta fase estableceremos la arquitectura de la aplicación, el diseño de la interfaz y la información que se mostrará en cada sección.
2 – Setup de la Aplicación yControl de Versiones con Git y GitHub: Preparar el setup del proyecto con una app mínima, inicializar un repositorio Git y publicar el código en GitHub para mantener un historial de cambios y facilitar el despliegue en el servidor.
3 – Desarrollo Local: Crearemos el proyecto Flask en nuestro entorno local, organizaremos la estructura de carpetas, diseñaremos las plantillas HTML y desarrollaremos los estilos CSS y la lógica necesaria para mostrar los proyectos de forma dinámica.
4 – Configuración del Dominio: Crearemos el subdominio projects.fernandorioseco.es y configuraremos el registro DNS para que apunte a la dirección IP pública del VPS.
5 – Preparación del VPS: Crearemos el directorio del proyecto en el servidor, clonaremos el repositorio desde GitHub, configuraremos el entorno virtual de Python e instalaremos todas las dependencias necesarias.
6 – Configuración del Servicio con Gunicorn y systemd: Configuraremos Gunicorn como servidor WSGI y crearemos un servicio de systemd para que la aplicación se ejecute automáticamente y permanezca activa tras reinicios del sistema.
7 – Configuración de Nginx y HTTPS: Configuraremos Nginx como proxy inverso para publicar la aplicación en Internet y habilitaremos HTTPS mediante certificados SSL de Let’s Encrypt.
8 – Actualización y Mantenimiento: Definiremos el procedimiento para desplegar nuevas versiones del portal y añadir proyectos al catálogo mediante archivos JSON individuales.
9 –Mejoras y Evolución: Una vez desplegado el portal, podremos incorporar nuevas funcionalidades, como filtros por tecnología, estadísticas del VPS en tiempo real o un panel de administración para gestionar los proyectos desde una interfaz web.
1.Planificación y Diseño
Arquitectura de la Aplicación
La aplicación Projects Portal se desarrollará con Flask siguiendo una arquitectura sencilla, modular y escalable. El objetivo es separar claramente la lógica de negocio, la presentación y los datos para facilitar el mantenimiento y la incorporación de nuevos proyectos.
La arquitectura se basará en los siguientes componentes:
Cliente web (navegador).
Servidor web Nginx.
Servidor de aplicaciones Gunicorn.
Aplicación Flask.
Plantillas HTML con Jinja2.
Archivos estáticos (CSS, JavaScript e imágenes).
Archivo de configuración con los datos de los proyectos.
app/__init__.py: Crea y configura la aplicación Flask, registra las rutas y devuelve la instancia principal de la aplicación.
app/routes.py: Define las rutas de la aplicación y establece que la página principal renderice la plantilla index.html.
run.py: Ejecuta la aplicación en modo desarrollo para realizar pruebas en el entorno local.
wsgi.py: Expone la aplicación para que pueda ser ejecutada por un servidor WSGI como Gunicorn en producción.
app/templates/index.html: Crea la estructura HTML básica de la página principal con el nombre del portal y una breve descripción del laboratorio.
app/data/projects.json: Almacenará la información de los proyectos que se mostrarán dinámicamente en el portal.
requirements.txt: Guarda el listado de dependencias Python necesarias para ejecutar el proyecto.
config.py: Centraliza la configuración general de la aplicación.
app/static/css/: Contendrá las hojas de estilo CSS de la aplicación.
app/static/js/: Contendrá los archivos JavaScript utilizados en la interfaz.
app/static/img/: Almacenará imágenes, iconos y recursos gráficos.
app/templates/base.html: Servirá como plantilla base común para todas las páginas del sitio.
app/templates/components/: Almacenará componentes reutilizables de la interfaz, como la barra de navegación o el footer.
Patrón Arquitectónico
El portal Projects Portal seguirá una versión simplificada del patrón arquitectónico MVC (Model-View-Controller), uno de los modelos de diseño más utilizados en el desarrollo de aplicaciones web. El objetivo es separar claramente los datos, la lógica de la aplicación, y la presentación visual.
Componente MVC
Implementación
Model
app/data/projects.json
View
app/templates/*.html
Controller
app/routes.py
Escalabilidad
La arquitectura permite evolucionar fácilmente hacia:
Base de datos relacional.
Panel de administración.
API REST.
Autenticación.
Caché con Redis.
Contenerización con Docker.
Estructura de la Interfaz de Usuario (UI)
La aplicación Projects Portal estará compuesta por una única página principal diseñada para presentar el laboratorio técnico y ofrecer acceso directo a todos los proyectos desplegados en el VPS. La interfaz se organizará en varias secciones claramente diferenciadas, cada una con una función específica dentro de la experiencia de usuario.
Boceto de referencia aproximado generado por IA:
Barra de Navegación (Navbar): La página comenzará con una barra de navegación fija en la parte superior. Elementos de la navbar:
El visitante accede a projects.fernandorioseco.es.
Visualiza la presentación del laboratorio.
Consulta la infraestructura y tecnologías utilizadas.
Explora las tarjetas de proyectos.
Accede a:
La aplicación en producción.
La documentación del proyecto.
El código fuente.
La documentación de la API, si existe.
Navega a GitHub, blog o LinkedIn.
Objetivo de la interfaz
La UI debe transmitir una imagen:
Profesional.
Moderna.
Tecnológica.
Clara y fácil de navegar.
Al mismo tiempo, debe funcionar como un punto central desde el que cualquier visitante pueda comprender tu infraestructura y acceder rápidamente a todos los proyectos de tu portfolio.
2. Setup de la Aplicación yControl de Versiones
Setup del Proyecto
En esta fase realizaremos la configuración inicial del proyecto en nuestro entorno local. El objetivo es preparar la estructura base de la aplicación, crear un entorno virtual, instalar las dependencias necesarias y verificar que Flask funciona correctamente antes de comenzar con el desarrollo de la interfaz. En esta fase desarrollamos:
La interfaz del Projects Portal se construye siguiendo una arquitectura de plantillas modular basada en el motor de plantillas Jinja, integrado de forma nativa en Flask. Este enfoque permite dividir la interfaz en componentes independientes y reutilizables, facilitando el mantenimiento y la escalabilidad de la aplicación.
La finalidad de esta arquitectura es separar la estructura visual del sitio en piezas con responsabilidades bien definidas:
Una plantilla base común.
Una página principal.
Componentes reutilizables.
Gracias a esta organización, cada sección de la interfaz puede desarrollarse y modificarse de forma aislada. Las plantillas HTML se almacenan en el directorio: app/templates/. La estructura adoptada es la siguiente:
Plantilla base de la aplicación. Define la estructura HTML común del sitio, incluyendo la cabecera del documento, la carga de estilos, la barra de navegación, el bloque de contenido principal y el footer.
index.html
Página principal del portal. Ensambla secuencialmente los distintos componentes visuales mediante instrucciones {% include %}. Contiene muy poca lógica y actúa como orquestador de la interfaz.
components/
Directorio que contiene las secciones independientes que conforman la página principal. Cada archivo representa un bloque funcional concreto.
navbar.html
Barra de navegación superior.
hero.html
Sección principal de presentación del portal.
vps_info.html
Información técnica del servidor VPS.
tech_stack.html
Tecnologías y herramientas utilizadas en el laboratorio.
projects.html
Listado dinámico de proyectos desplegados.
resources.html
Enlaces externos a documentación, GitHub, blog y LinkedIn.
Jinja2 se encarga automáticamente de resolver la herencia e inclusiones.
base.html – plantilla base de la aplicación
El archivo base.html constituye la plantilla base del Projects Portal. Su función es definir la estructura HTML común que compartirán todas las páginas del sitio. Este enfoque permite centralizar los elementos globales de la interfaz y evitar la duplicación de código. Ubicación del archivo: app/templates/base.html
Propósito
La plantilla base se encarga de definir la estructura general del documento HTML e incluir aquellos elementos que deben estar presentes en todas las páginas de la aplicación. En concreto, base.html incorpora:
Definir la estructura HTML general del documento.
Configurar las etiquetas meta necesarias.
Establecer un título por defecto.
Cargar la hoja de estilos.
Incluir la barra de navegación.
Definir un bloque de contenido que heredarán las páginas hijas.
Incluir el footer.
Estructura conceptual
[ DOCUMENTO HTML ] (Template Maestro) │ ├── [ HEAD ] (Configuración Invisible) │ ├── Metadatos (Viewport, Charset) │ ├── Título Dinámico {% block title %} │ └── Estilos Propios (styles.css) │ └── [ BODY ] (Estructura Visible) │ ├── [ HEADER ] ─── {% include "navbar.html" %} ── (Componente Reutilizable) │ ├── [ MAIN ] ───── {% block content %} ──────── (Espacio Inyectable/Variable) │ *Aquí se carga el contenido* │ *específico de cada ruta* │ └── [ FOOTER ] ─── {% include "footer.html" %} ── (Componente Reutilizable)
Elementos de Jinja2 utilizados
title: este permite definir un título por defecto y sobrescribirlo desde plantillas hijas.
content: reserva el espacio donde se insertará el contenido específico de cada página.
include: inserta componentes reutilizables como la barra de navegación y el footer.
url_for(): genera automáticamente la URL correcta para los archivos estáticos.
Relación con el resto de plantillas
base.html es utilizada por index.html mediante la instrucción: {% extends "base.html" %}. A partir de ese momento, index.html solo necesita definir el contenido correspondiente al bloque content.
La barra de navegación es el componente situado en la parte superior de la página y proporciona acceso a recursos externos relevantes. Al tratarse de un elemento común a todas las páginas del sitio, se implementa como un componente independiente que es incluido desde base.html. Ubicación del archivo: app/templates/components/navbar.html
Propósito del componente
Identificar visualmente el sitio mediante el nombre del portal.
Proporcionar acceso a recursos externos como GitHub, el blog y LinkedIn.
Los enlaces externos se abren en una nueva pestaña mediante target="_blank".
Relación con base.html
La plantilla base incorpora este componente mediante: {% include "components/navbar.html" %}. De esta forma, cualquier modificación realizada en navbar.html se reflejará automáticamente en todas las páginas del sitio.
Diseño visual
La navbar se diseñará con las siguientes características:
Posición fija o sticky en la parte superior.
Fondo blanco o semitransparente.
Sombra ligera.
Distribución horizontal.
Adaptación responsive para dispositivos móviles.
hero.html – sección principal de presentación
Define la sección superior de la página principal y constituye el primer elemento visual que verá el visitante al acceder al portal. Su objetivo es comunicar de forma inmediata qué es Projects Portal, cuál es su propósito y qué tipo de proyectos alberga. Ubicación del archivo: app/templates/components/hero.html
Elementos: La sección estará compuesta por dos columnas.
La sección presentará el portal como: Un laboratorio personal desplegado en un VPS, orientado a DevOps, Ciencia de Datos, MLOps e Ingeniería de Inteligencia Artificial.
Diseño visual
El Hero Section tendrá las siguientes características:
Fondo azul oscuro.
Texto en color blanco.
Diseño en dos columnas.
Botones de llamada a la acción.
Imagen ilustrativa a la derecha.
Amplio espaciado vertical.
Relación con index.html
La página principal incluirá este componente mediante: {% include "components/hero.html" %}
vps_info.html – infraestructura del VPS
El componente vps_info.html muestra un resumen de las principales características técnicas del servidor donde se alojan el Projects Portal y el resto de aplicaciones del portfolio. Esta sección aporta contexto técnico y refuerza la credibilidad del portal al evidenciar que los proyectos se ejecutan sobre una infraestructura real administrada por el propio autor. Ubicación del archivo: app/templates/components/vps_info.html
Propósito
La sección tiene como objetivo presentar, de forma visual y estructurada, la configuración básica del VPS utilizado como entorno de despliegue. Permite al visitante conocer:
Los iconos utilizados en esta sección se almacenarán en: app/static/img/icons/
Relación con index.html
La página principal incluirá esta sección mediante: {% include "components/vps_info.html" %}
projects.html – catálogo dinámico de proyectos
El componente projects.html es la sección más importante del portal, ya que muestra las aplicaciones desplegadas en el VPS y proporciona acceso directo a cada una de ellas.
A diferencia del resto de secciones, su contenido se genera dinámicamente a partir de los datos almacenados en projects.json. El archivo se ubica en: app/templates/components/projects.html.
La sección incorpora un buscador dinámico que permite filtrar proyectos por cualquier término relevante, como tecnologías, categorías, nombre o palabras incluidas en la descripción. El buscador permite al usuario encontrar proyectos escribiendo uno o varios términos de búsqueda.
Cada tarjeta de proyecto almacena en el atributo data-search el contenido que será evaluado por JavaScript. Este atributo incluye: nombre del proyecto, descripción y tecnologías utilizadas.
Lógica de filtrado
El archivo main.js:
Captura el contenido del input.
Divide el texto por comas.
Normaliza los términos.
Recorre todas las tarjetas.
Muestra solo las que contienen todos los términos.
Propósito
Esta sección tiene como objetivo presentar cada proyecto del portfolio mediante una tarjeta con información resumida y enlaces relevantes. Cada tarjeta permitirá al visitante acceder a:
Los datos de los proyectos se almacenarán en el archivo: app/data/projects.json. Cada registro del archivo contendrá toda la información necesaria para construir una tarjeta.
Información mostrada en cada tarjeta
Nombre del proyecto.
Descripción breve.
Tecnologías utilizadas.
Enlaces de acción.
Estructura conceptual de una tarjeta
[ DATOS: Lista de Proyectos ] │ ▼ (Ciclo for project in projects)[ ARTICLE: .project-card ] <──────────────────┐ │ │ ├── [ .project-title ] ─── {{ project.name }} │ │ │ ├── [ .project-description ] ─ {{ desc }} │ (Repite por cada │ │ proyecto en la lista) ├── [ .project-tech ] ────────────────────────┤ │ └── [ .tech-badge ] ─ {{ tech }} (Bucle) │ │ │ └── [ .project-links ] ───────────────────────┘ ├── App URL ├── Documentation ├── GitHub └── [ IF: API URL ] ── (Carga condicional)
Generación dinámica con Jinja2
La plantilla recorrerá la lista projects enviada desde Flask mediante un bucle for. Por cada elemento del JSON se generará automáticamente una nueva tarjeta.
Relación con Flask
La ruta principal cargará los datos y los enviará a la plantilla: return render_template("index.html", projects=projects)
Archivos involucrados
La página principal incluirá esta sección mediante: {% include "components/projects.html" %}, en la acción de búsqueda se involucran :
Archivo
Función
projects.html
Campo de búsqueda y atributo data-search.
main.js
Lógica de filtrado.
styles.css
Estilos del buscador.
base.html
Carga del archivo JavaScript.
resources.html – recursos y enlaces externos
El componente resources.html agrupa los enlaces externos más relevantes relacionados con el Projects Portal y con el perfil profesional del autor. Su objetivo es ofrecer al visitante un acceso rápido a la documentación técnica, al código fuente y a los perfiles profesionales asociados al proyecto. Ubicación del archivo: app/templates/components/resources.html
Propósito
Esta sección actúa como un bloque complementario al catálogo de proyectos. Mientras que cada tarjeta de proyecto contiene enlaces específicos, resources.html reúne los recursos generales del portal y del autor.
La página principal incluirá esta sección mediante: {% include "components/resources.html" %}
footer.html – pie de página
El componente footer.html define la sección final del sitio y contiene información general sobre el portal y enlaces complementarios. Aunque es un elemento sencillo, cumple una función importante al cerrar visualmente la página y ofrecer referencias adicionales al visitante. Ubicación del archivo: app/templates/components/footer.html
Propósito
Mostrar información de copyright.
Identificar al autor del portal.
Incluir enlaces a recursos relevantes.
Cerrar visualmente la página de forma consistente.
La plantilla base lo incluye mediante: {% include "components/footer.html" %}
index.html – ensamblado de la página principal
El archivo index.html es la plantilla principal del Projects Portal. Su función no es definir el contenido detallado de cada sección, sino ensamblar todos los componentes que conforman la página de inicio. Actúa como un punto de integración donde se combinan la plantilla base y los distintos componentes HTML desarrollados previamente. Ubicación del archivo: app/templates/index.html
Propósito
index.html tiene tres responsabilidades principales:
Heredar la estructura general definida en base.html.
Definir el contenido del bloque principal.
Incluir, en el orden correcto, todos los componentes de la interfaz.
Componentes incluidos
La página principal incorpora las siguientes secciones:
hero.html
vps_info.html
tech_stack.html
projects.html
resources.html
La barra de navegación y el footer no se incluyen directamente aquí, ya que son incorporados automáticamente por base.html.
Relación con base.html
La plantilla comienza con: {% extends "base.html" %}. Esto indica que utilizará la estructura general definida en la plantilla base.
Definición del contenido principal
Todo el contenido de la página se inserta dentro del bloque:
Una vez definida la estructura HTML de la aplicación, el siguiente paso consiste en desarrollar la capa de presentación mediante hojas de estilo CSS. En el Projects Portal se ha optado por utilizar CSS puro, sin frameworks externos como Bootstrap, con el objetivo de mantener un control total sobre el diseño y comprender en detalle el comportamiento visual de cada componente. Todos los estilos del proyecto se almacenan inicialmente en: app/static/css/styles.css
La arquitectura CSS tiene como finalidad:
Centralizar todos los estilos de la aplicación.
Mantener una organización clara y escalable.
Reutilizar reglas comunes.
Facilitar el mantenimiento.
Garantizar consistencia visual.
El archivo styles.css se estructurará en bloques claramente diferenciados.
styles.css├── Variables CSS├── Reset básico├── Estilos globales├── Componentes reutilizables├── Layout├── Estilos por sección└── Media queries
La arquitectura CSS del Projects Portal se basa en un único archivo styles.css, organizado en bloques lógicos que incluyen variables, estilos globales, componentes reutilizables y reglas específicas para cada sección de la interfaz.
El Projects Portal ha sido diseñado para adaptarse correctamente a distintos tamaños de pantalla, garantizando una experiencia de usuario adecuada tanto en ordenadores de escritorio como en tablets y teléfonos móviles.
El objetivo del diseño responsive es que todos los elementos de la interfaz se ajusten automáticamente al espacio disponible sin necesidad de crear versiones separadas de la página.
Breakpoints
La adaptación a diferentes resoluciones se realiza mediante media queries. Los puntos de ruptura utilizados se corresponden con los tamaños habituales de dispositivos:
Móviles pequeños.
Tablets.
Portátiles.
Monitores de escritorio.
Técnicas utilizadas
Para lograr el comportamiento responsive se emplean:
CSS Grid.
Flexbox.
Media queries.
Unidades relativas (rem, %, fr).
Imágenes fluidas (max-width: 100%).
Resultado preliminar de la estructura + css
Haz clic en la imagen para ver.
Incorporación de Recursos Visuales y Ajustes Finales de UI
El propósito es transformar una interfaz funcional en una interfaz visualmente pulida.
Descarga de iconos
Optimización de imágenes
Integración de logos tecnológicos
Ilustración del Hero Section
Ajustes de CSS
Refinamiento de tipografía y colores
Corrección de detalles responsive
Revisión visual final
Resultado final de la aplicación web
4. Configuración del dominio
Una vez completado el desarrollo local de la aplicación, el siguiente paso consiste en asociarla a un dominio público para que pueda ser accesible desde Internet. En este proyecto, la aplicación Flask se publicará en el subdominio: projects.fernandorioseco.es
En el proveedor donde gestionas tu dominio, debes crear un registro de tipo A.
Campo
Valor
Tipo
A
Nombre
projects
Valor
IP pública del VPS
TTL
Auto
Verificar la propagación DNS
Una vez creado el registro, el subdominio debe resolver hacia la IP del servidor.
nslookup projects.fernandorioseco.es
5. Preparación del VPS
Una vez que la aplicación ha sido desarrollada localmente y el subdominio ya apunta al servidor, el siguiente paso consiste en preparar el VPS para alojar el proyecto.
En esta fase se crea la estructura de directorios en el servidor, se descarga el código fuente desde GitHub, se configura un entorno virtual de Python y se instalan las dependencias necesarias para ejecutar la aplicación.
La preparación del VPS tiene como finalidad:
Crear el directorio donde residirá el proyecto: /var/www.
Descargar el código fuente desde el repositorio remoto.
Configurar un entorno virtual aislado.
Instalar las librerías definidas en requirements.txt.
Verificar que la aplicación puede ejecutarse en el servidor.
# Crear la carpeta del proyectosudomkdir-p/var/www/projects-portal# Cambiar propietario y grupo de los archivos del proyectosudochown-R $USER:$USER /var/www/projects-portal# clonar repositorio desde githubgitclonehttps://github.com/fer78/projects-portal.git/var/www/projects-portal
Crear entorno virtual
# en sistemas Debian/Ubuntu no viene instalado por defectosudoaptinstallpython3.12-venv# Accede al directoriocd/var/www/projects-portal# Crea el entorno virtual y activalopython3-mvenvvenvsourcevenv/bin/activate# Instalar dependenciaspipinstall--upgradepippipinstall-rrequirements.txt# comprobar que las dependencias se instalaron correctamentepiplist
Probar la aplicación Flask.
python run.py
Si todo es correcto, Flask iniciará el servidor de desarrollo.
Probar Gunicorn
gunicorn --bind 127.0.0.1:8000 wsgi:app
Este comando confirma que la aplicación está lista para ejecutarse en producción.
Archivos implicados
Archivo
Función
requirements.txt
Lista de dependencias Python.
wsgi.py
Punto de entrada para Gunicorn.
venv/
Entorno virtual del proyecto.
run.py
Script de desarrollo local.
6. Configuración del servicio con Gunicorn y systemd
Una vez que la aplicación Flask funciona correctamente en el VPS, el siguiente paso consiste en ejecutarla como un servicio del sistema. Para ello utilizaremos:
Gunicorn como servidor WSGI.
systemd como gestor de servicios de Linux.
Esta configuración permite que la aplicación se inicie automáticamente al arrancar el servidor y se reinicie en caso de fallo.
La configuración del servicio persigue los siguientes objetivos:
Ejecutar la aplicación Flask en segundo plano.
Iniciar el servicio automáticamente en cada reinicio.
Reiniciar el proceso si se detiene inesperadamente.
[Unit]Description=Gunicorn service for Projects PortalAfter=network.target[Service]User=tu usuarioGroup=www-dataWorkingDirectory=/var/www/projects-portalEnvironment="PATH=/var/www/projects-portal/venv/bin"ExecStart=/var/www/projects-portal/venv/bin/gunicorn \ --workers 3 \ --bind 127.0.0.1:8000 \ wsgi:appRestart=always[Install]WantedBy=multi-user.target
Poner a punto el servicio
# reiniciar systemdsudosystemctldaemon-reload# habilitar el serviciosudosystemctlstartprojects-portal# verificar el estadosudosystemctlstatusprojects-portal
La aplicación arrancará automáticamente tras cada reinicio.
Los logs estarán disponibles mediante journalctl.
El proceso se reiniciará automáticamente ante fallos.
7. Configuración de Nginx y HTTPS
Una vez que la aplicación Flask se ejecuta correctamente mediante Gunicorn y systemd, el siguiente paso consiste en publicarla en Internet a través de un servidor web. Para ello utilizaremos:
Nginx como proxy inverso.
Certbot para obtener certificados SSL.
Let’s Encrypt como autoridad certificadora.
Esta etapa permite:
Asociar el subdominio projects.fernandorioseco.es a la aplicación.
El siguiente paso es crear un archivo de configuración de Nginx para la aplicación Flask. Ese archivo define cómo Nginx debe responder cuando alguien acceda a projects.fernandorioseco.es.
sudoln-s/etc/nginx/sites-available/projects-portal\/etc/nginx/sites-enabled/# Validar la configuraciónsudonginx-t# Recargar Nginxsudosystemctlreloadnginx
Configurar HTTPS con Let’s Encrypt
Generar el certificado SSL con Let’s Encrypt y Certbot.
sudocertbot--nginx-dprojects.fernandorioseco.es
Verificar acceso seguro
La aplicación deberá quedar disponible en:
https://projects.fernandorioseco.es
Flujo de resolución del dominio
El usuario accede al subdominio.
DNS devuelve la IP del VPS.
Nginx recibe la petición.
Nginx reenvía la petición a Gunicorn.
Gunicorn ejecuta Flask.
Flask devuelve la página renderizada.
8. Actualización y Mantenimiento
Una vez desplegado el portal, es importante definir un procedimiento claro para actualizar el código, incorporar nuevos proyectos y mantener la aplicación operativa en el tiempo. La arquitectura adoptada, basada en archivos JSON individuales y control de versiones con Git, simplifica enormemente estas tareas de establecer un flujo de trabajo para:
Desplegar nuevas versiones del código.
Añadir proyectos al catálogo.
Corregir errores.
Reiniciar servicios.
Supervisar el funcionamiento del portal.
Flujo de actualización del código
El proceso habitual consiste en:
Realizar cambios en el entorno local (añadir nuevos proyectos o funcionalidades)
Confirmar los cambios en Git.
Enviar los cambios al repositorio remoto.
Actualizar el VPS mediante git pull.
Reiniciar el servicio.
# ve a la carpeta del proyectocd/var/www/projects-portal# Actualiza los ficheros desde githubgitpulloriginmain# reinicia el servicio sudosystemctlrestartprojects-portal# verifica el funcionamientosudosystemctlstatusprojects-portal
El mantenimiento del portal se reduce a actualizar el repositorio Git, añadir nuevos archivos JSON y reiniciar el servicio Gunicorn cuando sea necesario.
Script de Actualización del Portal
En vez de estar haciendo secuencias de comandos en cada actualización, creamos un script en Bash que agilice el proceso. Este script descargará los últimos cambios desde GitHub, reiniciará el servicio de Gunicorn y mostrará el estado final de la aplicación.
Crear el Script
nano/var/www/projects-portal/update.sh
#!/bin/bashset-eecho"=========================================="echo" Updating Projects Portal"echo"=========================================="cd/var/www/projects-portalecho"Pulling latest changes from GitHub..."gitpulloriginmainecho"Restarting Gunicorn service..."sudosystemctlrestartprojects-portalecho"Checking service status..."sudosystemctlstatusprojects-portal--no-pagerecho"=========================================="echo" Update completed successfully"echo"=========================================="
Permisos de ejecución y añadir al PATH.
# permisos de ejecucionchmod+x/var/www/projects-portal/update.sh# añadir al path para ejecutar desde cualquier ubicacionsudoln-s/var/www/projects-portal/update.sh/usr/local/bin/update-portal
Ahora el script de actualización es un comando que puede ejecutarse desde cualquier ubicación
update-portal
9. Mejoras y Evolución
El Projects Portal se ha diseñado con una arquitectura modular y escalable que facilita la incorporación de nuevas funcionalidades. A medida que el catálogo crezca, el portal puede evolucionar desde una página estática hacia una plataforma más avanzada de gestión y visualización de proyectos.
Posibles mejoras
Buscador avanzado: Filtros por tecnología, por tipo de proyecto u ordenación.
Métricas del VPS: CPU, memoria, uso de disco, uptime, etc.
Panel de administración: Crear y editar proyectos desde una interfaz web, subir imágenes y recursos.
Base de datos: migración desde archivos JSON a PostgreSQL.
API REST: Exponer los proyectos mediante una API desarrollada con FastAPI.
Integración con GitHub: mostrar automáticamente estrellas, forks y fecha de última actualización.
Dashboards embebidos: integrar aplicaciones desarrolladas con Streamlit, Dash o Gradio.
Conclusiones
El desarrollo de Projects Portal ha permitido construir una aplicación web completa utilizando una arquitectura moderna basada en Python y Flask, con despliegue profesional sobre un VPS Linux.
Aunque funcionalmente se trata de un portal sencillo, el proyecto integra todos los componentes fundamentales que intervienen en el ciclo de vida de una aplicación web en producción: desarrollo local, control de versiones, despliegue, automatización de servicios, configuración del servidor web y publicación segura mediante HTTPS.
Conocimientos aplicados
A lo largo del proyecto se han puesto en práctica competencias técnicas de distintas áreas.
Desarrollo Backend
Programación en Python.
Desarrollo web con Flask.
Uso de plantillas con Jinja2.
Lectura dinámica de archivos JSON.
Diseño modular de componentes.
Frontend
HTML5 semántico.
CSS3 con diseño responsive.
JavaScript para búsqueda y filtrado.
Integración de recursos gráficos e iconografía.
DevOps y Linux
Administración de Ubuntu.
Gestión de permisos y estructura de directorios.
Uso de entornos virtuales.
Automatización con systemd.
Despliegue con Gunicorn.
Configuración de Nginx.
Redes e Infraestructura
Configuración de DNS.
Gestión de subdominios.
Resolución de nombres.
Configuración de HTTPS.
Certificados SSL con Let’s Encrypt.
Control de versiones
Uso de Git.
Publicación en GitHub.
Flujo de despliegue basado en git pull.
Diseño arquitectónico
El proyecto ha sido diseñado siguiendo principios de modularidad y mantenibilidad:
Plantillas HTML desacopladas.
Archivos JSON individuales por proyecto.
Separación entre contenido, lógica y presentación.
Servicio persistente gestionado por systemd.
Proxy inverso con Nginx.
Valor del proyecto
Projects Portal cumple una doble función.
Portfolio técnico: Actúa como punto central para acceder a todos los proyectos desplegados.
Laboratorio práctico: Sirve como entorno real para aplicar conocimientos.
Reflexión final
Projects Portal demuestra la capacidad de diseñar, desarrollar y desplegar una aplicación web completa siguiendo prácticas profesionales de ingeniería de software e infraestructura.
El proyecto integra desarrollo backend, frontend, administración de sistemas, redes y automatización, constituyendo una evidencia sólida de competencias en programación, análisis de datos, DevOps y tecnologías modernas de Inteligencia Artificial.