<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Evolve</title>
    <description>The latest articles on DEV Community by Evolve (@evolve-space).</description>
    <link>https://dev.to/evolve-space</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F13182%2F181b19dd-cb59-49d1-a5ef-f7e8087a388b.jpg</url>
      <title>DEV Community: Evolve</title>
      <link>https://dev.to/evolve-space</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/evolve-space"/>
    <language>en</language>
    <item>
      <title>Diseñando una Base de Datos para una Tienda Online con PostgreSQL y DBeaver</title>
      <dc:creator>Maria Angeles Ciobanu</dc:creator>
      <pubDate>Thu, 11 Jun 2026 17:33:29 +0000</pubDate>
      <link>https://dev.to/evolve-space/disenando-una-base-de-datos-para-una-tienda-online-con-postgresql-y-dbeaver-2ep</link>
      <guid>https://dev.to/evolve-space/disenando-una-base-de-datos-para-una-tienda-online-con-postgresql-y-dbeaver-2ep</guid>
      <description>&lt;h1&gt;
  
  
  Diseñando una Base de Datos para una Tienda Online con PostgreSQL y DBeaver
&lt;/h1&gt;

&lt;p&gt;Como parte de mi formación en Data Science e Inteligencia Artificial, he desarrollado un proyecto de bases de datos utilizando PostgreSQL y DBeaver con el objetivo de poner en práctica conceptos avanzados de SQL aplicados a un entorno real.&lt;/p&gt;

&lt;p&gt;El proyecto consiste en el diseño e implementación de una base de datos para la gestión de una tienda online, incluyendo clientes, productos, categorías, pedidos y pagos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tecnologías utilizadas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;DBeaver&lt;/li&gt;
&lt;li&gt;Git y GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Funcionalidades implementadas
&lt;/h2&gt;

&lt;p&gt;Durante el desarrollo del proyecto he trabajado con diferentes elementos fundamentales del lenguaje SQL:&lt;/p&gt;

&lt;h3&gt;
  
  
  Diseño de la base de datos
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Creación de tablas relacionales.&lt;/li&gt;
&lt;li&gt;Definición de claves primarias y foráneas.&lt;/li&gt;
&lt;li&gt;Integridad referencial entre entidades.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consultas SQL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SELECT básicos.&lt;/li&gt;
&lt;li&gt;Filtros mediante WHERE.&lt;/li&gt;
&lt;li&gt;Operadores LIKE y BETWEEN.&lt;/li&gt;
&lt;li&gt;Consultas anidadas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  JOINs
&lt;/h3&gt;

&lt;p&gt;Implementación de diferentes tipos de JOIN:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;INNER JOIN&lt;/li&gt;
&lt;li&gt;LEFT JOIN&lt;/li&gt;
&lt;li&gt;RIGHT JOIN&lt;/li&gt;
&lt;li&gt;FULL OUTER JOIN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;permitiendo relacionar información procedente de múltiples tablas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Indicadores y métricas
&lt;/h3&gt;

&lt;p&gt;Se han desarrollado consultas orientadas al análisis de negocio, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ventas totales.&lt;/li&gt;
&lt;li&gt;Ticket medio.&lt;/li&gt;
&lt;li&gt;Clientes con mayor volumen de compras.&lt;/li&gt;
&lt;li&gt;Productos más vendidos.&lt;/li&gt;
&lt;li&gt;Productos con precio superior a la media.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Automatización mediante Triggers
&lt;/h3&gt;

&lt;p&gt;Uno de los aspectos más interesantes del proyecto ha sido la implementación de triggers para automatizar procesos dentro de la base de datos, como la actualización automática del stock tras una venta o el registro de auditorías sobre nuevos pedidos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Views
&lt;/h3&gt;

&lt;p&gt;También se han creado vistas que permiten simplificar consultas complejas y generar informes reutilizables para análisis posteriores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aprendizajes obtenidos
&lt;/h2&gt;

&lt;p&gt;Este proyecto me ha permitido profundizar en el diseño de bases de datos relacionales y comprender mejor cómo las empresas gestionan y explotan la información mediante SQL.&lt;/p&gt;

&lt;p&gt;Además de mejorar mis conocimientos técnicos, he podido trabajar con una estructura más cercana a un entorno profesional, aplicando buenas prácticas de organización, documentación y control de versiones mediante GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Próximos pasos
&lt;/h2&gt;

&lt;p&gt;Como evolución futura del proyecto me gustaría incorporar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gestión de inventario.&lt;/li&gt;
&lt;li&gt;Cuadros de mando y visualización de datos.&lt;/li&gt;
&lt;li&gt;Integración con herramientas de Business Intelligence.&lt;/li&gt;
&lt;li&gt;Modelos de análisis y predicción mediante Inteligencia Artificial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La mejor forma de aprender tecnología sigue siendo construir proyectos reales, equivocarse, mejorar y seguir iterando.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/MariaAngelesCiobanu" rel="noopener noreferrer"&gt;
        MariaAngelesCiobanu
      &lt;/a&gt; / &lt;a href="https://github.com/MariaAngelesCiobanu/online-store-sql-project" rel="noopener noreferrer"&gt;
        online-store-sql-project
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      sql modular
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Online Store SQL Project&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project was developed using PostgreSQL and DBeaver as part of an advanced SQL database exercise.&lt;/p&gt;
&lt;p&gt;The objective is to demonstrate the implementation of database concepts including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database design&lt;/li&gt;
&lt;li&gt;Table creation&lt;/li&gt;
&lt;li&gt;Data insertion&lt;/li&gt;
&lt;li&gt;SQL queries&lt;/li&gt;
&lt;li&gt;Filtering conditions&lt;/li&gt;
&lt;li&gt;Joins&lt;/li&gt;
&lt;li&gt;Aggregations&lt;/li&gt;
&lt;li&gt;Views&lt;/li&gt;
&lt;li&gt;Triggers&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;Common Table Expressions (CTE)&lt;/li&gt;
&lt;li&gt;Subqueries&lt;/li&gt;
&lt;li&gt;KPI and business metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Technologies Used&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;DBeaver&lt;/li&gt;
&lt;li&gt;SQL&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Database Structure&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The project simulates an online store management system.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Main Tables&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Table&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;customers&lt;/td&gt;
&lt;td&gt;Customer information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;categories&lt;/td&gt;
&lt;td&gt;Product categories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;products&lt;/td&gt;
&lt;td&gt;Available products&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;customer_orders&lt;/td&gt;
&lt;td&gt;Customer orders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;order_details&lt;/td&gt;
&lt;td&gt;Products included in each order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;payments&lt;/td&gt;
&lt;td&gt;Payment records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;audit_log&lt;/td&gt;
&lt;td&gt;Audit information generated by triggers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features Implemented&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Table Creation&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;The database includes relational tables with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Primary Keys&lt;/li&gt;
&lt;li&gt;Foreign Keys&lt;/li&gt;
&lt;li&gt;Constraints&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Data Management&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Sample data is inserted to simulate real business operations.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;SQL Queries&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Different query types are implemented:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic SELECT statements&lt;/li&gt;
&lt;li&gt;Conditional filtering&lt;/li&gt;
&lt;li&gt;Pattern matching&lt;/li&gt;
&lt;li&gt;Range filtering&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;div class="highlight highlight-source-sql notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;SELECT&lt;/span&gt; &lt;span class="pl-k"&gt;*&lt;/span&gt;
&lt;span class="pl-k"&gt;FROM&lt;/span&gt; products
&lt;span class="pl-k"&gt;WHERE&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/MariaAngelesCiobanu/online-store-sql-project" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  PostgreSQL #SQL #BasesDeDatos #DataScience #InteligenciaArtificial #DBeaver #GitHub #AprendizajeContinuo
&lt;/h1&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
      <category>datascience</category>
      <category>dbeaver</category>
    </item>
    <item>
      <title>Statistical analysis with Python: descriptive statistics, inference, regression, and time series</title>
      <dc:creator>Adel Aït Zineb</dc:creator>
      <pubDate>Thu, 04 Jun 2026 06:22:21 +0000</pubDate>
      <link>https://dev.to/evolve-space/statistical-analysis-with-python-descriptive-statistics-inference-regression-and-time-series-3d7b</link>
      <guid>https://dev.to/evolve-space/statistical-analysis-with-python-descriptive-statistics-inference-regression-and-time-series-3d7b</guid>
      <description>&lt;p&gt;Durante mi formación en el Máster de Data Science, Big Data y desarrollo de Inteligencia Artificial en Evolve, he completado una práctica final de estadística y probabilidad utilizando Python.&lt;/p&gt;

&lt;p&gt;El objetivo principal de este proyecto ha sido aplicar diferentes técnicas estadísticas de forma práctica, organizando el trabajo en varios ejercicios independientes y documentando los resultados obtenidos durante el proceso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Objetivo del proyecto
&lt;/h2&gt;

&lt;p&gt;El objetivo de esta práctica ha sido reforzar conceptos clave de estadística utilizados en Data Science y aplicarlos mediante Python.&lt;/p&gt;

&lt;p&gt;El proyecto está dividido en diferentes bloques, cada uno centrado en un área concreta del análisis estadístico:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Estadística descriptiva&lt;/li&gt;
&lt;li&gt;Inferencia estadística&lt;/li&gt;
&lt;li&gt;Regresión múltiple&lt;/li&gt;
&lt;li&gt;Análisis de series temporales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta estructura me ha ayudado a entender cómo se pueden aplicar distintas técnicas estadísticas a conjuntos de datos reales y cómo Python permite automatizar, visualizar e interpretar los resultados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estructura del repositorio
&lt;/h2&gt;

&lt;p&gt;El repositorio contiene los siguientes archivos y carpetas principales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data/&lt;/code&gt;: carpeta que contiene el dataset utilizado en el proyecto.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output/&lt;/code&gt;: carpeta con los resultados generados, gráficos y métricas.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;README.md&lt;/code&gt;: explicación general del proyecto.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Respuestas.md&lt;/code&gt;: respuestas, interpretaciones y conclusiones.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ejercicio1_descriptivo.py&lt;/code&gt;: análisis estadístico descriptivo.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ejercicio2_inferencia.py&lt;/code&gt;: ejercicios de inferencia estadística.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ejercicio3_regresion_multiple.py&lt;/code&gt;: análisis de regresión múltiple.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ejercicio4_series_temporales.py&lt;/code&gt;: análisis de series temporales.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta organización facilita la revisión del proyecto y permite ejecutar cada parte del análisis por separado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Herramientas utilizadas
&lt;/h2&gt;

&lt;p&gt;Las principales herramientas y librerías utilizadas en este proyecto han sido:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Pandas&lt;/li&gt;
&lt;li&gt;NumPy&lt;/li&gt;
&lt;li&gt;Matplotlib&lt;/li&gt;
&lt;li&gt;Análisis estadístico&lt;/li&gt;
&lt;li&gt;Regresión múltiple&lt;/li&gt;
&lt;li&gt;Análisis de series temporales&lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proceso de desarrollo
&lt;/h2&gt;

&lt;p&gt;La primera parte del proyecto se centra en la estadística descriptiva. En esta sección trabajé con medidas estadísticas básicas y visualizaciones para comprender mejor el dataset.&lt;/p&gt;

&lt;p&gt;La segunda parte se centra en la inferencia estadística. Esta parte me ayudó a practicar cómo extraer conclusiones a partir de los datos y entender cómo los métodos estadísticos pueden apoyar la toma de decisiones.&lt;/p&gt;

&lt;p&gt;La tercera parte del proyecto está basada en la regresión múltiple. Esta técnica resulta útil para estudiar la relación entre varias variables independientes y una variable objetivo.&lt;/p&gt;

&lt;p&gt;Por último, la cuarta parte se centra en el análisis de series temporales, donde el objetivo fue analizar datos que cambian a lo largo del tiempo e identificar posibles patrones o tendencias.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados
&lt;/h2&gt;

&lt;p&gt;El proyecto genera diferentes salidas, incluyendo visualizaciones, métricas, análisis de correlación, resultados de regresión y gráficos de series temporales.&lt;/p&gt;

&lt;p&gt;Estos resultados se almacenan en la carpeta &lt;code&gt;output/&lt;/code&gt;, lo que facilita revisar tanto el código como los resultados finales de cada ejercicio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que he aprendido
&lt;/h2&gt;

&lt;p&gt;Este proyecto me ha ayudado a reforzar conceptos importantes de estadística aplicada a Data Science.&lt;/p&gt;

&lt;p&gt;También me ha permitido mejorar la forma en la que organizo un proyecto técnico en GitHub, separando el código en diferentes archivos y documentando el proceso de manera clara.&lt;/p&gt;

&lt;p&gt;Una de las partes más útiles del proyecto ha sido ver cómo Python permite aplicar conceptos estadísticos de forma práctica y reproducible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repositorio del proyecto
&lt;/h2&gt;

&lt;p&gt;Puedes ver el proyecto completo en GitHub aquí:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/adeltoutouh-lab/Statistical_Analysis-Adel_Toutouh_El_Bouchti-" rel="noopener noreferrer"&gt;https://github.com/adeltoutouh-lab/Statistical_Analysis-Adel_Toutouh_El_Bouchti-&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Muchas gracias.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>statistics</category>
      <category>datanalysis</category>
    </item>
    <item>
      <title>Scanner de Seguridad Web Full-Stack con NestJS y React — construido para el Evolve Talent Day</title>
      <dc:creator>Andrés Caso Iglesias</dc:creator>
      <pubDate>Mon, 01 Jun 2026 21:12:31 +0000</pubDate>
      <link>https://dev.to/evolve-space/scanner-de-seguridad-web-full-stack-con-nestjs-y-react-construido-para-el-evolve-talent-day-4gmo</link>
      <guid>https://dev.to/evolve-space/scanner-de-seguridad-web-full-stack-con-nestjs-y-react-construido-para-el-evolve-talent-day-4gmo</guid>
      <description>&lt;p&gt;He creado una herramienta de auditoría de headers de seguridad que analiza 15 parámetros OWASP, TLS, DNS, fingerprinting tecnológico, CVEs, y mapea contra 4 marcos de cumplimiento. Todo con SSRF protection, SSE en tiempo real, y Docker multi-stage. La estoy presentando en el &lt;strong&gt;Evolve Talent Day&lt;/strong&gt; para darme a conocer como nuevo talento en el ecosistema.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por qué este proyecto?
&lt;/h2&gt;

&lt;p&gt;Cuando empiezas en ciberseguridad, uno de los primeros consejos que te dan es: &lt;em&gt;"analiza los headers de seguridad de una web"&lt;/em&gt;. Hazlo una vez. Dos. Diez. Después te aburres.&lt;/p&gt;

&lt;p&gt;Lo que no te dicen es que entender &lt;strong&gt;por qué&lt;/strong&gt; un header está mal configurado, &lt;strong&gt;qué frameworks de cumplimiento&lt;/strong&gt; afecta, y &lt;strong&gt;cómo priorizar las recomendaciones&lt;/strong&gt; es un ejercicio completamente diferente a simplemente ejecutar un &lt;code&gt;curl -I&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Quise construir eso: una herramienta que no solo diga &lt;em&gt;"te falta CSP"&lt;/em&gt;, sino que te explique el impacto, te mapee contra OWASP Top 10, NIS2, ENS e ISO 27001, y te genere un reporte exportable. Un proyecto de fin de máster que sea &lt;strong&gt;realmente útil&lt;/strong&gt; y que me permita demostrar lo que sé.&lt;/p&gt;

&lt;p&gt;Y como estoy participando en el &lt;strong&gt;Evolve Talent Day&lt;/strong&gt; — un evento de &lt;a href="https://evolve.es/" rel="noopener noreferrer"&gt;Evolve&lt;/a&gt; para dar a conocer nuevo talento en el ecosistema tech — quise que este proyecto tuviera la calidad suficiente para presentarlo con orgullo.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Qué hace exactamente?
&lt;/h2&gt;

&lt;p&gt;Dale una URL pública y la herramienta:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hace un request HTTP&lt;/strong&gt; a esa URL (con protección SSRF, que después explico)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analiza 15 headers de seguridad&lt;/strong&gt; contra el OWASP Secure Headers Project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verifica TLS/SSL&lt;/strong&gt; — protocolo, certificado, cadena de confianza&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revisa registros DNS&lt;/strong&gt; — SPF, DKIM, DMARC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detecta archivos sensibles expuestos&lt;/strong&gt; — &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.git/config&lt;/code&gt;, &lt;code&gt;wp-config.php&lt;/code&gt;, 40 rutas en total&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analiza SRI&lt;/strong&gt; (Subresource Integrity) en scripts y estilos externos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hace fingerprinting tecnológico&lt;/strong&gt; — detecta 23 tecnologías y busca CVEs asociadas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mapea contra 4 marcos de cumplimiento&lt;/strong&gt;: OWASP Top 10, NIS2, ENS, ISO 27001&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calcula un score de seguridad (0-100)&lt;/strong&gt; con calificación A-F&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Genera reportes exportables&lt;/strong&gt; en PDF y JSON&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Todo esto con progreso en tiempo real vía Server-Sent Events. No es un spinner que dice "escaneando...". Son 9 etapas con feedback granular.&lt;/p&gt;




&lt;h2&gt;
  
  
  El Stack (sí, usé todo lo último)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backend — NestJS 11
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Tecnología&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;Node.js 22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;NestJS 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP Client&lt;/td&gt;
&lt;td&gt;Axios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validación&lt;/td&gt;
&lt;td&gt;class-validator + Zod (env)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate Limiting&lt;/td&gt;
&lt;td&gt;@nestjs/throttler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;API Key (custom guard)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB&lt;/td&gt;
&lt;td&gt;SQLite via better-sqlite3 (WAL mode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF&lt;/td&gt;
&lt;td&gt;PDFKit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS&lt;/td&gt;
&lt;td&gt;Módulo nativo &lt;code&gt;tls&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Módulo nativo &lt;code&gt;dns/promises&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Docs&lt;/td&gt;
&lt;td&gt;Swagger/OpenAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Frontend — React 19
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Tecnología&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;React 19.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bundler&lt;/td&gt;
&lt;td&gt;Vite 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;Tailwind CSS 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Charts&lt;/td&gt;
&lt;td&gt;Chart.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Custom hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time&lt;/td&gt;
&lt;td&gt;SSE via EventSource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;Vitest + React Testing Library&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  DevOps
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Detalle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Containers&lt;/td&gt;
&lt;td&gt;Docker multi-stage builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compose&lt;/td&gt;
&lt;td&gt;2 servicios: backend + frontend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD&lt;/td&gt;
&lt;td&gt;GitHub Actions: jobs paralelos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proxy&lt;/td&gt;
&lt;td&gt;Nginx con SSE buffering deshabilitado&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Decisiones Arquitectónicas que me Enorgullecen
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. SSRF Protection en Cada Request Saliente
&lt;/h3&gt;

&lt;p&gt;Esta es la decisión de la que más orgulloso estoy. Un scanner de seguridad &lt;strong&gt;no puede ser vulnerable a SSRF&lt;/strong&gt;. Sería un chiste.&lt;/p&gt;

&lt;p&gt;La función &lt;code&gt;resolveAndCheckHostname()&lt;/code&gt; se ejecuta &lt;strong&gt;antes de cada request HTTP&lt;/strong&gt; que hace el scanner — ya sea al target, a OSV.dev para CVEs, o a cualquier otro lado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bloquea rangos IPv4 privados&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPrivateIPv4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ForbiddenException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SSRF blocked: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; resolves to private IP &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Bloquea loopback IPv6 y link-local&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fe80:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ForbiddenException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SSRF blocked: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; resolves to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resuelve el DNS, verifica &lt;strong&gt;todas&lt;/strong&gt; las IPs retornadas, y solo procede si ninguna es privada. Esto previene que la herramienta sea usada como vector de ataque.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Strategy Pattern para Header Checkers
&lt;/h3&gt;

&lt;p&gt;Cada uno de los 15 headers tiene su propio checker class que implementa una interfaz común:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HeaderChecker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;critical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;HeaderResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;AnalyzerService&lt;/code&gt; inyecta los 15 checkers vía DI y los itera automáticamente. ¿Quieres agregar un header nuevo? Creas la clase, la agregas al contenedor DI, y listo. Sin tocar el servicio de análisis.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Paralelismo con SSE
&lt;/h3&gt;

&lt;p&gt;El &lt;code&gt;scanCore()&lt;/code&gt; ejecuta 7 subsistemas en paralelo con &lt;code&gt;Promise.all&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Client ─────┐
TLS Checker ─────┤
DNS Checker ─────┤
Security Files ──┼──▶ Promise.all ──▶ Analyze ──▶ Comply ──▶ Report
Sensitive Files ─┤
SRI Checker ─────┤
Fingerprinting ──┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada subsistema emite eventos SSE (&lt;code&gt;scanning&lt;/code&gt; → &lt;code&gt;complete&lt;/code&gt;), y el frontend los consume en tiempo real con &lt;code&gt;EventSource&lt;/code&gt;. El usuario ve exactamente en qué etapa está el scan.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Detección de Soft 404
&lt;/h3&gt;

&lt;p&gt;Al probar las 40 rutas sensibles, un sitio podría devolver HTTP 200 con una página de error custom. El &lt;code&gt;SensitiveFileCheckerService&lt;/code&gt; detecta esto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si el status es 200 pero &lt;code&gt;Content-Type&lt;/code&gt; es &lt;code&gt;text/html&lt;/code&gt; y &lt;code&gt;Content-Length &amp;gt; 500&lt;/code&gt; → &lt;strong&gt;suspected soft 404&lt;/strong&gt; con confianza baja&lt;/li&gt;
&lt;li&gt;Si el status es 200 con &lt;code&gt;Content-Type&lt;/code&gt; tipo JSON o texto plano → &lt;strong&gt;probablemente real&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esto reduce falsos positivos, que en una herramienta de seguridad son el enemigo.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. CVE Dual: Local + OSV.dev
&lt;/h3&gt;

&lt;p&gt;Mantengo una base de datos local de 20 CVEs (WordPress, Joomla, Drupal, PHP, Apache, Nginx) con matchers de rango de versiones. En paralelo, consulto la API de OSV.dev (la base de datos de vulnerabilidades de Google) con caché in-memory de 30 minutos.&lt;/p&gt;

&lt;p&gt;Si OSV.dev está caído, la herramienta sigue funcionando con la base local. Si está disponible, fusiona los resultados. Resiliencia y profundidad.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Zod-First Environment Validation
&lt;/h3&gt;

&lt;p&gt;Las 16 variables de entorno se validan al startup con un schema Zod. Si alguna es inválida, la aplicación &lt;strong&gt;se niega a arrancar&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;envSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;CORS_ORIGINS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 13 variables más&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fail-fast. No surprises en producción.&lt;/p&gt;




&lt;h2&gt;
  
  
  El Frontend: No es Solo un CRUD Bonito
&lt;/h2&gt;

&lt;p&gt;El frontend tiene 8 secciones tabulares:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Headers&lt;/strong&gt; — Grid filtrable de 15 tarjetas con badges de severidad, barras de grade, findings y recomendaciones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance&lt;/strong&gt; — Grid de 4 columnas (OWASP/NIS2/ENS/ISO27001) con scores porcentuales y dots de estado por control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS/SSL&lt;/strong&gt; — Versión del protocolo, detalles del certificado, grade&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt; — Records SPF/DKIM/DMARC con grades&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SRI&lt;/strong&gt; — Análisis de integridad de recursos externos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fingerprinting&lt;/strong&gt; — Tecnologías detectadas con niveles de confianza, lista de CVEs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensitive Files&lt;/strong&gt; — Archivos expuestos con ratings de confianza&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommendations&lt;/strong&gt; — Lista priorizada de todos los subsistemas&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Detalles que Importan
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error boundaries granulares&lt;/strong&gt;: Cada sección tiene su propio &lt;code&gt;&amp;lt;ErrorBoundary&amp;gt;&lt;/code&gt;. Si ComplianceGrid crashea, el resto sigue funcionando.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clasificación de errores&lt;/strong&gt;: El hook &lt;code&gt;useScan&lt;/code&gt; clasifica errores en 5 tipos (network, timeout, server, validation, unknown) y muestra diferentes tratamientos UI con mensajes en español.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score Circle animado&lt;/strong&gt;: SVG animado con tooltip que explica la metodología: &lt;code&gt;Score = sum(weight × grade) / 165 × 100&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparencia sobre limitaciones&lt;/strong&gt;: El scoring es un heurístico autor-definido, no un estándar. El frontend lo documenta explícitamente.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  DevOps: Docker y CI/CD
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Multi-Stage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Builder stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:22-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Runner stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:22-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;-g&lt;/span&gt; 1001 &lt;span class="nt"&gt;-S&lt;/span&gt; appgroup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;-S&lt;/span&gt; appuser &lt;span class="nt"&gt;-u&lt;/span&gt; 1001 &lt;span class="nt"&gt;-G&lt;/span&gt; appgroup
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=appuser:appgroup /app/dist ./dist&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Non-root user en ambos contenedores. Multi-stage para minimizar el tamaño de imagen.&lt;/p&gt;

&lt;h3&gt;
  
  
  CI/CD con GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Dos jobs paralelos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: &lt;code&gt;npm ci&lt;/code&gt; → lint → test → build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: &lt;code&gt;npm ci&lt;/code&gt; → lint → test → build&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada uno corre en Ubuntu con Node 22. Independientes pero simultáneos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nginx para el Frontend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/api/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://backend:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_buffering&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;# Soporte SSE&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;chunked_transfer_encoding&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;proxy_buffering off&lt;/code&gt; es &lt;strong&gt;crítico&lt;/strong&gt; para que los eventos SSE lleguen al frontend sin buffering intermedio.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test Coverage
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: 33 suites de test (unitarios + e2e)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: 8 archivos de test con Vitest + React Testing Library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E&lt;/strong&gt;: Un test completo de la API de scan con supertest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No es 100% coverage, pero es testing &lt;strong&gt;real&lt;/strong&gt; — no assertions vacías.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que Aprendí Construyendo Esto
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Seguridad no es un feature, es una mentalidad.&lt;/strong&gt; La protección SSRF tiene que estar en cada capa, no solo en el endpoint principal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSE es superior a WebSocket para este caso.&lt;/strong&gt; No necesitas bidireccionalidad. El servidor emite, el cliente consume. SSE es más simple, más ligero, y funciona con HTTP/1.1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;El Strategy Pattern no es académico, es útil.&lt;/strong&gt; Los 15 header checkers son intercambiables, testeables, y extensibles. Si mañana necesito agregar un header, no toco código existente.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker multi-stage no es opcional, es hygiene.&lt;/strong&gt; Un builder stage de 900MB y un runner de 150MB. La diferencia es inaceptable si no lo haces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zod al startup te salva de problemas en runtime.&lt;/strong&gt; Fail-fast &amp;gt; fail-later. Siempre.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Evolve Talent Day
&lt;/h2&gt;

&lt;p&gt;Estoy presentando este proyecto en el &lt;strong&gt;Evolve Talent Day&lt;/strong&gt;, un evento organizado por &lt;a href="https://evolve.es/" rel="noopener noreferrer"&gt;Evolve&lt;/a&gt; para dar a conocer a nuevo talento en el ecosistema tech.&lt;/p&gt;

&lt;p&gt;La idea es simple: mostrar lo que sé, cómo lo sé, y cómo lo aplico. No es un proyecto perfecto — es un proyecto &lt;strong&gt;real&lt;/strong&gt; con decisiones arquitectónicas documentadas, trade-offs explícitos, y código que funciona.&lt;/p&gt;

&lt;p&gt;Si estás en el evento o te interesa el proyecto, puedes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Probarlo&lt;/strong&gt;: &lt;a href="https://github.com/Andres-Caso-Iglesias/security_header_scanner" rel="noopener noreferrer"&gt;Repositorio en GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leer la documentación&lt;/strong&gt;: &lt;code&gt;docs/&lt;/code&gt; tiene versiones en inglés y español&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Compose&lt;/strong&gt;: &lt;code&gt;docker compose up levanta los dos servicios&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Docs&lt;/strong&gt;: Swagger en &lt;code&gt;/api/docs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ¿Qué Sigue?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt; Autenticación JWT para escaneos privados&lt;/li&gt;
&lt;li&gt; Dashboard multi-usuario con historial compartido&lt;/li&gt;
&lt;li&gt; Comparación entre scans (delta analysis)&lt;/li&gt;
&lt;li&gt; Integración con pipelines CI/CD (GitHub Action custom)&lt;/li&gt;
&lt;li&gt; Más tecnologías en el fingerprinting (actualmente 23)&lt;/li&gt;
&lt;li&gt; Base de CVEs expandida&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Gracias por leer.&lt;/strong&gt; Si tienes preguntas sobre la arquitectura, las decisiones técnicas, o quieres discutir sobre ciberseguridad, deja un comentario.&lt;/p&gt;

&lt;p&gt;Y si estás en el &lt;strong&gt;Evolve Talent Day&lt;/strong&gt;, acércate a charlar. Siempre hay algo nuevo que aprender.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with NestJS 11, React 19, Vite 8, Tailwind CSS 4, TypeScript, Docker, and too much café con leche.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>react</category>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>MSF-Assistant: herramienta web de pentesting con Metasploit, nmap y msfvenom en Kali Linux</title>
      <dc:creator>david ayala</dc:creator>
      <pubDate>Fri, 29 May 2026 06:33:29 +0000</pubDate>
      <link>https://dev.to/evolve-space/msf-assistant-herramienta-web-de-pentesting-con-metasploit-nmap-y-msfvenom-en-kali-linux-13b2</link>
      <guid>https://dev.to/evolve-space/msf-assistant-herramienta-web-de-pentesting-con-metasploit-nmap-y-msfvenom-en-kali-linux-13b2</guid>
      <description>&lt;h2&gt;
  
  
  El problema que quería resolver
&lt;/h2&gt;

&lt;p&gt;Aprender pentesting con Metasploit tiene una curva de entrada alta. Cada sesión de prácticas implica recordar la secuencia exacta de comandos: qué exploit usar, qué parámetros configurar, cómo construir el payload, cómo lanzar el listener. Para alguien que está aprendiendo, esa fricción hace que te centres más en memorizar comandos que en entender lo que ocurre.&lt;/p&gt;

&lt;p&gt;Mi proyecto final de máster fue construir una herramienta que eliminara esa fricción: &lt;strong&gt;MSF-Assistant&lt;/strong&gt;, una aplicación web local que orquesta Metasploit desde el navegador, guiando por las distintas fases de un test de penetración.&lt;/p&gt;

&lt;h2&gt;
  
  
  El entorno: Kali Linux + Metasploitable en VirtualBox
&lt;/h2&gt;

&lt;p&gt;Todo el desarrollo y las pruebas se hicieron en Kali Linux corriendo en VirtualBox, con Metasploitable 3 como máquina objetivo en una red Host-Only aislada. Es el setup estándar para prácticas de pentesting en entornos controlados.&lt;/p&gt;

&lt;p&gt;El reto técnico de partida: &lt;code&gt;netdiscover&lt;/code&gt; y &lt;code&gt;nmap&lt;/code&gt; necesitan permisos de red que desde un navegador normal son imposibles de obtener. La solución fue construir la interfaz como una &lt;strong&gt;aplicación Flask que corre localmente en Kali&lt;/strong&gt; — el backend tiene acceso completo al sistema operativo y ejecuta los comandos reales, mientras el frontend recibe el output en tiempo real mediante &lt;strong&gt;Server-Sent Events&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  La arquitectura: Flask + SSE + subprocess
&lt;/h2&gt;

&lt;p&gt;El stack es intencionalmente simple:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;&lt;br&gt;
app.py               -&amp;gt; Backend Python/Flask&lt;br&gt;
templates/index.html -&amp;gt; Frontend SPA (HTML/CSS/JS vanilla)&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;La clave técnica está en el streaming de output. Cuando lanzas un escaneo o un exploit, el proceso puede tardar minutos. En lugar de esperar y mostrar todo al final, usé Server-Sent Events para que cada línea aparezca en pantalla en tiempo real, igual que en una terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`python&lt;br&gt;
@app.route('/api/scan/stream')&lt;br&gt;
def api_scan_stream():&lt;br&gt;
    cmd = ['sudo', 'nmap', '-sV', '--open', '-T4', network]&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def generate():
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT, text=True, bufsize=1)
    for line in proc.stdout:
        yield f"data: {line.rstrip()}\n\n"
    yield "data: __DONE__\n\n"

return Response(generate(), mimetype='text/event-stream')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;En el frontend, un &lt;code&gt;EventSource&lt;/code&gt; recibe cada línea y la pinta en un terminal simulado con colores según el tipo de mensaje.&lt;/p&gt;

&lt;h2&gt;
  
  
  Las fases del flujo
&lt;/h2&gt;

&lt;p&gt;La herramienta guía por el flujo completo de un pentest.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Escaneo de red
&lt;/h3&gt;

&lt;p&gt;Tres opciones reales que se ejecutan en el sistema: &lt;code&gt;netdiscover&lt;/code&gt;, &lt;code&gt;nmap ping scan&lt;/code&gt; y &lt;code&gt;nmap full scan&lt;/code&gt;. El output aparece en tiempo real y al terminar se parsean automáticamente las IPs descubiertas para mostrarlas como objetivos seleccionables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39a9mvl6tzmdytid2j1r.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39a9mvl6tzmdytid2j1r.webp" alt="Escaneo nmap mostrando los servicios de Metasploitable 3" width="800" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aquí se ven los servicios detectados en Metasploitable 3: ProFTPD en el puerto 21, OpenSSH en el 22, Apache en el 80, Samba en el 445, MySQL en el 3306 y Jetty en el 8080.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Selección de exploit
&lt;/h3&gt;

&lt;p&gt;Base de datos de exploits organizados por categoría (web, smb, ftp, ssh, irc, db, network) con buscador por nombre, CVE o puerto. Cada uno muestra su rank de Metasploit, CVE asociado y una nota sobre en qué condiciones funciona.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qk2oxx1uw147ldmyraj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qk2oxx1uw147ldmyraj.webp" alt="Lista de exploits disponibles en la herramienta" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Algunos de los incluidos:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exploit&lt;/th&gt;
&lt;th&gt;CVE&lt;/th&gt;
&lt;th&gt;Target típico&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSH Login (credenciales por defecto)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Metasploitable 2/3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drupalgeddon2&lt;/td&gt;
&lt;td&gt;CVE-2018-7600&lt;/td&gt;
&lt;td&gt;Metasploitable 3, Drupal 7/8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EternalBlue&lt;/td&gt;
&lt;td&gt;CVE-2017-0144&lt;/td&gt;
&lt;td&gt;Windows 7 / 2008 R2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shellshock CGI&lt;/td&gt;
&lt;td&gt;CVE-2014-6271&lt;/td&gt;
&lt;td&gt;Apache con CGI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Configuración automática
&lt;/h3&gt;

&lt;p&gt;Al seleccionar un host del escaneo y un exploit, la herramienta pre-rellena automáticamente &lt;code&gt;RHOSTS&lt;/code&gt; con la IP del target y &lt;code&gt;LHOST&lt;/code&gt; con la IP local de Kali. Solo hay que revisar y ajustar si hace falta.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7943wxsqsb7rxqkwhld.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7943wxsqsb7rxqkwhld.webp" alt="Configuración automática del exploit SSH" width="799" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para los módulos de login (SSH, MySQL) incluí un &lt;strong&gt;diccionario de credenciales por defecto&lt;/strong&gt; de las máquinas vulnerables más comunes: &lt;code&gt;vagrant/vagrant&lt;/code&gt; para Metasploitable 3, &lt;code&gt;msfadmin/msfadmin&lt;/code&gt; para Metasploitable 2, &lt;code&gt;root&lt;/code&gt; sin contraseña para MySQL. La herramienta las prueba automáticamente y abre una sesión al primer acierto.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Lanzar en Metasploit
&lt;/h3&gt;

&lt;p&gt;Genera un script &lt;code&gt;.rc&lt;/code&gt; con todos los comandos configurados y ejecuta &lt;code&gt;msfconsole -r&lt;/code&gt;. El output completo aparece en tiempo real en el terminal de la interfaz.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Generador de payloads (msfvenom)
&lt;/h3&gt;

&lt;p&gt;Diez tipos de payload: ELF Linux x86/x64, EXE Windows x86/x64, PHP webshell, Python, Bash one-liner, WAR para Tomcat, ASPX para IIS, PowerShell. Con soporte para encoders y descarga directa del payload generado.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Post-explotación
&lt;/h3&gt;

&lt;p&gt;Una vez dentro, panel con módulos &lt;code&gt;post/&lt;/code&gt; preconfigurados, comandos Meterpreter listos para copiar y técnicas de escalada de privilegios en Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultado: acceso conseguido contra Metasploitable 3
&lt;/h2&gt;

&lt;p&gt;El flujo completo que seguí en la prueba final:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Escaneo con &lt;code&gt;nmap full scan&lt;/code&gt; y aparece &lt;code&gt;192.168.56.101&lt;/code&gt; con sus puertos&lt;/li&gt;
&lt;li&gt;Selecciono el host, voy a Exploits y elijo &lt;strong&gt;SSH Login (credenciales por defecto)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;RHOSTS ya está relleno automáticamente, el diccionario se genera solo&lt;/li&gt;
&lt;li&gt;Pulso "Ejecutar en Metasploit"&lt;/li&gt;
&lt;li&gt;En segundos: &lt;code&gt;Success: 'vagrant:vagrant'&lt;/code&gt; y &lt;code&gt;SSH session 1 opened&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5dpj7s6k62f4qnd3q9a.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5dpj7s6k62f4qnd3q9a.webp" alt="Sesión SSH abierta contra Metasploitable 3" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Acceso conseguido con privilegios sudo (&lt;code&gt;groups=27(sudo)&lt;/code&gt;), todo automatizado desde la interfaz, sin escribir un solo comando manualmente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendí
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Técnicamente:&lt;/strong&gt; la parte más interesante fue el streaming via SSE. Los procesos de Metasploit y nmap bufferizan su output, así que tuve que configurar &lt;code&gt;bufsize=1&lt;/code&gt; y &lt;code&gt;text=True&lt;/code&gt; en &lt;code&gt;subprocess.Popen&lt;/code&gt; para obtener salida línea a línea en tiempo real. También aprendí a generar scripts &lt;code&gt;.rc&lt;/code&gt; de forma genérica para soportar cualquier exploit sin programar cada opción a mano.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;De seguridad:&lt;/strong&gt; construir la herramienta me obligó a entender en profundidad qué hace cada exploit, qué condiciones necesita y por qué funciona. Saber por qué Drupalgeddon2 no necesita credenciales, o por qué hay que apuntar al &lt;code&gt;TARGETURI&lt;/code&gt; correcto, es mucho más valioso que ejecutar comandos de memoria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lo que haría diferente:&lt;/strong&gt; añadiría autenticación a la interfaz web, un sistema de logs persistente para guardar los resultados de cada sesión, e integraría &lt;code&gt;db_nmap&lt;/code&gt; para aprovechar la base de datos interna de Metasploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Herramientas utilizadas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stack técnico:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python 3 + Flask&lt;/strong&gt; — backend y servidor web local&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML / CSS / JS vanilla&lt;/strong&gt; — frontend sin frameworks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metasploit Framework&lt;/strong&gt; — motor de exploits (&lt;code&gt;msfconsole&lt;/code&gt;, &lt;code&gt;msfvenom&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nmap&lt;/strong&gt; — escaneo de puertos y detección de servicios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;netdiscover&lt;/strong&gt; — descubrimiento de hosts via ARP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kali Linux + VirtualBox&lt;/strong&gt; — entorno de desarrollo y pruebas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metasploitable 3&lt;/strong&gt; — máquina objetivo para las pruebas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Desarrollo asistido por IA:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://claude.ai" rel="noopener noreferrer"&gt;Claude AI (Anthropic)&lt;/a&gt;&lt;/strong&gt; — co-desarrollado con Claude como asistente de programación. Claude ayudó a estructurar la arquitectura, implementar el streaming via Server-Sent Events, depurar el backend Flask y diseñar la interfaz. Trabajar con IA no reemplaza entender lo que se construye: cada decisión técnica fue revisada, probada y ajustada manualmente en Kali.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repositorio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El código completo está en GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Davidaah07/Proyecto-Master-Ciberseguridad-Evolve-DavidAah07" rel="noopener noreferrer"&gt;github.com/Davidaah07/Proyecto-Master-Ciberseguridad-Evolve-DavidAah07&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Incluye instrucciones de instalación, lista completa de exploits, diccionario de credenciales y aviso legal.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Proyecto académico desarrollado durante el Master en Ciberseguridad de &lt;a href="https://evolve.es" rel="noopener noreferrer"&gt;Evolve&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>python</category>
      <category>linux</category>
      <category>infosec</category>
    </item>
    <item>
      <title>10.430 muertes, 10 preguntas y un pipeline en Python: lo que los datos de violencia policial en EE.UU. no te cuentan a simple vista</title>
      <dc:creator>David Naranjo Ramírez</dc:creator>
      <pubDate>Wed, 27 May 2026 16:40:01 +0000</pubDate>
      <link>https://dev.to/evolve-space/10430-muertes-10-preguntas-y-un-pipeline-en-python-lo-que-los-datos-de-violencia-policial-en-54j6</link>
      <guid>https://dev.to/evolve-space/10430-muertes-10-preguntas-y-un-pipeline-en-python-lo-que-los-datos-de-violencia-policial-en-54j6</guid>
      <description>&lt;p&gt;Cuando el Washington Post empezó a contar los muertos en 2015, el FBI subestimaba casi el 50% de los casos. Ese dato me enganchó. No por el morbo, sino por lo que implica metodológicamente: si los registros oficiales fallan de manera tan estrepitosa, ¿qué más se pierde cuando no miras bien los datos?&lt;/p&gt;

&lt;p&gt;Ese fue el punto de partida de &lt;strong&gt;EDAFatalForce&lt;/strong&gt;, mi proyecto de Análisis Exploratorio de Datos del Máster de Data Science en Evolve.&lt;/p&gt;




&lt;h2&gt;
  
  
  El dataset: periodismo de datos como fuente primaria
&lt;/h2&gt;

&lt;p&gt;El core del proyecto es la base de datos &lt;strong&gt;Fatal Force&lt;/strong&gt; del Washington Post — 10.430 incidentes fatales entre 2015 y 2024 con variables como raza, edad, armamento, comportamiento de fuga, nivel de amenaza percibida y si el agente llevaba cámara corporal.&lt;/p&gt;

&lt;p&gt;Para enriquecerlo crucé tres fuentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Census Bureau ACS 2020&lt;/strong&gt; — datos socioeconómicos de 3.221 condados (ingresos, pobreza, desempleo)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NCSL&lt;/strong&gt; — políticas de cámara corporal por estado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tabla FIPS&lt;/strong&gt; — para enlazar condados con el Census&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El mayor desafío no fue el modelado. Fue la calidad del dato. El &lt;strong&gt;45% de los registros no tenía condado&lt;/strong&gt;, lo que bloqueaba cualquier join con el Census. La solución: construir internamente un mapa &lt;code&gt;ciudad + estado → condado&lt;/code&gt; usando la moda de los registros que sí lo tenían. Resultado: de 45% de nulos a 17,4%. Un cambio que desbloqueó más de 2.800 filas para el análisis socioeconómico.&lt;/p&gt;




&lt;h2&gt;
  
  
  El proceso: bugs, fixes y un pipeline modular
&lt;/h2&gt;

&lt;p&gt;El proyecto está estructurado como un pipeline reproducible en &lt;code&gt;src/&lt;/code&gt;: limpieza → enriquecimiento → feature engineering → EDA. Un solo &lt;code&gt;python main.py&lt;/code&gt; lo ejecuta todo, incluyendo la descarga del Census.&lt;/p&gt;

&lt;p&gt;Durante la auditoría encontré dos bugs críticos en el feature engineering que hubieran invalidado análisis completos:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug 1 — La paradoja de la fuga estaba rota.&lt;/strong&gt; El valor raw &lt;code&gt;"not"&lt;/code&gt; (53% de los registros) no coincidía con la regex diseñada para mapear "no huía", así que más de 5.500 víctimas que no huían quedaban clasificadas como comportamiento desconocido. La pregunta Q2 entera era inválida.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug 2 — "Undetermined" contaba como armado.&lt;/strong&gt; Los 463 registros con armamento &lt;code&gt;"undetermined"&lt;/code&gt; heredaban &lt;code&gt;is_armed = 1.0&lt;/code&gt; por caer en el &lt;code&gt;default&lt;/code&gt; del &lt;code&gt;np.select&lt;/code&gt;. Víctimas con armamento desconocido aparecían como armadas en todas las tasas.&lt;/p&gt;

&lt;p&gt;Ambos fixes son de una línea. El impacto analítico, considerable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resultados: lo que los datos dicen y lo que no
&lt;/h2&gt;

&lt;p&gt;Diez preguntas, diez respuestas con tests estadísticos. Las más reveladoras:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0l7gx4k7yionjv315u0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0l7gx4k7yionjv315u0o.png" alt=" " width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El ranking de estados respecto a número de incidentes fatales cambia por completo al normalizar.&lt;/strong&gt; California lidera con +1200 incidentes brutos. Pero al dividir por población, Misisipi sube al primer puesto con 85,5 casos por 100.000 habitantes. El volumen engaña. La tasa informa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fawc5t6scl78wgx7jpwou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fawc5t6scl78wgx7jpwou.png" alt=" " width="799" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Los protocolos de crisis mental funcionan.&lt;/strong&gt; Los incidentes con signos de enfermedad mental presentan una tasa de amenaza letal del 35,3% frente al 44,4% del resto (χ²=55,18, p&amp;lt;0,001). El resultado más robusto estadísticamente de todo el análisis, y el más contraintuitivo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubvbcq3btinhpc8bl727.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubvbcq3btinhpc8bl727.png" alt=" " width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huir en coche es más peligroso que no huir.&lt;/strong&gt; La tasa de amenaza letal resulta 4 puntos y medio porcentuales mayor en persecuciones vehiculares que en personas que no huyen. Un vehículo a alta velocidad es percibido como amenaza física directa, no solo como evasión.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuo6uoanth1yomuhrrjso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuo6uoanth1yomuhrrjso.png" alt=" " width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entre víctimas desarmadas, la raza sigue siendo predictora.&lt;/strong&gt; Las víctimas negras desarmadas tienen una tasa letal 4,2 puntos superior a las blancas (χ²=11,65, p=0,003). &lt;code&gt;threat_level&lt;/code&gt; es la narrativa del agente, no un hecho verificado — esa limitación es parte del análisis, no una nota al pie.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que aprendí
&lt;/h2&gt;

&lt;p&gt;Que los bugs de feature engineering son los más peligrosos porque no rompen el código — producen resultados plausibles pero incorrectos. Y que documentar las limitaciones no debilita un análisis: lo hace honesto y, paradójicamente, más creíble.&lt;/p&gt;

&lt;p&gt;Si lo repitiera, integraría coordenadas lat/lon para análisis espaciales con DBSCAN geográfico. Los hotspots por condado cuentan la mitad de la historia; los hotspots dentro del condado contarían la otra.&lt;/p&gt;




&lt;p&gt;📁 Repo completo en GitHub: https://github.com/dnarram/Proyecto-Master-DataScience-Evolve-DavidNaranjoRamirez-EDAFatalForce&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Proyecto desarrollado durante el &lt;a href="https://evolve.es" rel="noopener noreferrer"&gt;Máster en Data Science de Evolve&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>python</category>
      <category>panda</category>
      <category>jupyter</category>
    </item>
    <item>
      <title>Construyendo un recomendador de películas en Python: de los datos al modelo</title>
      <dc:creator>Alberto Martinez</dc:creator>
      <pubDate>Mon, 25 May 2026 18:22:03 +0000</pubDate>
      <link>https://dev.to/evolve-space/construyendo-un-recomendador-de-peliculas-en-python-de-los-datos-al-modelo-3m16</link>
      <guid>https://dev.to/evolve-space/construyendo-un-recomendador-de-peliculas-en-python-de-los-datos-al-modelo-3m16</guid>
      <description>&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Los sistemas de recomendación están presentes en muchas plataformas digitales: streaming, e-commerce, música, formación online o personalización de contenidos. Su objetivo es ayudar al usuario a descubrir elementos relevantes dentro de catálogos cada vez más grandes.&lt;/p&gt;

&lt;p&gt;En este proyecto he desarrollado &lt;strong&gt;CineMatch AI&lt;/strong&gt;, un sistema de recomendación de películas basado en filtrado colaborativo por ítems. La idea principal es sencilla: dada una película de referencia, el sistema recomienda otras películas similares a partir de los patrones históricos de valoración de los usuarios.&lt;/p&gt;

&lt;p&gt;El objetivo no era construir una solución empresarial completa, sino un prototipo funcional, reproducible y bien estructurado que permitiera trabajar un flujo completo de Data Science: análisis de datos, preprocesamiento, construcción del sistema de recomendación, evaluación exploratoria y presentación mediante una interfaz sencilla.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dataset utilizado
&lt;/h2&gt;

&lt;p&gt;Para el proyecto he utilizado el dataset &lt;strong&gt;MovieLens&lt;/strong&gt;, una referencia habitual para trabajar con sistemas de recomendación.&lt;/p&gt;

&lt;p&gt;El conjunto de datos incluye información sobre películas, usuarios y valoraciones. En este caso, el trabajo se ha centrado principalmente en dos archivos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ratings.csv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;movies.csv&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tras el proceso de filtrado, el sistema trabaja con:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;446.702 ratings finales&lt;/li&gt;
&lt;li&gt;3.728 películas&lt;/li&gt;
&lt;li&gt;4.356 usuarios activos&lt;/li&gt;
&lt;li&gt;una matriz usuario-película con una densidad aproximada del 2,75%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este último punto es importante, porque en los sistemas de recomendación es habitual trabajar con matrices muy dispersas: muchos usuarios han valorado solo una pequeña parte del catálogo total.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proceso seguido
&lt;/h2&gt;

&lt;p&gt;El flujo del proyecto se divide en varias fases.&lt;/p&gt;

&lt;p&gt;Primero se realizó la carga y exploración inicial de los datos, revisando el volumen de registros, la distribución de ratings y la información disponible sobre las películas.&lt;/p&gt;

&lt;p&gt;Después se aplicaron filtros para mejorar la calidad de las recomendaciones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;películas con al menos 20 valoraciones.&lt;/li&gt;
&lt;li&gt;usuarios con al menos 10 valoraciones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este filtrado reduce ruido y evita que el sistema se base en películas o usuarios con muy poca información histórica.&lt;/p&gt;

&lt;p&gt;A continuación se construyó una matriz usuario-película, donde cada fila representa un usuario, cada columna una película y cada valor corresponde a la puntuación asignada. A partir de esta matriz, el sistema compara películas entre sí usando &lt;strong&gt;similitud del coseno&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;La lógica es la siguiente: si dos películas han sido valoradas de forma parecida por muchos usuarios, el sistema interpreta que existe cierta similitud entre ellas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sistema de recomendación
&lt;/h2&gt;

&lt;p&gt;El recomendador está basado en &lt;strong&gt;filtrado colaborativo por ítems&lt;/strong&gt;. En lugar de comparar usuarios entre sí, compara películas a partir de los patrones de valoración.&lt;/p&gt;

&lt;p&gt;Este enfoque tiene varias ventajas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;es fácil de explicar.&lt;/li&gt;
&lt;li&gt;funciona bien cuando existen suficientes valoraciones.&lt;/li&gt;
&lt;li&gt;permite generar recomendaciones interpretables.&lt;/li&gt;
&lt;li&gt;puede adaptarse a otros dominios como productos, canciones, cursos o contenidos digitales.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Además del análisis en notebook, el proyecto incluye una pequeña aplicación con &lt;strong&gt;Streamlit&lt;/strong&gt;, pensada para probar el sistema de forma visual. Desde la interfaz se puede buscar una película, seleccionar una coincidencia del catálogo y obtener un top 5 o top 10 de recomendaciones con su score de similitud.&lt;/p&gt;

&lt;p&gt;También se incluye una API básica con &lt;strong&gt;FastAPI&lt;/strong&gt; como demostración complementaria de cómo podría exponerse la lógica del recomendador.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados
&lt;/h2&gt;

&lt;p&gt;El sistema genera recomendaciones coherentes, especialmente para películas con suficientes valoraciones históricas.&lt;/p&gt;

&lt;p&gt;Algunos resultados principales del análisis fueron:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;matriz usuario-película de 4.356 usuarios por 3.728 películas.&lt;/li&gt;
&lt;li&gt;446.702 ratings utilizados tras el filtrado.&lt;/li&gt;
&lt;li&gt;similitud media top-N de 0,5662.&lt;/li&gt;
&lt;li&gt;consistencia media de géneros del 81,5%.&lt;/li&gt;
&lt;li&gt;94 de 100 películas evaluadas con una consistencia de géneros superior al 50%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por ejemplo, para una película como &lt;code&gt;Scarface (1983)&lt;/code&gt;, el sistema recomienda títulos relacionados con géneros como crimen, drama, thriller o acción, lo que muestra una cierta coherencia temática en las recomendaciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitaciones
&lt;/h2&gt;

&lt;p&gt;Como ocurre en muchos sistemas de recomendación, el proyecto también tiene limitaciones.&lt;/p&gt;

&lt;p&gt;La más importante es el problema de &lt;strong&gt;cold start&lt;/strong&gt;: si una película o usuario no tiene valoraciones suficientes, el sistema no dispone de información histórica para generar recomendaciones fiables.&lt;/p&gt;

&lt;p&gt;Además, el filtrado por popularidad mejora la calidad de las recomendaciones, pero reduce la cobertura total del catálogo. También hay que tener en cuenta que el sistema recomienda a partir de patrones históricos de valoración, no por una comprensión semántica profunda del contenido de las películas.&lt;/p&gt;

&lt;p&gt;Por último, la evaluación realizada es exploratoria. No debe interpretarse como una evaluación supervisada definitiva, ya que no se ha aplicado una separación train/test ni métricas de ranking como Precision@K, Recall@K o NDCG, habituales en la evaluación de sistemas de recomendación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aprendizajes
&lt;/h2&gt;

&lt;p&gt;Este proyecto me ha servido para entender mejor cómo se estructura un sistema de recomendación desde una perspectiva práctica.&lt;/p&gt;

&lt;p&gt;Más allá de la técnica de similitud, una parte importante del trabajo ha estado en preparar los datos, tomar decisiones de filtrado, construir una estructura reproducible y presentar los resultados de forma clara.&lt;/p&gt;

&lt;p&gt;También me ha parecido especialmente interesante comprobar cómo una técnica relativamente sencilla, como la similitud del coseno, puede generar recomendaciones razonables cuando los datos están bien tratados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Próximos pasos
&lt;/h2&gt;

&lt;p&gt;Algunas mejoras naturales para futuras versiones serían:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;combinar ratings y géneros en un sistema híbrido.&lt;/li&gt;
&lt;li&gt;incorporar técnicas de factorización matricial como SVD o NMF.&lt;/li&gt;
&lt;li&gt;mejorar la evaluación offline con métricas de ranking.&lt;/li&gt;
&lt;li&gt;guardar la matriz de similitud precomputada para acelerar la demo.&lt;/li&gt;
&lt;li&gt;desplegar la aplicación para facilitar su uso externo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repositorio
&lt;/h2&gt;

&lt;p&gt;El código completo del proyecto está disponible en GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/evolve-space/Proyecto-Master-DataScience-Evolve-AlbertoMartinez" rel="noopener noreferrer"&gt;https://github.com/evolve-space/Proyecto-Master-DataScience-Evolve-AlbertoMartinez&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podeis contactarme en mi LinkedIn:&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/albertomartinezsanchez" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/albertomartinezsanchez&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Proyecto desarrollado durante el Máster en Data Science &amp;amp; Desarrollo de IA de Evolve.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>machinelearning</category>
      <category>streamlit</category>
    </item>
    <item>
      <title>What 3.9M powerlifting records tell us about competition strategy — an EDA with Python</title>
      <dc:creator>Rubén Gil</dc:creator>
      <pubDate>Sat, 23 May 2026 20:02:03 +0000</pubDate>
      <link>https://dev.to/evolve-space/what-39m-powerlifting-records-tell-us-about-competition-strategy-an-eda-with-python-5g6k</link>
      <guid>https://dev.to/evolve-space/what-39m-powerlifting-records-tell-us-about-competition-strategy-an-eda-with-python-5g6k</guid>
      <description>&lt;p&gt;&lt;strong&gt;What 3.9M powerlifting records tell us about competition strategy — an EDA with Python&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;When I started this EDA project for my Data Science Master at &lt;a href="https://evolve.es" rel="noopener noreferrer"&gt;Evolve&lt;/a&gt;, I picked the Open Powerlifting dataset because beyond being a gym-rat, I've always been curious about the competition strategy in powerlifting.&lt;/p&gt;




&lt;h2&gt;
  
  
  The dataset
&lt;/h2&gt;

&lt;p&gt;Open Powerlifting is an open-source project that tracks powerlifting competition results worldwide. The full dataset has ~3.9M rows and 42 columns covering athlete info, every single lift attempt, and performance metrics.&lt;/p&gt;

&lt;p&gt;Before any analysis I filtered it down to sanctioned, drug-tested competitions only and kept only the columns I'd actually use. The main challenge: &lt;strong&gt;negative values mean a failed lift&lt;/strong&gt;, not bad data. That required building boolean columns to track success/failure before converting negatives to NaN.&lt;/p&gt;




&lt;h2&gt;
  
  
  The process
&lt;/h2&gt;

&lt;p&gt;Fully modularized in Python using pandas, numpy, seaborn, matplotlib and pingouin. The pipeline runs end-to-end from &lt;code&gt;main.py&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;raw CSV → filter → clean → features → assert → analyze&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Imputation was done conservatively; age from AgeClass ranges, bodyweight from weight class, never synthetic values. Also, NaN values were filtered dynamically for each question.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc7cvcav5uaidjpe614i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc7cvcav5uaidjpe614i.png" alt="DOTS metric vs age to show performance by age and sex" width="799" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Peak performance age:&lt;/strong&gt; Athletes peak between 22-24 years old and decline steadily after. No meaningful difference between men and women once normalized by bodyweight.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0imcoyq0clik93pnskdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0imcoyq0clik93pnskdg.png" alt="Fail rates at third lift for Squat, Bench and Deadlift" width="799" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where do athletes fail most?&lt;/strong&gt; Bench press has a &lt;strong&gt;54% fail rate&lt;/strong&gt; on the 3rd attempt. Squat and deadlift sit around 36-40%. The gap is consistent across sexes and equipment types — bench just behaves differently.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hls5sep3unkziw5jh0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hls5sep3unkziw5jh0c.png" alt="Success rate for 4th attempt for Squat, Bench and Deadlift" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 4th attempt:&lt;/strong&gt; When athletes take a 4th attempt, they succeed &lt;strong&gt;~77% of the time&lt;/strong&gt; on average. Deadlift leads at 83%. This is the most actionable insight of the whole project — just take the 4th attempt.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;About powerlifting&lt;/strong&gt;&lt;br&gt;
Athletes peak between 22-24, always take the 4th attempt and make sure you won't fail the 3rd one, it can change the whole competition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About analysing data&lt;/strong&gt;&lt;br&gt;
If you have enough data, maybe it's better to not fill the gaps with artificial values. Also, some features must be built before cleaning or you'll spend an hour chating with AI wondering why all your booleans are NaN.&lt;/p&gt;

&lt;p&gt;Full code: &lt;a href="https://github.com/rubengil-dev/power_lifting_analisis" rel="noopener noreferrer"&gt;github.com/rubengil-dev/power_lifting_analisis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Project developed during the Data Science Master at &lt;a href="https://evolve.es" rel="noopener noreferrer"&gt;Evolve&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>data</category>
      <category>sportscience</category>
    </item>
    <item>
      <title>Procure Flow: Un SRM de gestión de proveedores.</title>
      <dc:creator>Jose Martos</dc:creator>
      <pubDate>Fri, 22 May 2026 11:39:45 +0000</pubDate>
      <link>https://dev.to/evolve-space/de-hojas-de-calculo-a-ia-construyendo-una-plataforma-srm-moderna-5h2h</link>
      <guid>https://dev.to/evolve-space/de-hojas-de-calculo-a-ia-construyendo-una-plataforma-srm-moderna-5h2h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9qqy73etpuccokmv4ss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9qqy73etpuccokmv4ss.png" alt=" " width="799" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Qué es ProcureFlow y qué valor aporta.
&lt;/h1&gt;

&lt;p&gt;La idea de ProcureFlow se me ocurrió a raíz de una conversación que tuve con mi pareja acerca de su trabajo. Ella trabaja de Product Manager en una empresa de aceites esenciales y hace un tiempo me comentó que tipo de herramientas utilizaba y el caos que tenía en su día a día.&lt;/p&gt;

&lt;p&gt;Tras escuchar lo que me contó se me ocurrió que tenía que haber una forma de poder integrar todas las herramientas y así poder tener un sistema que recoja todas las herramientas, lo cual abarataría costes de mantenimiento y de tiempo.&lt;/p&gt;

&lt;p&gt;Y entonces mi razonamiento fue: Todas las empresas tienen un CRM, que es una aplicación web que sirve para gestionar los clientes, por qué no hago lo mismo pero para los proveedores. &lt;/p&gt;

&lt;p&gt;Y así es como nace ProcureFlow AI, un SRM (Supplier Relationship Management), enfocada en digitalizar procesos relacionados con compras, proveedores y automatización operativa. &lt;/p&gt;

&lt;h2&gt;
  
  
  Arquitectura del proyecto
&lt;/h2&gt;

&lt;p&gt;Pensé que lo más idóneo es desarrollar una aplicación web, la cual tendría  integrada todos los procesos y todas las herramientas necesarias para la gestión de proveedores de la empresa. La idea es que la aplicación se adapte a cualquier tipo de empresa y mercado, por lo que la aplicación está totalmente modularizada y puede ser fácilmente adaptada de acuerdo a las necesidades requeridas.&lt;/p&gt;

&lt;p&gt;La aplicación fue desarrollada usando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React, HTML5 y CSS para el frontend&lt;/li&gt;
&lt;li&gt;Symfony y PHP para el backend&lt;/li&gt;
&lt;li&gt;MySQL como base de datos&lt;/li&gt;
&lt;li&gt;OpenAI API para integración de IA&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  El proceso de desarrollo
&lt;/h2&gt;

&lt;p&gt;Para este proyecto decidí que lo más óptimo es separar el backend del frontend para trabajar de manera más cómoda y eficiente.&lt;/p&gt;

&lt;p&gt;Lo primero que desarrollé fue el backend de la aplicación. En esta parte implementé toda la lógica encargada de gestionar los proveedores y su información, creando los distintos endpoints y consultas necesarios para realizar operaciones como crear, consultar, modificar y eliminar registros en la base de datos.&lt;/p&gt;

&lt;p&gt;Una vez finalizado el backend, desarrollé el frontend de la aplicación. Esta parte es la encargada de mostrar la información al usuario de forma visual e intuitiva, además de realizar las peticiones necesarias al backend para consultar, crear, modificar o eliminar los datos de los proveedores.&lt;/p&gt;

&lt;p&gt;Finalmente, incorporé un sistema multiagente con el objetivo de ayudar al usuario en determinadas tareas y reducir el tiempo dedicado a procesos repetitivos. La idea era que la aplicación no se limitara únicamente a almacenar información, sino que también pudiera ofrecer asistencia dentro de la propia plataforma.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados obtenidos
&lt;/h2&gt;

&lt;p&gt;El resultado final fue una aplicación web dedicada a la gestión de proveedores de una empresa, aplicación totalmente modularizada y escalable a cualquier tipo de empresa y de mercado.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Funcionales principales:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dashboard administrativo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gestión completa de proveedores&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sistema de usuarios&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gestión de correos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integración con IA&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uno de los aprendizajes más importantes que me ha aportado este proyecto ha sido entender cómo se comunican el frontend y el backend a través de APIs. Durante el desarrollo aprendí a gestionar las distintas peticiones entre ambas partes de la aplicación, procesar la información recibida y mostrar los resultados correctamente al usuario.&lt;/p&gt;

&lt;p&gt;A continuación dejo los enlaces de acceso a la aplicación:&lt;/p&gt;

&lt;p&gt;Repositorio del proyecto:&lt;br&gt;
GitHub: &lt;a href="https://github.com/evolve-space/Proyecto-Master-IA-Evolve-JoseMartos.git" rel="noopener noreferrer"&gt;https://github.com/evolve-space/Proyecto-Master-IA-Evolve-JoseMartos.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Demo:&lt;br&gt;
&lt;a href="https://srm-compras-front.vercel.app/" rel="noopener noreferrer"&gt;https://srm-compras-front.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imágenes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav0pd1slyxcx6cdbbt11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav0pd1slyxcx6cdbbt11.png" alt=" " width="799" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gestión de proveedores:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futn8k74njgt7won09h8g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futn8k74njgt7won09h8g.png" alt=" " width="799" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gestión de correos:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F965ygj23wae9uub0ktce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F965ygj23wae9uub0ktce.png" alt=" " width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ProcureFlow AI fue desarrollado como proyecto académico durante el Máster en Inteligencia Artificial de &lt;a href="https://evolve.es?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Evolve&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>showdev</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Luxury and nationalities, my first data science project!</title>
      <dc:creator>Miriam</dc:creator>
      <pubDate>Fri, 22 May 2026 06:59:53 +0000</pubDate>
      <link>https://dev.to/evolve-space/luxury-and-nationalities-my-first-data-science-project-1hf1</link>
      <guid>https://dev.to/evolve-space/luxury-and-nationalities-my-first-data-science-project-1hf1</guid>
      <description>&lt;p&gt;Good morning everyone!&lt;br&gt;
I'm happy to announce that yesterday I finished my first project in data science!&lt;br&gt;
I link here the github repository. &lt;br&gt;
This project, based on the dataset "515K Hotel Reviews Data in Europe" from Jiashen Liu, analyzes the level of demand required by luxury tourism depending on the different nationalities, through Sentiment Analysis and Natural Language Processing.&lt;/p&gt;

&lt;p&gt;This is my first project, as I mentioned, so I would appreciate any feedback.&lt;br&gt;
I'm very confident that this is the right path for me, and I want to do everything I can to improve and become a competent data analyst.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Miris-01/evolve-project-01" rel="noopener noreferrer"&gt;Here is the repo!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, I want to say thank you to my professor and classmates from Evolve!&lt;/p&gt;

&lt;p&gt;Read you soon!&lt;/p&gt;

&lt;p&gt;Miriam.&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>python</category>
      <category>jupyter</category>
      <category>nlp</category>
    </item>
    <item>
      <title>ENS Readiness Assistant — Automatizando evaluaciones de adecuación al ENS con HTML, CSS y JavaScript</title>
      <dc:creator>Himardlg</dc:creator>
      <pubDate>Tue, 19 May 2026 08:07:29 +0000</pubDate>
      <link>https://dev.to/evolve-space/ens-readiness-assistant-automatizando-evaluaciones-de-adecuacion-al-ens-con-html-css-y-javascript-5ah5</link>
      <guid>https://dev.to/evolve-space/ens-readiness-assistant-automatizando-evaluaciones-de-adecuacion-al-ens-con-html-css-y-javascript-5ah5</guid>
      <description>&lt;p&gt;Quizás no sean el mejor programador de los presentes en esta organización, soy un perfil mas técnico, pero seguramente de lo que mas le gustan los retos y desafíos. &lt;br&gt;
Uno de los mayores retos en proyectos GRC es convertir normativa compleja en algo accionable para las organizaciones.&lt;br&gt;
El Esquema Nacional de Seguridad (RD 311/2022) es obligatorio para la Administración Pública española y sus proveedores tecnológicos — pero muchas entidades no saben por dónde empezar, qué categoría les corresponde ni qué controles tienen que implementar.&lt;br&gt;
Por eso construí ENS Readiness Assistant. &lt;br&gt;
&lt;strong&gt;¿Qué es?&lt;/strong&gt;&lt;br&gt;
Una herramienta web que guía a cualquier organización a través de una evaluación de adecuación al ENS en 4 pasos y genera un informe con sus brechas y recomendaciones concretas.&lt;br&gt;
Sin instalación. Sin backend. Sin dependencias.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué hace exactamente?&lt;/strong&gt;&lt;br&gt;
Paso 1 — Datos de la organización&lt;br&gt;
Determina el tipo de entidad y sector para saber si el ENS aplica según el Art. 2 del RD 311/2022. No todas las organizaciones están obligadas — la herramienta lo aclara desde el principio.&lt;br&gt;
Paso 2 — Sistemas y servicios&lt;br&gt;
Evalúa el tipo de información gestionada y el impacto potencial de un incidente para determinar la categoría del sistema: Básica, Media o Alta.&lt;br&gt;
Paso 3 — Estado de controles&lt;br&gt;
Checklist de 10 controles clave del Anexo II del ENS con tres estados posibles: implementado, parcialmente implementado o no implementado.&lt;br&gt;
Paso 4 — Informe de resultados&lt;br&gt;
Genera automáticamente:&lt;/p&gt;

&lt;p&gt;✅ Si el ENS aplica o no y por qué&lt;br&gt;
🏷️ Categoría del sistema calculada&lt;br&gt;
📊 Porcentaje de cumplimiento actual&lt;br&gt;
🔍 Gap analysis con brechas por prioridad Alta / Media / Baja&lt;br&gt;
💡 Recomendaciones con plazos estimados de implementación&lt;br&gt;
🖨️ Exportación a PDF mediante impresión del navegador&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tecnología utilizada&lt;/strong&gt;&lt;br&gt;
MVP v1.0 construido con HTML, CSS y JavaScript vanilla — sin frameworks, sin backend, sin dependencias externas. Desplegado en GitHub Pages.&lt;br&gt;
La decisión fue deliberada — quería publicar rápido, aprender haciendo y que cualquiera pudiera clonar el repo y ejecutarlo abriendo un solo archivo HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lo que aprendí construyéndolo&lt;/strong&gt;&lt;br&gt;
Transformar normativa en lógica técnica es más complejo de lo que parece.&lt;br&gt;
Cada artículo del RD 311/2022 tiene matices que dependen del tipo de organización, la categoría del sistema y las dimensiones de seguridad afectadas. Por ejemplo, la obligación de usar MFA no es universal — aplica a accesos remotos en sistemas de categoría Media y Alta, no en todos los casos.&lt;br&gt;
Ese proceso de traducción normativa → lógica → código es exactamente el núcleo del trabajo GRC. Y construir esta herramienta me obligó a entenderlo de verdad, no solo a nivel teórico.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/evolve-space/ENS-Readiness-Assistant-Himardeleongonzalez" rel="noopener noreferrer"&gt;Proyecto ENS-Readiness-Assistant&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cybersecurity</category>
      <category>grc</category>
      <category>compliance</category>
    </item>
    <item>
      <title>RedRecruit: Cómo construí una plataforma de empleo en ciberseguridad con Python, Flask y Stripe</title>
      <dc:creator>Pablo Díaz</dc:creator>
      <pubDate>Mon, 18 May 2026 15:47:44 +0000</pubDate>
      <link>https://dev.to/evolve-space/redrecruit-como-construi-una-plataforma-de-empleo-en-ciberseguridad-con-python-flask-y-stripe-mio</link>
      <guid>https://dev.to/evolve-space/redrecruit-como-construi-una-plataforma-de-empleo-en-ciberseguridad-con-python-flask-y-stripe-mio</guid>
      <description>&lt;h1&gt;
  
  
  RedRecruit: Cómo construí una plataforma de empleo en ciberseguridad con Python, Flask y Stripe
&lt;/h1&gt;

&lt;p&gt;La mayoría de plataformas de empleo actuales son demasiado genéricas para perfiles técnicos especializados en ciberseguridad. Quería crear una aplicación enfocada específicamente en este sector, donde los usuarios pudieran buscar vacantes relevantes, gestionar su cuenta de forma segura y acceder a funcionalidades premium dentro de una experiencia moderna y profesional.&lt;/p&gt;

&lt;p&gt;Con ese objetivo nació RedRecruit, una plataforma desarrollada con Flask que combina autenticación segura, integración de pagos con Stripe y consumo de APIs reales de empleo orientadas al sector tecnológico y de ciberseguridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack tecnológico
&lt;/h2&gt;

&lt;p&gt;Estas son las principales tecnologías utilizadas durante el desarrollo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3&lt;/li&gt;
&lt;li&gt;Flask&lt;/li&gt;
&lt;li&gt;Flask-Login&lt;/li&gt;
&lt;li&gt;Flask-Bcrypt&lt;/li&gt;
&lt;li&gt;Stripe Checkout&lt;/li&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;li&gt;HTML/CSS&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Requests&lt;/li&gt;
&lt;li&gt;itsdangerous&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Funcionalidades principales
&lt;/h2&gt;

&lt;p&gt;La aplicación incluye múltiples funcionalidades orientadas a una experiencia realista de plataforma SaaS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Búsqueda de vacantes en ciberseguridad usando APIs reales&lt;/li&gt;
&lt;li&gt;Resultados organizados en páginas dedicadas&lt;/li&gt;
&lt;li&gt;Registro y login de usuarios&lt;/li&gt;
&lt;li&gt;Verificación de email y recuperación de contraseña&lt;/li&gt;
&lt;li&gt;Integración premium mediante Stripe Checkout&lt;/li&gt;
&lt;li&gt;Panel admin protegido&lt;/li&gt;
&lt;li&gt;Navegación profesional mediante breadcrumbs&lt;/li&gt;
&lt;li&gt;Gestión de sesiones y perfiles&lt;/li&gt;
&lt;li&gt;Integración con APIs Adzuna y JSearch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lo más técnico: autenticación y Stripe
&lt;/h2&gt;

&lt;p&gt;La parte más interesante del proyecto fue diseñar un sistema de autenticación seguro y combinarlo con una integración funcional de pagos online.&lt;/p&gt;

&lt;p&gt;Para la autenticación utilicé Flask-Login junto con Flask-Bcrypt para proteger las contraseñas mediante hashing seguro. Además, implementé verificación de email y recuperación de contraseña utilizando tokens temporales generados con &lt;code&gt;itsdangerous&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Por otro lado, integré Stripe Checkout para permitir la activación de cuentas premium durante 24 horas. Esto implicó gestionar sesiones de pago, validaciones y control de acceso premium desde el backend, simulando una arquitectura muy cercana a aplicaciones SaaS reales.&lt;/p&gt;

&lt;p&gt;También trabajé especialmente en la organización modular del backend, separando autenticación, base de datos, servicios de email y clientes API en distintos módulos para mantener una estructura limpia y escalable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultado final
&lt;/h2&gt;

&lt;p&gt;El resultado fue una plataforma web completamente funcional orientada a contratación especializada en ciberseguridad.&lt;/p&gt;

&lt;p&gt;🔗 Web desplegada:&lt;br&gt;
&lt;a href="https://pablodiescj-dot.github.io/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;RedRecruit Online&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 Repositorio GitHub:&lt;br&gt;
&lt;a href="https://github.com/pablodiescj-dot/Proyecto-Master-Ciberseguridad-Evolve-PabloDiazHernandez?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Proyecto RedRecruit en GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Características destacadas del resultado final:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arquitectura modular&lt;/li&gt;
&lt;li&gt;Sistema premium funcional&lt;/li&gt;
&lt;li&gt;Integración de APIs externas&lt;/li&gt;
&lt;li&gt;Seguridad en autenticación&lt;/li&gt;
&lt;li&gt;Gestión de usuarios&lt;/li&gt;
&lt;li&gt;Diseño profesional y responsive&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lo que aprendí
&lt;/h2&gt;

&lt;p&gt;Este proyecto me permitió combinar desarrollo backend, seguridad web, autenticación, APIs externas y pagos online dentro de una misma aplicación completa.&lt;/p&gt;

&lt;p&gt;Además de mejorar mis habilidades con Flask y Python, aprendí cómo estructurar aplicaciones web más cercanas a un entorno profesional y cómo integrar servicios externos de forma segura y mantenible.&lt;/p&gt;

&lt;p&gt;También entendí mejor la importancia de la experiencia de usuario y de construir proyectos no solo funcionales, sino también presentables y preparados para producción.&lt;/p&gt;

&lt;p&gt;Proyecto académico desarrollado durante el Master en Ciberseguridad de Evolve.&lt;/p&gt;

&lt;p&gt;Si quieres ver el código completo o probar la plataforma, puedes visitar tanto el repositorio como la demo online.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>python</category>
      <category>flask</category>
      <category>stripe</category>
    </item>
    <item>
      <title>ESP32 GARDEN</title>
      <dc:creator>Darío Rosindo</dc:creator>
      <pubDate>Mon, 18 May 2026 10:11:43 +0000</pubDate>
      <link>https://dev.to/evolve-space/esp32-garden-2m5k</link>
      <guid>https://dev.to/evolve-space/esp32-garden-2m5k</guid>
      <description>&lt;h1&gt;
  
  
  Construyendo ESP32 Garden: Un Sistema IoT de Monitorización Agrícola de Extremo a Extremo
&lt;/h1&gt;

&lt;p&gt;La gestión botánica eficaz requiere una monitorización ambiental precisa. Depender de suposiciones para los horarios de riego o las condiciones ambientales a menudo conduce a una salud subóptima de las plantas. Para solucionar esto, desarrollé &lt;strong&gt;ESP32 Garden&lt;/strong&gt;, un sistema IoT de monitorización agrícola de extremo a extremo.&lt;/p&gt;

&lt;p&gt;Este proyecto captura datos ambientales en tiempo real utilizando sensores de hardware, los procesa a través de una arquitectura de microservicios personalizada en Python y visualiza los datos históricos en un panel web interactivo . Además, use una carcasa impresa en 3D para proteger los microcontroladores de la exposición a la tierra y la humedad.&lt;/p&gt;

&lt;p&gt;A continuación, se detalla la arquitectura del sistema, los componentes de hardware y el stack de software utilizados para dar vida a este proyecto.&lt;/p&gt;

&lt;h2&gt;
  
  
  La Arquitectura de Hardware
&lt;/h2&gt;

&lt;p&gt;El sistema utiliza un microcontrolador &lt;strong&gt;ESP32&lt;/strong&gt;, seleccionado principalmente por sus capacidades Wi-Fi integradas y su decente potencia de procesamiento. Se interconecta con los siguientes componentes de hardware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sensor DHT11:&lt;/strong&gt; Captura la temperatura ambiente y la humedad relativa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensor Analógico de Humedad del Suelo:&lt;/strong&gt; Mide el contenido volumétrico de agua en la tierra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensor de Lluvia / Gotas:&lt;/strong&gt; Detecta eventos de precipitación.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pantalla OLED (SSD1306):&lt;/strong&gt; Proporciona depuración y lectura de estado en tiempo real en el propio dispositivo, sin necesidad de acceder al panel web.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Diseñando la Carcasa
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxqxev9vx4do2ncwbc08e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxqxev9vx4do2ncwbc08e.jpg" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Desplegar componentes electrónicos expuestos en un entorno agrícola introduce riesgos significativos de cortocircuitos debido al agua y la suciedad. Para mitigar esto,imprimí en 3D una carcasa personalizada. Diseñada para parecerse a una pequeña casa, esta cubierta aloja de forma segura el ESP32 y el cableado, dejando solo las sondas de los sensores necesarias expuestas al entorno.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Stack de Software
&lt;/h2&gt;

&lt;p&gt;La infraestructura de software es modular, dividiendo las responsabilidades entre el firmware integrado, una interfaz gráfica de gestión de escritorio, la ingesta de datos en segundo plano y la visualización frontend.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. El Firmware: C++,Arduino
&lt;/h3&gt;

&lt;p&gt;El ESP32 actúa como un dispositivo de borde (&lt;em&gt;edge device&lt;/em&gt;) ejecutando un servidor web ligero. En lugar de renderizar una página HTML pesada en el microcontrolador, expone una API REST a través del endpoint &lt;code&gt;/json&lt;/code&gt;. Cuando se le consulta, construye y devuelve las lecturas actuales de los sensores en un payload JSON estructurado.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleJSON&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;temperatura&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperatura&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;humedad_amb&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;humedadAmb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;suelo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;porcSuelo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;lluvia&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;porcLluvia&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Servicio de Ingesta de Datos
&lt;/h3&gt;

&lt;p&gt;Para registrar los datos a lo largo del tiempo, un script de Python actúa como un demonio en segundo plano. Envía peticiones HTTP periódicamente a la dirección IP del ESP32, analiza los datos JSON entrantes, añade una marca de tiempo y registra la entrada en un archivo CSV local utilizando &lt;code&gt;pandas&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;nueva_fila&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fecha_hora&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%d %H:%M:%S&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperatura&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperatura&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;humedad_amb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;humedad_amb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suelo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suelo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lluvia&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lluvia&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;nueva_fila&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;COLUMNAS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;COLUMNAS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error de red: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Panel de Visualización de Datos
&lt;/h3&gt;

&lt;p&gt;Los datos en bruto requieren interpretación. Utilicé &lt;code&gt;streamlit&lt;/code&gt; y &lt;code&gt;plotly&lt;/code&gt; para desarrollar una aplicación web que lee el archivo CSV compilado y renderiza gráficos interactivos de series temporales. Esto permite un análisis detallado de tendencias, como monitorizar la tasa exacta de agotamiento de la humedad del suelo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;plotly.express&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;

&lt;span class="c1"&gt;# Renderizar gráficos interactivos con Plotly
&lt;/span&gt;&lt;span class="n"&gt;col_left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col_right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;col_left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fig_suelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_filtrado&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fecha_hora&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suelo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Humedad del Suelo (%)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;color_discrete_sequence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#7D4F39&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plotly_chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fig_suelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_container_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**Media de humedad (Suelo):** &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;df_filtrado&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suelo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;col_right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fig_hum_amb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_filtrado&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fecha_hora&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;humedad_amb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                          &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Humedad Ambiental (%)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;color_discrete_sequence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#0077B6&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plotly_chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fig_hum_amb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_container_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Gestor de Configuración (main.py)
&lt;/h3&gt;

&lt;p&gt;Para agilizar el despliegue y gestionar los microservicios, el proyecto incluye un panel de control construido con &lt;code&gt;tkinter&lt;/code&gt;. Esta GUI permite al usuario monitorizar el estado de red del ESP32 en un hilo en segundo plano y alternar los servicios de captura de datos y del panel web mediante subprocesos (&lt;em&gt;subprocesses&lt;/em&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;monitorear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ip_esp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lbl_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ONLINE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;green&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lbl_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFFLINE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;red&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  El Montaje Final en Acción
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foj0smv2r117dtdh3cm78.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foj0smv2r117dtdh3cm78.jpg" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94dc5ivk6hoybarb5sma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94dc5ivk6hoybarb5sma.png" alt=" " width="392" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27v96rjnxtjeihb75fmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27v96rjnxtjeihb75fmd.png" alt=" " width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Código Abierto y Replicación
&lt;/h2&gt;

&lt;p&gt;Este proyecto acorta la brecha entre los sistemas integrados de bajo nivel y los frameworks de ciencia de datos de alto nivel. Sirve como una base altamente escalable para cualquiera que busque adentrarse en el espacio del IoT.&lt;/p&gt;

&lt;p&gt;Todo el proyecto es de código abierto. Puedes revisar la arquitectura completa, acceder al firmware y encontrar instrucciones detalladas de configuración en mi repositorio de GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ros1ndoo/ESP32Garden" rel="noopener noreferrer"&gt;ros1ndoo/ESP32Garden en GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pasos para el Despliegue:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Clona el repositorio e instala las dependencias.&lt;/li&gt;
&lt;li&gt;Conecta los sensores DHT11, de humedad del suelo y de lluvia a los pines GPIO designados del ESP32.&lt;/li&gt;
&lt;li&gt;Actualiza el archivo &lt;code&gt;settup.txt&lt;/code&gt; con las credenciales de tu red local.&lt;/li&gt;
&lt;li&gt;Ejecuta &lt;code&gt;main.py&lt;/code&gt;, haz clic en "Aplicar" para generar las cabeceras de C++ y flashea el ESP32.&lt;/li&gt;
&lt;li&gt;Utiliza la interfaz de Tkinter para inicializar el servicio de captura de datos y lanzar el panel de &lt;code&gt;streamlit&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>iot</category>
      <category>datascience</category>
      <category>esp32</category>
      <category>python</category>
    </item>
  </channel>
</rss>
