From 97e63d70303ce36e730595c9d66dfc45ca3a60e4 Mon Sep 17 00:00:00 2001 From: Kaia-Alenia Date: Wed, 22 Jul 2026 15:18:56 -0600 Subject: [PATCH 1/3] chore: despliegue de arquitectura modular v2 y esqueleto de python --- {ejemplos => 00-rompehielo}/.gitkeep | 0 {glosario => alenia-nerve}/.gitkeep | 0 {retos => archivo-historico/ejemplos}/.gitkeep | 0 .../general/01-lista-de-tareas-cli/README.md | 0 .../ejemplo_menu_interactivo.go | 0 .../ejemplo_menu_interactivo.js | 0 .../ejemplo_menu_interactivo.py | 0 .../ejemplo_menu_interactivo.rs | 0 .../general/02-web-scraper-simple/README.md | 0 .../02-web-scraper-simple/ejemplo_requests.go | 0 .../02-web-scraper-simple/ejemplo_requests.js | 0 .../02-web-scraper-simple/ejemplo_requests.py | 0 .../02-web-scraper-simple/ejemplo_requests.rs | 0 .../general/03-juego-de-adivinanza/README.md | 0 .../03-juego-de-adivinanza/ejemplo_validacion.go | 0 .../03-juego-de-adivinanza/ejemplo_validacion.js | 0 .../03-juego-de-adivinanza/ejemplo_validacion.py | 0 .../03-juego-de-adivinanza/ejemplo_validacion.rs | 0 .../general/04-analizador-de-gastos-csv/README.md | 0 .../04-analizador-de-gastos-csv/ejemplo_csv.go | 0 .../04-analizador-de-gastos-csv/ejemplo_csv.js | 0 .../04-analizador-de-gastos-csv/ejemplo_csv.py | 0 .../04-analizador-de-gastos-csv/ejemplo_csv.rs | 0 .../general/05-generador-de-contrasenas/README.md | 0 .../05-generador-de-contrasenas/ejemplo_secrets.go | 0 .../05-generador-de-contrasenas/ejemplo_secrets.js | 0 .../05-generador-de-contrasenas/ejemplo_secrets.py | 0 .../05-generador-de-contrasenas/ejemplo_secrets.rs | 0 .../general/06-conversor-de-unidades/README.md | 0 .../06-conversor-de-unidades/ejemplo_funciones.py | 0 .../general/07-calculadora-de-imc/README.md | 0 .../07-calculadora-de-imc/ejemplo_formateo.py | 0 .../general/08-organizador-de-archivos/README.md | 0 .../08-organizador-de-archivos/ejemplo_pathlib.py | 0 .../general/09-recordatorios-en-terminal/README.md | 0 .../ejemplo_datetime.py | 0 .../general/10-analizador-de-texto/README.md | 0 .../10-analizador-de-texto/ejemplo_counter.py | 0 .../ejemplos}/general/11-decoradores/README.md | 0 .../general/11-decoradores/ejemplo_decorador.py | 0 .../ejemplos}/general/12-regex/README.md | 0 .../ejemplos}/general/12-regex/ejemplo_regex.py | 0 .../ejemplos}/general/13-generadores/README.md | 0 .../general/13-generadores/ejemplo_generadores.py | 0 .../ejemplos}/general/14-argparse/README.md | 0 .../general/14-argparse/ejemplo_argparse.py | 0 .../ejemplos}/general/15-shutil/README.md | 0 .../ejemplos}/general/15-shutil/ejemplo_shutil.py | 0 .../ejemplos}/general/16-excepciones/README.md | 0 .../general/16-excepciones/ejemplo_excepciones.py | 0 .../ejemplos}/general/17-json/README.md | 0 .../ejemplos}/general/17-json/ejemplo_json.py | 0 .../ejemplos}/general/18-poo-avanzada/README.md | 0 .../general/18-poo-avanzada/ejemplo_propiedades.py | 0 .../ejemplos}/nerve/01-hola-nerve/README.md | 0 .../ejemplos}/nerve/01-hola-nerve/emisor.go | 0 .../ejemplos}/nerve/01-hola-nerve/emisor.js | 0 .../ejemplos}/nerve/01-hola-nerve/emisor.py | 0 .../ejemplos}/nerve/01-hola-nerve/emisor.rs | 0 .../ejemplos}/nerve/01-hola-nerve/receptor.go | 0 .../ejemplos}/nerve/01-hola-nerve/receptor.js | 0 .../ejemplos}/nerve/01-hola-nerve/receptor.py | 0 .../ejemplos}/nerve/01-hola-nerve/receptor.rs | 0 .../ejemplos}/nerve/02-pub-sub-basico/README.md | 0 .../ejemplos}/nerve/02-pub-sub-basico/publicador.go | 0 .../ejemplos}/nerve/02-pub-sub-basico/publicador.js | 0 .../ejemplos}/nerve/02-pub-sub-basico/publicador.py | 0 .../ejemplos}/nerve/02-pub-sub-basico/publicador.rs | 0 .../ejemplos}/nerve/02-pub-sub-basico/suscriptor.go | 0 .../ejemplos}/nerve/02-pub-sub-basico/suscriptor.js | 0 .../ejemplos}/nerve/02-pub-sub-basico/suscriptor.py | 0 .../ejemplos}/nerve/02-pub-sub-basico/suscriptor.rs | 0 .../ejemplos}/nerve/03-peticion-respuesta/README.md | 0 .../nerve/03-peticion-respuesta/cliente.go | 0 .../nerve/03-peticion-respuesta/cliente.js | 0 .../nerve/03-peticion-respuesta/cliente.py | 0 .../nerve/03-peticion-respuesta/cliente.rs | 0 .../nerve/03-peticion-respuesta/servidor.go | 0 .../nerve/03-peticion-respuesta/servidor.js | 0 .../nerve/03-peticion-respuesta/servidor.py | 0 .../nerve/03-peticion-respuesta/servidor.rs | 0 .../nerve/04-vigilante-de-carpetas/README.md | 0 .../nerve/04-vigilante-de-carpetas/ejemplo.go | 0 .../nerve/04-vigilante-de-carpetas/ejemplo.js | 0 .../nerve/04-vigilante-de-carpetas/ejemplo.py | 0 .../nerve/04-vigilante-de-carpetas/ejemplo.rs | 0 .../ejemplos}/nerve/05-logger-distribuido/README.md | 0 .../nerve/05-logger-distribuido/ejemplo.go | 0 .../nerve/05-logger-distribuido/ejemplo.js | 0 .../nerve/05-logger-distribuido/ejemplo.py | 0 .../nerve/05-logger-distribuido/ejemplo.rs | 0 .../nerve/06-puente-de-progreso-gif/README.md | 0 .../nerve/06-puente-de-progreso-gif/ejemplo.go | 0 .../nerve/06-puente-de-progreso-gif/ejemplo.js | 0 .../nerve/06-puente-de-progreso-gif/ejemplo.py | 0 .../nerve/06-puente-de-progreso-gif/ejemplo.rs | 0 .../ejemplos}/nerve/07-bot-discord-nerve/README.md | 0 .../ejemplos}/nerve/07-bot-discord-nerve/ejemplo.go | 0 .../ejemplos}/nerve/07-bot-discord-nerve/ejemplo.js | 0 .../ejemplos}/nerve/07-bot-discord-nerve/ejemplo.py | 0 .../ejemplos}/nerve/07-bot-discord-nerve/ejemplo.rs | 0 .../ejemplos}/nerve/08-puente-http/README.md | 0 .../ejemplos}/nerve/08-puente-http/ejemplo.go | 0 .../ejemplos}/nerve/08-puente-http/ejemplo.js | 0 .../ejemplos}/nerve/08-puente-http/ejemplo.py | 0 .../ejemplos}/nerve/08-puente-http/ejemplo.rs | 0 .../ejemplos}/nerve/09-probador-de-carga/README.md | 0 .../ejemplos}/nerve/09-probador-de-carga/ejemplo.go | 0 .../ejemplos}/nerve/09-probador-de-carga/ejemplo.js | 0 .../ejemplos}/nerve/09-probador-de-carga/ejemplo.py | 0 .../ejemplos}/nerve/09-probador-de-carga/ejemplo.rs | 0 .../nerve/10-cliente-nuevo-lenguaje/README.md | 0 .../nerve/10-cliente-nuevo-lenguaje/ejemplo.go | 0 .../nerve/10-cliente-nuevo-lenguaje/ejemplo.js | 0 .../nerve/10-cliente-nuevo-lenguaje/ejemplo.py | 0 .../nerve/10-cliente-nuevo-lenguaje/ejemplo.rs | 0 .../glosario}/.gitkeep | 0 {glosario => archivo-historico/glosario}/README.md | 0 .../glosario}/conceptos-software.md | 0 .../glosario}/git-github.md | 0 .../glosario}/go/01-fundamentos.md | 0 .../glosario}/go/02-control-de-flujo.md | 0 .../glosario}/go/03-funciones.md | 0 .../glosario}/go/04-estructuras-de-datos.md | 0 .../glosario}/go/05-modulos-y-librerias.md | 0 .../glosario}/go/06-manejo-de-errores.md | 0 .../glosario}/go/07-archivos-y-entrada-salida.md | 0 .../glosario}/go/08-poo-basica.md | 0 .../glosario}/go/09-concurrencia.md | 0 .../glosario}/go/README.md | 0 .../glosario}/javascript/01-fundamentos.md | 0 .../glosario}/javascript/02-control-de-flujo.md | 0 .../glosario}/javascript/03-funciones.md | 0 .../glosario}/javascript/04-estructuras-de-datos.md | 0 .../glosario}/javascript/05-modulos-y-librerias.md | 0 .../glosario}/javascript/06-manejo-de-errores.md | 0 .../javascript/07-archivos-y-entrada-salida.md | 0 .../glosario}/javascript/08-poo-basica.md | 0 .../glosario}/javascript/09-asincronia.md | 0 .../glosario}/javascript/README.md | 0 .../glosario}/python/01-fundamentos.md | 0 .../glosario}/python/02-control-de-flujo.md | 0 .../glosario}/python/03-funciones.md | 0 .../glosario}/python/04-estructuras-de-datos.md | 0 .../glosario}/python/05-modulos-y-librerias.md | 0 .../glosario}/python/06-manejo-de-errores.md | 0 .../python/07-archivos-y-entrada-salida.md | 0 .../glosario}/python/08-poo-basica.md | 0 .../glosario}/python/README.md | 0 .../glosario}/redes-sockets.md | 0 .../glosario}/rust/01-fundamentos.md | 0 .../glosario}/rust/02-control-de-flujo.md | 0 .../glosario}/rust/03-funciones.md | 0 .../glosario}/rust/04-estructuras-de-datos.md | 0 .../glosario}/rust/05-modulos-y-librerias.md | 0 .../glosario}/rust/06-manejo-de-errores.md | 0 .../glosario}/rust/07-archivos-y-entrada-salida.md | 0 .../glosario}/rust/08-poo-basica.md | 0 .../glosario}/rust/README.md | 0 .../glosario}/terminal-linux.md | 0 .../glosario}/terminal-windows.md | 0 .../soluciones => archivo-historico/retos}/.gitkeep | 0 .../retos}/00-rompehielo/CesarAAR.py | 0 .../retos}/00-rompehielo/README.md | 0 .../retos}/00-rompehielo/Vi-dot1.cpp | 0 .../retos}/00-rompehielo/ashu.py | 0 .../retos}/00-rompehielo/tacos.py | 0 .../retos}/general/01-lista-de-tareas-cli/README.md | 0 .../soluciones/CesarAAR-python/Functions.py | 0 .../soluciones/CesarAAR-python/Main.py | 0 .../soluciones/CesarAAR-python/Model.py | 0 .../soluciones/CesarAAR-python/README.md | 0 .../soluciones/CesarAAR-python/requirements.txt | Bin .../soluciones/Tacos-python/Readme.md | 0 .../soluciones/Tacos-python/Tareas.py | 0 .../retos}/general/02-web-scraper-simple/README.md | 0 .../02-web-scraper-simple}/soluciones/.gitkeep | 0 .../retos}/general/03-juego-de-adivinanza/README.md | 0 .../03-juego-de-adivinanza}/soluciones/.gitkeep | 0 .../general/04-analizador-de-gastos-csv/README.md | 0 .../soluciones/.gitkeep | 0 .../general/05-generador-de-contrasenas/README.md | 0 .../soluciones/.gitkeep | 0 .../general/06-conversor-de-unidades/README.md | 0 .../06-conversor-de-unidades}/soluciones/.gitkeep | 0 .../retos}/general/07-calculadora-de-imc/README.md | 0 .../07-calculadora-de-imc}/soluciones/.gitkeep | 0 .../isaacglezacosta-python/07_calculadora_de_imc.py | 0 .../soluciones/isaacglezacosta-python/README.md | 0 .../general/08-organizador-de-archivos/README.md | 0 .../08-organizador-de-archivos}/soluciones/.gitkeep | 0 .../general/09-recordatorios-en-terminal/README.md | 0 .../soluciones/.gitkeep | 0 .../retos}/general/10-analizador-de-texto/README.md | 0 .../10-analizador-de-texto}/soluciones/.gitkeep | 0 .../general/11-profiler-de-funciones/README.md | 0 .../11-profiler-de-funciones}/soluciones/.gitkeep | 0 .../retos}/general/12-validador-de-emails/README.md | 0 .../12-validador-de-emails}/soluciones/.gitkeep | 0 .../general/13-lector-archivos-gigantes/README.md | 0 .../soluciones/.gitkeep | 0 .../retos}/general/14-cli-del-clima/README.md | 0 .../general/14-cli-del-clima}/soluciones/.gitkeep | 0 .../general/15-herramienta-de-backup/README.md | 0 .../15-herramienta-de-backup}/soluciones/.gitkeep | 0 .../retos}/general/16-validador-de-edad/README.md | 0 .../16-validador-de-edad}/soluciones/.gitkeep | 0 .../general/17-lector-de-configuraciones/README.md | 0 .../soluciones}/.gitkeep | 0 .../general/18-inventario-de-tienda/README.md | 0 .../18-inventario-de-tienda}/soluciones/.gitkeep | 0 .../retos/nerve/01-chat-terminal}/.gitkeep | 0 .../retos}/nerve/01-chat-terminal/README.md | 0 .../nerve/01-chat-terminal}/soluciones/.gitkeep | 0 .../retos/nerve/02-reloj-sincronizado}/.gitkeep | 0 .../retos}/nerve/02-reloj-sincronizado/README.md | 0 .../02-reloj-sincronizado}/soluciones/.gitkeep | 0 .../retos/nerve/03-traductor-de-mensajes}/.gitkeep | 0 .../retos}/nerve/03-traductor-de-mensajes/README.md | 0 .../03-traductor-de-mensajes}/soluciones/.gitkeep | 0 .../retos/nerve/04-vigilante-de-carpetas}/.gitkeep | 0 .../retos}/nerve/04-vigilante-de-carpetas/README.md | 0 .../04-vigilante-de-carpetas}/soluciones/.gitkeep | 0 .../retos/nerve/05-logger-distribuido}/.gitkeep | 0 .../retos}/nerve/05-logger-distribuido/README.md | 0 .../05-logger-distribuido}/soluciones/.gitkeep | 0 .../soluciones/Vidot1-Rust/Cargo.lock | 0 .../soluciones/Vidot1-Rust/Cargo.toml | 0 .../soluciones/Vidot1-Rust/model.sqlite | 0 .../soluciones/Vidot1-Rust/readme.md | 0 .../soluciones/Vidot1-Rust/src/db.rs | 0 .../soluciones/Vidot1-Rust/src/lib.rs | 0 .../soluciones/Vidot1-Rust/src/main.rs | 0 .../soluciones/Vidot1-Rust/src/nerve.rs | 0 .../soluciones/Vidot1-Rust/tests/test.rs | 0 .../retos/nerve/06-puente-de-progreso-gif}/.gitkeep | 0 .../nerve/06-puente-de-progreso-gif/README.md | 0 .../06-puente-de-progreso-gif}/soluciones/.gitkeep | 0 .../retos/nerve/07-bot-discord-nerve}/.gitkeep | 0 .../retos}/nerve/07-bot-discord-nerve/README.md | 0 .../nerve/07-bot-discord-nerve}/soluciones/.gitkeep | 0 .../retos/nerve/08-puente-http}/.gitkeep | 0 .../retos}/nerve/08-puente-http/README.md | 0 .../retos/nerve/08-puente-http}/soluciones/.gitkeep | 0 .../retos/nerve/09-probador-de-carga}/.gitkeep | 0 .../retos}/nerve/09-probador-de-carga/README.md | 0 .../nerve/09-probador-de-carga}/soluciones/.gitkeep | 0 .../retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep | 0 .../nerve/10-cliente-nuevo-lenguaje/README.md | 0 .../10-cliente-nuevo-lenguaje/soluciones/.gitkeep | 0 go/.gitkeep | 0 javascript/.gitkeep | 0 python/.gitkeep | 0 python/Modulo-01-Fundamentos/.gitkeep | 0 python/Modulo-01-Fundamentos/01-hola-mundo/.gitkeep | 0 python/Modulo-01-Fundamentos/02-variables/.gitkeep | 0 .../03-tipos-de-datos/.gitkeep | 0 python/Modulo-01-Fundamentos/04-operadores/.gitkeep | 0 .../Modulo-01-Fundamentos/05-condicionales/.gitkeep | 0 .../Modulo-01-Fundamentos/RETO-FINAL-MOD-1/.gitkeep | 0 python/Modulo-02-Estructuras/.gitkeep | 0 python/Modulo-03-Exterior/.gitkeep | 0 python/Modulo-04-Nerve/.gitkeep | 0 rust/.gitkeep | 0 264 files changed, 0 insertions(+), 0 deletions(-) rename {ejemplos => 00-rompehielo}/.gitkeep (100%) rename {glosario => alenia-nerve}/.gitkeep (100%) rename {retos => archivo-historico/ejemplos}/.gitkeep (100%) rename {ejemplos => archivo-historico/ejemplos}/general/01-lista-de-tareas-cli/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/general/02-web-scraper-simple/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/02-web-scraper-simple/ejemplo_requests.go (100%) rename {ejemplos => archivo-historico/ejemplos}/general/02-web-scraper-simple/ejemplo_requests.js (100%) rename {ejemplos => archivo-historico/ejemplos}/general/02-web-scraper-simple/ejemplo_requests.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/02-web-scraper-simple/ejemplo_requests.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/general/03-juego-de-adivinanza/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/03-juego-de-adivinanza/ejemplo_validacion.go (100%) rename {ejemplos => archivo-historico/ejemplos}/general/03-juego-de-adivinanza/ejemplo_validacion.js (100%) rename {ejemplos => archivo-historico/ejemplos}/general/03-juego-de-adivinanza/ejemplo_validacion.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/03-juego-de-adivinanza/ejemplo_validacion.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/general/04-analizador-de-gastos-csv/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/04-analizador-de-gastos-csv/ejemplo_csv.go (100%) rename {ejemplos => archivo-historico/ejemplos}/general/04-analizador-de-gastos-csv/ejemplo_csv.js (100%) rename {ejemplos => archivo-historico/ejemplos}/general/04-analizador-de-gastos-csv/ejemplo_csv.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/04-analizador-de-gastos-csv/ejemplo_csv.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/general/05-generador-de-contrasenas/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/05-generador-de-contrasenas/ejemplo_secrets.go (100%) rename {ejemplos => archivo-historico/ejemplos}/general/05-generador-de-contrasenas/ejemplo_secrets.js (100%) rename {ejemplos => archivo-historico/ejemplos}/general/05-generador-de-contrasenas/ejemplo_secrets.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/05-generador-de-contrasenas/ejemplo_secrets.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/general/06-conversor-de-unidades/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/06-conversor-de-unidades/ejemplo_funciones.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/07-calculadora-de-imc/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/07-calculadora-de-imc/ejemplo_formateo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/08-organizador-de-archivos/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/08-organizador-de-archivos/ejemplo_pathlib.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/09-recordatorios-en-terminal/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/09-recordatorios-en-terminal/ejemplo_datetime.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/10-analizador-de-texto/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/10-analizador-de-texto/ejemplo_counter.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/11-decoradores/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/11-decoradores/ejemplo_decorador.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/12-regex/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/12-regex/ejemplo_regex.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/13-generadores/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/13-generadores/ejemplo_generadores.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/14-argparse/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/14-argparse/ejemplo_argparse.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/15-shutil/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/15-shutil/ejemplo_shutil.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/16-excepciones/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/16-excepciones/ejemplo_excepciones.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/17-json/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/17-json/ejemplo_json.py (100%) rename {ejemplos => archivo-historico/ejemplos}/general/18-poo-avanzada/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/general/18-poo-avanzada/ejemplo_propiedades.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/emisor.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/emisor.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/emisor.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/emisor.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/receptor.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/receptor.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/receptor.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/01-hola-nerve/receptor.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/publicador.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/publicador.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/publicador.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/publicador.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/suscriptor.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/suscriptor.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/suscriptor.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/02-pub-sub-basico/suscriptor.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/cliente.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/cliente.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/cliente.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/cliente.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/servidor.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/servidor.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/servidor.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/03-peticion-respuesta/servidor.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/04-vigilante-de-carpetas/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/04-vigilante-de-carpetas/ejemplo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/04-vigilante-de-carpetas/ejemplo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/04-vigilante-de-carpetas/ejemplo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/04-vigilante-de-carpetas/ejemplo.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/05-logger-distribuido/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/05-logger-distribuido/ejemplo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/05-logger-distribuido/ejemplo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/05-logger-distribuido/ejemplo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/05-logger-distribuido/ejemplo.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/06-puente-de-progreso-gif/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/06-puente-de-progreso-gif/ejemplo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/06-puente-de-progreso-gif/ejemplo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/06-puente-de-progreso-gif/ejemplo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/06-puente-de-progreso-gif/ejemplo.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/07-bot-discord-nerve/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/07-bot-discord-nerve/ejemplo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/07-bot-discord-nerve/ejemplo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/07-bot-discord-nerve/ejemplo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/07-bot-discord-nerve/ejemplo.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/08-puente-http/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/08-puente-http/ejemplo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/08-puente-http/ejemplo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/08-puente-http/ejemplo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/08-puente-http/ejemplo.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/09-probador-de-carga/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/09-probador-de-carga/ejemplo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/09-probador-de-carga/ejemplo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/09-probador-de-carga/ejemplo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/09-probador-de-carga/ejemplo.rs (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/10-cliente-nuevo-lenguaje/README.md (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/10-cliente-nuevo-lenguaje/ejemplo.go (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/10-cliente-nuevo-lenguaje/ejemplo.js (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/10-cliente-nuevo-lenguaje/ejemplo.py (100%) rename {ejemplos => archivo-historico/ejemplos}/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs (100%) rename {retos/general/02-web-scraper-simple/soluciones => archivo-historico/glosario}/.gitkeep (100%) rename {glosario => archivo-historico/glosario}/README.md (100%) rename {glosario => archivo-historico/glosario}/conceptos-software.md (100%) rename {glosario => archivo-historico/glosario}/git-github.md (100%) rename {glosario => archivo-historico/glosario}/go/01-fundamentos.md (100%) rename {glosario => archivo-historico/glosario}/go/02-control-de-flujo.md (100%) rename {glosario => archivo-historico/glosario}/go/03-funciones.md (100%) rename {glosario => archivo-historico/glosario}/go/04-estructuras-de-datos.md (100%) rename {glosario => archivo-historico/glosario}/go/05-modulos-y-librerias.md (100%) rename {glosario => archivo-historico/glosario}/go/06-manejo-de-errores.md (100%) rename {glosario => archivo-historico/glosario}/go/07-archivos-y-entrada-salida.md (100%) rename {glosario => archivo-historico/glosario}/go/08-poo-basica.md (100%) rename {glosario => archivo-historico/glosario}/go/09-concurrencia.md (100%) rename {glosario => archivo-historico/glosario}/go/README.md (100%) rename {glosario => archivo-historico/glosario}/javascript/01-fundamentos.md (100%) rename {glosario => archivo-historico/glosario}/javascript/02-control-de-flujo.md (100%) rename {glosario => archivo-historico/glosario}/javascript/03-funciones.md (100%) rename {glosario => archivo-historico/glosario}/javascript/04-estructuras-de-datos.md (100%) rename {glosario => archivo-historico/glosario}/javascript/05-modulos-y-librerias.md (100%) rename {glosario => archivo-historico/glosario}/javascript/06-manejo-de-errores.md (100%) rename {glosario => archivo-historico/glosario}/javascript/07-archivos-y-entrada-salida.md (100%) rename {glosario => archivo-historico/glosario}/javascript/08-poo-basica.md (100%) rename {glosario => archivo-historico/glosario}/javascript/09-asincronia.md (100%) rename {glosario => archivo-historico/glosario}/javascript/README.md (100%) rename {glosario => archivo-historico/glosario}/python/01-fundamentos.md (100%) rename {glosario => archivo-historico/glosario}/python/02-control-de-flujo.md (100%) rename {glosario => archivo-historico/glosario}/python/03-funciones.md (100%) rename {glosario => archivo-historico/glosario}/python/04-estructuras-de-datos.md (100%) rename {glosario => archivo-historico/glosario}/python/05-modulos-y-librerias.md (100%) rename {glosario => archivo-historico/glosario}/python/06-manejo-de-errores.md (100%) rename {glosario => archivo-historico/glosario}/python/07-archivos-y-entrada-salida.md (100%) rename {glosario => archivo-historico/glosario}/python/08-poo-basica.md (100%) rename {glosario => archivo-historico/glosario}/python/README.md (100%) rename {glosario => archivo-historico/glosario}/redes-sockets.md (100%) rename {glosario => archivo-historico/glosario}/rust/01-fundamentos.md (100%) rename {glosario => archivo-historico/glosario}/rust/02-control-de-flujo.md (100%) rename {glosario => archivo-historico/glosario}/rust/03-funciones.md (100%) rename {glosario => archivo-historico/glosario}/rust/04-estructuras-de-datos.md (100%) rename {glosario => archivo-historico/glosario}/rust/05-modulos-y-librerias.md (100%) rename {glosario => archivo-historico/glosario}/rust/06-manejo-de-errores.md (100%) rename {glosario => archivo-historico/glosario}/rust/07-archivos-y-entrada-salida.md (100%) rename {glosario => archivo-historico/glosario}/rust/08-poo-basica.md (100%) rename {glosario => archivo-historico/glosario}/rust/README.md (100%) rename {glosario => archivo-historico/glosario}/terminal-linux.md (100%) rename {glosario => archivo-historico/glosario}/terminal-windows.md (100%) rename {retos/general/03-juego-de-adivinanza/soluciones => archivo-historico/retos}/.gitkeep (100%) rename {retos => archivo-historico/retos}/00-rompehielo/CesarAAR.py (100%) rename {retos => archivo-historico/retos}/00-rompehielo/README.md (100%) rename {retos => archivo-historico/retos}/00-rompehielo/Vi-dot1.cpp (100%) rename {retos => archivo-historico/retos}/00-rompehielo/ashu.py (100%) rename {retos => archivo-historico/retos}/00-rompehielo/tacos.py (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/README.md (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md (100%) rename {retos => archivo-historico/retos}/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py (100%) rename {retos => archivo-historico/retos}/general/02-web-scraper-simple/README.md (100%) rename {retos/general/04-analizador-de-gastos-csv => archivo-historico/retos/general/02-web-scraper-simple}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/03-juego-de-adivinanza/README.md (100%) rename {retos/general/05-generador-de-contrasenas => archivo-historico/retos/general/03-juego-de-adivinanza}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/04-analizador-de-gastos-csv/README.md (100%) rename {retos/general/06-conversor-de-unidades => archivo-historico/retos/general/04-analizador-de-gastos-csv}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/05-generador-de-contrasenas/README.md (100%) rename {retos/general/07-calculadora-de-imc => archivo-historico/retos/general/05-generador-de-contrasenas}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/06-conversor-de-unidades/README.md (100%) rename {retos/general/08-organizador-de-archivos => archivo-historico/retos/general/06-conversor-de-unidades}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/07-calculadora-de-imc/README.md (100%) rename {retos/general/09-recordatorios-en-terminal => archivo-historico/retos/general/07-calculadora-de-imc}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py (100%) rename {retos => archivo-historico/retos}/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md (100%) rename {retos => archivo-historico/retos}/general/08-organizador-de-archivos/README.md (100%) rename {retos/general/10-analizador-de-texto => archivo-historico/retos/general/08-organizador-de-archivos}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/09-recordatorios-en-terminal/README.md (100%) rename {retos/general/11-profiler-de-funciones => archivo-historico/retos/general/09-recordatorios-en-terminal}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/10-analizador-de-texto/README.md (100%) rename {retos/general/12-validador-de-emails => archivo-historico/retos/general/10-analizador-de-texto}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/11-profiler-de-funciones/README.md (100%) rename {retos/general/13-lector-archivos-gigantes => archivo-historico/retos/general/11-profiler-de-funciones}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/12-validador-de-emails/README.md (100%) rename {retos/general/14-cli-del-clima => archivo-historico/retos/general/12-validador-de-emails}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/13-lector-archivos-gigantes/README.md (100%) rename {retos/general/15-herramienta-de-backup => archivo-historico/retos/general/13-lector-archivos-gigantes}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/14-cli-del-clima/README.md (100%) rename {retos/general/16-validador-de-edad => archivo-historico/retos/general/14-cli-del-clima}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/15-herramienta-de-backup/README.md (100%) rename {retos/general/17-lector-de-configuraciones => archivo-historico/retos/general/15-herramienta-de-backup}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/16-validador-de-edad/README.md (100%) rename {retos/general/18-inventario-de-tienda => archivo-historico/retos/general/16-validador-de-edad}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/17-lector-de-configuraciones/README.md (100%) rename {retos/nerve/01-chat-terminal => archivo-historico/retos/general/17-lector-de-configuraciones/soluciones}/.gitkeep (100%) rename {retos => archivo-historico/retos}/general/18-inventario-de-tienda/README.md (100%) rename {retos/nerve/01-chat-terminal => archivo-historico/retos/general/18-inventario-de-tienda}/soluciones/.gitkeep (100%) rename {retos/nerve/02-reloj-sincronizado => archivo-historico/retos/nerve/01-chat-terminal}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/01-chat-terminal/README.md (100%) rename {retos/nerve/02-reloj-sincronizado => archivo-historico/retos/nerve/01-chat-terminal}/soluciones/.gitkeep (100%) rename {retos/nerve/03-traductor-de-mensajes => archivo-historico/retos/nerve/02-reloj-sincronizado}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/02-reloj-sincronizado/README.md (100%) rename {retos/nerve/03-traductor-de-mensajes => archivo-historico/retos/nerve/02-reloj-sincronizado}/soluciones/.gitkeep (100%) rename {retos/nerve/04-vigilante-de-carpetas => archivo-historico/retos/nerve/03-traductor-de-mensajes}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/03-traductor-de-mensajes/README.md (100%) rename {retos/nerve/04-vigilante-de-carpetas => archivo-historico/retos/nerve/03-traductor-de-mensajes}/soluciones/.gitkeep (100%) rename {retos/nerve/05-logger-distribuido => archivo-historico/retos/nerve/04-vigilante-de-carpetas}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/04-vigilante-de-carpetas/README.md (100%) rename {retos/nerve/05-logger-distribuido => archivo-historico/retos/nerve/04-vigilante-de-carpetas}/soluciones/.gitkeep (100%) rename {retos/nerve/06-puente-de-progreso-gif => archivo-historico/retos/nerve/05-logger-distribuido}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/README.md (100%) rename {retos/nerve/06-puente-de-progreso-gif => archivo-historico/retos/nerve/05-logger-distribuido}/soluciones/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs (100%) rename {retos => archivo-historico/retos}/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs (100%) rename {retos/nerve/07-bot-discord-nerve => archivo-historico/retos/nerve/06-puente-de-progreso-gif}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/06-puente-de-progreso-gif/README.md (100%) rename {retos/nerve/07-bot-discord-nerve => archivo-historico/retos/nerve/06-puente-de-progreso-gif}/soluciones/.gitkeep (100%) rename {retos/nerve/08-puente-http => archivo-historico/retos/nerve/07-bot-discord-nerve}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/07-bot-discord-nerve/README.md (100%) rename {retos/nerve/08-puente-http => archivo-historico/retos/nerve/07-bot-discord-nerve}/soluciones/.gitkeep (100%) rename {retos/nerve/09-probador-de-carga => archivo-historico/retos/nerve/08-puente-http}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/08-puente-http/README.md (100%) rename {retos/nerve/09-probador-de-carga => archivo-historico/retos/nerve/08-puente-http}/soluciones/.gitkeep (100%) rename {retos/nerve/10-cliente-nuevo-lenguaje => archivo-historico/retos/nerve/09-probador-de-carga}/.gitkeep (100%) rename {retos => archivo-historico/retos}/nerve/09-probador-de-carga/README.md (100%) rename {retos/nerve/10-cliente-nuevo-lenguaje => archivo-historico/retos/nerve/09-probador-de-carga}/soluciones/.gitkeep (100%) create mode 100644 archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep rename {retos => archivo-historico/retos}/nerve/10-cliente-nuevo-lenguaje/README.md (100%) create mode 100644 archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep create mode 100644 go/.gitkeep create mode 100644 javascript/.gitkeep create mode 100644 python/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/02-variables/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/04-operadores/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/05-condicionales/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/.gitkeep create mode 100644 python/Modulo-02-Estructuras/.gitkeep create mode 100644 python/Modulo-03-Exterior/.gitkeep create mode 100644 python/Modulo-04-Nerve/.gitkeep create mode 100644 rust/.gitkeep diff --git a/ejemplos/.gitkeep b/00-rompehielo/.gitkeep similarity index 100% rename from ejemplos/.gitkeep rename to 00-rompehielo/.gitkeep diff --git a/glosario/.gitkeep b/alenia-nerve/.gitkeep similarity index 100% rename from glosario/.gitkeep rename to alenia-nerve/.gitkeep diff --git a/retos/.gitkeep b/archivo-historico/ejemplos/.gitkeep similarity index 100% rename from retos/.gitkeep rename to archivo-historico/ejemplos/.gitkeep diff --git a/ejemplos/general/01-lista-de-tareas-cli/README.md b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/README.md similarity index 100% rename from ejemplos/general/01-lista-de-tareas-cli/README.md rename to archivo-historico/ejemplos/general/01-lista-de-tareas-cli/README.md diff --git a/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go similarity index 100% rename from ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go rename to archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go diff --git a/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js similarity index 100% rename from ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js rename to archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js diff --git a/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py similarity index 100% rename from ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py rename to archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py diff --git a/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs similarity index 100% rename from ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs rename to archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs diff --git a/ejemplos/general/02-web-scraper-simple/README.md b/archivo-historico/ejemplos/general/02-web-scraper-simple/README.md similarity index 100% rename from ejemplos/general/02-web-scraper-simple/README.md rename to archivo-historico/ejemplos/general/02-web-scraper-simple/README.md diff --git a/ejemplos/general/02-web-scraper-simple/ejemplo_requests.go b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.go similarity index 100% rename from ejemplos/general/02-web-scraper-simple/ejemplo_requests.go rename to archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.go diff --git a/ejemplos/general/02-web-scraper-simple/ejemplo_requests.js b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.js similarity index 100% rename from ejemplos/general/02-web-scraper-simple/ejemplo_requests.js rename to archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.js diff --git a/ejemplos/general/02-web-scraper-simple/ejemplo_requests.py b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.py similarity index 100% rename from ejemplos/general/02-web-scraper-simple/ejemplo_requests.py rename to archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.py diff --git a/ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs similarity index 100% rename from ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs rename to archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs diff --git a/ejemplos/general/03-juego-de-adivinanza/README.md b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/README.md similarity index 100% rename from ejemplos/general/03-juego-de-adivinanza/README.md rename to archivo-historico/ejemplos/general/03-juego-de-adivinanza/README.md diff --git a/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go similarity index 100% rename from ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go rename to archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go diff --git a/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js similarity index 100% rename from ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js rename to archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js diff --git a/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py similarity index 100% rename from ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py rename to archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py diff --git a/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs similarity index 100% rename from ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs rename to archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs diff --git a/ejemplos/general/04-analizador-de-gastos-csv/README.md b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/README.md similarity index 100% rename from ejemplos/general/04-analizador-de-gastos-csv/README.md rename to archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/README.md diff --git a/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go similarity index 100% rename from ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go rename to archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go diff --git a/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js similarity index 100% rename from ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js rename to archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js diff --git a/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py similarity index 100% rename from ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py rename to archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py diff --git a/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs similarity index 100% rename from ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs rename to archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs diff --git a/ejemplos/general/05-generador-de-contrasenas/README.md b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/README.md similarity index 100% rename from ejemplos/general/05-generador-de-contrasenas/README.md rename to archivo-historico/ejemplos/general/05-generador-de-contrasenas/README.md diff --git a/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go similarity index 100% rename from ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go rename to archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go diff --git a/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js similarity index 100% rename from ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js rename to archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js diff --git a/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py similarity index 100% rename from ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py rename to archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py diff --git a/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs similarity index 100% rename from ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs rename to archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs diff --git a/ejemplos/general/06-conversor-de-unidades/README.md b/archivo-historico/ejemplos/general/06-conversor-de-unidades/README.md similarity index 100% rename from ejemplos/general/06-conversor-de-unidades/README.md rename to archivo-historico/ejemplos/general/06-conversor-de-unidades/README.md diff --git a/ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py b/archivo-historico/ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py similarity index 100% rename from ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py rename to archivo-historico/ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py diff --git a/ejemplos/general/07-calculadora-de-imc/README.md b/archivo-historico/ejemplos/general/07-calculadora-de-imc/README.md similarity index 100% rename from ejemplos/general/07-calculadora-de-imc/README.md rename to archivo-historico/ejemplos/general/07-calculadora-de-imc/README.md diff --git a/ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py b/archivo-historico/ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py similarity index 100% rename from ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py rename to archivo-historico/ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py diff --git a/ejemplos/general/08-organizador-de-archivos/README.md b/archivo-historico/ejemplos/general/08-organizador-de-archivos/README.md similarity index 100% rename from ejemplos/general/08-organizador-de-archivos/README.md rename to archivo-historico/ejemplos/general/08-organizador-de-archivos/README.md diff --git a/ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py b/archivo-historico/ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py similarity index 100% rename from ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py rename to archivo-historico/ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py diff --git a/ejemplos/general/09-recordatorios-en-terminal/README.md b/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/README.md similarity index 100% rename from ejemplos/general/09-recordatorios-en-terminal/README.md rename to archivo-historico/ejemplos/general/09-recordatorios-en-terminal/README.md diff --git a/ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py b/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py similarity index 100% rename from ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py rename to archivo-historico/ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py diff --git a/ejemplos/general/10-analizador-de-texto/README.md b/archivo-historico/ejemplos/general/10-analizador-de-texto/README.md similarity index 100% rename from ejemplos/general/10-analizador-de-texto/README.md rename to archivo-historico/ejemplos/general/10-analizador-de-texto/README.md diff --git a/ejemplos/general/10-analizador-de-texto/ejemplo_counter.py b/archivo-historico/ejemplos/general/10-analizador-de-texto/ejemplo_counter.py similarity index 100% rename from ejemplos/general/10-analizador-de-texto/ejemplo_counter.py rename to archivo-historico/ejemplos/general/10-analizador-de-texto/ejemplo_counter.py diff --git a/ejemplos/general/11-decoradores/README.md b/archivo-historico/ejemplos/general/11-decoradores/README.md similarity index 100% rename from ejemplos/general/11-decoradores/README.md rename to archivo-historico/ejemplos/general/11-decoradores/README.md diff --git a/ejemplos/general/11-decoradores/ejemplo_decorador.py b/archivo-historico/ejemplos/general/11-decoradores/ejemplo_decorador.py similarity index 100% rename from ejemplos/general/11-decoradores/ejemplo_decorador.py rename to archivo-historico/ejemplos/general/11-decoradores/ejemplo_decorador.py diff --git a/ejemplos/general/12-regex/README.md b/archivo-historico/ejemplos/general/12-regex/README.md similarity index 100% rename from ejemplos/general/12-regex/README.md rename to archivo-historico/ejemplos/general/12-regex/README.md diff --git a/ejemplos/general/12-regex/ejemplo_regex.py b/archivo-historico/ejemplos/general/12-regex/ejemplo_regex.py similarity index 100% rename from ejemplos/general/12-regex/ejemplo_regex.py rename to archivo-historico/ejemplos/general/12-regex/ejemplo_regex.py diff --git a/ejemplos/general/13-generadores/README.md b/archivo-historico/ejemplos/general/13-generadores/README.md similarity index 100% rename from ejemplos/general/13-generadores/README.md rename to archivo-historico/ejemplos/general/13-generadores/README.md diff --git a/ejemplos/general/13-generadores/ejemplo_generadores.py b/archivo-historico/ejemplos/general/13-generadores/ejemplo_generadores.py similarity index 100% rename from ejemplos/general/13-generadores/ejemplo_generadores.py rename to archivo-historico/ejemplos/general/13-generadores/ejemplo_generadores.py diff --git a/ejemplos/general/14-argparse/README.md b/archivo-historico/ejemplos/general/14-argparse/README.md similarity index 100% rename from ejemplos/general/14-argparse/README.md rename to archivo-historico/ejemplos/general/14-argparse/README.md diff --git a/ejemplos/general/14-argparse/ejemplo_argparse.py b/archivo-historico/ejemplos/general/14-argparse/ejemplo_argparse.py similarity index 100% rename from ejemplos/general/14-argparse/ejemplo_argparse.py rename to archivo-historico/ejemplos/general/14-argparse/ejemplo_argparse.py diff --git a/ejemplos/general/15-shutil/README.md b/archivo-historico/ejemplos/general/15-shutil/README.md similarity index 100% rename from ejemplos/general/15-shutil/README.md rename to archivo-historico/ejemplos/general/15-shutil/README.md diff --git a/ejemplos/general/15-shutil/ejemplo_shutil.py b/archivo-historico/ejemplos/general/15-shutil/ejemplo_shutil.py similarity index 100% rename from ejemplos/general/15-shutil/ejemplo_shutil.py rename to archivo-historico/ejemplos/general/15-shutil/ejemplo_shutil.py diff --git a/ejemplos/general/16-excepciones/README.md b/archivo-historico/ejemplos/general/16-excepciones/README.md similarity index 100% rename from ejemplos/general/16-excepciones/README.md rename to archivo-historico/ejemplos/general/16-excepciones/README.md diff --git a/ejemplos/general/16-excepciones/ejemplo_excepciones.py b/archivo-historico/ejemplos/general/16-excepciones/ejemplo_excepciones.py similarity index 100% rename from ejemplos/general/16-excepciones/ejemplo_excepciones.py rename to archivo-historico/ejemplos/general/16-excepciones/ejemplo_excepciones.py diff --git a/ejemplos/general/17-json/README.md b/archivo-historico/ejemplos/general/17-json/README.md similarity index 100% rename from ejemplos/general/17-json/README.md rename to archivo-historico/ejemplos/general/17-json/README.md diff --git a/ejemplos/general/17-json/ejemplo_json.py b/archivo-historico/ejemplos/general/17-json/ejemplo_json.py similarity index 100% rename from ejemplos/general/17-json/ejemplo_json.py rename to archivo-historico/ejemplos/general/17-json/ejemplo_json.py diff --git a/ejemplos/general/18-poo-avanzada/README.md b/archivo-historico/ejemplos/general/18-poo-avanzada/README.md similarity index 100% rename from ejemplos/general/18-poo-avanzada/README.md rename to archivo-historico/ejemplos/general/18-poo-avanzada/README.md diff --git a/ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py b/archivo-historico/ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py similarity index 100% rename from ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py rename to archivo-historico/ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py diff --git a/ejemplos/nerve/01-hola-nerve/README.md b/archivo-historico/ejemplos/nerve/01-hola-nerve/README.md similarity index 100% rename from ejemplos/nerve/01-hola-nerve/README.md rename to archivo-historico/ejemplos/nerve/01-hola-nerve/README.md diff --git a/ejemplos/nerve/01-hola-nerve/emisor.go b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.go similarity index 100% rename from ejemplos/nerve/01-hola-nerve/emisor.go rename to archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.go diff --git a/ejemplos/nerve/01-hola-nerve/emisor.js b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.js similarity index 100% rename from ejemplos/nerve/01-hola-nerve/emisor.js rename to archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.js diff --git a/ejemplos/nerve/01-hola-nerve/emisor.py b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.py similarity index 100% rename from ejemplos/nerve/01-hola-nerve/emisor.py rename to archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.py diff --git a/ejemplos/nerve/01-hola-nerve/emisor.rs b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.rs similarity index 100% rename from ejemplos/nerve/01-hola-nerve/emisor.rs rename to archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.rs diff --git a/ejemplos/nerve/01-hola-nerve/receptor.go b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.go similarity index 100% rename from ejemplos/nerve/01-hola-nerve/receptor.go rename to archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.go diff --git a/ejemplos/nerve/01-hola-nerve/receptor.js b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.js similarity index 100% rename from ejemplos/nerve/01-hola-nerve/receptor.js rename to archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.js diff --git a/ejemplos/nerve/01-hola-nerve/receptor.py b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.py similarity index 100% rename from ejemplos/nerve/01-hola-nerve/receptor.py rename to archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.py diff --git a/ejemplos/nerve/01-hola-nerve/receptor.rs b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.rs similarity index 100% rename from ejemplos/nerve/01-hola-nerve/receptor.rs rename to archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.rs diff --git a/ejemplos/nerve/02-pub-sub-basico/README.md b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/README.md similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/README.md rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/README.md diff --git a/ejemplos/nerve/02-pub-sub-basico/publicador.go b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.go similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/publicador.go rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.go diff --git a/ejemplos/nerve/02-pub-sub-basico/publicador.js b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.js similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/publicador.js rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.js diff --git a/ejemplos/nerve/02-pub-sub-basico/publicador.py b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.py similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/publicador.py rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.py diff --git a/ejemplos/nerve/02-pub-sub-basico/publicador.rs b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.rs similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/publicador.rs rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.rs diff --git a/ejemplos/nerve/02-pub-sub-basico/suscriptor.go b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.go similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/suscriptor.go rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.go diff --git a/ejemplos/nerve/02-pub-sub-basico/suscriptor.js b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.js similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/suscriptor.js rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.js diff --git a/ejemplos/nerve/02-pub-sub-basico/suscriptor.py b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.py similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/suscriptor.py rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.py diff --git a/ejemplos/nerve/02-pub-sub-basico/suscriptor.rs b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.rs similarity index 100% rename from ejemplos/nerve/02-pub-sub-basico/suscriptor.rs rename to archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.rs diff --git a/ejemplos/nerve/03-peticion-respuesta/README.md b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/README.md similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/README.md rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/README.md diff --git a/ejemplos/nerve/03-peticion-respuesta/cliente.go b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.go similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/cliente.go rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.go diff --git a/ejemplos/nerve/03-peticion-respuesta/cliente.js b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.js similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/cliente.js rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.js diff --git a/ejemplos/nerve/03-peticion-respuesta/cliente.py b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.py similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/cliente.py rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.py diff --git a/ejemplos/nerve/03-peticion-respuesta/cliente.rs b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.rs similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/cliente.rs rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.rs diff --git a/ejemplos/nerve/03-peticion-respuesta/servidor.go b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.go similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/servidor.go rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.go diff --git a/ejemplos/nerve/03-peticion-respuesta/servidor.js b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.js similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/servidor.js rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.js diff --git a/ejemplos/nerve/03-peticion-respuesta/servidor.py b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.py similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/servidor.py rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.py diff --git a/ejemplos/nerve/03-peticion-respuesta/servidor.rs b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.rs similarity index 100% rename from ejemplos/nerve/03-peticion-respuesta/servidor.rs rename to archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.rs diff --git a/ejemplos/nerve/04-vigilante-de-carpetas/README.md b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/README.md similarity index 100% rename from ejemplos/nerve/04-vigilante-de-carpetas/README.md rename to archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/README.md diff --git a/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go similarity index 100% rename from ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go rename to archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go diff --git a/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js similarity index 100% rename from ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js rename to archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js diff --git a/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py similarity index 100% rename from ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py rename to archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py diff --git a/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs similarity index 100% rename from ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs rename to archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs diff --git a/ejemplos/nerve/05-logger-distribuido/README.md b/archivo-historico/ejemplos/nerve/05-logger-distribuido/README.md similarity index 100% rename from ejemplos/nerve/05-logger-distribuido/README.md rename to archivo-historico/ejemplos/nerve/05-logger-distribuido/README.md diff --git a/ejemplos/nerve/05-logger-distribuido/ejemplo.go b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.go similarity index 100% rename from ejemplos/nerve/05-logger-distribuido/ejemplo.go rename to archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.go diff --git a/ejemplos/nerve/05-logger-distribuido/ejemplo.js b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.js similarity index 100% rename from ejemplos/nerve/05-logger-distribuido/ejemplo.js rename to archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.js diff --git a/ejemplos/nerve/05-logger-distribuido/ejemplo.py b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.py similarity index 100% rename from ejemplos/nerve/05-logger-distribuido/ejemplo.py rename to archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.py diff --git a/ejemplos/nerve/05-logger-distribuido/ejemplo.rs b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.rs similarity index 100% rename from ejemplos/nerve/05-logger-distribuido/ejemplo.rs rename to archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.rs diff --git a/ejemplos/nerve/06-puente-de-progreso-gif/README.md b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/README.md similarity index 100% rename from ejemplos/nerve/06-puente-de-progreso-gif/README.md rename to archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/README.md diff --git a/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go similarity index 100% rename from ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go rename to archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go diff --git a/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js similarity index 100% rename from ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js rename to archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js diff --git a/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py similarity index 100% rename from ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py rename to archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py diff --git a/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs similarity index 100% rename from ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs rename to archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs diff --git a/ejemplos/nerve/07-bot-discord-nerve/README.md b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/README.md similarity index 100% rename from ejemplos/nerve/07-bot-discord-nerve/README.md rename to archivo-historico/ejemplos/nerve/07-bot-discord-nerve/README.md diff --git a/ejemplos/nerve/07-bot-discord-nerve/ejemplo.go b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.go similarity index 100% rename from ejemplos/nerve/07-bot-discord-nerve/ejemplo.go rename to archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.go diff --git a/ejemplos/nerve/07-bot-discord-nerve/ejemplo.js b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.js similarity index 100% rename from ejemplos/nerve/07-bot-discord-nerve/ejemplo.js rename to archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.js diff --git a/ejemplos/nerve/07-bot-discord-nerve/ejemplo.py b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.py similarity index 100% rename from ejemplos/nerve/07-bot-discord-nerve/ejemplo.py rename to archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.py diff --git a/ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs similarity index 100% rename from ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs rename to archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs diff --git a/ejemplos/nerve/08-puente-http/README.md b/archivo-historico/ejemplos/nerve/08-puente-http/README.md similarity index 100% rename from ejemplos/nerve/08-puente-http/README.md rename to archivo-historico/ejemplos/nerve/08-puente-http/README.md diff --git a/ejemplos/nerve/08-puente-http/ejemplo.go b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.go similarity index 100% rename from ejemplos/nerve/08-puente-http/ejemplo.go rename to archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.go diff --git a/ejemplos/nerve/08-puente-http/ejemplo.js b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.js similarity index 100% rename from ejemplos/nerve/08-puente-http/ejemplo.js rename to archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.js diff --git a/ejemplos/nerve/08-puente-http/ejemplo.py b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.py similarity index 100% rename from ejemplos/nerve/08-puente-http/ejemplo.py rename to archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.py diff --git a/ejemplos/nerve/08-puente-http/ejemplo.rs b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.rs similarity index 100% rename from ejemplos/nerve/08-puente-http/ejemplo.rs rename to archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.rs diff --git a/ejemplos/nerve/09-probador-de-carga/README.md b/archivo-historico/ejemplos/nerve/09-probador-de-carga/README.md similarity index 100% rename from ejemplos/nerve/09-probador-de-carga/README.md rename to archivo-historico/ejemplos/nerve/09-probador-de-carga/README.md diff --git a/ejemplos/nerve/09-probador-de-carga/ejemplo.go b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.go similarity index 100% rename from ejemplos/nerve/09-probador-de-carga/ejemplo.go rename to archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.go diff --git a/ejemplos/nerve/09-probador-de-carga/ejemplo.js b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.js similarity index 100% rename from ejemplos/nerve/09-probador-de-carga/ejemplo.js rename to archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.js diff --git a/ejemplos/nerve/09-probador-de-carga/ejemplo.py b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.py similarity index 100% rename from ejemplos/nerve/09-probador-de-carga/ejemplo.py rename to archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.py diff --git a/ejemplos/nerve/09-probador-de-carga/ejemplo.rs b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.rs similarity index 100% rename from ejemplos/nerve/09-probador-de-carga/ejemplo.rs rename to archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.rs diff --git a/ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md similarity index 100% rename from ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md rename to archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md diff --git a/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go similarity index 100% rename from ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go rename to archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go diff --git a/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js similarity index 100% rename from ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js rename to archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js diff --git a/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py similarity index 100% rename from ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py rename to archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py diff --git a/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs similarity index 100% rename from ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs rename to archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs diff --git a/retos/general/02-web-scraper-simple/soluciones/.gitkeep b/archivo-historico/glosario/.gitkeep similarity index 100% rename from retos/general/02-web-scraper-simple/soluciones/.gitkeep rename to archivo-historico/glosario/.gitkeep diff --git a/glosario/README.md b/archivo-historico/glosario/README.md similarity index 100% rename from glosario/README.md rename to archivo-historico/glosario/README.md diff --git a/glosario/conceptos-software.md b/archivo-historico/glosario/conceptos-software.md similarity index 100% rename from glosario/conceptos-software.md rename to archivo-historico/glosario/conceptos-software.md diff --git a/glosario/git-github.md b/archivo-historico/glosario/git-github.md similarity index 100% rename from glosario/git-github.md rename to archivo-historico/glosario/git-github.md diff --git a/glosario/go/01-fundamentos.md b/archivo-historico/glosario/go/01-fundamentos.md similarity index 100% rename from glosario/go/01-fundamentos.md rename to archivo-historico/glosario/go/01-fundamentos.md diff --git a/glosario/go/02-control-de-flujo.md b/archivo-historico/glosario/go/02-control-de-flujo.md similarity index 100% rename from glosario/go/02-control-de-flujo.md rename to archivo-historico/glosario/go/02-control-de-flujo.md diff --git a/glosario/go/03-funciones.md b/archivo-historico/glosario/go/03-funciones.md similarity index 100% rename from glosario/go/03-funciones.md rename to archivo-historico/glosario/go/03-funciones.md diff --git a/glosario/go/04-estructuras-de-datos.md b/archivo-historico/glosario/go/04-estructuras-de-datos.md similarity index 100% rename from glosario/go/04-estructuras-de-datos.md rename to archivo-historico/glosario/go/04-estructuras-de-datos.md diff --git a/glosario/go/05-modulos-y-librerias.md b/archivo-historico/glosario/go/05-modulos-y-librerias.md similarity index 100% rename from glosario/go/05-modulos-y-librerias.md rename to archivo-historico/glosario/go/05-modulos-y-librerias.md diff --git a/glosario/go/06-manejo-de-errores.md b/archivo-historico/glosario/go/06-manejo-de-errores.md similarity index 100% rename from glosario/go/06-manejo-de-errores.md rename to archivo-historico/glosario/go/06-manejo-de-errores.md diff --git a/glosario/go/07-archivos-y-entrada-salida.md b/archivo-historico/glosario/go/07-archivos-y-entrada-salida.md similarity index 100% rename from glosario/go/07-archivos-y-entrada-salida.md rename to archivo-historico/glosario/go/07-archivos-y-entrada-salida.md diff --git a/glosario/go/08-poo-basica.md b/archivo-historico/glosario/go/08-poo-basica.md similarity index 100% rename from glosario/go/08-poo-basica.md rename to archivo-historico/glosario/go/08-poo-basica.md diff --git a/glosario/go/09-concurrencia.md b/archivo-historico/glosario/go/09-concurrencia.md similarity index 100% rename from glosario/go/09-concurrencia.md rename to archivo-historico/glosario/go/09-concurrencia.md diff --git a/glosario/go/README.md b/archivo-historico/glosario/go/README.md similarity index 100% rename from glosario/go/README.md rename to archivo-historico/glosario/go/README.md diff --git a/glosario/javascript/01-fundamentos.md b/archivo-historico/glosario/javascript/01-fundamentos.md similarity index 100% rename from glosario/javascript/01-fundamentos.md rename to archivo-historico/glosario/javascript/01-fundamentos.md diff --git a/glosario/javascript/02-control-de-flujo.md b/archivo-historico/glosario/javascript/02-control-de-flujo.md similarity index 100% rename from glosario/javascript/02-control-de-flujo.md rename to archivo-historico/glosario/javascript/02-control-de-flujo.md diff --git a/glosario/javascript/03-funciones.md b/archivo-historico/glosario/javascript/03-funciones.md similarity index 100% rename from glosario/javascript/03-funciones.md rename to archivo-historico/glosario/javascript/03-funciones.md diff --git a/glosario/javascript/04-estructuras-de-datos.md b/archivo-historico/glosario/javascript/04-estructuras-de-datos.md similarity index 100% rename from glosario/javascript/04-estructuras-de-datos.md rename to archivo-historico/glosario/javascript/04-estructuras-de-datos.md diff --git a/glosario/javascript/05-modulos-y-librerias.md b/archivo-historico/glosario/javascript/05-modulos-y-librerias.md similarity index 100% rename from glosario/javascript/05-modulos-y-librerias.md rename to archivo-historico/glosario/javascript/05-modulos-y-librerias.md diff --git a/glosario/javascript/06-manejo-de-errores.md b/archivo-historico/glosario/javascript/06-manejo-de-errores.md similarity index 100% rename from glosario/javascript/06-manejo-de-errores.md rename to archivo-historico/glosario/javascript/06-manejo-de-errores.md diff --git a/glosario/javascript/07-archivos-y-entrada-salida.md b/archivo-historico/glosario/javascript/07-archivos-y-entrada-salida.md similarity index 100% rename from glosario/javascript/07-archivos-y-entrada-salida.md rename to archivo-historico/glosario/javascript/07-archivos-y-entrada-salida.md diff --git a/glosario/javascript/08-poo-basica.md b/archivo-historico/glosario/javascript/08-poo-basica.md similarity index 100% rename from glosario/javascript/08-poo-basica.md rename to archivo-historico/glosario/javascript/08-poo-basica.md diff --git a/glosario/javascript/09-asincronia.md b/archivo-historico/glosario/javascript/09-asincronia.md similarity index 100% rename from glosario/javascript/09-asincronia.md rename to archivo-historico/glosario/javascript/09-asincronia.md diff --git a/glosario/javascript/README.md b/archivo-historico/glosario/javascript/README.md similarity index 100% rename from glosario/javascript/README.md rename to archivo-historico/glosario/javascript/README.md diff --git a/glosario/python/01-fundamentos.md b/archivo-historico/glosario/python/01-fundamentos.md similarity index 100% rename from glosario/python/01-fundamentos.md rename to archivo-historico/glosario/python/01-fundamentos.md diff --git a/glosario/python/02-control-de-flujo.md b/archivo-historico/glosario/python/02-control-de-flujo.md similarity index 100% rename from glosario/python/02-control-de-flujo.md rename to archivo-historico/glosario/python/02-control-de-flujo.md diff --git a/glosario/python/03-funciones.md b/archivo-historico/glosario/python/03-funciones.md similarity index 100% rename from glosario/python/03-funciones.md rename to archivo-historico/glosario/python/03-funciones.md diff --git a/glosario/python/04-estructuras-de-datos.md b/archivo-historico/glosario/python/04-estructuras-de-datos.md similarity index 100% rename from glosario/python/04-estructuras-de-datos.md rename to archivo-historico/glosario/python/04-estructuras-de-datos.md diff --git a/glosario/python/05-modulos-y-librerias.md b/archivo-historico/glosario/python/05-modulos-y-librerias.md similarity index 100% rename from glosario/python/05-modulos-y-librerias.md rename to archivo-historico/glosario/python/05-modulos-y-librerias.md diff --git a/glosario/python/06-manejo-de-errores.md b/archivo-historico/glosario/python/06-manejo-de-errores.md similarity index 100% rename from glosario/python/06-manejo-de-errores.md rename to archivo-historico/glosario/python/06-manejo-de-errores.md diff --git a/glosario/python/07-archivos-y-entrada-salida.md b/archivo-historico/glosario/python/07-archivos-y-entrada-salida.md similarity index 100% rename from glosario/python/07-archivos-y-entrada-salida.md rename to archivo-historico/glosario/python/07-archivos-y-entrada-salida.md diff --git a/glosario/python/08-poo-basica.md b/archivo-historico/glosario/python/08-poo-basica.md similarity index 100% rename from glosario/python/08-poo-basica.md rename to archivo-historico/glosario/python/08-poo-basica.md diff --git a/glosario/python/README.md b/archivo-historico/glosario/python/README.md similarity index 100% rename from glosario/python/README.md rename to archivo-historico/glosario/python/README.md diff --git a/glosario/redes-sockets.md b/archivo-historico/glosario/redes-sockets.md similarity index 100% rename from glosario/redes-sockets.md rename to archivo-historico/glosario/redes-sockets.md diff --git a/glosario/rust/01-fundamentos.md b/archivo-historico/glosario/rust/01-fundamentos.md similarity index 100% rename from glosario/rust/01-fundamentos.md rename to archivo-historico/glosario/rust/01-fundamentos.md diff --git a/glosario/rust/02-control-de-flujo.md b/archivo-historico/glosario/rust/02-control-de-flujo.md similarity index 100% rename from glosario/rust/02-control-de-flujo.md rename to archivo-historico/glosario/rust/02-control-de-flujo.md diff --git a/glosario/rust/03-funciones.md b/archivo-historico/glosario/rust/03-funciones.md similarity index 100% rename from glosario/rust/03-funciones.md rename to archivo-historico/glosario/rust/03-funciones.md diff --git a/glosario/rust/04-estructuras-de-datos.md b/archivo-historico/glosario/rust/04-estructuras-de-datos.md similarity index 100% rename from glosario/rust/04-estructuras-de-datos.md rename to archivo-historico/glosario/rust/04-estructuras-de-datos.md diff --git a/glosario/rust/05-modulos-y-librerias.md b/archivo-historico/glosario/rust/05-modulos-y-librerias.md similarity index 100% rename from glosario/rust/05-modulos-y-librerias.md rename to archivo-historico/glosario/rust/05-modulos-y-librerias.md diff --git a/glosario/rust/06-manejo-de-errores.md b/archivo-historico/glosario/rust/06-manejo-de-errores.md similarity index 100% rename from glosario/rust/06-manejo-de-errores.md rename to archivo-historico/glosario/rust/06-manejo-de-errores.md diff --git a/glosario/rust/07-archivos-y-entrada-salida.md b/archivo-historico/glosario/rust/07-archivos-y-entrada-salida.md similarity index 100% rename from glosario/rust/07-archivos-y-entrada-salida.md rename to archivo-historico/glosario/rust/07-archivos-y-entrada-salida.md diff --git a/glosario/rust/08-poo-basica.md b/archivo-historico/glosario/rust/08-poo-basica.md similarity index 100% rename from glosario/rust/08-poo-basica.md rename to archivo-historico/glosario/rust/08-poo-basica.md diff --git a/glosario/rust/README.md b/archivo-historico/glosario/rust/README.md similarity index 100% rename from glosario/rust/README.md rename to archivo-historico/glosario/rust/README.md diff --git a/glosario/terminal-linux.md b/archivo-historico/glosario/terminal-linux.md similarity index 100% rename from glosario/terminal-linux.md rename to archivo-historico/glosario/terminal-linux.md diff --git a/glosario/terminal-windows.md b/archivo-historico/glosario/terminal-windows.md similarity index 100% rename from glosario/terminal-windows.md rename to archivo-historico/glosario/terminal-windows.md diff --git a/retos/general/03-juego-de-adivinanza/soluciones/.gitkeep b/archivo-historico/retos/.gitkeep similarity index 100% rename from retos/general/03-juego-de-adivinanza/soluciones/.gitkeep rename to archivo-historico/retos/.gitkeep diff --git a/retos/00-rompehielo/CesarAAR.py b/archivo-historico/retos/00-rompehielo/CesarAAR.py similarity index 100% rename from retos/00-rompehielo/CesarAAR.py rename to archivo-historico/retos/00-rompehielo/CesarAAR.py diff --git a/retos/00-rompehielo/README.md b/archivo-historico/retos/00-rompehielo/README.md similarity index 100% rename from retos/00-rompehielo/README.md rename to archivo-historico/retos/00-rompehielo/README.md diff --git a/retos/00-rompehielo/Vi-dot1.cpp b/archivo-historico/retos/00-rompehielo/Vi-dot1.cpp similarity index 100% rename from retos/00-rompehielo/Vi-dot1.cpp rename to archivo-historico/retos/00-rompehielo/Vi-dot1.cpp diff --git a/retos/00-rompehielo/ashu.py b/archivo-historico/retos/00-rompehielo/ashu.py similarity index 100% rename from retos/00-rompehielo/ashu.py rename to archivo-historico/retos/00-rompehielo/ashu.py diff --git a/retos/00-rompehielo/tacos.py b/archivo-historico/retos/00-rompehielo/tacos.py similarity index 100% rename from retos/00-rompehielo/tacos.py rename to archivo-historico/retos/00-rompehielo/tacos.py diff --git a/retos/general/01-lista-de-tareas-cli/README.md b/archivo-historico/retos/general/01-lista-de-tareas-cli/README.md similarity index 100% rename from retos/general/01-lista-de-tareas-cli/README.md rename to archivo-historico/retos/general/01-lista-de-tareas-cli/README.md diff --git a/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py similarity index 100% rename from retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py rename to archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py diff --git a/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py similarity index 100% rename from retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py rename to archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py diff --git a/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py similarity index 100% rename from retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py rename to archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py diff --git a/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md similarity index 100% rename from retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md rename to archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md diff --git a/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt similarity index 100% rename from retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt rename to archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt diff --git a/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md similarity index 100% rename from retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md rename to archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md diff --git a/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py similarity index 100% rename from retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py rename to archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py diff --git a/retos/general/02-web-scraper-simple/README.md b/archivo-historico/retos/general/02-web-scraper-simple/README.md similarity index 100% rename from retos/general/02-web-scraper-simple/README.md rename to archivo-historico/retos/general/02-web-scraper-simple/README.md diff --git a/retos/general/04-analizador-de-gastos-csv/soluciones/.gitkeep b/archivo-historico/retos/general/02-web-scraper-simple/soluciones/.gitkeep similarity index 100% rename from retos/general/04-analizador-de-gastos-csv/soluciones/.gitkeep rename to archivo-historico/retos/general/02-web-scraper-simple/soluciones/.gitkeep diff --git a/retos/general/03-juego-de-adivinanza/README.md b/archivo-historico/retos/general/03-juego-de-adivinanza/README.md similarity index 100% rename from retos/general/03-juego-de-adivinanza/README.md rename to archivo-historico/retos/general/03-juego-de-adivinanza/README.md diff --git a/retos/general/05-generador-de-contrasenas/soluciones/.gitkeep b/archivo-historico/retos/general/03-juego-de-adivinanza/soluciones/.gitkeep similarity index 100% rename from retos/general/05-generador-de-contrasenas/soluciones/.gitkeep rename to archivo-historico/retos/general/03-juego-de-adivinanza/soluciones/.gitkeep diff --git a/retos/general/04-analizador-de-gastos-csv/README.md b/archivo-historico/retos/general/04-analizador-de-gastos-csv/README.md similarity index 100% rename from retos/general/04-analizador-de-gastos-csv/README.md rename to archivo-historico/retos/general/04-analizador-de-gastos-csv/README.md diff --git a/retos/general/06-conversor-de-unidades/soluciones/.gitkeep b/archivo-historico/retos/general/04-analizador-de-gastos-csv/soluciones/.gitkeep similarity index 100% rename from retos/general/06-conversor-de-unidades/soluciones/.gitkeep rename to archivo-historico/retos/general/04-analizador-de-gastos-csv/soluciones/.gitkeep diff --git a/retos/general/05-generador-de-contrasenas/README.md b/archivo-historico/retos/general/05-generador-de-contrasenas/README.md similarity index 100% rename from retos/general/05-generador-de-contrasenas/README.md rename to archivo-historico/retos/general/05-generador-de-contrasenas/README.md diff --git a/retos/general/07-calculadora-de-imc/soluciones/.gitkeep b/archivo-historico/retos/general/05-generador-de-contrasenas/soluciones/.gitkeep similarity index 100% rename from retos/general/07-calculadora-de-imc/soluciones/.gitkeep rename to archivo-historico/retos/general/05-generador-de-contrasenas/soluciones/.gitkeep diff --git a/retos/general/06-conversor-de-unidades/README.md b/archivo-historico/retos/general/06-conversor-de-unidades/README.md similarity index 100% rename from retos/general/06-conversor-de-unidades/README.md rename to archivo-historico/retos/general/06-conversor-de-unidades/README.md diff --git a/retos/general/08-organizador-de-archivos/soluciones/.gitkeep b/archivo-historico/retos/general/06-conversor-de-unidades/soluciones/.gitkeep similarity index 100% rename from retos/general/08-organizador-de-archivos/soluciones/.gitkeep rename to archivo-historico/retos/general/06-conversor-de-unidades/soluciones/.gitkeep diff --git a/retos/general/07-calculadora-de-imc/README.md b/archivo-historico/retos/general/07-calculadora-de-imc/README.md similarity index 100% rename from retos/general/07-calculadora-de-imc/README.md rename to archivo-historico/retos/general/07-calculadora-de-imc/README.md diff --git a/retos/general/09-recordatorios-en-terminal/soluciones/.gitkeep b/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/.gitkeep similarity index 100% rename from retos/general/09-recordatorios-en-terminal/soluciones/.gitkeep rename to archivo-historico/retos/general/07-calculadora-de-imc/soluciones/.gitkeep diff --git a/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py b/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py similarity index 100% rename from retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py rename to archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py diff --git a/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md b/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md similarity index 100% rename from retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md rename to archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md diff --git a/retos/general/08-organizador-de-archivos/README.md b/archivo-historico/retos/general/08-organizador-de-archivos/README.md similarity index 100% rename from retos/general/08-organizador-de-archivos/README.md rename to archivo-historico/retos/general/08-organizador-de-archivos/README.md diff --git a/retos/general/10-analizador-de-texto/soluciones/.gitkeep b/archivo-historico/retos/general/08-organizador-de-archivos/soluciones/.gitkeep similarity index 100% rename from retos/general/10-analizador-de-texto/soluciones/.gitkeep rename to archivo-historico/retos/general/08-organizador-de-archivos/soluciones/.gitkeep diff --git a/retos/general/09-recordatorios-en-terminal/README.md b/archivo-historico/retos/general/09-recordatorios-en-terminal/README.md similarity index 100% rename from retos/general/09-recordatorios-en-terminal/README.md rename to archivo-historico/retos/general/09-recordatorios-en-terminal/README.md diff --git a/retos/general/11-profiler-de-funciones/soluciones/.gitkeep b/archivo-historico/retos/general/09-recordatorios-en-terminal/soluciones/.gitkeep similarity index 100% rename from retos/general/11-profiler-de-funciones/soluciones/.gitkeep rename to archivo-historico/retos/general/09-recordatorios-en-terminal/soluciones/.gitkeep diff --git a/retos/general/10-analizador-de-texto/README.md b/archivo-historico/retos/general/10-analizador-de-texto/README.md similarity index 100% rename from retos/general/10-analizador-de-texto/README.md rename to archivo-historico/retos/general/10-analizador-de-texto/README.md diff --git a/retos/general/12-validador-de-emails/soluciones/.gitkeep b/archivo-historico/retos/general/10-analizador-de-texto/soluciones/.gitkeep similarity index 100% rename from retos/general/12-validador-de-emails/soluciones/.gitkeep rename to archivo-historico/retos/general/10-analizador-de-texto/soluciones/.gitkeep diff --git a/retos/general/11-profiler-de-funciones/README.md b/archivo-historico/retos/general/11-profiler-de-funciones/README.md similarity index 100% rename from retos/general/11-profiler-de-funciones/README.md rename to archivo-historico/retos/general/11-profiler-de-funciones/README.md diff --git a/retos/general/13-lector-archivos-gigantes/soluciones/.gitkeep b/archivo-historico/retos/general/11-profiler-de-funciones/soluciones/.gitkeep similarity index 100% rename from retos/general/13-lector-archivos-gigantes/soluciones/.gitkeep rename to archivo-historico/retos/general/11-profiler-de-funciones/soluciones/.gitkeep diff --git a/retos/general/12-validador-de-emails/README.md b/archivo-historico/retos/general/12-validador-de-emails/README.md similarity index 100% rename from retos/general/12-validador-de-emails/README.md rename to archivo-historico/retos/general/12-validador-de-emails/README.md diff --git a/retos/general/14-cli-del-clima/soluciones/.gitkeep b/archivo-historico/retos/general/12-validador-de-emails/soluciones/.gitkeep similarity index 100% rename from retos/general/14-cli-del-clima/soluciones/.gitkeep rename to archivo-historico/retos/general/12-validador-de-emails/soluciones/.gitkeep diff --git a/retos/general/13-lector-archivos-gigantes/README.md b/archivo-historico/retos/general/13-lector-archivos-gigantes/README.md similarity index 100% rename from retos/general/13-lector-archivos-gigantes/README.md rename to archivo-historico/retos/general/13-lector-archivos-gigantes/README.md diff --git a/retos/general/15-herramienta-de-backup/soluciones/.gitkeep b/archivo-historico/retos/general/13-lector-archivos-gigantes/soluciones/.gitkeep similarity index 100% rename from retos/general/15-herramienta-de-backup/soluciones/.gitkeep rename to archivo-historico/retos/general/13-lector-archivos-gigantes/soluciones/.gitkeep diff --git a/retos/general/14-cli-del-clima/README.md b/archivo-historico/retos/general/14-cli-del-clima/README.md similarity index 100% rename from retos/general/14-cli-del-clima/README.md rename to archivo-historico/retos/general/14-cli-del-clima/README.md diff --git a/retos/general/16-validador-de-edad/soluciones/.gitkeep b/archivo-historico/retos/general/14-cli-del-clima/soluciones/.gitkeep similarity index 100% rename from retos/general/16-validador-de-edad/soluciones/.gitkeep rename to archivo-historico/retos/general/14-cli-del-clima/soluciones/.gitkeep diff --git a/retos/general/15-herramienta-de-backup/README.md b/archivo-historico/retos/general/15-herramienta-de-backup/README.md similarity index 100% rename from retos/general/15-herramienta-de-backup/README.md rename to archivo-historico/retos/general/15-herramienta-de-backup/README.md diff --git a/retos/general/17-lector-de-configuraciones/soluciones/.gitkeep b/archivo-historico/retos/general/15-herramienta-de-backup/soluciones/.gitkeep similarity index 100% rename from retos/general/17-lector-de-configuraciones/soluciones/.gitkeep rename to archivo-historico/retos/general/15-herramienta-de-backup/soluciones/.gitkeep diff --git a/retos/general/16-validador-de-edad/README.md b/archivo-historico/retos/general/16-validador-de-edad/README.md similarity index 100% rename from retos/general/16-validador-de-edad/README.md rename to archivo-historico/retos/general/16-validador-de-edad/README.md diff --git a/retos/general/18-inventario-de-tienda/soluciones/.gitkeep b/archivo-historico/retos/general/16-validador-de-edad/soluciones/.gitkeep similarity index 100% rename from retos/general/18-inventario-de-tienda/soluciones/.gitkeep rename to archivo-historico/retos/general/16-validador-de-edad/soluciones/.gitkeep diff --git a/retos/general/17-lector-de-configuraciones/README.md b/archivo-historico/retos/general/17-lector-de-configuraciones/README.md similarity index 100% rename from retos/general/17-lector-de-configuraciones/README.md rename to archivo-historico/retos/general/17-lector-de-configuraciones/README.md diff --git a/retos/nerve/01-chat-terminal/.gitkeep b/archivo-historico/retos/general/17-lector-de-configuraciones/soluciones/.gitkeep similarity index 100% rename from retos/nerve/01-chat-terminal/.gitkeep rename to archivo-historico/retos/general/17-lector-de-configuraciones/soluciones/.gitkeep diff --git a/retos/general/18-inventario-de-tienda/README.md b/archivo-historico/retos/general/18-inventario-de-tienda/README.md similarity index 100% rename from retos/general/18-inventario-de-tienda/README.md rename to archivo-historico/retos/general/18-inventario-de-tienda/README.md diff --git a/retos/nerve/01-chat-terminal/soluciones/.gitkeep b/archivo-historico/retos/general/18-inventario-de-tienda/soluciones/.gitkeep similarity index 100% rename from retos/nerve/01-chat-terminal/soluciones/.gitkeep rename to archivo-historico/retos/general/18-inventario-de-tienda/soluciones/.gitkeep diff --git a/retos/nerve/02-reloj-sincronizado/.gitkeep b/archivo-historico/retos/nerve/01-chat-terminal/.gitkeep similarity index 100% rename from retos/nerve/02-reloj-sincronizado/.gitkeep rename to archivo-historico/retos/nerve/01-chat-terminal/.gitkeep diff --git a/retos/nerve/01-chat-terminal/README.md b/archivo-historico/retos/nerve/01-chat-terminal/README.md similarity index 100% rename from retos/nerve/01-chat-terminal/README.md rename to archivo-historico/retos/nerve/01-chat-terminal/README.md diff --git a/retos/nerve/02-reloj-sincronizado/soluciones/.gitkeep b/archivo-historico/retos/nerve/01-chat-terminal/soluciones/.gitkeep similarity index 100% rename from retos/nerve/02-reloj-sincronizado/soluciones/.gitkeep rename to archivo-historico/retos/nerve/01-chat-terminal/soluciones/.gitkeep diff --git a/retos/nerve/03-traductor-de-mensajes/.gitkeep b/archivo-historico/retos/nerve/02-reloj-sincronizado/.gitkeep similarity index 100% rename from retos/nerve/03-traductor-de-mensajes/.gitkeep rename to archivo-historico/retos/nerve/02-reloj-sincronizado/.gitkeep diff --git a/retos/nerve/02-reloj-sincronizado/README.md b/archivo-historico/retos/nerve/02-reloj-sincronizado/README.md similarity index 100% rename from retos/nerve/02-reloj-sincronizado/README.md rename to archivo-historico/retos/nerve/02-reloj-sincronizado/README.md diff --git a/retos/nerve/03-traductor-de-mensajes/soluciones/.gitkeep b/archivo-historico/retos/nerve/02-reloj-sincronizado/soluciones/.gitkeep similarity index 100% rename from retos/nerve/03-traductor-de-mensajes/soluciones/.gitkeep rename to archivo-historico/retos/nerve/02-reloj-sincronizado/soluciones/.gitkeep diff --git a/retos/nerve/04-vigilante-de-carpetas/.gitkeep b/archivo-historico/retos/nerve/03-traductor-de-mensajes/.gitkeep similarity index 100% rename from retos/nerve/04-vigilante-de-carpetas/.gitkeep rename to archivo-historico/retos/nerve/03-traductor-de-mensajes/.gitkeep diff --git a/retos/nerve/03-traductor-de-mensajes/README.md b/archivo-historico/retos/nerve/03-traductor-de-mensajes/README.md similarity index 100% rename from retos/nerve/03-traductor-de-mensajes/README.md rename to archivo-historico/retos/nerve/03-traductor-de-mensajes/README.md diff --git a/retos/nerve/04-vigilante-de-carpetas/soluciones/.gitkeep b/archivo-historico/retos/nerve/03-traductor-de-mensajes/soluciones/.gitkeep similarity index 100% rename from retos/nerve/04-vigilante-de-carpetas/soluciones/.gitkeep rename to archivo-historico/retos/nerve/03-traductor-de-mensajes/soluciones/.gitkeep diff --git a/retos/nerve/05-logger-distribuido/.gitkeep b/archivo-historico/retos/nerve/04-vigilante-de-carpetas/.gitkeep similarity index 100% rename from retos/nerve/05-logger-distribuido/.gitkeep rename to archivo-historico/retos/nerve/04-vigilante-de-carpetas/.gitkeep diff --git a/retos/nerve/04-vigilante-de-carpetas/README.md b/archivo-historico/retos/nerve/04-vigilante-de-carpetas/README.md similarity index 100% rename from retos/nerve/04-vigilante-de-carpetas/README.md rename to archivo-historico/retos/nerve/04-vigilante-de-carpetas/README.md diff --git a/retos/nerve/05-logger-distribuido/soluciones/.gitkeep b/archivo-historico/retos/nerve/04-vigilante-de-carpetas/soluciones/.gitkeep similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/.gitkeep rename to archivo-historico/retos/nerve/04-vigilante-de-carpetas/soluciones/.gitkeep diff --git a/retos/nerve/06-puente-de-progreso-gif/.gitkeep b/archivo-historico/retos/nerve/05-logger-distribuido/.gitkeep similarity index 100% rename from retos/nerve/06-puente-de-progreso-gif/.gitkeep rename to archivo-historico/retos/nerve/05-logger-distribuido/.gitkeep diff --git a/retos/nerve/05-logger-distribuido/README.md b/archivo-historico/retos/nerve/05-logger-distribuido/README.md similarity index 100% rename from retos/nerve/05-logger-distribuido/README.md rename to archivo-historico/retos/nerve/05-logger-distribuido/README.md diff --git a/retos/nerve/06-puente-de-progreso-gif/soluciones/.gitkeep b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/.gitkeep similarity index 100% rename from retos/nerve/06-puente-de-progreso-gif/soluciones/.gitkeep rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/.gitkeep diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs diff --git a/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs similarity index 100% rename from retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs rename to archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs diff --git a/retos/nerve/07-bot-discord-nerve/.gitkeep b/archivo-historico/retos/nerve/06-puente-de-progreso-gif/.gitkeep similarity index 100% rename from retos/nerve/07-bot-discord-nerve/.gitkeep rename to archivo-historico/retos/nerve/06-puente-de-progreso-gif/.gitkeep diff --git a/retos/nerve/06-puente-de-progreso-gif/README.md b/archivo-historico/retos/nerve/06-puente-de-progreso-gif/README.md similarity index 100% rename from retos/nerve/06-puente-de-progreso-gif/README.md rename to archivo-historico/retos/nerve/06-puente-de-progreso-gif/README.md diff --git a/retos/nerve/07-bot-discord-nerve/soluciones/.gitkeep b/archivo-historico/retos/nerve/06-puente-de-progreso-gif/soluciones/.gitkeep similarity index 100% rename from retos/nerve/07-bot-discord-nerve/soluciones/.gitkeep rename to archivo-historico/retos/nerve/06-puente-de-progreso-gif/soluciones/.gitkeep diff --git a/retos/nerve/08-puente-http/.gitkeep b/archivo-historico/retos/nerve/07-bot-discord-nerve/.gitkeep similarity index 100% rename from retos/nerve/08-puente-http/.gitkeep rename to archivo-historico/retos/nerve/07-bot-discord-nerve/.gitkeep diff --git a/retos/nerve/07-bot-discord-nerve/README.md b/archivo-historico/retos/nerve/07-bot-discord-nerve/README.md similarity index 100% rename from retos/nerve/07-bot-discord-nerve/README.md rename to archivo-historico/retos/nerve/07-bot-discord-nerve/README.md diff --git a/retos/nerve/08-puente-http/soluciones/.gitkeep b/archivo-historico/retos/nerve/07-bot-discord-nerve/soluciones/.gitkeep similarity index 100% rename from retos/nerve/08-puente-http/soluciones/.gitkeep rename to archivo-historico/retos/nerve/07-bot-discord-nerve/soluciones/.gitkeep diff --git a/retos/nerve/09-probador-de-carga/.gitkeep b/archivo-historico/retos/nerve/08-puente-http/.gitkeep similarity index 100% rename from retos/nerve/09-probador-de-carga/.gitkeep rename to archivo-historico/retos/nerve/08-puente-http/.gitkeep diff --git a/retos/nerve/08-puente-http/README.md b/archivo-historico/retos/nerve/08-puente-http/README.md similarity index 100% rename from retos/nerve/08-puente-http/README.md rename to archivo-historico/retos/nerve/08-puente-http/README.md diff --git a/retos/nerve/09-probador-de-carga/soluciones/.gitkeep b/archivo-historico/retos/nerve/08-puente-http/soluciones/.gitkeep similarity index 100% rename from retos/nerve/09-probador-de-carga/soluciones/.gitkeep rename to archivo-historico/retos/nerve/08-puente-http/soluciones/.gitkeep diff --git a/retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep b/archivo-historico/retos/nerve/09-probador-de-carga/.gitkeep similarity index 100% rename from retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep rename to archivo-historico/retos/nerve/09-probador-de-carga/.gitkeep diff --git a/retos/nerve/09-probador-de-carga/README.md b/archivo-historico/retos/nerve/09-probador-de-carga/README.md similarity index 100% rename from retos/nerve/09-probador-de-carga/README.md rename to archivo-historico/retos/nerve/09-probador-de-carga/README.md diff --git a/retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep b/archivo-historico/retos/nerve/09-probador-de-carga/soluciones/.gitkeep similarity index 100% rename from retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep rename to archivo-historico/retos/nerve/09-probador-de-carga/soluciones/.gitkeep diff --git a/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep b/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/retos/nerve/10-cliente-nuevo-lenguaje/README.md b/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/README.md similarity index 100% rename from retos/nerve/10-cliente-nuevo-lenguaje/README.md rename to archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/README.md diff --git a/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep b/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/go/.gitkeep b/go/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/javascript/.gitkeep b/javascript/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/.gitkeep b/python/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/.gitkeep b/python/Modulo-01-Fundamentos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/01-hola-mundo/.gitkeep b/python/Modulo-01-Fundamentos/01-hola-mundo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/02-variables/.gitkeep b/python/Modulo-01-Fundamentos/02-variables/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/03-tipos-de-datos/.gitkeep b/python/Modulo-01-Fundamentos/03-tipos-de-datos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/04-operadores/.gitkeep b/python/Modulo-01-Fundamentos/04-operadores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/05-condicionales/.gitkeep b/python/Modulo-01-Fundamentos/05-condicionales/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/.gitkeep b/python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/.gitkeep b/python/Modulo-02-Estructuras/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/.gitkeep b/python/Modulo-03-Exterior/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Nerve/.gitkeep b/python/Modulo-04-Nerve/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/rust/.gitkeep b/rust/.gitkeep new file mode 100644 index 0000000..e69de29 From 572f39fda5489fe5785b9b9f36b4d0a482037361 Mon Sep 17 00:00:00 2001 From: Kaia-Alenia Date: Wed, 22 Jul 2026 16:29:30 -0600 Subject: [PATCH 2/3] feat(core): despliegue de la superestructura 10x10 para Python (100 niveles) --- .../01-hola-mundo/README.md | 13 + .../01-hola-mundo/ejemplo.py | 8 + .../01-hola-mundo/reto.md | 24 ++ .../01-hola-mundo/solucion/.gitkeep | 0 .../01-hola-mundo/teoria.md | 27 ++ .../02-variables/README.md | 13 + .../02-variables/ejemplo.py | 22 ++ .../02-variables/reto.md | 28 ++ .../02-variables/solucion/.gitkeep | 0 .../02-variables/teoria.md | 37 ++ .../03-textos-enteros/.gitkeep | 0 .../03-tipos-de-datos/README.md | 13 + .../03-tipos-de-datos/ejemplo.py | 28 ++ .../03-tipos-de-datos/reto.md | 32 ++ .../03-tipos-de-datos/solucion/.gitkeep | 0 .../03-tipos-de-datos/teoria.md | 31 ++ .../04-flotantes-bool/.gitkeep | 0 .../04-operadores/README.md | 20 + .../04-operadores/ejemplo.py | 68 ++++ .../04-operadores/reto.md | 36 ++ .../04-operadores/solucion/.gitkeep | 0 .../04-operadores/teoria.md | 61 +++ .../05-aritmetica/.gitkeep | 0 .../05-condicionales/README.md | 17 + .../05-condicionales/ejemplo.py | 62 +++ .../05-condicionales/reto.md | 38 ++ .../05-condicionales/solucion/.gitkeep | 0 .../05-condicionales/teoria.md | 88 +++++ .../Modulo-01-Fundamentos/06-inputs/.gitkeep | 0 .../07-f-strings/.gitkeep | 0 .../08-comentarios/.gitkeep | 0 .../Modulo-01-Fundamentos/09-errores/.gitkeep | 0 .../10-RETO-ficha/.gitkeep | 0 .../RETO-FINAL-MOD-1/README.md | 70 ++++ .../RETO-FINAL-MOD-1/solucion/.gitkeep | 0 .../Modulo-02-Estructuras/01-listas/.gitkeep | 0 .../Modulo-02-Estructuras/01-listas/README.md | 13 + .../01-listas/ejemplo.py | 61 +++ .../Modulo-02-Estructuras/01-listas/reto.md | 36 ++ .../01-listas/solucion/.gitkeep | 0 .../Modulo-02-Estructuras/01-listas/teoria.md | 87 +++++ .../Modulo-02-Estructuras/02-tuplas/.gitkeep | 0 .../Modulo-02-Estructuras/02-tuplas/README.md | 13 + .../02-tuplas/ejemplo.py | 56 +++ .../Modulo-02-Estructuras/02-tuplas/reto.md | 34 ++ .../02-tuplas/solucion/.gitkeep | 0 .../Modulo-02-Estructuras/02-tuplas/teoria.md | 67 ++++ .../03-diccionarios/.gitkeep | 0 .../03-diccionarios/README.md | 13 + .../03-diccionarios/ejemplo.py | 59 +++ .../03-diccionarios/reto.md | 38 ++ .../03-diccionarios/solucion/.gitkeep | 0 .../03-diccionarios/teoria.md | 80 ++++ .../04-bucle-for/.gitkeep | 0 .../04-bucle-for/README.md | 13 + .../04-bucle-for/ejemplo.py | 50 +++ .../04-bucle-for/reto.md | 39 ++ .../04-bucle-for/solucion/.gitkeep | 0 .../04-bucle-for/teoria.md | 70 ++++ .../05-bucle-while/.gitkeep | 0 .../05-bucle-while/README.md | 13 + .../05-bucle-while/ejemplo.py | 65 ++++ .../05-bucle-while/reto.md | 38 ++ .../05-bucle-while/solucion/.gitkeep | 0 .../05-bucle-while/teoria.md | 80 ++++ .../RETO-FINAL-MOD-2/.gitkeep | 0 .../RETO-FINAL-MOD-2/README.md | 84 ++++ .../RETO-FINAL-MOD-2/solucion/.gitkeep | 0 python/Modulo-02-Flujo/.gitkeep | 0 .../11-operadores-logicos/.gitkeep | 0 .../Modulo-02-Flujo/12-comparadores/.gitkeep | 0 python/Modulo-02-Flujo/13-if/.gitkeep | 0 python/Modulo-02-Flujo/14-else-elif/.gitkeep | 0 .../15-logica-anidada/.gitkeep | 0 .../Modulo-02-Flujo/16-truthy-falsy/.gitkeep | 0 python/Modulo-02-Flujo/17-match-case/.gitkeep | 0 python/Modulo-02-Flujo/18-depuracion/.gitkeep | 0 .../Modulo-02-Flujo/19-optimizacion/.gitkeep | 0 .../Modulo-02-Flujo/20-RETO-portero/.gitkeep | 0 python/Modulo-03-Estructuras/.gitkeep | 0 .../Modulo-03-Estructuras/21-listas/.gitkeep | 0 .../22-metodos-lista/.gitkeep | 0 .../Modulo-03-Estructuras/23-slicing/.gitkeep | 0 .../Modulo-03-Estructuras/24-tuplas/.gitkeep | 0 .../25-diccionarios/.gitkeep | 0 python/Modulo-03-Estructuras/26-sets/.gitkeep | 0 .../27-estructuras-anidadas/.gitkeep | 0 .../28-mutabilidad/.gitkeep | 0 .../29-deepcopy/.gitkeep | 0 .../30-RETO-inventario/.gitkeep | 0 .../Modulo-03-Exterior/01-funciones/.gitkeep | 0 .../Modulo-03-Exterior/01-funciones/README.md | 13 + .../01-funciones/ejemplo.py | 68 ++++ .../Modulo-03-Exterior/01-funciones/reto.md | 33 ++ .../01-funciones/solucion/.gitkeep | 0 .../Modulo-03-Exterior/01-funciones/teoria.md | 89 +++++ .../02-input-usuario/.gitkeep | 0 .../02-input-usuario/README.md | 11 + .../02-input-usuario/ejemplo.py | 64 +++ .../02-input-usuario/reto.md | 37 ++ .../02-input-usuario/solucion/.gitkeep | 0 .../02-input-usuario/teoria.md | 69 ++++ .../03-manejo-errores/.gitkeep | 0 .../03-manejo-errores/README.md | 13 + .../03-manejo-errores/ejemplo.py | 70 ++++ .../03-manejo-errores/reto.md | 28 ++ .../03-manejo-errores/solucion/.gitkeep | 0 .../03-manejo-errores/teoria.md | 78 ++++ .../Modulo-03-Exterior/04-archivos/.gitkeep | 0 .../Modulo-03-Exterior/04-archivos/README.md | 13 + .../Modulo-03-Exterior/04-archivos/ejemplo.py | 60 +++ python/Modulo-03-Exterior/04-archivos/reto.md | 36 ++ .../04-archivos/solucion/.gitkeep | 0 .../Modulo-03-Exterior/04-archivos/teoria.md | 94 +++++ .../RETO-FINAL-MOD-3/.gitkeep | 0 .../RETO-FINAL-MOD-3/README.md | 71 ++++ .../RETO-FINAL-MOD-3/solucion/.gitkeep | 0 python/Modulo-04-Bucles/.gitkeep | 0 python/Modulo-04-Bucles/31-while/.gitkeep | 0 python/Modulo-04-Bucles/32-for/.gitkeep | 0 .../33-range-enumerate/.gitkeep | 0 .../34-break-continue/.gitkeep | 0 .../35-list-comprehension/.gitkeep | 0 .../36-dict-comprehension/.gitkeep | 0 python/Modulo-04-Bucles/37-zip/.gitkeep | 0 .../Modulo-04-Bucles/38-iteradores/.gitkeep | 0 python/Modulo-04-Bucles/39-matrices/.gitkeep | 0 .../40-RETO-analizador/.gitkeep | 0 python/Modulo-05-Funciones/.gitkeep | 0 python/Modulo-05-Funciones/41-def/.gitkeep | 0 .../42-parametros-return/.gitkeep | 0 .../43-args-defecto/.gitkeep | 0 .../44-args-kwargs/.gitkeep | 0 python/Modulo-05-Funciones/45-scope/.gitkeep | 0 .../Modulo-05-Funciones/46-lambdas/.gitkeep | 0 .../47-recursividad/.gitkeep | 0 python/Modulo-05-Funciones/48-import/.gitkeep | 0 .../Modulo-05-Funciones/49-pip-venvs/.gitkeep | 0 .../50-RETO-calculadora/.gitkeep | 0 python/Modulo-06-OS/.gitkeep | 0 python/Modulo-06-OS/51-os-rutas/.gitkeep | 0 python/Modulo-06-OS/52-txt-read/.gitkeep | 0 python/Modulo-06-OS/53-txt-write/.gitkeep | 0 python/Modulo-06-OS/54-csv/.gitkeep | 0 python/Modulo-06-OS/55-shutil/.gitkeep | 0 python/Modulo-06-OS/56-datetime/.gitkeep | 0 python/Modulo-06-OS/57-subprocess/.gitkeep | 0 python/Modulo-06-OS/58-logging/.gitkeep | 0 python/Modulo-06-OS/59-try-except/.gitkeep | 0 .../Modulo-06-OS/60-RETO-organizador/.gitkeep | 0 python/Modulo-07-Web-APIs/.gitkeep | 0 .../Modulo-07-Web-APIs/61-requests/.gitkeep | 0 python/Modulo-07-Web-APIs/62-json/.gitkeep | 0 .../63-beautifulsoup/.gitkeep | 0 .../Modulo-07-Web-APIs/64-selectores/.gitkeep | 0 .../Modulo-07-Web-APIs/65-selenium/.gitkeep | 0 .../Modulo-07-Web-APIs/66-auth-apis/.gitkeep | 0 .../67-rate-limiting/.gitkeep | 0 .../Modulo-07-Web-APIs/68-headless/.gitkeep | 0 .../Modulo-07-Web-APIs/69-pandas-web/.gitkeep | 0 .../70-RETO-monitor/.gitkeep | 0 python/Modulo-08-POO/.gitkeep | 0 .../Modulo-08-POO/71-clases-objetos/.gitkeep | 0 python/Modulo-08-POO/72-init/.gitkeep | 0 .../73-herencia-polimorfismo/.gitkeep | 0 .../Modulo-08-POO/74-encapsulamiento/.gitkeep | 0 python/Modulo-08-POO/75-propiedades/.gitkeep | 0 .../Modulo-08-POO/76-metodos-clase/.gitkeep | 0 .../Modulo-08-POO/77-dunder-methods/.gitkeep | 0 python/Modulo-08-POO/78-abstraccion/.gitkeep | 0 python/Modulo-08-POO/79-singleton/.gitkeep | 0 .../Modulo-08-POO/80-RETO-ecosistema/.gitkeep | 0 python/Modulo-09-Data-IA/.gitkeep | 0 python/Modulo-09-Data-IA/81-numpy/.gitkeep | 0 python/Modulo-09-Data-IA/82-pandas/.gitkeep | 0 .../83-limpieza-datos/.gitkeep | 0 .../Modulo-09-Data-IA/84-matplotlib/.gitkeep | 0 .../85-scikit-learn/.gitkeep | 0 .../Modulo-09-Data-IA/86-regresion/.gitkeep | 0 .../87-clasificacion/.gitkeep | 0 python/Modulo-09-Data-IA/88-nlp/.gitkeep | 0 python/Modulo-09-Data-IA/89-llms/.gitkeep | 0 .../90-RETO-predictor/.gitkeep | 0 python/Modulo-10-Nerve/.gitkeep | 0 .../100-RETO-FINAL-PILOT/.gitkeep | 0 .../Modulo-10-Nerve/91-local-first/.gitkeep | 0 .../92-arquitectura-nodos/.gitkeep | 0 python/Modulo-10-Nerve/93-p2p/.gitkeep | 0 .../Modulo-10-Nerve/94-estado-global/.gitkeep | 0 .../Modulo-10-Nerve/95-criptografia/.gitkeep | 0 .../96-sincronizacion/.gitkeep | 0 .../97-microservicios/.gitkeep | 0 python/Modulo-10-Nerve/98-despliegue/.gitkeep | 0 .../Modulo-10-Nerve/99-optimizacion/.gitkeep | 0 s.html | 364 ++++++++++++++++++ 195 files changed, 2956 insertions(+) create mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/README.md create mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/reto.md create mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/solucion/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/teoria.md create mode 100644 python/Modulo-01-Fundamentos/02-variables/README.md create mode 100644 python/Modulo-01-Fundamentos/02-variables/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/02-variables/reto.md create mode 100644 python/Modulo-01-Fundamentos/02-variables/solucion/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/02-variables/teoria.md create mode 100644 python/Modulo-01-Fundamentos/03-textos-enteros/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/README.md create mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/reto.md create mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/solucion/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/teoria.md create mode 100644 python/Modulo-01-Fundamentos/04-flotantes-bool/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/04-operadores/README.md create mode 100644 python/Modulo-01-Fundamentos/04-operadores/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/04-operadores/reto.md create mode 100644 python/Modulo-01-Fundamentos/04-operadores/solucion/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/04-operadores/teoria.md create mode 100644 python/Modulo-01-Fundamentos/05-aritmetica/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/05-condicionales/README.md create mode 100644 python/Modulo-01-Fundamentos/05-condicionales/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/05-condicionales/reto.md create mode 100644 python/Modulo-01-Fundamentos/05-condicionales/solucion/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/05-condicionales/teoria.md create mode 100644 python/Modulo-01-Fundamentos/06-inputs/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/07-f-strings/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/08-comentarios/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/09-errores/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/10-RETO-ficha/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/README.md create mode 100644 python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/solucion/.gitkeep create mode 100644 python/Modulo-02-Estructuras/01-listas/.gitkeep create mode 100644 python/Modulo-02-Estructuras/01-listas/README.md create mode 100644 python/Modulo-02-Estructuras/01-listas/ejemplo.py create mode 100644 python/Modulo-02-Estructuras/01-listas/reto.md create mode 100644 python/Modulo-02-Estructuras/01-listas/solucion/.gitkeep create mode 100644 python/Modulo-02-Estructuras/01-listas/teoria.md create mode 100644 python/Modulo-02-Estructuras/02-tuplas/.gitkeep create mode 100644 python/Modulo-02-Estructuras/02-tuplas/README.md create mode 100644 python/Modulo-02-Estructuras/02-tuplas/ejemplo.py create mode 100644 python/Modulo-02-Estructuras/02-tuplas/reto.md create mode 100644 python/Modulo-02-Estructuras/02-tuplas/solucion/.gitkeep create mode 100644 python/Modulo-02-Estructuras/02-tuplas/teoria.md create mode 100644 python/Modulo-02-Estructuras/03-diccionarios/.gitkeep create mode 100644 python/Modulo-02-Estructuras/03-diccionarios/README.md create mode 100644 python/Modulo-02-Estructuras/03-diccionarios/ejemplo.py create mode 100644 python/Modulo-02-Estructuras/03-diccionarios/reto.md create mode 100644 python/Modulo-02-Estructuras/03-diccionarios/solucion/.gitkeep create mode 100644 python/Modulo-02-Estructuras/03-diccionarios/teoria.md create mode 100644 python/Modulo-02-Estructuras/04-bucle-for/.gitkeep create mode 100644 python/Modulo-02-Estructuras/04-bucle-for/README.md create mode 100644 python/Modulo-02-Estructuras/04-bucle-for/ejemplo.py create mode 100644 python/Modulo-02-Estructuras/04-bucle-for/reto.md create mode 100644 python/Modulo-02-Estructuras/04-bucle-for/solucion/.gitkeep create mode 100644 python/Modulo-02-Estructuras/04-bucle-for/teoria.md create mode 100644 python/Modulo-02-Estructuras/05-bucle-while/.gitkeep create mode 100644 python/Modulo-02-Estructuras/05-bucle-while/README.md create mode 100644 python/Modulo-02-Estructuras/05-bucle-while/ejemplo.py create mode 100644 python/Modulo-02-Estructuras/05-bucle-while/reto.md create mode 100644 python/Modulo-02-Estructuras/05-bucle-while/solucion/.gitkeep create mode 100644 python/Modulo-02-Estructuras/05-bucle-while/teoria.md create mode 100644 python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/.gitkeep create mode 100644 python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/README.md create mode 100644 python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/solucion/.gitkeep create mode 100644 python/Modulo-02-Flujo/.gitkeep create mode 100644 python/Modulo-02-Flujo/11-operadores-logicos/.gitkeep create mode 100644 python/Modulo-02-Flujo/12-comparadores/.gitkeep create mode 100644 python/Modulo-02-Flujo/13-if/.gitkeep create mode 100644 python/Modulo-02-Flujo/14-else-elif/.gitkeep create mode 100644 python/Modulo-02-Flujo/15-logica-anidada/.gitkeep create mode 100644 python/Modulo-02-Flujo/16-truthy-falsy/.gitkeep create mode 100644 python/Modulo-02-Flujo/17-match-case/.gitkeep create mode 100644 python/Modulo-02-Flujo/18-depuracion/.gitkeep create mode 100644 python/Modulo-02-Flujo/19-optimizacion/.gitkeep create mode 100644 python/Modulo-02-Flujo/20-RETO-portero/.gitkeep create mode 100644 python/Modulo-03-Estructuras/.gitkeep create mode 100644 python/Modulo-03-Estructuras/21-listas/.gitkeep create mode 100644 python/Modulo-03-Estructuras/22-metodos-lista/.gitkeep create mode 100644 python/Modulo-03-Estructuras/23-slicing/.gitkeep create mode 100644 python/Modulo-03-Estructuras/24-tuplas/.gitkeep create mode 100644 python/Modulo-03-Estructuras/25-diccionarios/.gitkeep create mode 100644 python/Modulo-03-Estructuras/26-sets/.gitkeep create mode 100644 python/Modulo-03-Estructuras/27-estructuras-anidadas/.gitkeep create mode 100644 python/Modulo-03-Estructuras/28-mutabilidad/.gitkeep create mode 100644 python/Modulo-03-Estructuras/29-deepcopy/.gitkeep create mode 100644 python/Modulo-03-Estructuras/30-RETO-inventario/.gitkeep create mode 100644 python/Modulo-03-Exterior/01-funciones/.gitkeep create mode 100644 python/Modulo-03-Exterior/01-funciones/README.md create mode 100644 python/Modulo-03-Exterior/01-funciones/ejemplo.py create mode 100644 python/Modulo-03-Exterior/01-funciones/reto.md create mode 100644 python/Modulo-03-Exterior/01-funciones/solucion/.gitkeep create mode 100644 python/Modulo-03-Exterior/01-funciones/teoria.md create mode 100644 python/Modulo-03-Exterior/02-input-usuario/.gitkeep create mode 100644 python/Modulo-03-Exterior/02-input-usuario/README.md create mode 100644 python/Modulo-03-Exterior/02-input-usuario/ejemplo.py create mode 100644 python/Modulo-03-Exterior/02-input-usuario/reto.md create mode 100644 python/Modulo-03-Exterior/02-input-usuario/solucion/.gitkeep create mode 100644 python/Modulo-03-Exterior/02-input-usuario/teoria.md create mode 100644 python/Modulo-03-Exterior/03-manejo-errores/.gitkeep create mode 100644 python/Modulo-03-Exterior/03-manejo-errores/README.md create mode 100644 python/Modulo-03-Exterior/03-manejo-errores/ejemplo.py create mode 100644 python/Modulo-03-Exterior/03-manejo-errores/reto.md create mode 100644 python/Modulo-03-Exterior/03-manejo-errores/solucion/.gitkeep create mode 100644 python/Modulo-03-Exterior/03-manejo-errores/teoria.md create mode 100644 python/Modulo-03-Exterior/04-archivos/.gitkeep create mode 100644 python/Modulo-03-Exterior/04-archivos/README.md create mode 100644 python/Modulo-03-Exterior/04-archivos/ejemplo.py create mode 100644 python/Modulo-03-Exterior/04-archivos/reto.md create mode 100644 python/Modulo-03-Exterior/04-archivos/solucion/.gitkeep create mode 100644 python/Modulo-03-Exterior/04-archivos/teoria.md create mode 100644 python/Modulo-03-Exterior/RETO-FINAL-MOD-3/.gitkeep create mode 100644 python/Modulo-03-Exterior/RETO-FINAL-MOD-3/README.md create mode 100644 python/Modulo-03-Exterior/RETO-FINAL-MOD-3/solucion/.gitkeep create mode 100644 python/Modulo-04-Bucles/.gitkeep create mode 100644 python/Modulo-04-Bucles/31-while/.gitkeep create mode 100644 python/Modulo-04-Bucles/32-for/.gitkeep create mode 100644 python/Modulo-04-Bucles/33-range-enumerate/.gitkeep create mode 100644 python/Modulo-04-Bucles/34-break-continue/.gitkeep create mode 100644 python/Modulo-04-Bucles/35-list-comprehension/.gitkeep create mode 100644 python/Modulo-04-Bucles/36-dict-comprehension/.gitkeep create mode 100644 python/Modulo-04-Bucles/37-zip/.gitkeep create mode 100644 python/Modulo-04-Bucles/38-iteradores/.gitkeep create mode 100644 python/Modulo-04-Bucles/39-matrices/.gitkeep create mode 100644 python/Modulo-04-Bucles/40-RETO-analizador/.gitkeep create mode 100644 python/Modulo-05-Funciones/.gitkeep create mode 100644 python/Modulo-05-Funciones/41-def/.gitkeep create mode 100644 python/Modulo-05-Funciones/42-parametros-return/.gitkeep create mode 100644 python/Modulo-05-Funciones/43-args-defecto/.gitkeep create mode 100644 python/Modulo-05-Funciones/44-args-kwargs/.gitkeep create mode 100644 python/Modulo-05-Funciones/45-scope/.gitkeep create mode 100644 python/Modulo-05-Funciones/46-lambdas/.gitkeep create mode 100644 python/Modulo-05-Funciones/47-recursividad/.gitkeep create mode 100644 python/Modulo-05-Funciones/48-import/.gitkeep create mode 100644 python/Modulo-05-Funciones/49-pip-venvs/.gitkeep create mode 100644 python/Modulo-05-Funciones/50-RETO-calculadora/.gitkeep create mode 100644 python/Modulo-06-OS/.gitkeep create mode 100644 python/Modulo-06-OS/51-os-rutas/.gitkeep create mode 100644 python/Modulo-06-OS/52-txt-read/.gitkeep create mode 100644 python/Modulo-06-OS/53-txt-write/.gitkeep create mode 100644 python/Modulo-06-OS/54-csv/.gitkeep create mode 100644 python/Modulo-06-OS/55-shutil/.gitkeep create mode 100644 python/Modulo-06-OS/56-datetime/.gitkeep create mode 100644 python/Modulo-06-OS/57-subprocess/.gitkeep create mode 100644 python/Modulo-06-OS/58-logging/.gitkeep create mode 100644 python/Modulo-06-OS/59-try-except/.gitkeep create mode 100644 python/Modulo-06-OS/60-RETO-organizador/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/61-requests/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/62-json/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/63-beautifulsoup/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/64-selectores/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/65-selenium/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/66-auth-apis/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/67-rate-limiting/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/68-headless/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/69-pandas-web/.gitkeep create mode 100644 python/Modulo-07-Web-APIs/70-RETO-monitor/.gitkeep create mode 100644 python/Modulo-08-POO/.gitkeep create mode 100644 python/Modulo-08-POO/71-clases-objetos/.gitkeep create mode 100644 python/Modulo-08-POO/72-init/.gitkeep create mode 100644 python/Modulo-08-POO/73-herencia-polimorfismo/.gitkeep create mode 100644 python/Modulo-08-POO/74-encapsulamiento/.gitkeep create mode 100644 python/Modulo-08-POO/75-propiedades/.gitkeep create mode 100644 python/Modulo-08-POO/76-metodos-clase/.gitkeep create mode 100644 python/Modulo-08-POO/77-dunder-methods/.gitkeep create mode 100644 python/Modulo-08-POO/78-abstraccion/.gitkeep create mode 100644 python/Modulo-08-POO/79-singleton/.gitkeep create mode 100644 python/Modulo-08-POO/80-RETO-ecosistema/.gitkeep create mode 100644 python/Modulo-09-Data-IA/.gitkeep create mode 100644 python/Modulo-09-Data-IA/81-numpy/.gitkeep create mode 100644 python/Modulo-09-Data-IA/82-pandas/.gitkeep create mode 100644 python/Modulo-09-Data-IA/83-limpieza-datos/.gitkeep create mode 100644 python/Modulo-09-Data-IA/84-matplotlib/.gitkeep create mode 100644 python/Modulo-09-Data-IA/85-scikit-learn/.gitkeep create mode 100644 python/Modulo-09-Data-IA/86-regresion/.gitkeep create mode 100644 python/Modulo-09-Data-IA/87-clasificacion/.gitkeep create mode 100644 python/Modulo-09-Data-IA/88-nlp/.gitkeep create mode 100644 python/Modulo-09-Data-IA/89-llms/.gitkeep create mode 100644 python/Modulo-09-Data-IA/90-RETO-predictor/.gitkeep create mode 100644 python/Modulo-10-Nerve/.gitkeep create mode 100644 python/Modulo-10-Nerve/100-RETO-FINAL-PILOT/.gitkeep create mode 100644 python/Modulo-10-Nerve/91-local-first/.gitkeep create mode 100644 python/Modulo-10-Nerve/92-arquitectura-nodos/.gitkeep create mode 100644 python/Modulo-10-Nerve/93-p2p/.gitkeep create mode 100644 python/Modulo-10-Nerve/94-estado-global/.gitkeep create mode 100644 python/Modulo-10-Nerve/95-criptografia/.gitkeep create mode 100644 python/Modulo-10-Nerve/96-sincronizacion/.gitkeep create mode 100644 python/Modulo-10-Nerve/97-microservicios/.gitkeep create mode 100644 python/Modulo-10-Nerve/98-despliegue/.gitkeep create mode 100644 python/Modulo-10-Nerve/99-optimizacion/.gitkeep create mode 100644 s.html diff --git a/python/Modulo-01-Fundamentos/01-hola-mundo/README.md b/python/Modulo-01-Fundamentos/01-hola-mundo/README.md new file mode 100644 index 0000000..008f4c8 --- /dev/null +++ b/python/Modulo-01-Fundamentos/01-hola-mundo/README.md @@ -0,0 +1,13 @@ +# Tema 1 - Nivel 01: Hola Mundo 👋 + +¡Bienvenido al inicio de tu carrera en programación! En este primer escalón, nuestro objetivo es muy simple pero poderoso: lograr que la computadora imprima un mensaje en tu pantalla. + +## Ruta de aprendizaje + +Para superar este nivel, sigue estos pasos en orden: + +1. 📖 **Lee la teoría**: Abre el archivo `teoria.md` para entender cómo nos comunicamos con Python. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y ejecútalo en tu computadora. +3. ⚔️ **Supera el reto**: Abre `reto.md` y demuestra lo que aprendiste. + +¡No tengas prisa! Entender bien los cimientos es el secreto de los mejores ingenieros. diff --git a/python/Modulo-01-Fundamentos/01-hola-mundo/ejemplo.py b/python/Modulo-01-Fundamentos/01-hola-mundo/ejemplo.py new file mode 100644 index 0000000..3f09875 --- /dev/null +++ b/python/Modulo-01-Fundamentos/01-hola-mundo/ejemplo.py @@ -0,0 +1,8 @@ +# El símbolo de "gato" o "numeral" (#) sirve para dejar comentarios. +# Python ignora estas líneas, son solo notas para nosotros los humanos. + +# A continuación, le decimos a Python que imprima un saludo: +print("¡Hola, comunidad Nerve!") + +# También puedes usar comillas simples: +print('Este es mi primer programa en Python.') diff --git a/python/Modulo-01-Fundamentos/01-hola-mundo/reto.md b/python/Modulo-01-Fundamentos/01-hola-mundo/reto.md new file mode 100644 index 0000000..27e3495 --- /dev/null +++ b/python/Modulo-01-Fundamentos/01-hola-mundo/reto.md @@ -0,0 +1,24 @@ +# Reto 01: Presentación Oficial 🎤 (Rompehielo) + +Ya viste cómo funciona el comando `print`. Ahora es tu turno, ¡y lo haremos a lo grande para que quede registrado en la comunidad! + +Este reto no solo probará que sabes imprimir texto, sino que será tu primera contribución (Pull Request) al proyecto. + +## Instrucciones + +1. **Crea tu archivo:** Dentro de la carpeta `solucion/` que está en `01-hola-mundo/`, crea un nuevo archivo con el formato `tu-usuario-01.py` (por ejemplo, `kaia-01.py`). Así llevamos el registro de quién completó qué nivel. + +2. **Escribe el código:** Dentro de tu archivo, usa el comando `print` para que Python imprima tu nombre y tu comida favorita en la terminal. Puedes hacerlo en una sola línea o usar múltiples comandos `print`. + +3. **Pruébalo:** Ejecuta el archivo en tu consola. Estando en la raíz del proyecto, usa: + - **macOS / Linux:** `python3 python/Modulo-01-Fundamentos/01-hola-mundo/solucion/tu-usuario-01.py` + - **Windows:** `python python/Modulo-01-Fundamentos/01-hola-mundo/solucion/tu-usuario-01.py` + + **Resultado esperado en tu pantalla (ejemplo):** + ```text + ¡Hola! Me llamo Kaia y me encantan los tacos. + ``` + +4. **Súbelo a GitHub:** Si todo funciona sin errores, sigue las instrucciones de la guía [COMO-HACER-TU-PRIMER-PR.md](../../../COMO-HACER-TU-PRIMER-PR.md) para subir tus cambios y abrir tu primer Pull Request. + +¿Lograste abrir tu Pull Request? ¡Felicidades! Ya escribiste tu primer programa y contribuiste al código de la comunidad. Estás listo para avanzar al **Nivel 02: Variables**. diff --git a/python/Modulo-01-Fundamentos/01-hola-mundo/solucion/.gitkeep b/python/Modulo-01-Fundamentos/01-hola-mundo/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/01-hola-mundo/teoria.md b/python/Modulo-01-Fundamentos/01-hola-mundo/teoria.md new file mode 100644 index 0000000..2f9151a --- /dev/null +++ b/python/Modulo-01-Fundamentos/01-hola-mundo/teoria.md @@ -0,0 +1,27 @@ +# Teoría: Tu primer comando en Python + +Imagina que Python es un traductor muy estricto. Tú le das una orden, él la traduce al idioma de la máquina, y la máquina la ejecuta. + +Nuestra primera orden será pedirle que muestre un texto en la pantalla. Para hacer esto en Python, usamos un comando incorporado llamado `print`. + +## La instrucción print + +La palabra `print` en inglés significa "imprimir". En programación, no nos referimos a usar papel y tinta, sino a "imprimir" texto en la pantalla negra de tu computadora (llamada consola o terminal). + +La sintaxis (las reglas de escritura) es muy estricta. Así se ve: + +```python +print("¡Hola, mundo!") +``` + +**Anatomía del comando:** + +- `print`: Es la orden. Debe ir siempre en minúsculas. +- `( )`: Los paréntesis envuelven lo que queremos que Python imprima. Son obligatorios. +- `" "`: Las comillas (dobles o simples) le dicen a Python: "Lo que está aquí adentro es texto humano, no intentes ejecutarlo como si fuera código". + +## ¿Qué pasa si me equivoco? + +Python es muy chismoso y te avisará si escribes algo mal. Si olvidas una comilla o un paréntesis, Python detendrá el programa y te lanzará un "Error de Sintaxis" (`SyntaxError`). ¡No te asustes! Los errores son tus mejores maestros. Solo lee dónde te equivocaste y corrígelo. + +¿Listo? Ve al archivo `ejemplo.py` para ver esto en acción. diff --git a/python/Modulo-01-Fundamentos/02-variables/README.md b/python/Modulo-01-Fundamentos/02-variables/README.md new file mode 100644 index 0000000..9b42659 --- /dev/null +++ b/python/Modulo-01-Fundamentos/02-variables/README.md @@ -0,0 +1,13 @@ +# Nivel 02: Variables 📦 + +¡Felicidades por llegar hasta aquí! Ya sabes cómo hacer que Python hable usando `print()`. Ahora vamos a enseñarle cómo recordar cosas. + +En la vida real, necesitas recordar tu nombre, tu edad o dónde dejaste las llaves. Los programas también necesitan recordar información para funcionar (como los puntos del jugador o el nombre del usuario). ¡A eso aprenderemos hoy! + +## Ruta de aprendizaje + +Para superar este nivel, sigue estos pasos en orden: + +1. 📖 **Lee la teoría**: Abre el archivo `teoria.md` para entender cómo se guardan los datos en la memoria de la computadora. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y ejecútalo para ver la magia de guardar y recordar cosas. +3. ⚔️ **Supera el reto**: Abre `reto.md` y demuestra que puedes dominar la memoria de Python. diff --git a/python/Modulo-01-Fundamentos/02-variables/ejemplo.py b/python/Modulo-01-Fundamentos/02-variables/ejemplo.py new file mode 100644 index 0000000..79d886e --- /dev/null +++ b/python/Modulo-01-Fundamentos/02-variables/ejemplo.py @@ -0,0 +1,22 @@ +print("--- 1. CREANDO NUESTRA PRIMERA CAJA ---") +# Agarramos el texto "Nerve" y lo GUARDAMOS (asignamos) en la caja llamada 'usuario'. +# Recuerda: el símbolo '=' significa "guarda lo de la derecha en la izquierda". +usuario = "Nerve" +print("¡Caja 'usuario' creada con éxito!") + +print("--- 2. LEYENDO LO QUE HAY EN LA CAJA ---") +# Le pedimos a Python que mire dentro de la caja y lo muestre en pantalla. +# Observa que 'usuario' no tiene comillas. +print(usuario) + +print("--- 3. MÚLTIPLES CAJAS A LA VEZ ---") +# Podemos tener tantas cajas como queramos. +mensaje_bienvenida = "¡Bienvenido a tu segundo nivel!" +print(mensaje_bienvenida) + +print("--- 4. REUTILIZANDO CAJAS (SOBRESCRIBIR) ---") +# ¿Qué pasa si guardamos algo nuevo en una caja que ya existía? +# Python sacará lo viejo y meterá lo nuevo. +usuario = "Estudiante" +print("El contenido de la caja 'usuario' ha cambiado a:") +print(usuario) diff --git a/python/Modulo-01-Fundamentos/02-variables/reto.md b/python/Modulo-01-Fundamentos/02-variables/reto.md new file mode 100644 index 0000000..fc95ed9 --- /dev/null +++ b/python/Modulo-01-Fundamentos/02-variables/reto.md @@ -0,0 +1,28 @@ +# Reto 02: El Mensaje Secreto 🕵️ + +¡Ya sabes guardar información en la memoria de la computadora! Ahora vas a crear tus propias cajas. + +## Instrucciones + +1. Dentro de la carpeta `solucion/` que está en `02-variables/`, crea tu archivo con el formato `tu-usuario-02.py` (por ejemplo, `kaia-02.py`). +2. Crea una variable llamada `personaje_favorito` y usa el símbolo `=` para guardar el nombre de tu héroe, villano o personaje favorito (¡no olvides las comillas porque es texto!). +3. Crea una segunda variable llamada `poder` y guarda el superpoder que te gustaría tener. +4. Usa dos comandos `print()` separados para imprimir el contenido de cada variable en tu terminal. Usa la función `print("--- Título ---")` para que se vea limpio. + +**Regla inquebrantable:** Solo puedes usar variables y la función `print()`. ¡No escribas el superpoder directamente dentro de los paréntesis del `print()`! + +**Resultado esperado en la terminal:** +```text +--- Mi Personaje --- +Batman +--- Mi Superpoder --- +Invisibilidad +``` + +*(Los textos "Batman" e "Invisibilidad" cambiarán por los que tú elegiste)* + +Ejecuta tu código desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-01-Fundamentos/02-variables/solucion/tu-usuario-02.py` +- **Windows:** `python python/Modulo-01-Fundamentos/02-variables/solucion/tu-usuario-02.py` + +Si todo sale perfecto y sin errores `NameError`... ¡Felicidades! Ya dominas la memoria básica de la computadora. diff --git a/python/Modulo-01-Fundamentos/02-variables/solucion/.gitkeep b/python/Modulo-01-Fundamentos/02-variables/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/02-variables/teoria.md b/python/Modulo-01-Fundamentos/02-variables/teoria.md new file mode 100644 index 0000000..bcbf730 --- /dev/null +++ b/python/Modulo-01-Fundamentos/02-variables/teoria.md @@ -0,0 +1,37 @@ +# Teoría: Variables (Cajas con etiquetas) + +Imagina que estás organizando una mudanza. Para no perder tus cosas, las guardas en **cajas de cartón** y a cada caja le pones una **etiqueta** con un marcador para saber qué hay dentro. + +En programación, una **variable** es exactamente eso: una "caja" en la memoria de la computadora donde guardamos un dato, a la cual le ponemos una "etiqueta" (un nombre) para poder usar ese dato más tarde. + +## Anatomía de una variable y el símbolo `=` + +Para crear nuestra caja en Python, la sintaxis (la regla) es así: + +```python +nombre = "Kaia" +``` + +**Desmontaje de los símbolos:** + +1. `nombre`: Es la etiqueta de tu caja. +2. `=`: **¡ATENCIÓN AQUÍ!** En matemáticas, el símbolo `=` significa "igualdad". Pero en programación, **significa ASIGNACIÓN (guardado)**. Léelo como: *"Agarra lo que está a la derecha (`"Kaia"`) y guárdalo dentro de la caja que está a la izquierda (`nombre`)"*. +3. `"Kaia"`: Es el texto que quieres guardar. + +Para ver lo que hay dentro de la caja, usamos nuestro comando estrella: + +```python +print(nombre) +``` +*Ojo: Cuando imprimimos una variable, NUNCA usamos comillas alrededor del nombre de la caja. Si pusieras `print("nombre")`, Python imprimiría literalmente la palabra "nombre" en lugar de ver qué hay dentro de la caja.* + +## Reglas para etiquetar tus cajas + +Python es muy ordenado y tiene reglas estrictas para nombrar cajas: +1. **Sin espacios:** No puedes llamar a tu caja `mi nombre`. Usa guiones bajos: `mi_nombre`. +2. **Sin números al inicio:** `1caja` es ilegal. `caja1` es correcto. + +## ¿Qué pasa si me equivoco? + +El error de "bebé" más común es pedirle a Python que abra una caja que aún no existe, o escribir mal la etiqueta. +Si guardas un dato en `usuario` pero luego escribes `print(usuari)`, Python entrará en pánico y te mostrará un `NameError` (Error de Nombre). Si ves este error, respira y revisa tu ortografía: la computadora no adivina, obedece exactamente lo que escribes. diff --git a/python/Modulo-01-Fundamentos/03-textos-enteros/.gitkeep b/python/Modulo-01-Fundamentos/03-textos-enteros/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/03-tipos-de-datos/README.md b/python/Modulo-01-Fundamentos/03-tipos-de-datos/README.md new file mode 100644 index 0000000..2d8d089 --- /dev/null +++ b/python/Modulo-01-Fundamentos/03-tipos-de-datos/README.md @@ -0,0 +1,13 @@ +# Nivel 03: Tipos de Datos 🧬 + +Hasta ahora, hemos guardado texto en nuestras cajas (variables). Pero las computadoras son calculadoras gigantes; necesitan saber si lo que guardamos es texto, un número para hacer matemáticas, o un simple interruptor de encendido/apagado. + +En este nivel descubriremos cómo Python clasifica la información. Entender esto evitará que intentes sumar letras o multiplicar palabras por error. + +## Ruta de aprendizaje + +Para superar este nivel, sigue estos pasos en orden: + +1. 📖 **Lee la teoría**: Abre el archivo `teoria.md` para entender las tres naturalezas básicas de la información. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y ejecútalo para ver los tipos de datos en acción en tu terminal. +3. ⚔️ **Supera el reto**: Abre `reto.md` y demuestra que sabes clasificar tus datos. diff --git a/python/Modulo-01-Fundamentos/03-tipos-de-datos/ejemplo.py b/python/Modulo-01-Fundamentos/03-tipos-de-datos/ejemplo.py new file mode 100644 index 0000000..7c6d0a7 --- /dev/null +++ b/python/Modulo-01-Fundamentos/03-tipos-de-datos/ejemplo.py @@ -0,0 +1,28 @@ +# Nuestro primer contacto con los tipos de datos. + +print("--- 1. TIPO DE DATO: TEXTO (STRING) ---") +# Guardamos texto usando comillas. Así le decimos a Python que esto es lenguaje humano. +nombre_mascota = "Firulais" +print("El nombre de la mascota es:") +print(nombre_mascota) + +print("--- 2. TIPO DE DATO: NÚMERO ENTERO (INT) ---") +# Guardamos un número real. ¡Observa cómo no usamos comillas! +edad_mascota = 5 +print("La edad de la mascota en años es:") +print(edad_mascota) + +# Usamos el símbolo '+' para realizar una suma matemática. +# Le sumamos 2 a la caja 'edad_mascota' y guardamos el resultado en una nueva caja. +edad_futura = edad_mascota + 2 +print("En dos años, la mascota tendrá:") +print(edad_futura) + +print("--- 3. TIPO DE DATO: BOOLEANO (BOOL) ---") +# Guardamos un estado de "encendido o apagado": ¿La mascota tiene hambre? +# Usamos 'True' (Verdadero). Debe tener la T en mayúscula y no llevar comillas. +tiene_hambre = True +print("¿La mascota tiene hambre en este momento?") +print(tiene_hambre) + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-01-Fundamentos/03-tipos-de-datos/reto.md b/python/Modulo-01-Fundamentos/03-tipos-de-datos/reto.md new file mode 100644 index 0000000..2a33142 --- /dev/null +++ b/python/Modulo-01-Fundamentos/03-tipos-de-datos/reto.md @@ -0,0 +1,32 @@ +# Reto 03: Ficha de Personaje 🎮 + +Es hora de crear tu propia ficha de personaje de videojuego. Deberás guardar la información correcta utilizando el tipo de dato adecuado. + +## Instrucciones + +1. Dentro de la carpeta `solucion/` que está en `03-tipos-de-datos/`, crea tu archivo con el formato `tu-usuario-03.py` (por ejemplo, `kaia-03.py`). +2. Crea una variable llamada `alias` y asígnale el nombre de tu personaje como **Texto**. +3. Crea una variable llamada `nivel_actual` y asígnale el número `10` como un **Número Entero** (sin comillas). +4. Crea una variable llamada `nivel_proximo`. En esta caja vas a guardar el resultado de sumarle `1` a tu `nivel_actual` (usa el símbolo `+`). +5. Crea una variable llamada `modo_invencible` y asígnale el estado falso como un **Booleano**. +6. Usa varios `print()` intercalados con separadores `---` (tal como vimos en el ejemplo) para mostrar toda esta información en tu terminal. Imprime el `alias`, luego el `nivel_actual`, el `nivel_proximo` y finalmente el `modo_invencible`. + +**Resultado esperado en tu terminal:** +```text +--- Mi Personaje --- +Kaia +--- Nivel Actual --- +10 +--- Nivel Siguiente --- +11 +--- ¿Es Invencible? --- +False +``` + +*(Tu resultado tendrá el nombre que tú hayas elegido, ¡pero asegúrate de que los números y el estado `False` se vean tal cual!)* + +Ejecuta tu código desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-01-Fundamentos/03-tipos-de-datos/solucion/tu-usuario-03.py` +- **Windows:** `python python/Modulo-01-Fundamentos/03-tipos-de-datos/solucion/tu-usuario-03.py` + +Si lograste imprimir toda esta información de forma estructurada y Python no se quejó de ningún error... ¡Estás superando las expectativas y listo para el próximo reto! diff --git a/python/Modulo-01-Fundamentos/03-tipos-de-datos/solucion/.gitkeep b/python/Modulo-01-Fundamentos/03-tipos-de-datos/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/03-tipos-de-datos/teoria.md b/python/Modulo-01-Fundamentos/03-tipos-de-datos/teoria.md new file mode 100644 index 0000000..29c63b0 --- /dev/null +++ b/python/Modulo-01-Fundamentos/03-tipos-de-datos/teoria.md @@ -0,0 +1,31 @@ +# Teoría: La naturaleza de la información + +En el mundo real, no guardas agua en una caja de cartón ni libros en el refrigerador. Dependiendo de lo que vas a guardar, usas el recipiente adecuado. + +En Python, la información también tiene una naturaleza específica. A esto le llamamos **Tipos de Datos**. Hoy conoceremos los tres fundamentales: Texto, Números y Booleanos. + +## 1. Texto (String o `str`) + +Ya lo conoces. Es cualquier cosa escrita para que la lea un humano. +- **La regla visual:** SIEMPRE va entre comillas (dobles `" "` o simples `' '`). +- **Ejemplo:** `"Hola"`, `"123"`, `"¡Peligro!"`. +- *Ojo:* Si un número está entre comillas `"10"`, Python lo ve como un dibujo del número 10, no como una cantidad matemática. + +## 2. Números Enteros (Integer o `int`) + +Son números exactos, sin punto decimal, perfectos para contar cosas (1, 2, 3, -5, 100). +- **La regla visual:** NUNCA llevan comillas. +- **Ejemplo:** `10`, `42`, `-5`. +- **Anatomía del símbolo `+`**: Con los números enteros, el símbolo `+` no es un adorno, sirve para realizar sumas matemáticas exactas para la computadora. Si tienes `5 + 5`, el resultado real será `10`. + +## 3. Booleanos (`bool`) + +Imagina un interruptor de luz: solo tiene dos estados posibles, Encendido (Verdadero) o Apagado (Falso). En Python, esto es un booleano. +- **La regla visual:** Se escriben exactamente como `True` (Verdadero) o `False` (Falso). ¡La primera letra SIEMPRE debe ser mayúscula y NO llevan comillas! +- **Ejemplo:** `True`, `False`. + +## ¿Qué pasa si me equivoco? + +El error más común de los principiantes es confundir el texto con números. +Si intentas guardar un booleano en minúsculas (ej. `true`), Python te lanzará un `NameError` pensando que es una variable que olvidaste crear. +Si pones el número `"10"` entre comillas e intentas sumarle `5`, Python entrará en pánico porque no sabe cómo sumar letras con matemáticas (lanzará un `TypeError`). diff --git a/python/Modulo-01-Fundamentos/04-flotantes-bool/.gitkeep b/python/Modulo-01-Fundamentos/04-flotantes-bool/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/04-operadores/README.md b/python/Modulo-01-Fundamentos/04-operadores/README.md new file mode 100644 index 0000000..0a2e2b2 --- /dev/null +++ b/python/Modulo-01-Fundamentos/04-operadores/README.md @@ -0,0 +1,20 @@ +# Nivel 04: Operadores ➕➖✖️➗ + +Hasta ahora, nuestras "cajas" solo guardan datos quietos. Hoy les damos vida: vamos a **operar** con ellos. + +Un **operador** es un símbolo especial que le dice a Python "haz esto con estos datos". Existen dos familias que conoceremos hoy: + +- **Aritméticos**: hacen matemáticas con números (`+`, `-`, `*`, `/`, `//`, `%`). +- **De comparación**: responden preguntas de Sí/No entre dos valores (`==`, `!=`, `>`, `<`, `>=`, `<=`). + +Esta lección es el motor de toda lógica futura. Sin operadores, un programa no puede tomar decisiones. + +## Ruta de aprendizaje + +Para superar este nivel, sigue estos pasos en orden: + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende qué significa cada símbolo, uno por uno. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y ejecútalo para ver los operadores en acción. +3. ⚔️ **Supera el reto**: Abre `reto.md` y demuestra que sabes usar los operadores. + +¡Recuerda: cada símbolo nuevo se explica antes de usarse! diff --git a/python/Modulo-01-Fundamentos/04-operadores/ejemplo.py b/python/Modulo-01-Fundamentos/04-operadores/ejemplo.py new file mode 100644 index 0000000..4a2e3d4 --- /dev/null +++ b/python/Modulo-01-Fundamentos/04-operadores/ejemplo.py @@ -0,0 +1,68 @@ +# Los operadores aritméticos y de comparación en acción. + +print("--- 1. OPERADORES ARITMÉTICOS ---") + +# Tenemos dos números guardados en cajas. +puntos_jugador_a = 150 +puntos_jugador_b = 47 + +# El símbolo '+' suma los números de ambas cajas. +total_puntos = puntos_jugador_a + puntos_jugador_b +print("Suma de puntos:") +print(total_puntos) + +# El símbolo '-' resta. Cuántos puntos le lleva A a B. +diferencia = puntos_jugador_a - puntos_jugador_b +print("Diferencia de puntos:") +print(diferencia) + +# El símbolo '*' multiplica (es el asterisco, no la 'x'). +# Si cada punto vale 3 monedas, ¿cuántas monedas tiene el jugador A? +monedas_a = puntos_jugador_a * 3 +print("Monedas del jugador A:") +print(monedas_a) + +print("--- 2. DIVISIÓN NORMAL vs DIVISIÓN ENTERA ---") + +# El símbolo '/' divide y SIEMPRE da decimales. +# Repartimos 10 monedas entre 4 amigos. +monedas_totales = 10 +amigos = 4 + +# Con '/', Python devuelve el resultado exacto con decimales. +reparto_exacto = monedas_totales / amigos +print("Reparto exacto (con '/') :") +print(reparto_exacto) + +# Con '//', Python descarta los decimales. ¿Cuántas recibe cada uno SIN dividir monedas? +reparto_entero = monedas_totales // amigos +print("Reparto entero (con '//') :") +print(reparto_entero) + +# Con '%', Python devuelve las que SOBRAN. +monedas_sobrantes = monedas_totales % amigos +print("Monedas sobrantes (con '%') :") +print(monedas_sobrantes) + +print("--- 3. OPERADORES DE COMPARACIÓN ---") +# Los operadores de comparación hacen PREGUNTAS. La respuesta es True o False. + +vida_jugador = 80 +vida_maxima = 100 + +# ¿Tiene el jugador la vida máxima? Preguntamos con '==' +tiene_vida_llena = vida_jugador == vida_maxima +print("¿El jugador tiene vida llena?") +print(tiene_vida_llena) + +# ¿Tiene el jugador MENOS de 50 de vida? Preguntamos con '<' +esta_en_peligro = vida_jugador < 50 +print("¿El jugador está en peligro (vida < 50)?") +print(esta_en_peligro) + +# ¿El jugador A tiene más puntos que el jugador B? +a_gana = puntos_jugador_a > puntos_jugador_b +print("¿El jugador A va ganando?") +print(a_gana) + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-01-Fundamentos/04-operadores/reto.md b/python/Modulo-01-Fundamentos/04-operadores/reto.md new file mode 100644 index 0000000..d4085fa --- /dev/null +++ b/python/Modulo-01-Fundamentos/04-operadores/reto.md @@ -0,0 +1,36 @@ +# Reto 04: La Calculadora de la Tienda 🛒 + +Eres el cajero de una tienda y debes calcular el total de una compra, el cambio y responder algunas preguntas usando operadores. + +## Instrucciones + +Dentro de la carpeta `solucion/` que está en `04-operadores/`, crea tu archivo con el formato `tu-usuario-04.py` (por ejemplo, `kaia-04.py`). Luego sigue los pasos: + +1. Crea una variable `precio_camisa` con el valor `250` (un número entero). +2. Crea una variable `precio_pantalon` con el valor `480` (un número entero). +3. Crea una variable `total_compra` que guarde la **suma** de ambos precios usando `+`. +4. El cliente paga con `800`. Crea una variable `pago_cliente` con ese valor. +5. Crea una variable `cambio` que guarde la **resta** de `pago_cliente - total_compra`. +6. Crea una variable `puede_comprar` que guarde el resultado de preguntar si `pago_cliente >= total_compra` (usa `>=`). +7. Crea una variable `es_compra_cara` que guarde el resultado de preguntar si `total_compra > 600` (usa `>`). +8. Usa `print()` con separadores para mostrar todo como se ve en el resultado esperado. + +**Resultado esperado en tu terminal:** +```text +--- Ticket de Compra --- +730 +--- Pago del cliente --- +800 +--- Cambio --- +70 +--- ¿El cliente puede pagar? --- +True +--- ¿Es una compra cara (más de 600)? --- +True +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-01-Fundamentos/04-operadores/solucion/tu-usuario-04.py` +- **Windows:** `python python/Modulo-01-Fundamentos/04-operadores/solucion/tu-usuario-04.py` + +Si todos los valores coinciden y Python no arrojó ningún error, ¡pasaste al siguiente nivel! diff --git a/python/Modulo-01-Fundamentos/04-operadores/solucion/.gitkeep b/python/Modulo-01-Fundamentos/04-operadores/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/04-operadores/teoria.md b/python/Modulo-01-Fundamentos/04-operadores/teoria.md new file mode 100644 index 0000000..122eb45 --- /dev/null +++ b/python/Modulo-01-Fundamentos/04-operadores/teoria.md @@ -0,0 +1,61 @@ +# Teoría: Operadores — Los verbos de las matemáticas y las preguntas + +Ya sabes guardar datos en cajas (variables). Ahora aprenderás a **hacer cosas** con esos datos. + +--- + +## Familia 1: Operadores Aritméticos + +Imagina una calculadora de bolsillo. Los operadores aritméticos son exactamente esos botones que ya conoces, pero con alguna sorpresa. + +| Símbolo | Nombre | Qué hace | Ejemplo | Resultado | +|---------|--------|----------|---------|-----------| +| `+` | Suma | Suma dos números | `10 + 3` | `13` | +| `-` | Resta | Resta el derecho del izquierdo | `10 - 3` | `7` | +| `*` | Multiplicación | **Ojo:** no es la `x` del cuaderno, es el asterisco | `10 * 3` | `30` | +| `/` | División | Divide. El resultado **siempre** tiene decimales | `10 / 3` | `3.333...` | +| `//` | División Entera | Divide y **descarta** los decimales, solo devuelve la parte entera | `10 // 3` | `3` | +| `%` | Módulo (Residuo) | Divide y te da el **sobrante** | `10 % 3` | `1` | + +### Anatomía: el operador `%` (módulo) + +Este es el más raro para los principiantes. Piensa en él como la respuesta a: *"Si reparto 10 galletas entre 3 amigos, ¿cuántas galletas sobran?"*. La respuesta es `1` (3+3+3=9, sobra 1). El `%` da esa galleta sobrante. + +--- + +## Familia 2: Operadores de Comparación + +Aquí Python hace algo muy interesante: en lugar de calcular un número, responde una **pregunta de Verdadero o Falso**. El resultado siempre es un **Booleano** (`True` o `False`), que ya conocemos del Nivel 03. + +| Símbolo | Pregunta que hace | Ejemplo | Resultado | +|---------|------------------|---------|-----------| +| `==` | ¿Son iguales? | `5 == 5` | `True` | +| `!=` | ¿Son diferentes? | `5 != 3` | `True` | +| `>` | ¿El izquierdo es mayor? | `10 > 3` | `True` | +| `<` | ¿El izquierdo es menor? | `3 < 10` | `True` | +| `>=` | ¿Mayor o igual? | `5 >= 5` | `True` | +| `<=` | ¿Menor o igual? | `3 <= 10` | `True` | + +### ¡TRAMPA MORTAL! El `=` vs el `==` + +Este es el error que comete **todo principiante** al menos una vez: + +- **`=`** (un solo igual): Es la **asignación**. Le dice a Python "guarda este valor en esta caja". Lo conocemos del Nivel 02. +- **`==`** (dos iguales juntos): Es la **comparación**. Le pregunta a Python "¿estos dos valores son idénticos?". + +```python +# GUARDAR un valor en una caja: +puntos = 100 + +# PREGUNTAR si dos valores son iguales (resultado: True o False): +puntos == 100 +``` + +Usa un `=` cuando quieres guardar. Usa `==` cuando quieres preguntar. + +--- + +## ¿Qué pasa si me equivoco? + +- **Dividir por cero**: Si escribes `10 / 0`, Python lanzará un `ZeroDivisionError`. La computadora no puede dividir entre nada, igual que en matemáticas. +- **Operar tipos distintos**: Aún no puedes mezclar texto con números directamente. `"hola" + 5` generará un `TypeError`. El operador `+` con texto funciona diferente (lo veremos en un nivel futuro). diff --git a/python/Modulo-01-Fundamentos/05-aritmetica/.gitkeep b/python/Modulo-01-Fundamentos/05-aritmetica/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/05-condicionales/README.md b/python/Modulo-01-Fundamentos/05-condicionales/README.md new file mode 100644 index 0000000..c1be982 --- /dev/null +++ b/python/Modulo-01-Fundamentos/05-condicionales/README.md @@ -0,0 +1,17 @@ +# Nivel 05: Condicionales 🔀 + +Hasta ahora, nuestros programas son como trenes en un riel: siempre van en la misma dirección, línea por línea, sin importar nada. + +Hoy les damos **cerebro**: aprenderán a **tomar decisiones**. + +Con los condicionales, el programa puede preguntar "¿esto es verdad?" y elegir un camino diferente según la respuesta. Esto es exactamente lo que hace tu aplicación de mensajería cuando pregunta "¿Ingresaste la contraseña correcta?" y decide si te deja entrar o no. + +## Ruta de aprendizaje + +Para superar este nivel, sigue estos pasos en orden: + +1. 📖 **Lee la teoría**: Abre `teoria.md` para entender la estructura `if` / `else`. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y presta atención especial a los **espacios** (la indentación). +3. ⚔️ **Supera el reto**: Abre `reto.md` y escribe tu propia lógica de decisión. + +> ¡Advertencia! Este nivel introduce el concepto más importante de programación: la **indentación**. Un solo espacio de más o de menos rompe todo. Lee con calma. diff --git a/python/Modulo-01-Fundamentos/05-condicionales/ejemplo.py b/python/Modulo-01-Fundamentos/05-condicionales/ejemplo.py new file mode 100644 index 0000000..9cf4151 --- /dev/null +++ b/python/Modulo-01-Fundamentos/05-condicionales/ejemplo.py @@ -0,0 +1,62 @@ +# El guardia de seguridad: tomando decisiones con if / elif / else. + +print("--- 1. IF SIMPLE: ¿Tiene suficiente vida? ---") + +# Guardamos la vida actual del personaje en una caja. +vida_actual = 80 + +# Preguntamos: ¿La vida es mayor a 50? +# Si la respuesta es True, Python ejecuta el print de adentro. +if vida_actual > 50: + # Esta línea tiene 4 espacios al inicio. Está "dentro" del if. + print("El personaje está sano y puede pelear.") + +print("--- 2. IF / ELSE: Un camino o el otro ---") + +# Cambiamos la vida a un valor bajo para ver el otro camino. +vida_actual = 20 + +# Si la vida es mayor a 50 → camino A. +# Si NO (else) → camino B. +if vida_actual > 50: + print("El personaje está sano.") +else: + # Esta línea también tiene 4 espacios. Está "dentro" del else. + print("¡PELIGRO! El personaje necesita una poción.") + +print("--- 3. IF / ELIF / ELSE: Múltiples caminos ---") + +# Guardamos la puntuación del jugador. +puntuacion = 750 + +# Python revisa cada condición de arriba hacia abajo. +# Ejecuta SOLO el primer bloque cuya condición sea True y luego salta al final. + +if puntuacion >= 1000: + # ¿Tiene 1000 o más? → Rango "S" + print("Rango: S — ¡Maestro!") +elif puntuacion >= 700: + # Si no llegó a 1000, ¿tiene 700 o más? → Rango "A" + # 750 SÍ es >= 700, así que este bloque se ejecuta. + print("Rango: A — ¡Excelente!") +elif puntuacion >= 400: + # Si no llegó a 700, ¿tiene 400 o más? → Rango "B" + print("Rango: B — ¡Bien!") +else: + # Si ninguna condición anterior fue True → Rango "C" + print("Rango: C — Sigue practicando.") + +print("--- 4. COMPARANDO CON == ---") + +# El guardia pide una contraseña. +contrasena_correcta = 9999 +intento = 1234 + +# Usamos '==' para PREGUNTAR si el intento es igual a la contraseña correcta. +# RECUERDA: '==' es preguntar, '=' es guardar. +if intento == contrasena_correcta: + print("Acceso concedido.") +else: + print("Acceso denegado. Contraseña incorrecta.") + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-01-Fundamentos/05-condicionales/reto.md b/python/Modulo-01-Fundamentos/05-condicionales/reto.md new file mode 100644 index 0000000..e4cab1c --- /dev/null +++ b/python/Modulo-01-Fundamentos/05-condicionales/reto.md @@ -0,0 +1,38 @@ +# Reto 05: El Semáforo del Programador 🚦 + +Vas a simular la lógica de un semáforo inteligente que evalúa la velocidad de un vehículo y decide qué hacer. + +## Instrucciones + +Dentro de la carpeta `solucion/` que está en `05-condicionales/`, crea tu archivo con el formato `tu-usuario-05.py` (por ejemplo, `kaia-05.py`). Luego sigue los pasos: + +1. Crea una variable `velocidad_actual` y asígnale el valor `85` (un número entero que representa km/h). +2. Crea una variable `limite_velocidad` y asígnale el valor `80`. +3. Escribe una cadena de `if / elif / else` con la siguiente lógica: + - **SI** `velocidad_actual` es menor a `60`: imprime `"Velocidad baja. Puedes acelerar."` + - **SI NO, SI** `velocidad_actual` es menor o igual a `limite_velocidad`: imprime `"Velocidad correcta. Bien manejado."` + - **SI NO** (cualquier otro caso, es decir, si va por encima del límite): imprime `"¡EXCESO DE VELOCIDAD! Reduce la marcha."` +4. Después del bloque `if/elif/else`, crea una variable llamada `excede_limite` que guarde el resultado de preguntar si `velocidad_actual > limite_velocidad`. +5. Imprime el valor de `excede_limite`. + +**Resultado esperado en tu terminal** (con `velocidad_actual = 85`): +```text +--- Evaluación de Velocidad --- +¡EXCESO DE VELOCIDAD! Reduce la marcha. +--- ¿Excede el límite? --- +True +``` + +**Prueba adicional (opcional):** Cambia el valor de `velocidad_actual` a `55` y vuelve a ejecutar. Deberías ver: +```text +--- Evaluación de Velocidad --- +Velocidad baja. Puedes acelerar. +--- ¿Excede el límite? --- +False +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-01-Fundamentos/05-condicionales/solucion/tu-usuario-05.py` +- **Windows:** `python python/Modulo-01-Fundamentos/05-condicionales/solucion/tu-usuario-05.py` + +Si tu semáforo funciona correctamente para ambas pruebas, ¡estás listo para el Reto Final del Módulo! diff --git a/python/Modulo-01-Fundamentos/05-condicionales/solucion/.gitkeep b/python/Modulo-01-Fundamentos/05-condicionales/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/05-condicionales/teoria.md b/python/Modulo-01-Fundamentos/05-condicionales/teoria.md new file mode 100644 index 0000000..ee5bf47 --- /dev/null +++ b/python/Modulo-01-Fundamentos/05-condicionales/teoria.md @@ -0,0 +1,88 @@ +# Teoría: Condicionales — Enseñarle a tu programa a tomar decisiones + +Imagina un guardia de seguridad en la puerta de un club. Su trabajo es muy simple: + +- **SI** el visitante tiene más de 18 años → lo deja pasar. +- **Si NO** → le dice que no puede entrar. + +Esa lógica de "SI esto... entonces aquello... si no... lo otro" es exactamente lo que hace un `if` / `else` en Python. + +--- + +## Anatomía del `if` + +La estructura básica es: + +```python +if condicion: + accion_si_es_verdad +``` + +Desglosemos cada pieza: + +- **`if`**: Es una palabra reservada de Python que significa "SI". Nunca la uses como nombre de variable. +- **`condicion`**: Es cualquier expresión que resulte en `True` o `False`. Aquí es donde usamos los operadores de comparación del Nivel 04 (`==`, `>`, `<`, etc.). +- **`:`** (dos puntos): Símbolo obligatorio al final de la línea del `if`. Le dice a Python: "aquí termina la pregunta, lo que sigue es la respuesta". +- **` accion`** (4 espacios adelante): A esto se le llama **indentación**. Es el bloque de código que Python ejecuta **solo si** la condición es `True`. Los espacios no son decoración: son **obligatorios** y deben ser exactamente 4 espacios (o 1 Tab). + +--- + +## Anatomía del `if` / `else` + +El `else` captura el caso contrario, cuando la condición es `False`: + +```python +if condicion: + accion_si_es_verdad +else: + accion_si_es_falso +``` + +- **`else`**: Significa "SI NO" o "de lo contrario". No lleva condición propia. +- **`:`** (dos puntos): También obligatorio después del `else`. +- El bloque del `else` también debe estar **indentado** con 4 espacios. + +--- + +## Anatomía del `if` / `elif` / `else` + +¿Y si hay más de dos caminos? Usamos `elif` (abreviación de "else if", o sea "si no, si..."): + +```python +if condicion_1: + accion_1 +elif condicion_2: + accion_2 +else: + accion_si_ninguna_anterior_fue_verdad +``` + +Python revisa las condiciones de arriba hacia abajo y ejecuta **solo la primera** que encuentre verdadera. + +--- + +## ¡EL ENEMIGO NÚMERO UNO: LA INDENTACIÓN! + +Este es el error más común y frustrante para los principiantes en Python. + +```python +# CORRECTO: La acción está indentada con 4 espacios. +if True: + print("Este código es correcto") + +# ERROR: IndentationError. Falta la indentación. +if True: +print("Este código va a fallar") +``` + +Si ves un error `IndentationError` o `SyntaxError` al correr tu código, la primera sospecha siempre es la indentación. + +**Regla de oro:** Todo lo que está "dentro" de un `if`, `elif` o `else` debe tener exactamente 4 espacios al inicio. + +--- + +## ¿Qué pasa si me equivoco? + +- **Olvidar el `:`**: Python lanzará un `SyntaxError: expected ':'`. Es la queja más común. +- **Mala indentación**: `IndentationError`. Cuenta tus espacios. +- **Usar `=` en lugar de `==` dentro del `if`**: El programa probablemente no funcione como esperas. Recuerda: `=` guarda, `==` compara. diff --git a/python/Modulo-01-Fundamentos/06-inputs/.gitkeep b/python/Modulo-01-Fundamentos/06-inputs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/07-f-strings/.gitkeep b/python/Modulo-01-Fundamentos/07-f-strings/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/08-comentarios/.gitkeep b/python/Modulo-01-Fundamentos/08-comentarios/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/09-errores/.gitkeep b/python/Modulo-01-Fundamentos/09-errores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/10-RETO-ficha/.gitkeep b/python/Modulo-01-Fundamentos/10-RETO-ficha/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/README.md b/python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/README.md new file mode 100644 index 0000000..1f5530b --- /dev/null +++ b/python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/README.md @@ -0,0 +1,70 @@ +# Reto Final: Módulo 01 — Fundamentos 🏆 + +¡Llegaste al final del primer módulo! Ha llegado la hora de la verdad. + +Este reto no te da instrucciones paso a paso. Tú decides **cómo** resolver el problema usando todo lo que aprendiste. Solo tienes el enunciado y el resultado esperado. + +--- + +## El Problema: Sistema de Registro de un Club + +Eres el programador de un club exclusivo. Debes crear un sistema que evalúe a un nuevo solicitante y emita un **reporte de acceso**. + +Crea tu archivo en la carpeta `solucion/` dentro de `RETO-FINAL-MOD-1/` y nómbralo con el formato `tu-usuario-final.py` (por ejemplo, `kaia-final.py`). + +### Datos del solicitante (define estos valores tú mismo al inicio del archivo): + +- `nombre`: El nombre del solicitante. Guárdalo como texto (`str`). +- `edad`: La edad del solicitante. Guárdalo como número entero (`int`). Usa el valor `17`. +- `puntos_reputacion`: Los puntos de reputación del solicitante. Usa el valor `850`. +- `cuota_anual`: El costo anual del club. Usa el valor `1200`. +- `saldo_solicitante`: El dinero disponible del solicitante. Usa el valor `950`. + +### Lógica que debes programar: + +1. Calcula una variable `cuota_con_descuento`. Si el solicitante tiene `850` o más puntos de reputación, el club le da un descuento del 20%. El 20% de la cuota son 240 pesos, así que la cuota con descuento es `cuota_anual - 240`. + +2. Calcula una variable `puede_pagar`. Pregunta (con un operador de comparación) si el `saldo_solicitante` es **mayor o igual** a la `cuota_con_descuento`. + +3. Determina el **acceso al club** con `if / elif / else`: + - Crea una variable `estado_acceso`. + - **SI** la `edad` es **menor que** `18`: guarda en `estado_acceso` el texto `"Denegado: menor de edad"`. + - **SI NO, SI** `puede_pagar` es `False`: guarda en `estado_acceso` el texto `"Denegado: saldo insuficiente"`. + - **SI NO** (pasa ambas pruebas): guarda en `estado_acceso` el texto `"Acceso aprobado"`. + +4. Imprime el reporte completo con separadores, tal como se ve en el resultado esperado. + +--- + +## Resultado esperado en tu terminal + +```text +--- REPORTE DE ACCESO AL CLUB --- +--- Solicitante --- +Alex +--- Edad --- +17 +--- Puntos de reputación --- +850 +--- Cuota normal --- +1200 +--- Cuota con descuento (20%) --- +960 +--- ¿Puede pagar la cuota? --- +False +--- VEREDICTO FINAL --- +Denegado: menor de edad +``` + +> **Nota:** El `nombre` puede ser el que tú quieras. Los números deben coincidir exactamente con los del resultado. + +--- + +## Criterios de éxito ✓ + +- [ ] El código corre sin ningún error. +- [ ] Todos los valores numéricos coinciden con el resultado esperado. +- [ ] El `estado_acceso` muestra `"Denegado: menor de edad"` porque la `edad` es `17` (aunque no pueda pagar, la edad se evalúa primero). +- [ ] Usaste variables para **todo**: ningún número aparece "suelto" en un `print()`, solo variables. + +¡Mucho éxito! Si logras esto, dominas los fundamentos de Python. El Módulo 02 te espera. diff --git a/python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/solucion/.gitkeep b/python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/01-listas/.gitkeep b/python/Modulo-02-Estructuras/01-listas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/01-listas/README.md b/python/Modulo-02-Estructuras/01-listas/README.md new file mode 100644 index 0000000..1843140 --- /dev/null +++ b/python/Modulo-02-Estructuras/01-listas/README.md @@ -0,0 +1,13 @@ +# Nivel 06: Listas 📋 + +Hasta ahora, cada variable guardaba **un solo dato** a la vez. Pero ¿qué pasa si necesitas guardar los nombres de 100 jugadores? ¿Creas 100 variables? + +Las **Listas** resuelven ese problema: son una única "caja" que puede contener **múltiples datos en orden**, uno detrás del otro, como los vagones de un tren. + +Las listas son la estructura de datos más usada en Python. Las encontrarás en todo: resultados de búsquedas, filas de base de datos, feeds de redes sociales. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende a crear listas y acceder a sus elementos. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` para ver cómo manipular una lista paso a paso. +3. ⚔️ **Supera el reto**: Abre `reto.md` y construye tu propia lista desde cero. diff --git a/python/Modulo-02-Estructuras/01-listas/ejemplo.py b/python/Modulo-02-Estructuras/01-listas/ejemplo.py new file mode 100644 index 0000000..9a97171 --- /dev/null +++ b/python/Modulo-02-Estructuras/01-listas/ejemplo.py @@ -0,0 +1,61 @@ +# Las listas en acción: el tren de los datos. + +print("--- 1. CREAR UNA LISTA ---") + +# Creamos una lista con los nombres de 4 jugadores de un equipo. +# Observa los corchetes [ ] y las comas entre cada nombre. +equipo = ["Ana", "Bruno", "Carla", "Diego"] + +# Imprimimos la lista completa. Python la muestra con sus corchetes. +print("El equipo completo es:") +print(equipo) + +print("--- 2. ACCEDER A ELEMENTOS POR ÍNDICE ---") + +# RECUERDA: Python empieza a contar desde CERO. +# El primer jugador está en la posición 0, no en la 1. +jugador_uno = equipo[0] +print("El primer jugador (índice 0) es:") +print(jugador_uno) + +jugador_tres = equipo[2] +print("El tercer jugador (índice 2) es:") +print(jugador_tres) + +print("--- 3. CONOCER EL TAMAÑO CON len() ---") + +# len() nos dice cuántos elementos hay en la lista. +# Guardamos el resultado (un número entero) en una variable. +total_jugadores = len(equipo) +print("El número total de jugadores es:") +print(total_jugadores) + +print("--- 4. AÑADIR UN ELEMENTO CON .append() ---") + +# El equipo ficha a un nuevo jugador. Lo añadimos al final. +equipo.append("Elena") +print("Después de fichar a Elena, el equipo es:") +print(equipo) + +# El tamaño ahora cambió. +nuevo_total = len(equipo) +print("El nuevo total de jugadores es:") +print(nuevo_total) + +print("--- 5. ELIMINAR UN ELEMENTO CON .pop() ---") + +# Bruno (índice 1) se retira del equipo. Lo eliminamos. +equipo.pop(1) +print("Después de que Bruno se retiró, el equipo es:") +print(equipo) + +print("--- 6. COMBINANDO CON IF (del Nivel 05) ---") + +# ¿El equipo tiene suficientes jugadores para jugar (mínimo 4)? +jugadores_actuales = len(equipo) +if jugadores_actuales >= 4: + print("El equipo está completo y puede competir.") +else: + print("El equipo necesita más jugadores.") + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-02-Estructuras/01-listas/reto.md b/python/Modulo-02-Estructuras/01-listas/reto.md new file mode 100644 index 0000000..47483f2 --- /dev/null +++ b/python/Modulo-02-Estructuras/01-listas/reto.md @@ -0,0 +1,36 @@ +# Reto 06: La Lista de Películas 🎬 + +Vas a crear y administrar tu lista de películas favoritas. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-06.py` y sigue los pasos: + +1. Crea una lista llamada `peliculas` con exactamente **3 películas** de tu elección (texto `str`). +2. Imprime la lista completa. +3. Imprime **solo** la primera película usando su índice. +4. Imprime **solo** la última película usando su índice (recuerda: si hay 3 elementos, el último tiene índice `2`). +5. Añade una cuarta película a la lista usando `.append()`. +6. Imprime el nuevo total de películas usando `len()`. +7. Elimina la segunda película (índice `1`) usando `.pop()`. +8. Imprime la lista final. + +**Resultado esperado en tu terminal** (con tus propias películas): +```text +--- Mi Lista de Películas --- +['Interstellar', 'Matrix', 'El Padrino'] +--- Primera Película --- +Interstellar +--- Última Película --- +El Padrino +--- Añadiendo una nueva --- +4 +--- Lista Final (sin la segunda) --- +['Interstellar', 'El Padrino', 'Inception'] +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-02-Estructuras/01-listas/solucion/tu-usuario-06.py` +- **Windows:** `python python/Modulo-02-Estructuras/01-listas/solucion/tu-usuario-06.py` + +Si tu lista se construyó, creció y encogió sin errores, ¡pasaste al siguiente nivel! diff --git a/python/Modulo-02-Estructuras/01-listas/solucion/.gitkeep b/python/Modulo-02-Estructuras/01-listas/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/01-listas/teoria.md b/python/Modulo-02-Estructuras/01-listas/teoria.md new file mode 100644 index 0000000..35dd83c --- /dev/null +++ b/python/Modulo-02-Estructuras/01-listas/teoria.md @@ -0,0 +1,87 @@ +# Teoría: Listas — El vagón de tren de los datos + +Imagina una lista de compras en papel: está escrita en orden, puedes añadir cosas al final, tachar cosas del medio, y siempre sabes cuántos artículos hay contando los renglones. + +En Python, una **Lista** funciona exactamente igual. + +--- + +## Anatomía de una Lista + +Para crear una lista, usamos **corchetes** `[ ]` y separamos cada elemento con una **coma** `,`: + +```python +frutas = ["manzana", "plátano", "uva"] +``` + +Desglose de cada símbolo: + +- `frutas`: El nombre de la variable (la "etiqueta" de la caja, del Nivel 02). +- `=`: El símbolo de asignación/guardado (del Nivel 02). Guarda la lista en la variable. +- `[`: Abre la lista. Le dice a Python "aquí empieza la colección". +- `"manzana"`, `"plátano"`, `"uva"`: Los elementos de la lista. Cada uno es un dato (en este caso, texto `str` del Nivel 03). +- `,`: La coma **separa** un elemento del siguiente. Sin coma, Python pensaría que es todo un único elemento. +- `]`: Cierra la lista. + +--- + +## Cómo acceder a un elemento: el Índice + +Cada elemento de la lista tiene una **posición numérica** llamada **índice**. La regla más importante: + +> **Python empieza a contar desde CERO, no desde UNO.** + +```python +frutas = ["manzana", "plátano", "uva"] +# ^índice 0 ^índice 1 ^índice 2 +``` + +Para obtener un elemento, escribimos el nombre de la lista seguido del índice entre corchetes: + +```python +primera_fruta = frutas[0] # Guarda "manzana" +segunda_fruta = frutas[1] # Guarda "plátano" +``` + +- `frutas`: El nombre de la lista. +- `[0]`: El índice entre corchetes. Le dice a Python "dame el elemento en la posición 0". + +--- + +## Operaciones básicas + +### Conocer el tamaño: `len()` + +`len()` es una herramienta que Python ya tiene lista para ti. Le pasas una lista y te dice cuántos elementos tiene: + +```python +total = len(frutas) # total = 3 +``` + +- `len`: El nombre de la herramienta (la aprenderás a profundidad en módulos futuros). +- `(frutas)`: Los paréntesis le "entregan" la lista a `len` para que la cuente. + +### Añadir un elemento al final: `.append()` + +```python +frutas.append("naranja") +# La lista ahora es: ["manzana", "plátano", "uva", "naranja"] +``` + +- `.append(...)`: Un punto seguido de `append` y los datos entre paréntesis. El punto significa "hazle esto **a** la lista `frutas`". Por ahora memorízalo como la acción de "añadir al final". + +### Eliminar un elemento por posición: `.pop()` + +```python +frutas.pop(1) +# Elimina el elemento en la posición 1 ("plátano") +# La lista ahora es: ["manzana", "uva", "naranja"] +``` + +--- + +## ¿Qué pasa si me equivoco? + +- **Índice fuera de rango**: Si tu lista tiene 3 elementos (índices 0, 1, 2) y pides `frutas[5]`, Python lanzará un `IndexError: list index out of range`. Siempre verifica que el índice que pides exista. +- **Olvidar las comas**: `["manzana" "plátano"]` sin coma causará un `SyntaxError`. La coma es el separador obligatorio. +- **Confundir `( )` con `[ ]`**: Los paréntesis son para otras cosas. Para crear listas y acceder a elementos, siempre se usan corchetes `[ ]`. diff --git a/python/Modulo-02-Estructuras/02-tuplas/.gitkeep b/python/Modulo-02-Estructuras/02-tuplas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/02-tuplas/README.md b/python/Modulo-02-Estructuras/02-tuplas/README.md new file mode 100644 index 0000000..7640a7e --- /dev/null +++ b/python/Modulo-02-Estructuras/02-tuplas/README.md @@ -0,0 +1,13 @@ +# Nivel 07: Tuplas 🔒 + +Ya conoces las listas: colecciones que puedes modificar, añadir y eliminar elementos. + +Pero a veces necesitas una colección que **nadie pueda cambiar accidentalmente**: las coordenadas de un mapa, los días de la semana, las constantes de un sistema. Para eso existen las **Tuplas**. + +Una tupla es como una lista **sellada con candado**. Puedes leer su contenido, pero no puedes modificarlo una vez creada. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende la diferencia clave entre tuplas y listas. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` para ver cuándo usar cada una. +3. ⚔️ **Supera el reto**: Abre `reto.md` y demuestra que sabes elegir la estructura correcta. diff --git a/python/Modulo-02-Estructuras/02-tuplas/ejemplo.py b/python/Modulo-02-Estructuras/02-tuplas/ejemplo.py new file mode 100644 index 0000000..6d48aa5 --- /dev/null +++ b/python/Modulo-02-Estructuras/02-tuplas/ejemplo.py @@ -0,0 +1,56 @@ +# Las tuplas en acción: el acta sellada. + +print("--- 1. CREAR UNA TUPLA ---") + +# Los puntos cardinales nunca cambian. Son un dato fijo: usamos tupla. +# Observa los paréntesis ( ) en lugar de los corchetes [ ] de las listas. +puntos_cardinales = ("Norte", "Sur", "Este", "Oeste") + +print("Los puntos cardinales son:") +print(puntos_cardinales) + +print("--- 2. ACCEDER POR ÍNDICE (igual que las listas) ---") + +# El índice funciona exactamente igual que en las listas. +# Python sigue contando desde cero. +primer_punto = puntos_cardinales[0] +print("El primer punto cardinal (índice 0) es:") +print(primer_punto) + +tercer_punto = puntos_cardinales[2] +print("El tercer punto cardinal (índice 2) es:") +print(tercer_punto) + +print("--- 3. len() TAMBIÉN FUNCIONA EN TUPLAS ---") + +total = len(puntos_cardinales) +print("El total de puntos cardinales es:") +print(total) + +print("--- 4. COMPARANDO: LISTA vs TUPLA ---") + +# Una lista de tareas pendientes: SÍ puede cambiar → Lista +tareas_pendientes = ["comprar leche", "llamar al médico"] +tareas_pendientes.append("pagar renta") +print("Lista de tareas (puede cambiar):") +print(tareas_pendientes) + +# Las estaciones del año: NO cambian → Tupla +estaciones = ("Primavera", "Verano", "Otoño", "Invierno") +print("Estaciones del año (no cambian):") +print(estaciones) + +print("--- 5. IF CON TUPLAS: ¿Existe el dato que busco? ---") + +# Guardamos el punto buscado en una variable. +punto_buscado = "Sur" + +# El operador 'in' pregunta si un elemento está dentro de la colección. +# Devuelve True o False. Lo veremos a fondo más adelante; por ahora, obsérvalo. +esta_en_la_tupla = punto_buscado == puntos_cardinales[1] +if esta_en_la_tupla: + print("El punto Sur está en la posición 1 de la tupla.") +else: + print("No se encontró en esa posición.") + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-02-Estructuras/02-tuplas/reto.md b/python/Modulo-02-Estructuras/02-tuplas/reto.md new file mode 100644 index 0000000..d3e304a --- /dev/null +++ b/python/Modulo-02-Estructuras/02-tuplas/reto.md @@ -0,0 +1,34 @@ +# Reto 07: El Sistema de Coordenadas 🗺️ + +Vas a modelar la información de una tienda usando la estructura de datos correcta en cada caso. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-07.py` y sigue los pasos: + +1. Crea una **tupla** llamada `coordenadas_tienda` con dos números enteros que representen latitud y longitud (usa `19` y `-99` como valores de ejemplo). Las coordenadas de una tienda nunca cambian. +2. Crea una **lista** llamada `productos_disponibles` con 3 productos de texto (los que quieras). El inventario sí puede cambiar. +3. Imprime la tupla de coordenadas. +4. Imprime la lista de productos. +5. La tienda recibe un nuevo producto: añádelo a `productos_disponibles` con `.append()`. +6. Imprime el número total de productos con `len()`. +7. Imprime el primer producto de la lista (índice `0`). +8. Usa un `if` para preguntar si el total de productos (resultado de `len()`) es mayor a `3`. Si sí, imprime `"Inventario amplio"`; si no, imprime `"Inventario básico"`. + +**Resultado esperado en tu terminal:** +```text +--- Coordenadas de la Tienda --- +(19, -99) +--- Productos Disponibles --- +['Manzanas', 'Pan', 'Leche'] +--- Nuevo producto añadido. Total: --- +4 +--- Primer producto --- +Manzanas +--- Estado del inventario --- +Inventario amplio +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-02-Estructuras/02-tuplas/solucion/tu-usuario-07.py` +- **Windows:** `python python/Modulo-02-Estructuras/02-tuplas/solucion/tu-usuario-07.py` diff --git a/python/Modulo-02-Estructuras/02-tuplas/solucion/.gitkeep b/python/Modulo-02-Estructuras/02-tuplas/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/02-tuplas/teoria.md b/python/Modulo-02-Estructuras/02-tuplas/teoria.md new file mode 100644 index 0000000..6f20431 --- /dev/null +++ b/python/Modulo-02-Estructuras/02-tuplas/teoria.md @@ -0,0 +1,67 @@ +# Teoría: Tuplas — La colección con candado + +Imagina que grabas un acta notarial: una vez firmada y sellada, nadie puede cambiar ni una coma. Los datos son permanentes e inmutables. Eso es una tupla. + +--- + +## Anatomía de una Tupla + +La diferencia visual con la lista es solo un símbolo: en lugar de `[ ]` (corchetes), usamos `( )` (paréntesis): + +```python +dias_semana = ("Lunes", "Martes", "Miércoles", "Jueves", "Viernes") +``` + +Desglose: + +- `dias_semana`: El nombre de la variable. +- `=`: Asignación/guardado. +- `(`: Abre la tupla. +- `"Lunes"`, `"Martes"`, ...: Los elementos, separados por `,`. +- `)`: Cierra la tupla. + +--- + +## Acceder a elementos: igual que las listas + +Los índices funcionan exactamente igual — Python sigue contando desde cero: + +```python +dias_semana = ("Lunes", "Martes", "Miércoles", "Jueves", "Viernes") + +primer_dia = dias_semana[0] # "Lunes" +ultimo_dia = dias_semana[4] # "Viernes" +total_dias = len(dias_semana) # 5 +``` + +`len()` también funciona con tuplas. + +--- + +## La diferencia fundamental: inmutabilidad + +La palabra **inmutable** significa "que no puede ser cambiado". Una vez que creas una tupla, no puedes: + +- Añadirle elementos (no existe `.append()` en las tuplas). +- Eliminar elementos (no existe `.pop()` en las tuplas). +- Cambiar el valor de uno de sus elementos. + +Si lo intentas, Python lanzará un `TypeError: 'tuple' object does not support item assignment`. + +--- + +## ¿Cuándo usar Lista y cuándo Tupla? + +| Pregunta | Usa... | +|---|---| +| ¿Los datos pueden cambiar? (ej. lista de usuarios) | **Lista** `[ ]` | +| ¿Los datos son fijos para siempre? (ej. días de la semana, coordenadas GPS) | **Tupla** `( )` | + +La tupla también es más rápida de leer para la computadora porque sabe que nunca cambiará. Es una señal de intención: le dices al equipo "estos datos no deben modificarse". + +--- + +## ¿Qué pasa si me equivoco? + +- **Intentar modificar una tupla**: `TypeError`. Si necesitas modificar los datos, es señal de que debiste usar una Lista. +- **Tupla de un solo elemento**: `(42)` no es una tupla, Python lo interpreta como el número `42` entre paréntesis. Para crear una tupla de un solo elemento, debes poner una coma al final: `(42,)`. El `,` es la clave. diff --git a/python/Modulo-02-Estructuras/03-diccionarios/.gitkeep b/python/Modulo-02-Estructuras/03-diccionarios/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/03-diccionarios/README.md b/python/Modulo-02-Estructuras/03-diccionarios/README.md new file mode 100644 index 0000000..f369630 --- /dev/null +++ b/python/Modulo-02-Estructuras/03-diccionarios/README.md @@ -0,0 +1,13 @@ +# Nivel 08: Diccionarios 📖 + +Las listas guardan datos en orden por posición (índice `0`, `1`, `2`...). Pero a veces el orden no importa; lo que importa es el **nombre** del dato. + +¿Cómo guardas la información de un usuario: su nombre, su edad, su email? Con una lista tendrías que recordar que el email está en la posición `2`. Eso es un caos. + +Los **Diccionarios** guardan datos como pares `clave: valor`, igual que un diccionario de papel: buscas la **palabra** (clave) y obtienes su **definición** (valor). + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende la sintaxis de clave-valor. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y ve cómo modelar un perfil de usuario real. +3. ⚔️ **Supera el reto**: Abre `reto.md` y crea tu propio diccionario. diff --git a/python/Modulo-02-Estructuras/03-diccionarios/ejemplo.py b/python/Modulo-02-Estructuras/03-diccionarios/ejemplo.py new file mode 100644 index 0000000..3082323 --- /dev/null +++ b/python/Modulo-02-Estructuras/03-diccionarios/ejemplo.py @@ -0,0 +1,59 @@ +# Los diccionarios en acción: la ficha del jugador. + +print("--- 1. CREAR UN DICCIONARIO ---") + +# Modelamos la ficha de un jugador de videojuego. +# Cada clave (entre comillas) describe el dato. El valor puede ser str, int o bool. +jugador = { + "alias": "DragonX", + "nivel": 42, + "vida": 100, + "esta_conectado": True +} + +# Imprimimos el diccionario completo. Python lo muestra con sus llaves { }. +print("Ficha completa del jugador:") +print(jugador) + +print("--- 2. ACCEDER A VALORES POR CLAVE ---") + +# En lugar de un número, usamos el nombre de la clave entre corchetes. +alias = jugador["alias"] +print("El alias del jugador es:") +print(alias) + +vida_actual = jugador["vida"] +print("La vida actual del jugador es:") +print(vida_actual) + +print("--- 3. MODIFICAR UN VALOR EXISTENTE ---") + +# El jugador sube de nivel. Modificamos el valor de la clave "nivel". +# La sintaxis es igual que asignar una variable, pero con la clave como destino. +jugador["nivel"] = 43 +print("El jugador subió de nivel. Su nivel ahora es:") +print(jugador["nivel"]) + +print("--- 4. AÑADIR UNA CLAVE NUEVA ---") + +# El jugador consigue una habilidad especial. Creamos una nueva clave. +jugador["habilidad"] = "Teletransportación" +print("Se añadió la habilidad. La ficha actualizada es:") +print(jugador) + +print("--- 5. ELIMINAR UNA CLAVE CON .pop() ---") + +# El jugador se desconecta. Eliminamos el estado de conexión. +jugador.pop("esta_conectado") +print("El jugador se desconectó. La ficha sin ese campo es:") +print(jugador) + +print("--- 6. IF CON DICCIONARIO ---") + +# Usamos el valor de una clave en una condición. +if jugador["vida"] > 50: + print("El jugador tiene buena salud para continuar.") +else: + print("El jugador necesita curarse.") + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-02-Estructuras/03-diccionarios/reto.md b/python/Modulo-02-Estructuras/03-diccionarios/reto.md new file mode 100644 index 0000000..686b1e9 --- /dev/null +++ b/python/Modulo-02-Estructuras/03-diccionarios/reto.md @@ -0,0 +1,38 @@ +# Reto 08: La Ficha del Héroe 🦸 + +Vas a crear y administrar la ficha de un superhéroe usando un diccionario. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-08.py` y sigue los pasos: + +1. Crea un diccionario llamado `heroe` con las siguientes claves y valores: + - `"nombre"`: el nombre de tu héroe (texto). + - `"poder"`: su superpoder (texto). + - `"energia"`: `80` (número entero). + - `"derrotado"`: `False` (booleano). +2. Imprime el diccionario completo. +3. Imprime solo el valor de `"nombre"` usando su clave. +4. El héroe usa su poder y pierde 30 de energía. Modifica la clave `"energia"` restándole `30` (usa el operador `-` del Nivel 04). +5. Imprime el nuevo valor de `"energia"`. +6. Añade una nueva clave `"ciudad"` con la ciudad que protege tu héroe. +7. Usa un `if` para revisar si la `"energia"` del héroe es menor a `60`. Si sí: imprime `"El héroe necesita descansar."`; si no: imprime `"El héroe puede seguir luchando."`. +8. Imprime el diccionario final completo. + +**Resultado esperado en tu terminal:** +```text +--- Ficha del Héroe --- +{'nombre': 'Vulcano', 'poder': 'Control del fuego', 'energia': 80, 'derrotado': False} +--- Nombre --- +Vulcano +--- Energía tras usar el poder --- +50 +--- Estado del héroe --- +El héroe necesita descansar. +--- Ficha Final --- +{'nombre': 'Vulcano', 'poder': 'Control del fuego', 'energia': 50, 'derrotado': False, 'ciudad': 'Guadalajara'} +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-02-Estructuras/03-diccionarios/solucion/tu-usuario-08.py` +- **Windows:** `python python/Modulo-02-Estructuras/03-diccionarios/solucion/tu-usuario-08.py` diff --git a/python/Modulo-02-Estructuras/03-diccionarios/solucion/.gitkeep b/python/Modulo-02-Estructuras/03-diccionarios/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/03-diccionarios/teoria.md b/python/Modulo-02-Estructuras/03-diccionarios/teoria.md new file mode 100644 index 0000000..4c6a021 --- /dev/null +++ b/python/Modulo-02-Estructuras/03-diccionarios/teoria.md @@ -0,0 +1,80 @@ +# Teoría: Diccionarios — El fichero de datos etiquetado + +Imagina la ficha de un paciente en un hospital: tiene campos etiquetados como "Nombre:", "Edad:", "Tipo de sangre:". Para saber la edad, buscas la etiqueta "Edad", no tienes que contar cuál es el tercer renglón. + +En Python, un **Diccionario** funciona igual: cada dato tiene una **clave** (la etiqueta) y un **valor** (el dato). + +--- + +## Anatomía de un Diccionario + +Usamos **llaves** `{ }` para crear un diccionario: + +```python +usuario = { + "nombre": "Kaia", + "edad": 25, + "activo": True +} +``` + +Desglose de cada símbolo: + +- `usuario`: El nombre de la variable. +- `=`: Asignación/guardado. +- `{`: Abre el diccionario. +- `"nombre"`: La **clave** (siempre texto, entre comillas). +- `:`: El símbolo que **separa** la clave de su valor. Léelo como "tiene el valor de...". +- `"Kaia"`: El **valor** asociado a la clave `"nombre"`. Puede ser cualquier tipo de dato (`str`, `int`, `bool`, etc.). +- `,`: Separa un par clave-valor del siguiente. +- `}`: Cierra el diccionario. + +--- + +## Acceder a un valor: usar la clave entre corchetes + +Para obtener un valor, en lugar de un índice numérico, usamos el nombre de la clave entre corchetes `[ ]`: + +```python +nombre_del_usuario = usuario["nombre"] # "Kaia" +edad_del_usuario = usuario["edad"] # 25 +``` + +--- + +## Modificar un valor existente + +La sintaxis es la misma que para asignar a una variable, pero apuntando a la clave: + +```python +usuario["edad"] = 26 +# El diccionario ahora tiene "edad": 26 +``` + +--- + +## Añadir una clave nueva + +Si usas una clave que no existía, Python la crea automáticamente: + +```python +usuario["email"] = "kaia@ejemplo.com" +# El diccionario ahora tiene una nueva clave "email" +``` + +--- + +## Eliminar una clave: `.pop()` + +```python +usuario.pop("activo") +# La clave "activo" y su valor desaparecen del diccionario. +``` + +--- + +## ¿Qué pasa si me equivoco? + +- **Clave que no existe**: Si pides `usuario["telefono"]` y esa clave no existe, Python lanzará un `KeyError`. Siempre asegúrate de que la clave exista antes de pedirla. +- **Olvidar los dos puntos `:`**: `"nombre" "Kaia"` sin los dos puntos causará `SyntaxError`. +- **Claves duplicadas**: Si escribes la misma clave dos veces, Python solo guarda la última. `{"color": "rojo", "color": "azul"}` resultará en `{"color": "azul"}`. Cada clave debe ser única. diff --git a/python/Modulo-02-Estructuras/04-bucle-for/.gitkeep b/python/Modulo-02-Estructuras/04-bucle-for/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/04-bucle-for/README.md b/python/Modulo-02-Estructuras/04-bucle-for/README.md new file mode 100644 index 0000000..e89437c --- /dev/null +++ b/python/Modulo-02-Estructuras/04-bucle-for/README.md @@ -0,0 +1,13 @@ +# Nivel 09: Bucle for 🔁 + +Tienes una lista de 100 productos y necesitas imprimir cada uno. ¿Escribes 100 líneas `print()`? Nunca. + +El **bucle `for`** le dice a Python: "repite estas instrucciones una vez **por cada elemento** de esta colección". Es la herramienta más poderosa que has aprendido hasta ahora. + +Con el `for`, lo que tomaba 100 líneas de código se reduce a 2. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y entiende la anatomía del bucle `for`. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y observa cómo recorre listas y rangos. +3. ⚔️ **Supera el reto**: Abre `reto.md` y escribe tus propios bucles. diff --git a/python/Modulo-02-Estructuras/04-bucle-for/ejemplo.py b/python/Modulo-02-Estructuras/04-bucle-for/ejemplo.py new file mode 100644 index 0000000..9820e27 --- /dev/null +++ b/python/Modulo-02-Estructuras/04-bucle-for/ejemplo.py @@ -0,0 +1,50 @@ +# El bucle for en acción: recorriendo colecciones sin esfuerzo. + +print("--- 1. FOR CON UNA LISTA ---") + +# Tenemos una lista de planetas del sistema solar. +planetas = ["Mercurio", "Venus", "Tierra", "Marte"] + +# El bucle recorre la lista. En cada vuelta, 'planeta' guarda el elemento actual. +# No necesitamos saber los índices. El for se encarga de todo. +for planeta in planetas: + print(planeta) + +print("--- 2. FOR CON range(): CONTANDO NÚMEROS ---") + +# range(5) genera los números del 0 al 4 (5 no incluido). +# Útil para repetir algo un número exacto de veces. +print("Contando del 0 al 4:") +for numero in range(5): + print(numero) + +print("--- 3. range(inicio, fin): CONTROLANDO EL PUNTO DE PARTIDA ---") + +# range(1, 6) genera 1, 2, 3, 4, 5. El inicio está incluido, el fin NO. +print("Contando del 1 al 5:") +for numero in range(1, 6): + print(numero) + +print("--- 4. FOR + IF: FILTRANDO DENTRO DEL BUCLE ---") + +# Recorremos los planetas y con un if filtramos cuál nos interesa. +# El if está DENTRO del for (doble indentación: 4 + 4 = 8 espacios total). +puntuaciones = [88, 45, 72, 95, 30, 61] + +print("Puntuaciones aprobadas (70 o más):") +for puntuacion in puntuaciones: + if puntuacion >= 70: + # Este print está dentro del if, que está dentro del for. + # Necesita 8 espacios de indentación. + print(puntuacion) + +print("--- 5. FOR CON UNA TUPLA ---") + +# El for también funciona con tuplas. La sintaxis es idéntica. +dias_laborales = ("Lunes", "Martes", "Miércoles", "Jueves", "Viernes") + +print("Los días de trabajo son:") +for dia in dias_laborales: + print(dia) + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-02-Estructuras/04-bucle-for/reto.md b/python/Modulo-02-Estructuras/04-bucle-for/reto.md new file mode 100644 index 0000000..fde2a10 --- /dev/null +++ b/python/Modulo-02-Estructuras/04-bucle-for/reto.md @@ -0,0 +1,39 @@ +# Reto 09: El Marcador de Goles ⚽ + +Vas a procesar los goles de un torneo usando bucles y condicionales. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-09.py` y sigue los pasos: + +1. Crea una lista llamada `goles_por_partido` con los siguientes 6 valores enteros: `[2, 0, 5, 1, 3, 0]`. Cada número representa los goles marcados en un partido. +2. Usa un bucle `for` para imprimir cada número de la lista, uno por línea. +3. Fuera del bucle anterior, usa `range()` para imprimir los números del `1` al `6` (simulando el número de partido). +4. Crea una variable `partidos_ganados` con valor `0` (entero). Luego usa un `for` con un `if` dentro para recorrer `goles_por_partido`: si los goles de ese partido son mayores a `1`, súmale `1` a `partidos_ganados` (usa `partidos_ganados = partidos_ganados + 1`). Al terminar el bucle, imprime `partidos_ganados`. +5. Usa un `if` para revisar si `partidos_ganados` es mayor o igual a `3`. Si sí: imprime `"Clasificado a la siguiente ronda"`; si no: `"Eliminado del torneo"`. + +**Resultado esperado en tu terminal:** +```text +--- Goles por partido --- +2 +0 +5 +1 +3 +0 +--- Número de partidos --- +1 +2 +3 +4 +5 +6 +--- Partidos ganados (más de 1 gol) --- +3 +--- Resultado del torneo --- +Clasificado a la siguiente ronda +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-02-Estructuras/04-bucle-for/solucion/tu-usuario-09.py` +- **Windows:** `python python/Modulo-02-Estructuras/04-bucle-for/solucion/tu-usuario-09.py` diff --git a/python/Modulo-02-Estructuras/04-bucle-for/solucion/.gitkeep b/python/Modulo-02-Estructuras/04-bucle-for/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/04-bucle-for/teoria.md b/python/Modulo-02-Estructuras/04-bucle-for/teoria.md new file mode 100644 index 0000000..92c2f45 --- /dev/null +++ b/python/Modulo-02-Estructuras/04-bucle-for/teoria.md @@ -0,0 +1,70 @@ +# Teoría: El Bucle for — La cinta transportadora + +Imagina la cinta de un supermercado: pones todos tus productos uno por uno, y el cajero los procesa de izquierda a derecha, uno a la vez, hasta que no quedan más. Eso es exactamente un bucle `for`. + +--- + +## Anatomía del `for` + +```python +for elemento in coleccion: + accion_con_elemento +``` + +Desglose de cada pieza: + +- **`for`**: Palabra reservada. Significa "para cada". +- **`elemento`**: Un nombre de variable que **tú inventas**. Python usará este nombre para guardar el elemento actual en cada vuelta del bucle. Puedes llamarlo como quieras (`fruta`, `nombre`, `item`, `x`). +- **`in`**: Palabra reservada. Significa "dentro de". Conecta la variable temporal con la colección. +- **`coleccion`**: La lista, tupla u otra colección que vas a recorrer. +- **`:`**: Los dos puntos obligatorios al final de la línea, igual que en el `if`. +- **` accion`**: El bloque de código indentado (4 espacios) que se ejecuta en **cada vuelta**. + +--- + +## Ejemplo básico + +```python +frutas = ["manzana", "plátano", "uva"] + +for fruta in frutas: + print(fruta) +``` + +**Lo que pasa internamente, vuelta por vuelta:** + +| Vuelta | `fruta` vale... | Se ejecuta | +|--------|----------------|-----------| +| 1ª | `"manzana"` | `print("manzana")` | +| 2ª | `"plátano"` | `print("plátano")` | +| 3ª | `"uva"` | `print("uva")` | +| Fin | La lista se acabó. El bucle termina. | — | + +--- + +## `range()`: contar números sin crear una lista + +¿Qué pasa si quieres repetir algo un número exacto de veces? `range()` genera una secuencia de números para ti: + +```python +for numero in range(5): + print(numero) +# Imprime: 0, 1, 2, 3, 4 +``` + +- **`range(5)`**: Genera los números `0, 1, 2, 3, 4`. Empieza en 0 y llega **hasta antes** del número indicado (no incluye el 5). +- **`range(1, 6)`**: Genera `1, 2, 3, 4, 5`. El primer número es el inicio (incluido), el segundo es el tope (excluido). + +--- + +## ¡El `for` también tiene indentación! + +Al igual que el `if`, todo lo que quieras que se repita **debe ir con 4 espacios** dentro del bloque del `for`. Si olvidas la indentación, obtendrás un `IndentationError`. + +--- + +## ¿Qué pasa si me equivoco? + +- **No indentar**: `IndentationError`. El código "dentro" del `for` necesita sus 4 espacios. +- **Olvidar el `:`**: `SyntaxError`. Los dos puntos después de `in coleccion` son obligatorios. +- **Modificar la lista mientras la recorres**: Es una práctica peligrosa que puede causar comportamientos inesperados. Evítalo por ahora. diff --git a/python/Modulo-02-Estructuras/05-bucle-while/.gitkeep b/python/Modulo-02-Estructuras/05-bucle-while/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/05-bucle-while/README.md b/python/Modulo-02-Estructuras/05-bucle-while/README.md new file mode 100644 index 0000000..1f35d60 --- /dev/null +++ b/python/Modulo-02-Estructuras/05-bucle-while/README.md @@ -0,0 +1,13 @@ +# Nivel 10: Bucle while ⏳ + +El `for` repite código **un número conocido de veces** (una vez por cada elemento de una lista). + +Pero ¿qué pasa cuando **no sabes cuántas veces** se repetirá? Por ejemplo: "sigue pidiendo la contraseña mientras el usuario se equivoque" o "sigue disparando mientras el personaje tenga munición". + +El **bucle `while`** repite un bloque de código **mientras** una condición sea `True`. Se detiene cuando la condición se vuelve `False`. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende la diferencia entre `for` y `while`. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y observa cómo el `while` se detiene solo. +3. ⚔️ **Supera el reto**: Abre `reto.md` y programa tu propia cuenta regresiva. diff --git a/python/Modulo-02-Estructuras/05-bucle-while/ejemplo.py b/python/Modulo-02-Estructuras/05-bucle-while/ejemplo.py new file mode 100644 index 0000000..e6096e9 --- /dev/null +++ b/python/Modulo-02-Estructuras/05-bucle-while/ejemplo.py @@ -0,0 +1,65 @@ +# El bucle while en acción: contadores y condiciones dinámicas. + +print("--- 1. CUENTA REGRESIVA CLÁSICA ---") + +# Partimos de 5 y contamos hasta 0. +# La condición 'contador > 0' se revisa al inicio de CADA vuelta. +contador = 5 + +while contador > 0: + print(contador) + # CRÍTICO: restamos 1 en cada vuelta para que el bucle avance. + # Si olvidamos esta línea, el programa se congela. + contador = contador - 1 + +print("¡Despegue!") + +print("--- 2. ACUMULADOR: SUMANDO MIENTRAS SE PUEDE ---") + +# Un personaje recolecta monedas. Sigue recolectando mientras tenga energía. +energia = 100 +monedas = 0 + +# Cada vuelta gasta 25 de energía y gana 10 monedas. +while energia > 0: + energia = energia - 25 + monedas = monedas + 10 + +print("El personaje se quedó sin energía.") +print("Monedas recolectadas:") +print(monedas) +print("Energía final:") +print(energia) + +print("--- 3. while CON UNA LISTA: VACIANDO LA COLA ---") + +# Una lista de tareas pendientes. Seguimos trabajando mientras haya tareas. +tareas = ["Diseñar logo", "Escribir código", "Probar la app"] + +print("Procesando tareas...") +while len(tareas) > 0: + # .pop() sin argumento elimina y devuelve el ÚLTIMO elemento. + # Guardamos lo que eliminamos para poder imprimirlo. + tarea_actual = tareas.pop() + print("Completando:") + print(tarea_actual) + +print("Todas las tareas completadas.") +print("Lista de tareas restantes:") +print(tareas) + +print("--- 4. while + if: CONDICIÓN COMPUESTA ---") + +# Vaciamos una barra de vida con condiciones adicionales. +vida = 80 + +while vida > 0: + vida = vida - 30 + # Revisamos si la vida llegó a 0 o menos (evitamos números negativos en el mensaje). + if vida <= 0: + print("El personaje ha caído. Vida final: 0") + else: + print("Vida restante:") + print(vida) + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-02-Estructuras/05-bucle-while/reto.md b/python/Modulo-02-Estructuras/05-bucle-while/reto.md new file mode 100644 index 0000000..125bbb1 --- /dev/null +++ b/python/Modulo-02-Estructuras/05-bucle-while/reto.md @@ -0,0 +1,38 @@ +# Reto 10: El Reactor Nuclear 🔋 + +Debes programar el sistema de control de un reactor. El reactor tiene combustible y debes gestionar cuántas veces puede operar antes de agotarse. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-10.py` y sigue los pasos: + +1. Crea una variable `combustible` con el valor `100` (entero). +2. Crea una variable `ciclos_completados` con el valor `0` (entero). +3. Escribe un bucle `while` que se ejecute **mientras** `combustible` sea **mayor que** `0`. +4. Dentro del bucle: + - Resta `35` al `combustible` en cada ciclo. + - Suma `1` a `ciclos_completados`. + - Usa un `if` para revisar si `combustible > 0`. Si sí: imprime el valor actual de `combustible`. Si no: imprime `"Combustible agotado."`. +5. Fuera del bucle, imprime el total de `ciclos_completados`. +6. Usa un `if` para revisar si `ciclos_completados` es mayor o igual a `3`. Si sí: imprime `"El reactor operó de forma estable."`; si no: `"El reactor necesita más combustible."`. + +**Resultado esperado en tu terminal:** +```text +--- Estado del Reactor --- +--- Ciclo 1 --- +65 +--- Ciclo 2 --- +30 +--- Ciclo 3 --- +Combustible agotado. +--- Total de ciclos --- +3 +--- Diagnóstico --- +El reactor operó de forma estable. +``` + +> **Pista:** Para imprimir el número de ciclo dentro del bucle (`--- Ciclo 1 ---`, etc.), necesitas imprimir `ciclos_completados` después de haberle sumado `1`. Esto es perfectamente válido con lo que ya sabes. + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-02-Estructuras/05-bucle-while/solucion/tu-usuario-10.py` +- **Windows:** `python python/Modulo-02-Estructuras/05-bucle-while/solucion/tu-usuario-10.py` diff --git a/python/Modulo-02-Estructuras/05-bucle-while/solucion/.gitkeep b/python/Modulo-02-Estructuras/05-bucle-while/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/05-bucle-while/teoria.md b/python/Modulo-02-Estructuras/05-bucle-while/teoria.md new file mode 100644 index 0000000..932e4d6 --- /dev/null +++ b/python/Modulo-02-Estructuras/05-bucle-while/teoria.md @@ -0,0 +1,80 @@ +# Teoría: El Bucle while — El guardia que no se mueve hasta que cambia algo + +Imagina un guardia en la entrada de una sala VIP. Su instrucción es: **"Mientras la sala esté llena, no dejes entrar a nadie."** El guardia no sabe cuánto tiempo tardará en haber espacio; simplemente sigue revisando hasta que la condición cambie. + +Eso es el `while`. + +--- + +## Anatomía del `while` + +```python +while condicion: + accion_que_se_repite +``` + +Desglose: + +- **`while`**: Palabra reservada. Significa "mientras". +- **`condicion`**: Una expresión que devuelve `True` o `False` (igual que en el `if` del Nivel 05). Mientras sea `True`, el bucle continúa. Cuando se vuelve `False`, el bucle se detiene. +- **`:`**: Los dos puntos obligatorios. +- **` accion`**: El bloque indentado (4 espacios) que se repite. + +--- + +## Ejemplo básico: la cuenta regresiva + +```python +contador = 5 + +while contador > 0: + print(contador) + contador = contador - 1 + +print("¡Despegue!") +``` + +**Lo que pasa vuelta por vuelta:** + +| Vuelta | `contador` | ¿`contador > 0`? | Acción | +|--------|-----------|-----------------|--------| +| 1ª | `5` | `True` | Imprime `5`, luego `contador` pasa a `4` | +| 2ª | `4` | `True` | Imprime `4`, luego `contador` pasa a `3` | +| ... | ... | ... | ... | +| 5ª | `1` | `True` | Imprime `1`, luego `contador` pasa a `0` | +| Fin | `0` | `False` | El bucle se detiene. Se imprime `"¡Despegue!"` | + +--- + +## La trampa mortal: el bucle infinito + +Si la condición del `while` **nunca se vuelve `False`**, el programa se queda atrapado para siempre. A esto se llama **bucle infinito** y es el error más peligroso del `while`: + +```python +# PELIGRO: bucle infinito. contador nunca cambia. +contador = 5 +while contador > 0: + print(contador) + # FALTA: contador = contador - 1 +``` + +Si ejecutas esto por accidente, presiona `Ctrl + C` en la terminal para detener el programa a la fuerza. + +**Regla de oro:** Siempre asegúrate de que algo dentro del `while` modifique la variable de la condición para que eventualmente se vuelva `False`. + +--- + +## `for` vs `while` — ¿Cuándo usar cada uno? + +| Situación | Usa... | +|---|---| +| Sé exactamente cuántas veces necesito repetir (recorrer una lista, un rango) | **`for`** | +| No sé cuántas veces se repetirá; depende de una condición que puede cambiar | **`while`** | + +--- + +## ¿Qué pasa si me equivoco? + +- **Bucle infinito**: El programa se congela. Usa `Ctrl + C` para salir. +- **Olvidar actualizar el contador**: El error más común del `while`. La condición nunca cambia → bucle infinito. +- **Olvidar el `:`**: `SyntaxError`, igual que en el `if` y el `for`. diff --git a/python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/.gitkeep b/python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/README.md b/python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/README.md new file mode 100644 index 0000000..9fdf52a --- /dev/null +++ b/python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/README.md @@ -0,0 +1,84 @@ +# Reto Final: Módulo 02 — Estructuras 🏆 + +Llegaste al final del Módulo 02. Este reto integra **todo** lo aprendido: listas, tuplas, diccionarios, `for` y `while`. + +No hay instrucciones paso a paso. Solo el enunciado y el resultado esperado. Tú decides el camino. + +--- + +## El Problema: Sistema de Inventario de una Nave Espacial 🚀 + +Eres el programador de una nave espacial. Tu misión es gestionar el inventario de suministros, analizar los datos y emitir un reporte de estado. + +### Datos iniciales (define estos valores al inicio de tu archivo): + +- Una **tupla** llamada `nombre_nave` con dos elementos: el nombre `"Apolo-7"` y el año de lanzamiento `2031`. (Los datos de identidad de la nave nunca cambian.) +- Una **lista** llamada `suministros` con los siguientes 4 elementos de texto: `"Agua"`, `"Comida"`, `"Oxígeno"`, `"Combustible"`. +- Un **diccionario** llamado `estado_nave` con las siguientes claves: + - `"escudos"`: `100` (entero) + - `"tripulantes"`: `6` (entero) + - `"en_orbita"`: `True` (booleano) + +### Lógica que debes programar: + +1. **Sección Identidad**: Imprime el nombre de la nave (índice `0` de la tupla) y el año de lanzamiento (índice `1`). + +2. **Sección Inventario**: Usa un bucle `for` para imprimir cada elemento de la lista `suministros`. + +3. **Sección Baja de suministro**: La nave consume `"Combustible"`. Elimínalo de la lista con `.pop()` usando el índice `3`. Luego imprime la lista actualizada. + +4. **Sección Estado**: Imprime los valores de `"escudos"` y `"tripulantes"` del diccionario. + +5. **Sección Daño**: La nave recibió impactos. Usa un bucle `while` que reste `30` a `"escudos"` mientras `estado_nave["escudos"]` sea **mayor que** `0`. En cada vuelta imprime el valor actual de escudos (si es mayor a 0) o `"Escudos destruidos."` si llega a 0 o menos. + +6. **Sección Veredicto**: Usa un `if` sobre el valor final de `estado_nave["escudos"]`: + - Si los escudos son menores a `0`: imprime `"La nave ha sido destruida."`. + - Si son exactamente `0`: imprime `"La nave sobrevivió por los pelos."`. + - Si son mayores a `0`: imprime `"La nave está operativa."`. + +--- + +## Resultado esperado en tu terminal + +```text +--- Identidad de la Nave --- +Apolo-7 +2031 +--- Inventario Actual --- +Agua +Comida +Oxígeno +Combustible +--- Inventario tras consumir Combustible --- +['Agua', 'Comida', 'Oxígeno'] +--- Estado de la Nave --- +100 +6 +--- Impactos recibidos --- +70 +40 +10 +Escudos destruidos. +--- Veredicto --- +La nave ha sido destruida. +``` + +--- + +## Criterios de éxito ✓ + +- [ ] El código corre sin ningún error. +- [ ] Usaste una tupla para la identidad de la nave. +- [ ] Usaste una lista para los suministros y la modificaste con `.pop()`. +- [ ] Usaste un diccionario para el estado de la nave. +- [ ] Usaste `for` para recorrer la lista de suministros. +- [ ] Usaste `while` para simular los impactos. +- [ ] El veredicto final coincide con el resultado del bucle `while`. + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-final.py`. + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/solucion/tu-usuario-final.py` +- **Windows:** `python python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/solucion/tu-usuario-final.py` + +¡Si logras esto, dominas las estructuras de datos de Python. El Módulo 03 te espera! diff --git a/python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/solucion/.gitkeep b/python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/.gitkeep b/python/Modulo-02-Flujo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/11-operadores-logicos/.gitkeep b/python/Modulo-02-Flujo/11-operadores-logicos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/12-comparadores/.gitkeep b/python/Modulo-02-Flujo/12-comparadores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/13-if/.gitkeep b/python/Modulo-02-Flujo/13-if/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/14-else-elif/.gitkeep b/python/Modulo-02-Flujo/14-else-elif/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/15-logica-anidada/.gitkeep b/python/Modulo-02-Flujo/15-logica-anidada/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/16-truthy-falsy/.gitkeep b/python/Modulo-02-Flujo/16-truthy-falsy/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/17-match-case/.gitkeep b/python/Modulo-02-Flujo/17-match-case/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/18-depuracion/.gitkeep b/python/Modulo-02-Flujo/18-depuracion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/19-optimizacion/.gitkeep b/python/Modulo-02-Flujo/19-optimizacion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-02-Flujo/20-RETO-portero/.gitkeep b/python/Modulo-02-Flujo/20-RETO-portero/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/.gitkeep b/python/Modulo-03-Estructuras/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/21-listas/.gitkeep b/python/Modulo-03-Estructuras/21-listas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/22-metodos-lista/.gitkeep b/python/Modulo-03-Estructuras/22-metodos-lista/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/23-slicing/.gitkeep b/python/Modulo-03-Estructuras/23-slicing/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/24-tuplas/.gitkeep b/python/Modulo-03-Estructuras/24-tuplas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/25-diccionarios/.gitkeep b/python/Modulo-03-Estructuras/25-diccionarios/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/26-sets/.gitkeep b/python/Modulo-03-Estructuras/26-sets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/27-estructuras-anidadas/.gitkeep b/python/Modulo-03-Estructuras/27-estructuras-anidadas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/28-mutabilidad/.gitkeep b/python/Modulo-03-Estructuras/28-mutabilidad/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/29-deepcopy/.gitkeep b/python/Modulo-03-Estructuras/29-deepcopy/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Estructuras/30-RETO-inventario/.gitkeep b/python/Modulo-03-Estructuras/30-RETO-inventario/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/01-funciones/.gitkeep b/python/Modulo-03-Exterior/01-funciones/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/01-funciones/README.md b/python/Modulo-03-Exterior/01-funciones/README.md new file mode 100644 index 0000000..c191c3d --- /dev/null +++ b/python/Modulo-03-Exterior/01-funciones/README.md @@ -0,0 +1,13 @@ +# Nivel 11: Funciones 🧰 + +Hasta ahora, cada vez que necesitabas repetir una tarea, copiabas el código. Si lo necesitabas en 10 sitios distintos, lo copiabas 10 veces. Eso es un caos: si hay un error, lo tienes que corregir 10 veces. + +Las **funciones** resuelven eso: defines el código una sola vez, le pones un nombre, y lo llamas cuantas veces quieras desde cualquier parte del programa. + +En el mundo real, cada botón de una aplicación llama a una función. Sin funciones, no existe ningún software moderno. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende a definir y llamar funciones. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` para ver cómo las funciones organizan el código. +3. ⚔️ **Supera el reto**: Abre `reto.md` y escribe tus propias funciones. diff --git a/python/Modulo-03-Exterior/01-funciones/ejemplo.py b/python/Modulo-03-Exterior/01-funciones/ejemplo.py new file mode 100644 index 0000000..c7c64e9 --- /dev/null +++ b/python/Modulo-03-Exterior/01-funciones/ejemplo.py @@ -0,0 +1,68 @@ +# Las funciones en acción: recetas de código reutilizables. + +print("--- 1. FUNCIÓN SIN PARÁMETROS ---") + +# Definimos la función. Esto NO la ejecuta todavía. +# Es como escribir la receta, sin cocinar aún. +def mostrar_bienvenida(): + print("============================") + print(" Bienvenido al sistema ") + print("============================") + +# Aquí SÍ la ejecutamos. Los () le dicen a Python: "¡corre ahora!". +mostrar_bienvenida() + +# La llamamos una segunda vez. El código se reutiliza sin repetirlo. +mostrar_bienvenida() + +print("--- 2. FUNCIÓN CON UN PARÁMETRO ---") + +# Esta función recibe un dato (el nombre) para personalizarse. +# 'jugador' es el parámetro: una variable temporal que solo existe aquí dentro. +def anunciar_jugador(jugador): + print("El jugador en turno es:") + print(jugador) + +# Llamamos la función con diferentes datos cada vez. +anunciar_jugador("Ana") +anunciar_jugador("Bruno") +anunciar_jugador("Carla") + +print("--- 3. FUNCIÓN CON VARIOS PARÁMETROS ---") + +# Una función puede recibir más de un parámetro, separados por coma. +def mostrar_puntuacion(jugador, puntos): + print(jugador) + print(puntos) + +mostrar_puntuacion("Diego", 1500) +mostrar_puntuacion("Elena", 980) + +print("--- 4. FUNCIÓN QUE DEVUELVE UN RESULTADO (return) ---") + +# Esta función no imprime: CALCULA y devuelve el resultado. +# Quien la llame recibirá ese resultado como si fuera un valor normal. +def calcular_nivel(experiencia): + nivel = experiencia // 100 # División entera: 350 xp → nivel 3 + return nivel + +# Guardamos lo que la función devuelve en una variable. +nivel_jugador = calcular_nivel(350) +print("El nivel del jugador con 350 XP es:") +print(nivel_jugador) + +# Podemos usar el return directamente dentro de un print. +print("El nivel del jugador con 820 XP es:") +print(calcular_nivel(820)) + +print("--- 5. FUNCIÓN + FOR: PROCESANDO UNA LISTA ---") + +# Una función puede trabajar con estructuras que ya conocemos. +def mostrar_inventario(lista_items): + for item in lista_items: + print(item) + +inventario = ["Espada", "Escudo", "Poción"] +mostrar_inventario(inventario) + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-03-Exterior/01-funciones/reto.md b/python/Modulo-03-Exterior/01-funciones/reto.md new file mode 100644 index 0000000..829b4c3 --- /dev/null +++ b/python/Modulo-03-Exterior/01-funciones/reto.md @@ -0,0 +1,33 @@ +# Reto 11: La Calculadora de Estadísticas 📊 + +Vas a crear un conjunto de funciones que calculen estadísticas básicas de una lista de números. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-11.py` y sigue los pasos: + +1. Define una función llamada `mostrar_titulo` sin parámetros que imprima una línea decorativa con el texto `"=== Calculadora de Estadísticas ==="`. + +2. Define una función llamada `obtener_maximo` que reciba una **lista** como parámetro. Dentro, usa un bucle `for` y una variable auxiliar `maximo` (inicialízala con el primer elemento de la lista, `lista[0]`) para encontrar el número más grande: en cada vuelta del `for`, si el elemento actual es mayor que `maximo`, actualiza `maximo`. Al final, usa `return` para devolver `maximo`. + +3. Define una función llamada `calcular_total` que reciba una **lista** como parámetro. Usa un bucle `for` y una variable `total = 0` para sumar todos los elementos. Devuelve `total` con `return`. + +4. Al final del archivo (fuera de las funciones), define una lista `puntuaciones = [45, 82, 17, 96, 63, 51]`. + +5. Llama a `mostrar_titulo()`. +6. Llama a `obtener_maximo(puntuaciones)`, guarda el resultado en `puntaje_maximo` e imprímelo. +7. Llama a `calcular_total(puntuaciones)`, guarda el resultado en `suma_total` e imprímelo. + +**Resultado esperado en tu terminal:** +```text +--- Iniciando calculadora --- +=== Calculadora de Estadísticas === +--- Puntuación máxima --- +96 +--- Suma total --- +354 +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-03-Exterior/01-funciones/solucion/tu-usuario-11.py` +- **Windows:** `python python/Modulo-03-Exterior/01-funciones/solucion/tu-usuario-11.py` diff --git a/python/Modulo-03-Exterior/01-funciones/solucion/.gitkeep b/python/Modulo-03-Exterior/01-funciones/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/01-funciones/teoria.md b/python/Modulo-03-Exterior/01-funciones/teoria.md new file mode 100644 index 0000000..fef46f7 --- /dev/null +++ b/python/Modulo-03-Exterior/01-funciones/teoria.md @@ -0,0 +1,89 @@ +# Teoría: Funciones — La receta de cocina reutilizable + +Imagina que eres chef y tienes una receta de guacamole. En lugar de escribir todos los pasos cada vez que alguien pide guacamole, tienes la receta en una tarjeta. Cuando la necesitas, la sacas, la sigues, y listo. La receta es tu **función**. + +--- + +## Anatomía: Definir una función + +Para crear una función, usamos la palabra reservada `def`: + +```python +def saludar(): + print("¡Hola! Bienvenido.") +``` + +Desglose de cada pieza: + +- **`def`**: Palabra reservada. Significa "define". Le dice a Python "estoy creando una función". +- **`saludar`**: El nombre que le damos a nuestra función. Las mismas reglas que las variables: sin espacios, sin números al inicio. +- **`()`**: Los paréntesis son **obligatorios** al definir una función. Por ahora estarán vacíos; más adelante tendrán datos adentro. +- **`:`**: Los dos puntos de siempre. Indican que el bloque de código comienza. +- **` print(...)`**: El código **dentro** de la función, indentado con 4 espacios. + +> **Importante:** Definir una función NO la ejecuta. Es como escribir la receta sin cocinar nada aún. Solo la guarda para usarla después. + +--- + +## Anatomía: Llamar (ejecutar) una función + +Para ejecutar la función, la **llamamos** escribiendo su nombre seguido de paréntesis: + +```python +saludar() +``` + +- **`saludar`**: El nombre de la función que queremos ejecutar. +- **`()`**: Los paréntesis le dicen a Python "ejecuta esto ahora". Sin paréntesis, Python solo mira la función, no la corre. + +--- + +## Funciones con parámetros: recetas con ingredientes + +Una función puede recibir **datos** para trabajar con ellos. Esos datos se llaman **parámetros** y se ponen dentro de los paréntesis al definir la función: + +```python +def saludar_a(nombre): + print("¡Hola,") + print(nombre) +``` + +- **`nombre`**: Es el parámetro. Es como una variable temporal que solo existe dentro de la función. Cuando llamas a la función, le pasas el valor real. + +Para llamar esta función y darle el dato: + +```python +saludar_a("Kaia") +saludar_a("Bruno") +``` + +Cada llamada es independiente. La primera vez `nombre` vale `"Kaia"`, la segunda vez vale `"Bruno"`. + +--- + +## Funciones que devuelven un resultado: `return` + +Una función también puede **calcular algo y devolverte el resultado** para que lo uses: + +```python +def sumar(numero_a, numero_b): + resultado = numero_a + numero_b + return resultado +``` + +- **`return`**: Palabra reservada. Significa "devuelve este valor a quien me llamó". Cuando Python llega al `return`, la función termina y entrega el valor. + +Para capturar lo que devuelve: + +```python +total = sumar(10, 5) +print(total) # Imprime 15 +``` + +--- + +## ¿Qué pasa si me equivoco? + +- **Olvidar los paréntesis al llamar**: Si escribes `saludar` sin `()`, Python no la ejecuta. Solo muestra que es un objeto función, no corre su código. +- **Usar una variable de dentro de la función desde afuera**: Las variables creadas dentro de una función solo existen dentro de ella. Si intentas usarlas afuera, obtendrás un `NameError`. +- **Llamar la función antes de definirla**: Python lee el código de arriba a abajo. Si llamas `saludar()` en la línea 1 y la defines en la línea 10, obtendrás un `NameError`. Define siempre primero, llama después. diff --git a/python/Modulo-03-Exterior/02-input-usuario/.gitkeep b/python/Modulo-03-Exterior/02-input-usuario/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/02-input-usuario/README.md b/python/Modulo-03-Exterior/02-input-usuario/README.md new file mode 100644 index 0000000..2795fbb --- /dev/null +++ b/python/Modulo-03-Exterior/02-input-usuario/README.md @@ -0,0 +1,11 @@ +# Nivel 12: Input del Usuario ⌨️ + +Hasta ahora, todos los datos de tus programas los escribiste tú directamente en el código. Pero las aplicaciones reales son interactivas: le **preguntan** al usuario qué quiere. + +La función `input()` pausa el programa, muestra un mensaje y espera a que el usuario escriba algo y presione Enter. Con esto, tus programas cobran vida propia. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende a capturar datos del usuario. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y observa la trampa del tipo de dato. +3. ⚔️ **Supera el reto**: Abre `reto.md` y crea tu primer programa interactivo. diff --git a/python/Modulo-03-Exterior/02-input-usuario/ejemplo.py b/python/Modulo-03-Exterior/02-input-usuario/ejemplo.py new file mode 100644 index 0000000..220aba7 --- /dev/null +++ b/python/Modulo-03-Exterior/02-input-usuario/ejemplo.py @@ -0,0 +1,64 @@ +# El input en acción: el programa que escucha y responde. +# AVISO: Este archivo es INTERACTIVO. Al ejecutarlo, la terminal te pedirá datos. +# Escribe los valores que se piden y presiona Enter cada vez. + +print("--- 1. CAPTURA DE TEXTO ---") + +# input() pausa el programa y espera que el usuario escriba algo. +# Lo que escriba se guarda como texto (str) en la variable. +nombre_usuario = input("¿Cuál es tu nombre? ") + +print("¡Hola!") +print(nombre_usuario) + +print("--- 2. LA TRAMPA: input() SIEMPRE DA TEXTO ---") + +# Aunque el usuario escriba un número, Python lo guarda como texto. +# Usamos int() para convertirlo a número real antes de guardarlo. +edad_texto = input("¿Cuántos años tienes? ") + +# En este momento, 'edad_texto' es texto. No podemos hacer matemáticas. +print("Lo que recibimos (como texto):") +print(edad_texto) + +# Convertimos el texto a número entero. +edad_numero = int(edad_texto) + +# Ahora sí podemos operar con él. +anios_para_treinta = 30 - edad_numero +print("Años que faltan para los 30:") +print(anios_para_treinta) + +print("--- 3. CONVERSIÓN DIRECTA (en una sola línea) ---") + +# Lo más común es convertir y capturar en la misma línea. +# int() envuelve a input(). Python primero ejecuta lo de adentro (input), +# luego lo convierte con int(). +puntos = int(input("¿Cuántos puntos tienes? ")) + +if puntos >= 1000: + print("¡Rango de leyenda!") +elif puntos >= 500: + print("Rango avanzado.") +else: + print("Sigue practicando.") + +print("--- 4. INPUT DENTRO DE UNA FUNCIÓN ---") + +# Las funciones también pueden usar input() para recopilar datos. +def crear_perfil(): + alias = input("Elige tu alias: ") + nivel = int(input("¿En qué nivel empiezas? ")) + + # Usamos las variables dentro de la función para construir el perfil. + perfil = { + "alias": alias, + "nivel": nivel + } + return perfil + +datos_jugador = crear_perfil() +print("Perfil creado:") +print(datos_jugador) + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-03-Exterior/02-input-usuario/reto.md b/python/Modulo-03-Exterior/02-input-usuario/reto.md new file mode 100644 index 0000000..406e1fb --- /dev/null +++ b/python/Modulo-03-Exterior/02-input-usuario/reto.md @@ -0,0 +1,37 @@ +# Reto 12: La Máquina de Descuentos 🏷️ + +Vas a crear un programa interactivo que calcule el precio final de un producto con descuento. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-12.py` y sigue los pasos: + +1. Define una función llamada `calcular_precio_final` que reciba dos parámetros: `precio_original` (número entero) y `porcentaje_descuento` (número entero). Dentro, calcula el descuento (`precio_original * porcentaje_descuento // 100`) y el precio final (`precio_original - descuento`). Devuelve el precio final con `return`. + +2. Fuera de la función, usa `input()` para pedirle al usuario: + - El nombre del producto (texto, sin convertir). + - El precio original (conviértelo a `int`). + - El porcentaje de descuento (conviértelo a `int`). + +3. Llama a `calcular_precio_final()` con los datos del usuario y guarda el resultado. + +4. Usa un `if / elif / else` para evaluar el precio final: + - Si el precio final es mayor a `500`: imprime `"Producto premium con descuento."`. + - Si el precio final es mayor a `100`: imprime `"Buen precio."`. + - Si no: imprime `"¡Oferta increíble!"`. + +5. Imprime el resumen completo. + +**Resultado esperado en tu terminal** (si el usuario escribe `Teclado`, `800` y `25`): +```text +--- Datos del producto --- +Teclado +--- Precio con 25% de descuento --- +600 +--- Clasificación --- +Producto premium con descuento. +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-03-Exterior/02-input-usuario/solucion/tu-usuario-12.py` +- **Windows:** `python python/Modulo-03-Exterior/02-input-usuario/solucion/tu-usuario-12.py` diff --git a/python/Modulo-03-Exterior/02-input-usuario/solucion/.gitkeep b/python/Modulo-03-Exterior/02-input-usuario/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/02-input-usuario/teoria.md b/python/Modulo-03-Exterior/02-input-usuario/teoria.md new file mode 100644 index 0000000..bdd1aac --- /dev/null +++ b/python/Modulo-03-Exterior/02-input-usuario/teoria.md @@ -0,0 +1,69 @@ +# Teoría: Input del Usuario — El programa que escucha + +Hasta ahora tus programas eran monólogos: solo hablaban, nunca escuchaban. `input()` convierte tu programa en un diálogo. + +Imagina un cajero automático: te muestra un mensaje ("Ingrese su PIN"), espera a que escribas, y luego actúa según lo que recibió. + +--- + +## Anatomía de `input()` + +```python +nombre = input("¿Cuál es tu nombre? ") +``` + +Desglose: + +- **`nombre`**: La variable donde guardaremos lo que el usuario escriba. +- **`=`**: Asignación. El resultado de `input()` se guarda en esta variable. +- **`input`**: La herramienta de Python para escuchar al usuario. +- **`(...)`**: Los paréntesis le entregan a `input` el mensaje que mostrará al usuario. +- **`"¿Cuál es tu nombre? "`**: El mensaje (texto entre comillas) que aparece en la terminal antes de que el usuario escriba. El espacio al final es cosmético: evita que el cursor quede pegado al texto. + +Cuando Python llega a esta línea: +1. Imprime `¿Cuál es tu nombre? ` en la terminal. +2. El programa se **pausa** completamente. +3. El usuario escribe y presiona **Enter**. +4. Python guarda lo escrito como texto (`str`) en la variable `nombre`. + +--- + +## ¡TRAMPA CRÍTICA! `input()` SIEMPRE devuelve texto (`str`) + +Esta es la trampa más importante de este nivel. No importa si el usuario escribe `42` o `3.14`: Python siempre lo trata como texto (`str`). + +```python +edad = input("¿Cuántos años tienes? ") +# Si el usuario escribe 20, 'edad' guarda el texto "20", no el número 20. +``` + +Si intentas hacer matemáticas con ese texto, obtendrás un `TypeError`. + +### Solución: convertir el tipo de dato + +Para convertir el texto a número entero, envolvemos `input()` con `int()`: + +```python +edad = int(input("¿Cuántos años tienes? ")) +``` + +- **`int(...)`**: Convierte lo que está dentro a un número entero. Si el usuario escribe `"20"`, `int("20")` devuelve el número `20`. +- **`float(...)`**: Convierte a número con decimales. `float("3.14")` → `3.14`. + +### Anatomía de la conversión: + +``` +edad = int( input("¿Cuántos años tienes? ") ) + ^ ^ ^ + | | └── input() captura texto del usuario: "20" + | └─────────── int() convierte ese texto al número: 20 + └────────────────── = guarda el número en 'edad' +``` + +--- + +## ¿Qué pasa si me equivoco? + +- **No convertir a `int` cuando necesitas número**: `TypeError` al intentar sumar el texto con un número. +- **El usuario escribe texto cuando esperabas número**: Si usas `int(input(...))` y el usuario escribe `"hola"`, Python lanzará un `ValueError: invalid literal for int()`. El Nivel 13 (Manejo de Errores) resolverá esto. +- **Olvidar el espacio al final del mensaje**: Es válido, pero el cursor del usuario quedará pegado al texto, viéndose feo. diff --git a/python/Modulo-03-Exterior/03-manejo-errores/.gitkeep b/python/Modulo-03-Exterior/03-manejo-errores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/03-manejo-errores/README.md b/python/Modulo-03-Exterior/03-manejo-errores/README.md new file mode 100644 index 0000000..3d652fa --- /dev/null +++ b/python/Modulo-03-Exterior/03-manejo-errores/README.md @@ -0,0 +1,13 @@ +# Nivel 13: Manejo de Errores 🛡️ + +En el nivel anterior, si el usuario escribe `"hola"` cuando pedías un número, el programa explota con un `ValueError` y muere. En una aplicación real, eso es inaceptable. + +El **manejo de errores** te permite anticiparte a los fallos y decirle al programa qué hacer cuando algo sale mal, en lugar de simplemente caerse. + +Es la diferencia entre una app profesional y un programa frágil. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende la estructura `try / except`. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` y observa cómo el programa sobrevive a entradas incorrectas. +3. ⚔️ **Supera el reto**: Abre `reto.md` y blinda tu propio programa. diff --git a/python/Modulo-03-Exterior/03-manejo-errores/ejemplo.py b/python/Modulo-03-Exterior/03-manejo-errores/ejemplo.py new file mode 100644 index 0000000..f86bde0 --- /dev/null +++ b/python/Modulo-03-Exterior/03-manejo-errores/ejemplo.py @@ -0,0 +1,70 @@ +# El manejo de errores en acción: el programa a prueba de balas. +# AVISO: Este archivo es INTERACTIVO. La terminal te pedirá datos. +# Prueba escribir cosas incorrectas a propósito para ver el try/except en acción. + +print("--- 1. SIN PROTECCIÓN (el programa moriría) ---") + +# Este bloque está protegido. Si el usuario escribe texto, capturamos el error. +print("Intentando convertir un texto a número...") +try: + # Simulamos que alguien nos dio texto en lugar de un número. + numero_invalido = int("esto_no_es_un_numero") + print("Conversión exitosa.") +except ValueError: + # Python llegó aquí porque int("esto_no_es_un_numero") falló. + print("¡Error capturado! El valor no es un número entero válido.") + +print("El programa continúa sin haberse caído.") + +print("--- 2. PROTEGIENDO EL INPUT DEL USUARIO ---") + +# Ahora protegemos una entrada real del usuario. +# Si escribe texto cuando pedimos un número, el programa sobrevive. +try: + edad = int(input("Escribe tu edad (número entero): ")) + print("Edad registrada:") + print(edad) +except ValueError: + print("Eso no es un número entero. Se usará 0 como valor por defecto.") + edad = 0 + +print("Valor final de edad:") +print(edad) + +print("--- 3. CAPTURANDO MÚLTIPLES TIPOS DE ERROR ---") + +# Un mismo try puede tener varios except para distintos errores. +lista_ejemplo = [10, 20, 30] + +try: + indice = int(input("Dame un índice para buscar en la lista (0, 1 o 2): ")) + resultado = lista_ejemplo[indice] + print("El elemento en esa posición es:") + print(resultado) +except ValueError: + # El usuario escribió texto en lugar de un número. + print("Error: el índice debe ser un número entero.") +except IndexError: + # El usuario escribió un número pero fuera del rango [0, 2]. + print("Error: ese índice no existe en la lista.") + +print("--- 4. TRY / EXCEPT / ELSE / FINALLY ---") + +print("Intentando dividir dos números...") +try: + dividendo = int(input("Escribe el dividendo: ")) + divisor = int(input("Escribe el divisor (no pongas 0): ")) + cociente = dividendo / divisor +except ValueError: + print("Error: uno de los valores no es un número.") +except ZeroDivisionError: + print("Error: no se puede dividir entre cero.") +else: + # Solo se ejecuta si el try tuvo ÉXITO. + print("Resultado de la división:") + print(cociente) +finally: + # Se ejecuta SIEMPRE, haya error o no. + print("Operación de división finalizada.") + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-03-Exterior/03-manejo-errores/reto.md b/python/Modulo-03-Exterior/03-manejo-errores/reto.md new file mode 100644 index 0000000..65af875 --- /dev/null +++ b/python/Modulo-03-Exterior/03-manejo-errores/reto.md @@ -0,0 +1,28 @@ +# Reto 13: El Validador de Contraseña 🔐 + +Vas a crear un sistema de validación de contraseña que no se caiga ante entradas incorrectas. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-13.py` y sigue los pasos: + +1. Define una función llamada `validar_pin` que reciba un parámetro `intento_texto` (texto `str`). Dentro de la función: + - Usa un bloque `try / except / else` para intentar convertir `intento_texto` a `int`. + - En el `except ValueError`: devuelve con `return` el texto `"Error: el PIN debe ser un número."`. + - En el `else` (conversión exitosa): usa un `if` para revisar si el número es exactamente `1234`. Si sí: devuelve `"Acceso concedido."`. Si no: devuelve `"PIN incorrecto."`. + +2. Fuera de la función, crea una lista llamada `intentos` con los siguientes 3 valores de texto: `["abc", "9999", "1234"]`. + +3. Usa un bucle `for` para recorrer `intentos`. En cada vuelta, llama a `validar_pin()` con el elemento actual y guarda el resultado. Luego imprímelo. + +**Resultado esperado en tu terminal:** +```text +--- Validando intentos --- +Error: el PIN debe ser un número. +PIN incorrecto. +Acceso concedido. +``` + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-03-Exterior/03-manejo-errores/solucion/tu-usuario-13.py` +- **Windows:** `python python/Modulo-03-Exterior/03-manejo-errores/solucion/tu-usuario-13.py` diff --git a/python/Modulo-03-Exterior/03-manejo-errores/solucion/.gitkeep b/python/Modulo-03-Exterior/03-manejo-errores/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/03-manejo-errores/teoria.md b/python/Modulo-03-Exterior/03-manejo-errores/teoria.md new file mode 100644 index 0000000..08de62f --- /dev/null +++ b/python/Modulo-03-Exterior/03-manejo-errores/teoria.md @@ -0,0 +1,78 @@ +# Teoría: Manejo de Errores — El cinturón de seguridad del código + +Imagina que conduces un coche. Sabes que podría haber un accidente. No conduces más despacio (eso sería evitar el error); llevas puesto el cinturón (eso es manejarlo). Si hay un accidente, el cinturón te protege. Si no lo hay, el cinturón no molestó a nadie. + +El `try / except` es el cinturón de seguridad de tu código. + +--- + +## Anatomía de `try / except` + +```python +try: + codigo_que_puede_fallar +except NombreDelError: + codigo_que_se_ejecuta_si_falla +``` + +Desglose: + +- **`try`**: Palabra reservada. Significa "intenta esto". Python ejecuta el código dentro de este bloque. +- **`:`**: Dos puntos obligatorios. +- **` codigo_que_puede_fallar`**: El bloque indentado que Python intentará ejecutar. +- **`except`**: Palabra reservada. Significa "excepto si ocurre este error". Si el bloque `try` falla, Python salta aquí en lugar de explotar. +- **`NombreDelError`**: El tipo de error que queremos capturar. Los más comunes: + - `ValueError`: Cuando un dato tiene el formato incorrecto (ej. `int("hola")`). + - `ZeroDivisionError`: Cuando divides entre cero. + - `IndexError`: Cuando el índice está fuera del rango de una lista. + - `KeyError`: Cuando la clave no existe en un diccionario. +- **` codigo_si_falla`**: El bloque indentado que se ejecuta solo si ocurrió el error. + +--- + +## Ejemplo paso a paso + +```python +try: + numero = int(input("Escribe un número: ")) + print(numero) +except ValueError: + print("Eso no es un número válido.") +``` + +**Escenario A** — El usuario escribe `42`: +1. `try` se ejecuta: `int("42")` → `42`. Éxito. +2. `except` se ignora por completo. + +**Escenario B** — El usuario escribe `"hola"`: +1. `try` falla: `int("hola")` → `ValueError`. +2. Python salta al `except ValueError`. +3. Se imprime `"Eso no es un número válido."`. +4. El programa **continúa** en lugar de morir. + +--- + +## El bloque `else` y `finally` (opcional) + +```python +try: + numero = int(input("Escribe un número: ")) +except ValueError: + print("Error: eso no es un número.") +else: + # Se ejecuta SOLO si el try tuvo éxito (no hubo error). + print("Número recibido correctamente.") +finally: + # Se ejecuta SIEMPRE, haya error o no. + print("Proceso terminado.") +``` + +- **`else`**: Bloque que corre solo cuando `try` fue exitoso. +- **`finally`**: Bloque que corre sin importar qué pasó. Ideal para tareas de limpieza (ej. cerrar un archivo). + +--- + +## ¿Qué pasa si me equivoco? + +- **No especificar el tipo de error (`except:` desnudo)**: Captura absolutamente todos los errores, incluso los que no esperabas. Esto puede ocultar bugs graves. Siempre especifica el error que esperas capturar. +- **Código después del error en el `try`**: Si la línea que falla está en medio del bloque `try`, el resto del bloque `try` se salta. El control pasa directamente al `except`. diff --git a/python/Modulo-03-Exterior/04-archivos/.gitkeep b/python/Modulo-03-Exterior/04-archivos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/04-archivos/README.md b/python/Modulo-03-Exterior/04-archivos/README.md new file mode 100644 index 0000000..70e511c --- /dev/null +++ b/python/Modulo-03-Exterior/04-archivos/README.md @@ -0,0 +1,13 @@ +# Nivel 14: Archivos 💾 + +Todo lo que has programado hasta ahora vive en la memoria RAM. Cuando el programa termina, los datos desaparecen. + +Leer y escribir **archivos** cambia eso: permite que tu programa guarde información que persiste entre ejecuciones, lea configuraciones, procese datos externos y registre eventos. + +Es el primer paso hacia la persistencia de datos real. + +## Ruta de aprendizaje + +1. 📖 **Lee la teoría**: Abre `teoria.md` y aprende a abrir, leer y escribir archivos de texto. +2. 🔬 **Estudia el ejemplo**: Revisa `ejemplo.py` para ver el ciclo completo: escribir → leer → añadir. +3. ⚔️ **Supera el reto**: Abre `reto.md` y crea tu propio registro de datos en archivo. diff --git a/python/Modulo-03-Exterior/04-archivos/ejemplo.py b/python/Modulo-03-Exterior/04-archivos/ejemplo.py new file mode 100644 index 0000000..afc72d9 --- /dev/null +++ b/python/Modulo-03-Exterior/04-archivos/ejemplo.py @@ -0,0 +1,60 @@ +# Los archivos en acción: el cuaderno que no olvida. +# Al ejecutar este script, se creará un archivo 'registro.txt' +# en la misma carpeta donde lo ejecutes. + +print("--- 1. ESCRIBIR EN UN ARCHIVO (modo 'w') ---") + +# Abrimos el archivo en modo 'w' (write/escribir). +# Si 'registro.txt' no existe, Python lo crea. +# Si ya existe, BORRA su contenido y empieza desde cero. +with open("registro.txt", "w") as archivo: + # .write() escribe una cadena de texto en el archivo. + # '\n' es el salto de línea. Sin él, todo iría en una sola línea. + archivo.write("=== Registro del sistema ===\n") + archivo.write("Línea 1: Programa iniciado.\n") + archivo.write("Línea 2: Usuario autenticado.\n") + +# El bloque 'with' terminó, el archivo se cerró automáticamente. +print("Archivo 'registro.txt' creado con éxito.") + +print("--- 2. LEER TODO EL CONTENIDO (modo 'r') ---") + +# Abrimos el mismo archivo en modo 'r' (read/leer). +with open("registro.txt", "r") as archivo: + # .read() devuelve todo el contenido del archivo como un texto (str). + contenido_completo = archivo.read() + +print("Contenido del archivo:") +print(contenido_completo) + +print("--- 3. LEER LÍNEA POR LÍNEA CON for ---") + +# El bucle 'for' recorre el archivo una línea a la vez. +# Cada 'linea' incluye el '\n' al final, por eso el print agrega una línea extra. +with open("registro.txt", "r") as archivo: + for linea in archivo: + print(linea) + +print("--- 4. AÑADIR AL FINAL (modo 'a') ---") + +# Modo 'a' (append/añadir): no borra el contenido existente. +# Añade el nuevo texto al final del archivo. +with open("registro.txt", "a") as archivo: + archivo.write("Línea 3: Sesión cerrada correctamente.\n") + +print("Línea añadida al final. Leyendo el archivo completo...") + +with open("registro.txt", "r") as archivo: + print(archivo.read()) + +print("--- 5. MANEJO DE ERRORES CON ARCHIVOS ---") + +# ¿Qué pasa si intentamos leer un archivo que no existe? +# Usamos try/except para evitar que el programa explote. +try: + with open("archivo_inexistente.txt", "r") as archivo: + contenido = archivo.read() +except FileNotFoundError: + print("Error capturado: el archivo no existe. Continuando...") + +print("--- FIN DEL EJEMPLO ---") diff --git a/python/Modulo-03-Exterior/04-archivos/reto.md b/python/Modulo-03-Exterior/04-archivos/reto.md new file mode 100644 index 0000000..66d91a7 --- /dev/null +++ b/python/Modulo-03-Exterior/04-archivos/reto.md @@ -0,0 +1,36 @@ +# Reto 14: El Diario del Explorador 📓 + +Vas a crear un programa que guarde entradas de un diario en un archivo de texto. + +## Instrucciones + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-14.py` y sigue los pasos: + +1. Define una función llamada `guardar_entrada` que reciba dos parámetros: `nombre_archivo` (texto) y `entrada` (texto). Dentro, usa `with open(nombre_archivo, "a") as f` para abrir en modo añadir, y escribe `entrada` seguida de `"\n"`. + +2. Define una función llamada `leer_diario` que reciba `nombre_archivo`. Dentro, usa un `try / except FileNotFoundError` para intentar abrir y leer el archivo completo con `.read()`. Si existe, devuelve el contenido. Si no existe, devuelve el texto `"El diario está vacío."`. + +3. Fuera de las funciones: + - Crea una lista `entradas` con 3 textos (escríbelos tú: frases cortas de explorador). + - Usa un `for` para recorrer `entradas` y llamar a `guardar_entrada("diario.txt", entrada)` en cada vuelta. + - Llama a `leer_diario("diario.txt")` e imprime el resultado. + - Llama a `leer_diario("archivo_que_no_existe.txt")` e imprime el resultado. + +**Resultado esperado en tu terminal:** +```text +--- Guardando entradas --- +3 entradas guardadas. +--- Contenido del diario --- +Día 1: Llegamos a la selva. +Día 2: Encontramos un río. +Día 3: Acampamos en la montaña. + +--- Leyendo archivo inexistente --- +El diario está vacío. +``` + +> El archivo `diario.txt` se creará en la carpeta desde donde ejecutes el script. + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-03-Exterior/04-archivos/solucion/tu-usuario-14.py` +- **Windows:** `python python/Modulo-03-Exterior/04-archivos/solucion/tu-usuario-14.py` diff --git a/python/Modulo-03-Exterior/04-archivos/solucion/.gitkeep b/python/Modulo-03-Exterior/04-archivos/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/04-archivos/teoria.md b/python/Modulo-03-Exterior/04-archivos/teoria.md new file mode 100644 index 0000000..3579386 --- /dev/null +++ b/python/Modulo-03-Exterior/04-archivos/teoria.md @@ -0,0 +1,94 @@ +# Teoría: Archivos — El cuaderno que recuerda todo + +Un programa que no puede guardar datos es como un empleado con amnesia: olvida todo al terminar el día. Los archivos son el cuaderno donde ese empleado anota sus apuntes para consultarlos mañana. + +--- + +## La función `open()` y los modos + +Para trabajar con un archivo, lo abrimos con `open()`: + +```python +archivo = open("mi_archivo.txt", "w") +``` + +Desglose: + +- **`open`**: La herramienta de Python para abrir archivos. +- **`"mi_archivo.txt"`**: El nombre del archivo. Si no existe, Python lo crea (en modo escritura). La ruta es relativa a donde ejecutas el script. +- **`"w"`**: El **modo** de apertura. Le dice a Python qué quieres hacer: + +| Modo | Significado | ¿Qué hace si el archivo ya existe? | +|------|-------------|-----------------------------------| +| `"r"` | Read (leer) | Lo abre para leer. Error si no existe. | +| `"w"` | Write (escribir) | Lo crea o lo **borra y reemplaza** si ya existía. | +| `"a"` | Append (añadir) | Añade al final sin borrar lo que había. | + +--- + +## La forma correcta: `with open(...)` + +En lugar de abrir y cerrar manualmente el archivo, Python ofrece la instrucción `with`, que lo cierra automáticamente al terminar: + +```python +with open("mi_archivo.txt", "w") as archivo: + archivo.write("Hola desde Python.\n") +``` + +Desglose: + +- **`with`**: Palabra reservada. Gestiona el ciclo de vida del archivo automáticamente. +- **`open(...)`**: Abre el archivo. +- **`as archivo`**: Le da un nombre temporal a la conexión con el archivo. Puedes llamarlo como quieras. +- **`:`**: Dos puntos obligatorios. +- **` archivo.write(...)`**: Dentro del bloque indentado, escribimos en el archivo. +- **`"\n"`**: El símbolo de **salto de línea**. Es como presionar Enter dentro del archivo. Sin él, todo se escribiría en una sola línea. + +--- + +## Escribir: `.write()` + +```python +with open("notas.txt", "w") as f: + f.write("Primera línea.\n") + f.write("Segunda línea.\n") +``` + +--- + +## Leer todo el contenido: `.read()` + +```python +with open("notas.txt", "r") as f: + contenido = f.read() + print(contenido) +``` + +--- + +## Leer línea por línea con `for` + +El `for` también funciona sobre archivos: itera línea por línea. + +```python +with open("notas.txt", "r") as f: + for linea in f: + print(linea) +``` + +--- + +## Añadir sin borrar: modo `"a"` + +```python +with open("notas.txt", "a") as f: + f.write("Tercera línea añadida después.\n") +``` + +--- + +## ¿Qué pasa si me equivoco? + +- **Leer un archivo que no existe**: `FileNotFoundError`. Usa `try / except FileNotFoundError` para manejarlo. +- **Olvidar el `\n`**: Todo el texto aparece en una sola línea en el archivo. No es un error de Python, pero es un error de formato. +- **Usar modo `"w"` cuando querías `"a"`**: Borrarás accidentalmente el contenido del archivo. Siempre verifica el modo antes de abrir. diff --git a/python/Modulo-03-Exterior/RETO-FINAL-MOD-3/.gitkeep b/python/Modulo-03-Exterior/RETO-FINAL-MOD-3/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-03-Exterior/RETO-FINAL-MOD-3/README.md b/python/Modulo-03-Exterior/RETO-FINAL-MOD-3/README.md new file mode 100644 index 0000000..0a8d778 --- /dev/null +++ b/python/Modulo-03-Exterior/RETO-FINAL-MOD-3/README.md @@ -0,0 +1,71 @@ +# Reto Final: Módulo 03 — El Exterior 🏆 + +Has aprendido a crear funciones, escuchar al usuario, sobrevivir a errores y persistir datos en archivos. Este reto los combina todos en un sistema real. + +Sin instrucciones paso a paso. Solo el enunciado y el resultado esperado. + +--- + +## El Problema: Sistema de Registro de Puntuaciones 🎮 + +Eres el programador de una arcade. Debes crear un sistema que registre las puntuaciones de los jugadores en un archivo, las cargue y muestre las mejores. + +### Funciones que debes definir: + +1. **`registrar_puntuacion(nombre_archivo, jugador, puntos)`**: Abre el archivo en modo añadir (`"a"`) y escribe una línea con el formato `jugador:puntos\n` (por ejemplo: `"Ana:1500\n"`). + +2. **`cargar_puntuaciones(nombre_archivo)`**: Intenta abrir el archivo en modo lectura. Si no existe (`FileNotFoundError`), devuelve una lista vacía `[]`. Si existe, lee línea por línea y para cada línea: + - Usa el método `.strip()` para eliminar el `\n` del final (es la primera vez que lo ves: `.strip()` sobre un texto elimina espacios y saltos de línea de los extremos). + - Separa el nombre y los puntos usando `.split(":")` (también nuevo: `.split(":")` parte el texto en una lista usando `:` como separador; `"Ana:1500".split(":")` devuelve `["Ana", "1500"]`). + - Convierte los puntos a `int`. + - Añade un diccionario `{"nombre": nombre, "puntos": puntos}` a una lista. + - Devuelve esa lista al final. + +3. **`mostrar_ranking(lista_puntuaciones)`**: Recibe la lista de diccionarios. Usa un bucle `for` con un `if` para imprimir solo los jugadores con más de `1000` puntos. Imprímelos con su nombre y puntuación. + +### Lógica principal (fuera de las funciones): + +1. Registra 3 jugadores en `"puntuaciones.txt"` usando `registrar_puntuacion()`: + - `"Kaia"`, `1800` + - `"Bruno"`, `750` + - `"Carla"`, `2100` + +2. Carga las puntuaciones con `cargar_puntuaciones()` y guárdalas en una variable. + +3. Imprime el número total de jugadores registrados usando `len()`. + +4. Llama a `mostrar_ranking()` con la lista cargada. + +--- + +## Resultado esperado en tu terminal + +```text +--- Registrando jugadores --- +3 jugadores registrados. +--- Total de registros --- +3 +--- Ranking (más de 1000 puntos) --- +Kaia +1800 +Carla +2100 +``` + +--- + +## Criterios de éxito ✓ + +- [ ] El código corre sin errores. +- [ ] Se crea el archivo `puntuaciones.txt` con las 3 entradas. +- [ ] `cargar_puntuaciones` devuelve una lista de diccionarios (no texto plano). +- [ ] `mostrar_ranking` filtra correctamente (Bruno con 750 NO aparece). +- [ ] Usaste `try / except` para el caso de archivo inexistente. + +Crea tu archivo en `solucion/` con el nombre `tu-usuario-final.py`. + +Ejecuta desde la raíz del proyecto: +- **macOS / Linux:** `python3 python/Modulo-03-Exterior/RETO-FINAL-MOD-3/solucion/tu-usuario-final.py` +- **Windows:** `python python/Modulo-03-Exterior/RETO-FINAL-MOD-3/solucion/tu-usuario-final.py` + +¡Si logras esto, estás listo para el Módulo 04 — Nerve (el módulo del proyecto real)! diff --git a/python/Modulo-03-Exterior/RETO-FINAL-MOD-3/solucion/.gitkeep b/python/Modulo-03-Exterior/RETO-FINAL-MOD-3/solucion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/.gitkeep b/python/Modulo-04-Bucles/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/31-while/.gitkeep b/python/Modulo-04-Bucles/31-while/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/32-for/.gitkeep b/python/Modulo-04-Bucles/32-for/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/33-range-enumerate/.gitkeep b/python/Modulo-04-Bucles/33-range-enumerate/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/34-break-continue/.gitkeep b/python/Modulo-04-Bucles/34-break-continue/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/35-list-comprehension/.gitkeep b/python/Modulo-04-Bucles/35-list-comprehension/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/36-dict-comprehension/.gitkeep b/python/Modulo-04-Bucles/36-dict-comprehension/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/37-zip/.gitkeep b/python/Modulo-04-Bucles/37-zip/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/38-iteradores/.gitkeep b/python/Modulo-04-Bucles/38-iteradores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/39-matrices/.gitkeep b/python/Modulo-04-Bucles/39-matrices/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-04-Bucles/40-RETO-analizador/.gitkeep b/python/Modulo-04-Bucles/40-RETO-analizador/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/.gitkeep b/python/Modulo-05-Funciones/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/41-def/.gitkeep b/python/Modulo-05-Funciones/41-def/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/42-parametros-return/.gitkeep b/python/Modulo-05-Funciones/42-parametros-return/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/43-args-defecto/.gitkeep b/python/Modulo-05-Funciones/43-args-defecto/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/44-args-kwargs/.gitkeep b/python/Modulo-05-Funciones/44-args-kwargs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/45-scope/.gitkeep b/python/Modulo-05-Funciones/45-scope/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/46-lambdas/.gitkeep b/python/Modulo-05-Funciones/46-lambdas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/47-recursividad/.gitkeep b/python/Modulo-05-Funciones/47-recursividad/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/48-import/.gitkeep b/python/Modulo-05-Funciones/48-import/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/49-pip-venvs/.gitkeep b/python/Modulo-05-Funciones/49-pip-venvs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-05-Funciones/50-RETO-calculadora/.gitkeep b/python/Modulo-05-Funciones/50-RETO-calculadora/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/.gitkeep b/python/Modulo-06-OS/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/51-os-rutas/.gitkeep b/python/Modulo-06-OS/51-os-rutas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/52-txt-read/.gitkeep b/python/Modulo-06-OS/52-txt-read/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/53-txt-write/.gitkeep b/python/Modulo-06-OS/53-txt-write/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/54-csv/.gitkeep b/python/Modulo-06-OS/54-csv/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/55-shutil/.gitkeep b/python/Modulo-06-OS/55-shutil/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/56-datetime/.gitkeep b/python/Modulo-06-OS/56-datetime/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/57-subprocess/.gitkeep b/python/Modulo-06-OS/57-subprocess/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/58-logging/.gitkeep b/python/Modulo-06-OS/58-logging/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/59-try-except/.gitkeep b/python/Modulo-06-OS/59-try-except/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-06-OS/60-RETO-organizador/.gitkeep b/python/Modulo-06-OS/60-RETO-organizador/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/.gitkeep b/python/Modulo-07-Web-APIs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/61-requests/.gitkeep b/python/Modulo-07-Web-APIs/61-requests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/62-json/.gitkeep b/python/Modulo-07-Web-APIs/62-json/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/63-beautifulsoup/.gitkeep b/python/Modulo-07-Web-APIs/63-beautifulsoup/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/64-selectores/.gitkeep b/python/Modulo-07-Web-APIs/64-selectores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/65-selenium/.gitkeep b/python/Modulo-07-Web-APIs/65-selenium/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/66-auth-apis/.gitkeep b/python/Modulo-07-Web-APIs/66-auth-apis/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/67-rate-limiting/.gitkeep b/python/Modulo-07-Web-APIs/67-rate-limiting/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/68-headless/.gitkeep b/python/Modulo-07-Web-APIs/68-headless/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/69-pandas-web/.gitkeep b/python/Modulo-07-Web-APIs/69-pandas-web/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-07-Web-APIs/70-RETO-monitor/.gitkeep b/python/Modulo-07-Web-APIs/70-RETO-monitor/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/.gitkeep b/python/Modulo-08-POO/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/71-clases-objetos/.gitkeep b/python/Modulo-08-POO/71-clases-objetos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/72-init/.gitkeep b/python/Modulo-08-POO/72-init/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/73-herencia-polimorfismo/.gitkeep b/python/Modulo-08-POO/73-herencia-polimorfismo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/74-encapsulamiento/.gitkeep b/python/Modulo-08-POO/74-encapsulamiento/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/75-propiedades/.gitkeep b/python/Modulo-08-POO/75-propiedades/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/76-metodos-clase/.gitkeep b/python/Modulo-08-POO/76-metodos-clase/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/77-dunder-methods/.gitkeep b/python/Modulo-08-POO/77-dunder-methods/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/78-abstraccion/.gitkeep b/python/Modulo-08-POO/78-abstraccion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/79-singleton/.gitkeep b/python/Modulo-08-POO/79-singleton/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-08-POO/80-RETO-ecosistema/.gitkeep b/python/Modulo-08-POO/80-RETO-ecosistema/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/.gitkeep b/python/Modulo-09-Data-IA/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/81-numpy/.gitkeep b/python/Modulo-09-Data-IA/81-numpy/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/82-pandas/.gitkeep b/python/Modulo-09-Data-IA/82-pandas/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/83-limpieza-datos/.gitkeep b/python/Modulo-09-Data-IA/83-limpieza-datos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/84-matplotlib/.gitkeep b/python/Modulo-09-Data-IA/84-matplotlib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/85-scikit-learn/.gitkeep b/python/Modulo-09-Data-IA/85-scikit-learn/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/86-regresion/.gitkeep b/python/Modulo-09-Data-IA/86-regresion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/87-clasificacion/.gitkeep b/python/Modulo-09-Data-IA/87-clasificacion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/88-nlp/.gitkeep b/python/Modulo-09-Data-IA/88-nlp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/89-llms/.gitkeep b/python/Modulo-09-Data-IA/89-llms/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-09-Data-IA/90-RETO-predictor/.gitkeep b/python/Modulo-09-Data-IA/90-RETO-predictor/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/.gitkeep b/python/Modulo-10-Nerve/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/100-RETO-FINAL-PILOT/.gitkeep b/python/Modulo-10-Nerve/100-RETO-FINAL-PILOT/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/91-local-first/.gitkeep b/python/Modulo-10-Nerve/91-local-first/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/92-arquitectura-nodos/.gitkeep b/python/Modulo-10-Nerve/92-arquitectura-nodos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/93-p2p/.gitkeep b/python/Modulo-10-Nerve/93-p2p/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/94-estado-global/.gitkeep b/python/Modulo-10-Nerve/94-estado-global/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/95-criptografia/.gitkeep b/python/Modulo-10-Nerve/95-criptografia/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/96-sincronizacion/.gitkeep b/python/Modulo-10-Nerve/96-sincronizacion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/97-microservicios/.gitkeep b/python/Modulo-10-Nerve/97-microservicios/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/98-despliegue/.gitkeep b/python/Modulo-10-Nerve/98-despliegue/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/Modulo-10-Nerve/99-optimizacion/.gitkeep b/python/Modulo-10-Nerve/99-optimizacion/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/s.html b/s.html new file mode 100644 index 0000000..9f9fa2e --- /dev/null +++ b/s.html @@ -0,0 +1,364 @@ + + + + + + + Índice Maestro: 100 Pasos a la Maestría + + + + + + + + + +
+

El Manifiesto de los 100 + Niveles

+

+ Hemos fragmentado el universo de Python en 100 átomos de conocimiento. Cada nivel es una victoria; cada + módulo es una nueva capacidad profesional. +

+
+ +
+

Balance de Habilidades (Al completar los 100 + niveles)

+
+ +
+
+ +
+
+ + +
+

01. Fundamentos Alpha

+
+
01Hola Mundo y Sintaxis
+
02Cajas de Memoria (Vars)
+
03Tipos: Texto y Enteros
+
04Tipos: Flotantes y Bool
+
05Aritmética de Máquina
+
06Entrada del Usuario (Input)
+
07F-Strings y Formateo
+
08Comentarios y Clean Doc
+
09Errores de Sintaxis Básico
+
10Reto: Ficha de Personaje
+
+
+ + +
+

02. Control de Flujo

+
+
11Operadores Lógicos (AND/OR)
+
12Comparadores (>, <,==)
+
13El Camino del IF
+
14Alternativas con ELSE/ELIF
+
15Lógica Anidada (Inception)
+
16Manejo de Truthy/Falsy
+
17Match Case (Python 3.10+)
+
18Depuración de Flujo
+
19Optimización de Booleans
+
20Reto: El Portero Virtual
+
+
+ + +
+

03. Estructuras de Datos +

+
+
21Listas: El Almacén Simple
+
22Métodos de Lista (CRUD)
+
23Slicing (Rebanar Datos)
+
24Tuplas: Datos Inmutables
+
25Diccionarios (Clave:Valor)
+
26Sets: Conjuntos Únicos
+
27Estructuras Anidadas
+
28Mutabilidad vs Referencia
+
29Copiado Profundo (DeepCopy)
+
30Reto: Inventario RPG
+
+
+ + +
+

04. Bucles y Repetición +

+
+
31While: El Bucle Infinito
+
32For: Iterando el Mundo
+
33Range y Enumerate
+
34Break y Continue
+
35List Comprehension (Pro)
+
36Dict Comprehension
+
37Zip: Uniendo Colecciones
+
38Iteradores y Generadores
+
39Búcles Anidados (Matrices)
+
40Reto: Analizador de Datos
+
+
+ + +
+

05. Funciones y Módulos +

+
+
41Def: Creando Verbos
+
42Parámetros y Retornos
+
43Argumentos por Defecto
+
44*args y **kwargs
+
45Scope: Global vs Local
+
46Lambdas (Anónimas)
+
47Recursividad (Espejos)
+
48Importación de Librerías
+
49PIP y Entornos Virtuales
+
50Reto: Calculadora Modular
+
+
+ + +
+

06. Automatización OS

+
+
51Módulo OS y Rutas
+
52Lectura de Archivos TXT
+
53Escritura y Modos (w, a)
+
54Manejo de CSVs
+
55Shutil: Mover y Copiar
+
56Datetime y Cronos
+
57Subprocess: Comandos Pro
+
58Logging (Caja Negra)
+
59Try / Except / Finally
+
60Reto: Organizador de Archivos
+
+
+ + +
+

07. Web Scraping & APIs +

+
+
61Protocolo HTTP (Requests)
+
62Parseo de JSON Dinámico
+
63BeautifulSoup: HTML Básico
+
64Selectores CSS y XPATH
+
65Selenium: Web Dinámica
+
66Autenticación en APIs
+
67Rate Limiting y Proxies
+
68Headless Browsing
+
69Pandas para Web Data
+
70Reto: Monitor de Precios
+
+
+ + +
+

08. POO Avanzada

+
+
71Clases y Objetos (Blueprints)
+
72El Constructor __init__
+
73Herencia y Polimorfismo
+
74Encapsulamiento (Privados)
+
75Propiedades y Decoradores
+
76Métodos de Clase y Estáticos
+
77Dunder Methods (__str__, __len__) +
+
78Abstracción (ABC)
+
79Patrones de Diseño: Singleton
+
80Reto: Simulador de Ecosistema
+
+
+ + +
+

09. Ciencia de Datos e IA +

+
+
81NumPy: Álgebra de Máquina
+
82Pandas: Dataframes Pro
+
83Limpieza de Datos (Outliers)
+
84Matplotlib y Visualización
+
85Intro a Scikit-Learn
+
86Regresión Lineal Real
+
87Clasificación de Imágenes
+
88NLP: Procesando Lenguaje
+
89LLMs: Usando OpenAI/Groot
+
90Reto: Predictor de Bolsa
+
+
+ + +
+

10. Nerve Master +

+
+
91Local-First: El Cambio
+
92Arquitectura de Nodos
+
93Comunicación P2P
+
94Manejo de Estado Global
+
95Criptografía en Python
+
96Sincronización de Datos
+
97Microservicios con Nerve
+
98Despliegue Distribuido
+
99Optimización de Recursos
+
100PROYECTO FINAL: PILOT
+
+
+ +
+
+ +
+

© 2026 Nerve Community - Plan Maestro de Ingeniería +

+
+ + + + + + \ No newline at end of file From e232665e3c743a941c2e08945eb7d4e9df1701f8 Mon Sep 17 00:00:00 2001 From: Kaia-Alenia Date: Fri, 31 Jul 2026 13:45:42 -0600 Subject: [PATCH 3/3] Update issue templates for new structure --- .github/CONTRIBUTING.md | 165 +-- .github/ISSUE_TEMPLATE/bug_report.yml | 13 + .../ISSUE_TEMPLATE/propuesta_nuevo_reto.yml | 13 + .github/ISSUE_TEMPLATE/reto.yml | 52 +- .github/PULL_REQUEST_TEMPLATE.md | 44 +- .github/workflows/cierre-pr-alumno.yml | 105 ++ COMO-HACER-TU-PRIMER-PR.md | 171 +-- s.html => Cosas tontas que use/s.html | 0 MEGA_RETO.md | 36 - README.md | 176 +-- .../general/01-lista-de-tareas-cli/README.md | 18 - .../ejemplo_menu_interactivo.go | 152 --- .../ejemplo_menu_interactivo.js | 137 -- .../ejemplo_menu_interactivo.py | 140 -- .../ejemplo_menu_interactivo.rs | 144 --- .../general/02-web-scraper-simple/README.md | 17 - .../02-web-scraper-simple/ejemplo_requests.go | 95 -- .../02-web-scraper-simple/ejemplo_requests.js | 77 -- .../02-web-scraper-simple/ejemplo_requests.py | 83 -- .../02-web-scraper-simple/ejemplo_requests.rs | 75 -- .../general/03-juego-de-adivinanza/README.md | 19 - .../ejemplo_validacion.go | 83 -- .../ejemplo_validacion.js | 79 -- .../ejemplo_validacion.py | 64 - .../ejemplo_validacion.rs | 78 -- .../04-analizador-de-gastos-csv/README.md | 17 - .../ejemplo_csv.go | 125 -- .../ejemplo_csv.js | 88 -- .../ejemplo_csv.py | 107 -- .../ejemplo_csv.rs | 114 -- .../05-generador-de-contrasenas/README.md | 18 - .../ejemplo_secrets.go | 107 -- .../ejemplo_secrets.js | 100 -- .../ejemplo_secrets.py | 92 -- .../ejemplo_secrets.rs | 93 -- .../06-conversor-de-unidades/README.md | 17 - .../ejemplo_funciones.py | 110 -- .../general/07-calculadora-de-imc/README.md | 17 - .../07-calculadora-de-imc/ejemplo_formateo.py | 96 -- .../08-organizador-de-archivos/README.md | 17 - .../ejemplo_pathlib.py | 133 -- .../09-recordatorios-en-terminal/README.md | 17 - .../ejemplo_datetime.py | 112 -- .../general/10-analizador-de-texto/README.md | 17 - .../10-analizador-de-texto/ejemplo_counter.py | 144 --- .../ejemplos/general/11-decoradores/README.md | 19 - .../11-decoradores/ejemplo_decorador.py | 104 -- .../ejemplos/general/12-regex/README.md | 19 - .../general/12-regex/ejemplo_regex.py | 115 -- .../ejemplos/general/13-generadores/README.md | 19 - .../13-generadores/ejemplo_generadores.py | 108 -- .../ejemplos/general/14-argparse/README.md | 25 - .../general/14-argparse/ejemplo_argparse.py | 135 -- .../ejemplos/general/15-shutil/README.md | 20 - .../general/15-shutil/ejemplo_shutil.py | 144 --- .../ejemplos/general/16-excepciones/README.md | 19 - .../16-excepciones/ejemplo_excepciones.py | 131 -- .../ejemplos/general/17-json/README.md | 20 - .../ejemplos/general/17-json/ejemplo_json.py | 142 -- .../general/18-poo-avanzada/README.md | 19 - .../18-poo-avanzada/ejemplo_propiedades.py | 143 --- .../ejemplos/nerve/01-hola-nerve/README.md | 21 - .../ejemplos/nerve/01-hola-nerve/emisor.go | 34 - .../ejemplos/nerve/01-hola-nerve/emisor.js | 28 - .../ejemplos/nerve/01-hola-nerve/emisor.py | 29 - .../ejemplos/nerve/01-hola-nerve/emisor.rs | 33 - .../ejemplos/nerve/01-hola-nerve/receptor.go | 41 - .../ejemplos/nerve/01-hola-nerve/receptor.js | 35 - .../ejemplos/nerve/01-hola-nerve/receptor.py | 36 - .../ejemplos/nerve/01-hola-nerve/receptor.rs | 35 - .../nerve/02-pub-sub-basico/README.md | 22 - .../nerve/02-pub-sub-basico/publicador.go | 46 - .../nerve/02-pub-sub-basico/publicador.js | 35 - .../nerve/02-pub-sub-basico/publicador.py | 33 - .../nerve/02-pub-sub-basico/publicador.rs | 39 - .../nerve/02-pub-sub-basico/suscriptor.go | 49 - .../nerve/02-pub-sub-basico/suscriptor.js | 32 - .../nerve/02-pub-sub-basico/suscriptor.py | 38 - .../nerve/02-pub-sub-basico/suscriptor.rs | 43 - .../nerve/03-peticion-respuesta/README.md | 20 - .../nerve/03-peticion-respuesta/cliente.go | 61 - .../nerve/03-peticion-respuesta/cliente.js | 45 - .../nerve/03-peticion-respuesta/cliente.py | 45 - .../nerve/03-peticion-respuesta/cliente.rs | 72 -- .../nerve/03-peticion-respuesta/servidor.go | 60 - .../nerve/03-peticion-respuesta/servidor.js | 43 - .../nerve/03-peticion-respuesta/servidor.py | 48 - .../nerve/03-peticion-respuesta/servidor.rs | 66 - .../nerve/04-vigilante-de-carpetas/README.md | 16 - .../nerve/04-vigilante-de-carpetas/ejemplo.go | 95 -- .../nerve/04-vigilante-de-carpetas/ejemplo.js | 53 - .../nerve/04-vigilante-de-carpetas/ejemplo.py | 79 -- .../nerve/04-vigilante-de-carpetas/ejemplo.rs | 99 -- .../nerve/05-logger-distribuido/README.md | 14 - .../nerve/05-logger-distribuido/ejemplo.go | 75 -- .../nerve/05-logger-distribuido/ejemplo.js | 62 - .../nerve/05-logger-distribuido/ejemplo.py | 63 - .../nerve/05-logger-distribuido/ejemplo.rs | 71 - .../nerve/06-puente-de-progreso-gif/README.md | 16 - .../06-puente-de-progreso-gif/ejemplo.go | 88 -- .../06-puente-de-progreso-gif/ejemplo.js | 66 - .../06-puente-de-progreso-gif/ejemplo.py | 66 - .../06-puente-de-progreso-gif/ejemplo.rs | 68 - .../nerve/07-bot-discord-nerve/README.md | 18 - .../nerve/07-bot-discord-nerve/ejemplo.go | 91 -- .../nerve/07-bot-discord-nerve/ejemplo.js | 67 - .../nerve/07-bot-discord-nerve/ejemplo.py | 70 - .../nerve/07-bot-discord-nerve/ejemplo.rs | 86 -- .../ejemplos/nerve/08-puente-http/README.md | 77 -- .../ejemplos/nerve/08-puente-http/ejemplo.go | 81 -- .../ejemplos/nerve/08-puente-http/ejemplo.js | 50 - .../ejemplos/nerve/08-puente-http/ejemplo.py | 48 - .../ejemplos/nerve/08-puente-http/ejemplo.rs | 75 -- .../nerve/09-probador-de-carga/README.md | 49 - .../nerve/09-probador-de-carga/ejemplo.go | 68 - .../nerve/09-probador-de-carga/ejemplo.js | 57 - .../nerve/09-probador-de-carga/ejemplo.py | 57 - .../nerve/09-probador-de-carga/ejemplo.rs | 72 -- .../nerve/10-cliente-nuevo-lenguaje/README.md | 43 - .../10-cliente-nuevo-lenguaje/ejemplo.go | 72 -- .../10-cliente-nuevo-lenguaje/ejemplo.js | 69 - .../10-cliente-nuevo-lenguaje/ejemplo.py | 61 - .../10-cliente-nuevo-lenguaje/ejemplo.rs | 77 -- archivo-historico/glosario/README.md | 26 - .../glosario/conceptos-software.md | 76 -- archivo-historico/glosario/git-github.md | 72 -- archivo-historico/glosario/python/README.md | 14 - archivo-historico/glosario/redes-sockets.md | 70 - archivo-historico/glosario/terminal-linux.md | 88 -- .../glosario/terminal-windows.md | 119 -- .../general/01-lista-de-tareas-cli/README.md | 5 - .../soluciones/CesarAAR-python/Functions.py | 33 - .../soluciones/CesarAAR-python/Main.py | 282 ---- .../soluciones/CesarAAR-python/Model.py | 8 - .../soluciones/CesarAAR-python/README.md | 32 - .../CesarAAR-python/requirements.txt | Bin 414 -> 0 bytes .../soluciones/Tacos-python/Readme.md | 1 - .../soluciones/Tacos-python/Tareas.py | 83 -- .../general/02-web-scraper-simple/README.md | 5 - .../general/03-juego-de-adivinanza/README.md | 5 - .../04-analizador-de-gastos-csv/README.md | 5 - .../05-generador-de-contrasenas/README.md | 5 - .../06-conversor-de-unidades/README.md | 5 - .../general/07-calculadora-de-imc/README.md | 5 - .../07_calculadora_de_imc.py | 75 -- .../isaacglezacosta-python/README.md | 15 - .../08-organizador-de-archivos/README.md | 5 - .../09-recordatorios-en-terminal/README.md | 5 - .../general/10-analizador-de-texto/README.md | 5 - .../11-profiler-de-funciones/README.md | 5 - .../general/12-validador-de-emails/README.md | 5 - .../13-lector-archivos-gigantes/README.md | 5 - .../retos/general/14-cli-del-clima/README.md | 5 - .../15-herramienta-de-backup/README.md | 5 - .../general/16-validador-de-edad/README.md | 5 - .../17-lector-de-configuraciones/README.md | 5 - .../general/18-inventario-de-tienda/README.md | 5 - .../retos/nerve/01-chat-terminal/README.md | 5 - .../nerve/02-reloj-sincronizado/README.md | 5 - .../nerve/03-traductor-de-mensajes/README.md | 5 - .../soluciones/.gitkeep | 0 .../nerve/04-vigilante-de-carpetas/.gitkeep | 0 .../nerve/04-vigilante-de-carpetas/README.md | 5 - .../soluciones/.gitkeep | 0 .../nerve/05-logger-distribuido/.gitkeep | 0 .../nerve/05-logger-distribuido/README.md | 5 - .../05-logger-distribuido/soluciones/.gitkeep | 0 .../soluciones/Vidot1-Rust/Cargo.lock | 1142 ----------------- .../soluciones/Vidot1-Rust/Cargo.toml | 21 - .../soluciones/Vidot1-Rust/model.sqlite | 19 - .../soluciones/Vidot1-Rust/readme.md | 36 - .../soluciones/Vidot1-Rust/src/db.rs | 8 - .../soluciones/Vidot1-Rust/src/lib.rs | 82 -- .../soluciones/Vidot1-Rust/src/main.rs | 16 - .../soluciones/Vidot1-Rust/src/nerve.rs | 14 - .../soluciones/Vidot1-Rust/tests/test.rs | 87 -- .../nerve/06-puente-de-progreso-gif/.gitkeep | 0 .../nerve/06-puente-de-progreso-gif/README.md | 5 - .../soluciones/.gitkeep | 0 .../retos/nerve/07-bot-discord-nerve/.gitkeep | 0 .../nerve/07-bot-discord-nerve/README.md | 5 - .../07-bot-discord-nerve/soluciones/.gitkeep | 0 .../retos/nerve/08-puente-http/.gitkeep | 0 .../retos/nerve/08-puente-http/README.md | 5 - .../nerve/08-puente-http/soluciones/.gitkeep | 0 .../retos/nerve/09-probador-de-carga/.gitkeep | 0 .../nerve/09-probador-de-carga/README.md | 5 - .../09-probador-de-carga/soluciones/.gitkeep | 0 .../nerve/10-cliente-nuevo-lenguaje/.gitkeep | 0 .../nerve/10-cliente-nuevo-lenguaje/README.md | 5 - .../soluciones/.gitkeep | 0 assets/.gitkeep | 0 assets/Vi-dot1.png | Bin 47330 -> 0 bytes assets/images/.gitkeep | 0 assets/tacos-termux.jpg | Bin 248736 -> 0 bytes docs/COMO-USAR-COMO-ALUMNO.md | 73 ++ docs/EMPEZAR-CON-SOLO-TU-CELULAR.md | 131 -- docs/PYTHON_GENERAL_ISSUES.md | 655 ---------- docs/RETOS-SOCRATICOS.md | 95 ++ docs/ruta-de-aprendizaje.md | 37 - {00-rompehielo => glosario}/.gitkeep | 0 glosario/README.md | 27 + glosario/conceptos-software.md | 75 ++ glosario/git-github.md | 72 ++ .../go/01-fundamentos.md | 0 .../go/02-control-de-flujo.md | 0 .../glosario => glosario}/go/03-funciones.md | 0 .../go/04-estructuras-de-datos.md | 0 .../go/05-modulos-y-librerias.md | 0 .../go/06-manejo-de-errores.md | 0 .../go/07-archivos-y-entrada-salida.md | 0 .../glosario => glosario}/go/08-poo-basica.md | 0 .../go/09-concurrencia.md | 0 .../glosario => glosario}/go/README.md | 0 .../javascript/01-fundamentos.md | 0 .../javascript/02-control-de-flujo.md | 0 .../javascript/03-funciones.md | 0 .../javascript/04-estructuras-de-datos.md | 0 .../javascript/05-modulos-y-librerias.md | 0 .../javascript/06-manejo-de-errores.md | 0 .../07-archivos-y-entrada-salida.md | 0 .../javascript/08-poo-basica.md | 0 .../javascript/09-asincronia.md | 0 .../javascript/README.md | 0 .../python/01-fundamentos.md | 0 .../python/02-control-de-flujo.md | 0 .../python/03-funciones.md | 0 .../python/04-estructuras-de-datos.md | 0 .../python/05-modulos-y-librerias.md | 0 .../python/06-manejo-de-errores.md | 0 .../python/07-archivos-y-entrada-salida.md | 0 .../python/08-poo-basica.md | 0 glosario/python/09-data-ia.md | 144 +++ glosario/python/10-nerve-ipc.md | 162 +++ glosario/python/11-herramientas-avanzadas.md | 174 +++ glosario/python/12-arquitectura-avanzada.md | 173 +++ glosario/python/README.md | 47 + glosario/redes-sockets.md | 70 + .../rust/01-fundamentos.md | 0 .../rust/02-control-de-flujo.md | 0 .../rust/03-funciones.md | 0 .../rust/04-estructuras-de-datos.md | 0 .../rust/05-modulos-y-librerias.md | 0 .../rust/06-manejo-de-errores.md | 0 .../rust/07-archivos-y-entrada-salida.md | 0 .../rust/08-poo-basica.md | 0 .../glosario => glosario}/rust/README.md | 0 glosario/terminal-linux.md | 85 ++ glosario/terminal-windows.md | 115 ++ .../01-hola-mundo/README.md | 17 +- .../01-hola-mundo/ejemplo.py | 28 +- .../01-hola-mundo/reto.md | 35 +- .../01-hola-mundo/solucion/.gitkeep | 0 .../01-hola-mundo/teoria.md | 31 +- .../01-hola-mundo/test_main.py | 17 + .../02-variables/README.md | 17 +- .../02-variables/ejemplo.py | 50 +- .../02-variables/reto.md | 37 +- .../02-variables/solucion/.gitkeep | 0 .../02-variables/teoria.md | 45 +- .../02-variables/test_main.py | 15 + .../03-textos-enteros/README.md | 10 + .../03-textos-enteros/ejemplo.py | 33 + .../03-textos-enteros/reto.md | 27 + .../03-textos-enteros/teoria.md | 25 + .../03-textos-enteros/test_main.py | 17 + .../03-tipos-de-datos/.gitkeep | 0 .../03-tipos-de-datos/README.md | 13 - .../03-tipos-de-datos/ejemplo.py | 28 - .../03-tipos-de-datos/reto.md | 32 - .../03-tipos-de-datos/solucion/.gitkeep | 0 .../03-tipos-de-datos/teoria.md | 31 - .../04-flotantes-bool/README.md | 10 + .../04-flotantes-bool/ejemplo.py | 29 + .../04-flotantes-bool/reto.md | 27 + .../04-flotantes-bool/teoria.md | 21 + .../04-flotantes-bool/test_main.py | 16 + .../04-operadores/.gitkeep | 0 .../04-operadores/README.md | 20 - .../04-operadores/ejemplo.py | 68 - .../04-operadores/reto.md | 36 - .../04-operadores/solucion/.gitkeep | 0 .../04-operadores/teoria.md | 61 - .../05-aritmetica/README.md | 10 + .../05-aritmetica/ejemplo.py | 37 + .../05-aritmetica/reto.md | 21 + .../05-aritmetica/teoria.md | 34 + .../05-aritmetica/test_main.py | 15 + .../05-condicionales/.gitkeep | 0 .../05-condicionales/README.md | 17 - .../05-condicionales/ejemplo.py | 62 - .../05-condicionales/reto.md | 38 - .../05-condicionales/solucion/.gitkeep | 0 .../05-condicionales/teoria.md | 88 -- .../Modulo-01-Fundamentos/06-inputs/README.md | 10 + .../06-inputs/ejemplo.py | 26 + .../Modulo-01-Fundamentos/06-inputs/reto.md | 28 + .../Modulo-01-Fundamentos/06-inputs/teoria.md | 22 + .../06-inputs/test_main.py | 13 + .../07-f-strings/README.md | 10 + .../07-f-strings/ejemplo.py | 27 + .../07-f-strings/reto.md | 25 + .../07-f-strings/teoria.md | 26 + .../07-f-strings/test_main.py | 13 + .../08-comentarios/README.md | 10 + .../08-comentarios/ejemplo.py | 26 + .../08-comentarios/reto.md | 24 + .../08-comentarios/teoria.md | 27 + .../08-comentarios/test_main.py | 19 + .../09-errores/README.md | 19 + .../09-errores/ejemplo.py | 33 + .../Modulo-01-Fundamentos/09-errores/reto.md | 37 + .../09-errores/teoria.md | 59 + .../09-errores/test_main.py | 13 + .../10-RETO-ficha/README.md | 20 + .../10-RETO-ficha/ejemplo.py | 30 + .../10-RETO-ficha/reto.md | 41 + .../10-RETO-ficha/teoria.md | 55 + .../10-RETO-ficha/test_main.py | 16 + .../RETO-FINAL-MOD-1/.gitkeep | 0 .../RETO-FINAL-MOD-1/README.md | 70 - .../RETO-FINAL-MOD-1/solucion/.gitkeep | 0 python/Modulo-02-Estructuras/.gitkeep | 0 .../Modulo-02-Estructuras/01-listas/.gitkeep | 0 .../Modulo-02-Estructuras/01-listas/README.md | 13 - .../01-listas/ejemplo.py | 61 - .../Modulo-02-Estructuras/01-listas/reto.md | 36 - .../01-listas/solucion/.gitkeep | 0 .../Modulo-02-Estructuras/01-listas/teoria.md | 87 -- .../Modulo-02-Estructuras/02-tuplas/.gitkeep | 0 .../Modulo-02-Estructuras/02-tuplas/README.md | 13 - .../02-tuplas/ejemplo.py | 56 - .../Modulo-02-Estructuras/02-tuplas/reto.md | 34 - .../02-tuplas/solucion/.gitkeep | 0 .../Modulo-02-Estructuras/02-tuplas/teoria.md | 67 - .../03-diccionarios/.gitkeep | 0 .../03-diccionarios/README.md | 13 - .../03-diccionarios/ejemplo.py | 59 - .../03-diccionarios/reto.md | 38 - .../03-diccionarios/solucion/.gitkeep | 0 .../03-diccionarios/teoria.md | 80 -- .../04-bucle-for/.gitkeep | 0 .../04-bucle-for/README.md | 13 - .../04-bucle-for/ejemplo.py | 50 - .../04-bucle-for/reto.md | 39 - .../04-bucle-for/solucion/.gitkeep | 0 .../04-bucle-for/teoria.md | 70 - .../05-bucle-while/.gitkeep | 0 .../05-bucle-while/README.md | 13 - .../05-bucle-while/ejemplo.py | 65 - .../05-bucle-while/reto.md | 38 - .../05-bucle-while/solucion/.gitkeep | 0 .../05-bucle-while/teoria.md | 80 -- .../RETO-FINAL-MOD-2/.gitkeep | 0 .../RETO-FINAL-MOD-2/README.md | 84 -- .../RETO-FINAL-MOD-2/solucion/.gitkeep | 0 .../11-operadores-logicos/README.md | 10 + .../11-operadores-logicos/ejemplo.py | 45 + .../11-operadores-logicos/reto.md | 41 + .../11-operadores-logicos/teoria.md | 53 + .../11-operadores-logicos/test_main.py | 14 + .../Modulo-02-Flujo/12-comparadores/README.md | 10 + .../12-comparadores/ejemplo.py | 42 + .../Modulo-02-Flujo/12-comparadores/reto.md | 45 + .../Modulo-02-Flujo/12-comparadores/teoria.md | 52 + .../12-comparadores/test_main.py | 14 + python/Modulo-02-Flujo/13-if/README.md | 11 + python/Modulo-02-Flujo/13-if/ejemplo.py | 31 + python/Modulo-02-Flujo/13-if/reto.md | 49 + python/Modulo-02-Flujo/13-if/teoria.md | 45 + python/Modulo-02-Flujo/13-if/test_main.py | 14 + python/Modulo-02-Flujo/14-else-elif/README.md | 10 + .../Modulo-02-Flujo/14-else-elif/ejemplo.py | 50 + python/Modulo-02-Flujo/14-else-elif/reto.md | 44 + python/Modulo-02-Flujo/14-else-elif/teoria.md | 45 + .../Modulo-02-Flujo/14-else-elif/test_main.py | 14 + .../15-logica-anidada/README.md | 10 + .../15-logica-anidada/ejemplo.py | 45 + .../Modulo-02-Flujo/15-logica-anidada/reto.md | 42 + .../15-logica-anidada/teoria.md | 49 + .../15-logica-anidada/test_main.py | 14 + .../Modulo-02-Flujo/16-truthy-falsy/README.md | 11 + .../16-truthy-falsy/ejemplo.py | 50 + .../Modulo-02-Flujo/16-truthy-falsy/reto.md | 40 + .../Modulo-02-Flujo/16-truthy-falsy/teoria.md | 56 + .../16-truthy-falsy/test_main.py | 14 + .../Modulo-02-Flujo/17-match-case/README.md | 10 + .../Modulo-02-Flujo/17-match-case/ejemplo.py | 56 + python/Modulo-02-Flujo/17-match-case/reto.md | 43 + .../Modulo-02-Flujo/17-match-case/teoria.md | 42 + .../17-match-case/test_main.py | 10 + .../Modulo-02-Flujo/18-depuracion/README.md | 10 + .../Modulo-02-Flujo/18-depuracion/ejemplo.py | 55 + python/Modulo-02-Flujo/18-depuracion/reto.md | 51 + .../Modulo-02-Flujo/18-depuracion/teoria.md | 43 + .../18-depuracion/test_main.py | 10 + .../Modulo-02-Flujo/19-optimizacion/README.md | 10 + .../19-optimizacion/ejemplo.py | 50 + .../Modulo-02-Flujo/19-optimizacion/reto.md | 52 + .../Modulo-02-Flujo/19-optimizacion/teoria.md | 55 + .../19-optimizacion/test_main.py | 9 + .../Modulo-02-Flujo/20-RETO-portero/README.md | 10 + .../20-RETO-portero/ejemplo.py | 44 + .../Modulo-02-Flujo/20-RETO-portero/reto.md | 40 + .../Modulo-02-Flujo/20-RETO-portero/teoria.md | 30 + .../20-RETO-portero/test_main.py | 10 + .../Modulo-03-Estructuras/21-listas/README.md | 12 + .../21-listas/ejemplo.py | 44 + .../Modulo-03-Estructuras/21-listas/reto.md | 38 + .../Modulo-03-Estructuras/21-listas/teoria.md | 45 + .../21-listas/test_main.py | 11 + .../22-metodos-lista/README.md | 12 + .../22-metodos-lista/ejemplo.py | 42 + .../22-metodos-lista/reto.md | 37 + .../22-metodos-lista/teoria.md | 45 + .../22-metodos-lista/test_main.py | 11 + .../23-slicing/README.md | 12 + .../23-slicing/ejemplo.py | 42 + .../Modulo-03-Estructuras/23-slicing/reto.md | 36 + .../23-slicing/teoria.md | 35 + .../23-slicing/test_main.py | 11 + .../Modulo-03-Estructuras/24-tuplas/README.md | 12 + .../24-tuplas/ejemplo.py | 45 + .../Modulo-03-Estructuras/24-tuplas/reto.md | 34 + .../Modulo-03-Estructuras/24-tuplas/teoria.md | 33 + .../24-tuplas/test_main.py | 11 + .../25-diccionarios/README.md | 12 + .../25-diccionarios/ejemplo.py | 44 + .../25-diccionarios/reto.md | 38 + .../25-diccionarios/teoria.md | 45 + .../25-diccionarios/test_main.py | 11 + .../Modulo-03-Estructuras/26-sets/README.md | 12 + .../Modulo-03-Estructuras/26-sets/ejemplo.py | 42 + python/Modulo-03-Estructuras/26-sets/reto.md | 37 + .../Modulo-03-Estructuras/26-sets/teoria.md | 43 + .../26-sets/test_main.py | 11 + .../27-estructuras-anidadas/README.md | 12 + .../27-estructuras-anidadas/ejemplo.py | 51 + .../27-estructuras-anidadas/reto.md | 41 + .../27-estructuras-anidadas/teoria.md | 43 + .../27-estructuras-anidadas/test_main.py | 11 + .../28-mutabilidad/README.md | 14 + .../28-mutabilidad/ejemplo.py | 40 + .../28-mutabilidad/reto.md | 40 + .../28-mutabilidad/teoria.md | 35 + .../28-mutabilidad/test_main.py | 11 + .../29-deepcopy/README.md | 12 + .../29-deepcopy/ejemplo.py | 39 + .../Modulo-03-Estructuras/29-deepcopy/reto.md | 33 + .../29-deepcopy/teoria.md | 49 + .../29-deepcopy/test_main.py | 11 + .../30-RETO-inventario/README.md | 12 + .../30-RETO-inventario/ejemplo.py | 64 + .../30-RETO-inventario/reto.md | 35 + .../30-RETO-inventario/teoria.md | 32 + .../30-RETO-inventario/test_main.py | 11 + python/Modulo-03-Exterior/.gitkeep | 0 .../Modulo-03-Exterior/01-funciones/.gitkeep | 0 .../Modulo-03-Exterior/01-funciones/README.md | 13 - .../01-funciones/ejemplo.py | 68 - .../Modulo-03-Exterior/01-funciones/reto.md | 33 - .../01-funciones/solucion/.gitkeep | 0 .../Modulo-03-Exterior/01-funciones/teoria.md | 89 -- .../02-input-usuario/.gitkeep | 0 .../02-input-usuario/README.md | 11 - .../02-input-usuario/ejemplo.py | 64 - .../02-input-usuario/reto.md | 37 - .../02-input-usuario/solucion/.gitkeep | 0 .../02-input-usuario/teoria.md | 69 - .../03-manejo-errores/.gitkeep | 0 .../03-manejo-errores/README.md | 13 - .../03-manejo-errores/ejemplo.py | 70 - .../03-manejo-errores/reto.md | 28 - .../03-manejo-errores/solucion/.gitkeep | 0 .../03-manejo-errores/teoria.md | 78 -- .../Modulo-03-Exterior/04-archivos/.gitkeep | 0 .../Modulo-03-Exterior/04-archivos/README.md | 13 - .../Modulo-03-Exterior/04-archivos/ejemplo.py | 60 - python/Modulo-03-Exterior/04-archivos/reto.md | 36 - .../04-archivos/solucion/.gitkeep | 0 .../Modulo-03-Exterior/04-archivos/teoria.md | 94 -- .../RETO-FINAL-MOD-3/.gitkeep | 0 .../RETO-FINAL-MOD-3/README.md | 71 - .../RETO-FINAL-MOD-3/solucion/.gitkeep | 0 python/Modulo-04-Bucles/31-while/README.md | 10 + python/Modulo-04-Bucles/31-while/ejemplo.py | 38 + python/Modulo-04-Bucles/31-while/reto.md | 33 + python/Modulo-04-Bucles/31-while/teoria.md | 37 + python/Modulo-04-Bucles/31-while/test_main.py | 11 + python/Modulo-04-Bucles/32-for/README.md | 10 + python/Modulo-04-Bucles/32-for/ejemplo.py | 40 + python/Modulo-04-Bucles/32-for/reto.md | 34 + python/Modulo-04-Bucles/32-for/teoria.md | 39 + python/Modulo-04-Bucles/32-for/test_main.py | 11 + .../33-range-enumerate/README.md | 10 + .../33-range-enumerate/ejemplo.py | 32 + .../33-range-enumerate/reto.md | 30 + .../33-range-enumerate/teoria.md | 38 + .../33-range-enumerate/test_main.py | 11 + .../34-break-continue/README.md | 10 + .../34-break-continue/ejemplo.py | 36 + .../34-break-continue/reto.md | 31 + .../34-break-continue/teoria.md | 38 + .../34-break-continue/test_main.py | 11 + .../35-list-comprehension/README.md | 12 + .../35-list-comprehension/ejemplo.py | 31 + .../35-list-comprehension/reto.md | 22 + .../35-list-comprehension/teoria.md | 36 + .../35-list-comprehension/test_main.py | 11 + .../36-dict-comprehension/README.md | 10 + .../36-dict-comprehension/ejemplo.py | 31 + .../36-dict-comprehension/reto.md | 24 + .../36-dict-comprehension/teoria.md | 39 + .../36-dict-comprehension/test_main.py | 11 + python/Modulo-04-Bucles/37-zip/README.md | 12 + python/Modulo-04-Bucles/37-zip/ejemplo.py | 30 + python/Modulo-04-Bucles/37-zip/reto.md | 38 + python/Modulo-04-Bucles/37-zip/teoria.md | 35 + python/Modulo-04-Bucles/37-zip/test_main.py | 11 + .../Modulo-04-Bucles/38-iteradores/README.md | 10 + .../Modulo-04-Bucles/38-iteradores/ejemplo.py | 36 + python/Modulo-04-Bucles/38-iteradores/reto.md | 27 + .../Modulo-04-Bucles/38-iteradores/teoria.md | 40 + .../38-iteradores/test_main.py | 11 + python/Modulo-04-Bucles/39-matrices/README.md | 10 + .../Modulo-04-Bucles/39-matrices/ejemplo.py | 39 + python/Modulo-04-Bucles/39-matrices/reto.md | 33 + python/Modulo-04-Bucles/39-matrices/teoria.md | 44 + .../Modulo-04-Bucles/39-matrices/test_main.py | 11 + .../40-RETO-analizador/README.md | 12 + .../40-RETO-analizador/ejemplo.py | 39 + .../40-RETO-analizador/reto.md | 30 + .../40-RETO-analizador/teoria.md | 29 + .../40-RETO-analizador/test_main.py | 11 + python/Modulo-04-Nerve/.gitkeep | 0 .../1-yield/README.md | 17 + .../1-yield/ejemplo.py | 64 + .../1-yield/reto.md | 33 + .../1-yield/teoria.md | 56 + .../1-yield/test_main.py | 9 + .../2-generadores/README.md | 17 + .../2-generadores/ejemplo.py | 72 ++ .../2-generadores/reto.md | 46 + .../2-generadores/teoria.md | 84 ++ .../2-generadores/test_main.py | 9 + .../3-expresiones-generadoras/README.md | 17 + .../3-expresiones-generadoras/ejemplo.py | 61 + .../3-expresiones-generadoras/reto.md | 44 + .../3-expresiones-generadoras/teoria.md | 84 ++ .../3-expresiones-generadoras/test_main.py | 10 + .../4-RETO-pipeline/README.md | 19 + .../4-RETO-pipeline/ejemplo.py | 75 ++ .../4-RETO-pipeline/reto.md | 63 + .../4-RETO-pipeline/teoria.md | 69 + .../4-RETO-pipeline/test_main.py | 10 + python/Modulo-05-Funciones/41-def/README.md | 16 + python/Modulo-05-Funciones/41-def/ejemplo.py | 35 + python/Modulo-05-Funciones/41-def/reto.md | 40 + python/Modulo-05-Funciones/41-def/teoria.md | 52 + .../Modulo-05-Funciones/41-def/test_main.py | 9 + .../42-parametros-return/README.md | 19 + .../42-parametros-return/ejemplo.py | 51 + .../42-parametros-return/reto.md | 30 + .../42-parametros-return/teoria.md | 59 + .../42-parametros-return/test_main.py | 11 + .../43-args-defecto/README.md | 14 + .../43-args-defecto/ejemplo.py | 48 + .../43-args-defecto/reto.md | 34 + .../43-args-defecto/teoria.md | 57 + .../43-args-defecto/test_main.py | 9 + .../44-args-kwargs/README.md | 18 + .../44-args-kwargs/ejemplo.py | 60 + .../44-args-kwargs/reto.md | 42 + .../44-args-kwargs/teoria.md | 60 + .../44-args-kwargs/test_main.py | 10 + python/Modulo-05-Funciones/45-scope/README.md | 16 + .../Modulo-05-Funciones/45-scope/ejemplo.py | 48 + python/Modulo-05-Funciones/45-scope/reto.md | 36 + python/Modulo-05-Funciones/45-scope/teoria.md | 51 + .../Modulo-05-Funciones/45-scope/test_main.py | 9 + .../Modulo-05-Funciones/46-lambdas/README.md | 14 + .../Modulo-05-Funciones/46-lambdas/ejemplo.py | 41 + python/Modulo-05-Funciones/46-lambdas/reto.md | 26 + .../Modulo-05-Funciones/46-lambdas/teoria.md | 44 + .../46-lambdas/test_main.py | 10 + .../47-recursividad/README.md | 14 + .../47-recursividad/ejemplo.py | 57 + .../47-recursividad/reto.md | 30 + .../47-recursividad/teoria.md | 47 + .../47-recursividad/test_main.py | 10 + .../Modulo-05-Funciones/48-import/README.md | 12 + .../Modulo-05-Funciones/48-import/ejemplo.py | 40 + python/Modulo-05-Funciones/48-import/reto.md | 32 + .../Modulo-05-Funciones/48-import/teoria.md | 46 + .../48-import/test_main.py | 10 + .../49-pip-venvs/README.md | 4 + .../49-pip-venvs/ejemplo.py | 30 + .../Modulo-05-Funciones/49-pip-venvs/reto.md | 13 + .../49-pip-venvs/teoria.md | 33 + .../49-pip-venvs/test_main.py | 9 + .../50-RETO-calculadora/README.md | 4 + .../50-RETO-calculadora/ejemplo.py | 35 + .../50-RETO-calculadora/reto.md | 14 + .../50-RETO-calculadora/teoria.md | 13 + .../50-RETO-calculadora/test_main.py | 12 + .../1-type-hints/README.md | 19 + .../1-type-hints/ejemplo.py | 58 + .../1-type-hints/reto.md | 48 + .../1-type-hints/teoria.md | 84 ++ .../1-type-hints/test_main.py | 12 + .../2-docstrings/README.md | 19 + .../2-docstrings/ejemplo.py | 44 + .../2-docstrings/reto.md | 49 + .../2-docstrings/teoria.md | 92 ++ .../2-docstrings/test_main.py | 12 + .../3-pytest-intro/README.md | 17 + .../3-pytest-intro/ejemplo.py | 37 + .../3-pytest-intro/reto.md | 36 + .../3-pytest-intro/teoria.md | 44 + .../3-pytest-intro/test_main.py | 10 + .../4-fixtures-y-parametrize/README.md | 19 + .../4-fixtures-y-parametrize/ejemplo.py | 46 + .../4-fixtures-y-parametrize/reto.md | 29 + .../4-fixtures-y-parametrize/teoria.md | 66 + .../4-fixtures-y-parametrize/test_main.py | 11 + .../5-mocking/README.md | 22 + .../5-mocking/ejemplo.py | 51 + .../5-mocking/reto.md | 57 + .../5-mocking/teoria.md | 61 + .../5-mocking/test_main.py | 10 + .../6-RETO-suite-tests/README.md | 19 + .../6-RETO-suite-tests/ejemplo.py | 69 + .../6-RETO-suite-tests/reto.md | 51 + .../6-RETO-suite-tests/teoria.md | 27 + .../6-RETO-suite-tests/test_main.py | 12 + python/Modulo-06-OS/51-os-rutas/README.md | 14 + python/Modulo-06-OS/51-os-rutas/ejemplo.py | 35 + python/Modulo-06-OS/51-os-rutas/reto.md | 48 + python/Modulo-06-OS/51-os-rutas/teoria.md | 54 + python/Modulo-06-OS/51-os-rutas/test_main.py | 10 + python/Modulo-06-OS/52-txt-read/README.md | 14 + python/Modulo-06-OS/52-txt-read/ejemplo.py | 29 + python/Modulo-06-OS/52-txt-read/reto.md | 45 + python/Modulo-06-OS/52-txt-read/teoria.md | 46 + python/Modulo-06-OS/52-txt-read/test_main.py | 10 + python/Modulo-06-OS/53-txt-write/README.md | 14 + python/Modulo-06-OS/53-txt-write/ejemplo.py | 36 + python/Modulo-06-OS/53-txt-write/reto.md | 46 + python/Modulo-06-OS/53-txt-write/teoria.md | 41 + python/Modulo-06-OS/53-txt-write/test_main.py | 10 + python/Modulo-06-OS/54-csv/README.md | 16 + python/Modulo-06-OS/54-csv/ejemplo.py | 39 + python/Modulo-06-OS/54-csv/reto.md | 50 + python/Modulo-06-OS/54-csv/teoria.md | 51 + python/Modulo-06-OS/54-csv/test_main.py | 10 + python/Modulo-06-OS/55-shutil/README.md | 14 + python/Modulo-06-OS/55-shutil/ejemplo.py | 33 + python/Modulo-06-OS/55-shutil/reto.md | 40 + python/Modulo-06-OS/55-shutil/teoria.md | 46 + python/Modulo-06-OS/55-shutil/test_main.py | 9 + python/Modulo-06-OS/56-datetime/README.md | 14 + python/Modulo-06-OS/56-datetime/ejemplo.py | 40 + python/Modulo-06-OS/56-datetime/reto.md | 44 + python/Modulo-06-OS/56-datetime/teoria.md | 42 + python/Modulo-06-OS/56-datetime/test_main.py | 10 + python/Modulo-06-OS/57-subprocess/README.md | 14 + python/Modulo-06-OS/57-subprocess/ejemplo.py | 23 + python/Modulo-06-OS/57-subprocess/reto.md | 25 + python/Modulo-06-OS/57-subprocess/teoria.md | 45 + .../Modulo-06-OS/57-subprocess/test_main.py | 10 + python/Modulo-06-OS/58-logging/README.md | 12 + python/Modulo-06-OS/58-logging/ejemplo.py | 30 + python/Modulo-06-OS/58-logging/reto.md | 36 + python/Modulo-06-OS/58-logging/teoria.md | 46 + python/Modulo-06-OS/58-logging/test_main.py | 11 + python/Modulo-06-OS/59-try-except/README.md | 12 + python/Modulo-06-OS/59-try-except/ejemplo.py | 26 + python/Modulo-06-OS/59-try-except/reto.md | 29 + python/Modulo-06-OS/59-try-except/teoria.md | 47 + .../Modulo-06-OS/59-try-except/test_main.py | 11 + .../60-RETO-organizador/README.md | 12 + .../60-RETO-organizador/ejemplo.py | 37 + .../Modulo-06-OS/60-RETO-organizador/reto.md | 43 + .../60-RETO-organizador/teoria.md | 33 + .../60-RETO-organizador/test_main.py | 12 + .../Modulo-07-Web-APIs/61-requests/README.md | 14 + .../Modulo-07-Web-APIs/61-requests/ejemplo.py | 48 + python/Modulo-07-Web-APIs/61-requests/reto.md | 31 + .../Modulo-07-Web-APIs/61-requests/teoria.md | 58 + .../61-requests/test_main.py | 32 + python/Modulo-07-Web-APIs/62-json/README.md | 16 + python/Modulo-07-Web-APIs/62-json/ejemplo.py | 43 + python/Modulo-07-Web-APIs/62-json/reto.md | 30 + python/Modulo-07-Web-APIs/62-json/teoria.md | 50 + .../Modulo-07-Web-APIs/62-json/test_main.py | 32 + .../63-beautifulsoup/README.md | 16 + .../63-beautifulsoup/ejemplo.py | 49 + .../63-beautifulsoup/reto.md | 30 + .../63-beautifulsoup/teoria.md | 43 + .../63-beautifulsoup/test_main.py | 32 + .../64-selectores/README.md | 16 + .../64-selectores/ejemplo.py | 59 + .../Modulo-07-Web-APIs/64-selectores/reto.md | 31 + .../64-selectores/teoria.md | 45 + .../64-selectores/test_main.py | 32 + .../Modulo-07-Web-APIs/65-selenium/README.md | 16 + .../Modulo-07-Web-APIs/65-selenium/ejemplo.py | 51 + python/Modulo-07-Web-APIs/65-selenium/reto.md | 33 + .../Modulo-07-Web-APIs/65-selenium/teoria.md | 51 + .../65-selenium/test_main.py | 32 + .../Modulo-07-Web-APIs/66-auth-apis/README.md | 16 + .../66-auth-apis/ejemplo.py | 43 + .../Modulo-07-Web-APIs/66-auth-apis/reto.md | 35 + .../Modulo-07-Web-APIs/66-auth-apis/teoria.md | 42 + .../66-auth-apis/test_main.py | 32 + .../67-rate-limiting/README.md | 21 + .../67-rate-limiting/ejemplo.py | 38 + .../67-rate-limiting/reto.md | 36 + .../67-rate-limiting/teoria.md | 35 + .../67-rate-limiting/test_main.py | 32 + .../Modulo-07-Web-APIs/68-headless/README.md | 17 + .../Modulo-07-Web-APIs/68-headless/ejemplo.py | 40 + python/Modulo-07-Web-APIs/68-headless/reto.md | 37 + .../Modulo-07-Web-APIs/68-headless/teoria.md | 53 + .../68-headless/test_main.py | 32 + .../69-pandas-web/README.md | 14 + .../69-pandas-web/ejemplo.py | 35 + .../Modulo-07-Web-APIs/69-pandas-web/reto.md | 40 + .../69-pandas-web/teoria.md | 39 + .../69-pandas-web/test_main.py | 32 + .../70-RETO-monitor/README.md | 15 + .../70-RETO-monitor/ejemplo.py | 34 + .../70-RETO-monitor/reto.md | 42 + .../70-RETO-monitor/teoria.md | 31 + .../70-RETO-monitor/test_main.py | 32 + .../1-event-loop/README.md | 10 + .../1-event-loop/ejemplo.py | 22 + .../1-event-loop/reto.md | 24 + .../1-event-loop/teoria.md | 30 + .../1-event-loop/test_main.py | 10 + .../2-async-await/README.md | 8 + .../2-async-await/ejemplo.py | 38 + .../2-async-await/reto.md | 29 + .../2-async-await/teoria.md | 25 + .../2-async-await/test_main.py | 11 + .../3-asyncio-gather/README.md | 10 + .../3-asyncio-gather/ejemplo.py | 33 + .../3-asyncio-gather/reto.md | 30 + .../3-asyncio-gather/teoria.md | 19 + .../3-asyncio-gather/test_main.py | 11 + .../4-aiohttp/README.md | 10 + .../4-aiohttp/ejemplo.py | 34 + .../4-aiohttp/reto.md | 24 + .../4-aiohttp/teoria.md | 24 + .../4-aiohttp/test_main.py | 10 + .../5-try-except-en-red/README.md | 10 + .../5-try-except-en-red/ejemplo.py | 47 + .../5-try-except-en-red/reto.md | 28 + .../5-try-except-en-red/teoria.md | 31 + .../5-try-except-en-red/test_main.py | 10 + .../6-RETO-scraper-concurrente/README.md | 11 + .../6-RETO-scraper-concurrente/ejemplo.py | 67 + .../6-RETO-scraper-concurrente/reto.md | 37 + .../6-RETO-scraper-concurrente/teoria.md | 18 + .../6-RETO-scraper-concurrente/test_main.py | 11 + .../Modulo-08-POO/71-clases-objetos/README.md | 12 + .../71-clases-objetos/ejemplo.py | 42 + .../Modulo-08-POO/71-clases-objetos/reto.md | 32 + .../Modulo-08-POO/71-clases-objetos/teoria.md | 56 + .../71-clases-objetos/test_main.py | 11 + python/Modulo-08-POO/72-init/README.md | 12 + python/Modulo-08-POO/72-init/ejemplo.py | 47 + python/Modulo-08-POO/72-init/reto.md | 32 + python/Modulo-08-POO/72-init/teoria.md | 40 + python/Modulo-08-POO/72-init/test_main.py | 11 + .../73-herencia-polimorfismo/README.md | 14 + .../73-herencia-polimorfismo/ejemplo.py | 47 + .../73-herencia-polimorfismo/reto.md | 31 + .../73-herencia-polimorfismo/teoria.md | 45 + .../73-herencia-polimorfismo/test_main.py | 11 + .../74-encapsulamiento/README.md | 13 + .../74-encapsulamiento/ejemplo.py | 44 + .../Modulo-08-POO/74-encapsulamiento/reto.md | 31 + .../74-encapsulamiento/teoria.md | 45 + .../74-encapsulamiento/test_main.py | 11 + python/Modulo-08-POO/75-propiedades/README.md | 12 + .../Modulo-08-POO/75-propiedades/ejemplo.py | 44 + python/Modulo-08-POO/75-propiedades/reto.md | 32 + python/Modulo-08-POO/75-propiedades/teoria.md | 58 + .../Modulo-08-POO/75-propiedades/test_main.py | 11 + .../Modulo-08-POO/76-metodos-clase/README.md | 12 + .../Modulo-08-POO/76-metodos-clase/ejemplo.py | 42 + python/Modulo-08-POO/76-metodos-clase/reto.md | 30 + .../Modulo-08-POO/76-metodos-clase/teoria.md | 48 + .../76-metodos-clase/test_main.py | 11 + .../Modulo-08-POO/77-dunder-methods/README.md | 13 + .../77-dunder-methods/ejemplo.py | 35 + .../Modulo-08-POO/77-dunder-methods/reto.md | 26 + .../Modulo-08-POO/77-dunder-methods/teoria.md | 43 + .../77-dunder-methods/test_main.py | 11 + python/Modulo-08-POO/78-abstraccion/README.md | 15 + .../Modulo-08-POO/78-abstraccion/ejemplo.py | 54 + python/Modulo-08-POO/78-abstraccion/reto.md | 32 + python/Modulo-08-POO/78-abstraccion/teoria.md | 42 + .../Modulo-08-POO/78-abstraccion/test_main.py | 11 + python/Modulo-08-POO/79-singleton/README.md | 18 + python/Modulo-08-POO/79-singleton/ejemplo.py | 50 + python/Modulo-08-POO/79-singleton/reto.md | 39 + python/Modulo-08-POO/79-singleton/teoria.md | 45 + .../Modulo-08-POO/79-singleton/test_main.py | 11 + .../80-RETO-ecosistema/README.md | 12 + .../80-RETO-ecosistema/ejemplo.py | 82 ++ .../Modulo-08-POO/80-RETO-ecosistema/reto.md | 53 + .../80-RETO-ecosistema/teoria.md | 28 + .../80-RETO-ecosistema/test_main.py | 11 + python/Modulo-09-Data-IA/81-numpy/README.md | 14 + python/Modulo-09-Data-IA/81-numpy/ejemplo.py | 35 + python/Modulo-09-Data-IA/81-numpy/reto.md | 24 + python/Modulo-09-Data-IA/81-numpy/teoria.md | 41 + .../Modulo-09-Data-IA/81-numpy/test_main.py | 21 + python/Modulo-09-Data-IA/82-pandas/README.md | 14 + python/Modulo-09-Data-IA/82-pandas/ejemplo.py | 29 + python/Modulo-09-Data-IA/82-pandas/reto.md | 27 + python/Modulo-09-Data-IA/82-pandas/teoria.md | 53 + .../Modulo-09-Data-IA/82-pandas/test_main.py | 21 + .../83-limpieza-datos/README.md | 12 + .../83-limpieza-datos/ejemplo.py | 31 + .../83-limpieza-datos/reto.md | 32 + .../83-limpieza-datos/teoria.md | 44 + .../83-limpieza-datos/test_main.py | 21 + .../Modulo-09-Data-IA/84-matplotlib/README.md | 12 + .../84-matplotlib/ejemplo.py | 29 + .../Modulo-09-Data-IA/84-matplotlib/reto.md | 17 + .../Modulo-09-Data-IA/84-matplotlib/teoria.md | 42 + .../84-matplotlib/test_main.py | 21 + .../85-scikit-learn/README.md | 12 + .../85-scikit-learn/ejemplo.py | 36 + .../Modulo-09-Data-IA/85-scikit-learn/reto.md | 28 + .../85-scikit-learn/teoria.md | 41 + .../85-scikit-learn/test_main.py | 21 + .../Modulo-09-Data-IA/86-regresion/README.md | 12 + .../Modulo-09-Data-IA/86-regresion/ejemplo.py | 32 + python/Modulo-09-Data-IA/86-regresion/reto.md | 23 + .../Modulo-09-Data-IA/86-regresion/teoria.md | 33 + .../86-regresion/test_main.py | 21 + .../87-clasificacion/README.md | 15 + .../87-clasificacion/ejemplo.py | 40 + .../87-clasificacion/reto.md | 33 + .../87-clasificacion/teoria.md | 32 + .../87-clasificacion/test_main.py | 21 + python/Modulo-09-Data-IA/88-nlp/README.md | 12 + python/Modulo-09-Data-IA/88-nlp/ejemplo.py | 35 + python/Modulo-09-Data-IA/88-nlp/reto.md | 27 + python/Modulo-09-Data-IA/88-nlp/teoria.md | 41 + python/Modulo-09-Data-IA/88-nlp/test_main.py | 21 + python/Modulo-09-Data-IA/89-llms/README.md | 10 + python/Modulo-09-Data-IA/89-llms/ejemplo.py | 51 + python/Modulo-09-Data-IA/89-llms/reto.md | 36 + python/Modulo-09-Data-IA/89-llms/teoria.md | 29 + python/Modulo-09-Data-IA/89-llms/test_main.py | 21 + .../90-RETO-predictor/README.md | 12 + .../90-RETO-predictor/ejemplo.py | 46 + .../90-RETO-predictor/reto.md | 36 + .../90-RETO-predictor/teoria.md | 14 + .../90-RETO-predictor/test_main.py | 21 + .../100-RETO-FINAL-PILOT/.gitkeep | 0 .../100-topologia-red}/.gitkeep | 0 .../100-topologia-red/README.md | 11 + .../100-topologia-red/ejemplo.py | 40 + .../Modulo-10-Nerve/100-topologia-red/reto.md | 26 + .../100-topologia-red/teoria.md | 32 + .../100-topologia-red/test_main.py | 26 + .../Modulo-10-Nerve/91-local-first/README.md | 26 + .../Modulo-10-Nerve/91-local-first/ejemplo.py | 43 + python/Modulo-10-Nerve/91-local-first/reto.md | 48 + .../Modulo-10-Nerve/91-local-first/teoria.md | 56 + .../91-local-first/test_main.py | 26 + .../92-arquitectura-nodos/README.md | 13 + .../92-arquitectura-nodos/ejemplo.py | 38 + .../92-arquitectura-nodos/reto.md | 44 + .../92-arquitectura-nodos/teoria.md | 42 + .../92-arquitectura-nodos/test_main.py | 26 + python/Modulo-10-Nerve/93-p2p/README.md | 15 + python/Modulo-10-Nerve/93-p2p/ejemplo.py | 44 + python/Modulo-10-Nerve/93-p2p/reto.md | 44 + python/Modulo-10-Nerve/93-p2p/teoria.md | 46 + python/Modulo-10-Nerve/93-p2p/test_main.py | 26 + .../94-estado-global/README.md | 13 + .../94-estado-global/ejemplo.py | 42 + .../Modulo-10-Nerve/94-estado-global/reto.md | 44 + .../94-estado-global/teoria.md | 43 + .../94-estado-global/test_main.py | 26 + .../Modulo-10-Nerve/95-criptografia/README.md | 13 + .../95-criptografia/ejemplo.py | 42 + .../Modulo-10-Nerve/95-criptografia/reto.md | 39 + .../Modulo-10-Nerve/95-criptografia/teoria.md | 43 + .../95-criptografia/test_main.py | 26 + .../96-sincronizacion/README.md | 22 + .../96-sincronizacion/ejemplo.py | 71 + .../Modulo-10-Nerve/96-sincronizacion/reto.md | 29 + .../96-sincronizacion/teoria.md | 46 + .../96-sincronizacion/test_main.py | 26 + .../97-microservicios/README.md | 18 + .../97-microservicios/ejemplo.py | 73 ++ .../Modulo-10-Nerve/97-microservicios/reto.md | 34 + .../97-microservicios/teoria.md | 44 + .../97-microservicios/test_main.py | 26 + .../Modulo-10-Nerve/98-despliegue/README.md | 17 + .../Modulo-10-Nerve/98-despliegue/ejemplo.py | 27 + python/Modulo-10-Nerve/98-despliegue/reto.md | 38 + .../Modulo-10-Nerve/98-despliegue/teoria.md | 40 + .../98-despliegue/test_main.py | 26 + .../Modulo-10-Nerve/99-optimizacion/README.md | 17 + .../99-optimizacion/ejemplo.py | 35 + .../Modulo-10-Nerve/99-optimizacion/reto.md | 36 + .../Modulo-10-Nerve/99-optimizacion/teoria.md | 38 + .../99-optimizacion/test_main.py | 26 + .../Modulo-11-Herramientas-Nerve}/.gitkeep | 0 .../101-cli-start}/.gitkeep | 0 .../101-cli-start/README.md | 15 + .../101-cli-start/ejemplo.py | 31 + .../101-cli-start/reto.md | 39 + .../101-cli-start/teoria.md | 40 + .../101-cli-start/test_main.py | 26 + .../102-cli-monitor}/.gitkeep | 0 .../102-cli-monitor/README.md | 13 + .../102-cli-monitor/ejemplo.py | 39 + .../102-cli-monitor/reto.md | 30 + .../102-cli-monitor/teoria.md | 39 + .../102-cli-monitor/test_main.py | 26 + .../103-configuracion-externa}/.gitkeep | 0 .../103-configuracion-externa/README.md | 13 + .../103-configuracion-externa/ejemplo.py | 38 + .../103-configuracion-externa/reto.md | 30 + .../103-configuracion-externa/teoria.md | 40 + .../103-configuracion-externa/test_main.py | 26 + .../104-cli-bridge}/.gitkeep | 0 .../104-cli-bridge/README.md | 21 + .../104-cli-bridge/ejemplo.py | 28 + .../104-cli-bridge/teoria.md | 36 + .../105-empaquetado-basico}/.gitkeep | 0 .../105-empaquetado-basico/README.md | 14 + .../105-empaquetado-basico/ejemplo.py | 36 + .../105-empaquetado-basico/reto.md | 28 + .../105-empaquetado-basico/teoria.md | 31 + .../105-empaquetado-basico/test_main.py | 26 + .../106-criptografia-nrv}/.gitkeep | 0 .../106-criptografia-nrv/README.md | 12 + .../106-criptografia-nrv/ejemplo.py | 28 + .../106-criptografia-nrv/reto.md | 28 + .../106-criptografia-nrv/teoria.md | 31 + .../106-criptografia-nrv/test_main.py | 26 + .../107-desempaquetado-nrv}/.gitkeep | 0 .../107-desempaquetado-nrv/README.md | 12 + .../107-desempaquetado-nrv/ejemplo.py | 32 + .../107-desempaquetado-nrv/reto.md | 32 + .../107-desempaquetado-nrv/teoria.md | 33 + .../107-desempaquetado-nrv/test_main.py | 26 + .../108-interfaz-nrv}/.gitkeep | 0 .../108-interfaz-nrv/README.md | 12 + .../108-interfaz-nrv/ejemplo.py | 25 + .../108-interfaz-nrv/reto.md | 29 + .../108-interfaz-nrv/teoria.md | 28 + .../108-interfaz-nrv/test_main.py | 26 + .../109-generacion-pass}/.gitkeep | 0 .../109-generacion-pass/README.md | 12 + .../109-generacion-pass/ejemplo.py | 28 + .../109-generacion-pass/reto.md | 32 + .../109-generacion-pass/teoria.md | 25 + .../109-generacion-pass/test_main.py | 26 + .../110-asociacion-archivos}/.gitkeep | 0 .../110-asociacion-archivos/README.md | 10 + .../110-asociacion-archivos/ejemplo.py | 30 + .../110-asociacion-archivos/reto.md | 30 + .../110-asociacion-archivos/teoria.md | 39 + .../110-asociacion-archivos/test_main.py | 26 + .../Modulo-12-Arquitectura-Avanzada}/.gitkeep | 0 .../111-auto-reconexion}/.gitkeep | 0 .../111-auto-reconexion/README.md | 11 + .../111-auto-reconexion/ejemplo.py | 45 + .../111-auto-reconexion/reto.md | 27 + .../111-auto-reconexion/teoria.md | 51 + .../111-auto-reconexion/test_main.py | 26 + .../112-heartbeats}/.gitkeep | 0 .../112-heartbeats/README.md | 15 + .../112-heartbeats/ejemplo.py | 43 + .../112-heartbeats/reto.md | 28 + .../112-heartbeats/teoria.md | 44 + .../112-heartbeats/test_main.py | 26 + .../113-multiprocessing}/.gitkeep | 0 .../113-multiprocessing/README.md | 13 + .../113-multiprocessing/ejemplo.py | 57 + .../113-multiprocessing/reto.md | 36 + .../113-multiprocessing/teoria.md | 47 + .../113-multiprocessing/test_main.py | 26 + .../114-asyncio-bridge}/.gitkeep | 0 .../114-asyncio-bridge/README.md | 14 + .../114-asyncio-bridge/ejemplo.py | 35 + .../114-asyncio-bridge/reto.md | 29 + .../114-asyncio-bridge/teoria.md | 48 + .../114-asyncio-bridge/test_main.py | 26 + .../115-manejo-estado}/.gitkeep | 0 .../115-manejo-estado/README.md | 15 + .../115-manejo-estado/ejemplo.py | 64 + .../115-manejo-estado/reto.md | 29 + .../115-manejo-estado/teoria.md | 47 + .../115-manejo-estado/test_main.py | 26 + .../116-pipelines-datos}/.gitkeep | 0 .../116-pipelines-datos/README.md | 15 + .../116-pipelines-datos/ejemplo.py | 67 + .../116-pipelines-datos/reto.md | 29 + .../116-pipelines-datos/teoria.md | 39 + .../116-pipelines-datos/test_main.py | 26 + .../117-mensajeria-robusta}/.gitkeep | 0 .../117-mensajeria-robusta/README.md | 15 + .../117-mensajeria-robusta/ejemplo.py | 96 ++ .../117-mensajeria-robusta/reto.md | 38 + .../117-mensajeria-robusta/teoria.md | 50 + .../117-mensajeria-robusta/test_main.py | 26 + .../118-restricciones-rate-limit}/.gitkeep | 0 .../118-restricciones-rate-limit/README.md | 13 + .../118-restricciones-rate-limit/ejemplo.py | 53 + .../118-restricciones-rate-limit/reto.md | 26 + .../118-restricciones-rate-limit/teoria.md | 32 + .../118-restricciones-rate-limit/test_main.py | 26 + .../119-arquitectura-produccion}/.gitkeep | 0 .../119-arquitectura-produccion/README.md | 15 + .../119-arquitectura-produccion/ejemplo.py | 48 + .../119-arquitectura-produccion/reto.md | 34 + .../119-arquitectura-produccion/teoria.md | 29 + .../119-arquitectura-produccion/test_main.py | 26 + .../120-RETO-FINAL-DISTRIBUIDO}/.gitkeep | 0 .../120-RETO-FINAL-DISTRIBUIDO/README.md | 17 + .../120-RETO-FINAL-DISTRIBUIDO/ejemplo.py | 72 ++ .../120-RETO-FINAL-DISTRIBUIDO/reto.md | 46 + .../120-RETO-FINAL-DISTRIBUIDO/teoria.md | 45 + .../120-RETO-FINAL-DISTRIBUIDO/test_main.py | 26 + .../retos => retos}/00-rompehielo/CesarAAR.py | 0 .../retos => retos}/00-rompehielo/README.md | 0 .../retos => retos}/00-rompehielo/Vi-dot1.cpp | 0 .../retos => retos}/00-rompehielo/ashu.py | 0 .../retos => retos}/00-rompehielo/tacos.py | 0 .../go/M01-M02}/.gitkeep | 0 .../javascript/M01-M02}/.gitkeep | 0 .../M01-M02/01-generador-tarjetas/reto.md | 28 + .../01-generador-tarjetas/test_main.py | 106 ++ .../M01-M02/02-calculadora-cientifica/reto.md | 31 + .../02-calculadora-cientifica/test_main.py | 118 ++ .../M03-M04/01-organizador-coleccion/reto.md | 30 + .../01-organizador-coleccion/test_main.py | 115 ++ .../02-analizador-texto-simple/reto.md | 31 + .../02-analizador-texto-simple/test_main.py | 131 ++ retos/python/M05-M06/01-cli-personal/reto.md | 31 + .../M05-M06/01-cli-personal/test_main.py | 132 ++ .../python/M05-M06/02-procesador-csv/reto.md | 32 + .../M05-M06/02-procesador-csv/test_main.py | 108 ++ .../python/M07-M08/01-scraper-tablas/reto.md | 31 + .../M07-M08/01-scraper-tablas/test_main.py | 125 ++ .../M07-M08/02-gestor-inventario-poo/reto.md | 32 + .../02-gestor-inventario-poo/test_main.py | 135 ++ .../M09-M12/01-predictor-gastos/reto.md | 31 + .../M09-M12/01-predictor-gastos/test_main.py | 112 ++ .../M09-M12/02-red-scripts-nerve/reto.md | 30 + .../M09-M12/02-red-scripts-nerve/test_main.py | 111 ++ .../rust/M01-M02}/.gitkeep | 0 1065 files changed, 23503 insertions(+), 13590 deletions(-) create mode 100644 .github/workflows/cierre-pr-alumno.yml rename s.html => Cosas tontas que use/s.html (100%) delete mode 100644 MEGA_RETO.md delete mode 100644 archivo-historico/ejemplos/general/01-lista-de-tareas-cli/README.md delete mode 100644 archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go delete mode 100644 archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js delete mode 100644 archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py delete mode 100644 archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs delete mode 100644 archivo-historico/ejemplos/general/02-web-scraper-simple/README.md delete mode 100644 archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.go delete mode 100644 archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.js delete mode 100644 archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.py delete mode 100644 archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs delete mode 100644 archivo-historico/ejemplos/general/03-juego-de-adivinanza/README.md delete mode 100644 archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go delete mode 100644 archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js delete mode 100644 archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py delete mode 100644 archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs delete mode 100644 archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/README.md delete mode 100644 archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go delete mode 100644 archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js delete mode 100644 archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py delete mode 100644 archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs delete mode 100644 archivo-historico/ejemplos/general/05-generador-de-contrasenas/README.md delete mode 100644 archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go delete mode 100644 archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js delete mode 100644 archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py delete mode 100644 archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs delete mode 100644 archivo-historico/ejemplos/general/06-conversor-de-unidades/README.md delete mode 100644 archivo-historico/ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py delete mode 100644 archivo-historico/ejemplos/general/07-calculadora-de-imc/README.md delete mode 100644 archivo-historico/ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py delete mode 100644 archivo-historico/ejemplos/general/08-organizador-de-archivos/README.md delete mode 100644 archivo-historico/ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py delete mode 100644 archivo-historico/ejemplos/general/09-recordatorios-en-terminal/README.md delete mode 100644 archivo-historico/ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py delete mode 100644 archivo-historico/ejemplos/general/10-analizador-de-texto/README.md delete mode 100644 archivo-historico/ejemplos/general/10-analizador-de-texto/ejemplo_counter.py delete mode 100644 archivo-historico/ejemplos/general/11-decoradores/README.md delete mode 100644 archivo-historico/ejemplos/general/11-decoradores/ejemplo_decorador.py delete mode 100644 archivo-historico/ejemplos/general/12-regex/README.md delete mode 100644 archivo-historico/ejemplos/general/12-regex/ejemplo_regex.py delete mode 100644 archivo-historico/ejemplos/general/13-generadores/README.md delete mode 100644 archivo-historico/ejemplos/general/13-generadores/ejemplo_generadores.py delete mode 100644 archivo-historico/ejemplos/general/14-argparse/README.md delete mode 100644 archivo-historico/ejemplos/general/14-argparse/ejemplo_argparse.py delete mode 100644 archivo-historico/ejemplos/general/15-shutil/README.md delete mode 100644 archivo-historico/ejemplos/general/15-shutil/ejemplo_shutil.py delete mode 100644 archivo-historico/ejemplos/general/16-excepciones/README.md delete mode 100644 archivo-historico/ejemplos/general/16-excepciones/ejemplo_excepciones.py delete mode 100644 archivo-historico/ejemplos/general/17-json/README.md delete mode 100644 archivo-historico/ejemplos/general/17-json/ejemplo_json.py delete mode 100644 archivo-historico/ejemplos/general/18-poo-avanzada/README.md delete mode 100644 archivo-historico/ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/README.md delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.go delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.js delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.py delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.rs delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.go delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.js delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.py delete mode 100644 archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.rs delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/README.md delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.go delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.js delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.py delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.rs delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.go delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.js delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.py delete mode 100644 archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.rs delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/README.md delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.go delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.js delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.py delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.rs delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.go delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.js delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.py delete mode 100644 archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.rs delete mode 100644 archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/README.md delete mode 100644 archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go delete mode 100644 archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js delete mode 100644 archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py delete mode 100644 archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs delete mode 100644 archivo-historico/ejemplos/nerve/05-logger-distribuido/README.md delete mode 100644 archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.go delete mode 100644 archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.js delete mode 100644 archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.py delete mode 100644 archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.rs delete mode 100644 archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/README.md delete mode 100644 archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go delete mode 100644 archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js delete mode 100644 archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py delete mode 100644 archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs delete mode 100644 archivo-historico/ejemplos/nerve/07-bot-discord-nerve/README.md delete mode 100644 archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.go delete mode 100644 archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.js delete mode 100644 archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.py delete mode 100644 archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs delete mode 100644 archivo-historico/ejemplos/nerve/08-puente-http/README.md delete mode 100644 archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.go delete mode 100644 archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.js delete mode 100644 archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.py delete mode 100644 archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.rs delete mode 100644 archivo-historico/ejemplos/nerve/09-probador-de-carga/README.md delete mode 100644 archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.go delete mode 100644 archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.js delete mode 100644 archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.py delete mode 100644 archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.rs delete mode 100644 archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md delete mode 100644 archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go delete mode 100644 archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js delete mode 100644 archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py delete mode 100644 archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs delete mode 100644 archivo-historico/glosario/README.md delete mode 100644 archivo-historico/glosario/conceptos-software.md delete mode 100644 archivo-historico/glosario/git-github.md delete mode 100644 archivo-historico/glosario/python/README.md delete mode 100644 archivo-historico/glosario/redes-sockets.md delete mode 100644 archivo-historico/glosario/terminal-linux.md delete mode 100644 archivo-historico/glosario/terminal-windows.md delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/README.md delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md delete mode 100644 archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py delete mode 100644 archivo-historico/retos/general/02-web-scraper-simple/README.md delete mode 100644 archivo-historico/retos/general/03-juego-de-adivinanza/README.md delete mode 100644 archivo-historico/retos/general/04-analizador-de-gastos-csv/README.md delete mode 100644 archivo-historico/retos/general/05-generador-de-contrasenas/README.md delete mode 100644 archivo-historico/retos/general/06-conversor-de-unidades/README.md delete mode 100644 archivo-historico/retos/general/07-calculadora-de-imc/README.md delete mode 100644 archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py delete mode 100644 archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md delete mode 100644 archivo-historico/retos/general/08-organizador-de-archivos/README.md delete mode 100644 archivo-historico/retos/general/09-recordatorios-en-terminal/README.md delete mode 100644 archivo-historico/retos/general/10-analizador-de-texto/README.md delete mode 100644 archivo-historico/retos/general/11-profiler-de-funciones/README.md delete mode 100644 archivo-historico/retos/general/12-validador-de-emails/README.md delete mode 100644 archivo-historico/retos/general/13-lector-archivos-gigantes/README.md delete mode 100644 archivo-historico/retos/general/14-cli-del-clima/README.md delete mode 100644 archivo-historico/retos/general/15-herramienta-de-backup/README.md delete mode 100644 archivo-historico/retos/general/16-validador-de-edad/README.md delete mode 100644 archivo-historico/retos/general/17-lector-de-configuraciones/README.md delete mode 100644 archivo-historico/retos/general/18-inventario-de-tienda/README.md delete mode 100644 archivo-historico/retos/nerve/01-chat-terminal/README.md delete mode 100644 archivo-historico/retos/nerve/02-reloj-sincronizado/README.md delete mode 100644 archivo-historico/retos/nerve/03-traductor-de-mensajes/README.md delete mode 100644 archivo-historico/retos/nerve/03-traductor-de-mensajes/soluciones/.gitkeep delete mode 100644 archivo-historico/retos/nerve/04-vigilante-de-carpetas/.gitkeep delete mode 100644 archivo-historico/retos/nerve/04-vigilante-de-carpetas/README.md delete mode 100644 archivo-historico/retos/nerve/04-vigilante-de-carpetas/soluciones/.gitkeep delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/.gitkeep delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/README.md delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/.gitkeep delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs delete mode 100644 archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs delete mode 100644 archivo-historico/retos/nerve/06-puente-de-progreso-gif/.gitkeep delete mode 100644 archivo-historico/retos/nerve/06-puente-de-progreso-gif/README.md delete mode 100644 archivo-historico/retos/nerve/06-puente-de-progreso-gif/soluciones/.gitkeep delete mode 100644 archivo-historico/retos/nerve/07-bot-discord-nerve/.gitkeep delete mode 100644 archivo-historico/retos/nerve/07-bot-discord-nerve/README.md delete mode 100644 archivo-historico/retos/nerve/07-bot-discord-nerve/soluciones/.gitkeep delete mode 100644 archivo-historico/retos/nerve/08-puente-http/.gitkeep delete mode 100644 archivo-historico/retos/nerve/08-puente-http/README.md delete mode 100644 archivo-historico/retos/nerve/08-puente-http/soluciones/.gitkeep delete mode 100644 archivo-historico/retos/nerve/09-probador-de-carga/.gitkeep delete mode 100644 archivo-historico/retos/nerve/09-probador-de-carga/README.md delete mode 100644 archivo-historico/retos/nerve/09-probador-de-carga/soluciones/.gitkeep delete mode 100644 archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep delete mode 100644 archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/README.md delete mode 100644 archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep delete mode 100644 assets/.gitkeep delete mode 100644 assets/Vi-dot1.png delete mode 100644 assets/images/.gitkeep delete mode 100644 assets/tacos-termux.jpg create mode 100644 docs/COMO-USAR-COMO-ALUMNO.md delete mode 100644 docs/EMPEZAR-CON-SOLO-TU-CELULAR.md delete mode 100644 docs/PYTHON_GENERAL_ISSUES.md create mode 100644 docs/RETOS-SOCRATICOS.md delete mode 100644 docs/ruta-de-aprendizaje.md rename {00-rompehielo => glosario}/.gitkeep (100%) create mode 100644 glosario/README.md create mode 100644 glosario/conceptos-software.md create mode 100644 glosario/git-github.md rename {archivo-historico/glosario => glosario}/go/01-fundamentos.md (100%) rename {archivo-historico/glosario => glosario}/go/02-control-de-flujo.md (100%) rename {archivo-historico/glosario => glosario}/go/03-funciones.md (100%) rename {archivo-historico/glosario => glosario}/go/04-estructuras-de-datos.md (100%) rename {archivo-historico/glosario => glosario}/go/05-modulos-y-librerias.md (100%) rename {archivo-historico/glosario => glosario}/go/06-manejo-de-errores.md (100%) rename {archivo-historico/glosario => glosario}/go/07-archivos-y-entrada-salida.md (100%) rename {archivo-historico/glosario => glosario}/go/08-poo-basica.md (100%) rename {archivo-historico/glosario => glosario}/go/09-concurrencia.md (100%) rename {archivo-historico/glosario => glosario}/go/README.md (100%) rename {archivo-historico/glosario => glosario}/javascript/01-fundamentos.md (100%) rename {archivo-historico/glosario => glosario}/javascript/02-control-de-flujo.md (100%) rename {archivo-historico/glosario => glosario}/javascript/03-funciones.md (100%) rename {archivo-historico/glosario => glosario}/javascript/04-estructuras-de-datos.md (100%) rename {archivo-historico/glosario => glosario}/javascript/05-modulos-y-librerias.md (100%) rename {archivo-historico/glosario => glosario}/javascript/06-manejo-de-errores.md (100%) rename {archivo-historico/glosario => glosario}/javascript/07-archivos-y-entrada-salida.md (100%) rename {archivo-historico/glosario => glosario}/javascript/08-poo-basica.md (100%) rename {archivo-historico/glosario => glosario}/javascript/09-asincronia.md (100%) rename {archivo-historico/glosario => glosario}/javascript/README.md (100%) rename {archivo-historico/glosario => glosario}/python/01-fundamentos.md (100%) rename {archivo-historico/glosario => glosario}/python/02-control-de-flujo.md (100%) rename {archivo-historico/glosario => glosario}/python/03-funciones.md (100%) rename {archivo-historico/glosario => glosario}/python/04-estructuras-de-datos.md (100%) rename {archivo-historico/glosario => glosario}/python/05-modulos-y-librerias.md (100%) rename {archivo-historico/glosario => glosario}/python/06-manejo-de-errores.md (100%) rename {archivo-historico/glosario => glosario}/python/07-archivos-y-entrada-salida.md (100%) rename {archivo-historico/glosario => glosario}/python/08-poo-basica.md (100%) create mode 100644 glosario/python/09-data-ia.md create mode 100644 glosario/python/10-nerve-ipc.md create mode 100644 glosario/python/11-herramientas-avanzadas.md create mode 100644 glosario/python/12-arquitectura-avanzada.md create mode 100644 glosario/python/README.md create mode 100644 glosario/redes-sockets.md rename {archivo-historico/glosario => glosario}/rust/01-fundamentos.md (100%) rename {archivo-historico/glosario => glosario}/rust/02-control-de-flujo.md (100%) rename {archivo-historico/glosario => glosario}/rust/03-funciones.md (100%) rename {archivo-historico/glosario => glosario}/rust/04-estructuras-de-datos.md (100%) rename {archivo-historico/glosario => glosario}/rust/05-modulos-y-librerias.md (100%) rename {archivo-historico/glosario => glosario}/rust/06-manejo-de-errores.md (100%) rename {archivo-historico/glosario => glosario}/rust/07-archivos-y-entrada-salida.md (100%) rename {archivo-historico/glosario => glosario}/rust/08-poo-basica.md (100%) rename {archivo-historico/glosario => glosario}/rust/README.md (100%) create mode 100644 glosario/terminal-linux.md create mode 100644 glosario/terminal-windows.md delete mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/solucion/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/01-hola-mundo/test_main.py delete mode 100644 python/Modulo-01-Fundamentos/02-variables/solucion/.gitkeep create mode 100644 python/Modulo-01-Fundamentos/02-variables/test_main.py create mode 100644 python/Modulo-01-Fundamentos/03-textos-enteros/README.md create mode 100644 python/Modulo-01-Fundamentos/03-textos-enteros/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/03-textos-enteros/reto.md create mode 100644 python/Modulo-01-Fundamentos/03-textos-enteros/teoria.md create mode 100644 python/Modulo-01-Fundamentos/03-textos-enteros/test_main.py delete mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/.gitkeep delete mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/README.md delete mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/ejemplo.py delete mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/reto.md delete mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/solucion/.gitkeep delete mode 100644 python/Modulo-01-Fundamentos/03-tipos-de-datos/teoria.md create mode 100644 python/Modulo-01-Fundamentos/04-flotantes-bool/README.md create mode 100644 python/Modulo-01-Fundamentos/04-flotantes-bool/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/04-flotantes-bool/reto.md create mode 100644 python/Modulo-01-Fundamentos/04-flotantes-bool/teoria.md create mode 100644 python/Modulo-01-Fundamentos/04-flotantes-bool/test_main.py delete mode 100644 python/Modulo-01-Fundamentos/04-operadores/.gitkeep delete mode 100644 python/Modulo-01-Fundamentos/04-operadores/README.md delete mode 100644 python/Modulo-01-Fundamentos/04-operadores/ejemplo.py delete mode 100644 python/Modulo-01-Fundamentos/04-operadores/reto.md delete mode 100644 python/Modulo-01-Fundamentos/04-operadores/solucion/.gitkeep delete mode 100644 python/Modulo-01-Fundamentos/04-operadores/teoria.md create mode 100644 python/Modulo-01-Fundamentos/05-aritmetica/README.md create mode 100644 python/Modulo-01-Fundamentos/05-aritmetica/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/05-aritmetica/reto.md create mode 100644 python/Modulo-01-Fundamentos/05-aritmetica/teoria.md create mode 100644 python/Modulo-01-Fundamentos/05-aritmetica/test_main.py delete mode 100644 python/Modulo-01-Fundamentos/05-condicionales/.gitkeep delete mode 100644 python/Modulo-01-Fundamentos/05-condicionales/README.md delete mode 100644 python/Modulo-01-Fundamentos/05-condicionales/ejemplo.py delete mode 100644 python/Modulo-01-Fundamentos/05-condicionales/reto.md delete mode 100644 python/Modulo-01-Fundamentos/05-condicionales/solucion/.gitkeep delete mode 100644 python/Modulo-01-Fundamentos/05-condicionales/teoria.md create mode 100644 python/Modulo-01-Fundamentos/06-inputs/README.md create mode 100644 python/Modulo-01-Fundamentos/06-inputs/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/06-inputs/reto.md create mode 100644 python/Modulo-01-Fundamentos/06-inputs/teoria.md create mode 100644 python/Modulo-01-Fundamentos/06-inputs/test_main.py create mode 100644 python/Modulo-01-Fundamentos/07-f-strings/README.md create mode 100644 python/Modulo-01-Fundamentos/07-f-strings/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/07-f-strings/reto.md create mode 100644 python/Modulo-01-Fundamentos/07-f-strings/teoria.md create mode 100644 python/Modulo-01-Fundamentos/07-f-strings/test_main.py create mode 100644 python/Modulo-01-Fundamentos/08-comentarios/README.md create mode 100644 python/Modulo-01-Fundamentos/08-comentarios/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/08-comentarios/reto.md create mode 100644 python/Modulo-01-Fundamentos/08-comentarios/teoria.md create mode 100644 python/Modulo-01-Fundamentos/08-comentarios/test_main.py create mode 100644 python/Modulo-01-Fundamentos/09-errores/README.md create mode 100644 python/Modulo-01-Fundamentos/09-errores/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/09-errores/reto.md create mode 100644 python/Modulo-01-Fundamentos/09-errores/teoria.md create mode 100644 python/Modulo-01-Fundamentos/09-errores/test_main.py create mode 100644 python/Modulo-01-Fundamentos/10-RETO-ficha/README.md create mode 100644 python/Modulo-01-Fundamentos/10-RETO-ficha/ejemplo.py create mode 100644 python/Modulo-01-Fundamentos/10-RETO-ficha/reto.md create mode 100644 python/Modulo-01-Fundamentos/10-RETO-ficha/teoria.md create mode 100644 python/Modulo-01-Fundamentos/10-RETO-ficha/test_main.py delete mode 100644 python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/.gitkeep delete mode 100644 python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/README.md delete mode 100644 python/Modulo-01-Fundamentos/RETO-FINAL-MOD-1/solucion/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/01-listas/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/01-listas/README.md delete mode 100644 python/Modulo-02-Estructuras/01-listas/ejemplo.py delete mode 100644 python/Modulo-02-Estructuras/01-listas/reto.md delete mode 100644 python/Modulo-02-Estructuras/01-listas/solucion/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/01-listas/teoria.md delete mode 100644 python/Modulo-02-Estructuras/02-tuplas/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/02-tuplas/README.md delete mode 100644 python/Modulo-02-Estructuras/02-tuplas/ejemplo.py delete mode 100644 python/Modulo-02-Estructuras/02-tuplas/reto.md delete mode 100644 python/Modulo-02-Estructuras/02-tuplas/solucion/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/02-tuplas/teoria.md delete mode 100644 python/Modulo-02-Estructuras/03-diccionarios/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/03-diccionarios/README.md delete mode 100644 python/Modulo-02-Estructuras/03-diccionarios/ejemplo.py delete mode 100644 python/Modulo-02-Estructuras/03-diccionarios/reto.md delete mode 100644 python/Modulo-02-Estructuras/03-diccionarios/solucion/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/03-diccionarios/teoria.md delete mode 100644 python/Modulo-02-Estructuras/04-bucle-for/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/04-bucle-for/README.md delete mode 100644 python/Modulo-02-Estructuras/04-bucle-for/ejemplo.py delete mode 100644 python/Modulo-02-Estructuras/04-bucle-for/reto.md delete mode 100644 python/Modulo-02-Estructuras/04-bucle-for/solucion/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/04-bucle-for/teoria.md delete mode 100644 python/Modulo-02-Estructuras/05-bucle-while/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/05-bucle-while/README.md delete mode 100644 python/Modulo-02-Estructuras/05-bucle-while/ejemplo.py delete mode 100644 python/Modulo-02-Estructuras/05-bucle-while/reto.md delete mode 100644 python/Modulo-02-Estructuras/05-bucle-while/solucion/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/05-bucle-while/teoria.md delete mode 100644 python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/.gitkeep delete mode 100644 python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/README.md delete mode 100644 python/Modulo-02-Estructuras/RETO-FINAL-MOD-2/solucion/.gitkeep create mode 100644 python/Modulo-02-Flujo/11-operadores-logicos/README.md create mode 100644 python/Modulo-02-Flujo/11-operadores-logicos/ejemplo.py create mode 100644 python/Modulo-02-Flujo/11-operadores-logicos/reto.md create mode 100644 python/Modulo-02-Flujo/11-operadores-logicos/teoria.md create mode 100644 python/Modulo-02-Flujo/11-operadores-logicos/test_main.py create mode 100644 python/Modulo-02-Flujo/12-comparadores/README.md create mode 100644 python/Modulo-02-Flujo/12-comparadores/ejemplo.py create mode 100644 python/Modulo-02-Flujo/12-comparadores/reto.md create mode 100644 python/Modulo-02-Flujo/12-comparadores/teoria.md create mode 100644 python/Modulo-02-Flujo/12-comparadores/test_main.py create mode 100644 python/Modulo-02-Flujo/13-if/README.md create mode 100644 python/Modulo-02-Flujo/13-if/ejemplo.py create mode 100644 python/Modulo-02-Flujo/13-if/reto.md create mode 100644 python/Modulo-02-Flujo/13-if/teoria.md create mode 100644 python/Modulo-02-Flujo/13-if/test_main.py create mode 100644 python/Modulo-02-Flujo/14-else-elif/README.md create mode 100644 python/Modulo-02-Flujo/14-else-elif/ejemplo.py create mode 100644 python/Modulo-02-Flujo/14-else-elif/reto.md create mode 100644 python/Modulo-02-Flujo/14-else-elif/teoria.md create mode 100644 python/Modulo-02-Flujo/14-else-elif/test_main.py create mode 100644 python/Modulo-02-Flujo/15-logica-anidada/README.md create mode 100644 python/Modulo-02-Flujo/15-logica-anidada/ejemplo.py create mode 100644 python/Modulo-02-Flujo/15-logica-anidada/reto.md create mode 100644 python/Modulo-02-Flujo/15-logica-anidada/teoria.md create mode 100644 python/Modulo-02-Flujo/15-logica-anidada/test_main.py create mode 100644 python/Modulo-02-Flujo/16-truthy-falsy/README.md create mode 100644 python/Modulo-02-Flujo/16-truthy-falsy/ejemplo.py create mode 100644 python/Modulo-02-Flujo/16-truthy-falsy/reto.md create mode 100644 python/Modulo-02-Flujo/16-truthy-falsy/teoria.md create mode 100644 python/Modulo-02-Flujo/16-truthy-falsy/test_main.py create mode 100644 python/Modulo-02-Flujo/17-match-case/README.md create mode 100644 python/Modulo-02-Flujo/17-match-case/ejemplo.py create mode 100644 python/Modulo-02-Flujo/17-match-case/reto.md create mode 100644 python/Modulo-02-Flujo/17-match-case/teoria.md create mode 100644 python/Modulo-02-Flujo/17-match-case/test_main.py create mode 100644 python/Modulo-02-Flujo/18-depuracion/README.md create mode 100644 python/Modulo-02-Flujo/18-depuracion/ejemplo.py create mode 100644 python/Modulo-02-Flujo/18-depuracion/reto.md create mode 100644 python/Modulo-02-Flujo/18-depuracion/teoria.md create mode 100644 python/Modulo-02-Flujo/18-depuracion/test_main.py create mode 100644 python/Modulo-02-Flujo/19-optimizacion/README.md create mode 100644 python/Modulo-02-Flujo/19-optimizacion/ejemplo.py create mode 100644 python/Modulo-02-Flujo/19-optimizacion/reto.md create mode 100644 python/Modulo-02-Flujo/19-optimizacion/teoria.md create mode 100644 python/Modulo-02-Flujo/19-optimizacion/test_main.py create mode 100644 python/Modulo-02-Flujo/20-RETO-portero/README.md create mode 100644 python/Modulo-02-Flujo/20-RETO-portero/ejemplo.py create mode 100644 python/Modulo-02-Flujo/20-RETO-portero/reto.md create mode 100644 python/Modulo-02-Flujo/20-RETO-portero/teoria.md create mode 100644 python/Modulo-02-Flujo/20-RETO-portero/test_main.py create mode 100644 python/Modulo-03-Estructuras/21-listas/README.md create mode 100644 python/Modulo-03-Estructuras/21-listas/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/21-listas/reto.md create mode 100644 python/Modulo-03-Estructuras/21-listas/teoria.md create mode 100644 python/Modulo-03-Estructuras/21-listas/test_main.py create mode 100644 python/Modulo-03-Estructuras/22-metodos-lista/README.md create mode 100644 python/Modulo-03-Estructuras/22-metodos-lista/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/22-metodos-lista/reto.md create mode 100644 python/Modulo-03-Estructuras/22-metodos-lista/teoria.md create mode 100644 python/Modulo-03-Estructuras/22-metodos-lista/test_main.py create mode 100644 python/Modulo-03-Estructuras/23-slicing/README.md create mode 100644 python/Modulo-03-Estructuras/23-slicing/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/23-slicing/reto.md create mode 100644 python/Modulo-03-Estructuras/23-slicing/teoria.md create mode 100644 python/Modulo-03-Estructuras/23-slicing/test_main.py create mode 100644 python/Modulo-03-Estructuras/24-tuplas/README.md create mode 100644 python/Modulo-03-Estructuras/24-tuplas/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/24-tuplas/reto.md create mode 100644 python/Modulo-03-Estructuras/24-tuplas/teoria.md create mode 100644 python/Modulo-03-Estructuras/24-tuplas/test_main.py create mode 100644 python/Modulo-03-Estructuras/25-diccionarios/README.md create mode 100644 python/Modulo-03-Estructuras/25-diccionarios/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/25-diccionarios/reto.md create mode 100644 python/Modulo-03-Estructuras/25-diccionarios/teoria.md create mode 100644 python/Modulo-03-Estructuras/25-diccionarios/test_main.py create mode 100644 python/Modulo-03-Estructuras/26-sets/README.md create mode 100644 python/Modulo-03-Estructuras/26-sets/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/26-sets/reto.md create mode 100644 python/Modulo-03-Estructuras/26-sets/teoria.md create mode 100644 python/Modulo-03-Estructuras/26-sets/test_main.py create mode 100644 python/Modulo-03-Estructuras/27-estructuras-anidadas/README.md create mode 100644 python/Modulo-03-Estructuras/27-estructuras-anidadas/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/27-estructuras-anidadas/reto.md create mode 100644 python/Modulo-03-Estructuras/27-estructuras-anidadas/teoria.md create mode 100644 python/Modulo-03-Estructuras/27-estructuras-anidadas/test_main.py create mode 100644 python/Modulo-03-Estructuras/28-mutabilidad/README.md create mode 100644 python/Modulo-03-Estructuras/28-mutabilidad/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/28-mutabilidad/reto.md create mode 100644 python/Modulo-03-Estructuras/28-mutabilidad/teoria.md create mode 100644 python/Modulo-03-Estructuras/28-mutabilidad/test_main.py create mode 100644 python/Modulo-03-Estructuras/29-deepcopy/README.md create mode 100644 python/Modulo-03-Estructuras/29-deepcopy/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/29-deepcopy/reto.md create mode 100644 python/Modulo-03-Estructuras/29-deepcopy/teoria.md create mode 100644 python/Modulo-03-Estructuras/29-deepcopy/test_main.py create mode 100644 python/Modulo-03-Estructuras/30-RETO-inventario/README.md create mode 100644 python/Modulo-03-Estructuras/30-RETO-inventario/ejemplo.py create mode 100644 python/Modulo-03-Estructuras/30-RETO-inventario/reto.md create mode 100644 python/Modulo-03-Estructuras/30-RETO-inventario/teoria.md create mode 100644 python/Modulo-03-Estructuras/30-RETO-inventario/test_main.py delete mode 100644 python/Modulo-03-Exterior/.gitkeep delete mode 100644 python/Modulo-03-Exterior/01-funciones/.gitkeep delete mode 100644 python/Modulo-03-Exterior/01-funciones/README.md delete mode 100644 python/Modulo-03-Exterior/01-funciones/ejemplo.py delete mode 100644 python/Modulo-03-Exterior/01-funciones/reto.md delete mode 100644 python/Modulo-03-Exterior/01-funciones/solucion/.gitkeep delete mode 100644 python/Modulo-03-Exterior/01-funciones/teoria.md delete mode 100644 python/Modulo-03-Exterior/02-input-usuario/.gitkeep delete mode 100644 python/Modulo-03-Exterior/02-input-usuario/README.md delete mode 100644 python/Modulo-03-Exterior/02-input-usuario/ejemplo.py delete mode 100644 python/Modulo-03-Exterior/02-input-usuario/reto.md delete mode 100644 python/Modulo-03-Exterior/02-input-usuario/solucion/.gitkeep delete mode 100644 python/Modulo-03-Exterior/02-input-usuario/teoria.md delete mode 100644 python/Modulo-03-Exterior/03-manejo-errores/.gitkeep delete mode 100644 python/Modulo-03-Exterior/03-manejo-errores/README.md delete mode 100644 python/Modulo-03-Exterior/03-manejo-errores/ejemplo.py delete mode 100644 python/Modulo-03-Exterior/03-manejo-errores/reto.md delete mode 100644 python/Modulo-03-Exterior/03-manejo-errores/solucion/.gitkeep delete mode 100644 python/Modulo-03-Exterior/03-manejo-errores/teoria.md delete mode 100644 python/Modulo-03-Exterior/04-archivos/.gitkeep delete mode 100644 python/Modulo-03-Exterior/04-archivos/README.md delete mode 100644 python/Modulo-03-Exterior/04-archivos/ejemplo.py delete mode 100644 python/Modulo-03-Exterior/04-archivos/reto.md delete mode 100644 python/Modulo-03-Exterior/04-archivos/solucion/.gitkeep delete mode 100644 python/Modulo-03-Exterior/04-archivos/teoria.md delete mode 100644 python/Modulo-03-Exterior/RETO-FINAL-MOD-3/.gitkeep delete mode 100644 python/Modulo-03-Exterior/RETO-FINAL-MOD-3/README.md delete mode 100644 python/Modulo-03-Exterior/RETO-FINAL-MOD-3/solucion/.gitkeep create mode 100644 python/Modulo-04-Bucles/31-while/README.md create mode 100644 python/Modulo-04-Bucles/31-while/ejemplo.py create mode 100644 python/Modulo-04-Bucles/31-while/reto.md create mode 100644 python/Modulo-04-Bucles/31-while/teoria.md create mode 100644 python/Modulo-04-Bucles/31-while/test_main.py create mode 100644 python/Modulo-04-Bucles/32-for/README.md create mode 100644 python/Modulo-04-Bucles/32-for/ejemplo.py create mode 100644 python/Modulo-04-Bucles/32-for/reto.md create mode 100644 python/Modulo-04-Bucles/32-for/teoria.md create mode 100644 python/Modulo-04-Bucles/32-for/test_main.py create mode 100644 python/Modulo-04-Bucles/33-range-enumerate/README.md create mode 100644 python/Modulo-04-Bucles/33-range-enumerate/ejemplo.py create mode 100644 python/Modulo-04-Bucles/33-range-enumerate/reto.md create mode 100644 python/Modulo-04-Bucles/33-range-enumerate/teoria.md create mode 100644 python/Modulo-04-Bucles/33-range-enumerate/test_main.py create mode 100644 python/Modulo-04-Bucles/34-break-continue/README.md create mode 100644 python/Modulo-04-Bucles/34-break-continue/ejemplo.py create mode 100644 python/Modulo-04-Bucles/34-break-continue/reto.md create mode 100644 python/Modulo-04-Bucles/34-break-continue/teoria.md create mode 100644 python/Modulo-04-Bucles/34-break-continue/test_main.py create mode 100644 python/Modulo-04-Bucles/35-list-comprehension/README.md create mode 100644 python/Modulo-04-Bucles/35-list-comprehension/ejemplo.py create mode 100644 python/Modulo-04-Bucles/35-list-comprehension/reto.md create mode 100644 python/Modulo-04-Bucles/35-list-comprehension/teoria.md create mode 100644 python/Modulo-04-Bucles/35-list-comprehension/test_main.py create mode 100644 python/Modulo-04-Bucles/36-dict-comprehension/README.md create mode 100644 python/Modulo-04-Bucles/36-dict-comprehension/ejemplo.py create mode 100644 python/Modulo-04-Bucles/36-dict-comprehension/reto.md create mode 100644 python/Modulo-04-Bucles/36-dict-comprehension/teoria.md create mode 100644 python/Modulo-04-Bucles/36-dict-comprehension/test_main.py create mode 100644 python/Modulo-04-Bucles/37-zip/README.md create mode 100644 python/Modulo-04-Bucles/37-zip/ejemplo.py create mode 100644 python/Modulo-04-Bucles/37-zip/reto.md create mode 100644 python/Modulo-04-Bucles/37-zip/teoria.md create mode 100644 python/Modulo-04-Bucles/37-zip/test_main.py create mode 100644 python/Modulo-04-Bucles/38-iteradores/README.md create mode 100644 python/Modulo-04-Bucles/38-iteradores/ejemplo.py create mode 100644 python/Modulo-04-Bucles/38-iteradores/reto.md create mode 100644 python/Modulo-04-Bucles/38-iteradores/teoria.md create mode 100644 python/Modulo-04-Bucles/38-iteradores/test_main.py create mode 100644 python/Modulo-04-Bucles/39-matrices/README.md create mode 100644 python/Modulo-04-Bucles/39-matrices/ejemplo.py create mode 100644 python/Modulo-04-Bucles/39-matrices/reto.md create mode 100644 python/Modulo-04-Bucles/39-matrices/teoria.md create mode 100644 python/Modulo-04-Bucles/39-matrices/test_main.py create mode 100644 python/Modulo-04-Bucles/40-RETO-analizador/README.md create mode 100644 python/Modulo-04-Bucles/40-RETO-analizador/ejemplo.py create mode 100644 python/Modulo-04-Bucles/40-RETO-analizador/reto.md create mode 100644 python/Modulo-04-Bucles/40-RETO-analizador/teoria.md create mode 100644 python/Modulo-04-Bucles/40-RETO-analizador/test_main.py delete mode 100644 python/Modulo-04-Nerve/.gitkeep create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/1-yield/README.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/1-yield/ejemplo.py create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/1-yield/reto.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/1-yield/teoria.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/1-yield/test_main.py create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/2-generadores/README.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/2-generadores/ejemplo.py create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/2-generadores/reto.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/2-generadores/teoria.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/2-generadores/test_main.py create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/3-expresiones-generadoras/README.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/3-expresiones-generadoras/ejemplo.py create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/3-expresiones-generadoras/reto.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/3-expresiones-generadoras/teoria.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/3-expresiones-generadoras/test_main.py create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/4-RETO-pipeline/README.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/4-RETO-pipeline/ejemplo.py create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/4-RETO-pipeline/reto.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/4-RETO-pipeline/teoria.md create mode 100644 python/Modulo-04.5-Iteracion-Avanzada/4-RETO-pipeline/test_main.py create mode 100644 python/Modulo-05-Funciones/41-def/README.md create mode 100644 python/Modulo-05-Funciones/41-def/ejemplo.py create mode 100644 python/Modulo-05-Funciones/41-def/reto.md create mode 100644 python/Modulo-05-Funciones/41-def/teoria.md create mode 100644 python/Modulo-05-Funciones/41-def/test_main.py create mode 100644 python/Modulo-05-Funciones/42-parametros-return/README.md create mode 100644 python/Modulo-05-Funciones/42-parametros-return/ejemplo.py create mode 100644 python/Modulo-05-Funciones/42-parametros-return/reto.md create mode 100644 python/Modulo-05-Funciones/42-parametros-return/teoria.md create mode 100644 python/Modulo-05-Funciones/42-parametros-return/test_main.py create mode 100644 python/Modulo-05-Funciones/43-args-defecto/README.md create mode 100644 python/Modulo-05-Funciones/43-args-defecto/ejemplo.py create mode 100644 python/Modulo-05-Funciones/43-args-defecto/reto.md create mode 100644 python/Modulo-05-Funciones/43-args-defecto/teoria.md create mode 100644 python/Modulo-05-Funciones/43-args-defecto/test_main.py create mode 100644 python/Modulo-05-Funciones/44-args-kwargs/README.md create mode 100644 python/Modulo-05-Funciones/44-args-kwargs/ejemplo.py create mode 100644 python/Modulo-05-Funciones/44-args-kwargs/reto.md create mode 100644 python/Modulo-05-Funciones/44-args-kwargs/teoria.md create mode 100644 python/Modulo-05-Funciones/44-args-kwargs/test_main.py create mode 100644 python/Modulo-05-Funciones/45-scope/README.md create mode 100644 python/Modulo-05-Funciones/45-scope/ejemplo.py create mode 100644 python/Modulo-05-Funciones/45-scope/reto.md create mode 100644 python/Modulo-05-Funciones/45-scope/teoria.md create mode 100644 python/Modulo-05-Funciones/45-scope/test_main.py create mode 100644 python/Modulo-05-Funciones/46-lambdas/README.md create mode 100644 python/Modulo-05-Funciones/46-lambdas/ejemplo.py create mode 100644 python/Modulo-05-Funciones/46-lambdas/reto.md create mode 100644 python/Modulo-05-Funciones/46-lambdas/teoria.md create mode 100644 python/Modulo-05-Funciones/46-lambdas/test_main.py create mode 100644 python/Modulo-05-Funciones/47-recursividad/README.md create mode 100644 python/Modulo-05-Funciones/47-recursividad/ejemplo.py create mode 100644 python/Modulo-05-Funciones/47-recursividad/reto.md create mode 100644 python/Modulo-05-Funciones/47-recursividad/teoria.md create mode 100644 python/Modulo-05-Funciones/47-recursividad/test_main.py create mode 100644 python/Modulo-05-Funciones/48-import/README.md create mode 100644 python/Modulo-05-Funciones/48-import/ejemplo.py create mode 100644 python/Modulo-05-Funciones/48-import/reto.md create mode 100644 python/Modulo-05-Funciones/48-import/teoria.md create mode 100644 python/Modulo-05-Funciones/48-import/test_main.py create mode 100644 python/Modulo-05-Funciones/49-pip-venvs/README.md create mode 100644 python/Modulo-05-Funciones/49-pip-venvs/ejemplo.py create mode 100644 python/Modulo-05-Funciones/49-pip-venvs/reto.md create mode 100644 python/Modulo-05-Funciones/49-pip-venvs/teoria.md create mode 100644 python/Modulo-05-Funciones/49-pip-venvs/test_main.py create mode 100644 python/Modulo-05-Funciones/50-RETO-calculadora/README.md create mode 100644 python/Modulo-05-Funciones/50-RETO-calculadora/ejemplo.py create mode 100644 python/Modulo-05-Funciones/50-RETO-calculadora/reto.md create mode 100644 python/Modulo-05-Funciones/50-RETO-calculadora/teoria.md create mode 100644 python/Modulo-05-Funciones/50-RETO-calculadora/test_main.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/1-type-hints/README.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/1-type-hints/ejemplo.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/1-type-hints/reto.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/1-type-hints/teoria.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/1-type-hints/test_main.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/2-docstrings/README.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/2-docstrings/ejemplo.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/2-docstrings/reto.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/2-docstrings/teoria.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/2-docstrings/test_main.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/3-pytest-intro/README.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/3-pytest-intro/ejemplo.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/3-pytest-intro/reto.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/3-pytest-intro/teoria.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/3-pytest-intro/test_main.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/4-fixtures-y-parametrize/README.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/4-fixtures-y-parametrize/ejemplo.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/4-fixtures-y-parametrize/reto.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/4-fixtures-y-parametrize/teoria.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/4-fixtures-y-parametrize/test_main.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/5-mocking/README.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/5-mocking/ejemplo.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/5-mocking/reto.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/5-mocking/teoria.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/5-mocking/test_main.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/6-RETO-suite-tests/README.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/6-RETO-suite-tests/ejemplo.py create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/6-RETO-suite-tests/reto.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/6-RETO-suite-tests/teoria.md create mode 100644 python/Modulo-05.5-Calidad-de-Codigo/6-RETO-suite-tests/test_main.py create mode 100644 python/Modulo-06-OS/51-os-rutas/README.md create mode 100644 python/Modulo-06-OS/51-os-rutas/ejemplo.py create mode 100644 python/Modulo-06-OS/51-os-rutas/reto.md create mode 100644 python/Modulo-06-OS/51-os-rutas/teoria.md create mode 100644 python/Modulo-06-OS/51-os-rutas/test_main.py create mode 100644 python/Modulo-06-OS/52-txt-read/README.md create mode 100644 python/Modulo-06-OS/52-txt-read/ejemplo.py create mode 100644 python/Modulo-06-OS/52-txt-read/reto.md create mode 100644 python/Modulo-06-OS/52-txt-read/teoria.md create mode 100644 python/Modulo-06-OS/52-txt-read/test_main.py create mode 100644 python/Modulo-06-OS/53-txt-write/README.md create mode 100644 python/Modulo-06-OS/53-txt-write/ejemplo.py create mode 100644 python/Modulo-06-OS/53-txt-write/reto.md create mode 100644 python/Modulo-06-OS/53-txt-write/teoria.md create mode 100644 python/Modulo-06-OS/53-txt-write/test_main.py create mode 100644 python/Modulo-06-OS/54-csv/README.md create mode 100644 python/Modulo-06-OS/54-csv/ejemplo.py create mode 100644 python/Modulo-06-OS/54-csv/reto.md create mode 100644 python/Modulo-06-OS/54-csv/teoria.md create mode 100644 python/Modulo-06-OS/54-csv/test_main.py create mode 100644 python/Modulo-06-OS/55-shutil/README.md create mode 100644 python/Modulo-06-OS/55-shutil/ejemplo.py create mode 100644 python/Modulo-06-OS/55-shutil/reto.md create mode 100644 python/Modulo-06-OS/55-shutil/teoria.md create mode 100644 python/Modulo-06-OS/55-shutil/test_main.py create mode 100644 python/Modulo-06-OS/56-datetime/README.md create mode 100644 python/Modulo-06-OS/56-datetime/ejemplo.py create mode 100644 python/Modulo-06-OS/56-datetime/reto.md create mode 100644 python/Modulo-06-OS/56-datetime/teoria.md create mode 100644 python/Modulo-06-OS/56-datetime/test_main.py create mode 100644 python/Modulo-06-OS/57-subprocess/README.md create mode 100644 python/Modulo-06-OS/57-subprocess/ejemplo.py create mode 100644 python/Modulo-06-OS/57-subprocess/reto.md create mode 100644 python/Modulo-06-OS/57-subprocess/teoria.md create mode 100644 python/Modulo-06-OS/57-subprocess/test_main.py create mode 100644 python/Modulo-06-OS/58-logging/README.md create mode 100644 python/Modulo-06-OS/58-logging/ejemplo.py create mode 100644 python/Modulo-06-OS/58-logging/reto.md create mode 100644 python/Modulo-06-OS/58-logging/teoria.md create mode 100644 python/Modulo-06-OS/58-logging/test_main.py create mode 100644 python/Modulo-06-OS/59-try-except/README.md create mode 100644 python/Modulo-06-OS/59-try-except/ejemplo.py create mode 100644 python/Modulo-06-OS/59-try-except/reto.md create mode 100644 python/Modulo-06-OS/59-try-except/teoria.md create mode 100644 python/Modulo-06-OS/59-try-except/test_main.py create mode 100644 python/Modulo-06-OS/60-RETO-organizador/README.md create mode 100644 python/Modulo-06-OS/60-RETO-organizador/ejemplo.py create mode 100644 python/Modulo-06-OS/60-RETO-organizador/reto.md create mode 100644 python/Modulo-06-OS/60-RETO-organizador/teoria.md create mode 100644 python/Modulo-06-OS/60-RETO-organizador/test_main.py create mode 100644 python/Modulo-07-Web-APIs/61-requests/README.md create mode 100644 python/Modulo-07-Web-APIs/61-requests/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/61-requests/reto.md create mode 100644 python/Modulo-07-Web-APIs/61-requests/teoria.md create mode 100644 python/Modulo-07-Web-APIs/61-requests/test_main.py create mode 100644 python/Modulo-07-Web-APIs/62-json/README.md create mode 100644 python/Modulo-07-Web-APIs/62-json/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/62-json/reto.md create mode 100644 python/Modulo-07-Web-APIs/62-json/teoria.md create mode 100644 python/Modulo-07-Web-APIs/62-json/test_main.py create mode 100644 python/Modulo-07-Web-APIs/63-beautifulsoup/README.md create mode 100644 python/Modulo-07-Web-APIs/63-beautifulsoup/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/63-beautifulsoup/reto.md create mode 100644 python/Modulo-07-Web-APIs/63-beautifulsoup/teoria.md create mode 100644 python/Modulo-07-Web-APIs/63-beautifulsoup/test_main.py create mode 100644 python/Modulo-07-Web-APIs/64-selectores/README.md create mode 100644 python/Modulo-07-Web-APIs/64-selectores/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/64-selectores/reto.md create mode 100644 python/Modulo-07-Web-APIs/64-selectores/teoria.md create mode 100644 python/Modulo-07-Web-APIs/64-selectores/test_main.py create mode 100644 python/Modulo-07-Web-APIs/65-selenium/README.md create mode 100644 python/Modulo-07-Web-APIs/65-selenium/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/65-selenium/reto.md create mode 100644 python/Modulo-07-Web-APIs/65-selenium/teoria.md create mode 100644 python/Modulo-07-Web-APIs/65-selenium/test_main.py create mode 100644 python/Modulo-07-Web-APIs/66-auth-apis/README.md create mode 100644 python/Modulo-07-Web-APIs/66-auth-apis/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/66-auth-apis/reto.md create mode 100644 python/Modulo-07-Web-APIs/66-auth-apis/teoria.md create mode 100644 python/Modulo-07-Web-APIs/66-auth-apis/test_main.py create mode 100644 python/Modulo-07-Web-APIs/67-rate-limiting/README.md create mode 100644 python/Modulo-07-Web-APIs/67-rate-limiting/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/67-rate-limiting/reto.md create mode 100644 python/Modulo-07-Web-APIs/67-rate-limiting/teoria.md create mode 100644 python/Modulo-07-Web-APIs/67-rate-limiting/test_main.py create mode 100644 python/Modulo-07-Web-APIs/68-headless/README.md create mode 100644 python/Modulo-07-Web-APIs/68-headless/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/68-headless/reto.md create mode 100644 python/Modulo-07-Web-APIs/68-headless/teoria.md create mode 100644 python/Modulo-07-Web-APIs/68-headless/test_main.py create mode 100644 python/Modulo-07-Web-APIs/69-pandas-web/README.md create mode 100644 python/Modulo-07-Web-APIs/69-pandas-web/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/69-pandas-web/reto.md create mode 100644 python/Modulo-07-Web-APIs/69-pandas-web/teoria.md create mode 100644 python/Modulo-07-Web-APIs/69-pandas-web/test_main.py create mode 100644 python/Modulo-07-Web-APIs/70-RETO-monitor/README.md create mode 100644 python/Modulo-07-Web-APIs/70-RETO-monitor/ejemplo.py create mode 100644 python/Modulo-07-Web-APIs/70-RETO-monitor/reto.md create mode 100644 python/Modulo-07-Web-APIs/70-RETO-monitor/teoria.md create mode 100644 python/Modulo-07-Web-APIs/70-RETO-monitor/test_main.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/1-event-loop/README.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/1-event-loop/ejemplo.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/1-event-loop/reto.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/1-event-loop/teoria.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/1-event-loop/test_main.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/2-async-await/README.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/2-async-await/ejemplo.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/2-async-await/reto.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/2-async-await/teoria.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/2-async-await/test_main.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/3-asyncio-gather/README.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/3-asyncio-gather/ejemplo.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/3-asyncio-gather/reto.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/3-asyncio-gather/teoria.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/3-asyncio-gather/test_main.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/4-aiohttp/README.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/4-aiohttp/ejemplo.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/4-aiohttp/reto.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/4-aiohttp/teoria.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/4-aiohttp/test_main.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/5-try-except-en-red/README.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/5-try-except-en-red/ejemplo.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/5-try-except-en-red/reto.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/5-try-except-en-red/teoria.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/5-try-except-en-red/test_main.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/6-RETO-scraper-concurrente/README.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/6-RETO-scraper-concurrente/ejemplo.py create mode 100644 python/Modulo-07.5-Programacion-Asincrona/6-RETO-scraper-concurrente/reto.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/6-RETO-scraper-concurrente/teoria.md create mode 100644 python/Modulo-07.5-Programacion-Asincrona/6-RETO-scraper-concurrente/test_main.py create mode 100644 python/Modulo-08-POO/71-clases-objetos/README.md create mode 100644 python/Modulo-08-POO/71-clases-objetos/ejemplo.py create mode 100644 python/Modulo-08-POO/71-clases-objetos/reto.md create mode 100644 python/Modulo-08-POO/71-clases-objetos/teoria.md create mode 100644 python/Modulo-08-POO/71-clases-objetos/test_main.py create mode 100644 python/Modulo-08-POO/72-init/README.md create mode 100644 python/Modulo-08-POO/72-init/ejemplo.py create mode 100644 python/Modulo-08-POO/72-init/reto.md create mode 100644 python/Modulo-08-POO/72-init/teoria.md create mode 100644 python/Modulo-08-POO/72-init/test_main.py create mode 100644 python/Modulo-08-POO/73-herencia-polimorfismo/README.md create mode 100644 python/Modulo-08-POO/73-herencia-polimorfismo/ejemplo.py create mode 100644 python/Modulo-08-POO/73-herencia-polimorfismo/reto.md create mode 100644 python/Modulo-08-POO/73-herencia-polimorfismo/teoria.md create mode 100644 python/Modulo-08-POO/73-herencia-polimorfismo/test_main.py create mode 100644 python/Modulo-08-POO/74-encapsulamiento/README.md create mode 100644 python/Modulo-08-POO/74-encapsulamiento/ejemplo.py create mode 100644 python/Modulo-08-POO/74-encapsulamiento/reto.md create mode 100644 python/Modulo-08-POO/74-encapsulamiento/teoria.md create mode 100644 python/Modulo-08-POO/74-encapsulamiento/test_main.py create mode 100644 python/Modulo-08-POO/75-propiedades/README.md create mode 100644 python/Modulo-08-POO/75-propiedades/ejemplo.py create mode 100644 python/Modulo-08-POO/75-propiedades/reto.md create mode 100644 python/Modulo-08-POO/75-propiedades/teoria.md create mode 100644 python/Modulo-08-POO/75-propiedades/test_main.py create mode 100644 python/Modulo-08-POO/76-metodos-clase/README.md create mode 100644 python/Modulo-08-POO/76-metodos-clase/ejemplo.py create mode 100644 python/Modulo-08-POO/76-metodos-clase/reto.md create mode 100644 python/Modulo-08-POO/76-metodos-clase/teoria.md create mode 100644 python/Modulo-08-POO/76-metodos-clase/test_main.py create mode 100644 python/Modulo-08-POO/77-dunder-methods/README.md create mode 100644 python/Modulo-08-POO/77-dunder-methods/ejemplo.py create mode 100644 python/Modulo-08-POO/77-dunder-methods/reto.md create mode 100644 python/Modulo-08-POO/77-dunder-methods/teoria.md create mode 100644 python/Modulo-08-POO/77-dunder-methods/test_main.py create mode 100644 python/Modulo-08-POO/78-abstraccion/README.md create mode 100644 python/Modulo-08-POO/78-abstraccion/ejemplo.py create mode 100644 python/Modulo-08-POO/78-abstraccion/reto.md create mode 100644 python/Modulo-08-POO/78-abstraccion/teoria.md create mode 100644 python/Modulo-08-POO/78-abstraccion/test_main.py create mode 100644 python/Modulo-08-POO/79-singleton/README.md create mode 100644 python/Modulo-08-POO/79-singleton/ejemplo.py create mode 100644 python/Modulo-08-POO/79-singleton/reto.md create mode 100644 python/Modulo-08-POO/79-singleton/teoria.md create mode 100644 python/Modulo-08-POO/79-singleton/test_main.py create mode 100644 python/Modulo-08-POO/80-RETO-ecosistema/README.md create mode 100644 python/Modulo-08-POO/80-RETO-ecosistema/ejemplo.py create mode 100644 python/Modulo-08-POO/80-RETO-ecosistema/reto.md create mode 100644 python/Modulo-08-POO/80-RETO-ecosistema/teoria.md create mode 100644 python/Modulo-08-POO/80-RETO-ecosistema/test_main.py create mode 100644 python/Modulo-09-Data-IA/81-numpy/README.md create mode 100644 python/Modulo-09-Data-IA/81-numpy/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/81-numpy/reto.md create mode 100644 python/Modulo-09-Data-IA/81-numpy/teoria.md create mode 100644 python/Modulo-09-Data-IA/81-numpy/test_main.py create mode 100644 python/Modulo-09-Data-IA/82-pandas/README.md create mode 100644 python/Modulo-09-Data-IA/82-pandas/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/82-pandas/reto.md create mode 100644 python/Modulo-09-Data-IA/82-pandas/teoria.md create mode 100644 python/Modulo-09-Data-IA/82-pandas/test_main.py create mode 100644 python/Modulo-09-Data-IA/83-limpieza-datos/README.md create mode 100644 python/Modulo-09-Data-IA/83-limpieza-datos/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/83-limpieza-datos/reto.md create mode 100644 python/Modulo-09-Data-IA/83-limpieza-datos/teoria.md create mode 100644 python/Modulo-09-Data-IA/83-limpieza-datos/test_main.py create mode 100644 python/Modulo-09-Data-IA/84-matplotlib/README.md create mode 100644 python/Modulo-09-Data-IA/84-matplotlib/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/84-matplotlib/reto.md create mode 100644 python/Modulo-09-Data-IA/84-matplotlib/teoria.md create mode 100644 python/Modulo-09-Data-IA/84-matplotlib/test_main.py create mode 100644 python/Modulo-09-Data-IA/85-scikit-learn/README.md create mode 100644 python/Modulo-09-Data-IA/85-scikit-learn/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/85-scikit-learn/reto.md create mode 100644 python/Modulo-09-Data-IA/85-scikit-learn/teoria.md create mode 100644 python/Modulo-09-Data-IA/85-scikit-learn/test_main.py create mode 100644 python/Modulo-09-Data-IA/86-regresion/README.md create mode 100644 python/Modulo-09-Data-IA/86-regresion/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/86-regresion/reto.md create mode 100644 python/Modulo-09-Data-IA/86-regresion/teoria.md create mode 100644 python/Modulo-09-Data-IA/86-regresion/test_main.py create mode 100644 python/Modulo-09-Data-IA/87-clasificacion/README.md create mode 100644 python/Modulo-09-Data-IA/87-clasificacion/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/87-clasificacion/reto.md create mode 100644 python/Modulo-09-Data-IA/87-clasificacion/teoria.md create mode 100644 python/Modulo-09-Data-IA/87-clasificacion/test_main.py create mode 100644 python/Modulo-09-Data-IA/88-nlp/README.md create mode 100644 python/Modulo-09-Data-IA/88-nlp/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/88-nlp/reto.md create mode 100644 python/Modulo-09-Data-IA/88-nlp/teoria.md create mode 100644 python/Modulo-09-Data-IA/88-nlp/test_main.py create mode 100644 python/Modulo-09-Data-IA/89-llms/README.md create mode 100644 python/Modulo-09-Data-IA/89-llms/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/89-llms/reto.md create mode 100644 python/Modulo-09-Data-IA/89-llms/teoria.md create mode 100644 python/Modulo-09-Data-IA/89-llms/test_main.py create mode 100644 python/Modulo-09-Data-IA/90-RETO-predictor/README.md create mode 100644 python/Modulo-09-Data-IA/90-RETO-predictor/ejemplo.py create mode 100644 python/Modulo-09-Data-IA/90-RETO-predictor/reto.md create mode 100644 python/Modulo-09-Data-IA/90-RETO-predictor/teoria.md create mode 100644 python/Modulo-09-Data-IA/90-RETO-predictor/test_main.py delete mode 100644 python/Modulo-10-Nerve/100-RETO-FINAL-PILOT/.gitkeep rename {alenia-nerve => python/Modulo-10-Nerve/100-topologia-red}/.gitkeep (100%) create mode 100644 python/Modulo-10-Nerve/100-topologia-red/README.md create mode 100644 python/Modulo-10-Nerve/100-topologia-red/ejemplo.py create mode 100644 python/Modulo-10-Nerve/100-topologia-red/reto.md create mode 100644 python/Modulo-10-Nerve/100-topologia-red/teoria.md create mode 100644 python/Modulo-10-Nerve/100-topologia-red/test_main.py create mode 100644 python/Modulo-10-Nerve/91-local-first/README.md create mode 100644 python/Modulo-10-Nerve/91-local-first/ejemplo.py create mode 100644 python/Modulo-10-Nerve/91-local-first/reto.md create mode 100644 python/Modulo-10-Nerve/91-local-first/teoria.md create mode 100644 python/Modulo-10-Nerve/91-local-first/test_main.py create mode 100644 python/Modulo-10-Nerve/92-arquitectura-nodos/README.md create mode 100644 python/Modulo-10-Nerve/92-arquitectura-nodos/ejemplo.py create mode 100644 python/Modulo-10-Nerve/92-arquitectura-nodos/reto.md create mode 100644 python/Modulo-10-Nerve/92-arquitectura-nodos/teoria.md create mode 100644 python/Modulo-10-Nerve/92-arquitectura-nodos/test_main.py create mode 100644 python/Modulo-10-Nerve/93-p2p/README.md create mode 100644 python/Modulo-10-Nerve/93-p2p/ejemplo.py create mode 100644 python/Modulo-10-Nerve/93-p2p/reto.md create mode 100644 python/Modulo-10-Nerve/93-p2p/teoria.md create mode 100644 python/Modulo-10-Nerve/93-p2p/test_main.py create mode 100644 python/Modulo-10-Nerve/94-estado-global/README.md create mode 100644 python/Modulo-10-Nerve/94-estado-global/ejemplo.py create mode 100644 python/Modulo-10-Nerve/94-estado-global/reto.md create mode 100644 python/Modulo-10-Nerve/94-estado-global/teoria.md create mode 100644 python/Modulo-10-Nerve/94-estado-global/test_main.py create mode 100644 python/Modulo-10-Nerve/95-criptografia/README.md create mode 100644 python/Modulo-10-Nerve/95-criptografia/ejemplo.py create mode 100644 python/Modulo-10-Nerve/95-criptografia/reto.md create mode 100644 python/Modulo-10-Nerve/95-criptografia/teoria.md create mode 100644 python/Modulo-10-Nerve/95-criptografia/test_main.py create mode 100644 python/Modulo-10-Nerve/96-sincronizacion/README.md create mode 100644 python/Modulo-10-Nerve/96-sincronizacion/ejemplo.py create mode 100644 python/Modulo-10-Nerve/96-sincronizacion/reto.md create mode 100644 python/Modulo-10-Nerve/96-sincronizacion/teoria.md create mode 100644 python/Modulo-10-Nerve/96-sincronizacion/test_main.py create mode 100644 python/Modulo-10-Nerve/97-microservicios/README.md create mode 100644 python/Modulo-10-Nerve/97-microservicios/ejemplo.py create mode 100644 python/Modulo-10-Nerve/97-microservicios/reto.md create mode 100644 python/Modulo-10-Nerve/97-microservicios/teoria.md create mode 100644 python/Modulo-10-Nerve/97-microservicios/test_main.py create mode 100644 python/Modulo-10-Nerve/98-despliegue/README.md create mode 100644 python/Modulo-10-Nerve/98-despliegue/ejemplo.py create mode 100644 python/Modulo-10-Nerve/98-despliegue/reto.md create mode 100644 python/Modulo-10-Nerve/98-despliegue/teoria.md create mode 100644 python/Modulo-10-Nerve/98-despliegue/test_main.py create mode 100644 python/Modulo-10-Nerve/99-optimizacion/README.md create mode 100644 python/Modulo-10-Nerve/99-optimizacion/ejemplo.py create mode 100644 python/Modulo-10-Nerve/99-optimizacion/reto.md create mode 100644 python/Modulo-10-Nerve/99-optimizacion/teoria.md create mode 100644 python/Modulo-10-Nerve/99-optimizacion/test_main.py rename {archivo-historico/ejemplos => python/Modulo-11-Herramientas-Nerve}/.gitkeep (100%) rename {archivo-historico/glosario => python/Modulo-11-Herramientas-Nerve/101-cli-start}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/101-cli-start/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/101-cli-start/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/101-cli-start/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/101-cli-start/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/101-cli-start/test_main.py rename {archivo-historico/retos => python/Modulo-11-Herramientas-Nerve/102-cli-monitor}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/102-cli-monitor/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/102-cli-monitor/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/102-cli-monitor/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/102-cli-monitor/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/102-cli-monitor/test_main.py rename {archivo-historico/retos/general/02-web-scraper-simple/soluciones => python/Modulo-11-Herramientas-Nerve/103-configuracion-externa}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/103-configuracion-externa/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/103-configuracion-externa/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/103-configuracion-externa/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/103-configuracion-externa/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/103-configuracion-externa/test_main.py rename {archivo-historico/retos/general/03-juego-de-adivinanza/soluciones => python/Modulo-11-Herramientas-Nerve/104-cli-bridge}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/104-cli-bridge/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/104-cli-bridge/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/104-cli-bridge/teoria.md rename {archivo-historico/retos/general/04-analizador-de-gastos-csv/soluciones => python/Modulo-11-Herramientas-Nerve/105-empaquetado-basico}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/105-empaquetado-basico/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/105-empaquetado-basico/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/105-empaquetado-basico/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/105-empaquetado-basico/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/105-empaquetado-basico/test_main.py rename {archivo-historico/retos/general/05-generador-de-contrasenas/soluciones => python/Modulo-11-Herramientas-Nerve/106-criptografia-nrv}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/106-criptografia-nrv/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/106-criptografia-nrv/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/106-criptografia-nrv/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/106-criptografia-nrv/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/106-criptografia-nrv/test_main.py rename {archivo-historico/retos/general/06-conversor-de-unidades/soluciones => python/Modulo-11-Herramientas-Nerve/107-desempaquetado-nrv}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/107-desempaquetado-nrv/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/107-desempaquetado-nrv/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/107-desempaquetado-nrv/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/107-desempaquetado-nrv/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/107-desempaquetado-nrv/test_main.py rename {archivo-historico/retos/general/07-calculadora-de-imc/soluciones => python/Modulo-11-Herramientas-Nerve/108-interfaz-nrv}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/108-interfaz-nrv/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/108-interfaz-nrv/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/108-interfaz-nrv/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/108-interfaz-nrv/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/108-interfaz-nrv/test_main.py rename {archivo-historico/retos/general/08-organizador-de-archivos/soluciones => python/Modulo-11-Herramientas-Nerve/109-generacion-pass}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/109-generacion-pass/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/109-generacion-pass/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/109-generacion-pass/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/109-generacion-pass/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/109-generacion-pass/test_main.py rename {archivo-historico/retos/general/09-recordatorios-en-terminal/soluciones => python/Modulo-11-Herramientas-Nerve/110-asociacion-archivos}/.gitkeep (100%) create mode 100644 python/Modulo-11-Herramientas-Nerve/110-asociacion-archivos/README.md create mode 100644 python/Modulo-11-Herramientas-Nerve/110-asociacion-archivos/ejemplo.py create mode 100644 python/Modulo-11-Herramientas-Nerve/110-asociacion-archivos/reto.md create mode 100644 python/Modulo-11-Herramientas-Nerve/110-asociacion-archivos/teoria.md create mode 100644 python/Modulo-11-Herramientas-Nerve/110-asociacion-archivos/test_main.py rename {archivo-historico/retos/general/10-analizador-de-texto/soluciones => python/Modulo-12-Arquitectura-Avanzada}/.gitkeep (100%) rename {archivo-historico/retos/general/11-profiler-de-funciones/soluciones => python/Modulo-12-Arquitectura-Avanzada/111-auto-reconexion}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/111-auto-reconexion/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/111-auto-reconexion/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/111-auto-reconexion/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/111-auto-reconexion/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/111-auto-reconexion/test_main.py rename {archivo-historico/retos/general/12-validador-de-emails/soluciones => python/Modulo-12-Arquitectura-Avanzada/112-heartbeats}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/112-heartbeats/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/112-heartbeats/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/112-heartbeats/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/112-heartbeats/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/112-heartbeats/test_main.py rename {archivo-historico/retos/general/13-lector-archivos-gigantes/soluciones => python/Modulo-12-Arquitectura-Avanzada/113-multiprocessing}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/113-multiprocessing/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/113-multiprocessing/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/113-multiprocessing/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/113-multiprocessing/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/113-multiprocessing/test_main.py rename {archivo-historico/retos/general/14-cli-del-clima/soluciones => python/Modulo-12-Arquitectura-Avanzada/114-asyncio-bridge}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/114-asyncio-bridge/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/114-asyncio-bridge/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/114-asyncio-bridge/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/114-asyncio-bridge/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/114-asyncio-bridge/test_main.py rename {archivo-historico/retos/general/15-herramienta-de-backup/soluciones => python/Modulo-12-Arquitectura-Avanzada/115-manejo-estado}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/115-manejo-estado/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/115-manejo-estado/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/115-manejo-estado/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/115-manejo-estado/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/115-manejo-estado/test_main.py rename {archivo-historico/retos/general/16-validador-de-edad/soluciones => python/Modulo-12-Arquitectura-Avanzada/116-pipelines-datos}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/116-pipelines-datos/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/116-pipelines-datos/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/116-pipelines-datos/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/116-pipelines-datos/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/116-pipelines-datos/test_main.py rename {archivo-historico/retos/general/17-lector-de-configuraciones/soluciones => python/Modulo-12-Arquitectura-Avanzada/117-mensajeria-robusta}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/117-mensajeria-robusta/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/117-mensajeria-robusta/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/117-mensajeria-robusta/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/117-mensajeria-robusta/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/117-mensajeria-robusta/test_main.py rename {archivo-historico/retos/general/18-inventario-de-tienda/soluciones => python/Modulo-12-Arquitectura-Avanzada/118-restricciones-rate-limit}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/118-restricciones-rate-limit/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/118-restricciones-rate-limit/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/118-restricciones-rate-limit/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/118-restricciones-rate-limit/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/118-restricciones-rate-limit/test_main.py rename {archivo-historico/retos/nerve/01-chat-terminal => python/Modulo-12-Arquitectura-Avanzada/119-arquitectura-produccion}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/119-arquitectura-produccion/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/119-arquitectura-produccion/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/119-arquitectura-produccion/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/119-arquitectura-produccion/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/119-arquitectura-produccion/test_main.py rename {archivo-historico/retos/nerve/01-chat-terminal/soluciones => python/Modulo-12-Arquitectura-Avanzada/120-RETO-FINAL-DISTRIBUIDO}/.gitkeep (100%) create mode 100644 python/Modulo-12-Arquitectura-Avanzada/120-RETO-FINAL-DISTRIBUIDO/README.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/120-RETO-FINAL-DISTRIBUIDO/ejemplo.py create mode 100644 python/Modulo-12-Arquitectura-Avanzada/120-RETO-FINAL-DISTRIBUIDO/reto.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/120-RETO-FINAL-DISTRIBUIDO/teoria.md create mode 100644 python/Modulo-12-Arquitectura-Avanzada/120-RETO-FINAL-DISTRIBUIDO/test_main.py rename {archivo-historico/retos => retos}/00-rompehielo/CesarAAR.py (100%) rename {archivo-historico/retos => retos}/00-rompehielo/README.md (100%) rename {archivo-historico/retos => retos}/00-rompehielo/Vi-dot1.cpp (100%) rename {archivo-historico/retos => retos}/00-rompehielo/ashu.py (100%) rename {archivo-historico/retos => retos}/00-rompehielo/tacos.py (100%) rename {archivo-historico/retos/nerve/02-reloj-sincronizado => retos/go/M01-M02}/.gitkeep (100%) rename {archivo-historico/retos/nerve/02-reloj-sincronizado/soluciones => retos/javascript/M01-M02}/.gitkeep (100%) create mode 100644 retos/python/M01-M02/01-generador-tarjetas/reto.md create mode 100644 retos/python/M01-M02/01-generador-tarjetas/test_main.py create mode 100644 retos/python/M01-M02/02-calculadora-cientifica/reto.md create mode 100644 retos/python/M01-M02/02-calculadora-cientifica/test_main.py create mode 100644 retos/python/M03-M04/01-organizador-coleccion/reto.md create mode 100644 retos/python/M03-M04/01-organizador-coleccion/test_main.py create mode 100644 retos/python/M03-M04/02-analizador-texto-simple/reto.md create mode 100644 retos/python/M03-M04/02-analizador-texto-simple/test_main.py create mode 100644 retos/python/M05-M06/01-cli-personal/reto.md create mode 100644 retos/python/M05-M06/01-cli-personal/test_main.py create mode 100644 retos/python/M05-M06/02-procesador-csv/reto.md create mode 100644 retos/python/M05-M06/02-procesador-csv/test_main.py create mode 100644 retos/python/M07-M08/01-scraper-tablas/reto.md create mode 100644 retos/python/M07-M08/01-scraper-tablas/test_main.py create mode 100644 retos/python/M07-M08/02-gestor-inventario-poo/reto.md create mode 100644 retos/python/M07-M08/02-gestor-inventario-poo/test_main.py create mode 100644 retos/python/M09-M12/01-predictor-gastos/reto.md create mode 100644 retos/python/M09-M12/01-predictor-gastos/test_main.py create mode 100644 retos/python/M09-M12/02-red-scripts-nerve/reto.md create mode 100644 retos/python/M09-M12/02-red-scripts-nerve/test_main.py rename {archivo-historico/retos/nerve/03-traductor-de-mensajes => retos/rust/M01-M02}/.gitkeep (100%) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 086eeb3..2dda6fd 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,144 +1,75 @@ # Guía de Contribución para `nerve-community` -¡Qué emoción que quieras contribuir a este proyecto! No importa si es la primera vez que tocas código o si ya tienes años de experiencia, aquí eres bienvenido/a. +¡Qué emoción que quieras contribuir a este proyecto! -El objetivo principal de este repositorio es **aprender**. Nadie nace sabiendo hacer Pull Requests (PRs), así que no tengas miedo de equivocarte. Sigue estos pasos y, si te trabas, ¡pregunta sin pena! - -## Antes de empezar -Asegúrate de tener lo siguiente: -- Una cuenta en **GitHub**. -- **Git** instalado en tu computadora. -- **Python 3.10** o superior instalado. - ---- - -## 🛠️ Paso 1: Haz un Fork -Un *fork* es hacer una copia exacta de este proyecto y ponerla en tu propia cuenta de GitHub. Así puedes experimentar y romper cosas en tu copia sin afectar el repositorio original. -Para hacerlo, ve a la página principal de este repositorio y dale clic al botón **Fork** que está arriba a la derecha. - ---- - -## 💻 Paso 2: Clona tu fork en tu computadora -Abre tu terminal (línea de comandos) y escribe el siguiente comando, cambiando `TU-USUARIO` por tu nombre de usuario en GitHub: - -```bash -git clone https://github.com/TU-USUARIO/nerve-community.git -cd nerve-community -``` +El objetivo principal de este repositorio es **aprender** y mantener un currículum de alta calidad para la comunidad. Sigue estos pasos y reglas para asegurarte de que tu contribución sea aceptada rápidamente. --- -## Paso 3: Crea una rama nueva -**Nunca** trabajes directamente en la rama `main`. Es una buena práctica crear una "rama" (branch) separada para cada tarea o reto que vayas a resolver. +## 🚫 Regla de Oro: La estructura es sagrada -```bash -git checkout -b solucion-reto-01 -``` +La estructura de carpetas dentro de `python/` (`Modulo-01`, `Modulo-02`, etc.) es utilizada por sistemas automatizados. +- **NO cambies el nombre** de ninguna carpeta `Modulo-XX`. +- **NO cambies el nombre** de los archivos internos (`README.md`, `teoria.md`, `ejemplo.py`, `reto.md`, `test_main.py`). +- **NO agregues nuevos archivos** dentro de las carpetas de los niveles, a menos que un mantenedor lo apruebe. --- -## Paso 4: Elige un reto -Ve a la pestaña de **Issues** en el repositorio original. -Busca los issues que tengan la etiqueta `good-first-issue` o `disponible`. -Si encuentras uno que te guste, deja un comentario que diga: *"¡Yo lo tomo!"* o *"Me gustaría intentar resolver esto"*. Así, evitamos que dos personas hagan el mismo reto al mismo tiempo. +## 💡 ¿Qué y cómo puedes contribuir? ---- +Puedes ayudarnos a mejorar el contenido existente o crear nuevos retos socráticos. -## 📦 Paso 5: Instala dependencias y prueba que todo funcione -Crea un entorno virtual e instala lo necesario (si tu reto lo requiere): +### 1. Mejorar el contenido de aprendizaje +Puedes editar `teoria.md`, `ejemplo.py` o `README.md` de cualquier nivel si notas que: +- Una explicación es confusa o tiene faltas de ortografía. +- El código de ejemplo podría ser más claro o tener mejores comentarios. +- El reto está mal redactado. -```bash -# En Windows: -python -m venv env -.\env\Scripts\activate +### 2. Mejorar los tests automáticos +Si detectas que un `test_main.py` de algún reto es demasiado permisivo (deja pasar código malo) o es incorrecto (falla con código bueno), puedes mejorarlo. Asegúrate de probarlo bien antes de enviar el PR. -# En Mac / Linux: -python3 -m venv env -source env/bin/activate +### 3. Crear nuevos Retos Socráticos +La carpeta `retos/` contiene retos abiertos (sin instrucciones paso a paso) para que los alumnos apliquen lo aprendido. Puedes crear uno nuevo en la carpeta correspondiente a su dificultad (`retos/python/MXX-MYY/nombre-del-reto/reto.md`). -# Instalar Nerve (ejemplo): -pip install alenia-nerve -``` - ---- +**Plantilla para un nuevo reto socrático (`reto.md`):** -## Paso 6: Escribe tu código -Entra a la carpeta correspondiente a tu reto (por ejemplo `retos/nerve/01-chat-terminal/`). -Dentro de esa carpeta encontrarás el archivo `README.md` con las instrucciones del reto. +```markdown +# [Nombre del reto] -Para guardar tu solución, debes crear una carpeta dentro de `soluciones/` en la raíz del repositorio siguiendo la convención `TuUsuario-Lenguaje`. Por ejemplo: -- `soluciones/Tacos-python/` -- `soluciones/Maria-rust/` -- `soluciones/Juan-go/` -- `soluciones/Ana-js/` +> **Nivel**: Módulos [X]–[Y] completados +> **Tiempo estimado**: [N] horas +> **Lenguaje**: Python -Dentro de tu carpeta personal, puedes crear los archivos necesarios para resolver el reto. ¡Diviértete programando! +## Tu misión +[Descripción en 2-3 líneas de qué debe hacer el programa. Sin decir cómo.] -### 🛠️ Instrucciones por Lenguaje +## Lo que debe hacer tu programa +- [Comportamiento observable 1] +- [Comportamiento observable 2] -**Python:** -- Se recomienda crear un entorno virtual (`python -m venv env`). -- Si instalas dependencias, recuerda exportarlas en un `requirements.txt`. +## Restricciones (respétalas o el reto no cuenta) +- **Solo puedes usar** conceptos de los módulos [X] al [Y] +- **No puedes usar**: [librerías prohibidas] +- **Obligatorio**: [requisito técnico concreto] +- El programa debe correr con `python reto.py` -**Rust:** -- Usa `cargo new tu-proyecto` para crear tu proyecto dentro de tu carpeta de solución. -- Verifica tu código con `cargo clippy` y formatéalo con `cargo fmt`. - -**Go:** -- Inicializa tu módulo con `go mod init `. -- Formatea tu código usando `go fmt ./...`. - -**JavaScript (Node.js):** -- Inicializa tu proyecto con `npm init -y`. -- Es buena práctica usar `eslint` o `prettier` para mantener el código limpio. - ---- - -## Paso 7: Guarda tus cambios (Commit) -Una vez que termines y pruebes que funciona, debes guardar tus cambios en git. Asegúrate de escribir un mensaje claro de lo que hiciste. - -```bash -git add . -git commit -m "Agrega solución al reto 01: chat-terminal" +## Criterio de éxito +Tu solución es válida si: +1. [Verificación concreta 1] +2. [Verificación concreta 2] ``` --- -## Paso 8: Sube tu rama a tu fork -Ahora envía los cambios guardados desde tu computadora a tu repositorio en GitHub: - -```bash -git push origin solucion-reto-01 -``` - ---- - -## Paso 9: Abre el Pull Request -Ve a tu repositorio fork en GitHub. Verás un botón verde gigante que dice **Compare & pull request**. ¡Dale clic! -- Llena la plantilla del PR. -- Menciona qué número de Issue estás resolviendo (ejemplo: `Closes #1`). -- Añade una captura de pantalla si tu código hace algo visual. - ---- +## 🛠️ El proceso de contribución (Fork + PR) -## ⏳ Paso 10: Espera revisión -Los mantenedores del proyecto revisaremos tu PR. -Es **súper normal** que te pidamos hacer algún cambio o corrección. No te lo tomes a mal, ¡no es un rechazo! Es simplemente la mejor forma de darte *feedback* y aprender juntos. Haz los cambios en tu computadora, haz otro `commit`, haz `push` y el PR se actualizará solo. - ---- - -## ❓ Preguntas frecuentes - -- **¿Y si ya alguien más resolvió el reto que quería?** -¡No pasa nada! A veces aceptamos múltiples soluciones si tienen enfoques distintos, o simplemente puedes elegir otro reto. Pregúntanos en el Issue si tienes dudas. - -- **¿Y si mi código no es perfecto?** -¡Mejor! El código perfecto no existe. Lo importante es que funcione, que intentes aprender algo nuevo y que pierdas el miedo a compartirlo. - -- **¿Puedo proponer un reto nuevo?** -¡Sí! Ve a los Issues, elige "Propuesta de nuevo reto" y cuéntanos tu idea. - ---- +1. **Haz un Fork**: Crea una copia exacta de este proyecto en tu propia cuenta de GitHub dándole al botón "Fork". +2. **Clona**: Descarga tu copia a tu computadora (`git clone https://github.com/TU-USUARIO/nerve-community.git`). +3. **Crea una rama (branch)**: Nunca trabajes en `main`. Crea una rama descriptiva (`git checkout -b mejora-teoria-mod1`). +4. **Haz el cambio**: Modifica los archivos siguiendo las reglas mencionadas arriba. +5. **Commit y Push**: Guarda tus cambios (`git commit -m "feat: mejora explicación de diccionarios en Módulo 04"`) y súbelos a tu fork (`git push origin mejora-teoria-mod1`). +6. **Pull Request (PR)**: Ve a GitHub y abre un Pull Request hacia el repositorio original. + - En la descripción de tu PR, explica **qué** cambiaste y **por qué**. + - Marca la casilla correspondiente en nuestra plantilla de PR. -### ¡Los errores son bienvenidos! -Si algo sale mal con un comando de Git o con tu código, dínoslo. El propósito de `nerve-community` es crecer en comunidad. ¡Mucho éxito con tu primer PR! +¡Y listo! Los mantenedores revisaremos tu PR y te daremos feedback. ¡Gracias por ayudar a la comunidad! diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 74b8429..76293a3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -22,6 +22,19 @@ body: placeholder: "1. Abrí la carpeta del reto...\n2. Ejecuté el comando..." validations: required: true + - type: dropdown + id: lenguaje + attributes: + label: "Lenguaje afectado (si aplica)" + options: + - No aplica / General + - Python + - Rust + - Go + - Java + - JavaScript / TypeScript + validations: + required: false - type: textarea id: entorno attributes: diff --git a/.github/ISSUE_TEMPLATE/propuesta_nuevo_reto.yml b/.github/ISSUE_TEMPLATE/propuesta_nuevo_reto.yml index 28579ea..ad92133 100644 --- a/.github/ISSUE_TEMPLATE/propuesta_nuevo_reto.yml +++ b/.github/ISSUE_TEMPLATE/propuesta_nuevo_reto.yml @@ -22,6 +22,19 @@ body: placeholder: "- Manejo de JSON\n- Peticiones HTTP..." validations: required: true + - type: dropdown + id: lenguaje + attributes: + label: "¿Para qué lenguaje(s) propones este reto?" + options: + - Multilenguaje (Aplica para todos) + - Python + - Rust + - Go + - Java + - JavaScript / TypeScript + validations: + required: true - type: dropdown id: dificultad attributes: diff --git a/.github/ISSUE_TEMPLATE/reto.yml b/.github/ISSUE_TEMPLATE/reto.yml index d21782e..6a0fff4 100644 --- a/.github/ISSUE_TEMPLATE/reto.yml +++ b/.github/ISSUE_TEMPLATE/reto.yml @@ -1,26 +1,44 @@ -name: Solucionar un Reto -description: Sube tu solución a uno de los retos de código disponibles. -title: "Reto: [NOMBRE DEL RETO]" -labels: ["en-progreso"] +name: Compartir Solución de un Reto +description: Comparte el enlace de tu repositorio (creado con nerve--template) con tu solución a un reto. +title: "Solución: [NOMBRE DEL RETO]" +labels: ["solución-reto"] body: - type: markdown attributes: - value: "¡Gracias por animarte a resolver este reto! Por favor llena esta plantilla para que podamos revisar tu propuesta fácilmente." + value: "¡Felicidades por resolver este reto! Llena esta plantilla para compartir tu repositorio generado a partir de `nerve--template`. Así podremos revisar tu código y darte feedback." - type: dropdown - id: nivel + id: lenguaje attributes: - label: "¿De qué nivel es el reto que resolviste?" + label: "¿En qué lenguaje resolviste el reto?" options: - - Nivel 1 - Principiante - - Nivel 2 - Intermedio - - Nivel 3 - Avanzado + - Python + - Rust + - Go + - Java + - JavaScript / TypeScript + validations: + required: true + - type: dropdown + id: tipo + attributes: + label: "¿Qué tipo de reto es?" + options: + - Reto de Módulo (Evaluado por GitHub Actions) + - Reto Socrático + validations: + required: true + - type: input + id: modulo_nivel + attributes: + label: "Módulo y Nivel exactos" + placeholder: "Ejemplo: python/Modulo-01-Fundamentos/01-hola-mundo" validations: required: true - type: input - id: nombre_reto + id: repo_url attributes: - label: "Nombre exacto del reto" - placeholder: "Ejemplo: 01-chat-terminal" + label: "Enlace a tu repositorio público" + placeholder: "https://github.com/tu-usuario/tu-repo" validations: required: true - type: textarea @@ -36,13 +54,13 @@ body: attributes: label: "Lista de verificación" options: - - label: "Probé mi código localmente y funciona." + - label: "Usé el repositorio `nerve--template` para crear mi proyecto." required: true - - label: "Agregué mi código en la carpeta correspondiente al reto." + - label: "Los tests automatizados (si aplican) pasan exitosamente en mi GitHub Actions." required: true - - label: "Comenté mi código para que otras personas puedan aprender leyéndolo." + - label: "Comenté mi código para explicar mis decisiones de diseño." required: false - - label: "Creo que esta solución está lo suficientemente pulida como para considerarse para 'graduarse' a zenith-nerve-tools." + - label: "Creo que esta solución está tan pulida que podría integrarse como herramienta en `zenith-nerve-tools`." required: false - type: markdown attributes: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3d8b319..6170e46 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,35 @@ -## 📌 ¿Qué reto resuelve este PR? -(Ej: Reto 01 - chat-terminal. Enlaza el issue con "Closes #NUMERO") + + +## Tipo de contribucion + +- [ ] Mejora de contenido (`teoria.md`, `ejemplo.py`, `README.md` de un modulo) +- [ ] Correccion en `test_main.py` +- [ ] Nuevo reto socrático en `retos/` +- [ ] Correccion de bug general o documentacion +- [ ] Otro + +## Descripcion de los cambios + + + +## Checklist + +- [ ] He leido la [Guia de Contribucion](.github/CONTRIBUTING.md). +- [ ] No modifique la estructura de carpetas ni los nombres de archivos dentro de `python/Modulo-XX/`. +- [ ] Si modifique un `test_main.py`, lo probe localmente para verificar que es correcto. +- [ ] Si es un reto socratico nuevo, use la plantilla correcta y lo ubique en la carpeta de dificultad adecuada. + +## Notas para quien revise + + diff --git a/.github/workflows/cierre-pr-alumno.yml b/.github/workflows/cierre-pr-alumno.yml new file mode 100644 index 0000000..d3a3df9 --- /dev/null +++ b/.github/workflows/cierre-pr-alumno.yml @@ -0,0 +1,105 @@ +name: Cierre Automatico de PRs de Alumnos + +# Dispara en PRs de forks hacia este repo. +# pull_request_target tiene acceso a los secrets del repo base, lo que +# permite comentar y cerrar PRs incluso desde forks externos. +on: + pull_request_target: + types: [opened, reopened, synchronize] + +jobs: + detectar-pr-alumno: + name: Detectar PR de alumno y redirigir + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + + steps: + - name: Obtener archivos modificados en el PR + id: archivos + uses: actions/github-script@v7 + with: + script: | + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + per_page: 100, + }); + + // Patrones que indican que el PR es trabajo de un alumno, + // no una contribucion al material del repo. + const patronesAlumno = [ + /^python\/Modulo-\d+/, // Archivos dentro de cualquier modulo de Python + /^go\/Modulo-\d+/, + /^rust\/Modulo-\d+/, + /^javascript\/Modulo-\d+/, + ]; + + const nombresArchivos = files.map(f => f.filename); + const esDeAlumno = nombresArchivos.some(nombre => + patronesAlumno.some(patron => patron.test(nombre)) + ); + + console.log("Archivos en el PR:", nombresArchivos); + console.log("Es PR de alumno:", esDeAlumno); + + return esDeAlumno; + + - name: Cerrar PR y notificar al alumno + if: steps.archivos.outputs.result == 'true' + uses: actions/github-script@v7 + with: + script: | + const prAuthor = context.payload.pull_request.user.login; + const prNumber = context.payload.pull_request.number; + + const mensaje = [ + `Hola @${prAuthor},`, + ``, + `Gracias por tomarte el tiempo de abrir este PR. Sin embargo, este repositorio (\`nerve-community\`) es el repositorio público del **material del curso**, no el lugar donde los alumnos entregan su trabajo.`, + ``, + `**Tu trabajo como alumno va en tu propio repositorio privado, generado desde el template oficial.** Los PRs de alumnos a este repo no son evaluados ni cuentan como avance.`, + ``, + `### Qué hacer ahora`, + ``, + `1. Si ya tienes tu repositorio generado desde el template: trabaja y haz push directamente ahí. El CI de ese repo es el que valida tu avance.`, + `2. Si aún no tienes acceso al template: entra al Discord de la comunidad y pide el enlace al template en el canal correspondiente.`, + `3. Puedes mantener o eliminar tu fork de este repo, eso no afecta tu avance como alumno.`, + ``, + `Lee la guía completa aquí: [Cómo usar como Alumno](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/COMO-USAR-COMO-ALUMNO.md)`, + ``, + `Este PR se cierra automáticamente. No es una penalización — es solo que el canal correcto es otro.`, + ].join('\n'); + + // Publicar comentario + await github.rest.issues.createComment({ + issue_number: prNumber, + owner: context.repo.owner, + repo: context.repo.repo, + body: mensaje, + }); + + // Cerrar el PR + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + state: 'closed', + }); + + // Etiquetar para identificarlos facilmente en el historial + try { + await github.rest.issues.addLabels({ + issue_number: prNumber, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['redirigido-a-template'], + }); + } catch (e) { + // El label puede no existir aun; no es critico + console.log("Label no encontrado, se omite:", e.message); + } + + console.log(`PR #${prNumber} cerrado y notificado correctamente.`); diff --git a/COMO-HACER-TU-PRIMER-PR.md b/COMO-HACER-TU-PRIMER-PR.md index 03d8438..bfa13a8 100644 --- a/COMO-HACER-TU-PRIMER-PR.md +++ b/COMO-HACER-TU-PRIMER-PR.md @@ -1,116 +1,143 @@ -# ¿CÓMO HACER TU PRIMER PULL REQUEST (PR)? +# Cómo hacer tu primer Pull Request -Si esta es la primera vez que escuchas sobre Git, GitHub o un "Pull Request", estás en el lugar correcto. En este documento te explicaremos paso a paso y con palabras sencillas cómo contribuir a este (o a cualquier) proyecto open source. +> [!IMPORTANT] +> Si estás aquí para resolver los retos de aprendizaje como alumno, lee primero [COMO-USAR-COMO-ALUMNO.md](docs/COMO-USAR-COMO-ALUMNO.md) y [RETOS-SOCRATICOS.md](docs/RETOS-SOCRATICOS.md). Esta guía es para contribuidores que quieren mejorar el material del repositorio. ---- - -## Mini Glosario (Para que no te suene a chino) +Si es la primera vez que escuchas sobre Git, GitHub o Pull Request, estás en el lugar correcto. Aquí explicamos paso a paso cómo contribuir a este —o a cualquier— proyecto open source. -- **Git:** Es un programa en tu computadora que actúa como una "máquina del tiempo". Guarda el historial de todos los cambios que haces en tus archivos de código. -- **GitHub:** Es una página web (una red social para programadores) donde subimos nuestro código usando Git. -- **Fork:** Es como darle al botón de "Compartir -> Crear una copia". Haces un clon exacto del proyecto de alguien más y lo guardas en tu propia cuenta. -- **Clone:** Es descargar tu "Fork" (tu copia en GitHub) hacia tu computadora física para que puedas editar los archivos. -- **Branch (Rama):** Es una línea de tiempo paralela. Creas una rama para no arruinar el código principal (`main`) mientras intentas algo nuevo. -- **Commit:** Es como un punto de guardado (Save State) en un videojuego. Guarda tus cambios permanentemente con un pequeño mensaje explicando qué hiciste. -- **Push:** Es subir tus "Commits" (puntos de guardado) de tu computadora a GitHub. -- **Pull Request (PR):** Es cuando le dices al dueño del proyecto original: *"Oye, mira estos cambios que hice en mi copia. ¿Te gustan? ¿Los quieres añadir a tu proyecto oficial?"*. +Para entender los términos que aparecen en esta guía, consulta el [Glosario de Git y GitHub](glosario/git-github.md). --- -## El diagrama del flujo (Visualiza el proceso) +## El flujo completo (diagrama) ```text -[Proyecto Original] - │ - │ (1. Fork) - ▼ +[Repositorio Original] + | + | (1. Fork) + v [Tu copia en GitHub] ──────┐ - │ │ (6. Pull Request y Revisión) - │ (2. Clone) │ - ▼ │ -[Tu computadora] │ - │ (3. Branch) │ - │ (4. Escribir) │ - │ (5. Commit/Push) ─┘ + | | (6. Pull Request y Revisión) + | (2. Clone) | + v | +[Tu computadora] | + | (3. Branch) | + | (4. Escribir) | + | (5. Commit/Push) ─┘ ``` --- -## ⚙️ Preparativos (Sólo se hace una vez en la vida) - -1. **Instala Git:** - - Windows: Descarga [Git for Windows](https://gitforwindows.org/) e instálalo. - - Mac: Abre la terminal y escribe `git --version`. Si no lo tienes, te pedirá instalarlo. - - Linux: Escribe `sudo apt install git` en tu terminal. -2. **Identifícate ante Git:** - Abre tu terminal y escribe esto (cambiándolo por tu nombre y tu email de GitHub): - ```bash - git config --global user.name "Tu Nombre Aquí" - git config --global user.email "tu_correo@ejemplo.com" - ``` -3. **Autentícate con GitHub:** - GitHub ya no permite usar contraseñas normales en la terminal. La forma más amigable es instalar la [GitHub CLI (gh)](https://cli.github.com/). - Una vez que la instales, abre tu terminal y escribe: - ```bash - gh auth login - ``` - Sigue las instrucciones: elige `GitHub.com`, luego `HTTPS`, luego `Yes` para autenticar con tus credenciales, y finalmente `Login with a web browser`. Se abrirá tu navegador para dar permiso. ¡Y listo! - - *(💡 Alternativa manual: Si no quieres instalar `gh`, debes ir a tu perfil de GitHub -> **Settings > Developer Settings > Personal access tokens > Tokens (classic)**, generar un nuevo token marcando la casilla "repo", y usar ese código larguísimo como si fuera tu contraseña cuando la terminal te lo pida al hacer `git push`).* +## Preparativos (solo se hace una vez) + +**1. Instala Git** + +- Windows: descarga [Git for Windows](https://gitforwindows.org/) e instálalo. +- macOS: abre la terminal y escribe `git --version`. Si no lo tienes, te pedirá instalarlo. +- Linux: `sudo apt install git` + +**2. Identifícate ante Git** + +Abre tu terminal y configura tu nombre y correo (el mismo que usas en GitHub): + +```bash +git config --global user.name "Tu Nombre" +git config --global user.email "tu_correo@ejemplo.com" +``` + +**3. Autentícate con GitHub** + +GitHub no permite usar contraseñas normales desde la terminal. La forma más sencilla es instalar la [GitHub CLI](https://cli.github.com/) y ejecutar: + +```bash +gh auth login +``` + +Sigue las instrucciones: elige `GitHub.com`, luego `HTTPS`, luego `Login with a web browser`. Se abrirá el navegador para autorizar el acceso. + +Alternativa sin `gh`: ve a tu perfil de GitHub → Settings → Developer Settings → Personal access tokens → Tokens (classic). Genera un token marcando la casilla `repo` y úsalo como contraseña cuando la terminal te lo pida al hacer `git push`. --- -## Paso a Paso Práctico +## Paso a paso + +### 1. Fork -### 1. El Fork -Ve arriba a la derecha de este repositorio y presiona el botón que dice **Fork**. Esto creará una copia del proyecto en `https://github.com/TU-USUARIO/nerve-community`. +Ve a la página del repositorio y haz clic en el botón **Fork**. Esto crea una copia del proyecto en `https://github.com/TU-USUARIO/nerve-community`. + +### 2. Clone + +Abre tu terminal en la carpeta donde guardas tus proyectos y descarga tu fork: -### 2. El Clone -Abre tu terminal en la carpeta donde guardas tus proyectos (por ejemplo, en Documentos) y escribe: ```bash git clone https://github.com/TU-USUARIO/nerve-community.git cd nerve-community ``` -### 3. Crear una Branch (Rama) -Nunca trabajes en la rama `main`. Vamos a crear una dimensión paralela para tu trabajo: +### 3. Crear una rama + +Nunca trabajes directamente en `main`. Crea una rama con un nombre descriptivo del cambio que vas a hacer: + ```bash -git checkout -b solucion-reto-01 +git checkout -b mejora-glosario-git ``` ### 4. Haz tus cambios -Usa tu editor de texto favorito (VS Code, Cursor, PyCharm, el bloc de notas) y escribe tu código para resolver el reto. Pruébalo. -### 5. Crea el Commit -Revisa qué archivos cambiaste: +Usa tu editor preferido. Navega a los archivos que quieres mejorar y realiza los cambios. + +Si estás resolviendo un reto socrático, ve a la carpeta correspondiente (por ejemplo `python/Modulo-01-Fundamentos/`) y escribe tu código. Verifica que los tests pasen localmente antes de continuar: + ```bash -git status +pytest test_main.py ``` -Dile a Git que prepare todos los archivos modificados: + +### 5. Commit + +Revisa qué archivos modificaste: + ```bash -git add . +git status ``` -Guarda el punto de control con un mensaje claro: + +Prepara los archivos y crea el punto de guardado: + ```bash -git commit -m "Termino el reto 01 de chat en terminal" +git add . +git commit -m "feat: corrige explicación de git rebase en glosario" ``` -### 6. El Push -Sube tu rama desde tu computadora hacia tu copia en GitHub: +Usa mensajes descriptivos. El prefijo `feat:` es para cambios nuevos, `fix:` para correcciones, `docs:` para documentación. + +### 6. Push + +Sube tu rama a tu copia en GitHub: + ```bash -git push origin solucion-reto-01 +git push origin mejora-glosario-git ``` ### 7. Abre el Pull Request -Ve a la página de tu copia en GitHub. Arriba verás un banner amarillo/verde que dice "Compare & pull request". Dale clic. -Rellena la plantilla explicando qué hiciste. ¡Envía tu Pull Request! + +Ve a la página de tu fork en GitHub. Verás un banner que dice "Compare & pull request". Haz clic, rellena la plantilla del PR marcando las casillas correspondientes y envíalo. --- -## 🚨 Errores Comunes de Principiante +## Errores comunes + +**"Escribí código directamente en main por accidente."** +No pasa nada si aún no hiciste commit. Escribe `git checkout -b nueva-rama` y tus cambios se moverán a esa nueva rama. + +**"Hice un commit pero me equivoqué en el mensaje."** +Escribe `git commit --amend -m "Mensaje corregido"`. Solo funciona antes de hacer push. -- **"Escribí código directamente en main por accidente":** No pasa nada. No hagas `git commit` todavía. Simplemente escribe `git checkout -b nueva-rama` y tus cambios "flotarán" a la nueva rama. -- **"Hice un commit pero me equivoqué en el mensaje":** Escribe `git commit --amend -m "Mensaje corregido"`. -- **"Dice que tengo conflictos de merge":** Esto pasa si alguien más editó los mismos archivos que tú. En VS Code, abre los archivos con conflictos y verás resaltadas las diferencias. Elige qué código quieres mantener, guarda el archivo, haz `git add .` y luego `git commit -m "Resuelve conflictos"`. +**"Dice que tengo conflictos de merge."** +Ocurre cuando alguien más editó los mismos archivos que tú. En VS Code, los archivos con conflictos muestran las diferencias resaltadas. Elige qué código mantener, guarda el archivo y luego: +```bash +git add . +git commit -m "fix: resuelve conflictos de merge" +``` + +--- -¡Tú puedes! Recuerda que nadie rompe un proyecto Open Source por accidente; para eso están las revisiones de Pull Requests. Anímate. +← [Volver al repositorio](README.md) diff --git a/s.html b/Cosas tontas que use/s.html similarity index 100% rename from s.html rename to Cosas tontas que use/s.html diff --git a/MEGA_RETO.md b/MEGA_RETO.md deleted file mode 100644 index e79b483..0000000 --- a/MEGA_RETO.md +++ /dev/null @@ -1,36 +0,0 @@ -# El Mega Reto Trimestral - -¡Felicidades! Si estás leyendo esto, es porque has demostrado tu constancia y has completado el 100% de los retos de nivel Principiante requeridos. Has desbloqueado el **Mega Reto**, la puerta de entrada para participar en el Premio Trimestral de la comunidad. - -## Objetivo: Construye un MVP - -A diferencia de los retos anteriores donde se te daba un problema específico con instrucciones precisas, el Mega Reto evalúa tu capacidad para integrar lo aprendido. - -Tu misión es construir un **Producto Mínimo Viable (MVP)** que integre al menos **2 o 3 conceptos** vistos en los retos que ya completaste. Por ejemplo: -- Si hiciste el reto de scraping (PG 02) y el traductor (Nerve 03), podrías hacer una herramienta que scrapea noticias, las traduce y te las manda. -- Si hiciste el de gastos (PG 04) y recordatorios (PG 09), podrías hacer un bot de gastos con alertas locales. -- Si dominas Nerve, podrías hacer un sistema de nodos que hagan alguna de las tareas anteriores. - -¡Tú decides qué construir! Puede vivir dentro del ecosistema Nerve (usando IPC) o ser una herramienta Python independiente y útil para ti o para la comunidad. - -## El Premio Trimestral - -Al final de cada trimestre, los mantenedores y moderadores evaluaremos los MVPs entregados. El autor del mejor proyecto recibirá: -- **4 meses gratis de Google AI Pro** (acceso a Gemini Advanced, Antigravity, y beneficios de Google Cloud). - -## 📊 Criterios de Evaluación - -Tu proyecto será evaluado en base a 4 pilares: -1. **Utilidad Real:** ¿Resuelve un problema real, por pequeño que sea? -2. **Creatividad:** ¿Cómo combinaste los conceptos de los retos anteriores? -3. **Calidad de Código:** ¿Tu código está limpio, estructurado y es fácil de leer? -4. **Documentación:** ¿El `README.md` explica claramente qué es, cómo instalarlo y cómo usarlo? - -## 📦 Cómo Participar - -1. Desarrolla tu MVP en una carpeta nueva dentro de `retos/mega-reto/[tu-usuario]/`. -2. Incluye todo tu código y, muy importante, tu propio `README.md`. -3. Abre un **Pull Request** titulado `Mega Reto - [Nombre de tu MVP] - [Tu usuario]`. -4. El bot de Discord y los moderadores validarán que cumplas con los requisitos. - -¡Mucho éxito y a programar! diff --git a/README.md b/README.md index 48127d8..1258f0e 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,66 @@ -# nerve-community - -![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg) -![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg) -![Good First Issue](https://img.shields.io/badge/good%20first%20issue-welcome-7057ff.svg) -![Hacktoberfest](https://img.shields.io/badge/Hacktoberfest-participating-ff7518.svg) -![Idioma: Español](https://img.shields.io/badge/Idioma-Espa%C3%B1ol-c5def5.svg) - -> **Tu primer Pull Request puede empezar aquí.** - -## ¿Qué es este repo? -Este es un espacio de aprendizaje y contribución diseñado para cualquiera que quiera perderle el miedo a colaborar en proyectos Open Source. Usamos **Nerve** como excusa técnica para construir herramientas y scripts pequeños, divertidos y útiles. **No hay preguntas tontas.** Si nunca has hecho un Pull Request, o si apenas estás aprendiendo a programar en Python, este es el lugar ideal para ti. - -### 🔍 ¿Por qué existe este repo? -Creemos en ser honestos sobre esto. [Lee la explicación completa aquí](docs/Porque.md). - -### 📚 ¿No entiendes algún término? -Si eres nuevo y palabras como *fork*, *commit*, *push* o *pull request* te suenan raro, ¡no te preocupes! Preparamos un **[Glosario para Principiantes](glosario/README.md)** donde explicamos cada término (qué es, para qué sirve y un ejemplo) de forma súper sencilla. - -## ¿Por qué existe Nerve? -[alenia-nerve](https://github.com/Kaia-Alenia/alenia-nerve) es un sistema de comunicación entre procesos (IPC) local, multiplataforma y offline-first, que te permite conectar diferentes scripts (incluso en diferentes lenguajes) de forma súper sencilla usando sockets locales (Unix Sockets en Linux/macOS, TCP en Windows). - -## 💬 Únete a la comunidad -Si tienes dudas, quieres hablar con otros contribuidores o simplemente quieres saludar, ¡estamos en Discord! -👉 **[Únete al servidor de Nerve Community aquí](https://discord.gg/xFptGAr7t)** - -## Retos Disponibles - -### Reto Rompehielo -| Reto | Nivel | Tecnología principal | Estado | Enlace al Issue | -|---|---|---|---|---| -| **00-rompe-el-hielo** | 🟢 Principiante | GitHub | `disponible` | [#12](https://github.com/Kaia-Alenia/nerve-community/issues/12) | - -### Retos Core (Nerve) -| Reto | Nivel | Tecnología principal | Estado | Enlace al Issue | -|---|---|---|---|---| -| **01-chat-terminal** | 🟢 Principiante | Python (Sockets, Nerve) | `disponible` | [#1](https://github.com/Kaia-Alenia/nerve-community/issues/1) | -| **02-reloj-sincronizado** | 🟢 Principiante | Python (rich) | `disponible` | [#2](https://github.com/Kaia-Alenia/nerve-community/issues/2) | -| **03-traductor-de-mensajes** | 🟢 Principiante | Python (Requests/APIs) | `disponible` | [#3](https://github.com/Kaia-Alenia/nerve-community/issues/3) | -| **04-vigilante-de-carpetas** | 🟡 Intermedio | Python (watchdog) | `disponible` | [#4](https://github.com/Kaia-Alenia/nerve-community/issues/4) | -| **05-logger-distribuido** | 🟡 Intermedio | Python (SQLite) | `disponible` | [#5](https://github.com/Kaia-Alenia/nerve-community/issues/5) | -| **06-puente-de-progreso-gif** | 🟡 Intermedio | Python (Pillow) | `disponible` | [#6](https://github.com/Kaia-Alenia/nerve-community/issues/6) | -| **07-bot-discord-nerve** | 🟡 Intermedio | Python (discord.py) | `disponible` | [#7](https://github.com/Kaia-Alenia/nerve-community/issues/7) | -| **08-puente-http** | 🔴 Avanzado | Python (FastAPI) | `disponible` | [#8](https://github.com/Kaia-Alenia/nerve-community/issues/8) | -| **09-probador-de-carga** | 🔴 Avanzado | Python (Concurrencia) | `disponible` | [#9](https://github.com/Kaia-Alenia/nerve-community/issues/9) | -| **10-cliente-nuevo-lenguaje** | 🔴 Avanzado | Dart / Kotlin / Go / etc. | `disponible` | [#10](https://github.com/Kaia-Alenia/nerve-community/issues/10) | - -### Retos de Python General -| Reto | Nivel | Tecnología principal | Estado | Enlace al Issue | -|---|---|---|---|---| -| **01-lista-tareas** | 🟢 Principiante | Python | `disponible` | [#13](https://github.com/Kaia-Alenia/nerve-community/issues/13) | -| **02-web-scraper** | 🟢 Principiante | Python | `disponible` | [#14](https://github.com/Kaia-Alenia/nerve-community/issues/14) | -| **03-juego-adivinar** | 🟢 Principiante | Python | `disponible` | [#15](https://github.com/Kaia-Alenia/nerve-community/issues/15) | -| **04-analizador-gastos** | 🟢 Principiante | Python | `disponible` | [#16](https://github.com/Kaia-Alenia/nerve-community/issues/16) | -| **05-generador-passwords** | 🟢 Principiante | Python | `disponible` | [#17](https://github.com/Kaia-Alenia/nerve-community/issues/17) | -| **06-conversor-divisas** | 🟢 Principiante | Python | `disponible` | [#18](https://github.com/Kaia-Alenia/nerve-community/issues/18) | -| **07-calculadora-propinas** | 🟢 Principiante | Python | `disponible` | [#19](https://github.com/Kaia-Alenia/nerve-community/issues/19) | -| **08-organizador-archivos** | 🟢 Principiante | Python | `disponible` | [#20](https://github.com/Kaia-Alenia/nerve-community/issues/20) | -| **09-recordatorio-tareas** | 🟡 Intermedio | Python | `disponible` | [#21](https://github.com/Kaia-Alenia/nerve-community/issues/21) | -| **10-analizador-texto** | 🟢 Principiante | Python | `disponible` | [#22](https://github.com/Kaia-Alenia/nerve-community/issues/22) | -| **11-profiler-de-funciones** | 🟡 Intermedio | Python | `disponible` | [#25](https://github.com/Kaia-Alenia/nerve-community/issues/25) | -| **12-validador-de-emails** | 🟡 Intermedio | Python | `disponible` | [#26](https://github.com/Kaia-Alenia/nerve-community/issues/26) | -| **13-lector-archivos-gigantes** | 🟡 Intermedio | Python | `disponible` | [#27](https://github.com/Kaia-Alenia/nerve-community/issues/27) | -| **14-cli-del-clima** | 🟡 Intermedio | Python | `disponible` | [#28](https://github.com/Kaia-Alenia/nerve-community/issues/28) | -| **15-herramienta-de-backup** | 🟡 Intermedio | Python | `disponible` | [#29](https://github.com/Kaia-Alenia/nerve-community/issues/29) | -| **16-validador-de-edad** | 🟡 Intermedio | Python | `disponible` | [#30](https://github.com/Kaia-Alenia/nerve-community/issues/30) | -| **17-lector-de-configuraciones** | 🟡 Intermedio | Python | `disponible` | [#31](https://github.com/Kaia-Alenia/nerve-community/issues/31) | -| **18-inventario-de-tienda** | 🟡 Intermedio | Python | `disponible` | [#32](https://github.com/Kaia-Alenia/nerve-community/issues/32) | - -> 📝 **¿Eres mantenedor/a o quieres ver el texto original de los retos?** -> Consulta las plantillas de los issues en [RETOS_ISSUES.md](docs/RETOS_ISSUES.md) (para los retos Rompehielo y Core) y [PYTHON_GENERAL_ISSUES.md](docs/PYTHON_GENERAL_ISSUES.md) (para los retos de Python General). - -## 📱 ¿No tienes computadora? ¡Usa tu celular! -No tener PC no es excusa para no programar. Puedes resolver muchos de nuestros retos usando solo tu celular Android y la app **Termux**. -Varios de nuestros retos (los de Python General) están marcados con la etiqueta `termux-friendly`, lo que significa que no necesitan compiladores pesados ni bases de datos complejas. - -👉 **[Lee la guía paso a paso para empezar desde tu celular aquí](docs/EMPEZAR-CON-SOLO-TU-CELULAR.md)**. - -## ¿Nunca has hecho un Pull Request? -¡No te preocupes! Tenemos una guía paso a paso, desde cero, explicando cada comando que necesitas escribir. Léela aquí: **[CÓMO HACER TU PRIMER PR](COMO-HACER-TU-PRIMER-PR.md)**. - -## Reglas de oro (Innegociables) -- **NUNCA** pidas acceso de escritura directo. Siempre haz un *fork* de este repositorio. -- **¡Todos pueden participar!** En esta primera edición no hay límite de personas por reto. Si te interesa un reto, simplemente comenta "¡Yo lo tomo!" o "/tomar-reto" en su issue para avisarnos, ¡incluso si alguien más ya lo está haciendo! -- Todo Pull Request (PR) pasa por una **revisión** antes de hacer *merge*. No te asustes si te pedimos algún cambio; es parte de aprender. -- **Sé amable.** En los comentarios o reportes de bugs, recuerda que aquí todos estamos aprendiendo. - -## De la comunidad a producción -Las mejores herramientas construidas aquí no se quedan solo como ejercicio. Si tu contribución está bien hecha, es útil y estable, puede ser invitada a integrarse directamente al repositorio de producción **zenith-nerve-tools**, con crédito completo para ti como autor/a. Varios de los retos de Nivel 2 y 3 están pensados justo para eso: son herramientas reales que pueden pasar de "ejercicio" a "software que otros usan". - -## Premio Trimestral -La contribución más destacada del trimestre recibe **4 meses gratis de Google AI Pro** (acceso a Gemini Advanced, Antigravity con cuotas mucho más altas, agentes Claude, y beneficios del ecosistema Google Cloud). - -## Financiamiento y Transparencia -Las donaciones vía Ko-fi / GitHub Sponsors se usan exclusivamente para financiar los premios de la comunidad (suscripciones, reconocimientos, y eventualmente merch). Cada trimestre publicaremos un resumen transparente de cómo se usó lo recaudado en nuestro archivo [TRANSPARENCIA.md](TRANSPARENCIA.md). - -## Cumpliendo Retos -¡Gracias a todas las personas que hacen esto posible! Cada contribución cuenta. - -- [Tacos-1212](https://github.com/Tacos-1212) -- [Vi-dot1](https://github.com/Vi-dot1) -- [CesarAAR](https://github.com/CesarAAR) -- [isaacglezacosta](https://github.com/isaacglezacosta) -- [downstairs777](https://github.com/downstairs777) - -## Creando Retos -- [Alejandro (Alenia Studios)](https://github.com/Alenia-Studios) - -## 📄 Licencia -Este repositorio, al igual que Nerve, está licenciado bajo **GNU GPL v3**. Lee el archivo `LICENSE` para más detalles. +# Nerve Community + +Bienvenido a **nerve-community**, el repositorio público del ecosistema de aprendizaje de programación de Alenia Studios. Aquí reside el currículum completo, la teoría, los ejemplos y las pruebas que conforman los cursos. + +Para entender el propósito y enfoque pedagógico del proyecto, lee nuestra filosofía: **[Aprender a Programar de Verdad](docs/Porque.md)**. + +--- + +## Dos formas de usar este repositorio + +### Como Alumno — no hagas Fork de este repo + +Si estás tomando el programa o quieres hacer los cursos, **no debes hacer un Fork de este repositorio**. El flujo de aprendizaje parte de un template oficial que te genera un entorno aislado, automatizado y privado. + +- Instrucciones paso a paso: [Cómo usar como Alumno](docs/COMO-USAR-COMO-ALUMNO.md) +- Metodología de evaluación: [Filosofía de los Retos Socráticos](docs/RETOS-SOCRATICOS.md) + +En tu repositorio privado (generado desde el template): + +- Al inicio solo tendrás disponible el Módulo 01. +- Cada módulo tiene teoría (`teoria.md`), código de referencia (`ejemplo.py`) y un desafío (`reto.md`). +- Para cada reto, leerás el código, interpretarás los errores y resolverás el problema con el apoyo de los tests automáticos. +- Al pasar la validación de CI con un check verde, se desbloquea el siguiente módulo. + +### Como Contribuidor — Open Source + +Si quieres mejorar la calidad del material del curso: + +- Haz **Fork** de este repositorio (`nerve-community`). +- Mejora el contenido (documentación, ejemplos, retos) y envía un **Pull Request**. +- Toda la comunidad se beneficia de tus mejoras. + +Guías para contribuir: + +- [Guía de Contribución](.github/CONTRIBUTING.md) +- [Cómo hacer tu primer PR](COMO-HACER-TU-PRIMER-PR.md) + +--- + +## Estructura del repositorio + +```text +python/ ← Currículum completo del curso de Python (Módulos 01 a 12 + intermedios) +retos/ ← Retos socráticos clasificados por lenguaje y nivel de dificultad +docs/ ← Documentación del proyecto, filosofía y guías de uso +.github/ ← Workflows de GitHub Actions, plantillas de Issues/PRs y guía de contribución +glosario/ ← Diccionario técnico de términos clave, organizado por tema y lenguaje +``` + +> **Regla de Oro:** La estructura de carpetas dentro de los lenguajes (ej. `python/Modulo-XX`) no debe alterarse en nomenclatura. Estos archivos alimentan los templates de los alumnos y la evaluación automática. + +--- + +## Qué puedes mejorar + +- Aclarar explicaciones en los archivos `teoria.md`. +- Añadir comentarios o casos alternativos en los `ejemplo.py`. +- Corregir bugs o robustecer pruebas en los `test_main.py`. +- Proponer nuevos Retos Socráticos dentro de `retos/`. + +--- + +## Comunidad y Contacto + +- **Discord**: [Únete a nuestra comunidad](https://discord.gg/xFptGAr7t) para resolver dudas, compartir proyectos y hablar con otros estudiantes y desarrolladores. +- **Licencia**: GNU GPL v3. diff --git a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/README.md b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/README.md deleted file mode 100644 index 9355646..0000000 --- a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/README.md +++ /dev/null @@ -1,18 +0,0 @@ -## Manejo de Archivos JSON - -**¿Qué muestra este ejemplo?** -Cómo leer y escribir archivos JSON de forma segura usando el módulo `json` de la librería estándar, incluyendo cómo manejar el caso donde el archivo no existe en la primera ejecución. - -**¿Por qué es útil?** -Sirve para guardar configuración de un programa, cachear datos, o persistir información simple entre diferentes ejecuciones sin necesitar una base de datos completa. - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_json.py` - -**¿Qué retos usan esta base?** -- [#13 Python General 01 — Lista de tareas (to-do list) en terminal](https://github.com/Kaia-Alenia/nerve-community/issues/13) -- [#21 Python General 09 — Recordatorios en terminal](https://github.com/Kaia-Alenia/nerve-community/issues/21) - -**Nota para principiantes** -Recuerda que `json.dump` reescribe el archivo completo. Si quieres agregar un solo elemento a una lista, primero debes cargar la lista con `json.load`, hacer el `.append()`, y volver a guardarla completa. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go deleted file mode 100644 index c350db1..0000000 --- a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.go +++ /dev/null @@ -1,152 +0,0 @@ -// Ejemplo: Mini gestor de contactos con menú interactivo y persistencia JSON en Go -// -// Qué enseña este ejemplo: -// - Cómo crear un loop con for {} -// - Cómo usar bufio.NewScanner para leer líneas de la terminal -// - Cómo leer y guardar archivos con os.ReadFile y os.WriteFile -// - Cómo usar encoding/json para convertir structs de Go a JSON y viceversa -// -// Glosario de términos "raros": -// bufio.NewScanner — Crea un "escáner" que facilita leer texto de la consola línea por línea. -// os.ReadFile — Lee todo el contenido de un archivo de una sola vez. -// os.WriteFile — Guarda datos en un archivo, sobreescribiéndolo. -// json.Marshal — (De "marshall") Convierte tus variables de Go a texto JSON. -// json.Unmarshal — Convierte texto JSON de vuelta a variables de Go. -// 0644 — Permisos del archivo (lectura/escritura para ti, lectura para otros). - -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "os" - "strconv" - "strings" -) - -const ARCHIVO = "contactos.json" - -// Contacto representa un elemento de nuestra lista. -// Las etiquetas `json:"nombre"` indican cómo se llamará el campo en el archivo. -type Contacto struct { - Nombre string `json:"nombre"` - Telefono string `json:"telefono"` -} - -func cargarContactos() []Contacto { - // Intentamos leer el archivo - data, err := os.ReadFile(ARCHIVO) - if err != nil { - // Si hay un error (ej. el archivo no existe), devolvemos lista vacía - return []Contacto{} - } - - var contactos []Contacto - // Convertimos el JSON a la lista de structs - json.Unmarshal(data, &contactos) - return contactos -} - -func guardarContactos(contactos []Contacto) { - // MarshalIndent hace que el JSON sea legible (con espacios) - data, _ := json.MarshalIndent(contactos, "", " ") - os.WriteFile(ARCHIVO, data, 0644) -} - -func mostrarContactos(contactos []Contacto) { - if len(contactos) == 0 { - fmt.Println("No hay contactos guardados todavía.") - return - } - fmt.Println("\n--- Tus contactos ---") - for i, contacto := range contactos { - fmt.Printf(" %d. %s — %s\n", i+1, contacto.Nombre, contacto.Telefono) - } -} - -// Función auxiliar para leer texto -func preguntar(prompt string, scanner *bufio.Scanner) string { - fmt.Print(prompt) - scanner.Scan() - return strings.TrimSpace(scanner.Text()) -} - -func main() { - contactos := cargarContactos() - scanner := bufio.NewScanner(os.Stdin) - - for { - fmt.Println("\n=== Gestor de Contactos ===") - fmt.Println("1. Ver contactos") - fmt.Println("2. Agregar contacto") - fmt.Println("3. Actualizar teléfono") - fmt.Println("4. Eliminar contacto") - fmt.Println("5. Salir") - - opcion := preguntar("\nElige una opción (1-5): ", scanner) - - switch opcion { - case "1": - mostrarContactos(contactos) - - case "2": - nombre := preguntar("Nombre: ", scanner) - telefono := preguntar("Teléfono: ", scanner) - - if nombre != "" { - contactos = append(contactos, Contacto{Nombre: nombre, Telefono: telefono}) - guardarContactos(contactos) - fmt.Printf("✓ Contacto '%s' guardado.\n", nombre) - } else { - fmt.Println("El nombre no puede estar vacío.") - } - - case "3": - mostrarContactos(contactos) - if len(contactos) == 0 { - continue - } - - idxStr := preguntar("\nNúmero del contacto a actualizar: ", scanner) - idx, err := strconv.Atoi(idxStr) - idx -= 1 // Ajustamos para índice 0 - - if err == nil && idx >= 0 && idx < len(contactos) { - nuevoTel := preguntar("Nuevo teléfono: ", scanner) - contactos[idx].Telefono = nuevoTel - guardarContactos(contactos) - fmt.Println("✓ Contacto actualizado.") - } else { - fmt.Println("Número inválido.") - } - - case "4": - mostrarContactos(contactos) - if len(contactos) == 0 { - continue - } - - idxStr := preguntar("\nNúmero del contacto a eliminar: ", scanner) - idx, err := strconv.Atoi(idxStr) - idx -= 1 - - if err == nil && idx >= 0 && idx < len(contactos) { - eliminado := contactos[idx] - // Magia para eliminar un elemento de un slice en Go: - contactos = append(contactos[:idx], contactos[idx+1:]...) - guardarContactos(contactos) - fmt.Printf("✓ Contacto '%s' eliminado.\n", eliminado.Nombre) - } else { - fmt.Println("Número inválido.") - } - - case "5": - fmt.Println("¡Hasta luego!") - return - - default: - fmt.Println("Opción no reconocida, intenta de nuevo.") - } - } -} diff --git a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js deleted file mode 100644 index eff2c07..0000000 --- a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Ejemplo: Mini gestor de contactos con menú interactivo y persistencia JSON en Node.js - * - * Qué enseña este ejemplo: - * - Cómo leer entrada del usuario en la terminal (readline) - * - Cómo leer y escribir archivos de forma síncrona (fs.readFileSync / fs.writeFileSync) - * - Cómo convertir JSON de texto a objetos y viceversa (JSON.parse / JSON.stringify) - * - Cómo manejar arrays y modificar sus elementos - * - * Glosario de términos "raros": - * fs — (File System) Módulo nativo de Node.js para trabajar con archivos. - * readline — Módulo nativo para leer lo que el usuario escribe en la consola. - * JSON.parse — Convierte un texto JSON en un objeto o array de JavaScript. - * JSON.stringify — Convierte un objeto o array de JavaScript a texto JSON. - * utf8 — Formato de texto estándar (soporta acentos, ñ, etc.). - */ - -const fs = require('fs'); -const readline = require('readline'); - -const ARCHIVO = 'contactos.json'; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}); - -// En Node.js clásico, preguntar algo por consola es asíncrono con callbacks o promesas. -// Usaremos una pequeña función de ayuda para usar promesas y async/await: -function preguntar(texto) { - return new Promise((resolve) => { - rl.question(texto, resolve); - }); -} - -function cargarContactos() { - if (!fs.existsSync(ARCHIVO)) { - return []; - } - const data = fs.readFileSync(ARCHIVO, 'utf8'); - return JSON.parse(data); -} - -function guardarContactos(contactos) { - // stringify(obj, null, 4) formatea el JSON con 4 espacios - fs.writeFileSync(ARCHIVO, JSON.stringify(contactos, null, 4), 'utf8'); -} - -function mostrarContactos(contactos) { - if (contactos.length === 0) { - console.log("No hay contactos guardados todavía."); - return; - } - console.log("\n--- Tus contactos ---"); - contactos.forEach((contacto, index) => { - console.log(` ${index + 1}. ${contacto.nombre} — ${contacto.telefono}`); - }); -} - -async function agregarContacto(contactos) { - const nombre = (await preguntar("Nombre: ")).trim(); - const telefono = (await preguntar("Teléfono: ")).trim(); - - if (nombre) { - contactos.push({ nombre, telefono }); - guardarContactos(contactos); - console.log(`✓ Contacto '${nombre}' guardado.`); - } else { - console.log("El nombre no puede estar vacío."); - } -} - -async function actualizarContacto(contactos) { - mostrarContactos(contactos); - if (contactos.length === 0) return; - - const idxStr = await preguntar("\nNúmero del contacto a actualizar: "); - const idx = parseInt(idxStr) - 1; - - if (idx >= 0 && idx < contactos.length) { - const nuevoTel = (await preguntar("Nuevo teléfono: ")).trim(); - contactos[idx].telefono = nuevoTel; - guardarContactos(contactos); - console.log("✓ Contacto actualizado."); - } else { - console.log("Número inválido."); - } -} - -async function eliminarContacto(contactos) { - mostrarContactos(contactos); - if (contactos.length === 0) return; - - const idxStr = await preguntar("\nNúmero del contacto a eliminar: "); - const idx = parseInt(idxStr) - 1; - - if (idx >= 0 && idx < contactos.length) { - const eliminado = contactos.splice(idx, 1)[0]; - guardarContactos(contactos); - console.log(`✓ Contacto '${eliminado.nombre}' eliminado.`); - } else { - console.log("Número inválido."); - } -} - -async function main() { - let contactos = cargarContactos(); - - while (true) { - console.log("\n=== Gestor de Contactos ==="); - console.log("1. Ver contactos"); - console.log("2. Agregar contacto"); - console.log("3. Actualizar teléfono"); - console.log("4. Eliminar contacto"); - console.log("5. Salir"); - - const opcion = (await preguntar("\nElige una opción (1-5): ")).trim(); - - if (opcion === "1") { - mostrarContactos(contactos); - } else if (opcion === "2") { - await agregarContacto(contactos); - } else if (opcion === "3") { - await actualizarContacto(contactos); - } else if (opcion === "4") { - await eliminarContacto(contactos); - } else if (opcion === "5") { - console.log("¡Hasta luego!"); - break; - } else { - console.log("Opción no reconocida, intenta de nuevo."); - } - } - rl.close(); -} - -main(); diff --git a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py deleted file mode 100644 index 15f894c..0000000 --- a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.py +++ /dev/null @@ -1,140 +0,0 @@ -""" -Ejemplo: Mini gestor de contactos con menú interactivo y persistencia JSON - -Qué enseña este ejemplo: - - Cómo crear un loop de menú principal con while True + break - - Cómo usar input() para leer texto y números del usuario - - Cómo guardar y cargar datos con json.dump / json.load - - Cómo actualizar un elemento específico de una lista (modificar un diccionario) - - Cómo eliminar un elemento usando list.pop() o del - - Cómo manejar el caso en que el archivo aún no existe - -Para tu reto (lista de tareas): - Aplica el mismo patrón de loop + input() + guardado JSON, pero en vez de - contactos (nombre/teléfono) trabajarás con tareas (texto/completado). - - Tu "agregar" será para nuevas tareas. - - Tu "actualizar" será tu "marcar como completada". - - Tu "eliminar" funcionará igual que eliminar un contacto. - -Glosario de términos "raros": - with open(...) as f — Abre un archivo de forma segura. "f" es solo una variable - corta para "file" (archivo). Si hay error, cierra el archivo solo. - "r" o "w" — Modos al abrir un archivo: "r" (read = leer), "w" (write = escribir). - encoding="utf-8" — Asegura que los acentos y caracteres especiales (ñ, á) se guarden bien. - json.load() — Convierte texto JSON de un archivo a una lista/diccionario en Python. - json.dump() — Convierte una lista/diccionario en Python a texto JSON en un archivo. -""" - -import json -import os - -ARCHIVO = "contactos.json" - - -def cargar_contactos(): - """ - Carga la lista de contactos desde el archivo JSON. - Si el archivo no existe (primera vez), devuelve una lista vacía. - """ - if not os.path.exists(ARCHIVO): - return [] - - with open(ARCHIVO, "r", encoding="utf-8") as f: - return json.load(f) - - -def guardar_contactos(contactos): - """Guarda la lista de contactos en el archivo JSON.""" - with open(ARCHIVO, "w", encoding="utf-8") as f: - json.dump(contactos, f, indent=4, ensure_ascii=False) - - -def mostrar_contactos(contactos): - if not contactos: - print("No hay contactos guardados todavía.") - return - print("\n--- Tus contactos ---") - for i, contacto in enumerate(contactos, start=1): - print(f" {i}. {contacto['nombre']} — {contacto['telefono']}") - - -def agregar_contacto(contactos): - nombre = input("Nombre: ").strip() - telefono = input("Teléfono: ").strip() - if nombre: - contactos.append({"nombre": nombre, "telefono": telefono}) - guardar_contactos(contactos) - print(f"✓ Contacto '{nombre}' guardado.") - else: - print("El nombre no puede estar vacío.") - - -def actualizar_contacto(contactos): - """Demuestra cómo modificar un elemento existente en la lista.""" - mostrar_contactos(contactos) - if not contactos: - return - - try: - idx = int(input("\nNúmero del contacto a actualizar: ")) - 1 - if 0 <= idx < len(contactos): - nuevo_tel = input("Nuevo teléfono: ").strip() - # Modificamos el valor dentro del diccionario - contactos[idx]["telefono"] = nuevo_tel - guardar_contactos(contactos) - print("✓ Contacto actualizado.") - else: - print("Número inválido.") - except ValueError: - print("Por favor, ingresa un número válido.") - - -def eliminar_contacto(contactos): - """Demuestra cómo eliminar un elemento de la lista usando pop().""" - mostrar_contactos(contactos) - if not contactos: - return - - try: - idx = int(input("\nNúmero del contacto a eliminar: ")) - 1 - if 0 <= idx < len(contactos): - # pop() remueve y devuelve el elemento en ese índice - eliminado = contactos.pop(idx) - guardar_contactos(contactos) - print(f"✓ Contacto '{eliminado['nombre']}' eliminado.") - else: - print("Número inválido.") - except ValueError: - print("Por favor, ingresa un número válido.") - - -def main(): - contactos = cargar_contactos() - - while True: - print("\n=== Gestor de Contactos ===") - print("1. Ver contactos") - print("2. Agregar contacto") - print("3. Actualizar teléfono") - print("4. Eliminar contacto") - print("5. Salir") - - opcion = input("\nElige una opción (1-5): ").strip() - - if opcion == "1": - mostrar_contactos(contactos) - elif opcion == "2": - agregar_contacto(contactos) - elif opcion == "3": - actualizar_contacto(contactos) - elif opcion == "4": - eliminar_contacto(contactos) - elif opcion == "5": - print("¡Hasta luego!") - break - else: - print("Opción no reconocida, intenta de nuevo.") - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs b/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs deleted file mode 100644 index d8c0e5d..0000000 --- a/archivo-historico/ejemplos/general/01-lista-de-tareas-cli/ejemplo_menu_interactivo.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Ejemplo: Mini gestor de contactos con menú interactivo y persistencia JSON en Rust -// -// Qué enseña este ejemplo: -// - Cómo crear un loop con "loop {}" -// - Cómo leer la entrada del usuario con std::io::stdin() -// - Cómo leer y guardar archivos con std::fs -// - Cómo usar serde_json para convertir structs de Rust a JSON y viceversa -// -// Glosario de términos "raros": -// std::io::stdin().read_line — Lee una línea de texto que escribe el usuario. -// std::fs::read_to_string — Lee un archivo completo y lo guarda en un String. -// serde_json::from_str — Convierte un String con JSON en una estructura (Vector) de Rust. -// serde_json::to_string_pretty — Convierte variables de Rust en texto JSON formateado. -// #[derive(Serialize, Deserialize)] — Magia de Rust que le enseña a tu Struct cómo convertirse a JSON. - -use serde::{Deserialize, Serialize}; -use std::fs; -use std::io::{self, Write}; - -const ARCHIVO: &str = "contactos.json"; - -#[derive(Serialize, Deserialize, Clone)] -struct Contacto { - nombre: String, - telefono: String, -} - -fn cargar_contactos() -> Vec { - // Intentamos leer el archivo. Si falla (ej. no existe), devolvemos un vector vacío - if let Ok(contenido) = fs::read_to_string(ARCHIVO) { - if let Ok(contactos) = serde_json::from_str(&contenido) { - return contactos; - } - } - Vec::new() -} - -fn guardar_contactos(contactos: &[Contacto]) { - // Convertimos a JSON con formato bonito (pretty) - if let Ok(json_str) = serde_json::to_string_pretty(contactos) { - let _ = fs::write(ARCHIVO, json_str); - } -} - -fn mostrar_contactos(contactos: &[Contacto]) { - if contactos.is_empty() { - println!("No hay contactos guardados todavía."); - return; - } - println!("\n--- Tus contactos ---"); - for (i, contacto) in contactos.iter().enumerate() { - println!(" {}. {} — {}", i + 1, contacto.nombre, contacto.telefono); - } -} - -// Función auxiliar para preguntar algo en consola y obtener un String limpio -fn preguntar(prompt: &str) -> String { - print!("{}", prompt); - io::stdout().flush().unwrap(); // Asegura que el prompt se imprima antes de esperar el input - - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap_or_default(); - input.trim().to_string() -} - -fn main() { - let mut contactos = cargar_contactos(); - - loop { - println!("\n=== Gestor de Contactos ==="); - println!("1. Ver contactos"); - println!("2. Agregar contacto"); - println!("3. Actualizar teléfono"); - println!("4. Eliminar contacto"); - println!("5. Salir"); - - let opcion = preguntar("\nElige una opción (1-5): "); - - match opcion.as_str() { - "1" => mostrar_contactos(&contactos), - "2" => { - let nombre = preguntar("Nombre: "); - let telefono = preguntar("Teléfono: "); - - if !nombre.is_empty() { - contactos.push(Contacto { - nombre: nombre.clone(), - telefono, - }); - guardar_contactos(&contactos); - println!("✓ Contacto '{}' guardado.", nombre); - } else { - println!("El nombre no puede estar vacío."); - } - } - "3" => { - mostrar_contactos(&contactos); - if contactos.is_empty() { - continue; - } - - let idx_str = preguntar("\nNúmero del contacto a actualizar: "); - if let Ok(mut idx) = idx_str.parse::() { - idx -= 1; // Ajustamos a índice 0 - if idx < contactos.len() { - let nuevo_tel = preguntar("Nuevo teléfono: "); - contactos[idx].telefono = nuevo_tel; - guardar_contactos(&contactos); - println!("✓ Contacto actualizado."); - } else { - println!("Número inválido."); - } - } else { - println!("Por favor, ingresa un número válido."); - } - } - "4" => { - mostrar_contactos(&contactos); - if contactos.is_empty() { - continue; - } - - let idx_str = preguntar("\nNúmero del contacto a eliminar: "); - if let Ok(mut idx) = idx_str.parse::() { - idx -= 1; - if idx < contactos.len() { - let eliminado = contactos.remove(idx); // Elimina y retorna el elemento - guardar_contactos(&contactos); - println!("✓ Contacto '{}' eliminado.", eliminado.nombre); - } else { - println!("Número inválido."); - } - } else { - println!("Por favor, ingresa un número válido."); - } - } - "5" => { - println!("¡Hasta luego!"); - break; - } - _ => println!("Opción no reconocida, intenta de nuevo."), - } - } -} diff --git a/archivo-historico/ejemplos/general/02-web-scraper-simple/README.md b/archivo-historico/ejemplos/general/02-web-scraper-simple/README.md deleted file mode 100644 index 96cb837..0000000 --- a/archivo-historico/ejemplos/general/02-web-scraper-simple/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Peticiones HTTP (Requests) - -**¿Qué muestra este ejemplo?** -Cómo hacer peticiones HTTP (GET, POST) a APIs o páginas web externas usando la librería externa `requests`, manejando posibles errores de red o códigos de estado fallidos. - -**¿Por qué es útil?** -Es la base para consumir cualquier API REST (como consultar el clima, leer mensajes, o enviar notificaciones) y para hacer scraping de sitios web. - -**Cómo correrlo** -- Dependencias necesarias: `pip install requests` -- Comando para ejecutarlo: `python ejemplo_requests.py` - -**¿Qué retos usan esta base?** -- [#14 Python General 02 — Web scraper simple](https://github.com/Kaia-Alenia/nerve-community/issues/14) - -**Nota para principiantes** -Siempre revisa el `response.status_code` o usa `response.raise_for_status()` antes de intentar leer los datos; asumir que una petición web siempre será exitosa causará que tu programa se cierre inesperadamente si no hay internet o el servidor falla. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.go b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.go deleted file mode 100644 index 1b5dd2a..0000000 --- a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.go +++ /dev/null @@ -1,95 +0,0 @@ -// Ejemplo: Extraer múltiples elementos de una página con net/http + goquery en Go -// -// Qué enseña este ejemplo: -// - Cómo hacer una petición HTTP GET con net/http -// - Cómo usar PuerkitoBio/goquery (inspirado en jQuery) para buscar elementos HTML -// - Cómo guardar datos en un archivo CSV -// -// Para tu reto (web scraper): -// Instala la dependencia ejecutando: go get github.com/PuerkitoBio/goquery -// -// Glosario de términos "raros": -// http.Get — Pide datos a una URL de internet. -// defer res.Body.Close() — Asegura que la conexión de red se cierre automáticamente al terminar. -// goquery.NewDocumentFromReader — Lee el HTML y lo prepara para ser navegado. -// doc.Find() — Busca elementos usando selectores CSS (ej: ".quote"). -// s.Find().Text() — Extrae el texto dentro del elemento encontrado. - -package main - -import ( - "encoding/csv" - "fmt" - "log" - "net/http" - "os" - "strings" - - "github.com/PuerkitoBio/goquery" -) - -const URL = "https://quotes.toscrape.com/" - -type Dato struct { - Texto string - Autor string -} - -func main() { - // 1. Hacer la petición GET - res, err := http.Get(URL) - if err != nil { - log.Fatalf("Error al conectar: %v", err) - } - defer res.Body.Close() - - if res.StatusCode != 200 { - log.Fatalf("Error HTTP: %d", res.StatusCode) - } - - // 2. Crear el documento goquery desde la respuesta - doc, err := goquery.NewDocumentFromReader(res.Body) - if err != nil { - log.Fatalf("Error al procesar HTML: %v", err) - } - - // 3. Buscar TODOS los elementos con class="quote" - elementos := doc.Find(".quote") - fmt.Printf("Se encontraron %d frases en la página.\n\n", elementos.Length()) - - var datosExtraidos []Dato - - // 4. Iterar sobre cada elemento encontrado - elementos.Each(func(i int, s *goquery.Selection) { - // s representa cada bloque individual ".quote" - texto := s.Find(".text").Text() - autor := s.Find(".author").Text() - - datosExtraidos = append(datosExtraidos, Dato{Texto: texto, Autor: autor}) - - fmt.Printf("\"%s\"\n", texto) - fmt.Printf(" — %s\n\n", autor) - }) - - // 5. Guardar los datos en un archivo CSV - file, err := os.Create("frases.csv") - if err != nil { - log.Fatalf("Error al crear archivo CSV: %v", err) - } - defer file.Close() - - escritor := csv.NewWriter(file) - defer escritor.Flush() // Asegura que los datos en buffer se escriban al disco - - // Escribir cabeceras - escritor.Write([]string{"texto", "autor"}) - - // Escribir filas - for _, dato := range datosExtraidos { - // Reemplazamos saltos de línea para mantener el CSV limpio - textoLimpio := strings.ReplaceAll(dato.Texto, "\n", " ") - escritor.Write([]string{textoLimpio, dato.Autor}) - } - - fmt.Println("✓ Datos guardados exitosamente en 'frases.csv'") -} diff --git a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.js b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.js deleted file mode 100644 index 39e542b..0000000 --- a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Ejemplo: Extraer múltiples elementos de una página web en Node.js - * - * Qué enseña este ejemplo: - * - Cómo hacer una petición HTTP GET con fetch nativo de Node.js - * - Cómo usar cheerio (el jQuery para Node) para analizar HTML - * - Cómo buscar elementos y extraer su texto - * - Cómo manejar errores de red con try/catch - * - * Para tu reto (web scraper): - * Aplica este patrón pero apuntando a books.toscrape.com - * Instala dependencias con: npm install cheerio - * - * Glosario de términos "raros": - * fetch — Función nativa para hacer peticiones web. - * cheerio — Librería que permite manipular HTML en Node.js de forma muy rápida usando selectores CSS. - * $ — Símbolo tradicional para instanciar cheerio (convención heredada de jQuery). - * .each() — Método de cheerio para iterar sobre todos los elementos encontrados. - * .text() — Extrae solo el contenido de texto (limpio de etiquetas HTML). - */ - -const fs = require('fs'); -const cheerio = require('cheerio'); // Requiere: npm install cheerio - -const URL = "https://quotes.toscrape.com/"; - -async function obtenerFrases() { - try { - // 1. Hacer la petición GET a la página - const respuesta = await fetch(URL); - - // 2. Verificar si la respuesta fue exitosa (código 200) - if (!respuesta.ok) { - throw new Error(`HTTP Error: ${respuesta.status}`); - } - - // 3. Obtener el HTML como texto plano - const html = await respuesta.text(); - - // 4. Crear el objeto cheerio para navegar el HTML - const $ = cheerio.load(html); - - // 5. Buscar TODOS los elementos con class="quote" - const elementos = $(".quote"); - console.log(`Se encontraron ${elementos.length} frases en la página.\n`); - - const datosExtraidos = []; - - // .each() itera sobre cada elemento encontrado - elementos.each((index, elemento) => { - // Buscamos dentro de cada bloque individual - const texto = $(elemento).find(".text").text(); - const autor = $(elemento).find(".author").text(); - - datosExtraidos.push({ texto, autor }); - console.log(`"${texto}"`); - console.log(` — ${autor}\n`); - }); - - // 6. Guardar los datos en un archivo CSV simple - let csvContent = "texto,autor\n"; // Cabeceras - for (const dato of datosExtraidos) { - // Limpiamos comas para no romper el CSV simple - const textoLimpio = dato.texto.replace(/,/g, ''); - const autorLimpio = dato.autor.replace(/,/g, ''); - csvContent += `${textoLimpio},${autorLimpio}\n`; - } - - fs.writeFileSync("frases.csv", csvContent, "utf8"); - console.log("✓ Datos guardados exitosamente en 'frases.csv'"); - - } catch (error) { - console.error("Error al obtener la página:", error.message); - } -} - -obtenerFrases(); diff --git a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.py b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.py deleted file mode 100644 index dc346de..0000000 --- a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Ejemplo: Extraer múltiples elementos de una página con requests + BeautifulSoup - -Qué enseña este ejemplo: - - Cómo hacer una petición HTTP GET con requests.get() - - Qué significa raise_for_status() y para qué sirve - - Cómo crear un objeto BeautifulSoup (el "parser" del HTML) - - Cómo buscar TODOS los elementos de un tipo con find_all() - - Cómo manejar errores de red con try/except - -Para tu reto (web scraper): - Aplica el mismo patrón para extraer datos de books.toscrape.com - (título + precio de varios libros), que es una página pública - diseñada específicamente para practicar scraping. - -Glosario de términos "raros": - requests — Librería para pedir datos a internet (como lo hace tu navegador). - BeautifulSoup— Librería que toma código HTML feo y te deja buscar dentro de él fácilmente. - html.parser — El motor que lee el HTML. Sin él, BeautifulSoup no sabe cómo leer el texto. - raise_for_status() — Función que hace que el programa explote si la página no existe (Error 404). - find_all() — Busca TODOS los elementos que coincidan y devuelve una lista. - find() — Busca solo el PRIMER elemento que coincida. - .text — Extrae solo el texto limpio que ven los humanos (sin código HTML). - .get("attr") — Extrae un atributo invisible, como el enlace (href) de un botón. -""" - -import requests -from bs4 import BeautifulSoup - -# Esta URL es una página de ejemplo hecha para practicar scraping -URL = "https://quotes.toscrape.com/" - - -def obtener_frases(): - try: - # 1. Hacer la petición GET - respuesta = requests.get(URL, timeout=5) - - # 2. raise_for_status() lanza una excepción si el servidor devolvió - # un error (ej: 404 Not Found, 500 Server Error) - # Sin esto, requests no falla aunque la página no exista - respuesta.raise_for_status() - - # 3. Crear el objeto BeautifulSoup para navegar el HTML - # respuesta.text es el HTML de la página como texto plano - soup = BeautifulSoup(respuesta.text, "html.parser") - - # 4. Buscar TODOS los elementos con class="quote" - # find_all devuelve una lista, aunque no encuentre nada (lista vacía) - elementos = soup.find_all("div", class_="quote") - - print(f"Se encontraron {len(elementos)} frases en la página.\n") - - datos_extraidos = [] - for elemento in elementos: - # .find() dentro de un elemento busca solo en ese bloque - texto = elemento.find("span", class_="text").text - autor = elemento.find("small", class_="author").text - datos_extraidos.append({"texto": texto, "autor": autor}) - print(f'"{texto}"') - print(f" — {autor}\n") - - # 5. Guardar los datos en un archivo CSV (pieza final del rompecabezas) - import csv - - with open("frases.csv", "w", encoding="utf-8", newline="") as f: - # fieldnames define el nombre de las columnas (llaves del diccionario) - escritor = csv.DictWriter(f, fieldnames=["texto", "autor"]) - escritor.writeheader() - escritor.writerows(datos_extraidos) - - print("✓ Datos guardados exitosamente en 'frases.csv'") - - except requests.exceptions.ConnectionError: - print("Error: No se pudo conectar. Revisa tu conexión a internet.") - except requests.exceptions.Timeout: - print("Error: La petición tardó demasiado (timeout).") - except requests.exceptions.HTTPError as e: - print(f"Error HTTP: {e}") - - -if __name__ == "__main__": - obtener_frases() diff --git a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs b/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs deleted file mode 100644 index 4810645..0000000 --- a/archivo-historico/ejemplos/general/02-web-scraper-simple/ejemplo_requests.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Ejemplo: Extraer múltiples elementos de una página web en Rust con reqwest + scraper -// -// Qué enseña este ejemplo: -// - Cómo hacer una petición HTTP GET asíncrona con reqwest -// - Cómo extraer datos del HTML usando la librería scraper -// - Cómo escribir datos estructurados en un archivo CSV -// -// Para tu reto (web scraper): -// Dependencias necesarias en Cargo.toml: -// reqwest = { version = "0.11", features = ["blocking"] } -// scraper = "0.18" -// csv = "1.3" -// -// Glosario de términos "raros": -// reqwest::blocking::get — Hace la petición web en modo "bloqueante" (espera a que termine). -// scraper::Html::parse_document — Convierte el texto HTML a una estructura navegable. -// scraper::Selector::parse — Prepara una regla CSS (ej: ".quote") para buscar elementos. -// unwrap() — Atajo en Rust para "si hay error, explota el programa". Útil en scripts rápidos. -// csv::Writer — Crea un escritor que se encarga de formatear correctamente las comas y comillas en el CSV. - -use scraper::{Html, Selector}; -use csv::Writer; -use std::error::Error; - -const URL: &str = "https://quotes.toscrape.com/"; - -fn main() -> Result<(), Box> { - // 1. Hacer la petición GET (bloqueante) - let respuesta = reqwest::blocking::get(URL)?; - - // 2. Verificar que la respuesta sea OK y extraer el HTML como texto - let html = respuesta.error_for_status()?.text()?; - - // 3. Crear el objeto Html navegable - let documento = Html::parse_document(&html); - - // 4. Preparar los selectores CSS - // Selector::parse compila la regla. El unwrap() asume que escribiste bien la regla CSS. - let selector_quote = Selector::parse(".quote").unwrap(); - let selector_text = Selector::parse(".text").unwrap(); - let selector_author = Selector::parse(".author").unwrap(); - - let elementos = documento.select(&selector_quote).collect::>(); - println!("Se encontraron {} frases en la página.\n", elementos.len()); - - // 5. Preparar el escritor CSV - let mut escritor = Writer::from_path("frases.csv")?; - escritor.write_record(&["texto", "autor"])?; // Cabeceras - - // 6. Iterar sobre cada frase y extraer datos - for elemento in elementos { - // Encontramos el primer .text dentro del bloque - let texto = elemento.select(&selector_text) - .next() - .map(|t| t.text().collect::()) - .unwrap_or_default(); - - // Encontramos el primer .author dentro del bloque - let autor = elemento.select(&selector_author) - .next() - .map(|a| a.text().collect::()) - .unwrap_or_default(); - - println!("\"{}\"", texto); - println!(" — {}\n", autor); - - // Guardamos la fila en el CSV (la librería csv maneja las comas internas automáticamente) - escritor.write_record(&[&texto, &autor])?; - } - - escritor.flush()?; // Asegurar que todo se escriba al disco - println!("✓ Datos guardados exitosamente en 'frases.csv'"); - - Ok(()) -} diff --git a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/README.md b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/README.md deleted file mode 100644 index 196fabe..0000000 --- a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/README.md +++ /dev/null @@ -1,19 +0,0 @@ -## Validación de Inputs - -**¿Qué muestra este ejemplo?** -Cómo pedir datos al usuario usando `input()` y validarlos correctamente con bucles `while` y bloques `try/except` hasta que ingrese un tipo de dato válido. - -**¿Por qué es útil?** -Previene que un programa interactivo de consola colapse cuando un usuario ingresa texto en lugar de números o presiona "Enter" en lugar de elegir una opción válida de un menú. - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_validacion.py` - -**¿Qué retos usan esta base?** -- [#15 Python General 03 — Juego de adivina el número](https://github.com/Kaia-Alenia/nerve-community/issues/15) -- [#18 Python General 06 — Conversor de unidades](https://github.com/Kaia-Alenia/nerve-community/issues/18) -- [#19 Python General 07 — Calculadora de IMC](https://github.com/Kaia-Alenia/nerve-community/issues/19) - -**Nota para principiantes** -El valor devuelto por `input()` siempre es una cadena de texto (`str`). No olvides convertirlo explícitamente a `int` o `float` antes de hacer operaciones matemáticas. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go deleted file mode 100644 index d00a373..0000000 --- a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Ejemplo: Mini juego "adivina el color" con math/rand, bucle y conteo de intentos - -Qué enseña este ejemplo: - - Cómo usar rand.Intn() para seleccionar un elemento al azar de un slice - - Cómo estructurar el bucle de intentos con for + contador - - Cómo dar pistas simples (mayor/menor en números, aquí: letra inicial) - - Cómo manejar input no válido con validación condicional - -Para tu reto (adivina el número): - Aplica el mismo patrón de bucle + contador + pistas, - pero usa rand.Intn(100) + 1 para generar un número - y compara si el intento es mayor o menor al número secreto. - -Glosario: - rand.Intn(n) — genera un entero al azar entre 0 y n-1 - strings.ToLower(s) — convierte un string a minúsculas - strings.TrimSpace(s) — elimina espacios al inicio y final del string - bufio.NewReader(os.Stdin) — crea un lector para leer entrada desde la terminal -*/ - -package main - -import ( - "bufio" - "fmt" - "math/rand" - "os" - "strings" -) - -func contiene(slice []string, item string) bool { - for _, s := range slice { - if s == item { - return true - } - } - return false -} - -func main() { - colores := []string{"rojo", "azul", "verde", "amarillo", "morado", "naranja"} - maxIntentos := 4 - - // elegir color al azar - colorSecreto := colores[rand.Intn(len(colores))] - intentos := 0 - - fmt.Println("=== Adivina el color ===") - fmt.Printf("Tengo un color en mente. Tienes %d intentos.\n", maxIntentos) - fmt.Printf("Opciones posibles: %s\n\n", strings.Join(colores, ", ")) - - reader := bufio.NewReader(os.Stdin) - - for intentos < maxIntentos { - fmt.Printf("Intento %d/%d: ", intentos+1, maxIntentos) - respuesta, _ := reader.ReadString('\n') - intento := strings.ToLower(strings.TrimSpace(respuesta)) - - // Validar - if !contiene(colores, intento) { - fmt.Printf("'%s' no es un color válido. Elige de la lista.\n\n", intento) - continue - } - - intentos++ - - if intento == colorSecreto { - fmt.Printf("\n¡Correcto! Era '%s'. Lo lograste en %d intento(s).\n", colorSecreto, intentos) - return - } - - // Pista (comparando primer carácter) - if intento[0] < colorSecreto[0] { - fmt.Printf("No es '%s'. El color secreto va después alfabéticamente.\n", intento) - } else { - fmt.Printf("No es '%s'. El color secreto va antes alfabéticamente.\n", intento) - } - fmt.Println() - } - - fmt.Printf("\nSe acabaron los intentos. El color era '%s'.\n", colorSecreto) -} diff --git a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js deleted file mode 100644 index b85a431..0000000 --- a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Ejemplo: Mini juego "adivina el color" con Math.random, bucle y conteo de intentos - * - * Qué enseña este ejemplo: - * - Cómo usar Math.random() para seleccionar un elemento al azar de un array - * - Cómo estructurar el bucle de intentos con while + contador - * - Cómo dar pistas simples (mayor/menor en números, aquí: letra inicial) - * - Cómo manejar input no válido con validación condicional - * - * Para tu reto (adivina el número): - * Aplica el mismo patrón de bucle + contador + pistas, - * pero usa Math.floor(Math.random() * 100) + 1 para generar un número - * y compara si el intento es mayor o menor al número secreto. - * - * Glosario: - * Math.random() — Genera un número decimal entre 0 (incluido) y 1 (excluido) - * Math.floor() — Redondea hacia abajo al entero más cercano - * .toLowerCase() — Convierte un string a minúsculas (útil para comparar) - * .trim() — Elimina espacios al inicio y final del string - * readline — Módulo de Node.js para leer entrada desde la terminal - */ - -const readline = require('readline'); - -const COLORES = ["rojo", "azul", "verde", "amarillo", "morado", "naranja"]; -const MAX_INTENTOS = 4; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}); - -// prompt que devuelve una promesa para facilitar el uso asíncrono -const input = (pregunta) => new Promise(resolve => rl.question(pregunta, resolve)); - -async function jugar() { - // Math.random() junto con Math.floor() para elegir un índice al azar - const indiceAleatorio = Math.floor(Math.random() * COLORES.length); - const colorSecreto = COLORES[indiceAleatorio]; - let intentos = 0; - - console.log("=== Adivina el color ==="); - console.log(`Tengo un color en mente. Tienes ${MAX_INTENTOS} intentos.`); - console.log(`Opciones posibles: ${COLORES.join(', ')}\n`); - - while (intentos < MAX_INTENTOS) { - const respuesta = await input(`Intento ${intentos + 1}/${MAX_INTENTOS}: `); - const intento = respuesta.trim().toLowerCase(); - - // Validar que el input sea uno de los colores válidos - if (!COLORES.includes(intento)) { - console.log(`'${intento}' no es un color válido. Elige de la lista.\n`); - continue; // No contamos este como un intento fallido - } - - intentos++; - - if (intento === colorSecreto) { - console.log(`\n¡Correcto! Era '${colorSecreto}'. Lo lograste en ${intentos} intento(s).`); - rl.close(); - return; - } - - // Pista: comparar letra inicial (equivalente a mayor/menor en números) - if (intento[0] < colorSecreto[0]) { - console.log(`No es '${intento}'. El color secreto va después alfabéticamente.`); - } else { - console.log(`No es '${intento}'. El color secreto va antes alfabéticamente.`); - } - console.log(); - } - - console.log(`\nSe acabaron los intentos. El color era '${colorSecreto}'.`); - rl.close(); -} - -if (require.main === module) { - jugar(); -} diff --git a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py deleted file mode 100644 index 84e71ce..0000000 --- a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Ejemplo: Mini juego "adivina el color" con random.choice, loop y conteo de intentos - -Qué enseña este ejemplo: - - Cómo usar random.choice() para seleccionar un elemento al azar de una lista - - Cómo estructurar el loop de intentos con while + contador - - Cómo dar pistas simples (mayor/menor en números, aquí: letra inicial) - - Cómo manejar input no válido con validación condicional - -Para tu reto (adivina el número): - Aplica el mismo patrón de loop + contador + pistas, - pero usa random.randint(1, 100) para generar un número - y compara si el intento es mayor o menor al número secreto. - -Glosario: - random.choice(lista) — elige un elemento al azar de cualquier lista o string - random.randint(a, b) — genera un entero al azar entre a y b (ambos incluidos) - .lower() — convierte un string a minúsculas (útil para comparar) - .strip() — elimina espacios al inicio y final del string -""" - -import random - -COLORES = ["rojo", "azul", "verde", "amarillo", "morado", "naranja"] -MAX_INTENTOS = 4 - - -def jugar(): - # random.choice elige un color al azar de la lista - color_secreto = random.choice(COLORES) - intentos = 0 - - print("=== Adivina el color ===") - print(f"Tengo un color en mente. Tienes {MAX_INTENTOS} intentos.") - print(f"Opciones posibles: {', '.join(COLORES)}\n") - - while intentos < MAX_INTENTOS: - intento = input(f"Intento {intentos + 1}/{MAX_INTENTOS}: ").strip().lower() - - # Validar que el input sea uno de los colores válidos - if intento not in COLORES: - print(f"'{intento}' no es un color válido. Elige de la lista.\n") - continue # No contamos este como un intento fallido - - intentos += 1 - - if intento == color_secreto: - print( - f"\n¡Correcto! Era '{color_secreto}'. Lo lograste en {intentos} intento(s)." - ) - return - - # Pista: comparar letra inicial (equivalente a mayor/menor en números) - if intento[0] < color_secreto[0]: - print(f"No es '{intento}'. El color secreto va después alfabéticamente.") - else: - print(f"No es '{intento}'. El color secreto va antes alfabéticamente.") - print() - - print(f"\nSe acabaron los intentos. El color era '{color_secreto}'.") - - -if __name__ == "__main__": - jugar() diff --git a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs b/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs deleted file mode 100644 index 6c0dee7..0000000 --- a/archivo-historico/ejemplos/general/03-juego-de-adivinanza/ejemplo_validacion.rs +++ /dev/null @@ -1,78 +0,0 @@ -/* -Ejemplo: Mini juego "adivina el color" con rand, bucle y conteo de intentos - -Qué enseña este ejemplo: - - Cómo usar el crate rand (y slice::choose) para elegir un elemento al azar - - Cómo estructurar el bucle de intentos con loop + contador - - Cómo dar pistas simples (mayor/menor en números, aquí: letra inicial) - - Cómo manejar input no válido con validación condicional - -Para tu reto (adivina el número): - Aplica el mismo patrón de bucle + contador + pistas, - pero usa rand::thread_rng().gen_range(1..=100) para generar un número - y compara si el intento es mayor o menor al número secreto. - -Glosario: - rand::thread_rng() — obtiene el generador de números aleatorios local - slice.choose(...) — elige un elemento al azar de un arreglo o vector - .trim() — elimina espacios al inicio y final del string - .to_lowercase() — convierte un string a minúsculas - std::io::stdin() — la manera estándar de leer entrada en la consola -*/ - -use rand::seq::SliceRandom; -use std::io::{self, Write}; - -fn main() { - let colores = ["rojo", "azul", "verde", "amarillo", "morado", "naranja"]; - let max_intentos = 4; - - // Obtener RNG (Random Number Generator) - let mut rng = rand::thread_rng(); - - // choose devuelve un Option, hacemos unwrap porque sabemos que el arreglo no está vacío - let color_secreto = colores.choose(&mut rng).unwrap(); - let mut intentos = 0; - - println!("=== Adivina el color ==="); - println!("Tengo un color en mente. Tienes {} intentos.", max_intentos); - println!("Opciones posibles: {}\n", colores.join(", ")); - - while intentos < max_intentos { - print!("Intento {}/{}: ", intentos + 1, max_intentos); - io::stdout().flush().unwrap(); // Para que el print anterior se muestre antes del input - - let mut input = String::new(); - io::stdin() - .read_line(&mut input) - .expect("Error al leer la entrada"); - - let intento = input.trim().to_lowercase(); - - // Validar - if !colores.contains(&intento.as_str()) { - println!("'{}' no es un color válido. Elige de la lista.\n", intento); - continue; - } - - intentos += 1; - - if intento == *color_secreto { - println!("\n¡Correcto! Era '{}'. Lo lograste en {} intento(s).", color_secreto, intentos); - return; - } - - // Pista: obtenemos el primer char - let char_intento = intento.chars().next().unwrap(); - let char_secreto = color_secreto.chars().next().unwrap(); - - if char_intento < char_secreto { - println!("No es '{}'. El color secreto va después alfabéticamente.", intento); - } else { - println!("No es '{}'. El color secreto va antes alfabéticamente.", intento); - } - println!(); - } - - println!("\nSe acabaron los intentos. El color era '{}'.", color_secreto); -} diff --git a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/README.md b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/README.md deleted file mode 100644 index f8f9515..0000000 --- a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Lectura y Procesamiento de CSV - -**¿Qué muestra este ejemplo?** -Cómo leer y escribir archivos CSV (Comma-Separated Values) usando el módulo `csv` de la librería estándar, especialmente usando `csv.DictReader` para mayor claridad. - -**¿Por qué es útil?** -Es esencial para procesar exportaciones de bases de datos, hojas de cálculo, registros financieros y datos tabulares estructurados sin depender de librerías pesadas como Pandas. - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_csv.py` - -**¿Qué retos usan esta base?** -- [#16 Python General 04 — Analizador de gastos personales (CSV)](https://github.com/Kaia-Alenia/nerve-community/issues/16) - -**Nota para principiantes** -Presta mucha atención al parámetro `encoding="utf-8"` al abrir el archivo, especialmente en Windows, para evitar errores al leer acentos u otros caracteres especiales del idioma español. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go deleted file mode 100644 index 3dea2d2..0000000 --- a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Ejemplo: Leer un CSV real desde disco, filtrar y calcular sumas - -Qué enseña este ejemplo: - - Cómo abrir y leer un archivo CSV con os.Open y encoding/csv - - Cómo iterar por cada fila del archivo CSV - - Cómo convertir strings a números con strconv.ParseFloat - - Cómo agrupar filas y calcular sumas / promedios usando mapas - -Para tu reto (analizador de gastos): - Aplica el mismo patrón para leer tu CSV de gastos - (fecha, categoría, monto) y calcular totales por categoría. - -Glosario: - os.Open(archivo) — Abre un archivo en modo lectura. - defer f.Close() — Asegura que el archivo se cerrará cuando termine la función. - csv.NewReader(f) — Crea un lector especializado que entiende el formato CSV (comas, comillas). - strconv.ParseFloat(s) — Convierte un texto como "1234.50" a un número flotante (decimal). -*/ - -package main - -import ( - "encoding/csv" - "fmt" - "os" - "strconv" -) - -type Empleado struct { - Nombre string - Departamento string - Salario float64 -} - -func leerEmpleados(rutaCsv string) []Empleado { - file, err := os.Open(rutaCsv) - if err != nil { - fmt.Printf("No se pudo abrir el archivo: %v\n", err) - return []Empleado{} - } - defer file.Close() - - reader := csv.NewReader(file) - // Leer todas las filas - filas, err := reader.ReadAll() - if err != nil { - fmt.Printf("Error leyendo el CSV: %v\n", err) - return []Empleado{} - } - - var empleados []Empleado - - // Iterar omitiendo la cabecera (índice 0) - for i := 1; i < len(filas); i++ { - fila := filas[i] - if len(fila) < 3 { - continue - } - - nombre := fila[0] - departamento := fila[1] - salario, _ := strconv.ParseFloat(fila[2], 64) - - empleados = append(empleados, Empleado{ - Nombre: nombre, - Departamento: departamento, - Salario: salario, - }) - } - - return empleados -} - -type Estadisticas struct { - Suma float64 - Cantidad int -} - -func reportePorDepartamento(empleados []Empleado) { - totales := make(map[string]*Estadisticas) - - for _, emp := range empleados { - dept := emp.Departamento - if totales[dept] == nil { - totales[dept] = &Estadisticas{} - } - totales[dept].Suma += emp.Salario - totales[dept].Cantidad++ - } - - fmt.Println("\n--- Reporte por departamento ---") - for dept, stats := range totales { - promedio := stats.Suma / float64(stats.Cantidad) - // %.0f imprime sin decimales - fmt.Printf(" %s: total=$%.0f | promedio=$%.0f | empleados=%d\n", dept, stats.Suma, promedio, stats.Cantidad) - } -} - -func main() { - archivo := "empleados.csv" - - // Crear archivo de ejemplo si no existe - if _, err := os.Stat(archivo); os.IsNotExist(err) { - datos := []string{ - "nombre,departamento,salario", - "Ana García,Ingeniería,35000", - "Luis Martínez,Marketing,28000", - "Carmen López,Ingeniería,42000", - "Pedro Sánchez,Marketing,31000", - "Sofía Ramírez,Ingeniería,39000", - } - - f, _ := os.Create(archivo) - for _, linea := range datos { - f.WriteString(linea + "\n") - } - f.Close() - fmt.Printf("Archivo '%s' creado de ejemplo.\n", archivo) - } - - empleados := leerEmpleados(archivo) - fmt.Printf("\nSe leyeron %d empleados.\n", len(empleados)) - reportePorDepartamento(empleados) -} diff --git a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js deleted file mode 100644 index 98eb106..0000000 --- a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Ejemplo: Leer un CSV real desde disco, filtrar y calcular sumas - * - * Qué enseña este ejemplo: - * - Cómo abrir y leer un archivo CSV con el módulo fs de Node.js - * - Cómo procesar líneas de texto y dividirlas en columnas - * - Cómo convertir strings a números para operar con ellos - * - Cómo agrupar filas y calcular sumas / promedios - * - * Para tu reto (analizador de gastos): - * Aplica el mismo patrón para leer tu CSV de gastos - * (fecha, categoría, monto) y calcular totales por categoría. - * - * Glosario: - * fs.readFileSync(ruta) — Lee todo el contenido de un archivo a la memoria. - * "utf-8" — Asegura que los caracteres especiales se decodifiquen como texto y no como un buffer binario. - * .split('\n') — Divide un texto largo en una lista de líneas. - * .split(',') — Divide una línea en columnas separadas por comas. - * parseFloat(valor) — Convierte un texto como "1234.50" a un número decimal. - */ - -const fs = require('fs'); - -function leerEmpleados(rutaCsv) { - if (!fs.existsSync(rutaCsv)) { - console.log(`Archivo no encontrado: ${rutaCsv}`); - return []; - } - - const contenido = fs.readFileSync(rutaCsv, 'utf-8'); - const lineas = contenido.trim().split('\n'); - - // La primera línea son las cabeceras - const empleados = []; - - for (let i = 1; i < lineas.length; i++) { - const linea = lineas[i].trim(); - if (!linea) continue; - - const [nombre, departamento, salarioStr] = linea.split(','); - - empleados.push({ - nombre: nombre, - departamento: departamento, - salario: parseFloat(salarioStr) - }); - } - - return empleados; -} - -function reportePorDepartamento(empleados) { - const totales = {}; - - for (const emp of empleados) { - const dept = emp.departamento; - if (!totales[dept]) { - totales[dept] = { suma: 0, cantidad: 0 }; - } - totales[dept].suma += emp.salario; - totales[dept].cantidad += 1; - } - - console.log("\n--- Reporte por departamento ---"); - for (const [dept, datos] of Object.entries(totales)) { - const promedio = datos.suma / datos.cantidad; - console.log(` ${dept}: total=$${datos.suma.toLocaleString()} | promedio=$${promedio.toLocaleString()} | empleados=${datos.cantidad}`); - } -} - -// Crear archivo de ejemplo si no existe -const archivo = "empleados.csv"; -if (!fs.existsSync(archivo)) { - const datos = [ - "nombre,departamento,salario", - "Ana García,Ingeniería,35000", - "Luis Martínez,Marketing,28000", - "Carmen López,Ingeniería,42000", - "Pedro Sánchez,Marketing,31000", - "Sofía Ramírez,Ingeniería,39000" - ]; - fs.writeFileSync(archivo, datos.join("\n"), "utf-8"); - console.log(`Archivo '${archivo}' creado de ejemplo.`); -} - -const empleados = leerEmpleados(archivo); -console.log(`\nSe leyeron ${empleados.length} empleados.`); -reportePorDepartamento(empleados); diff --git a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py deleted file mode 100644 index 24526a4..0000000 --- a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -Ejemplo: Leer un CSV real desde disco con csv.DictReader, filtrar y calcular sumas - -Qué enseña este ejemplo: - - Cómo abrir y leer un archivo CSV real con open() + csv.DictReader - - Por qué DictReader (con D mayúscula) es mejor que reader simple - - Cómo convertir strings a números para operar con ellos - - Cómo filtrar filas y calcular sumas / promedios - -Para tu reto (analizador de gastos): - Aplica el mismo patrón para leer tu CSV de gastos - (fecha, categoría, monto) y calcular totales por categoría. - -Glosario de términos "raros": - with open(...) as f — Abre un archivo y lo cierra automáticamente al terminar. - 'f' es una variable corta para 'file'. - "r" o "w" — Modos: "r" (read = leer), "w" (write = escribir). - encoding="utf-8" — Asegura que los acentos y caracteres especiales (ñ, á) se guarden bien. - newline="" — Evita que se escriban líneas en blanco extra entre filas en Windows. - csv.DictReader — Lee el CSV convirtiendo cada fila en un diccionario (usa D mayúscula). - csv.reader — Más básico: devuelve cada fila como lista de strings. - float(valor) — Convierte un texto como "1234.50" a un número decimal. - -Archivo de ejemplo (empleados.csv): - nombre,departamento,salario - Ana García,Ingeniería,35000 - Luis Martínez,Marketing,28000 - Carmen López,Ingeniería,42000 - Pedro Sánchez,Marketing,31000 - Sofía Ramírez,Ingeniería,39000 -""" - -import csv -from pathlib import Path - - -def leer_empleados(ruta_csv: str) -> list: - """ - Lee el CSV y devuelve una lista de diccionarios. - Cada dict representa un empleado: {"nombre": ..., "departamento": ..., "salario": ...} - """ - empleados = [] - ruta = Path(ruta_csv) - - if not ruta.exists(): - print(f"Archivo no encontrado: {ruta_csv}") - return [] - - # open() abre el archivo; newline="" es recomendado por la documentación de csv - with open(ruta, "r", encoding="utf-8", newline="") as f: - # DictReader usa la primera fila como nombres de columna automáticamente - lector = csv.DictReader(f) - for fila in lector: - # Las filas de CSV son siempre strings — convertimos salario a float - empleados.append( - { - "nombre": fila["nombre"], - "departamento": fila["departamento"], - "salario": float(fila["salario"]), - } - ) - - return empleados - - -def reporte_por_departamento(empleados: list): - """ - Calcula el total y promedio de salario por departamento. - """ - totales = {} - - for emp in empleados: - dept = emp["departamento"] - if dept not in totales: - totales[dept] = {"suma": 0.0, "cantidad": 0} - totales[dept]["suma"] += emp["salario"] - totales[dept]["cantidad"] += 1 - - print("\n--- Reporte por departamento ---") - for dept, datos in totales.items(): - promedio = datos["suma"] / datos["cantidad"] - print( - f" {dept}: total=${datos['suma']:,.0f} | promedio=${promedio:,.0f} | empleados={datos['cantidad']}" - ) - - -if __name__ == "__main__": - # Crear archivo de ejemplo si no existe - archivo = "empleados.csv" - if not Path(archivo).exists(): - with open(archivo, "w", encoding="utf-8", newline="") as f: - writer = csv.writer(f) - writer.writerow(["nombre", "departamento", "salario"]) - writer.writerows( - [ - ["Ana García", "Ingeniería", 35000], - ["Luis Martínez", "Marketing", 28000], - ["Carmen López", "Ingeniería", 42000], - ["Pedro Sánchez", "Marketing", 31000], - ["Sofía Ramírez", "Ingeniería", 39000], - ] - ) - print(f"Archivo '{archivo}' creado de ejemplo.") - - empleados = leer_empleados(archivo) - print(f"\nSe leyeron {len(empleados)} empleados.") - reporte_por_departamento(empleados) diff --git a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs b/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs deleted file mode 100644 index 90a3b5b..0000000 --- a/archivo-historico/ejemplos/general/04-analizador-de-gastos-csv/ejemplo_csv.rs +++ /dev/null @@ -1,114 +0,0 @@ -/* -Ejemplo: Leer un CSV real desde disco, filtrar y calcular sumas - -Qué enseña este ejemplo: - - Cómo usar la biblioteca estándar para leer archivos estructurados. - - Cómo convertir strings a números con .parse() - - Cómo agrupar usando un HashMap - -Para tu reto (analizador de gastos): - Aplica el mismo patrón para leer tu CSV de gastos - (fecha, categoría, monto) y calcular totales por categoría. - -Glosario: - File::open(ruta) — Abre un archivo en modo lectura. - .parse::() — Intenta convertir un string a un número decimal (float de 64 bits). - HashMap::new() — Crea una estructura de diccionario para guardar totales por clave. - .entry(clave) — Busca una clave en el map para modificarla o insertarla si no existe. -*/ - -use std::collections::HashMap; -use std::fs::File; -use std::io::Write; -use std::path::Path; - -struct Empleado { - _nombre: String, - departamento: String, - salario: f64, -} - -struct Estadisticas { - suma: f64, - cantidad: i32, -} - -fn leer_empleados(ruta_csv: &str) -> Vec { - let mut empleados = Vec::new(); - - let file = match File::open(ruta_csv) { - Ok(f) => f, - Err(_) => { - println!("Archivo no encontrado: {}", ruta_csv); - return empleados; - } - }; - - use std::io::{BufRead, BufReader}; - let reader = BufReader::new(file); - - for (index, line) in reader.lines().enumerate() { - if index == 0 { - continue; // Saltar cabecera - } - if let Ok(l) = line { - let cols: Vec<&str> = l.split(',').collect(); - if cols.len() >= 3 { - let nombre = cols[0].trim().to_string(); - let departamento = cols[1].trim().to_string(); - let salario = cols[2].trim().parse::().unwrap_or(0.0); - - empleados.push(Empleado { - _nombre: nombre, - departamento, - salario, - }); - } - } - } - - empleados -} - -fn reporte_por_departamento(empleados: &[Empleado]) { - let mut totales: HashMap = HashMap::new(); - - for emp in empleados { - let entry = totales - .entry(emp.departamento.clone()) - .or_insert(Estadisticas { suma: 0.0, cantidad: 0 }); - entry.suma += emp.salario; - entry.cantidad += 1; - } - - println!("\n--- Reporte por departamento ---"); - for (dept, stats) in totales.iter() { - let promedio = stats.suma / stats.cantidad as f64; - println!( - " {}: total=${} | promedio=${} | empleados={}", - dept, stats.suma, promedio, stats.cantidad - ); - } -} - -fn main() { - let archivo = "empleados.csv"; - - if !Path::new(archivo).exists() { - let mut file = File::create(archivo).expect("No se pudo crear el archivo"); - let datos = "\ -nombre,departamento,salario -Ana García,Ingeniería,35000 -Luis Martínez,Marketing,28000 -Carmen López,Ingeniería,42000 -Pedro Sánchez,Marketing,31000 -Sofía Ramírez,Ingeniería,39000 -"; - file.write_all(datos.as_bytes()).unwrap(); - println!("Archivo '{}' creado de ejemplo.", archivo); - } - - let empleados = leer_empleados(archivo); - println!("\nSe leyeron {} empleados.", empleados.len()); - reporte_por_departamento(&empleados); -} diff --git a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/README.md b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/README.md deleted file mode 100644 index 0ba78d4..0000000 --- a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/README.md +++ /dev/null @@ -1,18 +0,0 @@ -## Aleatoriedad Segura (Secrets vs Random) - -**¿Qué muestra este ejemplo?** -La diferencia entre generar números o valores pseudoaleatorios usando el módulo `random` frente a generar datos criptográficamente seguros usando el módulo `secrets`. - -**¿Por qué es útil?** -Es crítico para crear tokens de sesión, contraseñas, o enlaces reseteo, garantizando que atacantes no puedan predecir los valores generados. - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_secrets.py` - -**¿Qué retos usan esta base?** -- [#15 Python General 03 — Juego de adivina el número](https://github.com/Kaia-Alenia/nerve-community/issues/15) -- [#17 Python General 05 — Generador de contraseñas seguras](https://github.com/Kaia-Alenia/nerve-community/issues/17) - -**Nota para principiantes** -Usa `random` para juegos o simulaciones, pero nunca para contraseñas o tokens de seguridad — para eso siempre usa `secrets`. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go deleted file mode 100644 index ec2a5e3..0000000 --- a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -Ejemplo: Construir un PIN bancario seguro usando criptografía - -Qué enseña este ejemplo: - - Cuándo usar crypto/rand en lugar de math/rand (y por qué importa) - - Cómo definir constantes de caracteres (alfabeto, números, símbolos) - - Cómo garantizar que el resultado incluya al menos un carácter de cada tipo - -Para tu reto (generador de contraseñas): - Aplica el mismo patrón: construye el pool de caracteres según los - parámetros del usuario (longitud, símbolos sí/no, mayúsculas sí/no), - y usa crypto/rand para construir la contraseña. - -Glosario: - crypto/rand — paquete que ofrece aleatoriedad criptográficamente segura. - math/rand — NO usar para contraseñas, sus resultados son predecibles. - rand.Int(rand.Reader, max) — Devuelve un entero seguro grande, hay que convertirlo. -*/ - -package main - -import ( - "crypto/rand" - "fmt" - "math/big" -) - -const ( - Digitos = "0123456789" - Mayusculas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - Simbolos = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" -) - -// Funciones de ayuda -func choice(pool string) byte { - max := big.NewInt(int64(len(pool))) - n, _ := rand.Int(rand.Reader, max) - return pool[n.Int64()] -} - -func shuffle(caracteres []byte) { - for i := len(caracteres) - 1; i > 0; i-- { - max := big.NewInt(int64(i + 1)) - n, _ := rand.Int(rand.Reader, max) - j := n.Int64() - caracteres[i], caracteres[j] = caracteres[j], caracteres[i] - } -} - -func generarPin(longitud int, soloNumeros bool) string { - var pool string - if soloNumeros { - pool = Digitos - } else { - pool = Mayusculas + Digitos - } - - pin := make([]byte, longitud) - for i := 0; i < longitud; i++ { - pin[i] = choice(pool) - } - return string(pin) -} - -func generarPinGarantizado(longitud int) string { - if longitud < 3 { - panic("La longitud mínima para un PIN garantizado es 3.") - } - - poolCompleto := Mayusculas + Digitos + Simbolos - - caracteres := make([]byte, longitud) - - // 1. Garantizar al menos uno de cada tipo requerido - caracteres[0] = choice(Mayusculas) - caracteres[1] = choice(Digitos) - caracteres[2] = choice(Simbolos) - - // 2. Rellenar el resto - for i := 3; i < longitud; i++ { - caracteres[i] = choice(poolCompleto) - } - - // 3. Mezclar - shuffle(caracteres) - - return string(caracteres) -} - -func main() { - fmt.Println("=== Generador de PINs seguros ===\n") - - fmt.Println("PINs numéricos (6 dígitos):") - for i := 0; i < 3; i++ { - fmt.Printf(" %s\n", generarPin(6, true)) - } - - fmt.Println("\nPINs alfanuméricos (8 caracteres):") - for i := 0; i < 3; i++ { - fmt.Printf(" %s\n", generarPin(8, false)) - } - - fmt.Println("\nPINs con garantía (letra + dígito + símbolo, 10 chars):") - for i := 0; i < 3; i++ { - fmt.Printf(" %s\n", generarPinGarantizado(10)) - } -} diff --git a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js deleted file mode 100644 index c7b5fab..0000000 --- a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Ejemplo: Construir un PIN bancario seguro con criptografía - * - * Qué enseña este ejemplo: - * - Cuándo usar crypto.randomInt en lugar de Math.random (y por qué importa) - * - Cómo definir listas de caracteres (alfabeto, números, símbolos) - * - Cómo garantizar que el resultado incluya al menos un carácter de cada tipo - * - * Para tu reto (generador de contraseñas): - * Aplica el mismo patrón: construye el pool de caracteres según los - * parámetros del usuario (longitud, símbolos sí/no, mayúsculas sí/no), - * y usa selección criptográfica para construir la contraseña. - * - * Glosario: - * crypto.randomInt(max) — Devuelve un entero aleatorio criptográficamente seguro entre 0 y max-1. - * Math.random() — NO usar para contraseñas, es predecible. - * arreglo.push(item) — Añade un elemento al final de un arreglo. - * arreglo.join('') — Une los elementos de un arreglo en un solo texto. - */ - -const crypto = require('crypto'); - -const DIGITOS = "0123456789"; -const MAYUSCULAS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const SIMBOLOS = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; - -// Función de ayuda para elegir un caracter seguro al azar -function choice(pool) { - const indice = crypto.randomInt(pool.length); - return pool[indice]; -} - -// Función de ayuda para mezclar un arreglo (Fisher-Yates con crypto) -function shuffle(arreglo) { - for (let i = arreglo.length - 1; i > 0; i--) { - const j = crypto.randomInt(i + 1); - [arreglo[i], arreglo[j]] = [arreglo[j], arreglo[i]]; - } -} - -function generarPin(longitud = 6, soloNumeros = true) { - let pool; - if (soloNumeros) { - pool = DIGITOS; - } else { - pool = MAYUSCULAS + DIGITOS; - } - - let pin = ""; - for (let i = 0; i < longitud; i++) { - pin += choice(pool); - } - return pin; -} - -function generarPinGarantizado(longitud = 8) { - if (longitud < 3) { - throw new Error("La longitud mínima para un PIN garantizado es 3."); - } - - const poolCompleto = MAYUSCULAS + DIGITOS + SIMBOLOS; - - // 1. Garantizar al menos uno de cada tipo requerido - const obligatorios = [ - choice(MAYUSCULAS), - choice(DIGITOS), - choice(SIMBOLOS) - ]; - - // 2. Rellenar el resto con caracteres aleatorios del pool completo - const relleno = []; - for (let i = 0; i < longitud - 3; i++) { - relleno.push(choice(poolCompleto)); - } - - // 3. Unir todo y mezclar - const caracteres = obligatorios.concat(relleno); - shuffle(caracteres); - - return caracteres.join(""); -} - -if (require.main === module) { - console.log("=== Generador de PINs seguros ===\n"); - - console.log("PINs numéricos (6 dígitos):"); - for (let i = 0; i < 3; i++) { - console.log(` ${generarPin(6, true)}`); - } - - console.log("\nPINs alfanuméricos (8 caracteres):"); - for (let i = 0; i < 3; i++) { - console.log(` ${generarPin(8, false)}`); - } - - console.log("\nPINs con garantía (letra + dígito + símbolo, 10 chars):"); - for (let i = 0; i < 3; i++) { - console.log(` ${generarPinGarantizado(10)}`); - } -} diff --git a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py deleted file mode 100644 index 3de3974..0000000 --- a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Ejemplo: Construir un PIN bancario seguro con secrets + string - -Qué enseña este ejemplo: - - Cuándo usar secrets en lugar de random (y por qué importa) - - Cómo usar string.digits, string.ascii_letters, string.punctuation - - Cómo construir una cadena aleatoria con join() + secrets.choice() - - Cómo garantizar que el resultado incluya al menos un carácter de cada tipo - -Para tu reto (generador de contraseñas): - Aplica el mismo patrón: construye el pool de caracteres según los - parámetros del usuario (longitud, símbolos sí/no, mayúsculas sí/no), - y usa secrets.choice() en un loop para construir la contraseña. - -Glosario de términos "raros": - secrets — módulo para aleatoriedad segura (usa fuentes del sistema). - random — módulo de aleatoriedad predecible (NO usar para contraseñas). - string.digits — contiene "0123456789". - string.ascii... — contiene letras ("abcdefghijklmnopqrstuvwxyz", etc). - string.punctuation — todos los símbolos: !"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ - "".join(lista) — une una lista de caracteres en un solo texto (ej: "a" + "b" = "ab"). - _ (guion bajo) — se usa en `for _ in range()` cuando vas a repetir algo N veces - pero no necesitas usar el número de la vuelta actual. -""" - -import secrets -import string - - -def generar_pin(longitud: int = 6, solo_numeros: bool = True) -> str: - """ - Genera un PIN seguro. - Si solo_numeros=True, usa únicamente dígitos (PIN bancario clásico). - Si solo_numeros=False, mezcla letras y dígitos (PIN alfanumérico). - """ - if solo_numeros: - pool = string.digits # "0123456789" - else: - pool = string.ascii_uppercase + string.digits - - # join() + list comprehension: llama secrets.choice(pool) N veces - # y une todos los caracteres resultantes en un string - pin = "".join(secrets.choice(pool) for _ in range(longitud)) - return pin - - -def generar_pin_garantizado(longitud: int = 8) -> str: - """ - Genera un PIN que GARANTIZA tener al menos: - - 1 letra mayúscula - - 1 dígito - - 1 símbolo - - Técnica: generar una de cada tipo obligatorio, luego rellenar - el resto con caracteres del pool completo y mezclar. - """ - if longitud < 3: - raise ValueError("La longitud mínima para un PIN garantizado es 3.") - - pool_completo = string.ascii_uppercase + string.digits + string.punctuation - - # 1. Garantizar al menos uno de cada tipo requerido - obligatorios = [ - secrets.choice(string.ascii_uppercase), - secrets.choice(string.digits), - secrets.choice(string.punctuation), - ] - - # 2. Rellenar el resto con caracteres aleatorios del pool completo - relleno = [secrets.choice(pool_completo) for _ in range(longitud - 3)] - - # 3. Unir todo y mezclar (secrets.SystemRandom para barajar de forma segura) - caracteres = obligatorios + relleno - secrets.SystemRandom().shuffle(caracteres) - - return "".join(caracteres) - - -if __name__ == "__main__": - print("=== Generador de PINs seguros ===\n") - - print("PINs numéricos (6 dígitos):") - for _ in range(3): - print(f" {generar_pin(6, solo_numeros=True)}") - - print("\nPINs alfanuméricos (8 caracteres):") - for _ in range(3): - print(f" {generar_pin(8, solo_numeros=False)}") - - print("\nPINs con garantía (letra + dígito + símbolo, 10 chars):") - for _ in range(3): - print(f" {generar_pin_garantizado(10)}") diff --git a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs b/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs deleted file mode 100644 index 1e4e889..0000000 --- a/archivo-historico/ejemplos/general/05-generador-de-contrasenas/ejemplo_secrets.rs +++ /dev/null @@ -1,93 +0,0 @@ -/* -Ejemplo: Construir un PIN bancario seguro con generadores de números aleatorios - -Qué enseña este ejemplo: - - Cómo usar el crate 'rand' para generar aleatoriedad criptográficamente segura (rand usa el OS RNG por defecto en thread_rng()) - - Cómo estructurar strings de constantes y mezclarlas - - Cómo garantizar que el resultado incluya al menos un carácter de cada tipo - -Para tu reto (generador de contraseñas): - Aplica el mismo patrón: construye el pool de caracteres según los - parámetros del usuario (longitud, símbolos sí/no, mayúsculas sí/no), - y usa el RNG para construir la contraseña. - -Glosario: - rand::thread_rng() — obtiene el generador de números aleatorios seguro local - slice::choose() — selecciona un elemento aleatorio de una lista - slice::shuffle() — mezcla los elementos de una lista -*/ - -use rand::seq::SliceRandom; - -const DIGITOS: &[u8] = b"0123456789"; -const MAYUSCULAS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const SIMBOLOS: &[u8] = b"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; - -fn generar_pin(longitud: usize, solo_numeros: bool) -> String { - let mut rng = rand::thread_rng(); - - let mut pool = Vec::new(); - if solo_numeros { - pool.extend_from_slice(DIGITOS); - } else { - pool.extend_from_slice(MAYUSCULAS); - pool.extend_from_slice(DIGITOS); - } - - let mut pin = String::with_capacity(longitud); - for _ in 0..longitud { - let ch = pool.choose(&mut rng).unwrap(); - pin.push(*ch as char); - } - pin -} - -fn generar_pin_garantizado(longitud: usize) -> String { - if longitud < 3 { - panic!("La longitud mínima para un PIN garantizado es 3."); - } - - let mut rng = rand::thread_rng(); - - let mut pool_completo = Vec::new(); - pool_completo.extend_from_slice(MAYUSCULAS); - pool_completo.extend_from_slice(DIGITOS); - pool_completo.extend_from_slice(SIMBOLOS); - - let mut caracteres = Vec::new(); - - // 1. Garantizar - caracteres.push(*MAYUSCULAS.choose(&mut rng).unwrap()); - caracteres.push(*DIGITOS.choose(&mut rng).unwrap()); - caracteres.push(*SIMBOLOS.choose(&mut rng).unwrap()); - - // 2. Rellenar - for _ in 3..longitud { - caracteres.push(*pool_completo.choose(&mut rng).unwrap()); - } - - // 3. Mezclar - caracteres.shuffle(&mut rng); - - // Convertir de bytes a String - caracteres.iter().map(|&b| *b as char).collect() -} - -fn main() { - println!("=== Generador de PINs seguros ===\n"); - - println!("PINs numéricos (6 dígitos):"); - for _ in 0..3 { - println!(" {}", generar_pin(6, true)); - } - - println!("\nPINs alfanuméricos (8 caracteres):"); - for _ in 0..3 { - println!(" {}", generar_pin(8, false)); - } - - println!("\nPINs con garantía (letra + dígito + símbolo, 10 chars):"); - for _ in 0..3 { - println!(" {}", generar_pin_garantizado(10)); - } -} diff --git a/archivo-historico/ejemplos/general/06-conversor-de-unidades/README.md b/archivo-historico/ejemplos/general/06-conversor-de-unidades/README.md deleted file mode 100644 index 447d9d0..0000000 --- a/archivo-historico/ejemplos/general/06-conversor-de-unidades/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Funciones Puras y Menús - -**¿Qué muestra este ejemplo?** -Cómo diseñar funciones puras (que solo reciben parámetros y devuelven un resultado, sin modificar variables globales ni usar `print` dentro) y cómo armar un menú interactivo en la terminal usando bucles y `if/elif`. - -**¿Por qué es útil?** -Dividir tu código en funciones pequeñas, especializadas y sin efectos secundarios hace que el programa sea mucho más fácil de probar, mantener y reutilizar. - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_funciones.py` - -**¿Qué retos usan esta base?** -- [#18 Python General 06 — Conversor de unidades](https://github.com/Kaia-Alenia/nerve-community/issues/18) - -**Nota para principiantes** -Evita poner llamadas a `print()` o `input()` dentro de tus funciones matemáticas o lógicas. La función debe encargarse solo de calcular y devolver (`return`) el resultado; el menú principal se encarga de mostrarlo al usuario. diff --git a/archivo-historico/ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py b/archivo-historico/ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py deleted file mode 100644 index b6dff3b..0000000 --- a/archivo-historico/ejemplos/general/06-conversor-de-unidades/ejemplo_funciones.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Ejemplo: Conversor de unidades con funciones puras, type hints y menú separado - -Qué enseña este ejemplo: - - Qué es una "función pura" y por qué es buena práctica separar lógica de UI - - Cómo usar type hints (los ": float" y "-> float" después del nombre) - - Cómo estructurar un menú con varias categorías sin repetir código - - Cómo validar inputs numéricos con try/except ValueError - -Para tu reto (conversor de unidades): - Aplica el mismo patrón: una función por conversión, un menú - que llame a la función correspondiente, y validación de input. - -Glosario de type hints: - def funcion(param: tipo) -> tipo_retorno: - param: tipo — indica qué tipo de dato espera el parámetro - -> tipo — indica qué tipo de dato devuelve la función - - Son opcionales en Python, pero sirven como documentación en vivo y - ayudan a los editores a detectar errores antes de correr el código. - - Ejemplos: - def sumar(a: float, b: float) -> float: # recibe 2 floats, devuelve float - def saludo(nombre: str) -> str: # recibe string, devuelve string - def imprimir(msg: str) -> None: # devuelve None (no retorna nada) -""" - -# --- Funciones puras: solo reciben datos y devuelven datos --- -# No imprimen nada, no llaman a input(), no dependen de variables globales. -# Son fáciles de probar: si le das el mismo input, SIEMPRE dan el mismo output. - - -def km_a_millas(km: float) -> float: - """Convierte kilómetros a millas.""" - return km * 0.621371 - - -def millas_a_km(millas: float) -> float: - """Convierte millas a kilómetros.""" - return millas / 0.621371 - - -def celsius_a_fahrenheit(c: float) -> float: - """Convierte grados Celsius a Fahrenheit.""" - return (c * 9 / 5) + 32 - - -def fahrenheit_a_celsius(f: float) -> float: - """Convierte grados Fahrenheit a Celsius.""" - return (f - 32) * 5 / 9 - - -def kg_a_libras(kg: float) -> float: - """Convierte kilogramos a libras.""" - return kg * 2.20462 - - -def libras_a_kg(libras: float) -> float: - """Convierte libras a kilogramos.""" - return libras / 2.20462 - - -# --- Mapa de opciones: conecta el menú con las funciones puras --- -OPCIONES = { - "1": ("km → millas", km_a_millas, "km", "millas"), - "2": ("millas → km", millas_a_km, "millas", "km"), - "3": ("°C → °F", celsius_a_fahrenheit, "°C", "°F"), - "4": ("°F → °C", fahrenheit_a_celsius, "°F", "°C"), - "5": ("kg → libras", kg_a_libras, "kg", "libras"), - "6": ("libras → kg", libras_a_kg, "libras", "kg"), -} - - -def mostrar_menu(): - print("\n=== Conversor de Unidades ===") - for clave, (descripcion, _, _, _) in OPCIONES.items(): - print(f" {clave}. {descripcion}") - print(" 7. Salir") - - -def pedir_valor(unidad: str) -> float | None: - """Pide un número al usuario. Devuelve None si el input no es válido.""" - entrada = input(f"Ingresa el valor en {unidad}: ").strip() - try: - return float(entrada) - except ValueError: - print(f"Error: '{entrada}' no es un número válido.") - return None - - -def main(): - while True: - mostrar_menu() - opcion = input("\nElige una opción: ").strip() - - if opcion == "7": - print("¡Hasta luego!") - break - elif opcion in OPCIONES: - descripcion, funcion, unidad_entrada, unidad_salida = OPCIONES[opcion] - valor = pedir_valor(unidad_entrada) - if valor is not None: - resultado = funcion(valor) # Llamamos a la función pura - print(f"\n{valor} {unidad_entrada} = {resultado:.4f} {unidad_salida}") - else: - print("Opción no válida.") - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/general/07-calculadora-de-imc/README.md b/archivo-historico/ejemplos/general/07-calculadora-de-imc/README.md deleted file mode 100644 index 4dd1676..0000000 --- a/archivo-historico/ejemplos/general/07-calculadora-de-imc/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Múltiples Parámetros y Formateo de Strings - -**¿Qué muestra este ejemplo?** -Cómo crear funciones que aceptan múltiples parámetros y cómo usar `f-strings` para redondear y formatear salidas numéricas de forma amigable para el usuario. - -**¿Por qué es útil?** -Cuando necesitas hacer cálculos que dependen de varios factores (como el IMC que usa peso y estatura) y quieres mostrar el resultado con un límite de decimales para que no se vea desordenado. - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_formateo.py` - -**¿Qué retos usan esta base?** -- [#19 Python General 07 — Calculadora de IMC](https://github.com/Kaia-Alenia/nerve-community/issues/19) - -**Nota para principiantes** -Para redondear un número flotante a 2 decimales dentro de un f-string, simplemente agrega `:.2f` después del nombre de la variable (ej. `f"El resultado es {valor:.2f}"`). diff --git a/archivo-historico/ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py b/archivo-historico/ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py deleted file mode 100644 index dc741a1..0000000 --- a/archivo-historico/ejemplos/general/07-calculadora-de-imc/ejemplo_formateo.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -Ejemplo: Calculadora de IMC para mascotas con float(input), round() y f-strings - -Qué enseña este ejemplo: - - Cómo convertir el input del usuario (siempre string) a float con float() - - Cómo manejar el error si el usuario escribe letras en vez de números - - Cómo usar round() y f-strings con decimales específicos - - Cómo definir rangos numéricos y devolver una categoría - -Para tu reto (calculadora de IMC humano): - Aplica el mismo patrón: float(input()) para peso y altura, - cálculo matemático, y comparación de rangos para categorizar el resultado. - -Glosario de términos "raros": - float(valor) — convierte un string a número decimal. Lanza ValueError si falla. - round(num, n) — redondea 'num' a 'n' decimales. round(7.5678, 2) → 7.57 - f"{valor:.2f}" — formatea un número en un f-string con exactamente 2 decimales - f"{valor:.1f}" — 1 decimal; f"{valor:.4f}" → 4 decimales, etc. - ValueError — error que lanza Python cuando intentas convertir un texto - no numérico a int o float. Ej: float("hola") → ValueError - -Nota: El IMC de mascotas no es un estándar médico. -Este ejemplo es solo didáctico — usa datos inventados para no prescribir nada real. -""" - - -def calcular_imc_mascota(peso_kg: float, largo_cm: float) -> float: - """ - Calcula un "índice de masa" simplificado para mascotas. - Fórmula inventada solo para el ejemplo: peso / (largo en metros)^2 - """ - if largo_cm <= 0 or peso_kg <= 0: - raise ValueError("El peso y el largo deben ser valores positivos.") - largo_m = largo_cm / 100 - return peso_kg / (largo_m**2) - - -def categorizar_mascota(imc: float) -> str: - """ - Clasifica el resultado en rangos (inventados para el ejemplo). - """ - if imc < 15: - return "Muy liviano para su tamaño" - elif imc < 25: - return "Proporción saludable" - elif imc < 35: - return "Un poco pesado para su tamaño" - else: - return "Muy pesado para su tamaño" - - -def pedir_float(mensaje: str) -> float: - """ - Pide un número al usuario en loop hasta que ingrese algo válido. - Demuestra el patrón try/except para validar inputs numéricos. - """ - while True: - entrada = input(mensaje).strip() - try: - # float() intentará convertir el string a decimal - valor = float(entrada) - if valor <= 0: - print("El valor debe ser mayor a cero.") - continue - return valor - except ValueError: - # Si el usuario escribe "gato" o "12a", float() lanza ValueError - print(f"'{entrada}' no es un número válido. Intenta de nuevo.") - - -def main(): - print("=== Calculadora de proporción peso/tamaño para mascotas ===") - print("(Solo para práctica — no es un indicador médico veterinario real)\n") - - nombre = input("Nombre de tu mascota: ").strip() or "tu mascota" - - # Usamos nuestra función de input seguro que valida el tipo - peso = pedir_float("Peso en kg (ej: 4.5): ") - largo = pedir_float("Largo de hocico a cola en cm (ej: 60): ") - - try: - imc = calcular_imc_mascota(peso, largo) - categoria = categorizar_mascota(imc) - - # f-string con .2f → siempre muestra exactamente 2 decimales - print(f"\nResultado para {nombre}:") - print(f" Peso: {peso} kg | Largo: {largo} cm") - print(f" Índice calculado: {imc:.2f}") - print(f" Categoría: {categoria}") - - except ValueError as e: - print(f"Error en el cálculo: {e}") - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/general/08-organizador-de-archivos/README.md b/archivo-historico/ejemplos/general/08-organizador-de-archivos/README.md deleted file mode 100644 index 6c6c5e8..0000000 --- a/archivo-historico/ejemplos/general/08-organizador-de-archivos/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Manejo de Rutas (Pathlib) - -**¿Qué muestra este ejemplo?** -Cómo construir, validar y manipular rutas de archivos del sistema operativo usando el módulo moderno `pathlib` en lugar del antiguo `os.path`. - -**¿Por qué es útil?** -Hace que el código que manipula archivos y carpetas sea legible y que funcione exactamente igual en Windows, macOS y Linux sin preocuparse por la dirección de las barras (`/` vs `\`). - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_pathlib.py` - -**¿Qué retos usan esta base?** -- [#20 Python General 08 — Organizador automático de archivos](https://github.com/Kaia-Alenia/nerve-community/issues/20) - -**Nota para principiantes** -Con `pathlib.Path`, puedes unir rutas usando el operador de división `/` (ej. `ruta_base / "carpeta" / "archivo.txt"`), lo cual es mucho más limpio que usar `os.path.join()`. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py b/archivo-historico/ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py deleted file mode 100644 index e5ba360..0000000 --- a/archivo-historico/ejemplos/general/08-organizador-de-archivos/ejemplo_pathlib.py +++ /dev/null @@ -1,133 +0,0 @@ -""" -Ejemplo: Organizar archivos de prueba en subcarpetas según su extensión - -Qué enseña este ejemplo: - - Cómo usar pathlib.Path para manejar rutas de forma moderna - - Cómo listar todos los archivos de una carpeta con iterdir() - - Cómo leer la extensión de un archivo con .suffix - - Cómo mover archivos con shutil.move() - - Cómo crear subcarpetas si no existen con mkdir(parents=True, exist_ok=True) - -Para tu reto (organizador de archivos): - Aplica el mismo patrón: recibe una ruta por argumento, - itera los archivos, clasifica por extensión, y muévelos. - Agrega el modo --dry-run para simular sin mover nada. - -Glosario de términos "raros" (pathlib y shutil): - Path("ruta") — crea un objeto Path (más potente que un string de ruta) - Path.cwd() — devuelve la carpeta de trabajo actual - ruta.iterdir() — genera todos los archivos y carpetas dentro de 'ruta' - archivo.is_file() — True si es un archivo, False si es carpeta o symlink - archivo.suffix — la extensión: ".py", ".txt", ".jpg", etc. (con el punto) - archivo.name — nombre completo: "foto.jpg" - archivo.stem — nombre sin extensión: "foto" - ruta.mkdir(...) — crea la carpeta (parents=True crea carpetas intermedias) - (exist_ok=True no falla si ya existe) - -Glosario de shutil: - shutil.move(origen, destino) — mueve un archivo o carpeta de una ruta a otra -""" - -import shutil -from pathlib import Path - -# Mapa de extensiones → nombre de subcarpeta -EXTENSIONES = { - ".jpg": "Imagenes", - ".jpeg": "Imagenes", - ".png": "Imagenes", - ".gif": "Imagenes", - ".pdf": "Documentos", - ".docx": "Documentos", - ".txt": "Documentos", - ".mp3": "Audio", - ".wav": "Audio", - ".py": "Scripts", - ".js": "Scripts", -} - - -def organizar_carpeta(ruta_carpeta: str, dry_run: bool = False): - """ - Organiza los archivos de 'ruta_carpeta' en subcarpetas según extensión. - - dry_run=True → solo imprime qué haría, sin mover nada. - dry_run=False → ejecuta los movimientos realmente. - """ - carpeta = Path(ruta_carpeta) - - if not carpeta.exists() or not carpeta.is_dir(): - print(f"Error: '{ruta_carpeta}' no es una carpeta válida.") - return - - modo = "[SIMULACIÓN]" if dry_run else "[EJECUTANDO]" - print(f"\n{modo} Organizando: {carpeta}\n") - - movidos = 0 - ignorados = 0 - - # iterdir() genera todos los elementos dentro de la carpeta - for archivo in carpeta.iterdir(): - # Solo procesamos archivos (ignoramos subcarpetas) - if not archivo.is_file(): - continue - - # .suffix devuelve la extensión en minúsculas con el punto (ej: ".jpg") - extension = archivo.suffix.lower() - - if extension not in EXTENSIONES: - print(f" ⚠ Sin categoría: {archivo.name} (ignorado)") - ignorados += 1 - continue - - # Determinar la subcarpeta destino - nombre_subcarpeta = EXTENSIONES[extension] - carpeta_destino = carpeta / nombre_subcarpeta - - print(f" → {archivo.name} → {nombre_subcarpeta}/") - - if not dry_run: - # Crear la subcarpeta si no existe - carpeta_destino.mkdir(parents=True, exist_ok=True) - # Mover el archivo - shutil.move(str(archivo), carpeta_destino / archivo.name) - - movidos += 1 - - print( - f"\nResumen: {movidos} archivo(s) {'a mover' if dry_run else 'movidos'}, {ignorados} ignorados." - ) - - -if __name__ == "__main__": - import tempfile - import os - - # Crear carpeta temporal con archivos de ejemplo para demostrar - with tempfile.TemporaryDirectory() as tmp: - archivos_prueba = [ - "foto_verano.jpg", - "presentacion.pdf", - "notas.txt", - "cancion.mp3", - "script_util.py", - "archivo_raro.xyz", - ] - for nombre in archivos_prueba: - (Path(tmp) / nombre).touch() - - print("Archivos de prueba creados:") - for f in os.listdir(tmp): - print(f" {f}") - - # Primero simulamos (dry_run=True) - organizar_carpeta(tmp, dry_run=True) - - # Luego ejecutamos - print("\n--- Ahora ejecutamos realmente ---") - organizar_carpeta(tmp, dry_run=False) - - print("\nEstructura resultante:") - for item in sorted(Path(tmp).rglob("*")): - nivel = len(item.relative_to(tmp).parts) - 1 - print(" " * nivel + item.name) diff --git a/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/README.md b/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/README.md deleted file mode 100644 index 3fbd98f..0000000 --- a/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Fechas y Tiempo (Datetime) - -**¿Qué muestra este ejemplo?** -Cómo obtener la fecha actual, hacer sumas/restas de tiempo con `timedelta`, y formatear fechas a texto (o texto a fechas) usando el módulo `datetime`. - -**¿Por qué es útil?** -Es necesario para cualquier sistema que maneje expiraciones, agendas, cronómetros, logs de eventos o tareas programadas. - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_datetime.py` - -**¿Qué retos usan esta base?** -- [#21 Python General 09 — Recordatorios en terminal](https://github.com/Kaia-Alenia/nerve-community/issues/21) - -**Nota para principiantes** -Trabajar con zonas horarias (Timezones) en Python es complicado. Si puedes elegir, intenta guardar siempre tus fechas en formato UTC y conviértelas a la hora local solo al momento de mostrárselas al usuario. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py b/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py deleted file mode 100644 index fe76883..0000000 --- a/archivo-historico/ejemplos/general/09-recordatorios-en-terminal/ejemplo_datetime.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -Ejemplo: Calcular días hasta un evento dado por el usuario con datetime - -Qué enseña este ejemplo: - - Cómo parsear una fecha dada por el usuario con strptime() - - Cómo calcular la diferencia entre dos fechas con timedelta - - Cómo comparar fechas para saber si ya pasaron - - Cómo revisar periódicamente (loop con time.sleep) - -Para tu reto (recordatorios en terminal): - Aplica el mismo patrón: guarda la fecha del recordatorio, - y en un loop compara datetime.now() con esa fecha. - Cuando now() >= fecha_recordatorio, muestra la alerta. - -Glosario de términos "raros": - datetime.datetime.now() — fecha y hora actual del sistema - datetime.datetime.strptime(texto, formato) — convierte un string a datetime - El formato usa códigos: - %Y = año con 4 dígitos (2025) - %m = mes con 2 dígitos (01-12) - %d = día con 2 dígitos (01-31) - %H = hora en formato 24h (00-23) - %M = minutos (00-59) - timedelta — representa una duración (días, horas, segundos) - timedelta(days=7) — 7 días - timedelta(hours=2, minutes=30) — 2 horas y 30 minutos - diferencia.days — días enteros en una diferencia de fechas - tiempo2 - tiempo1 — resta dos datetimes → devuelve un timedelta - time.sleep(segundos) — pausa el programa N segundos -""" - -import datetime -import time - - -def pedir_fecha_evento() -> datetime.datetime: - """ - Pide al usuario una fecha en formato YYYY-MM-DD HH:MM - y la convierte a un objeto datetime. - Repite hasta que el formato sea correcto. - """ - formato = "%Y-%m-%d %H:%M" - while True: - entrada = input("Fecha del evento (YYYY-MM-DD HH:MM): ").strip() - try: - # strptime: string parse time - # Convierte "2025-12-25 09:00" → datetime(2025, 12, 25, 9, 0) - fecha = datetime.datetime.strptime(entrada, formato) - return fecha - except ValueError: - print(f"Formato incorrecto. Usa: YYYY-MM-DD HH:MM (ej: 2025-12-25 09:00)") - - -def calcular_tiempo_restante(fecha_evento: datetime.datetime) -> str: - """ - Calcula cuánto tiempo falta (o pasó) desde ahora hasta el evento. - Devuelve un string descriptivo. - """ - ahora = datetime.datetime.now() - diferencia = fecha_evento - ahora - - if diferencia.total_seconds() < 0: - # El evento ya pasó — diferencia es negativa - diferencia_pasada = ahora - fecha_evento - dias = diferencia_pasada.days - horas = diferencia_pasada.seconds // 3600 - return f"Hace {dias} día(s) y {horas} hora(s)" - else: - dias = diferencia.days - horas = diferencia.seconds // 3600 - minutos = (diferencia.seconds % 3600) // 60 - return f"Faltan {dias} día(s), {horas} hora(s) y {minutos} minuto(s)" - - -def vigilar_evento( - nombre: str, fecha_evento: datetime.datetime, intervalo_seg: int = 5 -): - """ - Revisa periódicamente si el evento ya llegó. - Simula el loop de un sistema de recordatorios. - """ - print(f"\nVigilando el evento '{nombre}'...") - print("(Presiona Ctrl+C para detener)\n") - - try: - while True: - ahora = datetime.datetime.now() - tiempo_str = calcular_tiempo_restante(fecha_evento) - print(f" [{ahora.strftime('%H:%M:%S')}] {nombre}: {tiempo_str}") - - if ahora >= fecha_evento: - print(f"\n *** ¡RECORDATORIO! '{nombre}' es AHORA ***") - break - - time.sleep(intervalo_seg) - except KeyboardInterrupt: - print("\nVigilancia detenida.") - - -if __name__ == "__main__": - nombre = input("Nombre del evento: ").strip() or "Mi evento" - fecha = pedir_fecha_evento() - print( - f"\nEvento '{nombre}' programado para: {fecha.strftime('%d/%m/%Y a las %H:%M')}" - ) - print(calcular_tiempo_restante(fecha)) - - respuesta = input( - "\n¿Quieres que el programa vigile y avise cuando llegue? (s/n): " - ).lower() - if respuesta == "s": - vigilar_evento(nombre, fecha) diff --git a/archivo-historico/ejemplos/general/10-analizador-de-texto/README.md b/archivo-historico/ejemplos/general/10-analizador-de-texto/README.md deleted file mode 100644 index c436f18..0000000 --- a/archivo-historico/ejemplos/general/10-analizador-de-texto/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Conteo de Frecuencias (Counter) - -**¿Qué muestra este ejemplo?** -Cómo usar la estructura de datos especializada `collections.Counter` para contar la frecuencia de elementos en un iterable (como palabras o letras) de manera eficiente. - -**¿Por qué es útil?** -Evita escribir bucles largos y anidados con diccionarios para llevar la cuenta de repeticiones y permite obtener rápidamente los elementos más comunes (top N). - -**Cómo correrlo** -- No necesita instalar nada extra, todo es librería estándar de Python. -- Comando para ejecutarlo: `python ejemplo_counter.py` - -**¿Qué retos usan esta base?** -- [#22 Python General 10 — Analizador de texto](https://github.com/Kaia-Alenia/nerve-community/issues/22) - -**Nota para principiantes** -`Counter` tiene un método interno muy útil llamado `.most_common(n)` que te devuelve automáticamente una lista ordenada con los `n` elementos más repetidos sin tener que ordenarla tú mismo. \ No newline at end of file diff --git a/archivo-historico/ejemplos/general/10-analizador-de-texto/ejemplo_counter.py b/archivo-historico/ejemplos/general/10-analizador-de-texto/ejemplo_counter.py deleted file mode 100644 index f532abe..0000000 --- a/archivo-historico/ejemplos/general/10-analizador-de-texto/ejemplo_counter.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -Ejemplo: Analizar un párrafo real — palabras únicas, frecuencia, estadísticas - -Qué enseña este ejemplo: - - Cómo limpiar y tokenizar texto con split() y lower() - - Cómo eliminar puntuación con str.translate() + string.punctuation - - Cómo usar Counter para contar frecuencias - - Cómo filtrar stopwords (palabras vacías) de una lista - - Cómo combinar todo para un análisis de texto realista - -Para tu reto (analizador de texto): - Aplica el mismo patrón para leer un archivo .txt, - y reportar: líneas, palabras, promedio por línea, - y las 10 palabras más frecuentes (sin stopwords). - -Glosario de términos "raros": - split() — divide un string por espacios y devuelve una lista de palabras - "hola mundo".split() → ["hola", "mundo"] - lower() — convierte todo a minúsculas (para comparar sin importar case) - str.translate() — reemplaza o elimina caracteres según una tabla de mapeo - str.maketrans() — construye la tabla para translate() - str.maketrans("", "", ".,!?") → elimina esos caracteres - Counter(lista) — crea un diccionario donde la clave es el elemento - y el valor es cuántas veces aparece - .most_common(n) — devuelve los N elementos más frecuentes como lista de tuplas - set() — colección sin duplicados; útil para palabras únicas -""" - -import string -from collections import Counter - -# Palabras vacías en español (no aportan significado, no queremos contarlas) -STOPWORDS = { - "el", - "la", - "los", - "las", - "un", - "una", - "unos", - "unas", - "de", - "en", - "a", - "y", - "o", - "que", - "con", - "por", - "para", - "del", - "al", - "se", - "es", - "son", - "su", - "sus", - "lo", - "le", - "más", - "pero", - "como", - "si", - "no", - "ya", - "fue", - "era", - "hay", - "ser", - "me", - "te", - "nos", - "les", -} - - -def limpiar_palabra(palabra: str) -> str: - """ - Convierte a minúsculas y elimina puntuación de una palabra. - str.maketrans("", "", chars) crea una tabla que borra todos los chars. - """ - tabla = str.maketrans("", "", string.punctuation + "¡¿«»") - return palabra.lower().translate(tabla) - - -def analizar_texto(texto: str) -> dict: - """ - Analiza el texto y devuelve estadísticas en un diccionario. - """ - # 1. Separar en palabras y limpiar cada una - palabras_crudas = texto.split() - palabras_limpias = [limpiar_palabra(p) for p in palabras_crudas] - - # 2. Filtrar palabras vacías y palabras de 1 carácter - palabras_validas = [ - p for p in palabras_limpias if p and len(p) > 1 and p not in STOPWORDS - ] - - # 3. Contar frecuencias con Counter - conteo = Counter(palabras_validas) - - # 4. Palabras únicas (sin repetir) - unicas = set(palabras_validas) - - return { - "total_palabras": len(palabras_crudas), - "palabras_unicas": len(unicas), - "palabras_validas": len(palabras_validas), - "top_5": conteo.most_common(5), - "mas_frecuente": conteo.most_common(1)[0] if conteo else None, - } - - -def imprimir_reporte(stats: dict): - print("\n--- Reporte de Análisis de Texto ---") - print(f" Total de palabras: {stats['total_palabras']}") - print(f" Palabras únicas: {stats['palabras_unicas']}") - print(f" (Excluyendo stopwords y puntuación)") - - if stats["mas_frecuente"]: - palabra, veces = stats["mas_frecuente"] - print(f" Palabra más frecuente: '{palabra}' ({veces} veces)") - - print("\n Top 5 palabras más usadas:") - for palabra, veces in stats["top_5"]: - barra = "█" * veces - print(f" {palabra:<15} {barra} ({veces})") - - -if __name__ == "__main__": - TEXTO_EJEMPLO = """ - Python es un lenguaje de programación poderoso y fácil de aprender. - Python tiene estructuras de datos eficientes y un enfoque simple pero efectivo - hacia la programación orientada a objetos. La elegante sintaxis de Python y su - tipado dinámico, junto con su naturaleza interpretada, hacen de Python un - lenguaje ideal para scripting y desarrollo rápido de aplicaciones en muchas - áreas y en muchas plataformas distintas. - """ - - print("Texto a analizar:") - print(TEXTO_EJEMPLO) - - stats = analizar_texto(TEXTO_EJEMPLO) - imprimir_reporte(stats) diff --git a/archivo-historico/ejemplos/general/11-decoradores/README.md b/archivo-historico/ejemplos/general/11-decoradores/README.md deleted file mode 100644 index bc8a23e..0000000 --- a/archivo-historico/ejemplos/general/11-decoradores/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Decoradores Básicos - -## ¿Qué muestra este ejemplo? -Muestra cómo crear un decorador sencillo (`@log_ejecucion`) utilizando `functools.wraps` para añadir un comportamiento (imprimir mensajes de inicio y fin) alrededor de una función existente sin modificar su código interno. - -## ¿Por qué es útil? -Los decoradores son una característica poderosa de Python para reutilizar código. Permiten extraer lógica repetitiva (como registros, validaciones, o comprobaciones de acceso) y aplicarla limpiamente a múltiples funciones con solo agregar un `@nombre_decorador` arriba de su definición. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_decorador.py -``` - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #25 en GitHub: Python General 11 - Profiler de Funciones](https://github.com/Kaia-Alenia/nerve-community/issues/25)** - -## Nota para principiantes -Recuerda siempre importar `functools` y usar `@functools.wraps(funcion)` dentro de tu decorador. Si no lo haces, la función perderá su nombre original (`__name__`) y su docstring, lo cual dificulta la depuración. diff --git a/archivo-historico/ejemplos/general/11-decoradores/ejemplo_decorador.py b/archivo-historico/ejemplos/general/11-decoradores/ejemplo_decorador.py deleted file mode 100644 index 6e441e8..0000000 --- a/archivo-historico/ejemplos/general/11-decoradores/ejemplo_decorador.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Ejemplo: Decorador @medir_tiempo con functools.wraps - -Qué enseña este ejemplo: - - La anatomía completa de un decorador: función externa + wrapper - - Para qué sirve @functools.wraps y qué pasa si lo omites - - Cómo medir tiempo de ejecución con time.perf_counter() - - Cómo hacer un decorador que funcione con CUALQUIER función (*args, **kwargs) - -Para tu reto (profiler de funciones): - Este ejemplo ya es muy similar al reto. Tu variación debe: - - Usar time.perf_counter() (más preciso que time.time() para medición) - - Manejar funciones con argumentos arbitrarios (*args, **kwargs) - - Devolver el valor de retorno de la función original - -Glosario: - functools.wraps(funcion) — un decorador para decoradores. - Sin él, la función decorada pierde su nombre (__name__) y su docstring. - Con él, el wrapper "pretende" ser la función original. - Ejemplo sin wraps: saludar.__name__ → "wrapper" (MALO) - Ejemplo con wraps: saludar.__name__ → "saludar" (CORRECTO) - - *args — captura todos los argumentos posicionales como una tupla - **kwargs — captura todos los argumentos nombrados como un diccionario - Usarlos en el wrapper garantiza que el decorador funcione con CUALQUIER función. - - time.perf_counter() — reloj de alta resolución para medir duración - NO usar time.time() para benchmarks (menos preciso) -""" - -import functools -import time - - -def medir_tiempo(funcion): - """ - Decorador que mide cuántos segundos tarda en ejecutarse la función. - Se usa escribiendo @medir_tiempo justo antes de def. - """ - - @functools.wraps(funcion) - def wrapper(*args, **kwargs): - inicio = time.perf_counter() - - # Llamar a la función original con todos sus argumentos - resultado = funcion(*args, **kwargs) - - fin = time.perf_counter() - duracion = fin - inicio - - # Imprimimos el tiempo, pero devolvemos el resultado original - print(f" ⏱ '{funcion.__name__}' tardó {duracion:.6f} segundos") - return resultado - - return wrapper - - -# --- Aplicando el decorador --- -@medir_tiempo -def sumar_lista(numeros: list) -> float: - """Suma todos los números de una lista.""" - return sum(numeros) - - -@medir_tiempo -def buscar_en_lista(lista: list, objetivo) -> bool: - """Busca un elemento en una lista con iteración lineal (O(n)).""" - for item in lista: - if item == objetivo: - return True - return False - - -@medir_tiempo -def simular_trabajo_pesado(iteraciones: int) -> int: - """Simula trabajo computacional con un loop.""" - total = 0 - for i in range(iteraciones): - total += i * i - return total - - -if __name__ == "__main__": - print("=== Demo del decorador @medir_tiempo ===\n") - - # Demostración: las funciones se ven iguales, pero el tiempo se imprime solo - numeros = list(range(1_000_000)) - - print("1. Sumar 1 millón de números:") - resultado = sumar_lista(numeros) - print(f" Resultado: {resultado:,}\n") - - print("2. Buscar el último elemento (peor caso):") - encontrado = buscar_en_lista(numeros, 999_999) - print(f" Encontrado: {encontrado}\n") - - print("3. Trabajo pesado con 500,000 iteraciones:") - resultado = simular_trabajo_pesado(500_000) - print(f" Resultado: {resultado:,}\n") - - # Verificar que @functools.wraps preservó el nombre y docstring - print("--- Verificación de @functools.wraps ---") - print(f"sumar_lista.__name__ = '{sumar_lista.__name__}'") - print(f"sumar_lista.__doc__ = '{sumar_lista.__doc__}'") diff --git a/archivo-historico/ejemplos/general/12-regex/README.md b/archivo-historico/ejemplos/general/12-regex/README.md deleted file mode 100644 index 8324807..0000000 --- a/archivo-historico/ejemplos/general/12-regex/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Expresiones Regulares (Regex) - -## ¿Qué muestra este ejemplo? -Muestra cómo usar el módulo `re` de Python y expresiones regulares (regex) para validar que un número de teléfono mexicano tenga un formato estricto (`+52 XXXXXXXXXX`). - -## ¿Por qué es útil? -Validar entradas de usuario es una de las tareas más comunes en la programación. Usar expresiones regulares te permite describir formatos muy específicos de texto en una sola línea de código, evitando tener que hacer múltiples condicionales y ciclos manuales. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_regex.py -``` - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #26 en GitHub: Python General 12 - Validador de Emails con Regex](https://github.com/Kaia-Alenia/nerve-community/issues/26)** - -## Nota para principiantes -Los strings de las expresiones regulares suelen llevar una `r` antes de las comillas (ej. `r'^\+52'`). Esto indica que es un "raw string", evitando que Python interprete caracteres como `\n` o `\t` de forma especial, dejándolos intactos para que el motor regex los procese. diff --git a/archivo-historico/ejemplos/general/12-regex/ejemplo_regex.py b/archivo-historico/ejemplos/general/12-regex/ejemplo_regex.py deleted file mode 100644 index 93d9bfc..0000000 --- a/archivo-historico/ejemplos/general/12-regex/ejemplo_regex.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -Ejemplo: Validar emails con regex — patrón paso a paso - -Qué enseña este ejemplo: - - Cómo leer y construir un patrón de regex explicando cada parte - - re.match() vs re.search() vs re.fullmatch() — diferencias clave - - Cómo usar grupos de captura () para extraer partes del texto - - Casos de prueba que demuestran qué acepta y qué rechaza el patrón - -Para tu reto (validador de emails): - Este ejemplo ya es muy similar. Tu variación debe: - - Crear una función es_email_valido(texto) que devuelva True/False - - Incluir al menos 5 casos de prueba válidos y 5 inválidos - - Explicar en comentarios qué valida cada parte del patrón - -Glosario de regex: - ^ — inicio del string (sin esto, puede haber texto antes del patrón) - $ — fin del string (sin esto, puede haber texto extra al final) - . — cualquier carácter (excepto salto de línea) - \\. — un punto LITERAL (el \\ escapa el significado especial de .) - [a-z] — cualquier letra minúscula de la 'a' a la 'z' - [a-zA-Z0-9]— cualquier letra (mayúscula o minúscula) o dígito - [^@\\s] — cualquier carácter que NO sea @ ni espacio (^ dentro de [] niega) - + — 1 o más del elemento anterior - * — 0 o más del elemento anterior - {n} — exactamente n repeticiones - {n,m} — entre n y m repeticiones - () — grupo de captura — puedes extraer su contenido con .group(1) - - re.match() — busca solo al INICIO del string - re.search() — busca en CUALQUIER parte del string - re.fullmatch() — el patrón debe cubrir TODO el string (equivale a ^...$) -""" - -import re - - -def es_email_valido(texto: str) -> bool: - """ - Valida si un string tiene formato de email. - No es RFC-compliant (eso es casi imposible), pero rechaza los casos - más comunes de emails inválidos. - - Patrón desglosado: - ^ → inicio del string - [^@\\s]+ → usuario: 1+ caracteres que no sean @ ni espacio - @ → el símbolo @ obligatorio - [^@\\s]+ → dominio: 1+ caracteres que no sean @ ni espacio - \\. → un punto literal (necesario: gmail.com, not gmailcom) - [a-zA-Z]{2,}→ extensión: al menos 2 letras (com, mx, org, io, etc.) - $ → fin del string - """ - patron = r"^[^@\s]+@[^@\s]+\.[a-zA-Z]{2,}$" - return bool(re.fullmatch(patron, texto)) - - -def extraer_partes_email(email: str) -> dict | None: - """ - Si el email es válido, extrae usuario, dominio y extensión por separado - usando grupos de captura (). - """ - patron = r"^([^@\s]+)@([^@\s]+)\.([a-zA-Z]{2,})$" - match = re.fullmatch(patron, email) - - if match: - # .group(0) → todo el match; .group(1) → primer (), etc. - return { - "usuario": match.group(1), - "dominio": match.group(2), - "extension": match.group(3), - } - return None - - -if __name__ == "__main__": - print("=== Validador de Emails ===\n") - - casos = [ - # Válidos - ("usuario@gmail.com", True), - ("nombre.apellido@empresa.mx", True), - ("user+tag@mail.io", True), - ("contacto@sub.dominio.org", True), - ("a@b.co", True), - # Inválidos - ("sin-arroba-punto", False), - ("@sinusuario.com", False), - ("usuario@sincuenta", False), - ("usuario@.com", False), - ("usuario @gmail.com", False), # espacio - ("doble@@arroba.com", False), - ("", False), - ] - - validos_ok = 0 - total = len(casos) - - for email, esperado in casos: - resultado = es_email_valido(email) - icono = "✓" if resultado == esperado else "✗ ERROR" - estado = "Válido" if resultado else "Inválido" - print(f" [{icono}] {estado:8} | '{email}'") - if resultado == esperado: - validos_ok += 1 - - print(f"\n{validos_ok}/{total} casos se comportaron como se esperaba.\n") - - # Demo de extracción con grupos - email_demo = "john.doe@example.com" - partes = extraer_partes_email(email_demo) - if partes: - print(f"Partes de '{email_demo}':") - print(f" Usuario: {partes['usuario']}") - print(f" Dominio: {partes['dominio']}") - print(f" Extensión: {partes['extension']}") diff --git a/archivo-historico/ejemplos/general/13-generadores/README.md b/archivo-historico/ejemplos/general/13-generadores/README.md deleted file mode 100644 index ecf53fc..0000000 --- a/archivo-historico/ejemplos/general/13-generadores/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Generadores en Python - -## ¿Qué muestra este ejemplo? -Muestra cómo crear una función generadora usando la palabra clave `yield` para producir una secuencia de números de la serie de Fibonacci, bajo demanda. - -## ¿Por qué es útil? -Los generadores permiten procesar o crear secuencias enormes de datos de forma perezosa (lazy). En lugar de calcular y almacenar 1 millón de elementos en una lista (lo que agotaría la memoria RAM), un generador calcula y entrega solo el elemento que necesitas en ese exacto instante. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_generadores.py -``` - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #27 en GitHub: Python General 13 - Lector de Archivos Gigantes (Generadores)](https://github.com/Kaia-Alenia/nerve-community/issues/27)** - -## Nota para principiantes -Cuando una función tiene `yield` en lugar de `return`, Python sabe que es un generador. Al llamarla, no ejecuta el código inmediatamente, sino que devuelve un objeto iterador. El código solo avanza hasta el siguiente `yield` cada vez que usas un ciclo `for` o la función `next()`. diff --git a/archivo-historico/ejemplos/general/13-generadores/ejemplo_generadores.py b/archivo-historico/ejemplos/general/13-generadores/ejemplo_generadores.py deleted file mode 100644 index 4c2def5..0000000 --- a/archivo-historico/ejemplos/general/13-generadores/ejemplo_generadores.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -Ejemplo: Leer un archivo grande línea a línea con yield (generadores) - -Qué enseña este ejemplo: - - Qué es un generador y por qué ahorra memoria en archivos grandes - - La diferencia entre yield y return - - Cómo crear un archivo de prueba grande y procesarlo sin cargarlo completo - - Cómo usar next() y for directamente sobre un generador - -Para tu reto (lector de archivos gigantes): - Aplica el mismo patrón: una función generadora que haga yield línea a línea. - Luego usa el generador para procesar (contar, buscar, filtrar) sin listas. - -Glosario: - yield — como return, pero pausa la función en vez de terminarla. - La próxima vez que se pida un valor, continúa desde donde pausó. - Convierte automáticamente la función en un generador. - - generador — objeto que produce valores uno a uno, bajo demanda. - No calcula todos los valores de una vez (perezoso = lazy). - Ventaja: si el archivo tiene 10 GB, solo una línea ocupa - memoria a la vez. - - next(gen) — pide el siguiente valor del generador. Lanza StopIteration - cuando se agotan los valores. - - for x in gen — forma más cómoda de consumir un generador (maneja StopIteration) - - Diferencia clave: - Con lista: lineas = archivo.readlines() → carga TODO en RAM - Con yield: for linea in leer_lineas(archivo) → una línea a la vez en RAM -""" - -from pathlib import Path - - -def leer_lineas(ruta_archivo: str): - """ - Generador que entrega una línea a la vez del archivo. - El archivo nunca se carga completo en memoria. - """ - ruta = Path(ruta_archivo) - if not ruta.exists(): - print(f"Error: '{ruta_archivo}' no existe.") - return # En un generador, return sin valor termina la iteración - - with open(ruta, "r", encoding="utf-8") as f: - for linea in f: - # yield pausa aquí y entrega la línea al que llamó al generador - # La próxima vez que se pida un valor, el loop continúa - yield linea.rstrip("\n") # eliminamos el salto de línea - - -def contar_lineas(ruta_archivo: str) -> int: - """Cuenta líneas sin cargar el archivo completo.""" - return sum(1 for _ in leer_lineas(ruta_archivo)) - - -def buscar_palabra(ruta_archivo: str, palabra: str) -> list: - """ - Devuelve los números de línea donde aparece la palabra. - Solo procesa una línea a la vez gracias al generador. - """ - resultados = [] - for numero, linea in enumerate(leer_lineas(ruta_archivo), start=1): - if palabra.lower() in linea.lower(): - resultados.append((numero, linea)) - return resultados - - -def crear_archivo_prueba(ruta: str, num_lineas: int = 100_000): - """Crea un archivo grande para probar el generador.""" - with open(ruta, "w", encoding="utf-8") as f: - for i in range(1, num_lineas + 1): - if i % 5_000 == 0: - f.write(f"Línea especial número {i} — contiene la palabra CLAVE\n") - else: - f.write(f"Esta es la línea número {i} del archivo de prueba\n") - print(f"✓ Archivo creado: {ruta} ({num_lineas:,} líneas)") - - -if __name__ == "__main__": - ARCHIVO = "archivo_grande.txt" - - # Crear el archivo de prueba - crear_archivo_prueba(ARCHIVO, num_lineas=100_000) - - # Contar sin cargar todo en memoria - total = contar_lineas(ARCHIVO) - print(f"Total de líneas: {total:,}\n") - - # Buscar las líneas que contienen "CLAVE" - print("Buscando líneas con 'CLAVE'...") - encontradas = buscar_palabra(ARCHIVO, "CLAVE") - print(f"Se encontraron {len(encontradas)} coincidencias:") - for num, linea in encontradas[:3]: # Mostramos solo las primeras 3 - print(f" Línea {num:,}: {linea}") - - # Demostrar next() manualmente - print("\nUsando next() manualmente en el generador:") - gen = leer_lineas(ARCHIVO) - print(f" 1er valor: {next(gen)}") - print(f" 2do valor: {next(gen)}") - print(f" 3er valor: {next(gen)}") - - # Limpiar - Path(ARCHIVO).unlink() - print(f"\n✓ Archivo '{ARCHIVO}' eliminado.") diff --git a/archivo-historico/ejemplos/general/14-argparse/README.md b/archivo-historico/ejemplos/general/14-argparse/README.md deleted file mode 100644 index 23c2148..0000000 --- a/archivo-historico/ejemplos/general/14-argparse/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Interfaz de Línea de Comandos (CLI) con argparse - -## ¿Qué muestra este ejemplo? -Muestra cómo utilizar la librería estándar `argparse` para crear una mini-calculadora por línea de comandos que acepta argumentos como `--operacion`, `--a` y `--b`, validando sus tipos. - -## ¿Por qué es útil? -Scripts profesionales de Python rara vez utilizan `input()` para pedir datos de forma interactiva. Usar argumentos por terminal permite automatizar herramientas y usarlas en tuberías (pipelines) sin requerir intervención humana, y proveen ayuda (`--help`) de forma automática. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_argparse.py --operacion suma --a 10 --b 5.5 -python ejemplo_argparse.py --operacion multiplicacion --a 4 --b 2 -``` - -Puedes ver el menú de ayuda integrado corriendo: -```bash -python ejemplo_argparse.py --help -``` - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #28 en GitHub: Python General 14 - CLI del Clima (APIs y argparse)](https://github.com/Kaia-Alenia/nerve-community/issues/28)** - -## Nota para principiantes -Nota cómo definimos los tipos en `add_argument(..., type=float)`. `argparse` se encarga de convertir automáticamente el texto que introduces en la terminal al tipo de dato que programaste, e incluso lanzará un error limpio si el usuario pasa letras en vez de números. diff --git a/archivo-historico/ejemplos/general/14-argparse/ejemplo_argparse.py b/archivo-historico/ejemplos/general/14-argparse/ejemplo_argparse.py deleted file mode 100644 index 41ea31d..0000000 --- a/archivo-historico/ejemplos/general/14-argparse/ejemplo_argparse.py +++ /dev/null @@ -1,135 +0,0 @@ -""" -Ejemplo: CLI para consultar días festivos con argparse, subcomandos y args opcionales - -Qué enseña este ejemplo: - - Cómo estructurar una CLI con subcomandos (como git commit, git push) - - Cómo agregar argumentos opcionales con -- (ej: --año, --pais) - - Cómo usar type= para convertir el input automáticamente - - Cómo leer args.subcomando y ejecutar lógica según el subcomando - -Para tu reto (CLI del clima): - Aplica el mismo patrón: - python clima.py actual --ciudad "Monterrey" - python clima.py pronostico --ciudad "CDMX" --dias 5 - Usa argparse para parsear --ciudad y luego llama a la API. - -Glosario de argparse: - ArgumentParser(description=) — crea el parser con descripción del --help - add_argument("nombre") — argumento POSICIONAL (obligatorio) - add_argument("--nombre") — argumento OPCIONAL (con guiones) - required=True — hace que un argumento --nombre sea obligatorio - type=int / type=float — convierte el input a ese tipo automáticamente - default=valor — valor si el usuario no pasa el argumento - choices=["a","b"] — solo permite esos valores exactos - help="texto" — descripción en el --help - add_subparsers() — crea un gestor de subcomandos - subparsers.add_parser("cmd") — define un subcomando - args = parser.parse_args() — procesa los argumentos del usuario - args.nombre — accede al valor de --nombre -""" - -import argparse - -# Base de datos de festivos (simplificada para el ejemplo) -FESTIVOS = { - "mx": { - 2025: [ - "2025-01-01: Año Nuevo", - "2025-02-03: Día de la Constitución", - "2025-03-17: Natalicio de Benito Juárez", - "2025-05-01: Día del Trabajo", - "2025-09-16: Día de la Independencia", - "2025-11-17: Día de la Revolución", - "2025-12-25: Navidad", - ] - }, - "co": { - 2025: [ - "2025-01-01: Año Nuevo", - "2025-01-06: Reyes Magos", - "2025-05-01: Día del Trabajo", - "2025-07-20: Día de la Independencia", - "2025-08-07: Batalla de Boyacá", - "2025-12-25: Navidad", - ] - }, -} - - -def cmd_listar(args): - """Subcomando: listar días festivos de un país y año.""" - pais = args.pais.lower() - año = args.año - - if pais not in FESTIVOS: - print(f"Error: País '{pais}' no disponible. Opciones: {list(FESTIVOS.keys())}") - return - - if año not in FESTIVOS[pais]: - print(f"No hay datos para {pais.upper()} en {año}.") - return - - print(f"\n🗓 Festivos de {pais.upper()} — {año}:") - for festivo in FESTIVOS[pais][año]: - print(f" • {festivo}") - print(f"\nTotal: {len(FESTIVOS[pais][año])} días festivos") - - -def cmd_contar(args): - """Subcomando: contar festivos (sin mostrarlos).""" - pais = args.pais.lower() - año = args.año - - if pais not in FESTIVOS or año not in FESTIVOS[pais]: - print(f"No hay datos para {pais.upper()} {año}.") - return - - total = len(FESTIVOS[pais][año]) - print(f"{pais.upper()} tiene {total} días festivos en {año}.") - - -def main(): - # 1. Parser principal - parser = argparse.ArgumentParser( - description="Consulta días festivos por país y año.", - epilog="Ejemplo de uso: python festivos.py listar --pais mx --año 2025", - ) - - # 2. Gestor de subcomandos - subparsers = parser.add_subparsers( - dest="subcomando", - help="Subcomando a ejecutar", - ) - - # --- Subcomando: listar --- - sp_listar = subparsers.add_parser("listar", help="Lista todos los festivos") - sp_listar.add_argument("--pais", default="mx", help="Código del país (default: mx)") - sp_listar.add_argument("--año", type=int, default=2025, help="Año a consultar") - - # --- Subcomando: contar --- - sp_contar = subparsers.add_parser("contar", help="Cuenta cuántos festivos hay") - sp_contar.add_argument("--pais", default="mx") - sp_contar.add_argument("--año", type=int, default=2025) - - # 3. Parsear los argumentos - args = parser.parse_args() - - # 4. Ejecutar el subcomando correcto - if args.subcomando == "listar": - cmd_listar(args) - elif args.subcomando == "contar": - cmd_contar(args) - else: - # Si no pasan subcomando, mostrar ayuda - parser.print_help() - - -if __name__ == "__main__": - main() - -# Ejemplos de uso en terminal: -# python festivos.py listar -# python festivos.py listar --pais co --año 2025 -# python festivos.py contar --pais mx -# python festivos.py --help -# python festivos.py listar --help diff --git a/archivo-historico/ejemplos/general/15-shutil/README.md b/archivo-historico/ejemplos/general/15-shutil/README.md deleted file mode 100644 index 60bf460..0000000 --- a/archivo-historico/ejemplos/general/15-shutil/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Operaciones de Archivos con shutil - -## ¿Qué muestra este ejemplo? -Muestra cómo usar la librería estándar `shutil` (utilidades de shell) para realizar operaciones de alto nivel sobre archivos y directorios, específicamente cómo empaquetar una carpeta completa en un archivo `.zip` usando `make_archive`. - -## ¿Por qué es útil? -Mientras que la librería `os` o `pathlib` te sirven para interactuar con un solo archivo (leerlo, borrarlo), `shutil` está diseñado para operaciones masivas: copiar árboles completos de directorios, mover carpetas de un disco a otro, o comprimir proyectos enteros en una sola línea. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_shutil.py -``` -Al finalizar, verás un nuevo archivo llamado `mi_backup.zip`. - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #29 en GitHub: Python General 15 - Herramienta de Backup de Archivos](https://github.com/Kaia-Alenia/nerve-community/issues/29)** - -## Nota para principiantes -El módulo `shutil` tiene muchas funciones útiles como `shutil.copy()` y `shutil.move()`. Son las equivalentes a usar los comandos `cp` y `mv` en una terminal de Linux o Mac, pero directamente desde Python. diff --git a/archivo-historico/ejemplos/general/15-shutil/ejemplo_shutil.py b/archivo-historico/ejemplos/general/15-shutil/ejemplo_shutil.py deleted file mode 100644 index 6916312..0000000 --- a/archivo-historico/ejemplos/general/15-shutil/ejemplo_shutil.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -Ejemplo: Comprimir carpetas con filtro usando shutil.make_archive + pathlib - -Qué enseña este ejemplo: - - Cómo comprimir una carpeta completa con shutil.make_archive() - - Cómo copiar solo archivos que cumplan un criterio (filtro por extensión) - - Cómo usar pathlib.rglob() para buscar archivos de forma recursiva - - Cómo dar nombre al backup con la fecha actual - -Para tu reto (herramienta de backup): - Aplica el mismo patrón: shutil.copy2() para copiar archivos con metadatos, - y agrega la fecha al nombre para no sobreescribir backups anteriores. - Usa pathlib para manejar las rutas. - -Glosario: - shutil.make_archive(nombre, formato, carpeta) - → Comprime 'carpeta' en un archivo 'nombre.formato' - → Formatos disponibles: "zip", "tar", "gztar" (tar.gz), "bztar", "xztar" - → Devuelve la ruta del archivo creado - - shutil.copy2(origen, destino) - → Copia un archivo preservando metadatos (fecha de modificación, permisos) - → shutil.copy() solo copia contenido; copy2() también copia metadatos - - pathlib.rglob("*.ext") - → Busca archivos recursivamente en TODAS las subcarpetas - → rglob("*") → todos los archivos y carpetas - → rglob("*.py") → solo archivos .py en cualquier profundidad - → glob("*.py") → solo en la carpeta actual (no recursivo) - - datetime.now().strftime("%Y-%m-%d") - → Devuelve la fecha de hoy como string: "2025-07-17" - → Útil para nombrar backups: "backup_2025-07-17.zip" -""" - -import shutil -import datetime -from pathlib import Path - - -def hacer_backup_completo(carpeta_origen: str, carpeta_destino: str) -> str: - """ - Comprime toda la carpeta origen en un .zip con fecha en el nombre. - Devuelve la ruta del archivo creado. - """ - origen = Path(carpeta_origen) - destino = Path(carpeta_destino) - - if not origen.exists(): - raise FileNotFoundError(f"No existe la carpeta: {carpeta_origen}") - - # Crear la carpeta destino si no existe - destino.mkdir(parents=True, exist_ok=True) - - # Nombre del backup con fecha: "documentos_2025-07-17" - fecha = datetime.datetime.now().strftime("%Y-%m-%d") - nombre_backup = f"{origen.name}_{fecha}" - ruta_salida = destino / nombre_backup - - print(f"Comprimiendo '{origen}' → '{ruta_salida}.zip'...") - - # shutil.make_archive devuelve la ruta del archivo creado (con extensión) - archivo_creado = shutil.make_archive( - str(ruta_salida), # nombre de salida (sin extensión) - "zip", # formato de compresión - str(origen), # carpeta a comprimir - ) - - tamaño_kb = Path(archivo_creado).stat().st_size / 1024 - print(f"✓ Backup creado: {archivo_creado} ({tamaño_kb:.1f} KB)") - return archivo_creado - - -def hacer_backup_filtrado(carpeta_origen: str, carpeta_destino: str, extensiones: list): - """ - Copia solo los archivos con las extensiones indicadas, - respetando la estructura de subcarpetas. - - rglob() permite buscar de forma recursiva en todas las subcarpetas. - """ - origen = Path(carpeta_origen) - destino = Path(carpeta_destino) - - if not origen.exists(): - raise FileNotFoundError(f"No existe la carpeta: {carpeta_origen}") - - destino.mkdir(parents=True, exist_ok=True) - copiados = 0 - - # rglob("*") → busca TODOS los archivos en origen y subcarpetas - for archivo in origen.rglob("*"): - if not archivo.is_file(): - continue - - # Filtrar por extensión - if archivo.suffix.lower() not in extensiones: - continue - - # Reconstruir la ruta relativa para mantener estructura de carpetas - ruta_relativa = archivo.relative_to(origen) - archivo_destino = destino / ruta_relativa - - # Crear subcarpetas intermedias si son necesarias - archivo_destino.parent.mkdir(parents=True, exist_ok=True) - - # copy2 preserva fecha de modificación y permisos (más completo que copy) - shutil.copy2(archivo, archivo_destino) - print(f" ✓ {ruta_relativa}") - copiados += 1 - - print(f"\nBackup filtrado completado: {copiados} archivo(s) copiado(s)") - - -if __name__ == "__main__": - import tempfile - import os - - # Crear carpeta de prueba con archivos de distintos tipos - with tempfile.TemporaryDirectory() as tmp_origen: - origen = Path(tmp_origen) - - # Crear estructura de ejemplo - (origen / "docs").mkdir() - (origen / "scripts").mkdir() - (origen / "README.md").write_text("# Mi Proyecto") - (origen / "docs" / "manual.pdf").write_bytes(b"contenido pdf") - (origen / "docs" / "notas.txt").write_text("Notas importantes") - (origen / "scripts" / "util.py").write_text("print('hola')") - (origen / "imagen.jpg").write_bytes(b"datos imagen") - - print("Archivos de prueba:") - for f in origen.rglob("*"): - if f.is_file(): - print(f" {f.relative_to(origen)}") - - with tempfile.TemporaryDirectory() as tmp_destino: - # Demo 1: backup completo en zip - print("\n--- Demo 1: Backup completo (.zip) ---") - hacer_backup_completo(tmp_origen, tmp_destino) - - # Demo 2: backup filtrado (solo .txt y .md) - print("\n--- Demo 2: Solo archivos .txt y .md ---") - destino_filtrado = os.path.join(tmp_destino, "filtrado") - hacer_backup_filtrado(tmp_origen, destino_filtrado, [".txt", ".md"]) diff --git a/archivo-historico/ejemplos/general/16-excepciones/README.md b/archivo-historico/ejemplos/general/16-excepciones/README.md deleted file mode 100644 index aad447f..0000000 --- a/archivo-historico/ejemplos/general/16-excepciones/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Excepciones Personalizadas - -## ¿Qué muestra este ejemplo? -Muestra cómo crear una excepción propia heredando de la clase base `Exception` en Python, y cómo levantarla (`raise`) y atraparla (`except`) en un bloque `try/except`. - -## ¿Por qué es útil? -A veces los errores genéricos de Python (`ValueError`, `TypeError`) no describen bien el problema en la lógica de tu negocio. Crear tus propias excepciones (ej. `UsuarioNoEncontradoError`, `ConexionPerdidaError`) hace que tu código sea mucho más legible, y permite que otros programadores atrapen exactamente el error que diseñaste sin confundirlo con errores generales. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_excepciones.py -``` - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #30 en GitHub: Python General 16 - Validador de Edad (Excepciones Personalizadas)](https://github.com/Kaia-Alenia/nerve-community/issues/30)** - -## Nota para principiantes -Crear una excepción personalizada es extremadamente sencillo: solo creas una clase nueva, la haces heredar de `Exception`, y pones `pass` adentro. Python se encarga de que todo lo demás funcione (como pasarle un mensaje de error). diff --git a/archivo-historico/ejemplos/general/16-excepciones/ejemplo_excepciones.py b/archivo-historico/ejemplos/general/16-excepciones/ejemplo_excepciones.py deleted file mode 100644 index 233c511..0000000 --- a/archivo-historico/ejemplos/general/16-excepciones/ejemplo_excepciones.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -Ejemplo: Excepciones personalizadas con herencia — validación de edad - -Qué enseña este ejemplo: - - Cómo crear excepciones propias heredando de Exception - - Por qué crear excepciones específicas en vez de usar ValueError genérico - - Cómo usar raise para lanzar una excepción manualmente - - Cómo capturar excepciones específicas con except en orden de más a menos específico - -Para tu reto (validador de edad): - Este ejemplo es directamente aplicable. Crea tus excepciones - EdadNegativaError y EdadNoNumericaError heredando de Exception, - y lánzalas con raise en la función que valida la edad. - -Glosario: - class MiError(Exception) — crea una excepción personalizada. - Exception es la clase base de todos los errores en Python. - Al heredar de ella, tu clase "es" un error que Python puede capturar. - - raise MiError("mensaje") — lanza la excepción manualmente. - Detiene la ejecución y busca el except más cercano que la maneje. - - try / except / else / finally: - try: — bloque que puede fallar - except ErrorEspecifico: — captura ese tipo de error específico - except Exception: — captura CUALQUIER error (úsalo como último recurso) - else: — se ejecuta SOLO si no hubo excepciones - finally: — se ejecuta SIEMPRE (con o sin error) — útil para limpiar recursos - - Orden de except: de más específico a más general. - Si pones except Exception primero, nunca llegará a los except específicos. -""" - - -# --- Definición de excepciones personalizadas --- -class EdadInvalidaError(Exception): - """ - Clase base para todos los errores de edad. - Heredar de ella permite capturar TODOS los errores de edad con un solo except. - """ - - pass - - -class EdadNegativaError(EdadInvalidaError): - """Se lanza cuando la edad es un número negativo.""" - - pass - - -class EdadExcesivaError(EdadInvalidaError): - """Se lanza cuando la edad supera un máximo razonable.""" - - MAXIMO = 150 - - def __init__(self, edad_recibida: int): - # Podemos personalizar el mensaje de error en __init__ - super().__init__( - f"Edad {edad_recibida} supera el máximo posible de {self.MAXIMO} años." - ) - self.edad_recibida = edad_recibida - - -class EdadNoNumericaError(EdadInvalidaError): - """Se lanza cuando el input no es un número.""" - - pass - - -# --- Función que valida y lanza las excepciones --- -def validar_edad(entrada: str) -> int: - """ - Recibe un string (como vendrá de input()), valida la edad y la devuelve. - Lanza una excepción específica según el tipo de error. - """ - # Intentar convertir a entero - try: - edad = int(entrada) - except ValueError: - raise EdadNoNumericaError(f"'{entrada}' no es un número entero válido.") - - if edad < 0: - raise EdadNegativaError(f"La edad no puede ser negativa. Recibido: {edad}") - - if edad > EdadExcesivaError.MAXIMO: - raise EdadExcesivaError(edad) - - return edad - - -# --- Uso con try/except --- -def procesar_registro(nombre: str, edad_str: str): - """ - Procesa el registro de una persona, manejando cada tipo de error. - """ - print(f"\nProcesando: nombre='{nombre}', edad='{edad_str}'") - try: - edad = validar_edad(edad_str) - print(f" ✓ Registro válido: {nombre}, {edad} años") - - except EdadNoNumericaError as e: - # Más específico primero - print(f" ✗ Error de formato: {e}") - - except EdadNegativaError as e: - print(f" ✗ Edad negativa: {e}") - - except EdadExcesivaError as e: - print(f" ✗ Edad excesiva: {e}") - print(f" Valor recibido fue: {e.edad_recibida}") - - except EdadInvalidaError as e: - # Captura CUALQUIER EdadInvalidaError no capturado arriba - print(f" ✗ Error de edad genérico: {e}") - - -if __name__ == "__main__": - print("=== Demo de excepciones personalizadas ===") - - casos = [ - ("María", "28"), # Válido - ("Carlos", "-5"), # EdadNegativaError - ("Luis", "abc"), # EdadNoNumericaError - ("Ana", "200"), # EdadExcesivaError - ("Sofía", "0"), # Válido (recién nacida) - ("Pedro", "150"), # EdadExcesivaError (exactamente el límite) - ("Juan", "3.5"), # EdadNoNumericaError (float no es int) - ] - - for nombre, edad_str in casos: - procesar_registro(nombre, edad_str) diff --git a/archivo-historico/ejemplos/general/17-json/README.md b/archivo-historico/ejemplos/general/17-json/README.md deleted file mode 100644 index 8bdea5e..0000000 --- a/archivo-historico/ejemplos/general/17-json/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Manejo de Archivos JSON - -## ¿Qué muestra este ejemplo? -Muestra cómo utilizar la librería `json` integrada en Python para guardar (serializar) un diccionario en un archivo de texto, y posteriormente leer (deserializar) ese archivo de vuelta a un diccionario de Python. - -## ¿Por qué es útil? -JSON (JavaScript Object Notation) es el formato de intercambio de datos más usado en la web. Es perfecto para guardar configuraciones de programas, estado de un juego, o para mandar información a APIs externas. Python traduce diccionarios a JSON de manera casi transparente. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_json.py -``` -Pruébalo un par de veces consecutivas. Verás cómo el puntaje guardado es recordado en la siguiente ejecución. ¡Abre el archivo `score.json` generado para ver cómo luce! - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #31 en GitHub: Python General 17 - Lector de Configuraciones (JSON/YAML)](https://github.com/Kaia-Alenia/nerve-community/issues/31)** - -## Nota para principiantes -Fíjate siempre en la diferencia entre `json.dump()` (que escribe directamente en un archivo) y `json.dumps()` (que devuelve un *string* de JSON sin escribir nada). Lo mismo aplica para `load()` vs `loads()`. La letra "s" al final significa "string". diff --git a/archivo-historico/ejemplos/general/17-json/ejemplo_json.py b/archivo-historico/ejemplos/general/17-json/ejemplo_json.py deleted file mode 100644 index fa6a8ef..0000000 --- a/archivo-historico/ejemplos/general/17-json/ejemplo_json.py +++ /dev/null @@ -1,142 +0,0 @@ -""" -Ejemplo: Leer configuraciones JSON/YAML con manejo de claves faltantes - -Qué enseña este ejemplo: - - Cómo leer un archivo JSON con json.load() + pathlib - - Cómo manejar claves faltantes con .get() y KeyError - - Cómo detectar automáticamente el formato del archivo (.json o .yaml) - - Cómo devolver valores por defecto cuando el archivo no existe - -Para tu reto (lector de configuraciones): - Aplica el mismo patrón: detecta la extensión, usa json.load o yaml.safe_load, - y maneja los casos de archivo inexistente o claves faltantes con valores por defecto. - -Glosario: - json.load(archivo) — lee un archivo y devuelve dict/list de Python - json.dump(datos, archivo, indent=4) — escribe dict/list como JSON en un archivo - yaml.safe_load(archivo)— lee un archivo YAML y devuelve dict de Python - (usar safe_load, NO yaml.load — es más seguro) - dict.get(clave, default) — devuelve el valor de la clave, o 'default' si no existe - Nunca lanza KeyError (a diferencia de dict[clave]) - dict.get("a", {}).get("b", valor) — acceso seguro a claves anidadas - pathlib.Path.suffix — extensión del archivo: ".json", ".yaml", ".yml" - KeyError — error que lanza Python al acceder a una clave inexistente - con dict[clave] (en vez de dict.get(clave)) -""" - -import json -from pathlib import Path - -# pyyaml es un paquete externo: pip install pyyaml -# Solo se importa si el archivo es .yaml — no es obligatorio instalarlo -try: - import yaml - - YAML_DISPONIBLE = True -except ImportError: - YAML_DISPONIBLE = False - - -# Configuración por defecto (usada si el archivo no existe o faltan claves) -CONFIG_POR_DEFECTO = { - "base_url": "http://localhost:8000", - "timeout_seg": 30, - "debug": False, - "base_de_datos": { - "host": "localhost", - "puerto": 5432, - "nombre": "mi_app", - }, -} - - -def cargar_config(ruta: str) -> dict: - """ - Carga una configuración desde un archivo .json o .yaml. - Si el archivo no existe, devuelve la configuración por defecto. - Si faltan claves, las rellena con los valores por defecto. - """ - archivo = Path(ruta) - - if not archivo.exists(): - print(f"Archivo '{ruta}' no encontrado. Usando configuración por defecto.") - return CONFIG_POR_DEFECTO.copy() - - extension = archivo.suffix.lower() - - try: - with open(archivo, "r", encoding="utf-8") as f: - if extension == ".json": - datos = json.load(f) - elif extension in (".yaml", ".yml"): - if not YAML_DISPONIBLE: - print("pyyaml no instalado. Instala con: pip install pyyaml") - return CONFIG_POR_DEFECTO.copy() - datos = yaml.safe_load(f) - else: - print(f"Formato '{extension}' no soportado.") - return CONFIG_POR_DEFECTO.copy() - - # Combinar con los defaults: claves faltantes usan el valor por defecto - config_completa = CONFIG_POR_DEFECTO.copy() - config_completa.update(datos) - return config_completa - - except json.JSONDecodeError as e: - print(f"Error al parsear JSON: {e}") - return CONFIG_POR_DEFECTO.copy() - - -def acceder_configuracion(config: dict): - """ - Demuestra cómo acceder a claves de forma segura. - """ - print("\n--- Configuración cargada ---") - - # dict.get(clave, default) → NUNCA lanza KeyError - url = config.get("base_url", "sin-url") - timeout = config.get("timeout_seg", 30) - debug = config.get("debug", False) - - print(f" URL: {url}") - print(f" Timeout: {timeout}s") - print(f" Debug: {debug}") - - # Acceso anidado seguro con .get() encadenado - db = config.get("base_de_datos", {}) - host_db = db.get("host", "desconocido") - puerto_db = db.get("puerto", 5432) - - print(f" Base de datos: {host_db}:{puerto_db}") - - # Contraste: acceso con [] → lanza KeyError si no existe - try: - _ = config["clave_que_no_existe"] - except KeyError as e: - print(f"\n (Ejemplo de KeyError al usar config[{e}])") - print(" Solución: usar config.get('clave_que_no_existe', valor_defecto)") - - -if __name__ == "__main__": - # Demo 1: archivo inexistente → defaults - print("=== Demo 1: Archivo inexistente ===") - config1 = cargar_config("no_existe.json") - acceder_configuracion(config1) - - # Demo 2: crear un JSON parcial y cargarlo - print("\n=== Demo 2: JSON con solo algunas claves ===") - config_parcial = { - "base_url": "https://api.miempresa.com", - "debug": True, - # timeout_seg y base_de_datos faltan → usarán los defaults - } - with open("config_ejemplo.json", "w", encoding="utf-8") as f: - json.dump(config_parcial, f, indent=4) - print("Archivo 'config_ejemplo.json' creado con configuración parcial.") - - config2 = cargar_config("config_ejemplo.json") - acceder_configuracion(config2) - - # Limpiar - Path("config_ejemplo.json").unlink() - print("\n✓ Archivo de prueba eliminado.") diff --git a/archivo-historico/ejemplos/general/18-poo-avanzada/README.md b/archivo-historico/ejemplos/general/18-poo-avanzada/README.md deleted file mode 100644 index 3d4935b..0000000 --- a/archivo-historico/ejemplos/general/18-poo-avanzada/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# POO Avanzada: Propiedades Calculadas - -## ¿Qué muestra este ejemplo? -Muestra cómo usar el decorador `@property` en la Programación Orientada a Objetos (POO) para crear atributos computados o propiedades de "solo lectura". - -## ¿Por qué es útil? -A veces necesitas que un objeto exponga un dato que se arma a partir de otros (como juntar nombre y apellido, o resumir un libro). Usando `@property`, logras que el usuario de la clase pueda consultar el dato simplemente leyendo `objeto.resumen` (sin usar paréntesis), ocultando el cálculo interno. Además, al no definir un *setter*, garantizas que nadie desde fuera pueda corromper ese dato sobreescribiéndolo directamente. - -## ¿Cómo correrlo? -Abre tu terminal, navega a esta carpeta y ejecuta: -```bash -python ejemplo_propiedades.py -``` - -## ¿Qué retos usan esta base? -- 👉 **[Ver Issue #32 en GitHub: Python General 18 - Inventario de Tienda (POO Avanzada)](https://github.com/Kaia-Alenia/nerve-community/issues/32)** - -## Nota para principiantes -El decorador `@property` transforma un método en un atributo virtual. Esta es la forma "Pythónica" de manejar getters y setters. Si luego necesitas permitir que el valor cambie, agregarás un decorador secundario llamado `@nombre_de_propiedad.setter`. diff --git a/archivo-historico/ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py b/archivo-historico/ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py deleted file mode 100644 index 5e15e32..0000000 --- a/archivo-historico/ejemplos/general/18-poo-avanzada/ejemplo_propiedades.py +++ /dev/null @@ -1,143 +0,0 @@ -""" -Ejemplo: POO avanzada — @property con setter y validación + __repr__ - -Qué enseña este ejemplo: - - Cómo usar @property para acceso controlado a atributos - - Cómo usar @nombre.setter para validar el valor al asignar - - Cómo implementar __repr__ para que tu objeto sea legible en debugging - - Diferencia entre atributo público, privado (_) y name-mangled (__) - -Para tu reto (inventario de tienda): - Aplica el mismo patrón para la clase Producto: - @property precio → devuelve self._precio - @precio.setter → valida que sea positivo antes de guardar - Y para Inventario: - @property valor_total → calcula la suma al momento de pedirlo - -Glosario: - @property — convierte un método en un "atributo de solo lectura" - se accede sin paréntesis: objeto.precio (no objeto.precio()) - - @nombre.setter — complemento de @property que permite asignar con validación - se activa cuando escribes: objeto.precio = 100 - Si no defines setter, el atributo es de solo lectura. - - __repr__(self) — devuelve una representación oficial del objeto. - Se muestra en el REPL de Python, en listas, en f-strings, etc. - Convención: debe devolver algo que se pueda evaluar de nuevo. - Ejemplo: "Producto(nombre='Leche', precio=25.0, stock=50)" - - __str__(self) — representación amigable para el usuario (print()) - Si no está definido, Python usa __repr__ - - Atributos privados por convención: - _nombre — convención: no tocar desde afuera (pero Python no lo impide) - __nombre — name mangling: Python lo renombra a _Clase__nombre internamente - (sí previene acceso accidental desde subclases) -""" - - -class CuentaBancaria: - """ - Cuenta bancaria con @property para controlar el saldo. - Equivale al rol de Inventario con su valor_total en tu reto. - """ - - def __init__(self, titular: str, saldo_inicial: float = 0.0): - self.titular = titular - # Usamos _saldo (con _) para indicar que es "privado por convención" - self._saldo = 0.0 - # Usamos el setter para validar incluso en __init__ - self.saldo = saldo_inicial - - @property - def saldo(self) -> float: - """ - Propiedad de lectura: acceder con cuenta.saldo (sin paréntesis). - Permite hacer cálculos o transformaciones antes de devolver. - """ - return round(self._saldo, 2) - - @saldo.setter - def saldo(self, nuevo_valor: float): - """ - Setter: se activa con cuenta.saldo = 500. - Valida el valor ANTES de guardarlo. - """ - if not isinstance(nuevo_valor, (int, float)): - raise TypeError( - f"El saldo debe ser un número, no {type(nuevo_valor).__name__}" - ) - if nuevo_valor < 0: - raise ValueError(f"El saldo no puede ser negativo: {nuevo_valor}") - self._saldo = float(nuevo_valor) - - @property - def tiene_fondos(self) -> bool: - """Propiedad calculada de solo lectura (sin setter).""" - return self._saldo > 0 - - def depositar(self, cantidad: float): - if cantidad <= 0: - raise ValueError("La cantidad a depositar debe ser positiva.") - self.saldo = self._saldo + cantidad - print(f" ✓ Depósito de ${cantidad:.2f}. Nuevo saldo: ${self.saldo:.2f}") - - def retirar(self, cantidad: float): - if cantidad <= 0: - raise ValueError("La cantidad a retirar debe ser positiva.") - if cantidad > self._saldo: - raise ValueError(f"Fondos insuficientes. Saldo: ${self.saldo:.2f}") - self.saldo = self._saldo - cantidad - print(f" ✓ Retiro de ${cantidad:.2f}. Nuevo saldo: ${self.saldo:.2f}") - - def __repr__(self) -> str: - """ - Representación oficial: aparece en el REPL, en listas, en f-strings. - Intenta que sea evaluable: CuentaBancaria("Kaia", 1000.0) - """ - return f"CuentaBancaria(titular='{self.titular}', saldo={self.saldo})" - - def __str__(self) -> str: - """Representación amigable para print().""" - estado = "con fondos" if self.tiene_fondos else "sin fondos" - return f"Cuenta de {self.titular}: ${self.saldo:.2f} ({estado})" - - -if __name__ == "__main__": - print("=== Demo de @property y @setter ===\n") - - # Crear cuenta con validación en __init__ (pasa por el setter) - cuenta = CuentaBancaria("Kaia", saldo_inicial=1000.0) - - print(f"Cuenta creada: {cuenta}") # usa __str__ - print(f"repr: {repr(cuenta)}\n") # usa __repr__ - - # Acceso a property (sin paréntesis) - print(f"Saldo actual: ${cuenta.saldo}") - print(f"Tiene fondos: {cuenta.tiene_fondos}\n") - - # Operaciones - cuenta.depositar(500) - cuenta.retirar(200) - - # El setter valida antes de guardar - print("\n--- Pruebas de validación en el setter ---") - - try: - cuenta.saldo = -100 # Debe rechazarlo - except ValueError as e: - print(f" ✓ Error esperado: {e}") - - try: - cuenta.saldo = "mucho" # Debe rechazarlo - except TypeError as e: - print(f" ✓ Error esperado: {e}") - - # Sin setter: @property de solo lectura - try: - cuenta.tiene_fondos = True # No tiene setter → AttributeError - except AttributeError as e: - print(f" ✓ Error esperado (sin setter): {e}") - - print(f"\nEstado final: {cuenta}") diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/README.md b/archivo-historico/ejemplos/nerve/01-hola-nerve/README.md deleted file mode 100644 index 2fe9460..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# 01-hola-nerve - -## Propósito -Este es el ejemplo más básico ("Hello World") para entender cómo funciona la comunicación con `alenia-nerve`. Demuestra el patrón de Envío Directo (Point-to-Point). - -## 💡 Utilidad -- Enseña cómo un emisor puede enviar un mensaje directamente a un receptor específico usando el método `send()`. -- Muestra cómo un receptor puede usar `listen()` para quedarse a la escucha de mensajes. - -## ⚙️ Instalación / Requisitos -1. Asegúrate de tener el Hub encendido en una terminal: `nerve start` -2. Abre una terminal y corre el receptor: `python receptor.py` -3. Abre otra terminal y corre el emisor: `python emisor.py` - -## 🔗 Retos Relacionados -- [Reto 01 — Chat de terminal](https://github.com/Kaia-Alenia/nerve-community/issues/1) -- [Reto 03 — Traductor de mensajes](https://github.com/Kaia-Alenia/nerve-community/issues/3) - -## 🛡️ Buenas Prácticas -- Siempre inicializa el cliente de Nerve con un nombre de cliente único para evitar colisiones. -- Utiliza estructuras simples para los mensajes iniciales al explorar la API de `alenia-nerve`. diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.go b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.go deleted file mode 100644 index d123e35..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "nerve" -) - -func main() { - fmt.Println("Iniciando emisor...") - // 1. Crear el cliente de Nerve - cliente := nerve.NewNexusClient() - - // 2. Conectarse al Hub con un identificador único - cliente.Connect("emisor_01") - fmt.Println("✅ Emisor conectado al Nerve Hub.") - - // Esperamos un momento para asegurar que el receptor esté listo - time.Sleep(1 * time.Second) - - // 3. Preparar el mensaje (payload) y enviarlo al receptor - mensaje := map[string]interface{}{ - "texto": "¡Hola, Nerve!", - "timestamp": time.Now().Unix(), - } - - fmt.Println("Enviando mensaje a 'receptor_01'...") - cliente.Send("receptor_01", mensaje) - - fmt.Println("Mensaje enviado. Terminando emisor...") - // Desconectamos el cliente para liberar recursos - cliente.Disconnect() -} diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.js b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.js deleted file mode 100644 index 845c9af..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.js +++ /dev/null @@ -1,28 +0,0 @@ -const { NexusClient } = require('nerve'); - -async function main() { - console.log("Iniciando emisor..."); - // 1. Crear el cliente de Nerve - const cliente = new NexusClient(); - - // 2. Conectarse al Hub con un identificador único - await cliente.connect("emisor_01"); - console.log("✅ Emisor conectado al Nerve Hub."); - - // Esperamos un momento para asegurar que el receptor esté listo - await new Promise(r => setTimeout(r, 1000)); - - // 3. Preparar el mensaje (payload) y enviarlo al receptor - const mensaje = { texto: "¡Hola, Nerve!", timestamp: Date.now() }; - - console.log("Enviando mensaje a 'receptor_01'..."); - cliente.send("receptor_01", mensaje); - - console.log("Mensaje enviado. Terminando emisor..."); - // Desconectamos el cliente para liberar recursos - cliente.disconnect(); -} - -if (require.main === module) { - main(); -} diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.py b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.py deleted file mode 100644 index 9522a86..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.py +++ /dev/null @@ -1,29 +0,0 @@ -import time -from nerve import NexusClient - - -def main(): - print("Iniciando emisor...") - # 1. Crear el cliente de Nerve - cliente = NexusClient() - - # 2. Conectarse al Hub con un identificador único - cliente.connect("emisor_01") - print("✅ Emisor conectado al Nerve Hub.") - - # Esperamos un momento para asegurar que el receptor esté listo - time.sleep(1) - - # 3. Preparar el mensaje (payload) y enviarlo al receptor - mensaje = {"texto": "¡Hola, Nerve!", "timestamp": time.time()} - - print("Enviando mensaje a 'receptor_01'...") - cliente.send("receptor_01", mensaje) - - print("Mensaje enviado. Terminando emisor...") - # Desconectamos el cliente para liberar recursos - cliente.disconnect() - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.rs b/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.rs deleted file mode 100644 index cd81f03..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/emisor.rs +++ /dev/null @@ -1,33 +0,0 @@ -use nerve::NexusClient; -use serde_json::json; -use std::time::Duration; -use std::thread; - -fn main() { - println!("Iniciando emisor..."); - // 1. Crear el cliente de Nerve - let mut cliente = NexusClient::new(); - - // 2. Conectarse al Hub con un identificador único - cliente.connect("emisor_01").expect("Error al conectar"); - println!("✅ Emisor conectado al Nerve Hub."); - - // Esperamos un momento para asegurar que el receptor esté listo - thread::sleep(Duration::from_secs(1)); - - // 3. Preparar el mensaje (payload) y enviarlo al receptor - let mensaje = json!({ - "texto": "¡Hola, Nerve!", - "timestamp": std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() - }); - - println!("Enviando mensaje a 'receptor_01'..."); - cliente.send("receptor_01", mensaje).expect("Error al enviar"); - - println!("Mensaje enviado. Terminando emisor..."); - // Desconectamos el cliente para liberar recursos - cliente.disconnect(); -} diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.go b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.go deleted file mode 100644 index f774e0c..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/signal" - "syscall" - - "nerve" -) - -func procesarMensaje(mensajeCrudo nerve.Message) { - fmt.Printf(" ¡Mensaje recibido!: %+v\n", mensajeCrudo) - - payload, ok := mensajeCrudo.Payload.(map[string]interface{}) - if ok { - if texto, ok := payload["texto"].(string); ok { - fmt.Printf("Texto extraído: %s\n", texto) - } - } -} - -func main() { - fmt.Println("Iniciando receptor...") - cliente := nerve.NewNexusClient() - - // Nos conectamos como "receptor_01" - cliente.Connect("receptor_01") - fmt.Println("✅ Receptor conectado. Esperando mensajes...") - - // Registramos la función que se llamará al recibir mensajes - cliente.Listen(procesarMensaje) - - // Mantenemos el programa vivo para escuchar - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - <-c - - fmt.Println("\nSaliendo...") - cliente.Disconnect() -} diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.js b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.js deleted file mode 100644 index 3945d11..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.js +++ /dev/null @@ -1,35 +0,0 @@ -const { NexusClient } = require('nerve'); - -function procesarMensaje(mensajeCrudo) { - // El servidor nos entrega el mensaje completo. Lo extraemos. - console.log(` ¡Mensaje recibido!: ${JSON.stringify(mensajeCrudo)}`); - - // Podemos extraer el payload original si queremos - const payload = mensajeCrudo.payload || {}; - if (payload.texto) { - console.log(`Texto extraído: ${payload.texto}`); - } -} - -async function main() { - console.log("Iniciando receptor..."); - const cliente = new NexusClient(); - - // Nos conectamos como "receptor_01" - await cliente.connect("receptor_01"); - console.log("✅ Receptor conectado. Esperando mensajes..."); - - // Registramos la función que se llamará al recibir mensajes - cliente.listen(procesarMensaje); - - // Mantenemos el programa vivo para escuchar - process.on('SIGINT', () => { - console.log("\nSaliendo..."); - cliente.disconnect(); - process.exit(); - }); -} - -if (require.main === module) { - main(); -} diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.py b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.py deleted file mode 100644 index 4015706..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.py +++ /dev/null @@ -1,36 +0,0 @@ -import time -from nerve import NexusClient - - -def procesar_mensaje(mensaje_crudo): - # El servidor nos entrega el mensaje completo. Lo extraemos. - print(f" ¡Mensaje recibido!: {mensaje_crudo}") - - # Podemos extraer el payload original si queremos - payload = mensaje_crudo.get("payload", {}) - if "texto" in payload: - print(f"Texto extraído: {payload['texto']}") - - -def main(): - print("Iniciando receptor...") - cliente = NexusClient() - - # Nos conectamos como "receptor_01" - cliente.connect("receptor_01") - print("✅ Receptor conectado. Esperando mensajes...") - - # Registramos la función que se llamará al recibir mensajes - cliente.listen(procesar_mensaje) - - try: - # Mantenemos el programa vivo para escuchar - while True: - time.sleep(1) - except KeyboardInterrupt: - print("\nSaliendo...") - cliente.disconnect() - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.rs b/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.rs deleted file mode 100644 index 5940412..0000000 --- a/archivo-historico/ejemplos/nerve/01-hola-nerve/receptor.rs +++ /dev/null @@ -1,35 +0,0 @@ -use nerve::{NexusClient, Message}; -use std::sync::mpsc; - -fn procesar_mensaje(mensaje_crudo: Message) { - println!(" ¡Mensaje recibido!: {:?}", mensaje_crudo); - - if let Some(payload) = mensaje_crudo.payload { - if let Some(texto) = payload.get("texto") { - println!("Texto extraído: {}", texto); - } - } -} - -fn main() { - println!("Iniciando receptor..."); - let mut cliente = NexusClient::new(); - - // Nos conectamos como "receptor_01" - cliente.connect("receptor_01").expect("Error al conectar"); - println!("✅ Receptor conectado. Esperando mensajes..."); - - // Registramos la función que se llamará al recibir mensajes - cliente.listen(procesar_mensaje); - - // Mantenemos el programa vivo para escuchar (ctrl+c para salir) - let (tx, rx) = mpsc::channel(); - ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel.")) - .expect("Error setting Ctrl-C handler"); - - println!("Presiona Ctrl+C para salir."); - rx.recv().expect("Could not receive from channel."); - - println!("\nSaliendo..."); - cliente.disconnect(); -} diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/README.md b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/README.md deleted file mode 100644 index 3902f7e..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# 02-pub-sub-basico - -## Propósito -Este ejemplo demuestra cómo usar `broadcast()` para emitir eventos a múltiples clientes de manera simultánea usando el patrón Publish / Subscribe (Pub-Sub). - -## 💡 Utilidad -- A diferencia de `send()`, `broadcast()` reparte el mensaje a todos los clientes conectados. -- Muestra cómo el suscriptor filtra los mensajes que recibe basándose en algún dato del payload. - -## ⚙️ Instalación / Requisitos -1. Asegúrate de tener el Hub encendido: `nerve start` -2. Abre dos terminales para simular a dos personas distintas y ejecuta: - - Terminal 1: `python suscriptor.py usuario_1` - - Terminal 2: `python suscriptor.py usuario_2` -3. Abre una tercera terminal e inicia el publicador: `python publicador.py` - -## 🔗 Retos Relacionados -- [Reto 02 — Reloj sincronizado](https://github.com/Kaia-Alenia/nerve-community/issues/2) - -## 🛡️ Buenas Prácticas -- Usa identificadores en el payload para filtrar mensajes si los clientes solo deben reaccionar a un tipo de transmisión. -- Evita saturar el Hub con `broadcast` si el mensaje es para un destinatario en particular. diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.go b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.go deleted file mode 100644 index 667dc4b..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/signal" - "syscall" - "time" - - "nerve" -) - -func main() { - fmt.Println("Iniciando publicador (estación de radio)...") - cliente := nerve.NewNexusClient() - cliente.Connect("publicador_noticias") - fmt.Println("✅ Publicador conectado al Hub.") - - ticker := time.NewTicker(3 * time.Second) - quit := make(chan os.Signal, 1) - signal.Notify(quit, os.Interrupt, syscall.SIGTERM) - - contador := 1 - - go func() { - for { - select { - case <-ticker.C: - noticia := map[string]interface{}{ - "canal": "noticias_tech", - "titular": fmt.Sprintf("Noticia #%d: Nerve Hub funciona genial", contador), - "timestamp": time.Now().Unix(), - } - - fmt.Printf(" Transmitiendo (broadcast): %s\n", noticia["titular"]) - cliente.Broadcast(noticia) - contador++ - } - } - }() - - <-quit - fmt.Println("\nApagando publicador...") - ticker.Stop() - cliente.Disconnect() -} diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.js b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.js deleted file mode 100644 index 730dd84..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.js +++ /dev/null @@ -1,35 +0,0 @@ -const { NexusClient } = require('nerve'); - -async function main() { - console.log("Iniciando publicador (estación de radio)..."); - const cliente = new NexusClient(); - await cliente.connect("publicador_noticias"); - console.log("✅ Publicador conectado al Hub."); - - let contador = 1; - - // Función para transmitir continuamente - const intervalId = setInterval(() => { - const noticia = { - canal: "noticias_tech", - titular: `Noticia #${contador}: Nerve Hub funciona genial`, - timestamp: Date.now() - }; - - console.log(` Transmitiendo (broadcast): ${noticia.titular}`); - cliente.broadcast(noticia); - - contador++; - }, 3000); - - process.on('SIGINT', () => { - console.log("\nApagando publicador..."); - clearInterval(intervalId); - cliente.disconnect(); - process.exit(); - }); -} - -if (require.main === module) { - main(); -} diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.py b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.py deleted file mode 100644 index 54e116d..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.py +++ /dev/null @@ -1,33 +0,0 @@ -import time -from nerve import NexusClient - - -def main(): - print("Iniciando publicador (estación de radio)...") - cliente = NexusClient() - cliente.connect("publicador_noticias") - print("✅ Publicador conectado al Hub.") - - contador = 1 - try: - while True: - noticia = { - "canal": "noticias_tech", - "titular": f"Noticia #{contador}: Nerve Hub funciona genial", - "timestamp": time.time(), - } - - # Usamos broadcast para enviarlo a todos los nodos conectados - print(f" Transmitiendo (broadcast): {noticia['titular']}") - cliente.broadcast(noticia) - - contador += 1 - time.sleep(3) # Transmite cada 3 segundos - - except KeyboardInterrupt: - print("\nApagando publicador...") - cliente.disconnect() - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.rs b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.rs deleted file mode 100644 index 405cfe4..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/publicador.rs +++ /dev/null @@ -1,39 +0,0 @@ -use nerve::NexusClient; -use serde_json::json; -use std::sync::{atomic::{AtomicBool, Ordering}, Arc}; -use std::thread; -use std::time::Duration; - -fn main() { - println!("Iniciando publicador (estación de radio)..."); - let mut cliente = NexusClient::new(); - cliente.connect("publicador_noticias").expect("Error conectando"); - println!("✅ Publicador conectado al Hub."); - - let running = Arc::new(AtomicBool::new(true)); - let r = running.clone(); - - ctrlc::set_handler(move || { - r.store(false, Ordering::SeqCst); - }).expect("Error setting Ctrl-C handler"); - - let mut contador = 1; - - while running.load(Ordering::SeqCst) { - let titular = format!("Noticia #{}: Nerve Hub funciona genial", contador); - let noticia = json!({ - "canal": "noticias_tech", - "titular": titular, - "timestamp": std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() - }); - - println!(" Transmitiendo (broadcast): {}", titular); - let _ = cliente.broadcast(noticia); - - contador += 1; - thread::sleep(Duration::from_secs(3)); - } - - println!("\nApagando publicador..."); - cliente.disconnect(); -} diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.go b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.go deleted file mode 100644 index d221a2b..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/signal" - "syscall" - - "nerve" -) - -var nombreNodo string - -func alRecibirNoticia(mensajeCrudo nerve.Message) { - payload, ok := mensajeCrudo.Payload.(map[string]interface{}) - if !ok { - return - } - - if canal, ok := payload["canal"].(string); ok && canal == "noticias_tech" { - titular, ok := payload["titular"].(string) - if !ok { - titular = "Sin titular" - } - fmt.Printf("[%s] Nueva noticia interceptada: %s\n", nombreNodo, titular) - } -} - -func main() { - if len(os.Args) > 1 { - nombreNodo = os.Args[1] - } else { - nombreNodo = "suscriptor_default" - } - - fmt.Printf("Iniciando %s...\n", nombreNodo) - cliente := nerve.NewNexusClient() - cliente.Connect(nombreNodo) - fmt.Printf("✅ %s conectado. Escuchando el canal 'noticias_tech'...\n", nombreNodo) - - cliente.Listen(alRecibirNoticia) - - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - <-c - - fmt.Printf("\nApagando %s...\n", nombreNodo) - cliente.Disconnect() -} diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.js b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.js deleted file mode 100644 index 7b9e94d..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.js +++ /dev/null @@ -1,32 +0,0 @@ -const { NexusClient } = require('nerve'); - -const nombreNodo = process.argv[2] || "suscriptor_default"; - -function alRecibirNoticia(mensajeCrudo) { - const payload = mensajeCrudo.payload || {}; - - if (payload.canal === "noticias_tech") { - const titular = payload.titular || "Sin titular"; - console.log(`[${nombreNodo}] Nueva noticia interceptada: ${titular}`); - } -} - -async function main() { - console.log(`Iniciando ${nombreNodo}...`); - const cliente = new NexusClient(); - - await cliente.connect(nombreNodo); - console.log(`✅ ${nombreNodo} conectado. Escuchando el canal 'noticias_tech'...`); - - cliente.listen(alRecibirNoticia); - - process.on('SIGINT', () => { - console.log(`\nApagando ${nombreNodo}...`); - cliente.disconnect(); - process.exit(); - }); -} - -if (require.main === module) { - main(); -} diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.py b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.py deleted file mode 100644 index e721065..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.py +++ /dev/null @@ -1,38 +0,0 @@ -import time -import sys -from nerve import NexusClient - -# Tomamos el nombre del suscriptor por argumento de consola, -# para poder ejecutar varios distintos. -# Si no hay argumento, le ponemos uno por defecto. -nombre_nodo = sys.argv[1] if len(sys.argv) > 1 else "suscriptor_default" - - -def al_recibir_noticia(mensaje_crudo): - # En un patrón Pub-Sub usando 'broadcast', todos reciben el mensaje. - # El trabajo del suscriptor es FILTRAR lo que le interesa. - payload = mensaje_crudo.get("payload", {}) - - if payload.get("canal") == "noticias_tech": - titular = payload.get("titular", "Sin titular") - print(f"[{nombre_nodo}] Nueva noticia interceptada: {titular}") - - -def main(): - print(f"Iniciando {nombre_nodo}...") - cliente = NexusClient() - cliente.connect(nombre_nodo) - print(f"✅ {nombre_nodo} conectado. Escuchando el canal 'noticias_tech'...") - - cliente.listen(al_recibir_noticia) - - try: - while True: - time.sleep(1) - except KeyboardInterrupt: - print(f"\nApagando {nombre_nodo}...") - cliente.disconnect() - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.rs b/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.rs deleted file mode 100644 index 4340088..0000000 --- a/archivo-historico/ejemplos/nerve/02-pub-sub-basico/suscriptor.rs +++ /dev/null @@ -1,43 +0,0 @@ -use nerve::{NexusClient, Message}; -use std::env; -use std::sync::mpsc; - -fn main() { - let args: Vec = env::args().collect(); - let nombre_nodo = if args.len() > 1 { - args[1].clone() - } else { - "suscriptor_default".to_string() - } - - // Usar un Arc para compartir el nombre_nodo con el closure si es necesario, - // pero podemos capturarlo clonándolo - let nodo_clone = nombre_nodo.clone(); - - println!("Iniciando {}...", nombre_nodo); - let mut cliente = NexusClient::new(); - cliente.connect(&nombre_nodo).expect("Error conectando"); - println!("✅ {} conectado. Escuchando el canal 'noticias_tech'...", nombre_nodo); - - let al_recibir_noticia = move |mensaje_crudo: Message| { - if let Some(payload) = mensaje_crudo.payload { - if let Some(canal) = payload.get("canal") { - if canal == "noticias_tech" { - let titular = payload.get("titular").and_then(|t| t.as_str()).unwrap_or("Sin titular"); - println!("[{}] Nueva noticia interceptada: {}", nodo_clone, titular); - } - } - } - }; - - cliente.listen(al_recibir_noticia); - - let (tx, rx) = mpsc::channel(); - ctrlc::set_handler(move || tx.send(()).expect("Error enviando señal")) - .expect("Error al configurar ctrl+c"); - - rx.recv().expect("Error esperando señal"); - - println!("\nApagando {}...", nombre_nodo); - cliente.disconnect(); -} diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/README.md b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/README.md deleted file mode 100644 index aed13c9..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# 03-peticion-respuesta - -## Propósito -Demuestra cómo realizar comunicación bidireccional estructurada mediante el patrón Request / Reply (Petición - Respuesta). - -## 💡 Utilidad -- Un cliente solicita un dato a un servidor específico enviando un comando. -- El servidor escucha peticiones, procesa la tarea y usa el ID del cliente origen (`"from"`) para enviarle de regreso el resultado exacto. - -## ⚙️ Instalación / Requisitos -1. Asegúrate de tener el Hub encendido: `nerve start` -2. Abre una terminal y corre el servidor: `python servidor.py` -3. Abre otra terminal y ejecuta al cliente: `python cliente.py` - -## 🔗 Retos Relacionados -- Múltiples retos de nivel-2-intermedio, por ejemplo, [Reto 07 — Bot de Discord](https://github.com/Kaia-Alenia/nerve-community/issues/7). - -## 🛡️ Buenas Prácticas -- Incluye un identificador de petición o la dirección del cliente en el payload original si no usas los metadatos de Nerve. -- Maneja tiempos de espera (timeouts) en el cliente en caso de que el servidor no responda. diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.go b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.go deleted file mode 100644 index 317e466..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "nerve" -) - -type ClientePeticion struct { - cliente *nerve.NexusClient - respuestaRecibida *float64 -} - -func (c *ClientePeticion) manejarRespuesta(mensajeCrudo nerve.Message) { - payload, ok := mensajeCrudo.Payload.(map[string]interface{}) - if !ok { - return - } - - accion, ok := payload["accion"].(string) - if ok && accion == "respuesta_operacion" { - if resultado, ok := payload["resultado"].(float64); ok { - c.respuestaRecibida = &resultado - fmt.Printf("✅ ¡El servidor respondió! El resultado es: %v\n", resultado) - } - } -} - -func (c *ClientePeticion) iniciar() { - c.cliente.Connect("cliente_calculadora") - c.cliente.Listen(c.manejarRespuesta) - - fmt.Println("Enviando petición de suma al servidor (5 + 7)...") - peticion := map[string]interface{}{ - "accion": "sumar", - "a": 5, - "b": 7, - } - c.cliente.Send("servidor_calculadora", peticion) - - tiempoEspera := 0 - for c.respuestaRecibida == nil && tiempoEspera < 5 { - fmt.Println("⏳ Esperando respuesta...") - time.Sleep(1 * time.Second) - tiempoEspera++ - } - - if c.respuestaRecibida == nil { - fmt.Println("❌ Tiempo de espera agotado. El servidor no contestó.") - } - - c.cliente.Disconnect() -} - -func main() { - app := &ClientePeticion{ - cliente: nerve.NewNexusClient(), - } - app.iniciar() -} diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.js b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.js deleted file mode 100644 index 18df02a..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.js +++ /dev/null @@ -1,45 +0,0 @@ -const { NexusClient } = require('nerve'); - -class ClientePeticion { - constructor() { - this.cliente = new NexusClient(); - this.respuestaRecibida = null; - } - - manejarRespuesta = (mensajeCrudo) => { - const payload = mensajeCrudo.payload || {}; - const accion = payload.accion; - - if (accion === "respuesta_operacion") { - this.respuestaRecibida = payload.resultado; - console.log(`✅ ¡El servidor respondió! El resultado es: ${this.respuestaRecibida}`); - } - } - - async iniciar() { - await this.cliente.connect("cliente_calculadora"); - this.cliente.listen(this.manejarRespuesta); - - console.log("Enviando petición de suma al servidor (5 + 7)..."); - const peticion = { accion: "sumar", a: 5, b: 7 }; - this.cliente.send("servidor_calculadora", peticion); - - let tiempoEspera = 0; - while (this.respuestaRecibida === null && tiempoEspera < 5) { - console.log("⏳ Esperando respuesta..."); - await new Promise(r => setTimeout(r, 1000)); - tiempoEspera++; - } - - if (this.respuestaRecibida === null) { - console.log("❌ Tiempo de espera agotado. El servidor no contestó."); - } - - this.cliente.disconnect(); - } -} - -if (require.main === module) { - const app = new ClientePeticion(); - app.iniciar(); -} diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.py b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.py deleted file mode 100644 index 4427062..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.py +++ /dev/null @@ -1,45 +0,0 @@ -import time -from nerve import NexusClient - - -class ClientePeticion: - def __init__(self): - self.cliente = NexusClient() - self.respuesta_recibida = None - - def manejar_respuesta(self, mensaje_crudo): - # Filtramos para procesar solo los mensajes dirigidos a nosotros con un tipo esperado - payload = mensaje_crudo.get("payload", {}) - accion = payload.get("accion") - - if accion == "respuesta_operacion": - self.respuesta_recibida = payload.get("resultado") - print( - f"✅ ¡El servidor respondió! El resultado es: {self.respuesta_recibida}" - ) - - def iniciar(self): - self.cliente.connect("cliente_calculadora") - self.cliente.listen(self.manejar_respuesta) - - print("Enviando petición de suma al servidor (5 + 7)...") - # Mandamos un payload estructurado pidiendo algo específico - peticion = {"accion": "sumar", "a": 5, "b": 7} - self.cliente.send("servidor_calculadora", peticion) - - # Esperamos hasta que el servidor nos conteste - tiempo_espera = 0 - while self.respuesta_recibida is None and tiempo_espera < 5: - print("⏳ Esperando respuesta...") - time.sleep(1) - tiempo_espera += 1 - - if self.respuesta_recibida is None: - print("❌ Tiempo de espera agotado. El servidor no contestó.") - - self.cliente.disconnect() - - -if __name__ == "__main__": - app = ClientePeticion() - app.iniciar() diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.rs b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.rs deleted file mode 100644 index 064800e..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/cliente.rs +++ /dev/null @@ -1,72 +0,0 @@ -use nerve::{NexusClient, Message}; -use serde_json::json; -use std::sync::{Arc, Mutex}; -use std::thread; -use std::time::Duration; - -struct ClientePeticion { - cliente: NexusClient, - respuesta_recibida: Arc>>, -} - -impl ClientePeticion { - fn new() -> Self { - Self { - cliente: NexusClient::new(), - respuesta_recibida: Arc::new(Mutex::new(None)), - } - } - - fn iniciar(&mut self) { - self.cliente.connect("cliente_calculadora").expect("Error conectando"); - - let respuesta_clone = self.respuesta_recibida.clone(); - - self.cliente.listen(move |mensaje_crudo: Message| { - if let Some(payload) = mensaje_crudo.payload { - if let Some(accion) = payload.get("accion").and_then(|a| a.as_str()) { - if accion == "respuesta_operacion" { - if let Some(resultado) = payload.get("resultado").and_then(|r| r.as_f64()) { - let mut resp = respuesta_clone.lock().unwrap(); - *resp = Some(resultado); - println!("✅ ¡El servidor respondió! El resultado es: {}", resultado); - } - } - } - } - }); - - println!("Enviando petición de suma al servidor (5 + 7)..."); - let peticion = json!({ - "accion": "sumar", - "a": 5.0, - "b": 7.0 - }); - self.cliente.send("servidor_calculadora", peticion).expect("Error al enviar"); - - let mut tiempo_espera = 0; - loop { - let resp = self.respuesta_recibida.lock().unwrap(); - if resp.is_some() || tiempo_espera >= 5 { - break; - } - drop(resp); // liberar el lock para el sleep - - println!("⏳ Esperando respuesta..."); - thread::sleep(Duration::from_secs(1)); - tiempo_espera += 1; - } - - let resp = self.respuesta_recibida.lock().unwrap(); - if resp.is_none() { - println!("❌ Tiempo de espera agotado. El servidor no contestó."); - } - - self.cliente.disconnect(); - } -} - -fn main() { - let mut app = ClientePeticion::new(); - app.iniciar(); -} diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.go b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.go deleted file mode 100644 index 15429b4..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/signal" - "syscall" - - "nerve" -) - -type ServidorCalculadora struct { - cliente *nerve.NexusClient -} - -func (s *ServidorCalculadora) atenderPeticion(mensajeCrudo nerve.Message) { - remitente := mensajeCrudo.From - payload, ok := mensajeCrudo.Payload.(map[string]interface{}) - if !ok { - return - } - - accion, ok := payload["accion"].(string) - if ok && accion == "sumar" { - a, _ := payload["a"].(float64) - b, _ := payload["b"].(float64) - resultado := a + b - - fmt.Printf(" Solicitud de suma recibida de '%s': %v + %v = %v\n", remitente, a, b, resultado) - - respuesta := map[string]interface{}{ - "accion": "respuesta_operacion", - "resultado": resultado, - } - - fmt.Printf("Enviando resultado a '%s'...\n", remitente) - s.cliente.Send(remitente, respuesta) - } -} - -func (s *ServidorCalculadora) iniciar() { - s.cliente.Connect("servidor_calculadora") - fmt.Println("✅ Servidor Calculadora en línea y esperando peticiones...") - - s.cliente.Listen(s.atenderPeticion) - - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - <-c - - fmt.Println("\nApagando servidor...") - s.cliente.Disconnect() -} - -func main() { - app := &ServidorCalculadora{ - cliente: nerve.NewNexusClient(), - } - app.iniciar() -} diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.js b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.js deleted file mode 100644 index a915f2c..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.js +++ /dev/null @@ -1,43 +0,0 @@ -const { NexusClient } = require('nerve'); - -class ServidorCalculadora { - constructor() { - this.cliente = new NexusClient(); - } - - atenderPeticion = (mensajeCrudo) => { - const remitente = mensajeCrudo.from; - const payload = mensajeCrudo.payload || {}; - const accion = payload.accion; - - if (accion === "sumar") { - const a = payload.a || 0; - const b = payload.b || 0; - const resultado = a + b; - - console.log(` Solicitud de suma recibida de '${remitente}': ${a} + ${b} = ${resultado}`); - - const respuesta = { accion: "respuesta_operacion", resultado: resultado }; - console.log(`Enviando resultado a '${remitente}'...`); - this.cliente.send(remitente, respuesta); - } - } - - async iniciar() { - await this.cliente.connect("servidor_calculadora"); - console.log("✅ Servidor Calculadora en línea y esperando peticiones..."); - - this.cliente.listen(this.atenderPeticion); - - process.on('SIGINT', () => { - console.log("\nApagando servidor..."); - this.cliente.disconnect(); - process.exit(); - }); - } -} - -if (require.main === module) { - const app = new ServidorCalculadora(); - app.iniciar(); -} diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.py b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.py deleted file mode 100644 index 5bc0941..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.py +++ /dev/null @@ -1,48 +0,0 @@ -import time -from nerve import NexusClient - - -class ServidorCalculadora: - def __init__(self): - self.cliente = NexusClient() - - def atender_peticion(self, mensaje_crudo): - remitente = mensaje_crudo.get("from") - payload = mensaje_crudo.get("payload", {}) - - accion = payload.get("accion") - - # Validamos que el mensaje es para nosotros y es una acción conocida - if accion == "sumar": - a = payload.get("a", 0) - b = payload.get("b", 0) - resultado = a + b - - print( - f" Solicitud de suma recibida de '{remitente}': {a} + {b} = {resultado}" - ) - - # Construimos la respuesta - respuesta = {"accion": "respuesta_operacion", "resultado": resultado} - - # Mandamos la respuesta de vuelta a quien nos lo pidió - print(f"Enviando resultado a '{remitente}'...") - self.cliente.send(remitente, respuesta) - - def iniciar(self): - self.cliente.connect("servidor_calculadora") - print("✅ Servidor Calculadora en línea y esperando peticiones...") - - self.cliente.listen(self.atender_peticion) - - try: - while True: - time.sleep(1) - except KeyboardInterrupt: - print("\nApagando servidor...") - self.cliente.disconnect() - - -if __name__ == "__main__": - app = ServidorCalculadora() - app.iniciar() diff --git a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.rs b/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.rs deleted file mode 100644 index 45164c8..0000000 --- a/archivo-historico/ejemplos/nerve/03-peticion-respuesta/servidor.rs +++ /dev/null @@ -1,66 +0,0 @@ -use nerve::{NexusClient, Message}; -use serde_json::json; -use std::sync::mpsc; -use std::sync::{Arc, Mutex}; - -struct ServidorCalculadora { - cliente: Arc>, -} - -impl ServidorCalculadora { - fn new() -> Self { - Self { - cliente: Arc::new(Mutex::new(NexusClient::new())), - } - } - - fn iniciar(&mut self) { - let mut cli = self.cliente.lock().unwrap(); - cli.connect("servidor_calculadora").expect("Error conectando"); - println!("✅ Servidor Calculadora en línea y esperando peticiones..."); - - let cliente_clone = self.cliente.clone(); - - cli.listen(move |mensaje_crudo: Message| { - let remitente = mensaje_crudo.from.clone(); - - if let Some(payload) = mensaje_crudo.payload { - if let Some(accion) = payload.get("accion").and_then(|a| a.as_str()) { - if accion == "sumar" { - let a = payload.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0); - let b = payload.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0); - let resultado = a + b; - - println!(" Solicitud de suma recibida de '{}': {} + {} = {}", remitente, a, b, resultado); - - let respuesta = json!({ - "accion": "respuesta_operacion", - "resultado": resultado - }); - - println!("Enviando resultado a '{}'...", remitente); - let mut cli_lock = cliente_clone.lock().unwrap(); - let _ = cli_lock.send(&remitente, respuesta); - } - } - } - }); - - drop(cli); - - let (tx, rx) = mpsc::channel(); - ctrlc::set_handler(move || tx.send(()).expect("Error enviando señal")) - .expect("Error al configurar ctrl+c"); - - rx.recv().expect("Error esperando señal"); - - println!("\nApagando servidor..."); - let mut cli = self.cliente.lock().unwrap(); - cli.disconnect(); - } -} - -fn main() { - let mut app = ServidorCalculadora::new(); - app.iniciar(); -} diff --git a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/README.md b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/README.md deleted file mode 100644 index fb21727..0000000 --- a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Vigilante de Carpetas (File Watcher) - -**¿Qué muestra este ejemplo?** -Muestra cómo utilizar librerías nativas o de terceros para monitorear una carpeta y detectar cuándo se crean, modifican o eliminan archivos dentro de ella. - -**¿Por qué es útil?** -Muchos sistemas modernos necesitan reaccionar a cambios en tiempo real (por ejemplo, subir un archivo automáticamente cuando se guarda, o recargar una web al cambiar el código). - -**Librerías usadas por lenguaje:** -- **Python**: `watchdog` (terceros, `pip install watchdog`) -- **Node.js/JavaScript**: `fs.watch` o `chokidar` (terceros, `npm install chokidar`) -- **Go**: `fsnotify` (terceros, `go get github.com/fsnotify/fsnotify`) -- **Rust**: `notify` (terceros, `cargo add notify`) - -**¿Qué retos usan esta base?** -- [Reto 04 — Vigilante de carpetas](https://github.com/Kaia-Alenia/nerve-community/issues/4) diff --git a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go deleted file mode 100644 index b6119e9..0000000 --- a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.go +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Ejemplo: Monitoreo de carpeta con fsnotify en Go - * - * Qué enseña este ejemplo: - * - Cómo usar un módulo externo de Go (github.com/fsnotify/fsnotify). - * - Cómo usar goroutines (go func) para ejecutar tareas en segundo plano. - * - Cómo usar un bucle for-select para leer eventos desde canales (channels). - * - * Para tu reto (Vigilante de carpetas con Nerve): - * Aplica esta misma estructura. En vez de solo hacer `log.Println`, cuando se - * detecte un evento (ej. Create o Write), usarás el cliente de Nerve para - * notificar a otros nodos. - * - * Glosario de funciones/conceptos: - * fsnotify.NewWatcher() — Crea un nuevo observador del sistema de archivos. - * defer watcher.Close() — Asegura que el observador se cierre al terminar la función (limpieza). - * watcher.Add(path) — Añade una ruta (archivo o carpeta) a la lista de vigilancia. - * <-watcher.Events — Lee (recibe) datos del canal de eventos del observador. Bloquea hasta que haya uno. - * select — Permite a una goroutine esperar múltiples operaciones de comunicación (canales). - */ - -package main - -import ( - "log" - "os" - - "github.com/fsnotify/fsnotify" -) - -func main() { - carpetaAVigilar := "./carpeta_prueba" - - // Creamos la carpeta si no existe (0755 son los permisos estándar de lectura/escritura/ejecución) - if _, err := os.Stat(carpetaAVigilar); os.IsNotExist(err) { - err = os.Mkdir(carpetaAVigilar, 0755) - if err != nil { - log.Fatal("Error al crear carpeta:", err) - } - log.Printf("Carpeta creada: %s\n", carpetaAVigilar) - } - - // 1. Creamos un nuevo observador (watcher) - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Fatal(err) - } - // defer asegura que los recursos se liberen al terminar main() - defer watcher.Close() - - // 2. Iniciamos una goroutine (un hilo ligero en segundo plano) - // que estará escuchando constantemente por eventos o errores. - go func() { - for { - select { - case event, ok := <-watcher.Events: - if !ok { - return - } - // Verificamos qué tipo de operación fue (Create, Write, Remove, Rename, Chmod) - if event.Has(fsnotify.Create) { - log.Printf("📁 [CREADO] Archivo: %s\n", event.Name) - } - if event.Has(fsnotify.Write) { - log.Printf("✏️ [MODIFICADO] Archivo: %s\n", event.Name) - } - if event.Has(fsnotify.Remove) { - log.Printf("🗑️ [ELIMINADO] Archivo: %s\n", event.Name) - } - if event.Has(fsnotify.Rename) { - log.Printf("🔄 [RENOMBRADO] Archivo: %s\n", event.Name) - } - - case err, ok := <-watcher.Errors: - if !ok { - return - } - log.Println("❌ Error:", err) - } - } - }() - - // 3. Añadimos la carpeta que queremos vigilar al watcher - err = watcher.Add(carpetaAVigilar) - if err != nil { - log.Fatal(err) - } - - log.Printf("👀 Vigilando la carpeta '%s'...", carpetaAVigilar) - log.Println("Crea, modifica o elimina archivos ahí para ver los eventos. Presiona Ctrl+C para salir.") - - // Bloqueamos el hilo principal para que el programa no termine inmediatamente. - // <-make(chan struct{}) se queda esperando infinitamente (hasta que lo mates con Ctrl+C). - <-make(chan struct{}) -} diff --git a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js deleted file mode 100644 index a8073d8..0000000 --- a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Ejemplo: Monitoreo de carpeta con fs.watch en Node.js - * - * Qué enseña este ejemplo: - * - Cómo usar el módulo nativo `fs` para vigilar cambios (fs.watch). - * - Cómo manejar eventos asíncronos y callbacks en Node.js. - * - * Para tu reto (Vigilante de carpetas con Nerve): - * Aplica esta misma estructura. En vez de solo hacer `console.log`, cuando se - * detecte un cambio, usarás tu cliente de Nerve para notificar a otros nodos. - * - * Glosario de funciones: - * fs.mkdirSync — Crea una carpeta de forma síncrona (bloqueando la ejecución hasta que termine). - * recursive — Opción para que mkdirSync cree también carpetas intermedias si no existen. - * fs.watch — Inicia la vigilancia sobre un directorio o archivo. Retorna un objeto "watcher". - * eventType — El tipo de evento detectado por fs.watch (generalmente 'rename' o 'change'). - * 'rename' ocurre al crear o eliminar, 'change' al modificar. - */ - -const fs = require('fs'); -const path = require('path'); - -const carpetaAVigilar = path.join(__dirname, 'carpeta_prueba'); - -// Creamos la carpeta si no existe -if (!fs.existsSync(carpetaAVigilar)) { - fs.mkdirSync(carpetaAVigilar, { recursive: true }); - console.log(`Carpeta creada: ${carpetaAVigilar}`); -} - -console.log(`👀 Vigilando la carpeta '${carpetaAVigilar}'...`); -console.log("Crea, modifica o elimina archivos ahí para ver los eventos. Presiona Ctrl+C para salir."); - -// fs.watch es nativo de Node.js. No requiere instalar nada extra. -// Le pasamos la ruta de la carpeta, y un callback que se ejecutará cada vez que haya un evento. -const watcher = fs.watch(carpetaAVigilar, (eventType, filename) => { - if (filename) { - // En Node.js nativo, 'rename' suele significar creación o eliminación. - // 'change' suele significar modificación del contenido. - if (eventType === 'rename') { - console.log(`📁 [CREADO/ELIMINADO] Archivo afectado: ${filename}`); - } else if (eventType === 'change') { - console.log(`✏️ [MODIFICADO] Archivo afectado: ${filename}`); - } - } else { - console.log('filename no proporcionado'); - } -}); - -// Manejo de errores -watcher.on('error', (error) => { - console.error('❌ Error en el observador:', error); -}); diff --git a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py deleted file mode 100644 index a0204fd..0000000 --- a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Ejemplo: Monitoreo de carpeta con watchdog - -Qué enseña este ejemplo: - - Cómo usar una librería externa (watchdog) para vigilar el sistema de archivos. - - Cómo usar manejadores de eventos (event handlers) para reaccionar a cambios. - - Cómo mantener el programa vivo con un loop (while True) + sleep. - -Para tu reto (Vigilante de carpetas con Nerve): - Aplica esta misma estructura. En vez de solo hacer `print`, cuando se cree un - archivo, usarás `cliente.broadcast()` o `cliente.send()` de Nerve para - notificar a otros nodos. - -Glosario de funciones/librerías: - FileSystemEventHandler — Una clase base que tiene métodos como on_created, on_modified, etc. - Observer — El objeto que se encarga de vigilar la carpeta en un hilo separado. - time.sleep(1) — Hace que el programa espere 1 segundo, evitando usar el 100% de la CPU. -""" - -import time -import os -from watchdog.observers import Observer -from watchdog.events import FileSystemEventHandler - - -# Definimos cómo queremos reaccionar a los eventos -class MiManejador(FileSystemEventHandler): - - # Se llama automáticamente cuando se crea un archivo o carpeta - def on_created(self, event): - # event.src_path contiene la ruta del archivo afectado - print(f"📁 [NUEVO] Se ha creado: {event.src_path}") - - # Se llama cuando se modifica un archivo - def on_modified(self, event): - print(f"✏️ [MODIFICADO] Se ha modificado: {event.src_path}") - - # Se llama cuando se elimina un archivo - def on_deleted(self, event): - print(f"🗑️ [ELIMINADO] Se ha eliminado: {event.src_path}") - - -def main(): - carpeta_a_vigilar = "./carpeta_prueba" - - # Creamos la carpeta si no existe - if not os.path.exists(carpeta_a_vigilar): - os.makedirs(carpeta_a_vigilar) - print(f"Carpeta creada: {carpeta_a_vigilar}") - - # 1. Instanciamos nuestro manejador y el observador - manejador = MiManejador() - observador = Observer() - - # 2. Le decimos al observador qué carpeta vigilar, y qué manejador usar - # recursive=False significa que no vigilará subcarpetas. - observador.schedule(manejador, path=carpeta_a_vigilar, recursive=False) - - # 3. Iniciamos el observador (esto ocurre en segundo plano) - observador.start() - print(f"👀 Vigilando la carpeta '{carpeta_a_vigilar}'...") - print( - "Crea, modifica o elimina archivos ahí para ver los eventos. Presiona Ctrl+C para salir." - ) - - try: - # Mantenemos el hilo principal vivo - while True: - time.sleep(1) - except KeyboardInterrupt: - print("\nDeteniendo observador...") - observador.stop() - - # Esperamos a que termine de cerrarse limpiamente - observador.join() - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs b/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs deleted file mode 100644 index b03046b..0000000 --- a/archivo-historico/ejemplos/nerve/04-vigilante-de-carpetas/ejemplo.rs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Ejemplo: Monitoreo de carpeta con notify en Rust - * - * Qué enseña este ejemplo: - * - Cómo usar un crate externo (`notify`) para eventos de sistema de archivos. - * - Cómo manejar canales (mpsc) entre hilos para procesar eventos asíncronamente. - * - Manejo de errores con match y Result en el contexto de eventos continuos. - * - * Para tu reto (Vigilante de carpetas con Nerve): - * Aplica esta misma estructura. En vez de solo hacer `println!`, cuando - * ocurra un EventKind::Create o EventKind::Modify, enviarás un mensaje - * a los demás nodos usando el cliente Nerve. - * - * Glosario: - * std::sync::mpsc::channel — Crea un canal de comunicación de Un Productor a Un Consumidor (MPSC). - * Devuelve un (Sender, Receiver) (tx, rx). - * notify::Watcher — El trait principal para iniciar el monitoreo. - * std::fs::create_dir_all — Crea un directorio y todos sus padres si no existen (como mkdir -p). - * match — Estructura de control poderosa en Rust que evalúa todos los posibles - * casos de un enum o valor. - */ - -use notify::{Event, EventKind, RecursiveMode, Watcher}; -use std::fs; -use std::path::Path; -use std::sync::mpsc::channel; - -fn main() { - let carpeta_a_vigilar = "./carpeta_prueba"; - - // Creamos la carpeta si no existe - if !Path::new(carpeta_a_vigilar).exists() { - if let Err(e) = fs::create_dir_all(carpeta_a_vigilar) { - eprintln!("Error al crear carpeta: {}", e); - return; - } - println!("Carpeta creada: {}", carpeta_a_vigilar); - } - - // 1. Creamos un canal para recibir eventos. - // 'tx' (transmisor) enviará los eventos, 'rx' (receptor) los leerá en nuestro hilo principal. - let (tx, rx) = channel(); - - // 2. Creamos el observador (watcher). - // Usamos el "recommended_watcher" que elige el mejor método según el sistema operativo - // (inotify en Linux, FSEvents en macOS, ReadDirectoryChangesW en Windows). - let mut watcher = match notify::recommended_watcher(tx) { - Ok(w) => w, - Err(e) => { - eprintln!("Error creando el watcher: {:?}", e); - return; - } - }; - - // 3. Añadimos la ruta a vigilar. - // RecursiveMode::NonRecursive indica que NO vigilaremos subcarpetas internas. - if let Err(e) = watcher.watch(Path::new(carpeta_a_vigilar), RecursiveMode::NonRecursive) { - eprintln!("Error al vigilar la ruta: {:?}", e); - return; - } - - println!("👀 Vigilando la carpeta '{}'...", carpeta_a_vigilar); - println!("Crea, modifica o elimina archivos ahí para ver los eventos. Presiona Ctrl+C para salir."); - - // 4. Bucle infinito para recibir eventos a través del receptor (rx). - // Esto bloqueará el hilo principal esperando eventos. - loop { - match rx.recv() { - Ok(event_result) => { - match event_result { - Ok(event) => procesar_evento(event), - Err(e) => eprintln!("❌ Error de evento: {:?}", e), - } - } - Err(e) => { - eprintln!("Error leyendo el canal: {:?}", e); - break; - } - } - } -} - -fn procesar_evento(event: Event) { - // Los eventos en `notify` tienen un `kind` (tipo) y `paths` (rutas afectadas). - match event.kind { - EventKind::Create(_) => { - println!("📁 [CREADO] {:?}", event.paths); - } - EventKind::Modify(_) => { - println!("✏️ [MODIFICADO] {:?}", event.paths); - } - EventKind::Remove(_) => { - println!("🗑️ [ELIMINADO] {:?}", event.paths); - } - _ => { - // Ignoramos otros eventos como accesos de lectura o cambios de metadatos (opcional). - } - } -} diff --git a/archivo-historico/ejemplos/nerve/05-logger-distribuido/README.md b/archivo-historico/ejemplos/nerve/05-logger-distribuido/README.md deleted file mode 100644 index 91afc16..0000000 --- a/archivo-historico/ejemplos/nerve/05-logger-distribuido/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Logger Distribuido (Manejo de Logs) - -**¿Qué muestra este ejemplo?** -Muestra cómo capturar la fecha y hora actual (timestamps) y cómo escribir información en un archivo en modo *append* (agregar al final sin borrar lo anterior), que es la base fundamental de cualquier sistema de logs. - -**¿Por qué es útil?** -Todos los sistemas en producción necesitan un registro de qué ha pasado (cuándo inició, cuándo ocurrió un error, etc.). - -**Conceptos clave:** -- **Append**: Al abrir un archivo en modo append, todo lo nuevo se escribe al final. -- **Timestamps**: Formatear la fecha/hora en un formato legible, típicamente `[YYYY-MM-DD HH:MM:SS]`. - -**¿Qué retos usan esta base?** -- [Reto 05 — Logger distribuido](https://github.com/Kaia-Alenia/nerve-community/issues/5) diff --git a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.go b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.go deleted file mode 100644 index 28491d6..0000000 --- a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Ejemplo: Escritura de Logs en Go - * - * Qué enseña este ejemplo: - * - Cómo obtener la fecha y hora actual usando `time.Now()`. - * - Cómo formatear una fecha en Go usando su sistema de fechas de referencia. - * - Cómo abrir un archivo en modo Append usando `os.OpenFile` y los flags correctos. - * - * Para tu reto (Logger Distribuido con Nerve): - * En vez de llamar a `escribirLog` con datos estáticos, tu receptor de Nerve - * escuchará mensajes de otros nodos y llamará a tu función de log para - * registrarlos en un archivo central. - * - * Glosario: - * time.Now() — Obtiene la fecha y hora actual. - * Format("2006-01-02 15:04:05") — Formatea la fecha. OJO: Go usa una fecha de referencia exacta - * (2 de enero de 2006 a las 15:04:05) para definir formatos, NO usa %Y-%m-%d. - * os.OpenFile — Abre un archivo con opciones avanzadas. - * os.O_APPEND — Flag: si escribes, hazlo al final del archivo. - * os.O_CREATE — Flag: si el archivo no existe, créalo. - * os.O_WRONLY — Flag: abre el archivo solo para escritura. - * f.WriteString — Escribe un string en el archivo abierto. - */ - -package main - -import ( - "fmt" - "log" - "os" - "time" -) - -const ArchivoLog = "sistema.log" - -func escribirLog(nivel string, mensaje string) { - // 1. Obtenemos el timestamp actual y lo formateamos - // Recordatorio: "2006-01-02 15:04:05" es la forma de Go para decir "YYYY-MM-DD HH:MM:SS" - ahora := time.Now() - timestampFormateado := ahora.Format("2006-01-02 15:04:05") - - // 2. Construimos la línea de log final - // Ejemplo: [2026-07-17 15:30:05] [INFO] El servidor ha iniciado correctamente - lineaLog := fmt.Sprintf("[%s] [%s] %s\n", timestampFormateado, nivel, mensaje) - - // 3. Imprimimos en consola (opcional, para verlo en vivo) - fmt.Print(lineaLog) - - // 4. Abrimos el archivo en modo "Append" (añadir al final) - // Si no existe (O_CREATE), lo crea. Solo escritura (O_WRONLY). - // Permisos 0644 (lectura y escritura para el dueño, solo lectura para otros). - f, err := os.OpenFile(ArchivoLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - log.Printf("Error abriendo el archivo de log: %v", err) - return - } - defer f.Close() - - // 5. Escribimos la línea en el archivo - if _, err := f.WriteString(lineaLog); err != nil { - log.Printf("Error escribiendo en el archivo de log: %v", err) - } -} - -func main() { - fmt.Printf("Escribiendo logs en '%s'...\n\n", ArchivoLog) - - escribirLog("INFO", "Sistema iniciado.") - escribirLog("DEBUG", "Conexión a la base de datos establecida en 14ms.") - escribirLog("WARNING", "Poco espacio en disco (15% restante).") - escribirLog("ERROR", "Fallo al procesar el archivo 'datos.csv': Archivo no encontrado.") - escribirLog("INFO", "Apagando sistema de forma segura.") - - fmt.Printf("\nRevisa el archivo '%s' para ver los resultados guardados.\n", ArchivoLog) -} diff --git a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.js b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.js deleted file mode 100644 index 69811c0..0000000 --- a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Ejemplo: Escritura de Logs en Node.js - * - * Qué enseña este ejemplo: - * - Cómo obtener la fecha y hora actual usando el objeto nativo `Date`. - * - Cómo formatear una fecha al estilo ISO o local. - * - Cómo añadir texto al final de un archivo usando `fs.appendFileSync` (modo append). - * - * Para tu reto (Logger Distribuido con Nerve): - * En vez de llamar a `escribirLog` con datos estáticos, tu receptor de Nerve - * escuchará mensajes de otros nodos y llamará a tu función de log para - * registrarlos en un archivo central. - * - * Glosario: - * new Date() — Crea un nuevo objeto de fecha con la fecha y hora actual. - * toISOString() — Formatea la fecha al estándar ISO (ej: 2026-07-17T15:30:05.123Z). - * fs.appendFileSync — Escribe en un archivo síncronamente agregando el texto al final. - * Si el archivo no existe, lo crea. - */ - -const fs = require('fs'); -const path = require('path'); - -// El archivo donde guardaremos los logs -const ARCHIVO_LOG = path.join(__dirname, 'sistema.log'); - -function escribirLog(nivel, mensaje) { - // 1. Obtenemos el timestamp actual y lo formateamos - // toISOString da un formato como "2026-07-17T15:30:05.123Z" - // Reemplazamos la "T" por un espacio y quitamos los milisegundos y la Z final para que luzca mejor. - const ahora = new Date(); - const timestampFormateado = ahora.toISOString().replace('T', ' ').substring(0, 19); - - // 2. Construimos la línea de log final - // Ejemplo: [2026-07-17 15:30:05] [INFO] El servidor ha iniciado correctamente - const lineaLog = `[${timestampFormateado}] [${nivel}] ${mensaje}\n`; - - // 3. Imprimimos en consola (opcional, para verlo en vivo) - // Usamos process.stdout.write para evitar el salto de línea extra que pone console.log - process.stdout.write(lineaLog); - - // 4. Escribimos en el archivo agregando al final (append) - try { - fs.appendFileSync(ARCHIVO_LOG, lineaLog, 'utf8'); - } catch (error) { - console.error('Error al escribir en el archivo de log:', error); - } -} - -function main() { - console.log(`Escribiendo logs en '${ARCHIVO_LOG}'...\n`); - - escribirLog("INFO", "Sistema iniciado."); - escribirLog("DEBUG", "Conexión a la base de datos establecida en 14ms."); - escribirLog("WARNING", "Poco espacio en disco (15% restante)."); - escribirLog("ERROR", "Fallo al procesar el archivo 'datos.csv': Archivo no encontrado."); - escribirLog("INFO", "Apagando sistema de forma segura."); - - console.log(`\nRevisa el archivo '${ARCHIVO_LOG}' para ver los resultados guardados.`); -} - -main(); diff --git a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.py b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.py deleted file mode 100644 index 6d721f4..0000000 --- a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Ejemplo: Escritura de Logs en Python - -Qué enseña este ejemplo: - - Cómo obtener la fecha y hora actual con `datetime`. - - Cómo abrir un archivo en modo "append" ("a") para no sobreescribir datos anteriores. - - Cómo formatear strings para que luzcan como logs profesionales. - -Para tu reto (Logger Distribuido con Nerve): - En vez de llamar a `escribir_log` con datos estáticos, tu receptor de Nerve - escuchará mensajes de otros nodos y llamará a tu función de log para - registrarlos en un archivo central. - -Glosario: - datetime.now() — Obtiene la fecha y hora exactas del sistema. - strftime("%Y-%m-%d...") — Da formato a la fecha (String Format Time). %Y es año, %m es mes, etc. - open(..., "a") — Abre el archivo en modo "append" (agregar al final). Si usas "w", borra todo. -""" - -from datetime import datetime - -# El archivo donde guardaremos los logs -ARCHIVO_LOG = "sistema.log" - - -def escribir_log(nivel: str, mensaje: str): - """ - Escribe un mensaje de log en el archivo con un timestamp y un nivel (INFO, ERROR, etc.). - """ - # 1. Obtenemos el timestamp actual - ahora = datetime.now() - - # 2. Le damos un formato legible, ej: 2026-07-17 15:30:05 - timestamp_formateado = ahora.strftime("%Y-%m-%d %H:%M:%S") - - # 3. Construimos la línea de log final - # Ejemplo: [2026-07-17 15:30:05] [INFO] El servidor ha iniciado correctamente - linea_log = f"[{timestamp_formateado}] [{nivel}] {mensaje}\n" - - # 4. Imprimimos en consola (opcional, para verlo en vivo) - print(linea_log, end="") - - # 5. Escribimos en el archivo en modo "a" (append) - with open(ARCHIVO_LOG, "a", encoding="utf-8") as f: - f.write(linea_log) - - -def main(): - print(f"Escribiendo logs en '{ARCHIVO_LOG}'...\n") - - escribir_log("INFO", "Sistema iniciado.") - escribir_log("DEBUG", "Conexión a la base de datos establecida en 14ms.") - escribir_log("WARNING", "Poco espacio en disco (15% restante).") - escribir_log( - "ERROR", "Fallo al procesar el archivo 'datos.csv': Archivo no encontrado." - ) - escribir_log("INFO", "Apagando sistema de forma segura.") - - print(f"\nRevisa el archivo '{ARCHIVO_LOG}' para ver los resultados guardados.") - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.rs b/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.rs deleted file mode 100644 index 1f34251..0000000 --- a/archivo-historico/ejemplos/nerve/05-logger-distribuido/ejemplo.rs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Ejemplo: Escritura de Logs en Rust - * - * Qué enseña este ejemplo: - * - Cómo obtener la fecha y hora actual usando el crate externo `chrono`. - * - Cómo formatear una fecha en Rust. - * - Cómo abrir un archivo en modo Append usando `OpenOptions`. - * - Manejo seguro de errores de escritura usando `unwrap_or_else` o `if let Err`. - * - * Para tu reto (Logger Distribuido con Nerve): - * En vez de llamar a `escribir_log` con datos estáticos, tu receptor de Nerve - * escuchará mensajes de otros nodos y llamará a tu función de log para - * registrarlos en un archivo central. - * - * Glosario: - * chrono::Local::now() — Obtiene la fecha y hora local del sistema. - * format("%Y-%m-%d...") — Da formato a la fecha (requiere el crate chrono). - * OpenOptions::new() — Permite configurar cómo se abre un archivo. - * .append(true) — Configura la apertura para añadir al final. - * .create(true) — Crea el archivo si no existe. - * writeln! — Macro que escribe texto en un archivo con un salto de línea al final. - */ - -use chrono::Local; -use std::fs::OpenOptions; -use std::io::Write; - -const ARCHIVO_LOG: &str = "sistema.log"; - -fn escribir_log(nivel: &str, mensaje: &str) { - // 1. Obtenemos el timestamp actual y lo formateamos usando el crate `chrono` - let ahora = Local::now(); - let timestamp_formateado = ahora.format("%Y-%m-%d %H:%M:%S").to_string(); - - // 2. Construimos la línea de log final - // Ejemplo: [2026-07-17 15:30:05] [INFO] El servidor ha iniciado correctamente - let linea_log = format!("[{}] [{}] {}", timestamp_formateado, nivel, mensaje); - - // 3. Imprimimos en consola (opcional, para verlo en vivo) - println!("{}", linea_log); - - // 4. Abrimos el archivo en modo "Append" (añadir al final) y creamos si no existe - let archivo_resultado = OpenOptions::new() - .create(true) - .append(true) - .open(ARCHIVO_LOG); - - // 5. Verificamos si se abrió correctamente y escribimos - match archivo_resultado { - Ok(mut f) => { - if let Err(e) = writeln!(f, "{}", linea_log) { - eprintln!("Error al escribir en el log: {}", e); - } - } - Err(e) => { - eprintln!("Error al abrir el archivo de log: {}", e); - } - } -} - -fn main() { - println!("Escribiendo logs en '{}'...\n", ARCHIVO_LOG); - - escribir_log("INFO", "Sistema iniciado."); - escribir_log("DEBUG", "Conexión a la base de datos establecida en 14ms."); - escribir_log("WARNING", "Poco espacio en disco (15% restante)."); - escribir_log("ERROR", "Fallo al procesar el archivo 'datos.csv': Archivo no encontrado."); - escribir_log("INFO", "Apagando sistema de forma segura."); - - println!("\nRevisa el archivo '{}' para ver los resultados guardados.", ARCHIVO_LOG); -} diff --git a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/README.md b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/README.md deleted file mode 100644 index b387fc7..0000000 --- a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Puente de Progreso GIF (Manipulación de Imágenes) - -**¿Qué muestra este ejemplo?** -Muestra cómo crear una imagen en blanco desde cero y dibujar un rectángulo en ella, simulando una barra de progreso, usando librerías de manipulación de imágenes. - -**¿Por qué es útil?** -Generar gráficos dinámicos o alterar imágenes programáticamente es muy común para crear dashboards visuales, bots que devuelven imágenes o, en este caso, una barra de progreso que se puede ver en el navegador. - -**Librerías usadas por lenguaje:** -- **Python**: `Pillow` (instalación: `pip install Pillow`) -- **Node.js/JavaScript**: `jimp` (instalación: `npm install jimp`) - usamos Jimp porque no requiere compilación nativa y es fácil para principiantes. -- **Go**: `image`, `image/color`, `image/draw` (nativas de Go, no requiere instalar nada). -- **Rust**: `image` (instalación: `cargo add image`) e `imageproc` (`cargo add imageproc`). - -**¿Qué retos usan esta base?** -- [Reto 06 — Puente de progreso GIF](https://github.com/Kaia-Alenia/nerve-community/issues/6) diff --git a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go deleted file mode 100644 index 2f6b454..0000000 --- a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.go +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Ejemplo: Dibujar una barra de progreso nativa en Go - * - * Qué enseña este ejemplo: - * - Cómo usar la librería nativa de Go `image` y `image/draw`. - * - Cómo dibujar rectángulos en un lienzo RGBA. - * - Cómo codificar y guardar una imagen en formato PNG (image/png). - * - * Para tu reto (Puente de progreso GIF con Nerve): - * En lugar de un porcentaje fijo, tu receptor de Nerve recibirá actualizaciones - * (ej. 10%, 20%), y con cada actualización dibujarás la barra correspondiente - * y guardarás la imagen. - * - * Glosario: - * image.NewRGBA() — Crea una nueva imagen de un tamaño específico que soporta color. - * color.RGBA{} — Define un color usando rojo, verde, azul y alpha (transparencia). - * draw.Draw() — Copia una imagen/color sobre otra en un área específica. - * png.Encode() — Escribe los datos de la imagen en formato PNG a un archivo. - */ - -package main - -import ( - "fmt" - "image" - "image/color" - "image/draw" - "image/png" - "log" - "os" -) - -func generarBarraProgreso(porcentaje int, rutaSalida string) { - ancho := 400 - alto := 50 - - // Aseguramos que el porcentaje esté entre 0 y 100 - if porcentaje < 0 { - porcentaje = 0 - } else if porcentaje > 100 { - porcentaje = 100 - } - - // Calculamos el ancho de la barra - anchoProgreso := int((float64(porcentaje) / 100.0) * float64(ancho)) - - // 1. Definimos los colores - colorFondo := color.RGBA{220, 220, 220, 255} // Gris claro - colorBarra := color.RGBA{50, 205, 50, 255} // Verde lima - - // 2. Creamos el lienzo (la imagen base) - // image.Rect(x0, y0, x1, y1) define los límites - lienzo := image.NewRGBA(image.Rect(0, 0, ancho, alto)) - - // 3. Llenamos todo el lienzo con el color de fondo - // draw.Src significa que reemplazará el pixel completamente - draw.Draw(lienzo, lienzo.Bounds(), &image.Uniform{colorFondo}, image.Point{}, draw.Src) - - // 4. Dibujamos el rectángulo de progreso - // Creamos un rectángulo que va desde (0,0) hasta (anchoProgreso, alto) - rectProgreso := image.Rect(0, 0, anchoProgreso, alto) - draw.Draw(lienzo, rectProgreso, &image.Uniform{colorBarra}, image.Point{}, draw.Src) - - // 5. Guardamos la imagen en un archivo - archivo, err := os.Create(rutaSalida) - if err != nil { - log.Printf("Error al crear el archivo %s: %v", rutaSalida, err) - return - } - defer archivo.Close() - - if err := png.Encode(archivo, lienzo); err != nil { - log.Printf("Error al codificar PNG: %v", err) - return - } - - fmt.Printf("✅ Imagen guardada en: %s con un progreso de %d%%\n", rutaSalida, porcentaje) -} - -func main() { - fmt.Println("Generando ejemplos de barras de progreso...\n") - - generarBarraProgreso(25, "progreso_25.png") - generarBarraProgreso(75, "progreso_75.png") - generarBarraProgreso(100, "progreso_100.png") - - fmt.Println("\nRevisa tu carpeta, deberías ver tres archivos .png nuevos.") -} diff --git a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js deleted file mode 100644 index b9a38c8..0000000 --- a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Ejemplo: Dibujar una barra de progreso con Jimp (Node.js) - * - * Qué enseña este ejemplo: - * - Cómo crear una imagen nueva programáticamente sin usar canvas nativo. - * - Cómo manipular píxeles directamente o usar plugins de Jimp. - * - Cómo guardar una imagen asíncronamente. - * - * Para tu reto (Puente de progreso GIF con Nerve): - * En lugar de un porcentaje fijo, tu receptor de Nerve recibirá actualizaciones - * (ej. 10%, 20%), y con cada actualización dibujarás la barra correspondiente - * y guardarás la imagen. - * - * Glosario: - * new Jimp(ancho, alto, color) — Instancia una imagen nueva con fondo sólido. - * image.scan() — Recorre los píxeles de un área para modificarlos. - * image.writeAsync() — Guarda la imagen generada (devuelve una promesa). - */ - -const Jimp = require('jimp'); - -async function generarBarraProgreso(porcentaje, rutaSalida) { - const ancho = 400; - const alto = 50; - - // Aseguramos que el porcentaje esté entre 0 y 100 - const p = Math.max(0, Math.min(100, porcentaje)); - - // Calculamos el ancho de la barra verde - const anchoProgreso = Math.floor((p / 100) * ancho); - - // 1. Creamos el lienzo base (fondo gris claro en Hexadecimal) - // 0xDDDDDDFF = R: 221, G: 221, B: 221, Alpha: 255 (sólido) - const imagen = new Jimp(ancho, alto, 0xddddddff); - - // 2. Coloreamos la porción correspondiente a la barra de progreso - // Jimp no tiene una función nativa de dibujar rectángulos sin plugins, - // pero podemos hacerlo fácilmente modificando los píxeles del área deseada. - // El color verde lima es 0x32CD32FF - const colorVerde = 0x32cd32ff; - - imagen.scan(0, 0, anchoProgreso, alto, function (x, y, idx) { - // En lugar de acceder a los bytes, escribimos el color hexadecimal completo - this.setPixelColor(colorVerde, x, y); - }); - - // 3. Guardamos la imagen (es una operación asíncrona) - try { - await imagen.writeAsync(rutaSalida); - console.log(`✅ Imagen guardada en: ${rutaSalida} con un progreso de ${p}%`); - } catch (error) { - console.error("Error al guardar la imagen:", error); - } -} - -async function main() { - console.log("Generando ejemplos de barras de progreso...\n"); - - await generarBarraProgreso(25, "progreso_25.png"); - await generarBarraProgreso(75, "progreso_75.png"); - await generarBarraProgreso(100, "progreso_100.png"); - - console.log("\nRevisa tu carpeta, deberías ver tres archivos .png nuevos."); -} - -main(); diff --git a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py deleted file mode 100644 index 68652ef..0000000 --- a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Ejemplo: Dibujar una barra de progreso con Pillow (Python) - -Qué enseña este ejemplo: - - Cómo crear una imagen desde cero. - - Cómo dibujar formas geométricas (rectángulos) sobre la imagen. - - Cómo guardar la imagen generada en el disco. - -Para tu reto (Puente de progreso GIF con Nerve): - En lugar de un porcentaje fijo, tu receptor de Nerve recibirá actualizaciones - (ej. 10%, 20%), y con cada actualización dibujarás la barra correspondiente - y guardarás la imagen para que una página web u otro cliente pueda verla. - -Glosario: - Image.new() — Crea un lienzo en blanco. Pide el modo ("RGB"), tamaño (ancho, alto) y color de fondo. - ImageDraw.Draw() — Crea un objeto que permite dibujar sobre la imagen. - draw.rectangle() — Dibuja un rectángulo dadas las coordenadas [x0, y0, x1, y1] y un color. -""" - -from PIL import Image, ImageDraw - - -def generar_barra_progreso(porcentaje: int, ruta_salida: str): - """ - Crea una imagen con una barra de progreso que representa el porcentaje dado. - """ - # 1. Definimos dimensiones de la imagen - ancho = 400 - alto = 50 - - # 2. Creamos una nueva imagen con fondo gris claro - color_fondo = (220, 220, 220) # RGB - imagen = Image.new("RGB", (ancho, alto), color_fondo) - - # 3. Preparamos el objeto para dibujar - draw = ImageDraw.Draw(imagen) - - # 4. Calculamos el ancho de la barra verde (progreso) - # Si porcentaje es 50, el ancho será el 50% de 400 = 200. - porcentaje = max(0, min(100, porcentaje)) # Aseguramos que esté entre 0 y 100 - ancho_progreso = int((porcentaje / 100) * ancho) - - # 5. Dibujamos el rectángulo verde - # Las coordenadas son [x_inicial, y_inicial, x_final, y_final] - # Empieza en (0,0) y termina en (ancho_progreso, alto) - color_barra = (50, 205, 50) # Verde lima - draw.rectangle([0, 0, ancho_progreso, alto], fill=color_barra) - - # 6. Guardamos la imagen - imagen.save(ruta_salida) - print(f"✅ Imagen guardada en: {ruta_salida} con un progreso de {porcentaje}%") - - -def main(): - print("Generando ejemplos de barras de progreso...\n") - - # Generamos tres imágenes distintas - generar_barra_progreso(25, "progreso_25.png") - generar_barra_progreso(75, "progreso_75.png") - generar_barra_progreso(100, "progreso_100.png") - - print("\nRevisa tu carpeta, deberías ver tres archivos .png nuevos.") - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs b/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs deleted file mode 100644 index d3f141d..0000000 --- a/archivo-historico/ejemplos/nerve/06-puente-de-progreso-gif/ejemplo.rs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Ejemplo: Dibujar una barra de progreso en Rust con el crate `image` - * - * Qué enseña este ejemplo: - * - Cómo usar el crate externo `image` para crear un lienzo. - * - Cómo manipular los píxeles de una imagen usando bucles `for`. - * - Cómo guardar una imagen en disco. - * - * Para tu reto (Puente de progreso GIF con Nerve): - * En lugar de un porcentaje fijo, tu receptor de Nerve recibirá actualizaciones - * (ej. 10%, 20%), y con cada actualización dibujarás la barra correspondiente - * y guardarás la imagen. - * - * Glosario: - * RgbImage::new(w, h) — Crea un nuevo búfer de imagen RGB de ancho `w` y alto `h`. - * Rgb([r, g, b]) — Define un píxel con colores Rojo, Verde y Azul (0-255). - * put_pixel(x, y, c) — Pinta el píxel en las coordenadas (x, y) con el color `c`. - * save(ruta) — Guarda la imagen inferiendo el formato (ej. PNG) a partir de la extensión. - */ - -use image::{Rgb, RgbImage}; -use std::cmp; - -fn generar_barra_progreso(porcentaje: i32, ruta_salida: &str) { - let ancho = 400; - let alto = 50; - - // Aseguramos que el porcentaje esté entre 0 y 100 - let p = cmp::max(0, cmp::min(100, porcentaje)); - - // Calculamos el ancho de la barra - let ancho_progreso = ((p as f32 / 100.0) * (ancho as f32)) as u32; - - // 1. Creamos la imagen base (RgbImage) - let mut imagen = RgbImage::new(ancho, alto); - - let color_fondo = Rgb([220, 220, 220]); // Gris claro - let color_barra = Rgb([50, 205, 50]); // Verde lima - - // 2. Coloreamos la imagen iterando por los píxeles (x, y) - for y in 0..alto { - for x in 0..ancho { - if x < ancho_progreso { - // Si el píxel está dentro de la barra de progreso, lo pintamos verde - imagen.put_pixel(x, y, color_barra); - } else { - // Si no, lo pintamos del color de fondo - imagen.put_pixel(x, y, color_fondo); - } - } - } - - // 3. Guardamos la imagen - match imagen.save(ruta_salida) { - Ok(_) => println!("✅ Imagen guardada en: {} con un progreso de {}%", ruta_salida, p), - Err(e) => eprintln!("Error al guardar {}: {}", ruta_salida, e), - } -} - -fn main() { - println!("Generando ejemplos de barras de progreso...\n"); - - generar_barra_progreso(25, "progreso_25.png"); - generar_barra_progreso(75, "progreso_75.png"); - generar_barra_progreso(100, "progreso_100.png"); - - println!("\nRevisa tu carpeta, deberías ver tres archivos .png nuevos."); -} diff --git a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/README.md b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/README.md deleted file mode 100644 index 9b9fd2e..0000000 --- a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Bot de Discord (Básicos de API) - -**¿Qué muestra este ejemplo?** -Muestra cómo crear un bot básico de Discord que se conecta a los servidores, lee los mensajes entrantes y responde con un "¡Hola!". - -**¿Por qué es útil?** -La integración de sistemas de backend con plataformas de mensajería como Discord, Slack o Telegram es una habilidad muy valorada. - -**Librerías usadas por lenguaje:** -- **Python**: `discord.py` (`pip install discord.py`) -- **Node.js/JavaScript**: `discord.js` (`npm install discord.js`) -- **Go**: `discordgo` (`go get github.com/bwmarrin/discordgo`) -- **Rust**: `serenity` (`cargo add serenity tokio`) - -**¿Qué retos usan esta base?** -- [Reto 07 — Bot Discord Nerve](https://github.com/Kaia-Alenia/nerve-community/issues/7) - -*Nota: Para correr estos ejemplos necesitas un `TOKEN` válido de un bot de Discord que hayas creado en el [Portal de Desarrolladores de Discord](https://discord.com/developers/applications).* diff --git a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.go b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.go deleted file mode 100644 index a000d5a..0000000 --- a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.go +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Ejemplo: Bot básico con discordgo - * - * Qué enseña este ejemplo: - * - Cómo configurar e iniciar una sesión de discordgo en Go. - * - Configurar los "Intents" para especificar qué eventos quieres. - * - Añadir un "handler" (manejador) para eventos de nuevos mensajes. - * - * Para tu reto (Bot Discord Nerve): - * En vez de solo responder con "¡Hola!", conectarás el bot de Discord con - * tu cliente Nerve. Cuando llegue un mensaje a Discord, usarás tu cliente - * Nerve para notificar a la red, y viceversa. - * - * Glosario: - * discordgo.New() — Crea una nueva sesión del bot. - * dg.Identify.Intents — Define qué eventos de Discord recibirá el bot (ej. GuildMessages). - * dg.AddHandler() — Registra una función que se llamará cuando ocurra un evento. - * s.ChannelMessageSend() — Envía un mensaje a un canal de Discord. - */ - -package main - -import ( - "fmt" - "os" - "os/signal" - "strings" - "syscall" - - "github.com/bwmarrin/discordgo" -) - -func main() { - // Reemplaza esto con tu token real o usa variables de entorno - token := os.Getenv("DISCORD_TOKEN") - if token == "" { - token = "TU_TOKEN_AQUI" - } - - if token == "TU_TOKEN_AQUI" { - fmt.Println("⚠️ ADVERTENCIA: Reemplaza 'TU_TOKEN_AQUI' con el token de tu bot de Discord.") - fmt.Println("Puedes obtenerlo en: https://discord.com/developers/applications") - return - } - - // 1. Crear una nueva sesión de Discord usando el token del bot - dg, err := discordgo.New("Bot " + token) - if err != nil { - fmt.Println("Error creando la sesión de Discord:", err) - return - } - - // 2. Registrar el manejador (handler) para el evento MessageCreate - dg.AddHandler(messageCreate) - - // 3. Configurar los Intents (permisos). Necesitamos los de mensajes. - // Nota: Para MessageContent, asegúrate de habilitarlo en el portal de desarrolladores. - dg.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsMessageContent - - // 4. Abrir una conexión con Discord - err = dg.Open() - if err != nil { - fmt.Println("Error abriendo conexión:", err) - return - } - - // Esperar hasta que se reciba una señal para terminar (CTRL+C) - fmt.Println("El bot está funcionando. Presiona CTRL-C para salir.") - sc := make(chan os.Signal, 1) - signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) - <-sc - - // 5. Cerrar la sesión de forma limpia - dg.Close() -} - -// messageCreate se llamará CADA VEZ que se envíe un mensaje a un canal al que el bot tenga acceso -func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { - // Ignorar todos los mensajes creados por el bot mismo - if m.Author.ID == s.State.User.ID { - return - } - - // Si el mensaje dice "ping", responder con "pong" - if strings.ToLower(m.Content) == "ping" { - fmt.Printf("[%s] dijo ping, respondiendo...\n", m.Author.Username) - - // Enviar el mensaje al mismo canal donde se recibió el ping - s.ChannelMessageSend(m.ChannelID, "pong 🏓") - } -} diff --git a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.js b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.js deleted file mode 100644 index 297fe08..0000000 --- a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Ejemplo: Bot básico con discord.js - * - * Qué enseña este ejemplo: - * - Cómo configurar e iniciar un cliente de discord.js. - * - El uso de "GatewayIntents" para especificar qué eventos quieres. - * - Manejo de eventos con `.on` y `.once`. - * - * Para tu reto (Bot Discord Nerve): - * En vez de solo responder con "¡Hola!", conectarás el bot de Discord con - * tu cliente Nerve. Cuando llegue un mensaje a Discord, usarás tu cliente - * Nerve para notificar a la red, y viceversa. - * - * Glosario: - * Client() — Instancia principal del bot de Discord. - * GatewayIntentBits — Sistema de permisos de Discord. `GuildMessages` lee eventos de mensajes, - * y `MessageContent` es necesario para leer el texto en sí. - * client.once('ready') — Se ejecuta UNA SOLA VEZ cuando el bot termina de conectarse. - * client.on('messageCreate') — Se ejecuta CADA VEZ que se envía un mensaje. - * message.reply() — Envía un mensaje respondiendo (etiquetando) al usuario original. - */ - -const { Client, GatewayIntentBits } = require('discord.js'); - -// 1. Configurar el cliente y los Intents -const client = new Client({ - intents: [ - GatewayIntentBits.Guilds, // Para poder estar en servidores - GatewayIntentBits.GuildMessages, // Para recibir eventos de mensajes - GatewayIntentBits.MessageContent // Para poder leer el contenido de los mensajes - ] -}); - -// 2. Evento 'ready': Cuando el bot se conecta exitosamente -client.once('ready', () => { - console.log(`✅ Conectado exitosamente como ${client.user.tag}`); -}); - -// 3. Evento 'messageCreate': Cuando alguien envía un mensaje -client.on('messageCreate', async (message) => { - // Ignorar mensajes enviados por bots (incluyendo el nuestro) - if (message.author.bot) return; - - // Si alguien dice "ping", respondemos "pong" - if (message.content.toLowerCase() === 'ping') { - console.log(`[${message.author.tag}] dijo ping, respondiendo...`); - // Usar await porque enviar un mensaje es una operación de red - await message.reply('pong 🏓'); - } -}); - -// 4. Iniciar el bot -function main() { - // Debes reemplazar esto con tu token real o usar variables de entorno - const token = process.env.DISCORD_TOKEN || "TU_TOKEN_AQUI"; - - if (token === "TU_TOKEN_AQUI") { - console.log("⚠️ ADVERTENCIA: Reemplaza 'TU_TOKEN_AQUI' con el token de tu bot de Discord."); - console.log("Puedes obtenerlo en: https://discord.com/developers/applications"); - return; - } - - console.log("Iniciando bot..."); - client.login(token); -} - -main(); diff --git a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.py b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.py deleted file mode 100644 index d41430a..0000000 --- a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Ejemplo: Bot básico con discord.py - -Qué enseña este ejemplo: - - Cómo configurar y arrancar un bot de Discord. - - Cómo escuchar eventos (`on_ready`, `on_message`). - - El uso de `async`/`await` para programación asíncrona (requerido por Discord). - -Para tu reto (Bot Discord Nerve): - En vez de solo responder con "¡Hola!", conectarás el bot de Discord con - tu cliente Nerve. Cuando llegue un mensaje a Discord, usarás `cliente.send()` - para mandarlo a la red Nerve, y viceversa. - -Glosario: - discord.Client() — La clase base que maneja la conexión con los servidores de Discord. - discord.Intents — Los permisos que le pides a Discord. message_content es obligatorio - para poder leer el texto de los mensajes. - @client.event — Un decorador que le dice a la librería que esa función manejará un evento. - async def — Declara una función asíncrona. - await — Espera a que una tarea asíncrona (como enviar un mensaje a internet) termine. -""" - -import discord -import os - -# 1. Configurar los "intents" (permisos de qué eventos queremos recibir) -intents = discord.Intents.default() -intents.message_content = True # ¡Importante para poder leer lo que escriben! - -# 2. Inicializar el cliente del bot -client = discord.Client(intents=intents) - - -# 3. Definir qué pasa cuando el bot se conecta exitosamente -@client.event -async def on_ready(): - print(f"✅ Conectado exitosamente como {client.user}") - - -# 4. Definir qué pasa cuando alguien envía un mensaje en un canal -@client.event -async def on_message(message): - # Ignorar mensajes del propio bot para evitar bucles infinitos - if message.author == client.user: - return - - # Si alguien dice "ping", el bot responde "pong" - if message.content.lower() == "ping": - print(f"[{message.author}] dijo ping, respondiendo...") - # 'await' es necesario siempre que interactuemos con la API de Discord - await message.channel.send("pong 🏓") - - -def main(): - # Debes reemplazar esto con tu token real o usar una variable de entorno - token = os.environ.get("DISCORD_TOKEN", "TU_TOKEN_AQUI") - - if token == "TU_TOKEN_AQUI": - print( - "⚠️ ADVERTENCIA: Reemplaza 'TU_TOKEN_AQUI' con el token de tu bot de Discord." - ) - print("Puedes obtenerlo en: https://discord.com/developers/applications") - return - - print("Iniciando bot...") - client.run(token) - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs b/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs deleted file mode 100644 index b8c7b27..0000000 --- a/archivo-historico/ejemplos/nerve/07-bot-discord-nerve/ejemplo.rs +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Ejemplo: Bot básico con serenity - * - * Qué enseña este ejemplo: - * - Cómo configurar e iniciar un cliente de serenity en Rust. - * - Implementar el trait EventHandler para manejar eventos. - * - El uso de "GatewayIntents" para especificar qué eventos quieres. - * - * Para tu reto (Bot Discord Nerve): - * En vez de solo responder con "¡Hola!", conectarás el bot de Discord con - * tu cliente Nerve. Cuando llegue un mensaje a Discord, usarás tu cliente - * Nerve para notificar a la red, y viceversa. - * - * Glosario: - * serenity::Client::builder() — Constructor del cliente de Discord. - * GatewayIntents — Sistema de permisos. Necesitas GUILD_MESSAGES y MESSAGE_CONTENT. - * EventHandler — Trait que implementas para recibir eventos. - * ctx.say(&msg.content) — Método conveniente para responder en el mismo canal. - */ - -use serenity::async_trait; -use serenity::model::channel::Message; -use serenity::model::gateway::Ready; -use serenity::prelude::*; -use std::env; - -// 1. Definir nuestra estructura Handler que manejará los eventos -struct Handler; - -// 2. Implementar el trait EventHandler para nuestra estructura -#[async_trait] -impl EventHandler for Handler { - // Evento message: Se llama cada vez que se envía un mensaje - async fn message(&self, ctx: Context, msg: Message) { - // Ignorar mensajes enviados por nuestro propio bot (u otros bots) - if msg.author.bot { - return; - } - - // Si el mensaje es "ping", respondemos "pong" - if msg.content.to_lowercase() == "ping" { - println!("[{}] dijo ping, respondiendo...", msg.author.name); - - // Enviar un mensaje de respuesta - if let Err(why) = msg.reply(&ctx.http, "pong 🏓").await { - println!("Error enviando mensaje: {:?}", why); - } - } - } - - // Evento ready: Se llama cuando el bot se conecta exitosamente - async fn ready(&self, _: Context, ready: Ready) { - println!("✅ Conectado exitosamente como {}", ready.user.name); - } -} - -#[tokio::main] -async fn main() { - // 3. Obtener el token. Reemplaza esto o usa variables de entorno - let mut token = env::var("DISCORD_TOKEN").unwrap_or_else(|_| "TU_TOKEN_AQUI".to_string()); - - if token == "TU_TOKEN_AQUI" { - println!("⚠️ ADVERTENCIA: Reemplaza 'TU_TOKEN_AQUI' con el token de tu bot de Discord."); - println!("Puedes obtenerlo en: https://discord.com/developers/applications"); - return; - } - - // 4. Configurar los Intents (permisos) - // GUILD_MESSAGES para recibir los eventos y MESSAGE_CONTENT para leer el texto - let intents = GatewayIntents::GUILD_MESSAGES - | GatewayIntents::MESSAGE_CONTENT - | GatewayIntents::GUILDS; - - // 5. Crear el cliente con el token, los intents y nuestro Handler - let mut client = Client::builder(&token, intents) - .event_handler(Handler) - .await - .expect("Error creando el cliente"); - - println!("Iniciando bot..."); - - // 6. Iniciar la conexión - if let Err(why) = client.start().await { - println!("Error en el cliente: {:?}", why); - } -} diff --git a/archivo-historico/ejemplos/nerve/08-puente-http/README.md b/archivo-historico/ejemplos/nerve/08-puente-http/README.md deleted file mode 100644 index 4a9da45..0000000 --- a/archivo-historico/ejemplos/nerve/08-puente-http/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Ejemplos: Servidor HTTP Básico (Puente HTTP) - -Para construir un puente HTTP, necesitas levantar un servidor web que pueda escuchar peticiones (como un `POST`), leer un cuerpo en JSON, y luego hacer algo con él. En el reto "Puente HTTP para Nerve", tu servidor recibirá una petición HTTP y usará el cliente Nerve para enviar esa información a la red. - -Estos ejemplos muestran cómo crear servidores HTTP muy simples y modernos en distintos lenguajes, sin añadir mucha complejidad, preparándote para integrarlos con tu cliente Nerve. - -## 🐍 Python (FastAPI) - -FastAPI es un framework moderno y rápido. - -### Requisitos -```bash -pip install fastapi uvicorn -``` - -### Cómo ejecutarlo -```bash -# El archivo debe llamarse ejemplo.py -uvicorn ejemplo:app --reload -``` -Para probarlo, puedes usar cURL: -```bash -curl -X POST -H "Content-Type: application/json" -d '{"mensaje": "Hola Mundo"}' http://localhost:8000/enviar -``` - -## 🟨 JavaScript (Express) - -Express es el estándar de facto para servidores web rápidos en Node.js. - -### Requisitos -```bash -npm install express -``` - -### Cómo ejecutarlo -```bash -node ejemplo.js -``` -Para probarlo: -```bash -curl -X POST -H "Content-Type: application/json" -d '{"mensaje": "Hola Node"}' http://localhost:3000/enviar -``` - -## 🐹 Go (net/http estándar) - -Go incluye un servidor HTTP de producción directamente en su librería estándar, no necesitas instalar nada. - -### Cómo ejecutarlo -```bash -go run ejemplo.go -``` -Para probarlo: -```bash -curl -X POST -H "Content-Type: application/json" -d '{"mensaje": "Hola Go"}' http://localhost:8080/enviar -``` - -## 🦀 Rust (Axum) - -Axum es un framework web muy popular y rápido del ecosistema Tokio. - -### Requisitos (Cargo.toml) -```toml -[dependencies] -axum = "0.7" -tokio = { version = "1.0", features = ["full"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -``` - -### Cómo ejecutarlo -```bash -cargo run -``` -Para probarlo: -```bash -curl -X POST -H "Content-Type: application/json" -d '{"mensaje": "Hola Rust"}' http://localhost:3000/enviar -``` diff --git a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.go b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.go deleted file mode 100644 index 75fead2..0000000 --- a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.go +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Ejemplo: Servidor HTTP con net/http (Estándar de Go) - * - * Qué enseña este ejemplo: - * - Crear una aplicación web básica usando la librería estándar. - * - Decodificar un JSON que viene en el cuerpo de la petición. - * - Manejar métodos HTTP específicos (asegurarse de que sea un POST). - * - * Para tu reto (Puente HTTP para Nerve): - * Cuando la ruta reciba los datos, deberás usar el cliente Nerve - * para retransmitir la información hacia el hub de Nerve. - */ - -package main - -import ( - "encoding/json" - "fmt" - "net/http" -) - -// 1. Definir la estructura (struct) de los datos que esperamos recibir -// Las etiquetas `json:"mensaje"` indican a Go cómo buscar el campo en el JSON. -type MensajePayload struct { - Mensaje string `json:"mensaje"` -} - -func main() { - // 2. Definir las rutas (endpoints) - http.HandleFunc("/", rutaRaiz) - http.HandleFunc("/enviar", recibirYReenviar) - - puerto := ":8080" - fmt.Printf("Servidor Go escuchando en http://localhost%s\n", puerto) - - // 3. Encender el servidor - err := http.ListenAndServe(puerto, nil) - if err != nil { - fmt.Println("Error iniciando el servidor:", err) - } -} - -// Ruta GET básica -func rutaRaiz(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, "Servidor funcionando") -} - -// Ruta POST que será nuestro "puente" -func recibirYReenviar(w http.ResponseWriter, r *http.Request) { - // Asegurarnos de que sólo aceptamos peticiones POST - if r.Method != http.MethodPost { - http.Error(w, "Método no permitido. Usa POST.", http.StatusMethodNotAllowed) - return - } - - // Crear una variable para guardar los datos - var payload MensajePayload - - // Decodificar el JSON del cuerpo (body) de la petición hacia nuestro struct - err := json.NewDecoder(r.Body).Decode(&payload) - if err != nil { - http.Error(w, "JSON inválido", http.StatusBadRequest) - return - } - - if payload.Mensaje == "" { - http.Error(w, "Falta el campo 'mensaje' en el JSON", http.StatusBadRequest) - return - } - - // En la terminal veremos lo que llegó - fmt.Printf("📩 Recibido por HTTP: %s\n", payload.Mensaje) - - // ¡Aquí es donde integrarías Nerve! - // Ejemplo: clienteNerve.Publicar("canal_http", payload.Mensaje) - - // Responder a quien hizo la petición HTTP (con un JSON) - w.Header().Set("Content-Type", "application/json") - respuesta := fmt.Sprintf(`{"status": "ok", "recibido": "%s"}`, payload.Mensaje) - fmt.Fprintln(w, respuesta) -} diff --git a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.js b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.js deleted file mode 100644 index d8b2989..0000000 --- a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Ejemplo: Servidor HTTP con Express - * - * Qué enseña este ejemplo: - * - Crear una aplicación básica de Express. - * - Configurar `express.json()` para parsear el cuerpo de las peticiones. - * - Crear un endpoint (ruta) POST que lea esos datos. - * - * Para tu reto (Puente HTTP para Nerve): - * Cuando el endpoint reciba los datos, deberás usar el cliente Nerve - * para retransmitir la información hacia el hub de Nerve. - */ - -const express = require('express'); - -// 1. Inicializar la aplicación -const app = express(); -const puerto = 3000; - -// 2. Middleware para poder leer JSON en el cuerpo (body) del POST -app.use(express.json()); - -// 3. Ruta GET básica -app.get('/', (req, res) => { - res.json({ status: "Servidor funcionando" }); -}); - -// 4. Ruta POST que será nuestro "puente" -app.post('/enviar', (req, res) => { - // req.body contendrá el JSON que nos envíen (ej. {"mensaje": "Hola"}) - const mensaje = req.body.mensaje; - - if (!mensaje) { - return res.status(400).json({ error: "Falta el campo 'mensaje' en el JSON" }); - } - - // En la terminal veremos lo que llegó - console.log(`📩 Recibido por HTTP: ${mensaje}`); - - // ¡Aquí es donde integrarías Nerve! - // Ejemplo: clienteNerve.publicar("canal_http", mensaje); - - // Responder a quien hizo la petición HTTP que todo salió bien - res.json({ status: "ok", recibido: mensaje }); -}); - -// 5. Encender el servidor -app.listen(puerto, () => { - console.log(`Servidor Express escuchando en http://localhost:${puerto}`); -}); diff --git a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.py b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.py deleted file mode 100644 index 94b8e74..0000000 --- a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Ejemplo: Servidor HTTP con FastAPI - -Qué enseña este ejemplo: - - Crear una aplicación básica de FastAPI. - - Definir un modelo de datos (Pydantic) para recibir JSON. - - Crear un endpoint (ruta) POST que lea esos datos. - -Para tu reto (Puente HTTP para Nerve): - Cuando el endpoint reciba los datos, deberás usar el cliente Nerve (app.state.nerve) - para retransmitir la información hacia el hub de Nerve. -""" - -from fastapi import FastAPI -from pydantic import BaseModel - - -# 1. Definir el modelo de los datos que esperamos recibir -# Esto asegura que quien envíe el POST, nos mande un JSON con un campo "mensaje" -class MensajePayload(BaseModel): - mensaje: str - - -# 2. Inicializar la aplicación FastAPI -app = FastAPI() - - -# 3. Ruta GET básica para comprobar que funciona -@app.get("/") -def leer_raiz(): - return {"status": "Servidor funcionando"} - - -# 4. Ruta POST que será nuestro "puente" -# FastAPI automáticamente convierte el cuerpo del POST desde JSON hacia nuestro modelo -@app.post("/enviar") -def recibir_y_reenviar(payload: MensajePayload): - # En la terminal veremos lo que llegó - print(f"📩 Recibido por HTTP: {payload.mensaje}") - - # ¡Aquí es donde integrarías Nerve! - # Ejemplo: cliente_nerve.publicar("canal_http", payload.mensaje) - - return {"status": "ok", "recibido": payload.mensaje} - - -# NOTA: Para ejecutar esto se usa uvicorn desde la terminal: -# uvicorn ejemplo:app --reload diff --git a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.rs b/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.rs deleted file mode 100644 index 7f6c165..0000000 --- a/archivo-historico/ejemplos/nerve/08-puente-http/ejemplo.rs +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Ejemplo: Servidor HTTP con Axum - * - * Qué enseña este ejemplo: - * - Crear un servidor HTTP moderno y rápido en Rust usando axum. - * - Usar 'serde' para deserializar un cuerpo JSON automáticamente. - * - Configurar una ruta POST y enviar una respuesta JSON. - * - * Para tu reto (Puente HTTP para Nerve): - * Cuando la ruta reciba los datos, deberás usar el cliente Nerve - * para retransmitir la información hacia el hub de Nerve. Para compartir - * el cliente Nerve con Axum, necesitarás usar el sistema de "State" de Axum - * con un Arc>. - */ - -use axum::{ - routing::{get, post}, - Router, - Json, -}; -use serde::{Deserialize, Serialize}; - -// 1. Estructura para lo que ESPERAMOS recibir -// 'Deserialize' permite a Axum convertir automáticamente el JSON entrante a este struct -#[derive(Deserialize)] -struct MensajePayload { - mensaje: String, -} - -// 2. Estructura para lo que VAMOS a responder -// 'Serialize' permite a Axum convertir el struct a JSON para responder -#[derive(Serialize)] -struct RespuestaPayload { - status: String, - recibido: String, -} - -#[tokio::main] -async fn main() { - // 3. Crear el enrutador de la aplicación y definir las rutas - let app = Router::new() - .route("/", get(ruta_raiz)) - .route("/enviar", post(recibir_y_reenviar)); - - // 4. Iniciar el servidor - let puerto = "0.0.0.0:3000"; - let listener = tokio::net::TcpListener::bind(puerto).await.unwrap(); - - println!("Servidor Rust (Axum) escuchando en http://localhost:3000"); - axum::serve(listener, app).await.unwrap(); -} - -// Ruta GET básica -async fn ruta_raiz() -> &'static str { - "Servidor funcionando" -} - -// 5. Ruta POST que será nuestro "puente" -// Axum se encarga de verificar que el POST tenga un JSON válido y lo mapea a `payload` -async fn recibir_y_reenviar(Json(payload): Json) -> Json { - - // En la terminal veremos lo que llegó - println!("📩 Recibido por HTTP: {}", payload.mensaje); - - // ¡Aquí es donde integrarías Nerve! - // Ejemplo: state.cliente_nerve.publicar("canal_http", &payload.mensaje).await; - - // Retornamos un JSON de respuesta - let respuesta = RespuestaPayload { - status: "ok".to_string(), - recibido: payload.mensaje, - }; - - Json(respuesta) -} diff --git a/archivo-historico/ejemplos/nerve/09-probador-de-carga/README.md b/archivo-historico/ejemplos/nerve/09-probador-de-carga/README.md deleted file mode 100644 index 016b347..0000000 --- a/archivo-historico/ejemplos/nerve/09-probador-de-carga/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Ejemplos: Concurrencia y Pruebas de Carga (Load Tester) - -Para crear un "Probador de carga" (Load Tester) efectivo, necesitas enviar muchas peticiones al mismo tiempo (concurrencia) en lugar de enviar una, esperar a que termine, y luego enviar la siguiente. - -En el reto "Probador de carga para el Hub de Nerve", utilizarás estos conceptos de concurrencia para instanciar múltiples clientes Nerve o usar un solo cliente para bombardear la red de mensajes lo más rápido posible, midiendo cuánto tarda en responder. - -Estos ejemplos muestran cómo ejecutar múltiples tareas "al mismo tiempo" en distintos lenguajes. - -## 🐍 Python (asyncio) - -Python usa `asyncio` para la concurrencia basada en eventos. - -### Cómo ejecutarlo -```bash -python ejemplo.py -``` - -## 🟨 JavaScript (Promise.all) - -JavaScript maneja la concurrencia a través de Promesas asíncronas y el Event Loop. - -### Cómo ejecutarlo -```bash -node ejemplo.js -``` - -## 🐹 Go (Goroutines) - -Go fue diseñado para la concurrencia. Las `goroutines` son hilos súper ligeros. - -### Cómo ejecutarlo -```bash -go run ejemplo.go -``` - -## 🦀 Rust (Tokio) - -Rust usa `tokio` como runtime asíncrono, permitiendo `tokio::spawn` para tareas concurrentes de muy alto rendimiento. - -### Requisitos (Cargo.toml) -```toml -[dependencies] -tokio = { version = "1.0", features = ["full"] } -``` - -### Cómo ejecutarlo -```bash -cargo run -``` diff --git a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.go b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.go deleted file mode 100644 index 21d5902..0000000 --- a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.go +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Ejemplo: Concurrencia con Goroutines y WaitGroups en Go - * - * Qué enseña este ejemplo: - * - Lanzar "goroutines" (hilos ligeros) usando la palabra clave `go`. - * - Sincronizar las goroutines usando un `sync.WaitGroup` para esperar a que todas terminen. - * - Medir el tiempo transcurrido usando `time.Since`. - * - * Para tu reto (Probador de Carga Nerve): - * Reemplazarás `simularPeticion()` con un envío al servidor usando - * tu cliente Nerve para ver cuánto tráfico puede aguantar tu red. - */ - -package main - -import ( - "fmt" - "sync" - "time" -) - -// 1. Tarea que se ejecutará de forma concurrente -// Recibe un puntero al WaitGroup para avisar cuando termine -func simularPeticion(idPeticion int, wg *sync.WaitGroup) { - // Importante: al terminar la función, avisa al WaitGroup - defer wg.Done() - - // Simulamos que enviar un mensaje tarda 100 milisegundos (0.1s) - time.Sleep(100 * time.Millisecond) - - // Aquí iría tu cliente Nerve: clienteNerve.Publicar(...) -} - -func main() { - totalMensajes := 1000 - fmt.Printf("🚀 Iniciando prueba de carga: %d peticiones...\n", totalMensajes) - - // Guardar el tiempo de inicio - inicio := time.Now() - - // 2. Crear un WaitGroup para esperar a que todas las goroutines terminen - var wg sync.WaitGroup - - // Decirle al WaitGroup cuántas tareas vamos a lanzar - wg.Add(totalMensajes) - - // 3. Lanzar todas las tareas concurrentemente - for i := 0; i < totalMensajes; i++ { - // La palabra `go` lanza la función en un hilo ligero (goroutine) - // y el bucle for sigue inmediatamente a la siguiente iteración - go simularPeticion(i, &wg) - } - - // 4. Pausar la ejecución principal hasta que el contador del WaitGroup llegue a cero - wg.Wait() - - // Calcular el tiempo transcurrido - tiempoTotal := time.Since(inicio).Seconds() - - // En este ejemplo simple asumimos que todas fueron exitosas - // Para contar éxitos de forma segura en concurrencia necesitarías canales (channels) o sync.Mutex - exitosos := totalMensajes - - // 5. Mostrar estadísticas - fmt.Printf("✅ Prueba finalizada en %.2f segundos.\n", tiempoTotal) - fmt.Printf("📊 Mensajes exitosos: %d/%d\n", exitosos, totalMensajes) - fmt.Printf("⚡ Rendimiento: %.2f peticiones/segundo\n", float64(totalMensajes)/tiempoTotal) -} diff --git a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.js b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.js deleted file mode 100644 index dd8608f..0000000 --- a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Ejemplo: Concurrencia con Promise.all en JavaScript - * - * Qué enseña este ejemplo: - * - Usar `Promise.all` para ejecutar múltiples promesas concurrentemente. - * - Medir el tiempo usando `performance.now()`. - * - * Para tu reto (Probador de Carga Nerve): - * Reemplazarás `simularPeticion()` con llamadas al cliente Nerve - * (como publicar mensajes) para medir cuántas peticiones aguanta la red - * por segundo. - */ - -// Función auxiliar para simular un retraso sin bloquear el hilo principal -const setTimeoutAsync = (ms) => new Promise(resolve => setTimeout(resolve, ms)); - -// 1. Definir una tarea asíncrona que simula trabajo -async function simularPeticion(idPeticion) { - // Simulamos que enviar un mensaje tarda 100 milisegundos (0.1s) - await setTimeoutAsync(100); - // Retornamos true si fue exitoso (o podríamos hacer un try/catch) - return true; -} - -async function main() { - const totalMensajes = 1000; - console.log(`🚀 Iniciando prueba de carga: ${totalMensajes} peticiones...`); - - // Guardar el tiempo de inicio (más preciso que Date.now()) - const inicio = performance.now(); - - // 2. Crear un arreglo (array) con todas las promesas (las peticiones ejecutándose) - const promesas = []; - for (let i = 0; i < totalMensajes; i++) { - // Al llamar a la función sin `await`, comienza a ejecutarse - // pero no esperamos a que termine, solo guardamos la promesa - promesas.push(simularPeticion(i)); - } - - // 3. Promise.all espera a que TODAS las promesas terminen en paralelo - const resultados = await Promise.all(promesas); - - // Calcular el tiempo total transcurrido - const fin = performance.now(); - // Convertir de milisegundos a segundos - const tiempoTotal = (fin - inicio) / 1000; - - // Contar cuántos retornaron `true` - const exitosos = resultados.filter(r => r === true).length; - - // 4. Mostrar estadísticas - console.log(`✅ Prueba finalizada en ${tiempoTotal.toFixed(2)} segundos.`); - console.log(`📊 Mensajes exitosos: ${exitosos}/${totalMensajes}`); - console.log(`⚡ Rendimiento: ${(totalMensajes / tiempoTotal).toFixed(2)} peticiones/segundo`); -} - -main(); diff --git a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.py b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.py deleted file mode 100644 index 1a4465c..0000000 --- a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Ejemplo: Concurrencia con asyncio en Python - -Qué enseña este ejemplo: - - Usar `asyncio.gather` para lanzar múltiples tareas al mismo tiempo. - - Medir cuánto tarda un bloque de código en ejecutarse usando `time.time()`. - -Para tu reto (Probador de Carga Nerve): - Reemplazarás `simular_peticion()` con `cliente.publicar(...)` o - una llamada remota de Nerve para bombardear la red y medir el - rendimiento total (mensajes por segundo). -""" - -import asyncio -import time - - -# 1. Definir una tarea asíncrona que simula trabajo -async def simular_peticion(id_peticion: int): - # Simulamos que enviar un mensaje tarda 100 milisegundos (0.1s) - # Importante usar asyncio.sleep para no bloquear el hilo principal - await asyncio.sleep(0.1) - # Podrías retornar el resultado o simplemente confirmar - return True - - -async def main(): - total_mensajes = 1000 - print(f"🚀 Iniciando prueba de carga: {total_mensajes} peticiones...") - - # Guardar el tiempo de inicio - inicio = time.time() - - # 2. Crear una lista con todas las "promesas" o "tareas" a ejecutar - tareas = [] - for i in range(total_mensajes): - tareas.append(simular_peticion(i)) - - # 3. asyncio.gather ejecuta todas las tareas en paralelo (concurrentemente) - # Espera hasta que todas terminen y devuelve una lista con sus resultados - resultados = await asyncio.gather(*tareas) - - # Calcular el tiempo total transcurrido - fin = time.time() - tiempo_total = fin - inicio - - exitosos = sum(1 for r in resultados if r is True) - - # 4. Mostrar estadísticas - print(f"✅ Prueba finalizada en {tiempo_total:.2f} segundos.") - print(f"📊 Mensajes exitosos: {exitosos}/{total_mensajes}") - print(f"⚡ Rendimiento: {(total_mensajes / tiempo_total):.2f} peticiones/segundo") - - -if __name__ == "__main__": - # Iniciar el bucle de eventos asíncrono - asyncio.run(main()) diff --git a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.rs b/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.rs deleted file mode 100644 index 7d571ab..0000000 --- a/archivo-historico/ejemplos/nerve/09-probador-de-carga/ejemplo.rs +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Ejemplo: Concurrencia con tokio::spawn en Rust - * - * Qué enseña este ejemplo: - * - Lanzar tareas concurrentes asíncronas con `tokio::spawn`. - * - Usar `tokio::time::sleep` de manera no bloqueante. - * - Esperar a que un conjunto de tareas terminen y recolectar sus resultados con `JoinSet` o `join_all`. - * - Medir el tiempo con `std::time::Instant`. - * - * Para tu reto (Probador de Carga Nerve): - * Reemplazarás `simular_peticion()` con llamadas al cliente Nerve para - * inundar la red de mensajes lo más rápido posible. Rust y Tokio son - * excelentes para lograr el máximo rendimiento en estas pruebas. - */ - -use std::time::Instant; -use tokio::time::{sleep, Duration}; -use tokio::task::JoinSet; - -// 1. Definir una tarea asíncrona -async fn simular_peticion(id: usize) -> bool { - // Simulamos que enviar un mensaje tarda 100 milisegundos - sleep(Duration::from_millis(100)).await; - // Retornamos true indicando éxito - true -} - -#[tokio::main] -async fn main() { - let total_mensajes = 1000; - println!("🚀 Iniciando prueba de carga: {} peticiones...", total_mensajes); - - // Guardar el tiempo de inicio - let inicio = Instant::now(); - - // 2. JoinSet es una estructura de Tokio ideal para manejar múltiples tareas - // concurrentes y esperar a que terminen. - let mut tareas = JoinSet::new(); - - // 3. Lanzamos todas las tareas concurrentemente usando tokio::spawn implícito en JoinSet - for i in 0..total_mensajes { - tareas.spawn(async move { - simular_peticion(i).await - }); - } - - let mut exitosos = 0; - - // 4. Recolectar los resultados conforme van terminando - // join_next() espera hasta que UNA de las tareas termine y nos da su resultado - while let Some(resultado) = tareas.join_next().await { - match resultado { - Ok(fue_exito) => { - if fue_exito { - exitosos += 1; - } - } - Err(e) => println!("Error en una tarea: {:?}", e), - } - } - - // Calcular el tiempo transcurrido en segundos (con decimales) - let tiempo_total = inicio.elapsed().as_secs_f64(); - - // 5. Mostrar estadísticas - println!("✅ Prueba finalizada en {:.2} segundos.", tiempo_total); - println!("📊 Mensajes exitosos: {}/{}", exitosos, total_mensajes); - println!( - "⚡ Rendimiento: {:.2} peticiones/segundo", - (total_mensajes as f64) / tiempo_total - ); -} diff --git a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md deleted file mode 100644 index f8efe3c..0000000 --- a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Ejemplos: Sockets TCP Base (Nuevo Cliente) - -Para crear un cliente de Nerve en un lenguaje completamente nuevo, la pieza fundamental es entender cómo conectarse a un **Socket TCP** para enviar y recibir datos. Nerve internamente funciona pasando objetos JSON sobre TCP (IPC). - -Estos ejemplos no utilizan ninguna librería de Nerve, sino las librerías estándar de cada lenguaje para abrir un Socket TCP, enviar un mensaje "Hola" (o un JSON simulado), y esperar una respuesta. - -A partir de estas piezas básicas, en el reto "Cliente en Nuevo Lenguaje", construirás las clases o estructuras para encapsular esta conexión, manejar la reconexión y proveer una API amigable (`conectar()`, `publicar()`, `suscribir()`). - -## 🐍 Python (socket) - -### Cómo ejecutarlo -```bash -# Primero levanta un servidor TCP de prueba (como netcat) en otra terminal: -# nc -l 5000 -python ejemplo.py -``` - -## 🟨 JavaScript (net) - -### Cómo ejecutarlo -```bash -# Primero levanta un servidor TCP de prueba: -# nc -l 5000 -node ejemplo.js -``` - -## 🐹 Go (net) - -### Cómo ejecutarlo -```bash -# Primero levanta un servidor TCP de prueba: -# nc -l 5000 -go run ejemplo.go -``` - -## 🦀 Rust (std::net) - -### Cómo ejecutarlo -```bash -# Primero levanta un servidor TCP de prueba: -# nc -l 5000 -cargo run -``` diff --git a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go deleted file mode 100644 index 0320e94..0000000 --- a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.go +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Ejemplo: Cliente de Socket TCP en Go - * - * Qué enseña este ejemplo: - * - Conectarse a un servidor TCP con el paquete `net`. - * - Escribir en la conexión usando `fmt.Fprintf` o `conn.Write`. - * - Leer la respuesta del servidor usando `bufio.NewReader`. - * - * Para tu reto (Nuevo Cliente Nerve): - * Go es fenomenal para clientes de red. Podrás lanzar una goroutine que se - * quede bloqueada leyendo `bufio.ReadString('\n')` para recibir mensajes, - * mientras tu programa principal puede seguir llamando a `Publicar()`. - */ - -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "net" - "strings" -) - -func main() { - host := "127.0.0.1" - puerto := "5000" - direccion := host + ":" + puerto - - fmt.Printf("🔄 Conectando a %s...\n", direccion) - - // 1. Intentar conectar (net.Dial funciona para TCP, UDP, etc.) - conn, err := net.Dial("tcp", direccion) - if err != nil { - fmt.Println("❌ No se pudo conectar. ¿Está el servidor encendido?") - fmt.Println("Error:", err) - return - } - // Asegurar que cerraremos la conexión al terminar la función - defer conn.Close() - fmt.Println("✅ Conectado exitosamente") - - // 2. Preparar los datos - // Usamos un mapa como ejemplo rápido, en código real deberías usar un struct - payload := map[string]string{ - "tipo": "publicar", - "canal": "test", - "contenido": "Hola desde el nuevo cliente Go!", - } - - // 3. Convertir el mapa a JSON Bytes - datosBytes, err := json.Marshal(payload) - if err != nil { - fmt.Println("Error creando JSON:", err) - return - } - - // 4. Enviar los datos (añadiendo el salto de línea al final) - datosStr := string(datosBytes) + "\n" - fmt.Fprintf(conn, "%s", datosStr) - fmt.Printf("⬆️ Enviado: %s\n", strings.TrimSpace(datosStr)) - - // 5. Esperar la respuesta (leyendo hasta encontrar un salto de línea \n) - respuesta, err := bufio.NewReader(conn).ReadString('\n') - if err != nil { - fmt.Println("⚠️ El servidor cerró la conexión o hubo un error al leer.") - return - } - - // Mostrar la respuesta - fmt.Printf("⬇️ Recibido: %s\n", strings.TrimSpace(respuesta)) -} diff --git a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js deleted file mode 100644 index b07787a..0000000 --- a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Ejemplo: Cliente de Socket TCP en Node.js (net) - * - * Qué enseña este ejemplo: - * - Conectarse a un servidor usando el módulo `net` de Node.js. - * - Manejar eventos (connect, data, error, close). - * - Enviar y recibir strings JSON (Node.js maneja la codificación a Buffer por ti). - * - * Para tu reto (Nuevo Cliente Nerve): - * La naturaleza orientada a eventos de Node.js (client.on) es perfecta para - * Nerve, ya que puedes escuchar 'data' de forma permanente y emitir eventos - * propios (usando EventEmitter) cuando lleguen mensajes de otros clientes. - */ - -const net = require('net'); - -function main() { - const host = '127.0.0.1'; - const puerto = 5000; - - console.log(`🔄 Conectando a ${host}:${puerto}...`); - - // 1. Crear la conexión TCP - const client = new net.Socket(); - - // 2. Intentar conectar - client.connect(puerto, host, () => { - console.log('✅ Conectado exitosamente'); - - // 3. Preparar el payload - const payload = { - tipo: "publicar", - canal: "test", - contenido: "Hola desde el nuevo cliente JS!" - }; - - // 4. Enviar los datos convertidos a JSON String + salto de línea - const datosStr = JSON.stringify(payload) + "\n"; - client.write(datosStr); - console.log(`⬆️ Enviado: ${datosStr.trim()}`); - }); - - // 5. Escuchar el evento 'data' para recibir las respuestas del servidor - client.on('data', (data) => { - // data es un Buffer, al llamar .toString() lo convertimos a String - const respuesta = data.toString().trim(); - console.log(`⬇️ Recibido: ${respuesta}`); - - // En este ejemplo simple, cerramos la conexión luego de recibir respuesta. - // ¡En tu cliente Nerve querrás mantenerla abierta! - client.destroy(); - }); - - // Manejar errores (como si el servidor no está encendido) - client.on('error', (err) => { - if (err.code === 'ECONNREFUSED') { - console.log('❌ No se pudo conectar. ¿Está el servidor encendido?'); - } else { - console.log(`❌ Error: ${err.message}`); - } - }); - - // Saber cuándo se cerró la conexión - client.on('close', () => { - console.log('🔌 Conexión cerrada'); - }); -} - -main(); diff --git a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py deleted file mode 100644 index 6507af4..0000000 --- a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Ejemplo: Cliente de Socket TCP en Python - -Qué enseña este ejemplo: - - Conectarse a un servidor a través de una dirección IP y un puerto usando sockets. - - Codificar un JSON a bytes y enviarlo. - - Esperar y decodificar la respuesta del servidor. - -Para tu reto (Nuevo Cliente Nerve): - En un cliente Nerve real, querrás mantener este socket abierto (persistencia) - y ejecutar un bucle (loop) en un hilo (thread) separado para estar siempre - escuchando los nuevos mensajes que el Hub envía, disparando callbacks o - eventos a tu aplicación. -""" - -import socket -import json - - -def main(): - host = "127.0.0.1" - puerto = 5000 - - print(f"🔄 Conectando a {host}:{puerto}...") - - # 1. Crear el socket TCP (AF_INET para IPv4, SOCK_STREAM para TCP) - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - try: - # 2. Conectarse al servidor - s.connect((host, puerto)) - print("✅ Conectado exitosamente") - - # 3. Preparar el payload simulando un mensaje Nerve - payload = { - "tipo": "publicar", - "canal": "test", - "contenido": "Hola desde el nuevo cliente Python!", - } - - # 4. Enviar los datos. Deben convertirse de Diccionario -> JSON String -> Bytes - datos_str = json.dumps(payload) + "\n" - s.sendall(datos_str.encode("utf-8")) - print(f"⬆️ Enviado: {datos_str.strip()}") - - # 5. Esperar la respuesta (leyendo hasta 1024 bytes) - datos_recibidos = s.recv(1024) - if datos_recibidos: - # Decodificar de Bytes -> String - respuesta = datos_recibidos.decode("utf-8").strip() - print(f"⬇️ Recibido: {respuesta}") - else: - print("⚠️ El servidor cerró la conexión sin responder.") - - except ConnectionRefusedError: - print("❌ No se pudo conectar. ¿Está el servidor encendido?") - except Exception as e: - print(f"❌ Error inesperado: {e}") - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs b/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs deleted file mode 100644 index a470a48..0000000 --- a/archivo-historico/ejemplos/nerve/10-cliente-nuevo-lenguaje/ejemplo.rs +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Ejemplo: Cliente de Socket TCP en Rust - * - * Qué enseña este ejemplo: - * - Conectarse a un servidor usando `std::net::TcpStream` de la librería estándar de Rust. - * - Escribir en el stream de red de forma segura. - * - Leer la respuesta del servidor en un buffer. - * - * Para tu reto (Nuevo Cliente Nerve): - * Rust te obliga a ser muy preciso con la memoria y la pertenencia (ownership). - * En un cliente real, probablemente querrás usar `tokio::net::TcpStream` - * (asíncrono) para poder separar la lógica de envío de la lógica de escucha, - * o usar hilos (`std::thread::spawn`) para leer del socket continuamente - * mientras otra parte del programa escribe. - */ - -use std::io::{Read, Write}; -use std::net::TcpStream; -use serde_json::json; - -fn main() { - let host = "127.0.0.1"; - let puerto = "5000"; - let direccion = format!("{}:{}", host, puerto); - - println!("🔄 Conectando a {}...", direccion); - - // 1. Intentar conectar (esto bloquea hasta conectar o fallar) - match TcpStream::connect(&direccion) { - Ok(mut stream) => { - println!("✅ Conectado exitosamente"); - - // 2. Preparar el payload usando la macro `json!` de serde_json - let payload = json!({ - "tipo": "publicar", - "canal": "test", - "contenido": "Hola desde el nuevo cliente Rust!" - }); - - // 3. Convertir el JSON a String y agregar salto de línea - let mut datos_str = payload.to_string(); - datos_str.push('\n'); - - // 4. Enviar los datos escribiendo en el stream de red - // as_bytes() convierte el String en &[u8] que es lo que espera write_all - if let Err(e) = stream.write_all(datos_str.as_bytes()) { - println!("Error enviando los datos: {}", e); - return; - } - println!("⬆️ Enviado: {}", datos_str.trim()); - - // 5. Preparar un buffer (arreglo de 1024 bytes) para recibir la respuesta - let mut buffer = [0; 1024]; - - // 6. Leer la respuesta del servidor - match stream.read(&mut buffer) { - Ok(bytes_leidos) => { - if bytes_leidos == 0 { - println!("⚠️ El servidor cerró la conexión sin responder."); - } else { - // Convertir los bytes leídos de vuelta a String - // Solo tomamos el "slice" de los bytes que realmente se leyeron - let respuesta = String::from_utf8_lossy(&buffer[0..bytes_leidos]); - println!("⬇️ Recibido: {}", respuesta.trim()); - } - } - Err(e) => { - println!("Error leyendo respuesta: {}", e); - } - } - } - Err(e) => { - println!("❌ No se pudo conectar. ¿Está el servidor encendido?"); - println!("Detalle: {}", e); - } - } -} diff --git a/archivo-historico/glosario/README.md b/archivo-historico/glosario/README.md deleted file mode 100644 index a69e39a..0000000 --- a/archivo-historico/glosario/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# 📚 Glosario de Nerve Community - -Bienvenido al glosario completo. Aquí encontrarás explicaciones claras de **todos** los términos que usamos en el repositorio, organizados por tema para que puedas consultarlos rápidamente. - -## 📂 Índice de Glosarios - -| Archivo | Qué cubre | -| :--- | :--- | -| [🔧 Git y GitHub](git-github.md) | `git clone`, `fork`, `commit`, `push`, `pull`, `branch`, `PR`, `merge`, `rebase`... | -| [🌐 Redes y Sockets](redes-sockets.md) | `socket`, `IPC`, `Unix Socket`, `TCP`, `servidor`, `cliente`, `puerto`... | -| [⌨️ Terminal: Linux y macOS](terminal-linux.md) | `pwd`, `ls`, `nano`, `chmod`, `apt`, Termux, `pkg`, variables de entorno... | -| [🪟 Terminal: Windows](terminal-windows.md) | CMD, PowerShell, Git Bash, WSL, `dir`, `python`, instalar Python en Windows... | -| [🛠️ Conceptos de Software](conceptos-software.md) | `bug`, `refactoring`, `dependencias`, `librería estándar`, `CLI`, `API`, `CSV`, `JSON`... | - -## 🧑‍💻 Glosarios por lenguaje - -| Lenguaje | Carpeta | -| :--- | :--- | -| Python | [glosario/python/](python/README.md) | -| Go | [glosario/go/](go/README.md) | -| Rust | [glosario/rust/](rust/README.md) | -| JavaScript | [glosario/javascript/](javascript/README.md) | - ---- - -> 💡 **Tip:** Si encuentras un término que no está aquí, abre un Issue y lo agregamos. ¡El glosario lo construimos entre todos! diff --git a/archivo-historico/glosario/conceptos-software.md b/archivo-historico/glosario/conceptos-software.md deleted file mode 100644 index 15e6e51..0000000 --- a/archivo-historico/glosario/conceptos-software.md +++ /dev/null @@ -1,76 +0,0 @@ -# 🛠️ Glosario: Conceptos de Software - -Términos generales de desarrollo de software que aparecen en los retos, documentación y conversaciones del día a día en Nerve Community. Aquí nada se da por sabido. - ---- - -## Tipos de Archivos y Formatos de Datos - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Markdown (.md)** | Lenguaje de marcado simple | Formato de texto con una sintaxis sencilla (`**negrita**`, `# Título`) que se convierte en HTML. GitHub lo renderiza automáticamente. | Este mismo archivo que estás leyendo | N/A | - | -| **TXT** | Archivo de texto plano | El formato más básico. Sin formato, sin colores, solo caracteres. | `notas.txt`, `requirements.txt` | N/A | - | -| **YAML (.yml / .yaml)** | Yet Another Markup Language | Formato de configuración muy legible para humanos. Usado en archivos de CI/CD como GitHub Actions. | Los workflows en `.github/workflows/` | N/A | - | - ---- - -## Arquitectura y Diseño de Software - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **CLI (Command Line Interface)** | Interfaz de línea de comandos | Un programa que se usa desde la terminal escribiendo texto, en vez de clicando botones. La mayoría de los retos son CLIs. | `python3 lista_tareas.py --agregar "Estudiar"` | N/A | - | -| **Librería / Biblioteca** | Código reutilizable empaquetado | Colección de funciones y clases escritas por otros que puedes importar para no reinventar la rueda. | `import requests` | N/A | - | -| **Framework** | Estructura base para construir apps | Un esqueleto con reglas y herramientas que te dice cómo organizar tu proyecto. Más opinionado que una librería. | FastAPI (para APIs), discord.py (para bots) | N/A | - | -| **Módulo** | Unidad básica de código reutilizable | Un archivo `.py` con funciones que puedes importar en otros archivos de tu proyecto. | `from conversiones import celsius_a_fahrenheit` | N/A | - | -| **Dependencia** | Librería de la que depende tu código | Una librería externa que tu proyecto necesita para funcionar. Si no está instalada, el programa falla. | `requests` es una dependencia del reto 02 | N/A | - | -| **Open Source (Código Abierto)** | Software con código público y libre | Software cuyo código fuente es público, gratuito y puede ser modificado y redistribuido. Nerve y este repo son Open Source. | Licencia GNU GPL v3 | N/A | - | -| **GNU GPL v3** | Licencia de Nerve Community | Una licencia Open Source que garantiza que el software es libre, y que cualquier derivado también debe serlo. | El archivo `LICENSE.md` en el repo | N/A | - | -| **multiplataforma** | Funciona en varios sistemas operativos | Software que funciona en Linux, macOS y Windows sin cambios. Nerve es multiplataforma. | Nerve usa Unix Sockets en Linux/macOS y TCP en Windows | N/A | - | -| **offline-first** | Funciona sin internet | Diseñado para funcionar sin conexión a internet. Nerve se conecta localmente, no usa la nube. | Nerve solo necesita que los procesos estén en la misma máquina | N/A | - | - ---- - -## Calidad, Bugs y Proceso de Desarrollo - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Bug** | Error en el software | Fallo en el código que hace que el programa no funcione como se esperaba. | "El conversor de divisas dice que 1 USD = 0 MXN. Es un bug." | N/A | - | -| **Debugging** | Depuración — proceso de encontrar bugs | El arte de investigar y corregir errores en el código. Puede ser con herramientas o simplemente leyendo y pensando. | Usar `print()` para ver el valor de una variable | N/A | - | -| **Refactoring** | Mejorar código sin cambiar su comportamiento | Reescribir el código para que sea más limpio, legible o eficiente, sin que haga cosas diferentes. | Dividir una función gigante en varias funciones pequeñas | N/A | - | -| **Linter** | Analizador automático de estilo | Herramienta que revisa tu código buscando errores de formato, malas prácticas o inconsistencias de estilo. | `black` (Python), que usamos en el CI del repo | N/A | - | -| **CI/CD** | Integración y entrega continua | Automatizaciones que se ejecutan con cada PR: corren tests, verifican formato, despliegan código. | El Linter Compasivo que corre en cada PR de Nerve Community | N/A | - | -| **Test / Prueba** | Código que verifica que tu código funciona | Pequeños programas automáticos que comprueban si tus funciones devuelven los resultados esperados. | `assert suma(2, 3) == 5` | N/A | - | -| **edge case** | Caso borde o extremo | Una entrada o situación inusual que puede romper tu programa. Pensar en ellos es parte del desarrollo robusto. | ¿Qué pasa si el usuario escribe una letra donde va un número? | N/A | - | -| **hardcoded** | Valor fijo escrito directamente en el código | Un valor que no se puede cambiar sin editar el código fuente. Es mala práctica para configuraciones y rutas. | ❌ `ruta = "/home/user/Descargas"` | N/A | - | -| **dry-run** | Modo de simulación sin cambios reales | Ejecutar un script para que muestre qué haría, sin modificar nada. Fundamental en scripts que mueven archivos. | `python organizador.py --dry-run` | N/A | - | -| **logging** | Registro de eventos del programa | Guardar mensajes sobre lo que hace el programa mientras corre. Útil para diagnosticar problemas. | `logging.info("Archivo procesado: datos.csv")` | N/A | - | - ---- - -## Conceptos de Seguridad - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Criptografía** | Ciencia de proteger información | Técnicas para cifrar datos y hacer comunicaciones seguras. | HTTPS cifra los datos entre tu navegador y el servidor | N/A | - | -| **random vs secrets** | Diferencia crucial en Python | `random` genera números predecibles (no seguros para contraseñas). `secrets` genera números impredecibles, criptográficamente seguros. | ✓ `secrets.token_urlsafe(16)` para contraseñas reales | N/A | - | -| **Token de acceso** | Llave de autenticación | Una cadena de texto larga y aleatoria que actúa como contraseña para autenticar accesos a una API o servicio. | Token de GitHub para usar `gh auth login` | N/A | - | - ---- - -## El Ecosistema Alenia / Nerve - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Nerve** | Motor IPC de Alenia Studios | El sistema de comunicación entre procesos que es el tema central de este repo. Los retos de nivel Core giran en torno a él. | [alenia-nerve en GitHub](https://github.com/Kaia-Alenia/alenia-nerve) | N/A | - | -| **Zenith** | Framework principal de Alenia Studios | El framework de Alenia donde viven las herramientas más maduras. Las mejores soluciones de este repo pueden "graduarse" a Zenith. | `zenith-nerve-tools` | N/A | - | -| **nerve-community** | Este repositorio | El espacio de aprendizaje y contribución para construir herramientas y scripts usando Nerve, de forma abierta y colaborativa. | `https://github.com/Kaia-Alenia/nerve-community` | N/A | - | -| **Premio Trimestral** | Reconocimiento a la mejor contribución | La contribución más destacada del trimestre recibe 4 meses gratis de Google AI Pro. | Ver sección "Premio Trimestral" en el README | N/A | - | -| **TRANSPARENCIA.md** | Informe de uso de donaciones | Documento público donde se detalla cómo se usaron los fondos de donaciones de la comunidad. | [TRANSPARENCIA.md](../TRANSPARENCIA.md) | N/A | - | - ---- - -> 💡 **Regla de oro del desarrollo:** Antes de instalar una librería o escribir código complejo, pregúntate: ¿ya existe algo en la librería estándar que haga esto? La mayoría de las veces, ¡la respuesta es sí! - ---- - -← [Volver al Índice del Glosario](README.md) diff --git a/archivo-historico/glosario/git-github.md b/archivo-historico/glosario/git-github.md deleted file mode 100644 index 5418284..0000000 --- a/archivo-historico/glosario/git-github.md +++ /dev/null @@ -1,72 +0,0 @@ -# 🔧 Glosario: Git y GitHub - -Todo lo que necesitas saber sobre el control de versiones y la plataforma GitHub para contribuir en cualquier proyecto Open Source, incluyendo Nerve Community. - ---- - -## Conceptos Fundamentales - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Git** | Programa de control de versiones | Es como una "máquina del tiempo" para tu código. Guarda el historial completo de todos los cambios que hiciste, quién los hizo y cuándo. | `git --version` (para verificar que está instalado) | N/A | - | -| **GitHub** | Plataforma web para guardar código | Una "red social para programadores" donde subes tu código con Git y puedes colaborar con otros. Nerve Community vive aquí. | `https://github.com/Kaia-Alenia/nerve-community` | N/A | - | -| **Repositorio (Repo)** | Carpeta de proyecto con historial | Contiene todos los archivos del proyecto más el historial completo de cambios. Es lo que "clonas" cuando quieres trabajar en un proyecto. | `https://github.com/Kaia-Alenia/nerve-community` | N/A | - | -| **Open Source** | Código abierto y público | Software cuyo código fuente es visible para todos y puede ser modificado y distribuido libremente. Nerve y este repo son Open Source. | Licencia GNU GPL v3 que usa este repo | N/A | - | - ---- - -## Flujo de Trabajo Básico - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Fork** | Copia personal del repositorio | Crea una copia exacta de un repo ajeno en tu cuenta de GitHub. Es como sacarle una fotocopia a un libro de la biblioteca para poder rayarlo sin arruinar el original. | Botón "Fork" en GitHub | N/A | - | -| **git clone** | Descarga del repo a tu computadora | Trae los archivos de GitHub a tu máquina local. Es como llevarte esa fotocopia desde la nube a tu disco duro para trabajar. Siempre clonas *tu fork*. | `git clone https://github.com/...` | N/A | - | -| **Branch (Rama)** | Línea de trabajo independiente | Crea una "dimensión paralela" de tu código. Trabajas en una rama separada para no tocar ni romper la rama principal (`main`). | `git checkout -b solucion-reto-01` | N/A | - | -| **Checkout** | Cambiar de rama | Te "teletransporta" entre ramas. También sirve para crear ramas nuevas con la bandera `-b`. | `git checkout main` o `git checkout -b mi-rama` | N/A | - | -| **git add** | Preparar archivos para el commit | Marca qué archivos quieres incluir en el próximo punto de guardado. Con `.` preparas todos los archivos modificados. | `git add .` o `git add mi_archivo.py` | N/A | - | -| **Commit** | Punto de guardado permanente | Guarda los cambios preparados con un mensaje descriptivo. Es el "Save State" del videojuego, pero para código. | `git commit -m "feat: agregar solución al reto 01"` | N/A | - | -| **Push** | Subir commits a GitHub | Envía los commits que tienes en tu computadora hacia tu repositorio en GitHub (en la nube). | `git push origin solucion-reto-01` | N/A | - | -| **Pull** | Bajar/actualizar desde GitHub | Descarga los cambios más recientes que existan en GitHub hacia tu computadora local. | `git pull origin main` | N/A | - | -| **Pull Request (PR)** | Solicitud de integración de cambios | Le dices al dueño del repo original: *"Oye, hice estos cambios en mi fork, ¿los quieres agregar al proyecto oficial?"* | Clic en "Compare & pull request" en GitHub después de hacer push | N/A | - | -| **Merge** | Fusionar código | Acción de aceptar un Pull Request y unir el código nuevo con el proyecto principal. Lo hacen los maintainers. | "¡Felicidades, hicimos merge de tu PR!" | N/A | - | - ---- - -## Sincronización y Resolución de Problemas - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **origin** | Nombre del repo remoto por defecto | Es el "apodo" que Git le da a tu repositorio en GitHub cuando clonas. Cuando dices `git push origin`, empujas hacia ahí. | `git push origin main` | N/A | - | -| **upstream** | El repositorio original (del que hiciste fork) | Se usa para mantener tu fork sincronizado con los cambios del proyecto original. Se configura una sola vez. | `git remote add upstream https://github.com/Kaia-Alenia/nerve-community.git` | N/A | - | -| **git status** | Estado actual de tu trabajo | Muestra qué archivos modificaste, cuáles están preparados (staged) y cuáles no. Úsalo antes de hacer commit. | `git status` | N/A | - | -| **Conflict (Conflicto)** | Dos versiones del mismo código chocan | Ocurre cuando tú y otra persona editaron la misma línea del mismo archivo. Git no sabe cuál versión elegir y te pide que lo resuelvas manualmente. | "CONFLICT (content): Merge conflict in archivo.py" | N/A | - | -| **Rebase** | Reorganizar historial de commits | Mueve o reaplica tus commits encima de otra rama, como si tu trabajo lo hubieras empezado después de los últimos cambios. Más avanzado que merge. | `git rebase main` | N/A | - | -| **git log** | Historial de commits | Muestra una lista de todos los commits que se han hecho en la rama actual, con autor, fecha y mensaje. | `git log --oneline` | N/A | - | -| **git diff** | Ver qué cambió exactamente | Muestra las diferencias línea por línea entre tu versión actual y la última guardada en un commit. Útil antes de hacer commit. | `git diff mi_archivo.py` | N/A | - | -| **git stash** | Guardar cambios temporalmente | "Guarda" tus cambios sin hacer commit para que puedas cambiar de rama limpiamente y recuperarlos después. | `git stash` y luego `git stash pop` | N/A | - | -| **git commit --amend** | Corregir el último commit | Modifica el mensaje o los archivos del commit que acabas de hacer, sin crear uno nuevo. Solo funciona si aún no hiciste push. | `git commit --amend -m "Mensaje corregido"` | N/A | - | - ---- - -## GitHub: Conceptos de Plataforma - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Issue** | Ticket o tarea dentro de GitHub | Un foro donde se reportan bugs, se piden funciones nuevas o se listan los retos disponibles. En Nerve Community cada reto tiene su propio Issue. | [Issue #1 — Reto 01 Chat Terminal](https://github.com/Kaia-Alenia/nerve-community/issues/1) | N/A | - | -| **Maintainer** | Mantenedor del proyecto | La persona (o equipo) que tiene permisos para hacer merge de PRs y tomar decisiones sobre el proyecto. Alejandro es el maintainer de Nerve Community. | `@Alenia-Studios` | N/A | - | -| **Contribuidor (Contributor)** | Persona que ha enviado código | Cualquier persona cuyo Pull Request haya sido aceptado (merged) en el proyecto. ¡Tú puedes ser uno! | Ver sección "Cumpliendo Retos" en el README | N/A | - | -| **Label (Etiqueta)** | Categoría de un Issue o PR | Tags de colores que clasifican un Issue. Ejemplos: `good-first-issue` (ideal para principiantes), `bug`, `disponible`. | `good-first-issue`, `python-general`, `disponible` | N/A | - | -| **Squash and Merge** | Fusionar aplastando todos los commits en uno | Cuando se hace merge de un PR, todos los commits de esa rama se "aplastan" en un solo commit limpio en `main`. | "Squashed and merged pull request #47" | N/A | - | -| **gh CLI** | GitHub CLI, la línea de comandos de GitHub | Herramienta oficial de GitHub para usar la plataforma desde la terminal: crear PRs, ver issues, autenticarte, etc. | `gh pr create`, `gh auth login`, `gh pr list` | N/A | - | -| **GitHub Actions** | Automatizaciones del repositorio | Scripts automáticos que se ejecutan cuando ocurre algo en el repo (ej: alguien abre un PR). En Nerve Community, se usa para revisar formato del código automáticamente. | El Linter Compasivo que formatea Python con `black` | N/A | - | -| **README.md** | El archivo de presentación del repo | Es el primer archivo que GitHub muestra al entrar a un repositorio. Explica qué es el proyecto, cómo usarlo y cómo contribuir. | Este mismo archivo que estás leyendo | N/A | - | -| **Sparse-Checkout** | Clonar solo una carpeta específica | Técnica avanzada para descargar únicamente la parte del repositorio que necesitas, en vez de todo el proyecto. Ideal si tienes datos limitados (ej: en Termux). | `git sparse-checkout set retos/general/01-lista-de-tareas-cli` | N/A | - | -| **.gitignore** | Archivo de ignorados | Un archivo especial que le dice a Git qué archivos o carpetas NO debe rastrear ni subir (ej. contraseñas, dependencias como `node_modules`). | Archivo llamado `.gitignore` | N/A | - | - ---- - -> 💡 **IMPORTANTE:** No tienes que memorizar todo esto de golpe. Con que recuerdes el ciclo básico — **fork → clone → branch → commit → push → PR** — ya puedes hacer tu primera contribución. ¡El resto lo aprenderás con la práctica! - ---- - -← [Volver al Índice del Glosario](README.md) diff --git a/archivo-historico/glosario/python/README.md b/archivo-historico/glosario/python/README.md deleted file mode 100644 index a159d99..0000000 --- a/archivo-historico/glosario/python/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Glosario de Python - -Índice de todos los términos de Python usados en los retos de nerve-community. - -| Archivo | Qué cubre | -| :--- | :--- | -| [Fundamentos](01-fundamentos.md) | Variables, tipos de datos, operadores, comentarios | -| [Control de flujo](02-control-de-flujo.md) | if/else, for, while, match | -| [Funciones](03-funciones.md) | def, parámetros por defecto, *args/**kwargs, return, lambdas | -| [Estructuras de datos](04-estructuras-de-datos.md) | Listas, tuplas, dicts, sets, list comprehension | -| [Módulos y librerías](05-modulos-y-librerias.md) | import, pip, venv, requirements.txt | -| [Manejo de errores](06-manejo-de-errores.md) | try/except, raise, excepciones comunes | -| [Archivos y E/S](07-archivos-y-entrada-salida.md) | open(), CSV, JSON | -| [POO básica](08-poo-basica.md) | class, __init__, self, herencia básica | diff --git a/archivo-historico/glosario/redes-sockets.md b/archivo-historico/glosario/redes-sockets.md deleted file mode 100644 index 50555e5..0000000 --- a/archivo-historico/glosario/redes-sockets.md +++ /dev/null @@ -1,70 +0,0 @@ -# 🌐 Glosario: Redes y Sockets (Nerve) - -Estos son los conceptos de redes y comunicación que usa **Nerve** para conectar scripts entre sí. No te asustes, se explican desde cero. - ---- - -## Conceptos Fundamentales de Redes - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Red (Network)** | Sistema de conexión entre computadoras | Permite que diferentes programas o máquinas se comuniquen e intercambien datos. Internet es una red de redes. | Tu WiFi conecta tu celular con el router | N/A | - | -| **Protocolo** | Reglas de comunicación acordadas | Un "idioma en común" que dos partes usan para entenderse. Como las reglas del correo postal. | TCP, UDP, HTTP, WebSocket | N/A | - | -| **Puerto (Port)** | Número de "puerta" de un servicio | Un número (0–65535) que identifica un servicio específico dentro de una computadora. Es como el número de apartamento dentro de un edificio. | Puerto 80 → HTTP, Puerto 443 → HTTPS, Puerto 8080 → Servidor de desarrollo | N/A | - | -| **IP (Dirección IP)** | Identificador de una computadora en la red | Un número único que identifica cada dispositivo en una red. Como la dirección de un edificio. | `127.0.0.1` = tu propia computadora (localhost) | N/A | - | -| **localhost / 127.0.0.1** | Tu propia máquina | Cuando un programa se conecta a "localhost", se conecta a sí mismo. Usado para probar servidores sin internet. | `http://localhost:8080` | N/A | - | -| **TCP (Transmission Control Protocol)** | Protocolo de comunicación confiable | Garantiza que los datos lleguen completos y en orden. Si se pierde algo, lo reenvía. Más lento pero seguro. Nerve lo usa en Windows. | Transferencias de archivos, navegación web | N/A | - | -| **UDP** | Protocolo rápido sin garantías | Envía datos sin verificar que lleguen. Más rápido que TCP pero puede perder paquetes. Ideal para videollamadas o juegos. | Streaming de video, juegos online | N/A | - | - ---- - -## Sockets y IPC (Lo que hace Nerve) - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Socket** | Punto de conexión entre procesos | Es como un "enchufe" que dos programas usan para mandarse mensajes. Un socket tiene dos lados: quien envía y quien recibe. | `import socket` en Python | N/A | - | -| **IPC (Inter-Process Communication)** | Comunicación entre procesos | Forma en que dos programas corriendo al mismo tiempo (procesos) se mandan mensajes entre sí. Es el concepto central de Nerve. | Un script Python le habla a un script Go a través de Nerve | N/A | - | -| **Unix Socket** | Socket de archivo en Linux/macOS | Un tipo especial de socket que usa un archivo en el sistema (en vez de TCP) para comunicación local. Más rápido que TCP en Linux/macOS. Nerve los usa en Linux/macOS. | `/tmp/nerve.sock` | N/A | - | -| **Socket TCP** | Socket de red usando TCP | Socket que usa el protocolo TCP y una dirección IP + puerto para comunicarse. Funciona también entre diferentes computadoras. Nerve los usa en Windows. | `socket.connect(("127.0.0.1", 7878))` | N/A | - | -| **Servidor (Server)** | El que escucha y responde | En una arquitectura cliente-servidor, el servidor está siempre corriendo y espera que los clientes se conecten. | El proceso de Nerve que escucha conexiones | N/A | - | -| **Cliente (Client)** | El que inicia la conexión | Se conecta al servidor para enviarle datos o pedirle algo. | Tu script Python que se conecta a Nerve | N/A | - | -| **bind()** | Asignar dirección al socket del servidor | El servidor "ata" su socket a una dirección y puerto específicos para que los clientes sepan dónde conectarse. | `server.bind(("127.0.0.1", 7878))` | N/A | - | -| **listen()** | Poner el socket a escuchar | El servidor entra en modo de espera, listo para aceptar conexiones entrantes. | `server.listen(5)` (acepta hasta 5 en cola) | N/A | - | -| **accept()** | Aceptar una conexión entrante | El servidor acepta la conexión de un cliente que llamó a la puerta. Devuelve un nuevo socket para hablar con ese cliente específico. | `conn, addr = server.accept()` | N/A | - | -| **connect()** | Conectarse al servidor (desde el cliente) | El cliente llama a la puerta del servidor. | `client.connect(("127.0.0.1", 7878))` | N/A | - | -| **send() / recv()** | Enviar y recibir datos | Las funciones básicas para mandarse bytes a través del socket. | `socket.send(b"Hola!")` / `data = socket.recv(1024)` | N/A | - | - ---- - -## Conceptos de HTTP y APIs - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **HTTP** | Protocolo de la Web | El protocolo que usan los navegadores para pedir páginas web. "La lengua de internet". | `http://` al inicio de una URL | N/A | - | -| **HTTPS** | HTTP Seguro (con cifrado) | Versión cifrada de HTTP. Tus datos viajan encriptados. GitHub y casi todos los sitios modernos lo usan. | `https://github.com` | N/A | - | -| **API (Application Programming Interface)** | Interfaz para comunicar programas | Un conjunto de reglas para que dos programas se hablen. Una API web te permite pedirle datos a un servicio sin necesidad de una interfaz visual. | API de OpenWeatherMap para obtener el clima (reto 14) | N/A | - | -| **REST API** | Tipo de API que usa HTTP | El estilo más común de APIs web. Usa URLs y métodos HTTP (GET, POST, etc.) para hacer operaciones. | `requests.get("https://api.ejemplo.com/datos")` | N/A | - | -| **GET** | Solicitar/leer datos | El método HTTP para pedir información a un servidor sin modificar nada. | `requests.get("https://api.clima.com/ciudad/monterrey")` | N/A | - | -| **POST** | Enviar datos al servidor | El método HTTP para enviar datos nuevos al servidor (crear algo). | `requests.post(url, json={"nombre": "Ana"})` | N/A | - | -| **WebSocket** | Conexión bidireccional persistente | A diferencia de HTTP (donde tú preguntas y el servidor responde), un WebSocket mantiene la conexión abierta para que ambos puedan hablar cuando quieran. | Chat en tiempo real | N/A | - | -| **Endpoint** | Dirección de un recurso en una API | La URL específica de un recurso dentro de una API. Cada endpoint sirve para una operación distinta. | `GET /api/clima` o `POST /api/usuarios` | N/A | - | -| **timeout** | Límite de tiempo de espera | El tiempo máximo que tu programa esperará una respuesta de la red antes de rendirse y reportar error. | `requests.get(url, timeout=5)` → espera máx. 5 segundos | N/A | - | - ---- - -## Conceptos de Concurrencia (Reto 09) - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Proceso** | Programa en ejecución | Una instancia de tu programa corriendo. Tiene su propia memoria independiente. | Tu script de Python = un proceso | N/A | - | -| **Hilo (Thread)** | Unidad de ejecución dentro de un proceso | Un proceso puede tener varios hilos corriendo "en paralelo" (o casi). Comparten memoria. | `import threading` en Python | N/A | - | -| **Concurrencia** | Manejar varias tareas a la vez | La capacidad de un programa de avanzar en varias tareas al mismo tiempo, aunque no sea literalmente simultáneo. | El servidor Nerve atendiendo a 10 clientes a la vez | N/A | - | -| **Carga (Load)** | Nivel de trabajo de un servidor | Cuántas peticiones está manejando un servidor en un momento dado. El reto 09 prueba cuánta carga puede soportar Nerve. | 1000 peticiones por segundo = alta carga | N/A | - | - ---- - -> 💡 **Dato clave sobre Nerve:** Nerve abstrae toda esta complejidad. Tú no tienes que saber de sockets para usar Nerve, pero saber cómo funcionan te ayuda a entender *por qué* Nerve existe y qué problema resuelve. Los retos de nivel Avanzado (08, 09, 10) sí requieren entender estos conceptos. - ---- - -← [Volver al Índice del Glosario](README.md) diff --git a/archivo-historico/glosario/terminal-linux.md b/archivo-historico/glosario/terminal-linux.md deleted file mode 100644 index b7f4f57..0000000 --- a/archivo-historico/glosario/terminal-linux.md +++ /dev/null @@ -1,88 +0,0 @@ -# ⌨️ Glosario: Terminal — Linux y macOS - -Guía de la terminal para usuarios de **Linux y macOS** (y Termux en Android). Si estás en Windows, consulta el [Glosario de Windows](terminal-windows.md). - ---- - -## Conceptos Universales de Terminal - -Estos términos aplican en cualquier sistema operativo. - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Shell** | El intérprete de comandos | El programa que lee lo que escribes y lo ejecuta. Es el "cerebro" de la terminal. | `bash`, `zsh`, `fish` | N/A | - | -| **bash** | El shell más común en Linux | El intérprete por defecto en la mayoría de distros Linux y en todos los servidores. macOS usa `zsh` por defecto desde 2019. | `#!/bin/bash` al inicio de un script | N/A | - | -| **`~` (virgulilla)** | Carpeta de inicio del usuario | Atajo que representa `/home/tu_usuario/` en Linux o `/Users/tu_usuario/` en macOS. | `cd ~` = ir a tu carpeta de inicio | N/A | - | -| **`.` (punto)** | La carpeta actual | Representa "donde estás ahora mismo". | `git add .` = preparar todo lo que hay aquí | N/A | - | -| **`..` (dos puntos)** | La carpeta superior (padre) | Sube un nivel en el árbol de carpetas. | `cd ..` = ir a la carpeta de arriba | N/A | - | - ---- - -## Comandos Esenciales de Linux / macOS - -| :--- | :--- | :--- | :--- | :--- | -| **pwd** | Print Working Directory — muestra dónde estás | `pwd` → `/home/alejandro/nerve-community` | -| **ls** | List — lista archivos y carpetas | `ls` o `ls -la` (con detalles y archivos ocultos) | -| **cd** | Change Directory — cambia de carpeta | `cd retos/general` o `cd ..` | -| **mkdir** | Make Directory — crea una carpeta nueva | `mkdir mi-solucion` | -| **cp** | Copy — copia archivos o carpetas | `cp archivo.py copia.py` | -| **mv** | Move — mueve o renombra archivos | `mv viejo.py nuevo.py` | -| **rm** | Remove — borra archivos (⚠️ permanente, sin papelera) | `rm archivo.py` o `rm -rf carpeta/` | -| **cat** | Muestra el contenido de un archivo | `cat README.md` | -| **nano** | Editor de texto simple en terminal | `nano mi_script.py` → `Ctrl+O` guardar → `Ctrl+X` salir | -| **echo** | Imprime texto en la terminal | `echo "Hola mundo"` | -| **clear** | Limpia la pantalla | `clear` | -| **which** | Muestra dónde está instalado un programa | `which python3` → `/usr/bin/python3` | -| **grep** | Busca texto dentro de archivos | `grep "def " mi_script.py` | -| **chmod** | Cambia permisos de un archivo | `chmod +x script.sh` (hacerlo ejecutable) | -| **df -h** | Muestra espacio en disco disponible | `df -h` (`-h` = tamaño legible: MB/GB) | -| **sudo** | Superuser Do — ejecutar como administrador | `sudo apt install git` (instalar paquetes del sistema) | -| **apt** | Gestor de paquetes de Ubuntu/Debian | `sudo apt install python3` | -| **&&** | Encadenar comandos (el 2° solo corre si el 1° funcionó) | `git add . && git commit -m "mensaje"` | - ---- - -## Python desde la Terminal (Linux/macOS) - -| :--- | :--- | :--- | :--- | :--- | -| **python3** | Ejecutar un script Python | `python3 mi_script.py` | -| **python3 --version** | Ver versión instalada | `python3 --version` → `Python 3.11.2` | -| **pip3 install** | Instalar una librería externa | `pip3 install requests` | -| **pip3 install -r requirements.txt** | Instalar todas las dependencias del proyecto | `pip3 install -r requirements.txt` | -| **pip3 list** | Ver librerías instaladas | `pip3 list` | -| **pip3 freeze** | Generar requirements.txt | `pip3 freeze > requirements.txt` | - -> ⚠️ En Linux/macOS usa `python3` y `pip3` (con el `3`). Sin el `3`, podría ejecutar Python 2, que ya está obsoleto. - ---- - -## Termux (Android) - -Termux es una terminal Linux que corre en tu celular Android, sin necesidad de root ni PC. - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Termux** | Terminal Linux para Android | Emula una terminal Linux completa en tu celular. **Solo desde F-Droid**, la versión de Play Store está descontinuada. | `https://f-droid.org/packages/com.termux/` | N/A | - | -| **F-Droid** | Tienda de apps Open Source | La tienda de donde se descarga la versión correcta de Termux. | `https://f-droid.org` | N/A | - | -| **pkg** | Gestor de paquetes de Termux | El equivalente de `apt` dentro de Termux. Instala herramientas del sistema. | `pkg install python git gh -y` | N/A | - | -| **pkg update && pkg upgrade** | Actualizar paquetes de Termux | Primer comando a correr al abrir Termux por primera vez. | `pkg update && pkg upgrade` | N/A | - | -| **termux-setup-storage** | Dar acceso al almacenamiento | Permite que Termux acceda a las carpetas del celular (Descargas, etc.). | `termux-setup-storage` | N/A | - | -| **Acode** | Editor de código para Android | App con resaltado de sintaxis para editar archivos de Termux cómodamente. | Disponible en F-Droid y Play Store | N/A | - | -| **root** | Superusuario del sistema | El usuario con todos los permisos. Termux **NO** requiere root. | Termux es seguro por no necesitar root | N/A | - | - ---- - -## Variables de Entorno (Linux/macOS) - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **export** | Exportar variable al entorno | Hace que la variable esté disponible para los procesos hijos de esa sesión. | `export API_KEY="mi_clave"` | N/A | - | -| **.env** | Archivo de variables de entorno locales | Guarda keys y configuración. **NUNCA** se sube a GitHub (va en `.gitignore`). | `API_KEY=mi_clave` dentro del archivo `.env` | N/A | - | - ---- - -> 💡 **Tip:** En Termux puedes usar exactamente los mismos comandos de Linux que ves aquí. ¡Es la misma shell! - ---- - -← [Volver al Índice del Glosario](README.md) | → [Glosario Windows](terminal-windows.md) diff --git a/archivo-historico/glosario/terminal-windows.md b/archivo-historico/glosario/terminal-windows.md deleted file mode 100644 index dce2e88..0000000 --- a/archivo-historico/glosario/terminal-windows.md +++ /dev/null @@ -1,119 +0,0 @@ -# 🪟 Glosario: Terminal — Windows - -Guía de la terminal para usuarios de **Windows**. Si estás en Linux o macOS, consulta el [Glosario de Linux/macOS](terminal-linux.md). - ---- - -## ¿Qué Terminal Usar en Windows? - -Windows tiene **tres** opciones de terminal. Aquí te explicamos cuál te conviene para los retos de Nerve Community: - -| Terminal | 🔍 ¿Qué es? | ¿Se recomienda? | -| :--- | :--- | :--- | :--- | :--- | -| **CMD (Símbolo del Sistema)** | La terminal antigua de Windows. Comandos propios (`dir`, `copy`, `del`). | ⚠️ Funciona pero es limitada. Úsala solo si no tienes otra opción. | -| **PowerShell** | Terminal moderna de Microsoft. Más potente que CMD. | ✓ Buena opción. Viene preinstalada en Windows 10/11. | -| **Git Bash** | Terminal que emula bash de Linux dentro de Windows. Viene con Git for Windows. | ✅ **Recomendada**. Te permite usar comandos de Linux (`ls`, `cat`, `nano`) y toda la guía de los retos aplica igual. | -| **WSL (Windows Subsystem for Linux)** | Una instalación completa de Linux dentro de Windows. | ✅ **La mejor opción** si quieres la experiencia idéntica a Linux. Un poco más compleja de configurar. | - -> 💡 **Recomendación para los retos:** Instala **Git for Windows** (incluye Git Bash) y usarás los mismos comandos que aparecen en todas las guías del repo. Descárgalo en [gitforwindows.org](https://gitforwindows.org/). - ---- - -## Conceptos Universales de Terminal - -Estos términos aplican en cualquier sistema operativo. - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Terminal** | Interfaz de texto para tu sistema | Un programa donde escribes comandos de texto. En Windows: CMD, PowerShell o Git Bash. | `Win + R` → escribe `cmd` → Enter | N/A | - | -| **Prompt** | La línea de espera | El símbolo que indica que la terminal espera un comando. | CMD: `C:\Users\alejandro>` / Git Bash: `alejandro@PC MINGW64 ~$` | N/A | - | -| **Comando** | Instrucción para la terminal | Una palabra que le dices a la terminal que ejecute. | `cd`, `python`, `git` | N/A | - | -| **Flag / Bandera** | Modificador de un comando | Modifica el comportamiento. Empieza con `/` en CMD o `-`/`--` en PowerShell y Git Bash. | `dir /a` (CMD) o `ls -la` (Git Bash) | N/A | - | -| **Ruta (Path)** | Dirección de un archivo o carpeta | La ubicación exacta de un archivo. En Windows usa `\`, en Git Bash usa `/`. | `C:\Users\alejandro\Documents\` o `/c/Users/alejandro/Documents/` (Git Bash) | N/A | - | -| **Ruta Absoluta** | Ruta completa desde la raíz | En Windows empieza con la letra de unidad (`C:\`). | `C:\Users\alejandro\nerve-community\README.md` | N/A | - | -| **Ruta Relativa** | Ruta desde la carpeta actual | Funciona igual que en Linux: `.`, `..`, nombre de carpeta. | `..\README.md` (CMD) o `../README.md` (Git Bash) | N/A | - | -| **`.` (punto)** | La carpeta actual | Igual que en Linux. | `git add .` = preparar todos los cambios | N/A | - | -| **`..` (dos puntos)** | La carpeta superior | Igual que en Linux. | `cd ..` = subir un nivel | N/A | - | - ---- - -## Comandos: Comparativa CMD vs Git Bash - -Aquí el equivalente de cada comando de Linux en CMD nativo de Windows, para que entiendas qué hace cada uno sin importar desde dónde lo veas en la documentación: - -| Función | CMD (Windows nativo) | Git Bash / PowerShell | -| :--- | :--- | :--- | :--- | :--- | -| Ver dónde estás | `cd` (sin argumentos) | `pwd` | -| Listar archivos | `dir` | `ls` o `ls -la` | -| Cambiar de carpeta | `cd nombre-carpeta` | `cd nombre-carpeta` | -| Crear carpeta | `mkdir nombre` | `mkdir nombre` | -| Copiar archivo | `copy origen destino` | `cp origen destino` | -| Mover/renombrar | `move origen destino` | `mv origen destino` | -| Borrar archivo | `del archivo.py` | `rm archivo.py` | -| Mostrar contenido | `type archivo.txt` | `cat archivo.txt` | -| Limpiar pantalla | `cls` | `clear` | -| Buscar texto | `findstr "texto" archivo` | `grep "texto" archivo` | -| Imprimir texto | `echo Hola mundo` | `echo "Hola mundo"` | -| Encadenar comandos | `comando1 && comando2` | `comando1 && comando2` | - -> ✅ **Con Git Bash instalado**, puedes usar directamente la columna de Git Bash/PowerShell, que es la misma sintaxis que usan todas las guías de los retos. - ---- - -## Python en Windows - -| Comando | 🔍 ¿Qué hace? | 💻 Ejemplo | -| :--- | :--- | :--- | :--- | :--- | -| **Instalar Python** | Descarga el instalador oficial | [python.org/downloads](https://www.python.org/downloads/) — marca "Add Python to PATH" durante la instalación | -| **python** (o `python3`) | Ejecutar un script Python | `python mi_script.py` o `python3 mi_script.py` | -| **python --version** | Ver versión instalada | `python --version` → `Python 3.11.2` | - -> ⚠️ En Windows, el comando puede ser `python` (sin el `3`) si solo tienes Python 3 instalado. Si tienes ambas versiones, usa `py -3` para asegurarte. - ---- - -## Abrir la Terminal en Windows - -| Método | Cómo hacerlo | -| :--- | :--- | :--- | :--- | -| **CMD** | `Win + R` → escribe `cmd` → Enter | -| **PowerShell** | `Win + X` → selecciona "Windows PowerShell" | -| **Git Bash** | Clic derecho en cualquier carpeta → "Git Bash Here" | -| **Terminal de Windows** | Busca "Terminal" en el menú inicio (Windows 11 ya la incluye) | -| **Desde VS Code** | `Ctrl + ` ` ` (acento grave) — abre la terminal integrada | - ---- - -## Variables de Entorno en Windows - -| Término | 🔍 ¿Qué es? | 🎯 ¿Para qué sirve? | 💻 Ejemplo | ⚠️ Error común | 🔗 Relacionado | -| :--- | :--- | :--- | :--- | :--- | :--- | -| **Variable de entorno** | Variable global del sistema | Disponible para todos los programas. Se usa para API keys y configuración. | `API_KEY=abc123` | N/A | - | -| **PATH** | Lista de carpetas donde buscar ejecutables | Cuando escribes `python`, Windows busca en las carpetas del PATH. Si Python no se encuentra, no está en el PATH. | Panel de control → Variables de entorno → PATH | N/A | - | -| **set** | Crear variable temporal (CMD) | Crea una variable solo para esa sesión del CMD. | `set API_KEY=mi_clave` | N/A | - | -| **$env:** | Crear variable en PowerShell | Equivalente a `set` pero en PowerShell. | `$env:API_KEY = "mi_clave"` | N/A | - | - ---- - -## WSL (Windows Subsystem for Linux) — Opción Avanzada - -WSL te permite tener una distribución completa de Linux (Ubuntu, Debian, etc.) corriendo dentro de Windows, sin máquina virtual. - -| Concepto | Descripción | -| :--- | :--- | :--- | :--- | -| **¿Qué es WSL?** | Una capa de compatibilidad que permite correr Linux nativo dentro de Windows 10/11. | -| **¿Por qué usarlo?** | Tienes acceso a todos los comandos de Linux (`ls`, `nano`, `apt`, etc.) y la experiencia es idéntica a un servidor real. Los retos más avanzados (Rust, Go) funcionan mucho mejor en WSL que en Windows nativo. | -| **¿Cómo instalarlo?** | Abre PowerShell como administrador y ejecuta: `wsl --install` (instala Ubuntu por defecto). Requiere reiniciar. | -| **¿Dónde viven mis archivos?** | Puedes acceder a tus archivos de Windows desde WSL en `/mnt/c/Users/tu_usuario/`. | - ---- - -> 💡 **Resumen para empezar rápido en Windows:** -> 1. Instala **Git for Windows** → [gitforwindows.org](https://gitforwindows.org/) -> 2. Instala **Python 3** → [python.org/downloads](https://www.python.org/downloads/) (marca "Add to PATH") -> 3. Usa **Git Bash** como tu terminal principal -> 4. Con eso, todos los comandos de las guías del repo funcionarán igual en tu Windows - ---- - -← [Volver al Índice del Glosario](README.md) | → [Glosario Linux/macOS](terminal-linux.md) diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/README.md b/archivo-historico/retos/general/01-lista-de-tareas-cli/README.md deleted file mode 100644 index 9f2d989..0000000 --- a/archivo-historico/retos/general/01-lista-de-tareas-cli/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 01 — Lista de tareas (to-do list) en terminal - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #13 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/13)** diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py deleted file mode 100644 index 3ae267f..0000000 --- a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Functions.py +++ /dev/null @@ -1,33 +0,0 @@ -import pandas as pd -from pathlib import Path - -BASE_DIR = Path(__file__).parent.resolve() - - -def obtener_ruta_absoluta(ruta: str) -> Path: - clean_path = ruta.strip("'\"") - path = Path(clean_path) - if not path.is_absolute(): - path = BASE_DIR / path - return path - - -def validar_ruta_csv(ruta: str) -> str: - path = obtener_ruta_absoluta(ruta) - - if path.is_file(): - return str(path) - raise FileNotFoundError(f"Archivo no encontrado en la ruta: {path}") - - -def validar_contenido_csv(ruta: str) -> bool: - # 2.- Leer la ruta ya limpia para validar su estructura - df = pd.read_csv(ruta, encoding="utf-8") - required_columns = ["id", "titulo", "description", "estado"] - - if all(column in df.columns for column in required_columns): - return True - - raise ValueError( - f"El archivo CSV no contiene la estructura requerida: {required_columns}" - ) diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py deleted file mode 100644 index 1f8c304..0000000 --- a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Main.py +++ /dev/null @@ -1,282 +0,0 @@ -from Model import Tareas -import pandas as pd -from Functions import validar_ruta_csv, validar_contenido_csv, obtener_ruta_absoluta - -df = pd.DataFrame(columns=["id", "titulo", "description", "estado"]) - -id_task = 1 - -ruta_csv = "" - - -def option1(): # Insertar Tarea - try: - global id_task - global df - titulo_tarea = input("Ingrese el titulo de la tarea: ") - description_tarea = input("Descripción de la tarea: ") - estado_tarea = False - tarea = Tareas( - id=id_task, - titulo=titulo_tarea, - description=description_tarea, - estado=estado_tarea, - ) - id_task += 1 - nueva_fila = pd.DataFrame( - [ - { - "id": tarea.id, - "titulo": tarea.titulo, - "description": tarea.description, - "estado": tarea.estado, - } - ] - ) - df = pd.concat([df, nueva_fila], ignore_index=True) - print(f"Tarea creada: {tarea}\n") - except Exception as e: - print(f"Error al crear tarea: {e}") - - -def option2(): # Modificar Tarea - global df - try: - id_buscar = int(input("Ingrese el id de la tarea a modificar: ")) - if id_buscar not in df["id"].values: - print(f"No existe una tarea con dicha id.") - else: - while True: - print("¿Qué desea modificar?") - print("----------------------------------") - print("1. Titulo") - print("2. Descripción") - print("3. Ambos") - - res = int(input("Introduce la opción elegida: ")) - - if res == 1: - titulo_new = input("Introduce el nuevo titulo: ") - df.loc[df["id"] == id_buscar, "titulo"] = titulo_new - print("Titulo cambiado") - print(df.loc[df["id"] == id_buscar]) - break - elif res == 2: - des_new = input("Introduce la nueva descripción: ") - df.loc[df["id"] == id_buscar, "description"] = des_new - print("Descripción cambiado") - print(df.loc[df["id"] == id_buscar]) - break - elif res == 3: - titulo_new = input("Introduce el nuevo titulo: ") - des_new = input("Introduce la nueva descripción: ") - df.loc[df["id"] == id_buscar, "titulo"] = titulo_new - df.loc[df["id"] == id_buscar, "description"] = des_new - print("Tarea modificada") - print(df.loc[df["id"] == id_buscar]) - break - else: - print("Ingrese una opción valida") - except Exception as e: - print(f"Error al modificar el registro: {e}") - - -def option3(): # Actualizar Estado tarea - global df - try: - id_buscar = int(input("Ingrese el id de la tarea a actualizar estado: ")) - if id_buscar not in df["id"].values: - print(f"No existe una tarea con dicha id: {id_buscar}") - else: - df.loc[df["id"] == id_buscar, "estado"] = True - print(f"Estado de la tarea con id: {id_buscar} actualizado.") - print(df.loc[df["id"] == id_buscar]) - - except Exception as e: - print(f"Error al actualizar estado de la tarea: {e}") - - -def option4(): # Eliminar tarea - global df - try: - id_buscar = int(input("Ingresa el id de la tarea a eliminar: ")) - if id_buscar not in df["id"].values: - print(f"No existe una tarea con dicha id: {id_buscar}") - else: - df = df[df["id"] != id_buscar] - print(f"Tarea eliminada") - print(df) - except Exception as e: - print(f"Error al eliminar tarea: {e}") - - -def option5(): # Ver tarea especifica - global df - try: - id_buscar = int(input("Ingrese la tarea a ver(id): ")) - tarea = df.loc[df["id"] == id_buscar] - print(tarea) - except Exception as e: - print(f"Error al buscar tarea: {e}") - - -def option6(): # ver lista de tareas - if df.empty: - print("No hay tareas registradas.") - else: - print(df.to_string(index=False)) - - -def option7(): - global id_task - global df - global ruta_csv - - if df.empty: - ruta = input("Ingrese la ruta del archivo CSV: ") - try: - ruta_limpia = validar_ruta_csv(ruta) - if validar_contenido_csv(ruta_limpia): - ruta_csv = ruta_limpia - df_csv = pd.read_csv(ruta_limpia) - df = pd.concat([df, df_csv], ignore_index=True) - df["id"] = df["id"].astype(int) - df["estado"] = df["estado"].astype(bool) - id_task = df["id"].max() + 1 - print(df.to_string(index=False)) - except Exception as e: - print(f"Error durante el proceso de CSV:\n{e}") - else: - while True: - print("Hay datos existentes en el dataframe.") - print("¿Qué desea hacer con ellos?") - print("----------------------------------------") - print( - "1. Fusionar los registros con los del CSV (Se dará prioridad a los registros existentes del DF)." - ) - print( - "2. Reemplazar los registros existentes con los del CSV (Se eliminarán los registros existentes)." - ) - - option = input("Ingrese su opción (1 o 2):") - - if option == "1": - print("Fusionando registros existentes con los del CSV...") - ruta = input("Ingrese la ruta del archivo CSV: ") - try: - ruta_limpia = validar_ruta_csv(ruta) - if validar_contenido_csv(ruta_limpia): - ruta_csv = ruta_limpia - df_csv = pd.read_csv(ruta_limpia) - ultimo_id_df = df["id"].max() - primer_id_csv = df_csv["id"].min() - df_csv["id"] = df_csv["id"] - primer_id_csv + (ultimo_id_df + 1) - df = pd.concat([df, df_csv], ignore_index=True) - df["id"] = df["id"].astype(int) - df["estado"] = df["estado"].astype(bool) - id_task = df["id"].max() + 1 - print(df.to_string(index=False)) - break - except Exception as e: - print(f"Error durante el proceso de CSV:\n{e}") - break - elif option == "2": - print( - "Eliminando registros existentes y reemplazando con los del CSV..." - ) - df = df.iloc[0:0] - id_task = 1 - ruta = input("Ingrese la ruta del archivo CSV: ") - try: - ruta_limpia = validar_ruta_csv(ruta) - if validar_contenido_csv(ruta_limpia): - ruta_csv = ruta_limpia - df_csv = pd.read_csv(ruta_limpia) - df = pd.concat([df, df_csv], ignore_index=True) - df["id"] = df["id"].astype(int) - df["estado"] = df["estado"].astype(bool) - id_task = df["id"].max() + 1 - print(df.to_string(index=False)) - break - except Exception as e: - print(f"Error durante el proceso de CSV:\n{e}") - else: - print("Ingresa una opción válida (1 o 2).") - - -def option8(): - global df - global ruta_csv - - if df.empty: - print("No hay datos para exportar. ¡Hasta luego!") - return - - destino = ruta_csv - - if ruta_csv: - print(f"Ruta actual configurada: {ruta_csv}") - print("1. Sobrescribir en el archivo importado") - print("2. Exportar a un nuevo archivo CSV") - opc = input("Seleccione una opción (1 o 2, por defecto 1): ").strip() - if opc == "2": - destino = input("Ingrese la nueva ruta o nombre del archivo CSV: ").strip( - "'\"" - ) - else: - destino = input( - "Ingrese el nombre o ruta del archivo CSV para exportar (ej: tareas.csv): " - ).strip("'\"") - - if not destino: - destino = "tareas.csv" - - path_destino = obtener_ruta_absoluta(destino) - - print(f"Exportando información a archivo CSV: {path_destino}...") - try: - df.to_csv(path_destino, index=False, encoding="utf-8") - print(f"Información exportada correctamente en: {path_destino}") - print("Bye Bye!") - except Exception as e: - print(f"Error al exportar a CSV: {e}") - - -def menu(): - print("Administrador de tareas") - while True: - print("--------------------------") - print("1. Insertar Tarea") - print("2. Modificar Tarea") - print("3. Actualizar Estado de Tarea") - print("4. Eliminar Tarea") - print("5. Ver Tarea Específica") - print("6. Ver Lista de Tareas") - print("7. Importar lista desde un archivo CSV") - print("8. Salir") - - option = input("¿Qué desea hacer?") - - if option == "1": - option1() - elif option == "2": - option2() - elif option == "3": - option3() - elif option == "4": - option4() - elif option == "5": - option5() - elif option == "6": - option6() - elif option == "7": - option7() - elif option == "8": - option8() - break - else: - print("Ingrese una opción valida") - - -if __name__ == "__main__": - menu() diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py deleted file mode 100644 index 0e685c4..0000000 --- a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/Model.py +++ /dev/null @@ -1,8 +0,0 @@ -from pydantic import BaseModel, StrictBool, Field - - -class Tareas(BaseModel): - id: int = Field(ge=1) # ge = greater than or equal to, mayor o igual a 1 - titulo: str - description: str - estado: StrictBool # Se limita a solo True o False; True = Completado, False = Pendiente. diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md deleted file mode 100644 index 8b0a296..0000000 --- a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Solución 01 lista de tareas CLI - -La solución usa librerias como Pandas para el manejo de csv (importación/exportación) y un manejo más limpio de las tareas por medio de dataframes. - -## Opciones - -Existen las siguientes opciones en el código: -1. Insertar Tarea. -2. Modificar Tarea. -3. Actualizar Estado de Tarea. -4. Eliminar Tarea. -5. Ver Tarea Específica. -6. Ver Lista de Tareas. -7. Importar lista desde un archivo CSV. -8. Salir (Exporta y actualiza el csv subido.) - -## Estructura -```text -CesarAAR-python/ -├── Functions.py # Módulos con funciones secundarias -├── Main.py -├── Model.py # Definición de modelos o lógica de datos -├── README.md -├── requirements.txt # Los requerimientos para ser instalados. -└── test.csv # Un csv para que vea la estructura del documento. -``` - -### Aclaraciones - -La columna estado puede ser True|False. -- True = Completado. -- False = Pendiente. \ No newline at end of file diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/CesarAAR-python/requirements.txt deleted file mode 100644 index e8faf75e6f81fe8d8119d6ccbd8db87baa25cb39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 414 zcmZvY;R=F45QOJ<(4!=tTG5Z5MGlJ+>Xc5w>*1@}y-N(@I8L0|*>Cs#vT!^d23X!R zPT24b==tq&6q?T%2w&(CdF@*KQat>CTg2#bLPo>abq7R=0H3E diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md deleted file mode 100644 index f757993..0000000 --- a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Readme.md +++ /dev/null @@ -1 +0,0 @@ -El archivo se ejecuta en con python o python3 directamente en cd python-general/nivel-1-principiante/01-lista-de-tareas-cli/Tacos una vez ahi hacer python o python3 Tareas.py diff --git a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py b/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py deleted file mode 100644 index e15d6a7..0000000 --- a/archivo-historico/retos/general/01-lista-de-tareas-cli/soluciones/Tacos-python/Tareas.py +++ /dev/null @@ -1,83 +0,0 @@ -import json -import os - -ARCHIVO = "tareas.json" - - -def cargar_tareas(): - if os.path.exists(ARCHIVO): - with open(ARCHIVO, "r", encoding="utf-8") as f: - return json.load(f) - return [] - - -def guardar_tareas(tareas): - with open(ARCHIVO, "w", encoding="utf-8") as f: - json.dump(tareas, f, indent=4) - - -def mostrar_menu(): - print("\n--- Lista de Tareas ---") - print("1. Ver tareas pendientes") - print("2. Agregar tarea") - print("3. Marcar como completada") - print("4. Eliminar tarea") - print("5. Salir") - - -def principal(): - tareas = cargar_tareas() - - while True: - mostrar_menu() - opcion = input("Selecciona una opción: ") - - if opcion == "1": - if not tareas: - print("\nNo hay tareas registradas.") - else: - print("\nTareas:") - for i, tarea in enumerate(tareas): - estado = "[X]" if tarea["completada"] else "[ ]" - print(f"{i + 1}. {estado} {tarea['descripcion']}") - - elif opcion == "2": - descripcion = input("\nEscribe la nueva tarea: ") - tareas.append({"descripcion": descripcion, "completada": False}) - guardar_tareas(tareas) - print("Tarea agregada exitosamente.") - - elif opcion == "3": - try: - indice = int(input("\nNúmero de la tarea a completar: ")) - 1 - if 0 <= indice < len(tareas): - tareas[indice]["completada"] = True - guardar_tareas(tareas) - print("Tarea marcada como completada.") - else: - print("Error: El número de tarea no existe.") - except ValueError: - print("Error: Por favor ingresa un número válido.") - - elif opcion == "4": - try: - indice = int(input("\nNúmero de la tarea a eliminar: ")) - 1 - if 0 <= indice < len(tareas): - tarea_eliminada = tareas.pop(indice) - guardar_tareas(tareas) - print(f"Tarea '{tarea_eliminada['descripcion']}' eliminada.") - else: - print("Error: El número de tarea no existe.") - except ValueError: - print("Error: Por favor ingresa un número válido.") - - elif opcion == "5": - print("\nSaliendo del programa...") - break - - else: - print("\nOpción no válida. Intenta de nuevo.") - - -if __name__ == "__main__": - principal() diff --git a/archivo-historico/retos/general/02-web-scraper-simple/README.md b/archivo-historico/retos/general/02-web-scraper-simple/README.md deleted file mode 100644 index 73437fb..0000000 --- a/archivo-historico/retos/general/02-web-scraper-simple/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 02 — Web scraper simple - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #14 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/14)** diff --git a/archivo-historico/retos/general/03-juego-de-adivinanza/README.md b/archivo-historico/retos/general/03-juego-de-adivinanza/README.md deleted file mode 100644 index 6797942..0000000 --- a/archivo-historico/retos/general/03-juego-de-adivinanza/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 03 — Juego de adivina el número - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #15 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/15)** diff --git a/archivo-historico/retos/general/04-analizador-de-gastos-csv/README.md b/archivo-historico/retos/general/04-analizador-de-gastos-csv/README.md deleted file mode 100644 index ba27748..0000000 --- a/archivo-historico/retos/general/04-analizador-de-gastos-csv/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 04 — Analizador de gastos personales (CSV) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #16 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/16)** diff --git a/archivo-historico/retos/general/05-generador-de-contrasenas/README.md b/archivo-historico/retos/general/05-generador-de-contrasenas/README.md deleted file mode 100644 index ad95688..0000000 --- a/archivo-historico/retos/general/05-generador-de-contrasenas/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 05 — Generador de contraseñas seguras - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #17 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/17)** diff --git a/archivo-historico/retos/general/06-conversor-de-unidades/README.md b/archivo-historico/retos/general/06-conversor-de-unidades/README.md deleted file mode 100644 index 65aea06..0000000 --- a/archivo-historico/retos/general/06-conversor-de-unidades/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 06 — Conversor de unidades - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #18 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/18)** diff --git a/archivo-historico/retos/general/07-calculadora-de-imc/README.md b/archivo-historico/retos/general/07-calculadora-de-imc/README.md deleted file mode 100644 index 7836f12..0000000 --- a/archivo-historico/retos/general/07-calculadora-de-imc/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 07 — Calculadora de IMC - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #19 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/19)** diff --git a/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py b/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py deleted file mode 100644 index d793927..0000000 --- a/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/07_calculadora_de_imc.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Calculadora de índice de masa corporal (IMC) -Este programa solicita al usuario su peso (kg) y altura (m), calcula su IMC -y lo categoriza según las siguientes categorías: - - Bajo peso: IMC < 18.5 - - Peso normal: 18.5 <= IMC < 25 - - Sobrepeso: 25 <= IMC < 30 - - Obesidad: IMC >= 30 -""" - - -def calcular_imc(peso: float, altura: float) -> float: - """ - Calcula el índice de masa corporal. - Fórmula: IMC = peso (kg) / altura^2 (m) - """ - return peso / altura**2 - - -def categorizar_imc(imc: float) -> str: - """ - Categoriza el IMC del usuario. - """ - - if imc > 30.0: - return "Obesidad" - elif imc > 25.0: - return "Sobrepeso" - elif imc > 18.5: - return "Peso normal" - else: - return "Bajo peso" - - -def pedir_float(mensaje: str) -> float: - """ - Solicita al usuario un número flotante y maneja errores de entrada. - """ - while True: - entrada = input(mensaje).strip() - try: - valor = float(entrada) - if valor <= 0: - print("Por favor, ingrese un número mayor que cero.") - continue - else: - return valor - except ValueError: - print("Por favor, ingrese un número válido.") - - -def main(): - print("=== Calculadora de índice de masa corporal ===") - print("¡Bienvenido a la calculadora de IMC!\n") - - nombre = input("Ingrese su nombre: ").strip() - peso = pedir_float("Ingrese su peso en kg (ej. 70): ") - altura = pedir_float("Ingrese su altura en m (ej. 1.75): ") - - try: - imc = calcular_imc(peso, altura) - categoria = categorizar_imc(imc) - - print(f"\nResultado para {nombre}:") - print(f"Peso: {peso} kg | Altura: {altura:.2f} m") - print(f"IMC calculado: {imc:.2f}") - print(f"Categoría: {categoria}") - - except ValueError as e: - print("Error en el cálculo.") - print(e) - - -if __name__ == "__main__": - main() diff --git a/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md b/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md deleted file mode 100644 index 64af64a..0000000 --- a/archivo-historico/retos/general/07-calculadora-de-imc/soluciones/isaacglezacosta-python/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Calculadora de IMC - -Solución en Python para el reto 07. El programa pide tu nombre, peso y altura, calcula el IMC y muestra su categoría. - -## Requisitos - -No se necesita instalar ninguna dependencia. - -## Cómo ejecutar - -Desde esta carpeta, ejecuta: - -```bash -python 07_calculadora_de_imc.py -``` \ No newline at end of file diff --git a/archivo-historico/retos/general/08-organizador-de-archivos/README.md b/archivo-historico/retos/general/08-organizador-de-archivos/README.md deleted file mode 100644 index 413618e..0000000 --- a/archivo-historico/retos/general/08-organizador-de-archivos/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 08 — Organizador automático de archivos - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #20 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/20)** diff --git a/archivo-historico/retos/general/09-recordatorios-en-terminal/README.md b/archivo-historico/retos/general/09-recordatorios-en-terminal/README.md deleted file mode 100644 index d1e5533..0000000 --- a/archivo-historico/retos/general/09-recordatorios-en-terminal/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 09 — Recordatorios en terminal - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #21 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/21)** diff --git a/archivo-historico/retos/general/10-analizador-de-texto/README.md b/archivo-historico/retos/general/10-analizador-de-texto/README.md deleted file mode 100644 index bc855dc..0000000 --- a/archivo-historico/retos/general/10-analizador-de-texto/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 10 — Analizador de texto - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #22 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/22)** diff --git a/archivo-historico/retos/general/11-profiler-de-funciones/README.md b/archivo-historico/retos/general/11-profiler-de-funciones/README.md deleted file mode 100644 index 068d5cf..0000000 --- a/archivo-historico/retos/general/11-profiler-de-funciones/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 11 — Profiler de funciones (Decoradores) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #25 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/25)** diff --git a/archivo-historico/retos/general/12-validador-de-emails/README.md b/archivo-historico/retos/general/12-validador-de-emails/README.md deleted file mode 100644 index 702c22d..0000000 --- a/archivo-historico/retos/general/12-validador-de-emails/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 12 — Validador de emails con tests (Pytest) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #26 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/26)** diff --git a/archivo-historico/retos/general/13-lector-archivos-gigantes/README.md b/archivo-historico/retos/general/13-lector-archivos-gigantes/README.md deleted file mode 100644 index f02d8d9..0000000 --- a/archivo-historico/retos/general/13-lector-archivos-gigantes/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 13 — Lector de archivos gigantes (Generadores) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #27 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/27)** diff --git a/archivo-historico/retos/general/14-cli-del-clima/README.md b/archivo-historico/retos/general/14-cli-del-clima/README.md deleted file mode 100644 index cf7f8ff..0000000 --- a/archivo-historico/retos/general/14-cli-del-clima/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 14 — CLI del clima (APIs y Auth) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #28 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/28)** diff --git a/archivo-historico/retos/general/15-herramienta-de-backup/README.md b/archivo-historico/retos/general/15-herramienta-de-backup/README.md deleted file mode 100644 index 6e66431..0000000 --- a/archivo-historico/retos/general/15-herramienta-de-backup/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 15 — Herramienta de backup en CLI (Argparse) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #29 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/29)** diff --git a/archivo-historico/retos/general/16-validador-de-edad/README.md b/archivo-historico/retos/general/16-validador-de-edad/README.md deleted file mode 100644 index bcc3403..0000000 --- a/archivo-historico/retos/general/16-validador-de-edad/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 16 — Validador de edad (Excepciones Custom) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #30 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/30)** diff --git a/archivo-historico/retos/general/17-lector-de-configuraciones/README.md b/archivo-historico/retos/general/17-lector-de-configuraciones/README.md deleted file mode 100644 index eac42e3..0000000 --- a/archivo-historico/retos/general/17-lector-de-configuraciones/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 17 — Lector de configuraciones (.env) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #31 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/31)** diff --git a/archivo-historico/retos/general/18-inventario-de-tienda/README.md b/archivo-historico/retos/general/18-inventario-de-tienda/README.md deleted file mode 100644 index c830c93..0000000 --- a/archivo-historico/retos/general/18-inventario-de-tienda/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Python General 18 — Inventario de tienda (Dataclasses) - -La descripción completa, instrucciones y criterios de aceptación de este reto se encuentran en su respectivo **Issue de GitHub**. - -👉 **[Ver Issue #32 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/32)** diff --git a/archivo-historico/retos/nerve/01-chat-terminal/README.md b/archivo-historico/retos/nerve/01-chat-terminal/README.md deleted file mode 100644 index 1f39fa6..0000000 --- a/archivo-historico/retos/nerve/01-chat-terminal/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 01 — Chat de terminal con Nerve - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #1 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/1).** diff --git a/archivo-historico/retos/nerve/02-reloj-sincronizado/README.md b/archivo-historico/retos/nerve/02-reloj-sincronizado/README.md deleted file mode 100644 index 53fd3a7..0000000 --- a/archivo-historico/retos/nerve/02-reloj-sincronizado/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 02 — Reloj sincronizado entre nodos - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #2 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/2).** diff --git a/archivo-historico/retos/nerve/03-traductor-de-mensajes/README.md b/archivo-historico/retos/nerve/03-traductor-de-mensajes/README.md deleted file mode 100644 index 65050ee..0000000 --- a/archivo-historico/retos/nerve/03-traductor-de-mensajes/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 03 — Traductor de mensajes en tiempo real - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #3 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/3).** diff --git a/archivo-historico/retos/nerve/03-traductor-de-mensajes/soluciones/.gitkeep b/archivo-historico/retos/nerve/03-traductor-de-mensajes/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/04-vigilante-de-carpetas/.gitkeep b/archivo-historico/retos/nerve/04-vigilante-de-carpetas/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/04-vigilante-de-carpetas/README.md b/archivo-historico/retos/nerve/04-vigilante-de-carpetas/README.md deleted file mode 100644 index 0d8c13a..0000000 --- a/archivo-historico/retos/nerve/04-vigilante-de-carpetas/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 04 — Vigilante de carpetas (file watcher) - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #4 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/4).** diff --git a/archivo-historico/retos/nerve/04-vigilante-de-carpetas/soluciones/.gitkeep b/archivo-historico/retos/nerve/04-vigilante-de-carpetas/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/.gitkeep b/archivo-historico/retos/nerve/05-logger-distribuido/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/README.md b/archivo-historico/retos/nerve/05-logger-distribuido/README.md deleted file mode 100644 index 8fd920b..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 05 — Logger distribuido con SQLite - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #5 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/5).** diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/.gitkeep b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock deleted file mode 100644 index 89f7858..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.lock +++ /dev/null @@ -1,1142 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "alenia-nerve" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99bd2f9e3661de45b8fdd5b4c43b890caa556d9cd6e53886a8bc6476f5c9ab8" -dependencies = [ - "native-tls", - "serde", - "serde_json", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.2.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "bitflags 1.3.2", - "textwrap", - "unicode-width", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "criterion" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" -dependencies = [ - "atty", - "cast", - "clap", - "criterion-plot", - "csv", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" - -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - -[[package]] -name = "either" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "futures-core" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" - -[[package]] -name = "futures-task" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" - -[[package]] -name = "futures-util" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "libc", - "r-efi", -] - -[[package]] -name = "half" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashlink" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32069d97bb81e38fa67eab65e3393bf804bb85969f2bc06bf13f64aef5aba248" -dependencies = [ - "hashbrown 0.17.1", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libsqlite3-sys" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c19a05435c21ac299d71b6a9c13db3e3f47c520517d58990a462a1397a61db" -dependencies = [ - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "log" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "mio" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" -dependencies = [ - "libc", - "wasi", - "windows-sys", -] - -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nerveLogger" -version = "0.1.0" -dependencies = [ - "alenia-nerve", - "chrono", - "criterion", - "rusqlite", - "serde", - "serde_json", - "tokio", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "openssl" -version = "0.10.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" -dependencies = [ - "bitflags 2.13.1", - "cfg-if", - "foreign-types", - "libc", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "openssl-sys" -version = "0.9.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rayon" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "regex" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "rsqlite-vfs" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" -dependencies = [ - "hashbrown 0.16.1", - "thiserror", -] - -[[package]] -name = "rusqlite" -version = "0.40.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323" -dependencies = [ - "bitflags 2.13.1", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", - "sqlite-wasm-rs", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.13.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.13.1", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "socket2" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "sqlite-wasm-rs" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75" -dependencies = [ - "cc", - "js-sys", - "rsqlite-vfs", - "wasm-bindgen", -] - -[[package]] -name = "syn" -version = "2.0.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom", - "once_cell", - "rustix", - "windows-sys", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tokio" -version = "1.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml deleted file mode 100644 index 32b4d5a..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "nerveLogger" -version = "0.1.0" -edition = "2024" - -[dependencies] - -# Conexion con Nerve y DB -alenia-nerve = "1.5.0" -tokio = "1.53.0" -rusqlite = "0.40.1" - -# Serializacion -serde = "1.0.228" -serde_json = "1.0.150" - -# Canales para hilos -chrono = "0.4.45" - -[dev-dependencies] -criterion = { version = "0.3", features = ["html_reports"] } diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite deleted file mode 100644 index 4867830..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/model.sqlite +++ /dev/null @@ -1,19 +0,0 @@ --- En caso de querer crear la basede datos desde 0 --- necesitas tener sqlite instalado en tu maquina - --- Depues de eso en terminal ejecuta: --- >> sqlite3 logs.db --- >> sqlite3> .read model .sqlite --- --- Deberia aparecer un una nueva db como lo logs.db --- Puedes comprobar que la tabla exista con --- --- >> sqlite3> select name from sqlite_master where type='table'; --- -CREATE TABLE IF NOT EXISTS logs ( - id INTEGER PRIMARY KEY, - timestamp TEXT, - nivel TEXT, - origen TEXT, - mensaje TEXT -); \ No newline at end of file diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md deleted file mode 100644 index 8d23a18..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/readme.md +++ /dev/null @@ -1,36 +0,0 @@ - -Mi solucion al reto #5, estoy todavia familiarizandome con Rust -asi que fue un proceso el ver y probar las librerias bien antes de siquiera empezar el programa. - -En perspectiva es mas simple de lo que parece, recibe mensajes y los va despachando, -pero el ir entendiendo cada parte para al final juntar las piezas me tomo un poco de tiempo. - -Todo esta implementado en `lib.rs`, `main.rs` solo inicializa -el logger y corre un loop de forma indefinida. - -# Ejecutar -Asegurate de tener `cargo` instalado, - -## Iniciar `nerve` - -Inicializa nerve desde un entorno virtual en python: -```bash -python -m venv alenia-env - -Linux -> source alenia-env/bin/activate -Windows -> ./alenia-env/bin/activate - -(alenia-env) pip install alenia-nerve -(alenia-env) nerve start --verbose -``` - -`nerve` debe estar ejecutandose para usar el programa. - -## Ejecutar -```bash -cargo run -``` -## Correr Tests -```bash -cargo test -``` diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs deleted file mode 100644 index c375dce..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/db.rs +++ /dev/null @@ -1,8 +0,0 @@ -use rusqlite::{Connection, Result}; - -pub fn create_connection(path:&str) -> Result{ - match Connection::open(path) { - Ok(c) => Ok(c), - Err(e) => Err(e), - } -} diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs deleted file mode 100644 index 28521f8..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/lib.rs +++ /dev/null @@ -1,82 +0,0 @@ -pub mod nerve; -mod db; - -// Async en Rust es chistoso, MUY chistoso -// tremendo rabbit hole en que entre por usar await -// -// Fue mas divertido que continuar el curso - -use rusqlite::{Connection, params}; -use serde_json::{Value}; -use chrono::Local; - -pub const LABEL: &str = "loggerCentral"; - -// DEvuelve o el valor o el default establecido -fn get_str_param<'a>(msg:&'a Value, param:&str, default: &'a str) -> &'a str { - - // El default de serde_json es Value::Null - // lo que produciria None a la siguiente funcion en cadena - // y haria que se devuelva default al final - msg.get(param) - .unwrap_or_default() - .as_str() - .unwrap_or(default) -} - -// Obten los string que nos interesan del mensaje -fn process_msg(notification: &Value) -> (&str, &str, &str) { - - // Nombre del nodo - let origen = notification["from"].as_str().unwrap(); - - // Nivel y origen - let msg = ¬ification["payload"]; - - let nivel = get_str_param(msg, "nivel", "NONE"); - let mensaje = get_str_param(msg, "mensaje", ""); - - (origen, nivel, mensaje) -} - -// Escribe a la db... duh -fn write_log_to_db(origen: &str, nivel: &str, mensaje: &str) -> Result { - - let db_connection: Connection = db::create_connection("logs.db")?; - let timestamp = Local::now().format("%d-%m-%Y %H:%M:%S").to_string(); - - db_connection.execute( - "INSERT INTO logs (origen, nivel, mensaje, timestamp) VALUES (?1, ?2, ?3, ?4)", - params![origen, nivel, mensaje, timestamp] - ) -} - -// Despachador -fn message_dispacher(msg: Value) { - - let (origen, nivel, mensaje) = process_msg(&msg); - - if nivel == "NONE" || mensaje.is_empty() { - println!("Log sent by {} rejected: incorrect parameters", origen); - return; - } - match write_log_to_db(origen, nivel, mensaje) { - Ok(_) => {}, - Err(e) => println!("Couldn't write log to db: {e}"), - } -} - -pub async fn start_logger() -> Result<(), Box> { - - // Crea cliente - let client = nerve::make_client(&LABEL).await?; - - // Configura la funcion para despachar mensajes - client.listen( - move |msg| { message_dispacher(msg); }, - None - ).await; - - println!("Central logger started."); - Ok(()) -} \ No newline at end of file diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs deleted file mode 100644 index af66df2..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::time::Duration; - -use nerveLogger::{self, start_logger}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - - let _ = start_logger().await?; - - println!("Press Ctrl+C to stop..."); - loop { - tokio::time::sleep(Duration::from_millis(500)).await; - }; - - Ok(()) -} \ No newline at end of file diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs deleted file mode 100644 index 2899595..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/src/nerve.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::time::Duration; -use alenia_nerve::{NexusClient}; - -const RETRY_INTERVAL:Duration = Duration::from_millis(250); -const CONFIG_PATH:&str = ""; - -pub async fn make_client(name: &str) -> Result> { - let mut client = NexusClient::new(RETRY_INTERVAL, CONFIG_PATH, None); - - let _response = client - .connect(name) - .await?; - Ok(client) -} \ No newline at end of file diff --git a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs b/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs deleted file mode 100644 index 57eaf3c..0000000 --- a/archivo-historico/retos/nerve/05-logger-distribuido/soluciones/Vidot1-Rust/tests/test.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::{time::Duration}; - -use nerveLogger::{self, nerve}; -use serde_json::json; - -async fn send_mock_logs() -> Result<(), Box> { - - let sender = nerve::make_client("TestSender1").await?; - - sender.send( - nerveLogger::LABEL, - json!({ - "nivel": "WARN", - "mensaje": "Defectos procesando mensajes", - }) - )?; - - sender.send( - nerveLogger::LABEL, - json!({ - "nivel": "INFO", - "mensaje": "Inicializado proceso #422", - }) - )?; - - sender.send( - nerveLogger::LABEL, - json!({ - "nivel": "ERROR", - "mensaje": "Proceso inexistente, cerrando...", - }) - )?; - - Ok(()) -} - -async fn send_invalid_logs() -> Result<(), Box> { - - let sender = nerve::make_client("TestSender2").await?; - - sender.send( - nerveLogger::LABEL, - json!({ - "nivel": true, - "mensaje": false, - }) - )?; - - sender.send( - nerveLogger::LABEL, - json!({ - "nivel": ["no", "valido"], - "mensaje": 32, - }) - )?; - - sender.send( - nerveLogger::LABEL, - json!({ - "nivel": 34.4, - "mensaje": 'd', - }) - )?; - - Ok(()) -} - -#[tokio::test] -async fn test_send_msg() { - - let _ = nerveLogger::start_logger().await; - - // Estos deberian quedar en logs.db - match send_mock_logs().await { - Ok(_) => println!("TestSender1 terminado con exito"), - Err(e) => println!("{}", e), - } - - // Estos deberias bloquearse, mal formados - match send_invalid_logs().await { - Ok(_) => println!("TestSender2 terminado con exito"), - Err(e) => println!("{}", e), - }; - - // Dale tiempo para que los mensajes puedan enviarse desde nerve a su destino - tokio::time::sleep(Duration::from_millis(1000)).await; -} \ No newline at end of file diff --git a/archivo-historico/retos/nerve/06-puente-de-progreso-gif/.gitkeep b/archivo-historico/retos/nerve/06-puente-de-progreso-gif/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/06-puente-de-progreso-gif/README.md b/archivo-historico/retos/nerve/06-puente-de-progreso-gif/README.md deleted file mode 100644 index 3d39d41..0000000 --- a/archivo-historico/retos/nerve/06-puente-de-progreso-gif/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 06 — Puente de progreso al procesar imágenes - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #6 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/6).** diff --git a/archivo-historico/retos/nerve/06-puente-de-progreso-gif/soluciones/.gitkeep b/archivo-historico/retos/nerve/06-puente-de-progreso-gif/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/07-bot-discord-nerve/.gitkeep b/archivo-historico/retos/nerve/07-bot-discord-nerve/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/07-bot-discord-nerve/README.md b/archivo-historico/retos/nerve/07-bot-discord-nerve/README.md deleted file mode 100644 index 0e6b334..0000000 --- a/archivo-historico/retos/nerve/07-bot-discord-nerve/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 07 — Bot de Discord que controla scripts locales vía Nerve - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #7 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/7).** diff --git a/archivo-historico/retos/nerve/07-bot-discord-nerve/soluciones/.gitkeep b/archivo-historico/retos/nerve/07-bot-discord-nerve/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/08-puente-http/.gitkeep b/archivo-historico/retos/nerve/08-puente-http/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/08-puente-http/README.md b/archivo-historico/retos/nerve/08-puente-http/README.md deleted file mode 100644 index 497de10..0000000 --- a/archivo-historico/retos/nerve/08-puente-http/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 08 — Puente HTTP para Nerve (FastAPI) - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #8 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/8).** diff --git a/archivo-historico/retos/nerve/08-puente-http/soluciones/.gitkeep b/archivo-historico/retos/nerve/08-puente-http/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/09-probador-de-carga/.gitkeep b/archivo-historico/retos/nerve/09-probador-de-carga/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/09-probador-de-carga/README.md b/archivo-historico/retos/nerve/09-probador-de-carga/README.md deleted file mode 100644 index c934c31..0000000 --- a/archivo-historico/retos/nerve/09-probador-de-carga/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 09 — Probador de carga (load tester) para el Hub de Nerve - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #9 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/9).** diff --git a/archivo-historico/retos/nerve/09-probador-de-carga/soluciones/.gitkeep b/archivo-historico/retos/nerve/09-probador-de-carga/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep b/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/README.md b/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/README.md deleted file mode 100644 index 3640aec..0000000 --- a/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reto 10 — Portar el cliente de Nerve a un nuevo lenguaje - -Este es el directorio donde la comunidad sube los entregables de este reto. - -👉 **Para conocer las instrucciones completas, criterios y pistas, visita el [Issue #10 en GitHub](https://github.com/Kaia-Alenia/nerve-community/issues/10).** diff --git a/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep b/archivo-historico/retos/nerve/10-cliente-nuevo-lenguaje/soluciones/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/assets/.gitkeep b/assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/assets/Vi-dot1.png b/assets/Vi-dot1.png deleted file mode 100644 index 032062d832dbe5343a04e51127d54d558964a445..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47330 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 diff --git a/assets/images/.gitkeep b/assets/images/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/assets/tacos-termux.jpg b/assets/tacos-termux.jpg deleted file mode 100644 index 3f3d8b25a2cffd7fee77cfa80e502ea73356723f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248736 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