diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 0000000..4fc8c6b --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,51 @@ +name: Build Docs + +on: + push: + branches: [ "main" ] # Se ejecuta en cada push a main + workflow_dispatch: # Permite lanzarlo manualmente desde la pestaña Actions + +jobs: + docs: + runs-on: ubuntu-latest + + steps: + # 1. Clonar el repositorio + - name: Checkout repo + uses: actions/checkout@v4 + + # 2. Configurar Python (para scripts de documentación) + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + # 3. Instalar dependencias necesarias + - name: Install dependencies + run: | + pip install markdown pandoc + + # 4. Generar projects.json recorriendo carpetas y subcarpetas + - name: Generate projects.json + run: python scripts/scan_projects.py + + # 5. Concatenar todos los README.md en un GLOBAL.md + - name: Generate GLOBAL.md + run: python scripts/concat_readmes.py + + # 6. Convertir GLOBAL.md a PDF + - name: Install Pandoc + run: sudo apt-get update && sudo apt-get install -y pandoc + - name: Install LaTeX engine + run: sudo apt-get install -y texlive texlive-xetex texlive-fonts-recommended + - name: Convert to PDF + run: pandoc GLOBAL.md -o GLOBAL.pdf + + # 7. Subir artefactos generados (JSON y PDF) + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: portfolio-docs + path: | + projects.json + GLOBAL.pdf diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..1d4b50e --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,105 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '16 4 * * 1' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: c-cpp + build-mode: autobuild + - language: javascript-typescript + build-mode: none + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 0000000..4613569 --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,22 @@ +# This workflow will triage pull requests and apply a label based on the +# paths that are modified in the pull request. +# +# To use this workflow, you will need to set up a .github/labeler.yml +# file with configuration. For more information, see: +# https://github.com/actions/labeler + +name: Labeler +on: [pull_request_target] + +jobs: + label: + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 0000000..b92553e --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,53 @@ +name: Pylint + Black + +on: + push: + branches: [ "main" ] # Ejecutar solo en 'main' o las ramas que definas + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v4 # 1. Clonar el repositorio + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Instalar dependencias, Pylint y Black + run: | + python -m pip install --upgrade pip + + # Buscar y instalar dependencias desde requisitos.txt + REQS=$(find . -name requisitos.txt) + if [ -n "$REQS" ]; then + echo "Se encontraron archivos de requisitos:" + echo "$REQS" + for req in $REQS; do + echo "Instalando dependencias desde $req..." + pip install -r "$req" + done + else + echo "No se encontraron archivos requisitos.txt. Omitiendo instalación de dependencias." + fi + + # 2. Instalar Pylint y Black + pip install pylint black + + - name: Ejecutar formateador Black + run: | + echo "Verificando formato de código con Black..." + black --line-length 100 . + + - name: Analizando el código con Pylint + run: | + # 4. Ejecutar Pylint en todos los archivos .py rastreados por Git + export PYTHONPATH=$PYTHONPATH:$(pwd)/unidad_7/pystock_management + pylint $(git ls-files '*.py') \ No newline at end of file diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..aaabb02 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,49 @@ +name: Python application + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ "3.8","3.9","3.10","3.11" ] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest pytest-cov + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Lint with flake8 + run: | + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Test with pytest + run: | + pip install pytest-cov + PYTHONPATH=. pytest tests/ --cov=unidad_1 --cov-report=term-missing diff --git a/.github/workflows/summary.yml b/.github/workflows/summary.yml new file mode 100644 index 0000000..9b07bb8 --- /dev/null +++ b/.github/workflows/summary.yml @@ -0,0 +1,34 @@ +name: Summarize new issues + +on: + issues: + types: [opened] + +jobs: + summary: + runs-on: ubuntu-latest + permissions: + issues: write + models: read + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run AI inference + id: inference + uses: actions/ai-inference@v1 + with: + prompt: | + Summarize the following GitHub issue in one paragraph: + Title: ${{ github.event.issue.title }} + Body: ${{ github.event.issue.body }} + + - name: Comment with AI summary + run: | + gh issue comment $ISSUE_NUMBER --body '${{ steps.inference.outputs.response }}' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + RESPONSE: ${{ steps.inference.outputs.response }} diff --git a/Curso-CPP/20241019-Base.cpp b/Curso-CPP/20241019-Base.cpp deleted file mode 100644 index 87cb8c9..0000000 --- a/Curso-CPP/20241019-Base.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include -#include -#include -using namespace std; - -struct Cont{ -int dat; -Cont *foll; -}; - -Cont *phill= NULL; -Cont *front=NULL; -Cont *end=NULL; - -//Crear pila, puntero y dirección + el dato para rellenar -void sozoPhill(Cont *&phill , int dato){ - Cont *novo_dono= new Cont(); - novo_dono->dat=dato; //Rellenar dato - novo_dono->foll=phill; - phill=novo_dono; -} - -//Sacar pila, puntero y dirección + dirección del dato rellenado -void hakaiPhill(Cont *&phill, int &dat){ - if (phill == NULL) { - cout << "La pila está vacía." << endl; - return; - } - cout << "Datos:" << endl; - while (phill != NULL) { - cout << "" << phill->dat << endl; - Cont *axil = phill; - stutu= axil->stu; - phill = phill->foll; - delete axil; - } -} - -//Cola vacía -bool VoidCoil(Cont *front){ - if (front == NULL){ - return true; - } - else{ - return false; - } -} - -//Crear cola, puntero con dirección para el comienzo y final de la cola + su dato a rellenar -void sozoCoil(Cont *&front, Cont *&end, struct stu) { - Cont *novo_dono = new Cont(); - novo_dono->stu = stu; - novo_dono->foll = NULL; - if (VoidCoil(front)) { - front = novo_dono; - } else { - end->foll = novo_dono; - } - end = novo_dono; -} - - -//Sacar cola, puntero con dirección para el comienzo y final de la cola + dirección del dato rellenado -void hakaiCoil(Cont *&front, Cont *&end, &dat) { - if (VoidCoil(front)) { - cout << "La cola está vacía." << endl; - return; - } - cout << "Datos:" << endl; - while (!VoidCoil(front)) { - cout << "" << front->dat<< endl; - stutu= axil->stu; - front = front->foll; - if (front == NULL) { - end = NULL; - } - delete axil; - } - -} - -//Función Archivo -void save_arch(){ - string filename; - string ext; - fstream arch; - ext=".txt"; - cout << "Ingrese el nombre con el que desea guardar el archivo: " << endl; - cin >> filename; - filename+=ext; - arch.open(filename.c_str(),ios::out); - if (arch.fail()) { - cout << "* ERROR GENERARANDO RECIBO *" << endl; - } - arch << "/n " << endl; - cout << "Archivo Generado" << endl; - arch.close(); -} -//Menú rotativo -int main (){ - char opc, res; - int op; - do { - cout << "MENU" << endl; - cout << "1. Ingresar y Sacar pila" << endl; - cout << "2. Ingresar y Sacar cola" << endl; - cout << "3. Salir" << endl; - cin >> op; - switch (op) { - case 1: - do { - cout << "Ingresar datos" << endl; - sozo - cout << "¿Ingresar mas datos? (s/n): " << endl; - cin >> opc; - system ("cls"); - } while (opc == 's'); - hakai - break; - case 2: - do { - cout << "Ingresar datos" << endl; - sozo - cout << "¿Ingresar mas datos? (s/n): " << endl; - cin >> opc; - system ("cls"); - } while (opc == 's'); - hakai - break; - case 3: - cout << "Saliendo..." << endl; - return 0; - default: - cout << "Opción no válida. Intente de nuevo." << endl; - break; - } - cout << "¿Desea regresar al menu? (s/n): " << endl; - cin >> res; - system ("cls"); - } while (res == 's'); - return 0; -} diff --git a/Curso-CPP/20241019-prom.cpp b/Curso-CPP/20241019-prom.cpp deleted file mode 100644 index f9b9b5b..0000000 --- a/Curso-CPP/20241019-prom.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include - -using namespace std; - - -struct estudiante{ - - char nombre[]; - char materia[60]; - float nota1,nota2,nota3,promedio; - -}; - -float calcularPromedio(float,float,float); - -int main(){ - - estudiante alumno[1]; - int i=1; - - for(int i=0;i<=1;i++){ - - - cout<<"ingrese el nombre del estudiante :"<>alumno[i].nombre; - cout<<"ingrese el nombre de la materia :"<>alumno[i].materia; - - cout<<"ingrese el la nota 1 :"<>alumno[i].nota1; - cout<<"ingrese el la nota 2 :"<>alumno[i].nota2; - cout<<"ingrese el la nota 3 :"<>alumno[i].nota3; - - alumno[i].promedio=calcularPromedio(alumno[i].nota1,alumno[i].nota2,alumno[i].nota3); - - cout<<"Nota del estudiante "< +#include +#include +using namespace std; + +class Loger { +private: + string user; + string password; + string q1, q2; // Preguntas secretas + string a1, a2; // Respuestas correctas + +public: + Loger(string user, string password, string q1, string a1, string q2, string a2) + : user(user), password(password), q1(q1), a1(a1), q2(q2), a2(a2) {} + + // Login básico con 3 intentos + bool login() { + for (int intento = 1; intento <= 3; intento++) { + string u, p; + cout << "Usuario: "; + getline(cin, u); + cout << "Contraseña: "; + getline(cin, p); + if (u == user && p == password) { + cout << "Acceso concedido.\n"; + return true; + } + cout << "Credenciales incorrectas. Intento " << intento << "/3.\n"; + } + cout << "Demasiados intentos fallidos.\n"; + return false; + } + + // Cambio directo de contraseña (requiere estar logueado) + void cambiarContrasena() { + string nueva, confirmar; + cout << "Ingrese nueva contraseña: "; + getline(cin, nueva); + cout << "Confirme nueva contraseña: "; + getline(cin, confirmar); + if (nueva.empty()) { + cout << "La contraseña no puede estar vacía.\n"; + return; + } + if (nueva != confirmar) { + cout << "Las contraseñas no coinciden.\n"; + return; + } + password = nueva; + cout << "Contraseña actualizada exitosamente.\n"; + } + + // Recuperación de contraseña usando 2 preguntas secretas + bool recuperarContrasena() { + string r1, r2; + cout << "Pregunta 1: " << q1 << "\nRespuesta: "; + getline(cin, r1); + cout << "Pregunta 2: " << q2 << "\nRespuesta: "; + getline(cin, r2); + + if (normalize(r1) == normalize(a1) && normalize(r2) == normalize(a2)) { + cout << "Validación correcta. Establezca nueva contraseña.\n"; + cambiarContrasena(); + return true; + } else { + cout << "Respuestas incorrectas. No se pudo recuperar la contraseña.\n"; + return false; + } + } + + // Utilidad: muestra datos (sin exponer la contraseña) + void mostrarPerfil() const { + cout << "Usuario: " << user << "\n"; + cout << "Pregunta 1: " << q1 << "\n"; + cout << "Pregunta 2: " << q2 << "\n"; + } + +private: + static string normalize(const string& s) { + // Trim simple + case-insensitive + size_t start = s.find_first_not_of(" \t\n\r"); + size_t end = s.find_last_not_of(" \t\n\r"); + string t = (start == string::npos) ? "" : s.substr(start, end - start + 1); + for (char& c : t) c = tolower(static_cast(c)); + return t; + } +}; + +// Multiplica dos números y repite un mensaje n = a * b veces +void multiplicarYRepetir() { + long long a, b; + string frase; + + cout << "Ingrese el primer número: "; + while (!(cin >> a)) { + cout << "Número inválido. Intente de nuevo: "; + cin.clear(); + cin.ignore(numeric_limits::max(), '\n'); + } + cout << "Ingrese el segundo número: "; + while (!(cin >> b)) { + cout << "Número inválido. Intente de nuevo: "; + cin.clear(); + cin.ignore(numeric_limits::max(), '\n'); + } + cin.ignore(numeric_limits::max(), '\n'); // limpiar salto + + cout << "Ingrese la frase: "; + getline(cin, frase); + + long long n = a * b; + if (n < 0) { + cout << "El producto es negativo. No se puede repetir una cantidad negativa.\n"; + return; + } + if (n > 100000) { + cout << "Cantidad excesiva (" << n << "). Limite de seguridad: 100000.\n"; + return; + } + for (long long i = 0; i < n; i++) { + cout << frase << "\n"; + } +} + +int main() { + // Configuración inicial (puedes reemplazar por entradas del usuario si deseas) + string user = "Fullo355"; + string pass = "346464"; + string q1 = "Color favorito"; + string a1 = "Azul"; + string q2 = "Ciudad natal"; + string a2 = "Caracas"; + + Loger auth(user, pass, q1, a1, q2, a2); + + cout << "=== Sistema de Login ===\n"; + auth.mostrarPerfil(); + cout << "\nIntente iniciar sesión.\n"; + + bool acceso = auth.login(); + + int opcion = -1; + do { + cout << "\n--- MENU ---\n"; + cout << "1. Cambiar contraseña\n"; + cout << "2. Recuperar contraseña (2 preguntas secretas)\n"; + cout << "3. Multiplicar y repetir mensaje (n = a*b)\n"; + cout << "4. Mostrar perfil\n"; + cout << "5. Salir\n"; + cout << "Seleccione una opción: "; + if (!(cin >> opcion)) { + cin.clear(); + cin.ignore(numeric_limits::max(), '\n'); + cout << "Entrada inválida.\n"; + continue; + } + cin.ignore(numeric_limits::max(), '\n'); // limpiar salto + + switch (opcion) { + case 1: + if (!acceso) { + cout << "Debe iniciar sesión para cambiar la contraseña.\n"; + } else { + auth.cambiarContrasena(); + } + break; + case 2: + auth.recuperarContrasena(); + // Si recuperación exitosa, considera acceso concedido + acceso = true; + break; + case 3: + multiplicarYRepetir(); + break; + case 4: + auth.mostrarPerfil(); + break; + case 5: + cout << "Saliendo...\n"; + break; + default: + cout << "Opción no válida.\n"; + } + } while (opcion != 5); + + return 0; +} \ No newline at end of file diff --git a/Curso-CPP/AuthUtilsPOO_C++/POO.md b/Curso-CPP/AuthUtilsPOO_C++/POO.md new file mode 100644 index 0000000..d1aed6b --- /dev/null +++ b/Curso-CPP/AuthUtilsPOO_C++/POO.md @@ -0,0 +1,105 @@ +# 🔐 Sistema de Autenticación y Utilidades en C++ + +![C++ Badge](https://img.shields.io/badge/Language-C++-blue.svg) +![Status](https://img.shields.io/badge/Status-Educational-green.svg) + +Este proyecto implementa un sistema de gestión de usuarios robusto (clase `Loger`) junto con utilidades matemáticas, demostrando conceptos fundamentales de Programación Orientada a Objetos (POO), validación de flujos y seguridad básica en C++. + +--- + +## ✨ Características Principales + +* 🛡️ **Login Seguro:** Sistema de autenticación con límite de 3 intentos fallidos. +* 🚑 **Recuperación de Cuenta:** Mecanismo de preguntas secretas (Security Questions) con normalización de texto (ignora mayúsculas/espacios). +* 🔄 **Gestión de Credenciales:** Funcionalidad para cambiar la contraseña de forma segura. +* 🔢 **Math Utils:** Herramienta para calcular $n = a \times b$ y repetir patrones de texto, con protección contra desbordamiento de buffer. + +--- + +## 🚀 Instrucciones de Uso + +### Pre-requisitos +Necesitas un compilador de C++ estándar (como G++, Clang o MSVC). + +### 🛠️ Compilación +Para compilar el código, abre tu terminal y ejecuta: + +```bash +g++ main.cpp -o sistema_auth +```` + +### ▶️ Ejecución + +Una vez compilado, ejecuta el programa: + +**En Windows:** + +```bash +sistema_auth.exe +``` + +**En Linux/Mac:** + +```bash +./sistema_auth +``` + +----- + +## 🧠 Documentación del Código + +### 1\. Clase `Loger` 👤 + +El corazón del sistema. Encapsula la lógica de seguridad. + +| Método | Descripción | +| :--- | :--- | +| `login()` | Solicita credenciales. Retorna `true` si el acceso es concedido. Bloquea tras 3 fallos. | +| `recuperarContrasena()` | Valida identidad mediante `q1` y `q2`. Si es correcto, fuerza cambio de clave. | +| `cambiarContrasena()` | Pide nueva clave y confirmación. Valida que no estén vacías. | +| `mostrarPerfil()` | Muestra datos públicos (Usuario y Preguntas) sin revelar secretos. | +| `normalize(string)` | *Privado*. Limpia el texto (trim + lowercase) para comparaciones flexibles. | + +### 2\. Utilidad `multiplicarYRepetir` ✖️ + +Función auxiliar fuera de la clase. + +**Fórmula:** +Calcula las iteraciones usando $n = a \times b$. + +**Validaciones:** + + * Protección contra entrada de letras en campos numéricos (`cin.clear`). + * Límite de seguridad: `n <= 100000`. + * Validación de signos: `n >= 0`. + +----- + +## 🗺️ Flujo de Ejecución + +El programa sigue este ciclo de vida: + +```mermaid +graph TD + A[Inicio] --> B{Login Inicial}; + B -- Credenciales OK --> C[Menú Principal]; + B -- 3 Fallos --> X[Terminar Programa]; + + C --> D[1. Cambiar Contraseña]; + C --> E[2. Recuperar Contraseña]; + C --> F[3. Multiplicar y Repetir]; + C --> G[4. Ver Perfil]; + C --> H[5. Salir]; + + E -- Recuperación OK --> D; +``` + +----- + +## ⚠️ Notas de Seguridad (Disclaimer) + +Este código es con fines **educativos**. Para un entorno de producción real, considera las siguientes mejoras: + +1. **Hashing:** Nunca almacenar contraseñas en texto plano (usar SHA-256 o bcrypt). +2. **Persistencia:** Guardar los usuarios en una base de datos o archivo, no en variables volátiles. +3. **Ocultamiento:** Usar librerías como `conio.h` o `termios` para mostrar asteriscos `***` al escribir la contraseña. \ No newline at end of file diff --git a/Curso-CPP/Chuleta_Base.cpp b/Curso-CPP/Chuleta_Base.cpp new file mode 100644 index 0000000..4d67a84 --- /dev/null +++ b/Curso-CPP/Chuleta_Base.cpp @@ -0,0 +1,227 @@ +#include +#include +#include +using namespace std; + +// -------------------- Struct base para pila/cola -------------------- +struct Cont { + int dat; + Cont* foll; +}; + +// Punteros globales para pila y cola +Cont* phill = nullptr; // pila +Cont* front = nullptr; // cola inicio +Cont* end = nullptr; // cola fin + +// Crear pila +void sozoPhill(Cont*& phill, int dato) { + Cont* novo_dono = new Cont(); + novo_dono->dat = dato; + novo_dono->foll = phill; + phill = novo_dono; +} + +// Sacar pila +void hakaiPhill(Cont*& phill) { + while (phill != nullptr) { + cout << "Dato pila: " << phill->dat << endl; + Cont* axil = phill; + phill = phill->foll; + delete axil; + } +} + +// Cola vacía +bool VoidCoil(Cont* front) { + return (front == nullptr); +} + +// Crear cola +void sozoCoil(Cont*& front, Cont*& end, int dato) { + Cont* novo_dono = new Cont(); + novo_dono->dat = dato; + novo_dono->foll = nullptr; + if (VoidCoil(front)) { + front = novo_dono; + } else { + end->foll = novo_dono; + } + end = novo_dono; +} + +// Sacar cola +void hakaiCoil(Cont*& front, Cont*& end) { + while (!VoidCoil(front)) { + cout << "Dato cola: " << front->dat << endl; + Cont* axil = front; + front = front->foll; + if (front == nullptr) end = nullptr; + delete axil; + } +} + +// -------------------- Ejemplo if y for -------------------- +void ejemploIfFor() { + cout << "\nEjemplo IF y FOR:\n"; + for (int i = 1; i <= 5; i++) { + if (i % 2 == 0) { + cout << i << " es par\n"; + } else { + cout << i << " es impar\n"; + } + } +} + +// -------------------- Lista enlazada -------------------- +struct NodoLista { + int dato; + NodoLista* sig; +}; + +void ejemploLista() { + cout << "\nEjemplo Lista Enlazada:\n"; + NodoLista* head = nullptr; + // Insertar 3 nodos + for (int i = 1; i <= 3; i++) { + NodoLista* nuevo = new NodoLista(); + nuevo->dato = i * 10; + nuevo->sig = head; + head = nuevo; + } + // Recorrer lista + NodoLista* temp = head; + while (temp != nullptr) { + cout << "Nodo lista: " << temp->dato << endl; + temp = temp->sig; + } +} + +// -------------------- Árbol binario -------------------- +struct NodoArbol { + int dato; + NodoArbol* izq; + NodoArbol* der; +}; + +NodoArbol* insertarArbol(NodoArbol* raiz, int valor) { + if (raiz == nullptr) { + NodoArbol* nuevo = new NodoArbol(); + nuevo->dato = valor; + nuevo->izq = nuevo->der = nullptr; + return nuevo; + } + if (valor < raiz->dato) raiz->izq = insertarArbol(raiz->izq, valor); + else raiz->der = insertarArbol(raiz->der, valor); + return raiz; +} + +void recorrerArbol(NodoArbol* raiz) { + if (raiz != nullptr) { + recorrerArbol(raiz->izq); + cout << "Nodo árbol: " << raiz->dato << endl; + recorrerArbol(raiz->der); + } +} + +void ejemploArbol() { + cout << "\nEjemplo Árbol Binario:\n"; + NodoArbol* raiz = nullptr; + raiz = insertarArbol(raiz, 50); + raiz = insertarArbol(raiz, 30); + raiz = insertarArbol(raiz, 70); + recorrerArbol(raiz); +} + +// -------------------- Clase básica -------------------- +class ClaseBasica { +private: + int valor; // encapsulado privado +public: + ClaseBasica(int v) { // constructor + valor = v; + cout << "Constructor llamado con valor " << valor << endl; + } + ~ClaseBasica() { // destructor + cout << "Destructor llamado\n"; + } + void mostrar() { // método público + cout << "Valor: " << valor << endl; + } +}; + +void ejemploClase() { + cout << "\nEjemplo Clase Básica:\n"; + ClaseBasica obj(42); + obj.mostrar(); +} + +// -------------------- Guardar archivo -------------------- +void save_arch() { + string filename; + fstream arch; + cout << "Ingrese nombre de archivo: "; + cin >> filename; + filename += ".txt"; + arch.open(filename.c_str(), ios::out); + if (!arch) { + cout << "Error generando archivo\n"; + } else { + arch << "Ejemplo de escritura\n"; + cout << "Archivo generado\n"; + } + arch.close(); +} + +// -------------------- Menú rotativo -------------------- +int main() { + int op; + char res; + do { + cout << "\n--- MENU ---\n"; + cout << "1. Ingresar y sacar pila\n"; + cout << "2. Ingresar y sacar cola\n"; + cout << "3. Guardar archivo\n"; + cout << "4. Ejemplo IF y FOR\n"; + cout << "5. Ejemplo Lista Enlazada\n"; + cout << "6. Ejemplo Árbol Binario\n"; + cout << "7. Ejemplo Clase Básica\n"; + cout << "8. Salir\n"; + cin >> op; + switch (op) { + case 1: + sozoPhill(phill, 10); + sozoPhill(phill, 20); + hakaiPhill(phill); + break; + case 2: + sozoCoil(front, end, 30); + sozoCoil(front, end, 40); + hakaiCoil(front, end); + break; + case 3: + save_arch(); + break; + case 4: + ejemploIfFor(); + break; + case 5: + ejemploLista(); + break; + case 6: + ejemploArbol(); + break; + case 7: + ejemploClase(); + break; + case 8: + cout << "Saliendo...\n"; + return 0; + default: + cout << "Opción no válida.\n"; + } + cout << "¿Desea regresar al menú? (s/n): "; + cin >> res; + } while (res == 's'); + return 0; +} diff --git a/Curso-CPP/Code.cpp b/Curso-CPP/Code.cpp deleted file mode 100644 index 47c60e5..0000000 --- a/Curso-CPP/Code.cpp +++ /dev/null @@ -1,84 +0,0 @@ -//Crea un programa que maneje una lista de estudiantes utilizando una pila. Cada estudiante debe tener un "struct" que contenga su nombre, edad y nota. -#include -#include -#include -using namespace std; - -struct estudiante{ - string nom; - int edad, ci, nota; -}; - -struct nodo{ - estudiante estu; - nodo *siguiente; -}; - -void ingresarpila(nodo *&, estudiante estu); -void sacarpila(nodo *&, estudiante &estupila); - -int main (){ - nodo *pila=NULL; - estudiante estu; - string rep; - - do{ - cout << "Ingrese el nombre del alumno:"<< endl; - cin>> estu.nom; - cout << "Ingrese la edad del alumno:" << endl; - cin>> estu.edad; - cout << "Ingrese la cedula del alumno:" << endl; - cin >> estu.ci; - cout << "Ingrese la calificacion del alumno:" << endl; - cin >> estu.nota; - ingresarpila(pila,estu); - cout << "Desea agregar mas alumnos al sistema? (s/n)" << endl; - cin>> rep; - system("cls"); - } - while (rep=="s"); - while (pila != NULL){ - sacarpila (pila,estu); - if (pila != NULL){ - cout << "Nombre: " << estu.nom << endl; - cout << "Edad: " << estu.edad << endl; - cout << "Cedula: " << estu.ci << endl; - cout << "Calificacion: " << estu.nota << endl; - cout << "-----------" << endl; - } - else { - cout << "." << endl; - } - } - - return 0; -} -void ingresarpila(nodo *&pila,estudiante estupila){ - nodo *nuevo_nodo = new nodo(); - nuevo_nodo->estu = estupila; - nuevo_nodo->siguiente = pila; - pila = nuevo_nodo; - cout << "Ingresando datos del estudiante a la pila..."<< endl; -} -void sacarpila(nodo *&pila, estudiante &estupila){ - nodo *aux=pila; - estupila = aux->estu; - pila = aux->siguiente; - delete aux; - string o; - fstream sacarpila; - cout << "Ingrese un nombre para guardar el archivo y su extension" << endl; - cin>> o; - sacarpila.open(o.c_str(),ios::app); - if (sacarpila.fail()){ - cout<< "Error" << endl; - } - else { - sacarpila << "Nombre: " << estupila.nom << endl; - sacarpila << "Edad: " << estupila.edad << endl; - sacarpila << "Cedula: " << estupila.ci << endl; - sacarpila << "Calificacion: " << estupila.nota << endl; - sacarpila << "-----------" << endl; - } - sacarpila.close(); -} diff --git a/Curso-CPP/Copia prueba.cpp b/Curso-CPP/Copia prueba.cpp deleted file mode 100644 index da2357c..0000000 --- a/Curso-CPP/Copia prueba.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace std; - -struct Producto { - string nombre; - int cantidad; - float precio; - bool disponible; // true si está disponible, false si está en espera -}; - -// Funciones -void agregarProducto(stack& pila, queue& cola); -void mostrarProductos(const stack& pila, const queue& cola); -void guardarProductos(const stack& pila, const queue& cola); - -int main() { - stack pilaProductos; - queue colaProductos; - - int opcion; - do { - cout << "1. Agregar producto\n"; - cout << "2. Mostrar productos\n"; - cout << "3. Guardar productos\n"; - cout << "4. Salir\n"; - cout << "Seleccione una opción: "; - cin >> opcion; - - switch (opcion) { - case 1: - agregarProducto(pilaProductos, colaProductos); - break; - case 2: - mostrarProductos(pilaProductos, colaProductos); - break; - case 3: - guardarProductos(pilaProductos, colaProductos); - break; - case 4: - cout << "Saliendo del programa...\n"; - break; - default: - cout << "Opción no válida. Intente de nuevo.\n"; - } - } while (opcion != 4); - - return 0; -} - -void agregarProducto(stack& pila, queue& cola) { - Producto nuevoProducto; - cout << "Nombre del producto: "; - cin.ignore(); // Limpiar el buffer - getline(cin, nuevoProducto.nombre); - cout << "Cantidad del producto: "; - cin >> nuevoProducto.cantidad; - cout << "Precio del producto: "; - cin >> nuevoProducto.precio; - cout << "¿Está disponible? (1: Sí, 0: No): "; - cin >> nuevoProducto.disponible; - - if (nuevoProducto.disponible) { - cola.push(nuevoProducto); - } else { - pila.push(nuevoProducto); - } -} - -void mostrarProductos(const stack& pila, const queue& cola) { - cout << "\nProductos en Espera:\n"; - stack pilaTemporal = pila; // Copia de la pila para mostrar - while (!pilaTemporal.empty()) { - Producto p = pilaTemporal.top(); - cout << "- " << p.nombre << ", Cantidad: " << p.cantidad << ", Precio: $" << p.precio << endl; - pilaTemporal.pop(); - } - - cout << "\nProductos Disponibles:\n"; - queue colaTemporal = cola; // Copia de la cola para mostrar - while (!colaTemporal.empty()) { - Producto p = colaTemporal.front(); - cout << "- " << p.nombre << ", Cantidad: " << p.cantidad << ", Precio: $" << p.precio << endl; - colaTemporal.pop(); - } - cout << endl; -} - -void guardarProductos(const stack& pila, const queue& cola) { - ofstream archivo("productos.txt"); - if (!archivo) { - cerr << "Error al abrir el archivo para guardar.\n"; - return; - } - - // Guardar productos en espera - stack pilaTemporal = pila; - while (!pilaTemporal.empty()) { - Producto p = pilaTemporal.top(); - archivo << "0 " << p.nombre << " " << p.cantidad << " " << p.precio << endl; // 0 para en espera - pilaTemporal.pop(); - } - - // Guardar productos disponibles - queue colaTemporal = cola; - while (!colaTemporal.empty()) { - Producto p = colaTemporal.front(); - archivo << "1 " << p.nombre << " " << p.cantidad << " " << p.precio << endl; // 1 para disponible - colaTemporal.pop(); - } - - archivo.close(); - cout << "Productos guardados exitosamente.\n"; -} \ No newline at end of file diff --git a/Curso-CPP/pil_col.cpp b/Curso-CPP/DSAStackQueueStu/pil_col.cpp similarity index 100% rename from Curso-CPP/pil_col.cpp rename to Curso-CPP/DSAStackQueueStu/pil_col.cpp diff --git a/Curso-CPP/DSAStackQueueStu/pil_col.md b/Curso-CPP/DSAStackQueueStu/pil_col.md new file mode 100644 index 0000000..9a9e977 --- /dev/null +++ b/Curso-CPP/DSAStackQueueStu/pil_col.md @@ -0,0 +1,123 @@ +# Proyecto: Gestión de Estudiantes con Pila y Cola 🎓 + +## 📌 Descripción +Este programa en C++ permite registrar estudiantes y almacenarlos en dos estructuras: +- **Pila (stack)** → último en entrar, primero en salir. +- **Cola (queue)** → primero en entrar, primero en salir. + +Cada estudiante tiene: +- Nombre y apellido +- Edad +- Nacionalidad +- Curso +- Promedio de tres notas (ponderado: 30%, 30%, 40%) + +El sistema determina si el estudiante aprueba (≥10) o reprueba. + +--- + +## ⚙️ Requisitos +- Lenguaje: C++ (versión 11 o superior recomendada) +- Librerías estándar: ``, `` + +--- + +## 🚀 Compilación y ejecución +```bash +g++ estudiantes.cpp -o estudiantes +./estudiantes +``` + +--- + +## 🧩 Estructura del código + +`struct per` +- `nom`: nombre +- `ap`: apellido +- `age`: edad +- `contry`: nacionalidad + +`struct estudiante` +- `pipol`: datos personales (per) +- `cur`: curso +- `prom`: promedio de notas + +`struct Cont` +- Nodo que almacena un estudiante +- Puntero foll al siguiente nodo (para pila o cola) + +**Funciones principales** +- `float prom(float, float, float)` + Calcula promedio ponderado de tres notas. +- `bool aprob(estudiante)` + Retorna true si el promedio ≥ 10. +- `sozoPhill(...) / hakaiPhill(...)` + Insertar y extraer estudiantes de la pila. +- `sozoCoil(...) / hakaiCoil(...)` + Insertar y extraer estudiantes de la cola. + +`int main()` +- Muestra menú principal: + 1. Ingresar y sacar pila + 2. Ingresar y sacar cola + 3. Salir +- Permite ingresar múltiples estudiantes. +- Al finalizar, muestra y vacía la pila o cola. + +--- + +## 🧪 Ejemplo de uso + +**Entrada (usuario)** + +``` +MENU +1. Ingresar y Sacar pila +2. Ingresar y Sacar cola +3. Salir +Seleccione opción: 1 +Ingrese nombre de la persona: +Juan +Ingrese apellido de la persona: +Pérez +Ingrese nacionalidad de la persona: +Venezolana +Ingrese edad de la persona: +20 +Ingrese el curso del estudiante: +Matemáticas +Ingrese la nota 1: +12 +Ingrese nota 2: +14 +Ingrese nota 3: +15 +El estudiante aprobo +¿Ingresar más datos? (s/n): +n +``` + +**Salida en consola** + +``` +Estudiantes en la pila: +Nombre y Apellido: Juan Pérez +Edad: 20 +Nacionalidad: Venezolana +Curso: Matemáticas +Promedio: 13.9 - Aprobado +``` + +--- + +## 📌 Notas +- Usar `std::stack` y `std::queue` en lugar de punteros manuales. +- Validar entradas (notas entre 0–20, edad positiva). +- Evitar system("cls") por ser dependiente de Windows. +- Modularizar en clases `(GestorEstudiantes)`. +- Estandarizar nombres de funciones y variables. +- Este programa es un buen ejemplo de estructuras dinámicas (pila y cola) implementadas manualmente. +- Demuestra el uso de punteros y memoria dinámica en C++. +- Puede servir como base para sistemas más grandes de gestión académica. +- En aplicaciones reales se recomienda usar estructuras estándar `(std::stack, std::queue)` y manejo de excepciones. diff --git a/Curso-CPP/DS_Stack_IO/tempila.cpp b/Curso-CPP/DS_Stack_IO/tempila.cpp new file mode 100644 index 0000000..80894d1 --- /dev/null +++ b/Curso-CPP/DS_Stack_IO/tempila.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +using namespace std; + +struct Tiempo { + int semanas, dias, horas; + long long segundos; +}; + +struct Persona { + string nom, curso; + int ci; +}; + +struct Nodo { + Persona per; + Tiempo tem; + Nodo* siguiente; +}; + +void calcularSegundos(Tiempo& t) { + int diasTotales = t.semanas * 7 + t.dias; + int horasTotales = diasTotales * 24 + t.horas; + t.segundos = static_cast(horasTotales) * 3600; +} + +void ingresarPila(Nodo*& pila, const Persona& p, const Tiempo& t) { + Nodo* nuevo = new Nodo(); + nuevo->per = p; + nuevo->tem = t; + nuevo->siguiente = pila; + pila = nuevo; + cout << "Estudiante agregado a la pila.\n"; +} + +bool sacarPila(Nodo*& pila, Persona& p, Tiempo& t) { + if (pila == nullptr) return false; + Nodo* aux = pila; + p = aux->per; + t = aux->tem; + pila = aux->siguiente; + delete aux; + return true; +} + +int main() { + Persona per; + Tiempo tem; + Nodo* pila = nullptr; + string nombreArchivo; + + cout << "Bienvenido\n"; + cout << "Ingrese su nombre: "; + getline(cin, per.nom); + cout << "Ingrese su cédula: "; + cin >> per.ci; + cout << "Ingrese la materia que desea cursar: "; + cin >> per.curso; + cout << "Ingrese cuántas semanas cursará: "; + cin >> tem.semanas; + while (tem.semanas < 0) { cout << "Valor inválido. Ingrese semanas >= 0: "; cin >> tem.semanas; } + cout << "Ingrese los días adicionales: "; + cin >> tem.dias; + while (tem.dias < 0) { cout << "Valor inválido. Ingrese días >= 0: "; cin >> tem.dias; } + cout << "Ingrese las horas adicionales: "; + cin >> tem.horas; + while (tem.horas < 0) { cout << "Valor inválido. Ingrese horas >= 0: "; cin >> tem.horas; } + + calcularSegundos(tem); + ingresarPila(pila, per, tem); + + cout << "Ingrese el nombre del archivo de salida (ej. curso.txt): "; + cin >> nombreArchivo; + ofstream archivo(nombreArchivo); + if (!archivo) { + cerr << "Error al abrir archivo.\n"; + return 1; + } + + cout << "\n--- Extrayendo datos de la pila ---\n"; + while (sacarPila(pila, per, tem)) { + cout << "Nombre: " << per.nom << endl; + cout << "Cédula: " << per.ci << endl; + cout << "Curso: " << per.curso << endl; + cout << "Semanas: " << tem.semanas << endl; + cout << "Días: " << tem.dias << endl; + cout << "Horas: " << tem.horas << endl; + cout << "Tiempo total en segundos: " << tem.segundos << endl; + cout << "-----------\n"; + + archivo << "Nombre: " << per.nom << endl; + archivo << "Cédula: " << per.ci << endl; + archivo << "Curso: " << per.curso << endl; + archivo << "Semanas: " << tem.semanas << endl; + archivo << "Días: " << tem.dias << endl; + archivo << "Horas: " << tem.horas << endl; + archivo << "Tiempo total en segundos: " << tem.segundos << endl; + archivo << "-----------\n"; + } + + archivo.close(); + cout << "Datos guardados en " << nombreArchivo << endl; + return 0; +} \ No newline at end of file diff --git a/Curso-CPP/DS_Stack_IO/tempila.md b/Curso-CPP/DS_Stack_IO/tempila.md new file mode 100644 index 0000000..f49361e --- /dev/null +++ b/Curso-CPP/DS_Stack_IO/tempila.md @@ -0,0 +1,62 @@ +# 📚 Gestor de Cursos con Pila Dinámica (Stack) en C++ + +![C++ Badge](https://img.shields.io/badge/Language-C++-blue.svg) +![Structure](https://img.shields.io/badge/Data_Structure-Stack_(LIFO)-purple.svg) + +Este proyecto implementa una estructura de datos de tipo **Pila (Stack)** utilizando punteros y memoria dinámica para gestionar registros de estudiantes y cálculos de tiempo. + +## ✨ Características Técnicas + +* **Estructura LIFO:** El último registro en entrar es el primero en procesarse y guardarse. +* **Memoria Dinámica:** Uso eficiente de RAM mediante asignación (`new`) y liberación (`delete`) manual. +* **Cálculo de Tiempo:** Algoritmo de conversión: `(Semanas + Días + Horas) -> Segundos`. +* **Persistencia:** Exportación de la pila procesada a archivos de texto. + +## 🚀 Instrucciones + +### Compilación +Necesitas un compilador C++ estándar. + +```bash +g++ main.cpp -o gestor_pila +```` + +### Ejecución + +```bash +./gestor_pila +``` + +## 🛠️ Estructura del Código + +### Definición del Nodo + +El contenedor principal de la información: + +```cpp +struct Nodo { + Persona per; // Datos del alumno + Tiempo tem; // Datos calculados + Nodo* siguiente; // Enlace al siguiente nodo +}; +``` + +### Lógica del Vaciado de Pila + +El programa extrae los datos siguiendo este ciclo hasta que la pila queda vacía: + +```mermaid +graph LR + A[Inicio Loop] --> B{¿Pila vacía?}; + B -- No --> C[Copiar Datos a Variables]; + C --> D[Mover Puntero al Siguiente]; + D --> E[Delete Nodo Actual]; + E --> F[Escribir en Archivo]; + F --> A; + B -- Sí --> G[Cerrar Archivo y Fin]; +``` + +## ⚠️ Notas sobre Entrada de Datos + + * **Cadenas:** El campo `curso` utiliza `cin >>`, por lo que no debe contener espacios (ej. usar `Programacion_1` en lugar de `Programacion 1`). + * **Validación:** Los tiempos (semanas, días, horas) deben ser números positivos. diff --git a/Curso-CPP/DataFly/If-matason-fly.cpp b/Curso-CPP/DataFly/If-matason-fly.cpp new file mode 100644 index 0000000..75c075f --- /dev/null +++ b/Curso-CPP/DataFly/If-matason-fly.cpp @@ -0,0 +1,76 @@ +#include +#include +using namespace std; + +struct Fly { + int code; // Código del vuelo + float km; // Kilómetros recorridos + int num; // Número de pasajeros +}; + +// Genera archivo con el recorrido del vuelo con más pasajeros +void reciboPasajeros(float km) { + string filename; + cout << "Ingrese el nombre para guardar el archivo del vuelo con más pasajeros: "; + cin >> filename; + filename += ".txt"; + + ofstream recibo(filename); + if (!recibo) { + cout << "* ERROR GENERANDO RECIBO *" << endl; + return; + } + recibo << "El vuelo con más pasajeros recorrió: " << km << " km" << endl; + cout << "Archivo generado: " << filename << endl; +} + +// Genera archivo con el código del vuelo con mayor recorrido +void reciboKilometros(int code) { + string filename; + cout << "Ingrese el nombre para guardar el archivo del vuelo con mayor recorrido: "; + cin >> filename; + filename += ".txt"; + + ofstream recibo(filename); + if (!recibo) { + cout << "* ERROR GENERANDO RECIBO *" << endl; + return; + } + recibo << "El vuelo con mayor recorrido tiene el código: " << code << endl; + cout << "Archivo generado: " << filename << endl; +} + +int main() { + const int N = 5; // número de vuelos + Fly vuelos[N]; + + // Entrada de datos + for (int i = 0; i < N; i++) { + cout << "Ingrese código del vuelo " << i+1 << ": "; + cin >> vuelos[i].code; + cout << "Ingrese número de pasajeros del vuelo " << i+1 << ": "; + cin >> vuelos[i].num; + cout << "Ingrese kilómetros del vuelo " << i+1 << ": "; + cin >> vuelos[i].km; + } + + // Buscar vuelo con mayor recorrido + int idxMaxKm = 0; + for (int i = 1; i < N; i++) { + if (vuelos[i].km > vuelos[idxMaxKm].km) { + idxMaxKm = i; + } + } + reciboKilometros(vuelos[idxMaxKm].code); + + // Buscar vuelo con mayor número de pasajeros + int idxMaxPasajeros = 0; + for (int i = 1; i < N; i++) { + if (vuelos[i].num > vuelos[idxMaxPasajeros].num) { + idxMaxPasajeros = i; + } + } + reciboPasajeros(vuelos[idxMaxPasajeros].km); + + return 0; +} \ No newline at end of file diff --git a/Curso-CPP/DataFly/If-matason-fly.md b/Curso-CPP/DataFly/If-matason-fly.md new file mode 100644 index 0000000..fb57e5d --- /dev/null +++ b/Curso-CPP/DataFly/If-matason-fly.md @@ -0,0 +1,74 @@ +# Proyecto: Registro de Vuelos ✈️ + +## 📌 Descripción +Este programa en C++ permite registrar información de varios vuelos (código, kilómetros recorridos y número de pasajeros). +Luego determina: +- El vuelo con **mayor recorrido en kilómetros**. +- El vuelo con **mayor número de pasajeros**. + +Genera archivos `.txt` con los resultados. + +--- + +## ⚙️ Requisitos +- Lenguaje: C++ (versión 11 o superior recomendada) +- Librerías estándar: ``, `` + +--- + +## 🚀 Compilación y ejecución +```bash +g++ vuelos.cpp -o vuelos +./vuelos +``` + +--- + +## 🧩 Estructura del código + +`struct Fly` +Estructura que almacena los datos de cada vuelo: +- code: código identificador del vuelo. +- km: kilómetros recorridos. +- num: número de pasajeros. + +`void reciboPasajeros(float km)` +- Genera un archivo .txt con el recorrido en kilómetros del vuelo que tuvo más pasajeros. +- Parámetro: km → kilómetros recorridos por ese vuelo. + +`void reciboKilometros(int code)` +- Genera un archivo .txt con el código del vuelo que recorrió más kilómetros. +- Parámetro: code → código del vuelo con mayor recorrido. + +`int main()` +- Solicita datos de 5 vuelos (código, pasajeros, kilómetros). +- Determina el vuelo con mayor recorrido en kilómetros. +- Determina el vuelo con mayor número de pasajeros. +- Llama a las funciones para generar los archivos de recibo. + +## 🧪 Ejemplo de uso + +**Entrada:** +``` +Ingrese código del vuelo 1: 101 +Ingrese número de pasajeros del vuelo 1: 120 +Ingrese kilómetros del vuelo 1: 500 +... +``` + +**Salida en consola:** +``` +Archivo generado: mayorRecorrido.txt +Archivo generado: masPasajeros.txt +``` + +**Archivos creados:** +- `mayorRecorrido.txt` → contiene el código del vuelo con mayor recorrido. +- `masPasajeros.txt` → contiene los kilómetros recorridos por el vuelo con más pasajeros. + +--- + +## 📌 Notas +- El programa actualmente procesa 5 vuelos. Puede ampliarse modificando la constante N. +- Los archivos generados se guardan en el mismo directorio donde se ejecuta el programa. +- Se recomienda validar entradas para evitar errores (ej. pasajeros negativos). diff --git a/Curso-CPP/If-matason-fly.cpp b/Curso-CPP/If-matason-fly.cpp deleted file mode 100644 index 4849b42..0000000 --- a/Curso-CPP/If-matason-fly.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -using namespace std; - -struct fly{ -int code; -float km; -int num; -}; - -void recibocode(float vuela){ - string filename; - string ext; - fstream recibo; - ext=".txt"; - cout << "Ingrese el nombre con el que desea guardar el archivo para el recibo del recorrido con mas pasajeros: " << endl; - cin >> filename; - filename+=ext; - recibo.open(filename.c_str(),ios::out); - if (recibo.fail()) { - cout << "* ERROR GENERARANDO RECIBO *" << endl; - } - recibo << "El vuelo con mayor pasajeros recorrío: " << vuela << endl; - cout << "Archivo Generado" << endl; - recibo.close(); -} - -void recibokm(int vuela){ - string filename; - fstream recibo; - string ext; - ext=".txt"; - cout << "Ingrese el nombre con el que desea guardar el archivo para el recibo del codigo con mayor recorrido: " << endl; - cin >> filename; - filename+=ext; - recibo.open(filename.c_str(),ios::out); - if (recibo.fail()) { - cout << "* ERROR GENERARANDO RECIBO *" << endl; - } - recibo << "El vuelo con mayor pasajeros recorrío: " << vuela << endl; - cout << "Archivo Generado" << endl; - recibo.close(); -} - -int main (){ - int i; - i=1; - fly vuelo [i]; - for(i=1; i<=5; i++){ - cout << "Ingrese codigo del vuelo " << i << endl; - cin>> vuelo[i].code; - cout << "Ingrese el numero de pasajeros del vuelo " << i <> vuelo[i].num; - cout << "Ingrese los kilometros que recorrera el vuelo " << i <> vuelo[i].km; - } - if (vuelo[1].km>vuelo[2].km){ - if (vuelo[1].km > vuelo[3].km){ - if (vuelo[1].km > vuelo[4].km){ - if (vuelo[1].km > vuelo[5].km){ - int vuela; - vuela=vuelo[1].code; - recibokm (vuela); - - } - } - } -}else; -if (vuelo[2].km>vuelo[3].km){ - if (vuelo[2].km > vuelo[4].km){ - if (vuelo[2].km > vuelo[5].km){ - int vuela; - vuela=vuelo[2].code; - recibokm (vuela); - } - } -}else; -if (vuelo[3].km>vuelo[4].km){ - if (vuelo[3].km > vuelo[5].km){ - int vuela; - vuela=vuelo[3].code; - recibokm (vuela); - } - }else; - if (vuelo[4].km>vuelo[5].km){ - int vuela; - vuela=vuelo[3].code; - recibokm (vuela); - }else; - int vuela; - vuela=vuelo[5].code; - recibokm (vuela); -if (vuelo[1].num>vuelo[2].num){ - if (vuelo[1].num > vuelo[3].num){ - if (vuelo[1].num > vuelo[4].num){ - if (vuelo[1].num > vuelo[5].num){ - int vuele; - vuele=vuelo[1].km; - recibocode (vuele); - - } - } - } -}else; -if (vuelo[2].num>vuelo[3].num){ - if (vuelo[2].num > vuelo[4].num){ - if (vuelo[2].num > vuelo[5].num){ - int vuele; - vuela=vuelo[2].num; - recibocode (vuele); - } - } -}else; -if (vuelo[3].num>vuelo[4].num){ - if (vuelo[3].num > vuelo[5].num){ - int vuele; - vuele=vuelo[3].km; - recibocode (vuele); - } - }else; - if (vuelo[4].num>vuelo[5].num){ - int vuele; - vuele=vuelo[4].km; - recibocode (vuela); - }else; - int vuele; - vuele=vuelo[5].km; - recibocode (vuele); - -return 0; -} diff --git "a/Curso-CPP/Jerarqu\303\255a_persona.cpp" "b/Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.cpp" similarity index 100% rename from "Curso-CPP/Jerarqu\303\255a_persona.cpp" rename to "Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.cpp" diff --git "a/Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.md" "b/Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.md" new file mode 100644 index 0000000..60a04e6 --- /dev/null +++ "b/Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.md" @@ -0,0 +1,117 @@ +# Proyecto: Sistema de Personas, Trabajadores y Estudiantes 👥 + +## 📌 Descripción +Este programa en C++ implementa un sistema orientado a objetos para representar diferentes tipos de personas: +- **Persona**: atributos básicos (nombre, apellido, cédula, edad). +- **Trabajador**: hereda de persona, añade puesto y salario. +- **Estudiante (stu)**: hereda de persona, añade curso, notas y promedio. +- **Universitario (uni)**: hereda de estudiante, añade carrera y título. + +El programa demuestra **herencia, polimorfismo y encapsulamiento** en C++. + +--- + +## ⚙️ Requisitos +- Lenguaje: C++ (versión 11 o superior recomendada) +- Librerías estándar: `` + +--- + +## 🚀 Compilación y ejecución +```bash +g++ personas.cpp -o personas +./personas +``` + +--- + +## 🧩 Estructura del código + +`class persona` +- **Atributos privados:** `nom, ap, ci, age`. +- **Métodos:** + - `ter_setpersona(...)`: asigna valores a los atributos. + - `tergetnom()`, `tergetap()`, `tergetci()`, `tergetage()`: devuelven los atributos. + - `show_pipol()`: imprime la información de la persona. + +`class trabajador : public persona` +- **Atributos privados:** `puesto, salario`. +- **Métodos:** + - `ter_setjob(...)`: asigna datos de persona + puesto y salario. + - `tergetpuesto(), tergetsalario()`: devuelven atributos. + - `show_job()`: imprime datos de persona y del trabajador. + +`class stu : public persona` +- **Atributos privados:** `curso, nota1, nota2, nota3, prom`. +- **Métodos:** + - `ter_setestu(...)`: asigna datos de persona + curso y notas. + - `set_promedio(...)`: calcula el promedio de las notas. + - `tergetcuerso(), tergetprom()`: devuelven atributos. + - `show_estu()`: imprime datos de persona y del estudiante. + +`class uni : public stu` +- **Atributos privados:** `carear, title`. +- **Métodos:** + - `ter_setuni(...)`: asigna datos de persona + estudiante + carrera y título. + - `tergetcarear(), tergettitle()`: devuelven atributos. + - `show_unity()`: imprime datos de persona, estudiante y universitario. + +`int main()` +- Crea objetos dinámicos de trabajador, stu y uni. +- Asigna valores con los métodos set. +- Muestra la información con los métodos show. +- Libera memoria con delete. + +--- + +## 🧪 Ejemplo de uso + +**Entrada (en código)** +```cpp +trabajador* emp = new trabajador(); +emp->ter_setjob("Mario", "Garcia", 4567233, 34, "Obrero", 130); + +stu* estudiante = new stu(); +estudiante->ter_setestu("Adol", "Cristin", 30665344, 18, "Castellano", 16, 13, 14); + +uni* estUni = new uni(); +estUni->ter_setuni("Juan", "Pérez", 12345678, 20, "Ingeniería", 18, 17, 19, "Ingeniería de Sistemas", "Licenciado"); +``` + +**Salida esperada** +``` +-Empleado- +Nombre: Mario +Apellido: Garcia +Cedula de identidad: 4567233 +Edad: 34 +Cargo: Obrero +Salario: 130Bs. + +-Estudiante- +Nombre: Adol +Apellido: Cristin +Cedula de identidad: 30665344 +Edad: 18 +Curso: Castellano +Notas: 16/13/14 +Promedio: 14.3333 + +-Estudiante universitario +Nombre: Juan +Apellido: Pérez +Cedula de identidad: 12345678 +Edad: 20 +Curso: Ingeniería +Notas: 18/17/19 +Promedio: 18 +Carreara: Ingeniería de Sistemas +Grado de formación: Licenciado +``` + +--- + +## 📌 Notas +- Se utiliza herencia múltiple en niveles: persona → stu → uni. +- Se demuestra polimorfismo con show_pipol() y sus extensiones en las clases derivadas. +- El uso de new y delete muestra gestión manual de memoria, aunque puede adaptarse a objetos automáticos. \ No newline at end of file diff --git a/Curso-CPP/POO.cpp b/Curso-CPP/POO.cpp deleted file mode 100644 index ad9a4d6..0000000 --- a/Curso-CPP/POO.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -using namespace std; - -//Un programa que haga login de acceso, ingrese a menú, que pueda hacer cambio y recuperación de clave y utilece 2 preguntas secretas, multiplique dos numeros y muestre un mensaje n cantidad de veces n=a*b - -class loger { - protected: - string usser, contra, as1, as2, r1, r2; - public: - loger(string, string, string, string, string, string); - string stich(string, string, string, string, string); -}; - -loger::loger (string c_usser, string c_contra, string c_as1, string c_as2, string c_r1, string c_r2){ - usser=c_usser; - contra= c_contra; - as1=c_as1; - as2=c_as2; - r1=c_r1; - r2=c_r2; -} - - -string stich(string as1,string as2, string r1, string r2, string contra){ - cout << "" << r1<> r1; - do { - cout << "Respuesta correcta!"<< endl; - cout << "" <> contra; - }while (r2==as2); - }while (r1==as1); -} -void multi (int a, int b){ - int i,n; - string sentence; - cout << "Ingrese un numero:"<>a; - cout << "Ingrese otro numero:"<> b; - n=a*b; - cout << "Ingrese una frase:"<> sentence; - i=1; - for(i=1; i<=n; i++) { - cout<<""<> c_r1; - cout << "Respuesta:"<> c_as1; - cout<<"Pregunta 2:"<> c_r2; - cout << "Respuesta:"<> c_as2; - loger("Fullo355", "346464", c_as1,c_as2,c_r1,c_r2); - cout<< "-MENU-"<>op; - switch(op) { - case 1: - stich (c_as1,c_as2,c_r1,c_r2,c_contra); - default: -} - return 0; -} diff --git a/Curso-CPP/Registro_arch.cpp b/Curso-CPP/Registro_arch.cpp deleted file mode 100644 index 412e9c5..0000000 --- a/Curso-CPP/Registro_arch.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -using namespace std; -int sem, dai, hours, h2, h3, dai2, dai3, seg, temp; -string nomapp; -int op; -struct Registro { - char nombre [45]; - char cur [45]; - int ci; -}; -int resolver( int sem, int hours, int dai); -int main() -{ - Registro log; - cout << "Bienvenido usuario" << endl; - cout << "Por favor ingresa tu nombre" << endl; - cin >> log.nombre; - cout << "Hola " << log.nombre<< ", ingresa tu número de cédula"<< endl; - cin >> log.ci; - cout << log.nombre<< ", ¿Que curso estás cursando?" << endl; - cin >> log.cur; - cout << "¿Cuántas semanas dura el curso?" << endl; - cin >> sem; - cout << "¿Cuántos días extra tiene además de las semanas?" << endl; - cin >> dai; - cout << "¿Cuántas horas extra tiene además de los días extra?" << endl; - cin >> hours; - cout << "Nombre para el archivo: " << endl; - cin >> nomapp; - cout << "Seleccione una extensión para el archivo" << endl; - cout << "1. Texto (.txt)" << endl; - cout << "2. Registro (.log)" << endl; - cout << "3. Documento (.doc)" << endl; - cin >> op; - ofstream file; - file.open (nomapp.c_str()); - if (!file){ - cout<< "Error abriendo el archivo\n"; - } - file << log.nombre<<"\n"; - file << log.ci<<"\n"; - file << log.cur<<"\n"; - file.close(); - - resolver (sem, hours, dai); - return 0; -} -int resolver( int sem, int hours, int dai) { - dai2=sem*7; - dai3=dai+dai2; - h2=dai2*24; - h3=h2+hours; - seg=h3*3600; - cin >> temp; - return temp; -} - diff --git a/Curso-CPP/teor_pil.cpp b/Curso-CPP/StackMathCpp/teor_pil.cpp similarity index 100% rename from Curso-CPP/teor_pil.cpp rename to Curso-CPP/StackMathCpp/teor_pil.cpp diff --git a/Curso-CPP/StackMathCpp/teor_pil.md b/Curso-CPP/StackMathCpp/teor_pil.md new file mode 100644 index 0000000..9c62698 --- /dev/null +++ b/Curso-CPP/StackMathCpp/teor_pil.md @@ -0,0 +1,116 @@ +# Proyecto: Pila de Personas con Factorial 👤 + +## 📌 Descripción +Este programa en C++ permite registrar personas y almacenarlas en una **pila**. +Cada persona tiene: +- Nombre y apellido +- Edad +- Altura +- Número asignado +- Factorial del número asignado + +El sistema: +- Calcula el factorial del número ingresado. +- Almacena los datos en una pila. +- Al vaciar la pila, muestra los datos en consola y los guarda en un archivo `.txt`. + +--- + +## ⚙️ Requisitos +- Lenguaje: C++ (versión 11 o superior recomendada) +- Librerías estándar: ``, ``, `` + +--- + +## 🚀 Compilación y ejecución +```bash +g++ personas.cpp -o personas +./personas +``` + +--- + +## 🧩 Estructura del código + +`struct per` +- `nom`: nombre +- `ap`: apellido +- `age`: edad +- `alt`: altura +- `numper`: número asignado +- `numfact`: factorial del número asignado + +`struct Cont` +- Nodo que almacena una persona +- Puntero foll al siguiente nodo (para pila) + +**Funciones principales** +- `int facto(int n)` + Calcula el factorial de un número. +- `sozoPhill(...)` + Inserta una persona en la pila. +- `hakaiPhill(...)` + Extrae personas de la pila, muestra datos y los guarda en archivo. + +`int main()` +- Solicita datos de varias personas. +- Calcula el factorial del número ingresado. +- Inserta personas en la pila. +- Al finalizar, pide nombre de archivo y guarda los datos. + +--- + +## 🧪 Ejemplo de uso + +**Entrada (usuario)** +``` +Ingrese nombre de la persona: +Juan +Ingrese apellido de la persona: +Pérez +Ingrese edad de la persona: +25 +Ingrese altura de la persona: +1.75 +Ingrese numero para la persona: +5 +Debe ingresar más datos? (s/n): +n +Ingrese el nombre con el que desea guardar el archivo: +personas +``` + +**Salida en consola** +``` +Nombre: Juan Pérez +Apellido: Pérez +Edad: 25 +Altura: 1.75 +Numero asignado: 5 +Numero factorial: 120 +¿Sacar siguiente dato? (s/n) +``` + +**Archivo generado (personas.txt)** +``` +Nombre: Juan +Apellido: Pérez +Edad: 25 +Altura: 1.75m +Numero asignado: 5 +Numero factorial: 120 +``` + +--- + +## 📌 Notas +- Usar std::stack en lugar de punteros manuales. +- Validar entradas (edad positiva, altura razonable, número factorial no excesivo). +- Usar long long para factorial y evitar overflow. +- Evitar system("cls") por ser dependiente de Windows. +- Guardar en modo ios::app para no sobrescribir archivos previos. +- Modularizar en clases para mayor claridad. +- Este programa es un buen ejemplo de estructuras dinámicas (pila) implementadas manualmente. +- Demuestra el uso de punteros y memoria dinámica en C++. +- Puede servir como base para sistemas más grandes de gestión de datos. +- En aplicaciones reales se recomienda usar estructuras estándar `(std::stack)` y manejo de excepciones. diff --git a/Curso-CPP/banco-cuenta.cpp b/Curso-CPP/StructBank/banco-cuenta.cpp similarity index 100% rename from Curso-CPP/banco-cuenta.cpp rename to Curso-CPP/StructBank/banco-cuenta.cpp diff --git a/Curso-CPP/StructBank/banco-cuenta.md b/Curso-CPP/StructBank/banco-cuenta.md new file mode 100644 index 0000000..d60368d --- /dev/null +++ b/Curso-CPP/StructBank/banco-cuenta.md @@ -0,0 +1,85 @@ +# Proyecto: Cuenta Bancaria 💳 + +## 📌 Descripción +Este programa en C++ implementa una clase `CuentaBancaria` que permite: +- Crear una cuenta con titular, número de cuenta y saldo inicial. +- Realizar depósitos. +- Realizar retiros con validación de fondos. +- Mostrar información de la cuenta. + +--- + +## ⚙️ Requisitos +- Lenguaje: C++ (versión 11 o superior recomendada) +- Librerías estándar: ``, `` + +--- + +## 🚀 Compilación y ejecución +```bash +g++ cuenta.cpp -o cuenta +./cuenta +``` + +--- + +## 🧩 Estructura del código + +`class CuentaBancaria` +- **Atributos privados:** + - `titular`: nombre del titular de la cuenta. + - `numeroCuenta`: número identificador de la cuenta. + - `saldo`: saldo actual. + +- **Constructor:** + - Inicializa titular, número de cuenta y saldo inicial. + +- **Métodos:** + - `depositar(double monto)`: añade dinero al saldo si el monto es válido. + - `retirar(double monto)`: descuenta dinero si hay fondos suficientes. + - `mostrarInformacionCuenta()`: imprime los datos de la cuenta. + +`int main()` +- Crea una cuenta bancaria. +- Muestra información inicial. +- Realiza operaciones de depósito y retiro. +- Muestra información final. + +--- + +## 🧪 Ejemplo de uso + +**Salida esperada** +``` +Información inicial de la cuenta: +Titular de la cuenta: Juan Pérez +Número de cuenta: 123456789 +Saldo: $1000 + +Realizando un depósito de $500... +Depósito exitoso. Nuevo saldo: $1500 + +Intentando retirar $2000... +Fondos insuficientes o monto inválido. + +Retirando $300... +Retiro exitoso. Nuevo saldo: $1200 + +Información final de la cuenta: +Titular de la cuenta: Juan Pérez +Número de cuenta: 123456789 +Saldo: $1200 +``` + +--- + +## 📌 Notas +- Añadir validación para saldo inicial (no permitir valores negativos). +- Usar iomanip para mostrar el saldo con dos decimales. +- Implementar métodos get y set para mayor encapsulación. +- Manejar múltiples cuentas en un vector o lista. +- Añadir autenticación o PIN para operaciones seguras. +- Este programa es un ejemplo básico de POO en C++. +- Demuestra encapsulación (atributos privados), constructores y métodos públicos. +- Puede servir como base para sistemas más complejos de gestión bancaria. +- En aplicaciones reales se requeriría manejo de errores más robusto, persistencia en base de datos y seguridad. \ No newline at end of file diff --git a/Curso-CPP/Tareas completa.cpp b/Curso-CPP/TaskFlow/Tareas completa.cpp similarity index 100% rename from Curso-CPP/Tareas completa.cpp rename to Curso-CPP/TaskFlow/Tareas completa.cpp diff --git a/Curso-CPP/TaskFlow/Tareas completa.md b/Curso-CPP/TaskFlow/Tareas completa.md new file mode 100644 index 0000000..09788dc --- /dev/null +++ b/Curso-CPP/TaskFlow/Tareas completa.md @@ -0,0 +1,97 @@ +# Proyecto: Gestor de Tareas 📝 + +## 📌 Descripción +Este programa en C++ implementa un gestor de tareas simple que clasifica las tareas en: +- **Urgentes** → almacenadas en una **pila** (stack). +- **Normales** → almacenadas en una **cola** (queue). + +Permite: +- Agregar tareas. +- Mostrar tareas. +- Guardar tareas en un archivo (`tareas.txt`). +- Cargar tareas desde el archivo. + +--- + +## ⚙️ Requisitos +- Lenguaje: C++ (versión 11 o superior recomendada) +- Librerías estándar: ``, ``, ``, ``, `` + +--- + +## 🚀 Compilación y ejecución +```bash +g++ tareas.cpp -o tareas +./tareas +``` + +--- + +## 🧩 Estructura del código + +`struct Tarea` +- descripcion: texto de la tarea. +- urgente: booleano (true = urgente, false = normal). + +**Funciones principales** +- `agregarTarea(stack&, queue&)` + Solicita descripción y urgencia, agrega la tarea a la pila o cola. + +- `mostrarTareas(const stack&, const queue&)` + Muestra todas las tareas urgentes y normales. + +- `guardarTareas(const stack&, const queue&)` + Guarda las tareas en tareas.txt (1 = urgente, 0 = normal). + +- `cargarTareas(stack&, queue&)` + Carga las tareas desde tareas.txt. + +`int main()` +- Inicializa pila y cola. +- Carga tareas desde archivo. +- Muestra menú interactivo: + 1. Agregar tarea + 2. Mostrar tareas + 3. Guardar tareas + 4. Salir + +--- + +## 🧪 Ejemplo de uso + +**Entrada (usuario)** +``` +1. Agregar tarea +Descripción de la tarea: Comprar pan +¿Es urgente? (1: Sí, 0: No): 0 +``` + +**Salida en consola** +``` +Tareas Normales: +- Comprar pan +``` + +Archivo generado (tareas.txt) +``` +0 Comprar pan +``` + +--- + +## 📌 Notas: +- Validar entrada de urgencia (solo 0 o 1). +- Usar `cin.ignore(numeric_limits::max(), '\n')`; para limpiar buffer. +- Añadir numeración al mostrar tareas. +- Implementar clase `GestorTareas` para encapsular lógica. +- Guardar en modo `ios::app` para no sobrescribir tareas previas. +- **Estructura de datos:** el uso de pila y cola es un buen ejemplo para diferenciar prioridades. La pila refleja urgencias inmediatas (última tarea agregada es la primera en atenderse), mientras que la cola refleja tareas normales en orden de llegada. +- **Persistencia:** actualmente el programa guarda en texto plano. Para proyectos más grandes, podría usarse un formato estructurado como JSON o CSV. +- **Escalabilidad:** el programa funciona bien para pocas tareas, pero si se manejan cientos, convendría optimizar la carga/guardado y añadir búsqueda o filtrado. +- **Interfaz de usuario:** la interacción es por consola. Una mejora futura sería implementar una interfaz gráfica o web. +- **Buenas prácticas:** encapsular en clases, usar `std::vector` para mayor flexibilidad, y aplicar manejo de excepciones `(try/catch)` para robustez. +- Extensiones posibles: + - Marcar tareas como completadas. + - Añadir fechas límite. + - Prioridades múltiples (no solo urgente/normal). + - Exportar reportes de tareas. diff --git a/Curso-CPP/Timekeeper Cpp/Registro_arch.cpp b/Curso-CPP/Timekeeper Cpp/Registro_arch.cpp new file mode 100644 index 0000000..43c366e --- /dev/null +++ b/Curso-CPP/Timekeeper Cpp/Registro_arch.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +using namespace std; + +struct Registro { + string nombre; + string curso; + int ci; +}; + +// Función que calcula duración total del curso +void resolver(int semanas, int diasExtra, int horasExtra) { + int diasTotales = semanas * 7 + diasExtra; + int horasTotales = diasTotales * 24 + horasExtra; + long long segundosTotales = static_cast(horasTotales) * 3600; + + cout << "\n--- Duración del curso ---\n"; + cout << "Días totales: " << diasTotales << endl; + cout << "Horas totales: " << horasTotales << endl; + cout << "Segundos totales: " << segundosTotales << endl; +} + +int main() { + Registro log; + int semanas, diasExtra, horasExtra; + string nombreArchivo; + int opcionExt; + + cout << "Bienvenido usuario\n"; + cout << "Por favor ingresa tu nombre: "; + getline(cin, log.nombre); + + cout << "Hola " << log.nombre << ", ingresa tu número de cédula: "; + cin >> log.ci; + cin.ignore(); + + cout << log.nombre << ", ¿Qué curso estás cursando?: "; + getline(cin, log.curso); + + cout << "¿Cuántas semanas dura el curso?: "; + cin >> semanas; + while (semanas < 0) { + cout << "Valor inválido. Ingrese semanas >= 0: "; + cin >> semanas; + } + + cout << "¿Cuántos días extra tiene además de las semanas?: "; + cin >> diasExtra; + while (diasExtra < 0) { + cout << "Valor inválido. Ingrese días >= 0: "; + cin >> diasExtra; + } + + cout << "¿Cuántas horas extra tiene además de los días extra?: "; + cin >> horasExtra; + while (horasExtra < 0) { + cout << "Valor inválido. Ingrese horas >= 0: "; + cin >> horasExtra; + } + + cout << "Nombre para el archivo: "; + cin >> nombreArchivo; + + cout << "Seleccione una extensión para el archivo:\n"; + cout << "1. Texto (.txt)\n"; + cout << "2. Registro (.log)\n"; + cout << "3. Documento (.doc)\n"; + cin >> opcionExt; + + string extension; + switch (opcionExt) { + case 1: extension = ".txt"; break; + case 2: extension = ".log"; break; + case 3: extension = ".doc"; break; + default: extension = ".txt"; cout << "Opción inválida, se usará .txt\n"; break; + } + + string archivoFinal = nombreArchivo + extension; + ofstream file(archivoFinal); + if (!file) { + cerr << "Error abriendo el archivo\n"; + return 1; + } + + file << "Nombre: " << log.nombre << "\n"; + file << "Cédula: " << log.ci << "\n"; + file << "Curso: " << log.curso << "\n"; + file.close(); + + cout << "Datos guardados en " << archivoFinal << endl; + + resolver(semanas, diasExtra, horasExtra); + + return 0; +} \ No newline at end of file diff --git a/Curso-CPP/Timekeeper Cpp/Registro_arch.md b/Curso-CPP/Timekeeper Cpp/Registro_arch.md new file mode 100644 index 0000000..bce3d3c --- /dev/null +++ b/Curso-CPP/Timekeeper Cpp/Registro_arch.md @@ -0,0 +1,64 @@ +# 🗂️ Sistema de Registro y Calculadora Temporal en C++ + +![C++ Badge](https://img.shields.io/badge/Language-C++-blue.svg) +![IO](https://img.shields.io/badge/Feature-File_Handling-orange.svg) + +Herramienta de consola para registrar estudiantes en cursos, generar archivos de respaldo y calcular la duración total del aprendizaje en segundos exactos. + +## ✨ Características + +* **Persistencia de Datos:** Genera archivos automáticamente con extensiones personalizables (`.txt`, `.log`, `.doc`). +* **Validación de Entrada:** Protege contra valores de tiempo negativos. +* **Precisión Matemática:** Conversión precisa de semanas a segundos utilizando tipos de datos extendidos (`long long`). +* **Estructuras:** Uso de `struct` para modelado de datos. + +## 🚀 Instrucciones + +### Compilación +```bash +g++ main.cpp -o registro_curso +```` + +### Ejecución + +```bash +./registro_curso +``` + +## 🛠️ Funcionamiento Interno + +### Flujo de Guardado de Archivos + +El sistema construye el nombre del archivo dinámicamente: + +```mermaid +graph LR + A[Input: Nombre Archivo] --> B{Selector Extensión}; + B -- 1 --> C[.txt]; + B -- 2 --> D[.log]; + B -- 3 --> E[.doc]; + C --> F[Crear Archivo]; + D --> F; + E --> F; + F --> G[Escribir Datos del Struct]; +``` + +### Lógica de Conversión + +La función `resolver()` transforma el tiempo humano (Semanas/Días) a tiempo de máquina (Segundos): + +| Input | Proceso | Output | +| :--- | :--- | :--- | +| Semanas | `* 7 + dias` | Días Totales | +| Días Totales | `* 24 + horas` | Horas Totales | +| Horas Totales | `* 3600` | **Segundos Totales** | + +## 📄 Ejemplo de Salida (Archivo Generado) + +Si el usuario elige la opción 1, se crea `archivo.txt` con este contenido: + +```text +Nombre: Juan Perez +Cédula: 12345678 +Curso: Programación Avanzada +``` \ No newline at end of file diff --git a/Curso-CPP/codelab.cpp b/Curso-CPP/codelab.cpp deleted file mode 100644 index d317533..0000000 --- a/Curso-CPP/codelab.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -using namespace std; - -struct persona{ - string nom, ape, nacionalidad; - int ci, edad; -}; -struct estudiante{ - string curso; - float nota,nota2,nota3; - float promedio; - persona per; - -}; -struct nodo{ - nodo *siguiente; - estudiante estu; -}; - -void notafinal (estudiante &estu); -void ingresarpila(nodo *&pila, estudiante estu); //Para crear pila: puntero y direccin de memoria + el dato para rellenar -void sacarpila (nodo *&pila, estudiante &estu); - -int main(){ - nodo *pila=NULL; - estudiante estu; - int op; - string rpt; - - do{ - cout << "Bienvenido" << endl; - cout << "Seleccione una opcion:" << endl; - cout << "1. Ingresar datos del estudiante a pila" << endl; - cout << "2. Sacar datos de los estudiantes ingresados a pila" << endl; - cout << "3. Ingresar datos del estudiante a cola" << endl; - cout << "4. Sacar datos de los estudiantes ingresados a cola" << endl; - cout << "5. Salir" << endl; - cin >> op; - system ("cls"); - switch (op){ - case 1: - do { - cout << "Ingrese el nombre del estudiante:" << endl; - cin >> estu.per.nom; - cout << "Ingrese el apellido del estudiante:" << endl; - cin >> estu.per.ape; - cout << "Ingrese la cedula del estudiante:" << endl; - cin >> estu.per.ci; - cout << "Ingrese la edad del estudiante:" << endl; - cin >> estu.per.edad; - cout << "Ingrese la nacionalidad del estudiante:" << endl; - cin >> estu.per.nacionalidad; - cout << "Ingrese el curso del estudiante:" << endl; - cin >> estu.curso; - cout << "Ingrese la primer nota:" << endl; - cin >> estu.nota; - cout << "Ingrese la segunda nota:" << endl; - cin >> estu.nota2; - cout << "Ingrese la tercer nota:" << endl; - cin >> estu.nota3; - system ("cls"); - ingresarpila (pila, estu); - cout << "Desea agregar mas alumnos? (s/n)" << endl; - cin >> rpt; - }while (rpt == "s"); - case 2: - while (pila != NULL){ - cout << "Nombre del estudiante: " << estu.per.nom << endl; - cout << "Apellido del estudiante: " << estu.per.ape << endl; - cout << "Cedula del estudiante: " << estu.per.ci << endl; - cout << "Edad del estudiante: " << estu.per.edad << endl; - cout << "Nacionalidad del estudiante: " << estu.per.nacionalidad << endl; - cout << "Curso:" << estu.curso << endl; - notafinal (estu); - cout << "Nota final: " << estu.promedio << endl; - sacarpila (pila, estu); - } - cout << "Desea continuar? (s/n)" << endl; - cin >> rpt; - } - }while (rpt == "s"); - - return 0; -} -void notafinal(estudiante &estu){ - estu.promedio = (estu.nota*0.3)+(estu.nota2*0.3)+(estu.nota3*0.4); - if (estu.promedio>=10){ - cout << "Aprobo" << endl; - } - else { - cout << "Reprobo" << endl; - } -} -void ingresarpila (nodo *&pila, estudiante estupila){//Le agregue "&" a "nodo *pila" - nodo *nuevo_nodo = new nodo(); - nuevo_nodo->estu = estupila; - nuevo_nodo->siguiente = pila;//Nuevo nodo apuntando a siguiente debe ser igual a pila, bachiller - pila = nuevo_nodo; - cout << "Ingresando datos a la pila...." << endl; -} -void sacarpila (nodo *&pila, estudiante &estupila){ - nodo *aux = pila; - estupila = aux->estu; - pila = aux->siguiente; - delete aux; -} diff --git a/Curso-CPP/tempila.cpp b/Curso-CPP/tempila.cpp deleted file mode 100644 index 0891e5a..0000000 --- a/Curso-CPP/tempila.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -using namespace std; - - -struct tiempo{ - int semanas, dias, horas, minutos, segundos; -}; - -struct persona{ - string nom,curso; - int ci; -}; - -struct nodo{ - persona per; - tiempo tem; - nodo *siguiente; -}; - -void seg(tiempo &tempila); -void ingresarpila (nodo *&, persona perpila, tiempo tempila); -void sacarpila(nodo *&, persona &perpila, tiempo &tempila); - - -int main(){ - persona per; - tiempo tem; - nodo *pila = NULL; - cout << "Bienvenido" << endl; - cout << "Ingrese su nombre:" << endl; - getline (cin,per.nom); - cout << "Ingrese su cedula:" << endl; - cin >> per.ci; - cout << "Ingrese la materia que desea cursar:" << endl; - cin >> per.curso; - cout << "Ingrese cuantas semanas cursara:" << endl; - cin>> tem.semanas; - cout << "Ingrese los dias que vera el curso:" << endl; - cin>> tem.dias; - cout << "Ingrese las horas que vera el curso:" << endl; - cin >> tem.horas; - seg (tem); - ingresarpila(pila, per, tem); - - while (pila != NULL){ - sacarpila(pila, per, tem); - if (pila != NULL){ - cout << "Horas: " << tem.horas << endl; - cout << "Dias: " << tem.dias << endl; - cout << "Semanas: " << tem.semanas << endl; - cout << "Curso: " << per.curso << endl; - cout << "Cedula: " << per.ci << endl; - cout << "Nombre: " << per.nom << endl; - } - else { - cout << "Ahora se mostraran los datos ingresados: " << endl; - cout << "Nombre: " << per.nom << endl; - cout << "Cedula: " << per.ci << endl; - cout << "Curso: " << per.curso << endl; - cout << "Semanas: " << tem.semanas << endl; - cout << "Dias: " << tem.dias << endl; - cout << "Horas: " << tem.horas << endl; - cout << "Tiempo total en segundos: " << tem.segundos << endl; - } - } - - return 0; -} - -void seg (tiempo &tempila){ - int dias2,horas2; - dias2 = tempila.semanas*7; - dias2 = tempila.dias+dias2; - horas2=dias2*24; - horas2=tempila.horas+horas2; - tempila.segundos = horas2*3600; -} - -void ingresarpila (nodo *&pila, persona perpila, tiempo tempila){ - nodo *nuevo_nodo = new nodo(); - nuevo_nodo->per = perpila; - nuevo_nodo->tem = tempila; - nuevo_nodo->siguiente = pila; - pila = nuevo_nodo; - cout << "Ingresando datos a la pila..." << endl; - string o; - fstream ingresarpila; - cout << "Ingrese el nombre y la extension con la que guardara el archvio" << endl; - cin >> o; - ingresarpila.open(o.c_str(),ios::out); - if (ingresarpila.fail()){ - cout << "Error" << endl; - } - else { - ingresarpila << "Tiempo total en segundos: " << tempila.segundos << endl; - ingresarpila << "Horas: " << tempila.horas << endl; - ingresarpila << "Dias: " << tempila.dias << endl; - ingresarpila << "Semanas: " << tempila.semanas << endl; - ingresarpila << "Curso: " << perpila.curso << endl; - ingresarpila << "Cedula: " << perpila.ci << endl; - ingresarpila << "Nombre: " << perpila.nom << endl; - } - ingresarpila.close(); -} -void sacarpila (nodo *&pila, persona &perpila, tiempo &tempila){ - nodo *aux = pila; - perpila = aux->per; - tempila = aux->tem; - pila = aux->siguiente; - delete aux; - string o; - fstream sacarpila; - cout << "Ingrese el nombre y la extension con la que guardara el archvio" << endl; - cin >> o; - sacarpila.open(o.c_str(),ios::out); - if (sacarpila.fail()){ - cout << "Error" << endl; - } - else { - sacarpila << "Nombre: " << perpila.nom << endl; - sacarpila << "Cedula: " << perpila.ci << endl; - sacarpila << "Curso: " << perpila.curso << endl; - sacarpila << "Horas: " << tempila.horas << endl; - sacarpila << "Dias: " << tempila.dias << endl; - sacarpila << "Semanas: " << tempila.semanas << endl; - sacarpila << "Tiempo total en segundos: " << tempila.segundos << endl; - } - sacarpila.close(); -} - diff --git a/Ejercicio 1/Listar Archivos en Drive por fecha.py b/Ejercicio 1/Listar Archivos en Drive por fecha.py deleted file mode 100644 index 01e052b..0000000 --- a/Ejercicio 1/Listar Archivos en Drive por fecha.py +++ /dev/null @@ -1,38 +0,0 @@ -import os -from google.colab import drive -import time - -# Montar Google Drive -drive.mount('/content/drive') - -# Definir el directorio -DIRUTH= '/content/drive/My Drive/' - -def list_arch(directory): - try: - files = os.listdir(directory) - files_with_dates = [] - - for file in files: - file_path = os.path.join(directory, file) - if os.path.isfile(file_path): - # Obtener la fecha de modificación - modi_time = os.path.getmtime(file_path) - file_dates.append((file, modi_time)) - - # Ordenar archivos por fecha de modificación (más reciente primero) - file_dates.sort(key=lambda x: x[1], reverse=True) - - print("Archivos en el directorio ordenados por fecha de subida:") - for file, modi_time in file_dates: - print(f"{file} - {time.ctime(modi_time)}") - - except FileNotFoundError: - print("Directorio no encontrado.") - except PermissionError: - print("No tienes permiso para acceder a este directorio.") - except Exception as e: - print(f"Ocurrió un error: {e}") - -# Llamar a la función -list_arch(DIRUTH) diff --git a/Ejercicio 1/README.md b/Ejercicio 1/README.md deleted file mode 100644 index b3e017e..0000000 --- a/Ejercicio 1/README.md +++ /dev/null @@ -1 +0,0 @@ -Enlace para ejecutar el código en Colab: https://colab.research.google.com/drive/11fyVzhjgdmzw0ANOTUvON102tRKuWkxN#scrollTo=DY92pSQB9Wnj diff --git a/Flask/app.py b/Flask/app.py deleted file mode 100644 index 352870e..0000000 --- a/Flask/app.py +++ /dev/null @@ -1,509 +0,0 @@ -from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, send_file -import os -import pandas as pd -from fpdf import FPDF -import datetime -import time -import random -import string -import csv -import io # Necesario para send_file con CSV generado en memoria - -# Configuración básica de Flask -app = Flask(__name__) -app.secret_key = "secret-key" # ¡Cambia esto por una clave secreta segura en producción! -UPLOAD_FOLDER = "uploads" -# Crear carpeta uploads si no existe -if not os.path.exists(UPLOAD_FOLDER): - os.makedirs(UPLOAD_FOLDER) -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER - -# Función para generar código alfanumérico aleatorio -def generar_codigo_aleatorio(longitud=6): - caracteres = string.ascii_uppercase + string.digits - return ''.join(random.choices(caracteres, k=longitud)) - -# Función para limpiar archivos antiguos en la carpeta uploads -def limpiar_archivos_antiguos(): - ahora = time.time() - un_dia_en_segundos = 24 * 3600 - archivos_eliminados = 0 - print("Ejecutando limpieza de archivos antiguos...") - - if os.path.exists(app.config['UPLOAD_FOLDER']): - for nombre_archivo in os.listdir(app.config['UPLOAD_FOLDER']): - ruta_archivo = os.path.join(app.config['UPLOAD_FOLDER'], nombre_archivo) - try: - if os.path.isfile(ruta_archivo): - timestamp_creacion = os.path.getctime(ruta_archivo) # Usar getctime o getmtime según preferencia - if (ahora - timestamp_creacion) > un_dia_en_segundos: - os.remove(ruta_archivo) - print(f"Archivo antiguo eliminado: {ruta_archivo}") - archivos_eliminados += 1 - except Exception as e: - print(f"Error al procesar o eliminar {ruta_archivo}: {e}") - - if archivos_eliminados > 0: - print(f"Se eliminaron {archivos_eliminados} archivos antiguos de la carpeta uploads.") - else: - print("No se encontraron archivos antiguos para eliminar en la carpeta uploads.") - -# Ejecutar la limpieza al inicio de la aplicación (en contexto) -with app.app_context(): - limpiar_archivos_antiguos() - -# ========================================================================== -# RUTAS DE LA APLICACIÓN -# ========================================================================== - -# Ruta para crear inventario manualmente -@app.route("/crear_inventario", methods=["GET", "POST"]) -def crear_inventario(): - if request.method == "POST": - # Manejar petición JSON enviada por fetch desde el frontend - if request.is_json: - data = request.get_json() - inventario_para_guardar = data.get('inventario') - if data.get('guardar_csv') and inventario_para_guardar: - # Generar nombre de archivo único - nombre_archivo_creado = f"inventario_creado_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" - ruta_archivo_creado = os.path.join(app.config['UPLOAD_FOLDER'], nombre_archivo_creado) - try: - # Validar estructura básica (asegurar que es lista de diccionarios) - if not isinstance(inventario_para_guardar, list) or not all(isinstance(item, dict) for item in inventario_para_guardar): - raise ValueError("Formato de inventario inválido.") - - # Obtener encabezados del primer producto (si existe) - fieldnames = inventario_para_guardar[0].keys() if inventario_para_guardar else [] - - # Escribir el archivo CSV - with open(ruta_archivo_creado, 'w', newline='', encoding='utf-8') as csvfile: - writer = csv.DictWriter(csvfile, fieldnames=fieldnames) - writer.writeheader() - writer.writerows(inventario_para_guardar) - - # Cargar datos en la sesión como si se hubiera subido el archivo - session['csv_filepath'] = ruta_archivo_creado - df = pd.read_csv(ruta_archivo_creado) - # Asegurarse que las columnas esperadas existan, manejar NaNs si es necesario - df.fillna('', inplace=True) # Rellenar NaNs para evitar problemas con JSON - session['productos'] = df.to_dict(orient="records") - - # Limpiar el inventario temporal de la sesión si lo estabas usando - session.pop('inventario_creado', None) - - # Devolver respuesta JSON de éxito para fetch - return jsonify({"message": f"Inventario creado y guardado como {nombre_archivo_creado}."}), 200 - - except KeyError as e: - print(f"Error de clave al procesar inventario: {e}") - return jsonify({"message": f"Error en los datos del inventario: falta la clave {e}"}), 400 - except ValueError as e: - print(f"Error de valor al procesar inventario: {e}") - return jsonify({"message": f"Error en los datos del inventario: {e}"}), 400 - except Exception as e: - print(f"Error al guardar el archivo CSV: {e}") - # Devolver respuesta JSON de error para fetch - return jsonify({"message": f"Error interno al guardar el archivo CSV: {e}"}), 500 - else: - # Devolver respuesta JSON de error para fetch - return jsonify({"message": "Datos inválidos o acción no reconocida."}), 400 - else: - # Manejar envío de formulario tradicional (si aún se permite) - # Probablemente ya no necesario para esta acción específica si JS usa fetch - flash("Método no soportado, use la interfaz interactiva.", "error") - return redirect(url_for('crear_inventario')) - - # Método GET: Mostrar la página - # Pasar inventario temporal si se guarda en sesión (opcional si todo es JS) - inventario_temporal = session.get('inventario_creado', []) - return render_template("crear_inventario.html", inventario_creado=inventario_temporal) - - -# Ruta de inicio (para subir CSV y seleccionar tipo de pedido) -@app.route("/", methods=["POST", "GET"]) -def upload_csv(): - if request.method == "POST": - # Verificar si se envió un archivo - if 'file' not in request.files: - flash("No se encontró el archivo en la petición.") - # Para fetch, devolver JSON - if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html: - return jsonify({"message": "No se encontró el archivo."}), 400 - return redirect(request.url) - - file = request.files["file"] - - # Verificar si se seleccionó un archivo - if file.filename == '': - flash("No se seleccionó ningún archivo.") - if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html: - return jsonify({"message": "No se seleccionó ningún archivo."}), 400 - return redirect(request.url) - - # Verificar extensión y tipo MIME (más seguro) - if file and file.filename.lower().endswith(".csv") and file.content_type == 'text/csv': - try: - filename = file.filename # Considerar usar werkzeug.utils.secure_filename - filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename) - file.save(filepath) - - # Leer y procesar el CSV - data = pd.read_csv(filepath) - # Validación básica de columnas (ajustar según necesidad) - columnas_esperadas = ['Código', 'Nombre', 'Cantidad', 'Demanda', 'Tiempo entrega(Días)', 'Fecha límite'] - if not all(col in data.columns for col in columnas_esperadas): - os.remove(filepath) # Eliminar archivo inválido - raise ValueError(f"El CSV debe contener las columnas: {', '.join(columnas_esperadas)}") - - data.fillna('', inplace=True) # Manejar NaNs - json_data = data.to_dict(orient="records") - - # Guardar en sesión - session['productos'] = json_data - session['csv_filepath'] = filepath - print(f"Archivo CSV cargado: {filepath}") - - # Respuesta para fetch - if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html: - return jsonify({"message": "Archivo CSV cargado con éxito."}), 200 - # Respuesta para envío tradicional - flash("Archivo CSV de inventario cargado con éxito.") - return redirect(url_for("upload_csv")) - - except pd.errors.EmptyDataError: - message = "El archivo CSV está vacío." - if os.path.exists(filepath): os.remove(filepath) - except pd.errors.ParserError: - message = "Error al procesar el archivo CSV. Verifique el formato." - if os.path.exists(filepath): os.remove(filepath) - except ValueError as e: - message = str(e) - except Exception as e: - message = f"Ocurrió un error inesperado: {e}" - if os.path.exists(filepath): os.remove(filepath) # Intentar eliminar - - print(f"Error al cargar CSV: {message}") - if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html: - return jsonify({"message": message}), 400 - flash(message) - return redirect(request.url) - - else: - message = "Por favor, sube un archivo CSV válido (.csv)." - if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html: - return jsonify({"message": message}), 400 - flash(message) - return redirect(request.url) - - # Método GET: Mostrar la página - return render_template("subir_csv.html") - - -# Ruta para pedir stock interno -@app.route("/pedir_stock", methods=["GET", "POST"]) -def pedir_stock(): - # Asegurarse de que el inventario esté cargado - productos = session.get('productos') - if not productos: - flash("No hay inventario cargado. Sube o crea un archivo CSV.", "warning") - return redirect(url_for("upload_csv")) - - if request.method == "POST": - try: - pedido_stock = {} - # Extraer datos del formulario (enviado por fetch o tradicional) - for key, value in request.form.items(): - if key.startswith("pedido_stock[") and key.endswith("]"): - # Extraer código limpiamente - codigo_limpio = key[len("pedido_stock["):-1] - try: - cantidad_pedida = int(value) - if cantidad_pedida > 0: - # Validar que el código exista en productos (más seguro) - if any(str(p['Código']) == codigo_limpio for p in productos): - pedido_stock[codigo_limpio] = cantidad_pedida - else: - print(f"Advertencia: Código de producto no encontrado en pedido_stock: {codigo_limpio}") - elif cantidad_pedida < 0: - # Devolver error si se envía cantidad negativa - return jsonify({"message": f"La cantidad para {codigo_limpio} no puede ser negativa."}), 400 - except ValueError: - # Ignorar valores no numéricos o devolver error - print(f"Advertencia: Valor no numérico para {codigo_limpio}: {value}") - # return jsonify({"message": f"Valor inválido para {codigo_limpio}."}), 400 - - if not pedido_stock: - # Devolver error si no se pidió nada válido - return jsonify({"message": "No se especificó ninguna cantidad válida para el pedido."}), 400 - - productos_actualizados = [] - resultados_pedido_msg = [] # Mensajes para la respuesta - hubo_cambios = False - - for producto in productos: - codigo_str = str(producto['Código']) - if codigo_str in pedido_stock: - cantidad_pedida = pedido_stock[codigo_str] - stock_actual = int(producto.get('Cantidad', 0)) # Manejar posible ausencia o no número - - if cantidad_pedida <= stock_actual: - producto['Cantidad'] = stock_actual - cantidad_pedida - demanda_actual = int(producto.get('Demanda', 0)) - - # Aproximación: ajustar la demanda - factor_aprendizaje = 0.3 - nueva_demanda = demanda_actual + (cantidad_pedida - demanda_actual) * factor_aprendizaje - producto['Demanda'] = min(round(nueva_demanda), 10) # Limitar a 10 - - resultados_pedido_msg.append(f"Pedido de {cantidad_pedida} {producto['Nombre']} OK. Nuevo stock: {producto['Cantidad']}, Nueva demanda: {producto['Demanda']}") - hubo_cambios = True - else: - resultados_pedido_msg.append(f"Stock insuficiente de {producto['Nombre']} (disp: {stock_actual}) para pedir {cantidad_pedida}.") - productos_actualizados.append(producto) # Añadir siempre, modificado o no - - if hubo_cambios: - # Actualizar sesión y archivo CSV - session['productos'] = productos_actualizados - csv_filepath = session.get('csv_filepath') - if csv_filepath and os.path.exists(csv_filepath): - df = pd.DataFrame(productos_actualizados) - df.to_csv(csv_filepath, index=False, encoding='utf-8') - else: - print("Advertencia: No se encontró el archivo CSV para actualizar.") - # Considerar devolver un error o advertencia al cliente - - # Devolver respuesta JSON para fetch - return jsonify({ - "message": "Proceso de pedido de stock completado.", - "detalles": resultados_pedido_msg, - "hubo_cambios": hubo_cambios - # Opcional: podrías devolver 'productos_actualizados' si el frontend lo necesita - }), 200 - - except Exception as e: - print(f"Error en /pedir_stock: {e}") - return jsonify({"message": f"Error interno al procesar el pedido de stock: {e}"}), 500 - - # Método GET: Mostrar la página - return render_template("pedir_stock.html", productos_stock=productos) - - -# Ruta para pedir al proveedor -@app.route("/pedir_proveedor", methods=["GET", "POST"]) -def pedir_proveedor(): - productos = session.get('productos') - if not productos: - flash("No hay inventario cargado. Sube o crea un archivo CSV.", "warning") - return redirect(url_for("upload_csv")) - - if request.method == "POST": - try: - pedido_proveedor = [] - errores_validacion = [] - item_count = 0 - - # Crear un diccionario para buscar productos por código más eficientemente - productos_dict = {str(p['Código']): p for p in productos} - - # Procesar datos del formulario - codigos_pedidos = set() - for key, value in request.form.items(): - if key.startswith("pedido_proveedor[") and key.endswith("]"): - codigo_limpio = key[len("pedido_proveedor["):-1] - codigos_pedidos.add(codigo_limpio) - - for codigo_limpio in codigos_pedidos: - cantidad_str = request.form.get(f"pedido_proveedor[{codigo_limpio}]") - precio_str = request.form.get(f"precio_proveedor[{codigo_limpio}]") - - cantidad_pedida = 0 - precio = 0.0 - error_en_item = False - - # Validar cantidad - try: - cantidad_pedida = int(cantidad_str) if cantidad_str else 0 - if cantidad_pedida < 0: - errores_validacion.append(f"Producto {codigo_limpio}: Cantidad no puede ser negativa.") - error_en_item = True - except (ValueError, TypeError): - errores_validacion.append(f"Producto {codigo_limpio}: Cantidad inválida.") - error_en_item = True - - # Validar precio solo si la cantidad es > 0 - if cantidad_pedida > 0: - item_count += 1 - try: - precio = float(precio_str) if precio_str else 0.0 - if precio <= 0: - errores_validacion.append(f"Producto {codigo_limpio}: Precio debe ser mayor a 0.") - error_en_item = True - except (ValueError, TypeError): - errores_validacion.append(f"Producto {codigo_limpio}: Precio inválido.") - error_en_item = True - - # Si no hay errores y cantidad > 0, añadir al pedido - if not error_en_item and cantidad_pedida > 0: - producto_info = productos_dict.get(codigo_limpio) - if producto_info: - pedido_proveedor.append({ - "codigo": producto_info['Código'], # Usar el tipo original si es necesario - "nombre": producto_info['Nombre'], - "cantidad": cantidad_pedida, - "precio_unitario": precio, - # Obtener datos adicionales de forma segura - "tiempo_entrega": producto_info.get('Tiempo entrega(Días)', 'N/A'), - "stock_actual": producto_info.get('Cantidad', 'N/A') - }) - else: - errores_validacion.append(f"Producto con código {codigo_limpio} no encontrado en inventario.") - - - if errores_validacion: - return jsonify({"message": "Errores en el formulario.", "errors": errores_validacion}), 400 - - if item_count == 0: - return jsonify({"message": "No se especificó ningún producto válido para el pedido."}), 400 - - # Guardar el pedido en la sesión para la generación del PDF - session['pedido_proveedor'] = pedido_proveedor - - # Devolver éxito para que JS redirija a la generación del PDF - return jsonify({"message": "Pedido listo para generar PDF.", "redirect_url": url_for('generar_pdf_proveedor')}), 200 - - except Exception as e: - print(f"Error en /pedir_proveedor: {e}") - return jsonify({"message": f"Error interno al procesar el pedido al proveedor: {e}"}), 500 - - # Método GET: Mostrar la página - return render_template("pedir_proveedor.html", productos_proveedor=productos) - - -# Ruta para generar PDF de pedido al proveedor -@app.route("/generar_pdf_proveedor", methods=["GET"]) -def generar_pdf_proveedor(): - pedido_proveedor = session.get('pedido_proveedor') - if not pedido_proveedor: - flash("No hay ningún pedido pendiente para generar el PDF.", "warning") - return redirect(url_for("pedir_proveedor")) # Redirigir a la página de pedido - - try: - pdf = FPDF() - pdf.add_page() - pdf.set_font("Arial", size=10) # Ajustar tamaño para más contenido - - fecha_hora = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') - pdf.set_font("Arial", 'B', size=12) - pdf.cell(0, 10, f"Pedido al Proveedor - Touken Café", ln=True, align="C") - pdf.set_font("Arial", size=9) - pdf.cell(0, 5, f"Fecha de Generación: {fecha_hora}", ln=True, align="C") - pdf.ln(8) - - # Encabezados de la tabla - pdf.set_font("Arial", 'B', size=10) - col_widths = [25, 75, 25, 30, 35] # Ajustar anchos de columna - headers = ["Código", "Producto", "Cantidad", "Precio Unit.", "Precio Total"] - for i, header in enumerate(headers): - pdf.cell(col_widths[i], 10, header, border=1, align="C") - pdf.ln() - - # Contenido de la tabla - pdf.set_font("Arial", size=9) - total_pedido = 0 - for item in pedido_proveedor: - if item['cantidad'] > 0: # Doble verificación - precio_total_item = item['cantidad'] * item['precio_unitario'] - pdf.cell(col_widths[0], 8, str(item['codigo']), border=1) - # Usar MultiCell para nombres largos - x_before = pdf.get_x() - y_before = pdf.get_y() - pdf.multi_cell(col_widths[1], 8, item['nombre'], border=1, align="L") - pdf.set_xy(x_before + col_widths[1], y_before) # Resetear posición X - - pdf.cell(col_widths[2], 8, str(item['cantidad']), border=1, align="C") - pdf.cell(col_widths[3], 8, f"${item['precio_unitario']:.2f}", border=1, align="R") - pdf.cell(col_widths[4], 8, f"${precio_total_item:.2f}", border=1, align="R") - pdf.ln() # Asegurar nueva línea después de MultiCell - total_pedido += precio_total_item - - # Total del pedido - pdf.ln(5) - pdf.set_font("Arial", 'B', size=10) - pdf.cell(sum(col_widths) - col_widths[-1], 10, "Total del Pedido:", border=0, align="R") - pdf.cell(col_widths[-1], 10, f"${total_pedido:.2f}", border=1, align="R") - pdf.ln() - - # Guardar PDF - pdf_filename = f"pedido_proveedor_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf" - filepath = os.path.join(app.config["UPLOAD_FOLDER"], pdf_filename) - pdf.output(filepath) - - # Limpiar pedido de la sesión después de generar el PDF - session.pop('pedido_proveedor', None) - - flash(f"PDF de pedido al proveedor generado: {pdf_filename}. Guardado en 'uploads'.", "success") - # Opcional: Podrías devolver el archivo para descarga directa - # return send_file(filepath, as_attachment=True, download_name=pdf_filename) - - except Exception as e: - print(f"Error al generar PDF: {e}") - flash("Error al generar el archivo PDF.", "error") - # Limpiar sesión incluso si hay error para evitar reintentos con datos malos - session.pop('pedido_proveedor', None) - - return redirect(url_for("upload_csv")) # Redirigir a inicio después de generar/error - - -# Ruta para exportar inventario como JSON -@app.route("/export_json") -def export_json(): - productos = session.get('productos') - if not productos: - return jsonify({"message": "No hay datos de inventario para exportar."}), 404 - return jsonify(productos) - -# Ruta para exportar inventario como CSV -@app.route("/export_csv") -def export_csv(): - productos = session.get('productos') - if not productos: - flash("No hay datos de inventario para exportar.", "warning") - return redirect(url_for("upload_csv")) - - try: - # Generar CSV en memoria para evitar problemas con el archivo original - output = io.StringIO() - if productos: - # Asegurar que todos los dicts tengan las mismas claves y en el mismo orden - df = pd.DataFrame(productos) - df.to_csv(output, index=False, encoding='utf-8') - output.seek(0) - return send_file( - io.BytesIO(output.getvalue().encode('utf-8')), - mimetype='text/csv', - as_attachment=True, - download_name="inventario_exportado.csv" - ) - else: - # Devolver CSV vacío si no hay productos - output.write("") # Escribir cadena vacía - output.seek(0) - return send_file( - io.BytesIO(output.getvalue().encode('utf-8')), - mimetype='text/csv', - as_attachment=True, - download_name="inventario_vacio.csv" - ) - - except Exception as e: - print(f"Error al exportar CSV: {e}") - flash("Error al generar el archivo CSV de exportación.", "error") - return redirect(url_for("upload_csv")) - - -# Ejecución principal -if __name__ == "__main__": - # debug=True es útil para desarrollo, ¡desactívalo en producción! - # host='0.0.0.0' permite acceso desde otros dispositivos en la red local - app.run(debug=True, host='0.0.0.0') \ No newline at end of file diff --git a/Flask/requisistos.txt b/Flask/requisistos.txt deleted file mode 100644 index 44d39b7..0000000 --- a/Flask/requisistos.txt +++ /dev/null @@ -1,5 +0,0 @@ -Flask -pandas -fpdf -os -datetime \ No newline at end of file diff --git a/Unidad 2/Gestion-inventario-interactiva_varias-tablas-v1.2.py b/Unidad 2/Gestion-inventario-interactiva_varias-tablas-v1.2.py deleted file mode 100644 index 9b324c6..0000000 --- a/Unidad 2/Gestion-inventario-interactiva_varias-tablas-v1.2.py +++ /dev/null @@ -1,242 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib import table -import datetime -import csv -import random -import string - -# ------------------------------------------ -# Función para generar código alfanumérico aleatorio -def generar_codigo_aleatorio(longitud=6): - caracteres = string.ascii_uppercase + string.digits - return ''.join(random.choices(caracteres, k=longitud)) - -# ------------------------------------------ -# 1. Ingreso interactivo de productos con código alfanumérico -inventario = [] - -while True: - print("\nIngrese los datos del nuevo producto:") - - # Se solicita el código; si se deja vacío, se genera automáticamente un código alfanumérico - codigo_input = input("Código: ").strip() - if codigo_input == "": - codigo = generar_codigo_aleatorio() # Código aleatorio de 6 caracteres - # Verificar que el código sea único: - existentes = [p["codigo"] for p in inventario] - while codigo in existentes: - codigo = generar_codigo_aleatorio() - print(f"Se generó el código automático: {codigo}") - else: - codigo = codigo_input # Se acepta el código alfanumérico ingresado por el usuario - - nombre = input("Nombre del producto: ").strip() - - try: - demanda = int(input("Demanda (valor entre 1-10): ").strip()) - except ValueError: - print("Entrada inválida. Se espera un número entero para la demanda.") - continue - - try: - tiempo_entrega = int(input("Tiempo de entrega (en días): ").strip()) - except ValueError: - print("Entrada inválida. Se espera un número entero para el tiempo de entrega.") - continue - - fecha_limite = input("Fecha límite para consumo (YYYY-MM-DD): ").strip() - try: - datetime.datetime.strptime(fecha_limite, "%Y-%m-%d") - except ValueError: - print("Fecha inválida. Asegúrate de ingresar en el formato YYYY-MM-DD.") - continue - - try: - cantidad = int(input("Cantidad en inventario: ").strip()) - except ValueError: - print("Entrada inválida. Se espera un número entero para la cantidad.") - continue - - producto = { - "codigo": codigo, - "nombre": nombre, - "demanda": demanda, - "tiempo_entrega": tiempo_entrega, - "fecha_limite": fecha_limite, - "cantidad": cantidad - } - inventario.append(producto) - - continuar = input("¿Desea ingresar otro producto? (s/n): ").strip().lower() - if continuar != 's': - break - -if not inventario: - print("No se ingresaron productos. El programa se cerrará.") - exit() - -# ------------------------------------------ -# 2. Funciones de ordenamiento - -# Insertion Sort: -def insertion_sort(lista, key, descending=False): - for i in range(1, len(lista)): - actual = lista[i] - j = i - 1 - if descending: - while j >= 0 and key(lista[j]) < key(actual): - lista[j + 1] = lista[j] - j -= 1 - else: - while j >= 0 and key(lista[j]) > key(actual): - lista[j + 1] = lista[j] - j -= 1 - lista[j + 1] = actual - return lista - -# Bubble Sort: -def bubble_sort(lista, key, descending=False): - n = len(lista) - swapped = True - while swapped: - swapped = False - for i in range(1, n): - if descending: - if key(lista[i-1]) < key(lista[i]): - lista[i-1], lista[i] = lista[i], lista[i-1] - swapped = True - else: - if key(lista[i-1]) > key(lista[i]): - lista[i-1], lista[i] = lista[i], lista[i-1] - swapped = True - n -= 1 - return lista - -# Quick Sort: -def quick_sort(lista, key): - if len(lista) <= 1: - return lista - else: - pivot = lista[0] - menos = [x for x in lista[1:] if key(x) < key(pivot)] - iguales = [x for x in lista if key(x) == key(pivot)] - mayor = [x for x in lista[1:] if key(x) >= key(pivot)] - return quick_sort(menos, key) + iguales + quick_sort(mayor, key) - -# Selection Sort: -def selection_sort(lista, key, reverse=False): - nueva_lista = [] - temp = lista.copy() - while temp: - candidato = temp[0] - for item in temp: - if reverse: - if key(item) > key(candidato): - candidato = item - else: - if key(item) < key(candidato): - candidato = item - nueva_lista.append(candidato) - temp.remove(candidato) - return nueva_lista - -# ------------------------------------------ -# 3. Generar listas ordenadas utilizando distintos algoritmos - -# Ordenar por Cantidad usando Insertion Sort (Mas agotados primero) -inventario_por_cantidad = insertion_sort(inventario.copy(), key=lambda x: x["cantidad"], descending=False) - -# Ordenar por Tiempo de entrega usando Bubble Sort (Mas tardados primero) -inventario_por_tiempo = bubble_sort(inventario.copy(), key=lambda x: x["tiempo_entrega"], descending=True) - -# Ordenar por Fecha límite usando Quick Sort (las fechas más próximas primero) -inventario_por_fecha = quick_sort(inventario.copy(), key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d")) - -# Ordenar por Demanda usando Selection Sort (Preferencia del cliente) -inventario_por_demanda = selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True) - -# ------------------------------------------ -# 4. Función para visualizar y guardar cada tabla - -def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_key): - # Preparar la matriz de datos para la tabla - data_matrix = [] - for item in sorted_data: - row = [ - item["codigo"], - item["nombre"], - item["demanda"], - item["tiempo_entrega"], - item["fecha_limite"], - item["cantidad"] - ] - data_matrix.append(row) - - # Crear figura para la tabla - fig, ax = plt.subplots(figsize=(10, len(sorted_data)*0.6 + 2)) - ax.axis('off') - tbl = ax.table(cellText=data_matrix, colLabels=headers, loc='center', cellLoc='center') - tbl.auto_set_font_size(False) - tbl.set_fontsize(10) - tbl.scale(1.2, 1.2) - - plt.title(title, fontsize=14) - plt.figtext(0.5, 0.01, f"Consulta generada: {query_date}", ha="center", fontsize=8) - - # Guardar la figura con un nombre que incluya la clave del algoritmo y un timestamp - timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") - image_filename = f"inventario_{algorithm_key}_{timestamp}.png" - plt.savefig(image_filename, bbox_inches="tight") - plt.show() - plt.close(fig) - - # Guardar registro en CSV - csv_filename = f"registro_{algorithm_key}_{timestamp}.csv" - with open(csv_filename, mode='w', newline='', encoding='utf-8') as csvfile: - writer = csv.writer(csvfile) - writer.writerow(["Fecha Consulta"] + headers) - for row in data_matrix: - writer.writerow([query_date] + row) - - print(f"{title} - Archivos generados:") - print(" Imagen:", image_filename) - print(" Registro CSV:", csv_filename) - print("-----------------------------------------") - -# ------------------------------------------ -# 5. Visualización: Generar una tabla para cada criterio de ordenamiento - -headers = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"] -fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - -ordenamientos = [ - { - "title": "Ordenamiento por Cantidad (Insertion Sort)", - "data": inventario_por_cantidad, - "algorithm_key": "insertion_cantidad" - }, - { - "title": "Ordenamiento por Tiempo de entrega (Bubble Sort)", - "data": inventario_por_tiempo, - "algorithm_key": "bubble_tiempo" - }, - { - "title": "Ordenamiento por Fecha límite (Quick Sort)", - "data": inventario_por_fecha, - "algorithm_key": "quick_fecha" - }, - { - "title": "Ordenamiento por Demanda (Selection Sort)", - "data": inventario_por_demanda, - "algorithm_key": "selection_demanda" - } -] - -for orden in ordenamientos: - generate_table_and_csv( - sorted_data=orden["data"], - title=orden["title"], - headers=headers, - query_date=fecha_consulta, - algorithm_key=orden["algorithm_key"] - ) \ No newline at end of file diff --git a/Unidad 2/Gestion-restorant-v0.2alpha.py b/Unidad 2/Gestion-restorant-v0.2alpha.py deleted file mode 100644 index f1a9f11..0000000 --- a/Unidad 2/Gestion-restorant-v0.2alpha.py +++ /dev/null @@ -1,89 +0,0 @@ -from datetime import datetime - -# Ejemplo para el inventario -inventario = [ - {"codigo": 1, "nombre": "Manzana", "demanda": 8, "tiempo_entrega": 3, "fecha_limite": "2025-04-28", "cantidad": 15}, - {"codigo": 2, "nombre": "Banana", "demanda": 7, "tiempo_entrega": 5, "fecha_limite": "2025-04-25", "cantidad": 2}, - {"codigo": 3, "nombre": "Lechuga", "demanda": 6, "tiempo_entrega": 2, "fecha_limite": "2025-04-22", "cantidad": 7}, - {"codigo": 4, "nombre": "Tomate", "demanda": 9, "tiempo_entrega": 4, "fecha_limite": "2025-05-01", "cantidad": 3}, -] - -# Algoritmos de ordenamiento que se usarán -# 1. Insertion Sort para ordenar por cantidad (de menor a mayor) -def insertion_sort(lista, key): - for i in range(1, len(lista)): - actual = lista[i] - j = i - 1 - while j >= 0 and key(lista[j]) > key(actual): - lista[j + 1] = lista[j] - j -= 1 - lista[j + 1] = actual - return lista - -# Ordenar por 'cantidad' para identificar alimentos que se están agotando -inventario_por_cantidad = insertion_sort(inventario.copy(), key=lambda x: x["cantidad"]) -print("Ordenados por cantidad (Mas agotados primero):") -for item in inventario_por_cantidad: - print(f"{item['nombre']} - Cantidad: {item['cantidad']}") - -# 2. Bubble Sort para ordenar por tiempo de entrega (de mayor a menor) -def bubble_sort(lista, key, descending=False): - n = len(lista) - swapped = True - while swapped: - swapped = False - for i in range(1, n): - if descending: - if key(lista[i-1]) < key(lista[i]): - lista[i-1], lista[i] = lista[i], lista[i-1] - swapped = True - else: - if key(lista[i-1]) > key(lista[i]): - lista[i-1], lista[i] = lista[i], lista[i-1] - swapped = True - n -= 1 - return lista - -# Ordenar por 'tiempo_entrega' de mayor a menor (priorizando entregas mas tardías) -inventario_por_tiempo = bubble_sort(inventario.copy(), key=lambda x: x["tiempo_entrega"], descending=True) -print("\nOrdenados por tiempo de entrega(en Días):") -for item in inventario_por_tiempo: - print(f"{item['nombre']} - Tiempo de entrega: {item['tiempo_entrega']}") - -# 3. Quick Sort para ordenar por fecha límite de consumo (las más próximas primero) -def quick_sort(lista, key): - if len(lista) <= 1: - return lista - else: - pivot = lista[0] - menos = [x for x in lista[1:] if key(x) < key(pivot)] - iguales = [x for x in lista if key(x) == key(pivot)] - mayor = [x for x in lista[1:] if key(x) >= key(pivot)] - return quick_sort(menos, key) + iguales + quick_sort(mayor, key) - -# Conversión de la fecha -inventario_por_fecha = quick_sort(inventario.copy(), key=lambda x: datetime.strptime(x["fecha_limite"], "%Y-%m-%d")) -print("\nOrdenados por fecha límite (próximas primero):") -for item in inventario_por_fecha: - print(f"{item['nombre']} - Fecha límite para consumir: {item['fecha_limite']}") - -# 4. Selection Sort para ordenar por demanda (de mayor a menor) -def selection_sort(lista, key, reverse=False): - n = len(lista) - for i in range(n): - index_extremo = i - for j in range(i+1, n): - if reverse: - if key(lista[j]) > key(lista[index_extremo]): - index_extremo = j - else: - if key(lista[j]) < key(lista[index_extremo]): - index_extremo = j - lista[i], lista[index_extremo] = lista[index_extremo], lista[i] - return lista - -# Ordenar por 'demanda' de mayor a menor (Preferencias del cliente primero) -inventario_por_demanda = selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True) -print("\nOrdenados por demanda (Mas populares primero):") -for item in inventario_por_demanda: - print(f"{item['nombre']} - Demanda: {item['demanda']}") diff --git a/Unidad 2/Gestion-restorant-v0.5beta.py b/Unidad 2/Gestion-restorant-v0.5beta.py deleted file mode 100644 index 77f2bb6..0000000 --- a/Unidad 2/Gestion-restorant-v0.5beta.py +++ /dev/null @@ -1,86 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib import table -import datetime -import csv - -# Ejemplo para el inventario -inventario = [ - {"codigo": 1, "nombre": "Manzana", "demanda": 8, "tiempo_entrega": 3, "fecha_limite": "2025-04-28", "cantidad": 15}, - {"codigo": 2, "nombre": "Banana", "demanda": 7, "tiempo_entrega": 5, "fecha_limite": "2025-04-25", "cantidad": 2}, - {"codigo": 3, "nombre": "Lechuga", "demanda": 6, "tiempo_entrega": 2, "fecha_limite": "2025-04-22", "cantidad": 7}, - {"codigo": 4, "nombre": "Tomate", "demanda": 9, "tiempo_entrega": 4, "fecha_limite": "2025-05-01", "cantidad": 3}, -] - -# 1. Selection Sort para ordenar por demanda (de mayor a menor) -def selection_sort(lista, key, reverse=False): - n = len(lista) - for i in range(n): - index_extremo = i - for j in range(i + 1, n): - if reverse: - if key(lista[j]) > key(lista[index_extremo]): - index_extremo = j - else: - if key(lista[j]) < key(lista[index_extremo]): - index_extremo = j - lista[i], lista[index_extremo] = lista[index_extremo], lista[i] - return lista - -inventario_ordenado = selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True) - -# 2. Preparar la estructura de la tabla para visualizar -headers = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"] - -# Crear la matriz de datos -data_matrix = [] -for item in inventario_ordenado: - row = [ - item["codigo"], - item["nombre"], - item["demanda"], - item["tiempo_entrega"], - item["fecha_limite"], - item["cantidad"] - ] - data_matrix.append(row) - -# Obtener la fecha y hora de la consulta -fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - -# 3. Visualización con Matplotlib -fig, ax = plt.subplots(figsize=(8, 3)) -ax.xaxis.set_visible(False) -ax.yaxis.set_visible(False) -ax.set_frame_on(False) - -tabla = table.Table(ax, bbox=[0, 0, 1, 1]) -tabla = ax.table(cellText=data_matrix, - colLabels=headers, - loc='center', - cellLoc='center') - -tabla.auto_set_font_size(False) -tabla.set_fontsize(10) -tabla.scale(1.2, 1.2) - -plt.title("Inventario ordenado por Demanda", fontsize=14) - -# Agregar la fecha de la consulta en la parte inferior -plt.figtext(0.5, 0.01, f"Consulta generada: {fecha_consulta}", ha="center", fontsize=8) - -# 4. Guardar la figura generada con una fecha en el nombre del archivo -imagen_filename = f"inventario_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.png" -plt.savefig(imagen_filename, bbox_inches="tight") -plt.show() - -# 5. Crear un archivo CSV de registro con los datos y la fecha de consulta -csv_filename = f"registro_inventario_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" -with open(csv_filename, mode='w', newline='', encoding='utf-8') as file: - writer = csv.writer(file) - writer.writerow(["Fecha Consulta"] + headers) - for row in data_matrix: - writer.writerow([fecha_consulta] + row) - -print("Archivos generados:") -print("Imagen:", imagen_filename) -print("Registro CSV:", csv_filename) diff --git a/Unidad 2/Gestion-restorant-varias-tablasv1.0.py b/Unidad 2/Gestion-restorant-varias-tablasv1.0.py deleted file mode 100644 index c38f560..0000000 --- a/Unidad 2/Gestion-restorant-varias-tablasv1.0.py +++ /dev/null @@ -1,176 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib import table -import datetime -import csv - -# --------------------------------------- -# Ejemplo del inventario -inventario = [ - {"codigo": 1, "nombre": "Manzana", "demanda": 8, "tiempo_entrega": 3, "fecha_limite": "2025-04-28", "cantidad": 15}, - {"codigo": 2, "nombre": "Banana", "demanda": 7, "tiempo_entrega": 5, "fecha_limite": "2025-04-25", "cantidad": 2}, - {"codigo": 3, "nombre": "Lechuga", "demanda": 6, "tiempo_entrega": 2, "fecha_limite": "2025-04-22", "cantidad": 7}, - {"codigo": 4, "nombre": "Tomate", "demanda": 9, "tiempo_entrega": 4, "fecha_limite": "2025-05-01", "cantidad": 3}, -] - -# --------------------------------------- -# Funciones de ordenamiento - -# 1. Insertion Sort: -def insertion_sort(lista, key): - for i in range(1, len(lista)): - actual = lista[i] - j = i - 1 - while j >= 0 and key(lista[j]) > key(actual): - lista[j + 1] = lista[j] - j -= 1 - lista[j + 1] = actual - return lista - -# 2. Bubble Sort: -def bubble_sort(lista, key, descending=False): - n = len(lista) - swapped = True - while swapped: - swapped = False - for i in range(1, n): - if descending: - if key(lista[i - 1]) < key(lista[i]): - lista[i - 1], lista[i] = lista[i], lista[i - 1] - swapped = True - else: - if key(lista[i - 1]) > key(lista[i]): - lista[i - 1], lista[i] = lista[i], lista[i - 1] - swapped = True - n -= 1 - return lista - -# 3. Quick Sort: -def quick_sort(lista, key): - if len(lista) <= 1: - return lista - else: - pivot = lista[0] - menos = [x for x in lista[1:] if key(x) < key(pivot)] - iguales = [x for x in lista if key(x) == key(pivot)] - mayor = [x for x in lista[1:] if key(x) >= key(pivot)] - return quick_sort(menos, key) + iguales + quick_sort(mayor, key) - -# 4. Selection Sort: -def selection_sort(lista, key, reverse=False): - n = len(lista) - for i in range(n): - index_extremo = i - for j in range(i + 1, n): - if reverse: - if key(lista[j]) > key(lista[index_extremo]): - index_extremo = j - else: - if key(lista[j]) < key(lista[index_extremo]): - index_extremo = j - lista[i], lista[index_extremo] = lista[index_extremo], lista[i] - return lista - -# --------------------------------------- -# Criterios de Ordenamiento - -# Ordenar por Cantidad en inevtario (usando Insertion Sort: de menor a mayor) -inventario_por_cantidad = insertion_sort(inventario.copy(), key=lambda x: x["cantidad"]) - -# Ordenar por Tiempo de entrega (usando Bubble Sort: de mayor a menor) -inventario_por_tiempo = bubble_sort(inventario.copy(), key=lambda x: x["tiempo_entrega"], descending=True) - -# Ordenar por Fecha límite (usando Quick Sort: las fechas más próximas primero) -inventario_por_fecha = quick_sort( - inventario.copy(), - key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d") -) - -# Ordenar por Demanda del cliente (usando Selection Sort: de mayor a menor) -inventario_por_demanda = selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True) - -# --------------------------------------- -# Función para generar una tabla con Matplotlib y guardar un CSV de registro -def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_key): - # Crear una matriz con los datos ordenados - data_matrix = [] - for item in sorted_data: - row = [item["codigo"], item["nombre"], item["demanda"], - item["tiempo_entrega"], item["fecha_limite"], item["cantidad"]] - data_matrix.append(row) - - # Crear figura y ejes para la tabla - fig, ax = plt.subplots(figsize=(10, len(sorted_data)*0.6 + 2)) - ax.axis('off') - - # Crear la tabla en el gráfico - table_obj = ax.table(cellText=data_matrix, - colLabels=headers, - loc='center', - cellLoc='center') - table_obj.auto_set_font_size(False) - table_obj.set_fontsize(10) - table_obj.scale(1.2, 1.2) - - plt.title(title, fontsize=14) - plt.figtext(0.5, 0.01, f"Consulta generada: {query_date}", ha="center", fontsize=8) - - # Guardar una imagen PNG de los datos - timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") - image_filename = f"inventario_{algorithm_key}_{timestamp}.png" - plt.savefig(image_filename, bbox_inches="tight") - plt.show() # Se mostrará una tabla (se debe cerrar la tabla para continuar) - plt.close(fig) - - # Generar archivo CSV con un registro de la consulta - csv_filename = f"registro_{algorithm_key}_{timestamp}.csv" - with open(csv_filename, mode='w', newline='', encoding='utf-8') as csvfile: - writer = csv.writer(csvfile) - writer.writerow(["Fecha Consulta"] + headers) - for row in data_matrix: - writer.writerow([query_date] + row) - - print(f"{title} - Archivos generados:") - print(" Imagen:", image_filename) - print(" Registro CSV:", csv_filename) - print("-----------------------------------------") - -# --------------------------------------- -# Cabeceras para la tabla -headers = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"] - -# Fecha y hora de la consulta -fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - -# Diccionarios con cada ordenamiento -ordenamientos = [ - { - "title": "Ordenamiento por Cantidad (Insertion Sort)", - "data": inventario_por_cantidad, - "algorithm_key": "insertion" - }, - { - "title": "Ordenamiento por Tiempo de entrega (Bubble Sort)", - "data": inventario_por_tiempo, - "algorithm_key": "bubble" - }, - { - "title": "Ordenamiento por Fecha límite (Quick Sort)", - "data": inventario_por_fecha, - "algorithm_key": "quick" - }, - { - "title": "Ordenamiento por Demanda (Selection Sort)", - "data": inventario_por_demanda, - "algorithm_key": "selection" - } -] - -# Iterar sobre cada criterio de ordenamiento, generar y mostrar la tabla, y guardar el CSV correspondiente. -for orden in ordenamientos: - generate_table_and_csv( - sorted_data=orden["data"], - title=orden["title"], - headers=headers, - query_date=fecha_consulta, - algorithm_key=orden["algorithm_key"] - ) diff --git a/Unidad 2/Gestion-restorant_una-tabla-v1.0.py b/Unidad 2/Gestion-restorant_una-tabla-v1.0.py deleted file mode 100644 index 36fb398..0000000 --- a/Unidad 2/Gestion-restorant_una-tabla-v1.0.py +++ /dev/null @@ -1,128 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib import table -import datetime -import csv - -# --------------------------------------- -# Ejemplo del inventario -inventario = [ - {"codigo": 1, "nombre": "Manzana", "demanda": 8, "tiempo_entrega": 3, "fecha_limite": "2025-04-28", "cantidad": 15}, - {"codigo": 2, "nombre": "Banana", "demanda": 7, "tiempo_entrega": 5, "fecha_limite": "2025-04-25", "cantidad": 2}, - {"codigo": 3, "nombre": "Lechuga", "demanda": 6, "tiempo_entrega": 2, "fecha_limite": "2025-04-22", "cantidad": 7}, - {"codigo": 4, "nombre": "Tomate", "demanda": 9, "tiempo_entrega": 4, "fecha_limite": "2025-05-01", "cantidad": 3}, -] - -# --------------------------------------- -# Algoritmos de ordenamiento -# 1. Insertion Sort (modificado para aceptar orden descendente o ascendente) -def insertion_sort(lista, key, descending=False): - for i in range(1, len(lista)): - actual = lista[i] - j = i - 1 - if descending: - while j >= 0 and key(lista[j]) < key(actual): - lista[j + 1] = lista[j] - j -= 1 - else: - while j >= 0 and key(lista[j]) > key(actual): - lista[j + 1] = lista[j] - j -= 1 - lista[j + 1] = actual - return lista - -# 2. Quick Sort (modificado para ordenar por fecha límite de forma recursiva y asumiendo orden ascendente) -def quick_sort(lista, key): - if len(lista) <= 1: - return lista - else: - pivot = lista[0] - menos = [x for x in lista[1:] if key(x) < key(pivot)] - iguales = [x for x in lista if key(x) == key(pivot)] - mayor = [x for x in lista[1:] if key(x) >= key(pivot)] - return quick_sort(menos, key) + iguales + quick_sort(mayor, key) - -# 3. Selection Sort (implementado de forma estable para listas pequeñas) -def selection_sort(lista, key, reverse=False): - nueva_lista = [] - temp = lista.copy() - while temp: - candidato = temp[0] - for item in temp: - if reverse: - if key(item) > key(candidato): - candidato = item - else: - if key(item) < key(candidato): - candidato = item - nueva_lista.append(candidato) - temp.remove(candidato) - return nueva_lista - -# --------------------------------------- -# Ordenamiento jerárquico - -# Para aplicar un sort múltiple estable se ordena partiendo del criterio menos importante: -# 4º (menos importante): Demanda (Selection Sort, orden descendente) -ordenados = selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True) - -# 3º: Tiempo de entrega (con Insertion Sort en orden descendente) -ordenados = insertion_sort(ordenados, key=lambda x: x["tiempo_entrega"], descending=True) - -# 2º: Cantidad en inventario (con Insertion Sort en orden ascendente) -ordenados = insertion_sort(ordenados, key=lambda x: x["cantidad"], descending=False) - -# 1º (más importante): Fecha límite (con Quick Sort en orden ascendente) -ordenados = quick_sort(ordenados, key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d")) - -# --------------------------------------- -# Preparar datos en cabeceras para la visualizar -headers = ["Código", "Nombre", "Demanda", "Tiempo entrega(Días)", "Fecha límite", "Cantidad"] - -data_matrix = [] -for item in ordenados: - row = [item["codigo"], - item["nombre"], - item["demanda"], - item["tiempo_entrega"], - item["fecha_limite"], - item["cantidad"]] - data_matrix.append(row) - -# Fecha y hora de la consulta -fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - -# --------------------------------------- -# Visualización con Matplotlib -fig, ax = plt.subplots(figsize=(10, len(ordenados)*0.6 + 2)) -ax.axis('off') - -tabla = ax.table(cellText=data_matrix, - colLabels=headers, - loc='center', - cellLoc='center') -tabla.auto_set_font_size(False) -tabla.set_fontsize(10) -tabla.scale(1.2, 1.2) - -plt.title("Inventario Ordenado Jerárquicamente", fontsize=16) -plt.figtext(0.5, 0.01, f"Consulta generada: {fecha_consulta}", ha="center", fontsize=8) - -# Guardar una imagen con un timestamp (PNG) -timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") -nombre_imagen = f"inventario_jerarquico_{timestamp}.png" -plt.savefig(nombre_imagen, bbox_inches="tight") -plt.show() - -# --------------------------------------- -# Generar un archivo CSV con el registro de la consulta -nombre_csv = f"registro_inventario_jerarquico_{timestamp}.csv" -with open(nombre_csv, mode='w', newline='', encoding='utf-8') as file: - writer = csv.writer(file) - # Escribir encabezado con la fecha de consulta - writer.writerow(["Fecha Consulta"] + headers) - for row in data_matrix: - writer.writerow([fecha_consulta] + row) - -print("Archivos generados:") -print(" Imagen:", nombre_imagen) -print(" Registro CSV:", nombre_csv) diff --git a/Unidad 3/Flask/ReadMe.md b/Unidad 3/Flask/ReadMe.md deleted file mode 100644 index 77a4817..0000000 --- a/Unidad 3/Flask/ReadMe.md +++ /dev/null @@ -1,106 +0,0 @@ - -# Touken Café - Sistema de Gestión de Pedidos - -## Descripción General - -Touken Café es una aplicación web desarrollada en Python con Flask para la gestión integral de pedidos e inventario. Permite a los usuarios cargar su inventario desde un archivo CSV, realizar pedidos de stock interno, generar órdenes de compra para proveedores en formato PDF, y exportar la información del inventario en formatos JSON y CSV. La aplicación está diseñada para ser intuitiva y facilitar la gestión de suministros del café Touken. - -## Funcionalidades Principales - -* **Carga de Inventario CSV:** Permite a los usuarios cargar fácilmente su lista de productos, cantidades, demanda, tiempo de entrega y otras especificaciones desde un archivo CSV. Utilizar el script de la unidad 2 de ser necesario. -* **Pedido de Stock Interno:** Una interfaz para realizar pedidos entre las diferentes áreas del café, controlando el stock disponible y ajustando la demanda de los productos. La demanda se recalcula dinámicamente en función de las cantidades pedidas internamente, dentro de un rango de 1 a 10. -* **Pedido a Proveedor:** Permite generar órdenes de compra para proveedores, especificando la cantidad deseada y el precio unitario de cada producto. También muestra la cantidad actual en stock como referencia y el tiempo de entrega estimado desde el proveedor (importado del CSV). -* **Generación de PDF de Pedido a Proveedor:** Las órdenes de compra para proveedores se generan automáticamente en formato PDF, listas para ser enviadas. Solo se incluyen en el PDF los productos con una cantidad de pedido mayor que cero. -* **Exportación de Inventario:** Los datos del inventario actual pueden ser exportados en formatos JSON y CSV para su análisis o integración con otros sistemas. -* **Interfaz de Usuario Intuitiva:** La aplicación cuenta con una interfaz web sencilla y fácil de usar, guiando al usuario a través de los diferentes procesos. - -## Tecnologías Utilizadas - -* **Python:** Lenguaje de programación principal. -* **Flask:** Framework web ligero para Python. -* **pandas:** Librería para manipulación y análisis de datos (lectura de CSV). -* **fpdf:** Librería para generación de archivos PDF. -* **Werkzeug:** Biblioteca de utilidades para aplicaciones WSGI (utilizada por Flask). -* **Jinja2:** Motor de plantillas para Flask (creación de las páginas HTML dinámicas). - -## Requisitos de Instalación - -* Python 3.x -* pip (gestor de paquetes de Python) - -## Instalación - -1. **Clonar el repositorio (si aplica):** - ```bash - git clone [https://github.com/WolveJC/ACADE-Prog.git](https://github.com/WolveJC/ACADE-Prog.git) - cd ACADE-Prog - cd Unidad\ 3/Flask - ``` - -2. **Crear un entorno virtual (recomendado):** - ```bash - python -m venv venv - source venv/bin/activate # En Linux/macOS - venv\Scripts\activate # En Windows - ``` - -3. **Instalar las dependencias:** - ```bash - pip install -r requisitos.txt - ``` - -## Estructura de archivos - -``` -ACADE-Prog/Unidad 3/Flask -├── uploads/ #Carpeta donde se guardará el PDF (incluye un CSV de prueba) -├── static/ #Carpeta para archivos estáticos -│ └── css/ -│ └── styles.css -│ └── js/ -│ └── scripts.js -│ └── img/ #Carpeta para logos (por implementar) -├── templates/ #Carpeta para las plantillas HTML (Jinja2) -│ ├── base.html -│ ├── subir_csv.html -│ ├── pedir_stock.html -│ └── pedir_proveedor.html -├── app.py #App principal de la aplicación -├── requisitos.txt #Archivo con las dependencias del proyecto -├── README.md #Archivo de documentación del proyecto -``` - -## Ejecución de la Aplicación - -1. Asegúrate de estar dentro del entorno virtual (si lo creaste). -2. Ejecuta la aplicación Flask: - ```bash - python app.py - ``` -3. Abre tu navegador web y ve a `http://127.0.0.1:5000/` (o la dirección que indique la consola). - -## Uso - -1. **Crear Archivo CSV:** En ```https://github.com/WolveJC/ACADE-Prog/tree/main/Unidad%202``` se encuentran varios scripts para crear el inventario ncesesario para esta aplicación. Seleccione el script de preferencia y gener un archivo de inventario CSV (Aún así, en la carpeta uploads, de este proyecto, contiene un archivo que se utilizó para la prueba del programa) - ### NOTA: - Ahora la app tiene integrada una aplicación para crear el CSV necesario. **ADVERTENCIA:** Los archivos que hayan sido creados con un día de diferencia en la carpeta `/uploads` serán eliminados para mantener el servidor más estable -3. **Subir Archivo CSV:** En la página principal, selecciona y sube tu archivo CSV de inventario. Asegúrate de que el archivo tenga las columnas en el siguiente orden: `Fecha Consulta`, `Código`, `Nombre`, `Demanda`, `Tiempo entrega(Días)`, `Fecha límite`, `Cantidad`. -4. **Seleccionar Tipo de Pedido:** Después de subir el archivo, aparecerán opciones para realizar un "Pedido de Stock Interno" o un "Pedido al Proveedor". -5. **Pedido de Stock Interno:** En esta página, puedes especificar la cantidad a pedir del stock disponible para cada producto. La demanda se actualizará automáticamente dentro de un rango de 1 a 10. Los resultados del pedido se mostrarán en la misma página. -6. **Pedido al Proveedor:** Aquí, verás la cantidad actual en stock de cada producto y podrás ingresar la cantidad que deseas pedir y el precio unitario. El tiempo de entrega desde el proveedor se muestra como referencia. -7. **Generar PDF de Pedido a Proveedor:** Al enviar el formulario de pedido al proveedor, se generará un archivo PDF con los detalles del pedido (solo para productos con cantidad mayor a 0), que se guardará en la carpeta `uploads` y se te notificará. -8. **Exportar Inventario:** En varias páginas, encontrarás enlaces para exportar el inventario actual a archivos JSON y CSV. -9. **Navegación:** Utiliza los enlaces proporcionados en las diferentes páginas para navegar entre las funcionalidades de la aplicación. - -## Notas Adicionales -* La lógica de recálculo de la demanda en el pedido de stock interno es una aproximación simple y puede ser ajustada según las necesidades específicas del negocio. -* La aplicación asume que el archivo CSV de inventario tiene el formato y las columnas esperadas. Cualquier desviación puede causar errores. -* La gestión de errores y la validación de datos podrían ser mejoradas en futuras versiones. - - ## Contribuciones -Las contribuciones son bienvenidas. Si deseas mejorar esta aplicación, por favor, sigue los siguientes pasos: - 1. Haz un fork del repositorio. - 2. Crea una rama para tu contribución (`git checkout -b feature/nueva-funcionalidad`). - 3. Realiza tus cambios y commitea (`git commit -am 'Añade nueva funcionalidad'`). - 4. Sube tus cambios al repositorio remoto (`git push origin feature/nueva-funcionalidad`). - 5. Crea un pull request. diff --git a/Unidad 3/Flask/app.py b/Unidad 3/Flask/app.py deleted file mode 100644 index 39043f6..0000000 --- a/Unidad 3/Flask/app.py +++ /dev/null @@ -1,308 +0,0 @@ -from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, send_file -import os -import pandas as pd -from fpdf import FPDF -import datetime -import time -import random -import string -import csv - -# Configuración básica de Flask -app = Flask(__name__) -app.secret_key = "secret-key" -UPLOAD_FOLDER = "uploads" -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER - -# Función para generar código alfanumérico aleatorio -def generar_codigo_aleatorio(longitud=6): - caracteres = string.ascii_uppercase + string.digits - return ''.join(random.choices(caracteres, k=longitud)) - -# Función para limpiar archivos antiguos en la carpeta uploads -def limpiar_archivos_antiguos(): - ahora = time.time() - un_dia_en_segundos = 24 * 3600 - archivos_eliminados = 0 - - if os.path.exists(app.config['UPLOAD_FOLDER']): - for nombre_archivo in os.listdir(app.config['UPLOAD_FOLDER']): - ruta_archivo = os.path.join(app.config['UPLOAD_FOLDER'], nombre_archivo) - try: - if os.path.isfile(ruta_archivo): - timestamp_creacion = os.path.getctime(ruta_archivo) - if (ahora - timestamp_creacion) > un_dia_en_segundos: - os.remove(ruta_archivo) - print(f"Archivo antiguo eliminado: {ruta_archivo}") - archivos_eliminados += 1 - except Exception as e: - print(f"Error al procesar {ruta_archivo}: {e}") - - if archivos_eliminados > 0: - print(f"Se eliminaron {archivos_eliminados} archivos antiguos de la carpeta uploads.") - else: - print("No se encontraron archivos antiguos para eliminar en la carpeta uploads.") - -# Ejecutar la limpieza al inicio de la aplicación -with app.app_context(): - limpiar_archivos_antiguos() - -# Ruta para crear inventario manualmente -@app.route("/crear_inventario", methods=["GET", "POST"]) -def crear_inventario(): - if request.method == "POST": - if 'agregar_producto' in request.form: - codigo = request.form.get('codigo') - if not codigo: - codigo = generar_codigo_aleatorio() - productos_en_sesion = session.get('inventario_creado', []) - while any(p['codigo'] == codigo for p in productos_en_sesion): - codigo = generar_codigo_aleatorio() - - nombre = request.form.get('nombre') - try: - demanda = int(request.form.get('demanda')) - if not 1 <= demanda <= 10: - flash("La demanda debe ser un valor entre 1 y 10.", 'error') - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - except ValueError: - flash("Demanda inválida.", 'error') - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - - try: - tiempo_entrega = int(request.form.get('tiempo_entrega')) - except ValueError: - flash("Tiempo de entrega inválido.", 'error') - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - - fecha_limite = request.form.get('fecha_limite') - try: - datetime.datetime.strptime(fecha_limite, "%Y-%m-%d") - except ValueError: - flash("Formato de fecha inválido (YYYY-MM-DD).", 'error') - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - - try: - cantidad = int(request.form.get('cantidad')) - except ValueError: - flash("Cantidad inválida.", 'error') - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - - nuevo_producto = { - "codigo": codigo, - "nombre": nombre, - "demanda": demanda, - "tiempo_entrega": tiempo_entrega, - "fecha_limite": fecha_limite, - "cantidad": cantidad - } - - inventario_creado = session.get('inventario_creado', []) - inventario_creado.append(nuevo_producto) - session['inventario_creado'] = inventario_creado - return redirect(url_for('crear_inventario')) - - elif 'guardar_csv' in request.form: - inventario_para_guardar = session.get('inventario_creado') - if inventario_para_guardar: - nombre_archivo_creado = f"inventario_creado_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" - ruta_archivo_creado = os.path.join(app.config['UPLOAD_FOLDER'], nombre_archivo_creado) - try: - fieldnames_reales = ['codigo', 'nombre', 'demanda', 'tiempo_entrega', 'fecha_limite', 'cantidad'] - encabezados_csv = ['Código', 'Nombre', 'Demanda', 'Tiempo entrega(Días)', 'Fecha límite', 'Cantidad'] - with open(ruta_archivo_creado, 'w', newline='', encoding='utf-8') as csvfile: - writer = csv.DictWriter(csvfile, fieldnames=fieldnames_reales) - encabezado_dict = dict(zip(fieldnames_reales, encabezados_csv)) - csvfile.write(','.join(encabezados_csv) + '\n') - writer.writerows(inventario_para_guardar) - session['csv_filepath'] = ruta_archivo_creado - # Simular la subida exitosa del archivo creado - data = pd.read_csv(ruta_archivo_creado) - session['productos'] = data.to_dict(orient="records") - flash(f"Inventario creado y guardado como {nombre_archivo_creado}.", 'success') - return redirect(url_for('upload_csv')) # Redirigir a la página principal - except Exception as e: - flash(f"Error al guardar el archivo CSV: {e}", 'error') - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - else: - flash("No hay productos para guardar.", 'warning') - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - - return render_template("crear_inventario.html", inventario_creado=session.get('inventario_creado', [])) - -# Ruta de inicio (para subir CSV y seleccionar tipo de pedido) -@app.route("/", methods=["POST", "GET"]) -def upload_csv(): - if request.method == "POST": - file = request.files["file"] - if file and file.filename.endswith(".csv"): - filename = file.filename - filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename) - file.save(filepath) - - data = pd.read_csv(filepath) - json_data = data.to_dict(orient="records") - - session['productos'] = json_data - session['csv_filepath'] = filepath - flash("Archivo CSV de inventario cargado con éxito.") - - # Considera eliminar el CSV aquí si solo lo necesitas para la sesión activa - # os.remove(filepath) - # print(f"Archivo CSV eliminado: {filepath}") - - return redirect(url_for("upload_csv")) # Redirigir de nuevo a la página principal - else: - flash("Por favor, sube un archivo CSV válido.") - return render_template("subir_csv.html") - -# Ruta para pedir stock interno -@app.route("/pedir_stock", methods=["GET", "POST"]) -def pedir_stock(): - productos = session.get('productos') - if not productos: - flash("No hay inventario cargado.") - return redirect(url_for("upload_csv")) - - resultados_pedido = [] - - if request.method == "POST": - pedido_stock = {} - for codigo, cantidad in request.form.items(): - if codigo.startswith("pedido_stock["): - codigo_limpio = codigo[len("pedido_stock["):-1] - cantidad_pedida = int(cantidad) - if cantidad_pedida > 0: - pedido_stock[codigo_limpio] = cantidad_pedida - - if pedido_stock: - productos_actualizados = [] - for producto in productos: - codigo = str(producto['Código']) - if codigo in pedido_stock: - cantidad_pedida = pedido_stock[codigo] - if cantidad_pedida <= producto['Cantidad']: - producto['Cantidad'] -= cantidad_pedida - demanda_actual = producto['Demanda'] - - # Aproximación: ajustar la demanda hacia la cantidad pedida - factor_aprendizaje = 0.3 - nueva_demanda = demanda_actual + (cantidad_pedida - demanda_actual) * factor_aprendizaje - producto['Demanda'] = min(round(nueva_demanda), 10) - - resultados_pedido.append(f"Pedido de {cantidad_pedida} {producto['Nombre']} registrado. Nuevo stock: {producto['Cantidad']}, Nueva demanda: {producto['Demanda']}") - else: - resultados_pedido.append(f"No hay suficiente stock de {producto['Nombre']} (disponible: {producto['Cantidad']}) para pedir {cantidad_pedida}.") - productos_actualizados.append(producto) - - session['productos'] = productos_actualizados - df = pd.DataFrame(productos_actualizados) - df.to_csv(session.get('csv_filepath'), index=False) - for resultado in resultados_pedido: - flash(resultado) - return redirect(url_for("pedir_stock")) - else: - flash("No se especificó ninguna cantidad para el pedido de stock.") - - return render_template("pedir_stock.html", productos_stock=productos) - -# Ruta para pedir al proveedor -@app.route("/pedir_proveedor", methods=["GET", "POST"]) -def pedir_proveedor(): - productos = session.get('productos') - if not productos: - flash("No hay inventario cargado.") - return redirect(url_for("upload_csv")) - - if request.method == "POST": - pedido_proveedor = [] - for codigo, cantidad in request.form.items(): - if codigo.startswith("pedido_proveedor["): - codigo_limpio = codigo[len("pedido_proveedor["):-1] - cantidad_pedida = int(cantidad) - precio_str = request.form.get(f"precio_proveedor[{codigo_limpio}]") - precio = 0.0 - - if precio_str: - try: - precio = float(precio_str) - except ValueError: - flash(f"Por favor, ingrese un precio válido para el producto con código {codigo_limpio}.") - return render_template("pedir_proveedor.html", productos_proveedor=productos) - - if cantidad_pedida > 0 and precio > 0: - producto_info = next((p for p in productos if str(p['Código']) == codigo_limpio), None) - if producto_info: - pedido_proveedor.append({ - "codigo": producto_info['Código'], - "nombre": producto_info['Nombre'], - "cantidad": cantidad_pedida, - "precio_unitario": precio, - "tiempo_entrega": producto_info.get('Tiempo entrega(Días)'), - "stock_actual": producto_info.get('Cantidad') - }) - - if pedido_proveedor: - session['pedido_proveedor'] = pedido_proveedor - return redirect(url_for("generar_pdf_proveedor")) - else: - flash("No se especificó ninguna cantidad o precio válido para el pedido al proveedor.") - - return render_template("pedir_proveedor.html", productos_proveedor=productos) - -# Ruta para generar PDF de pedido al proveedor -@app.route("/generar_pdf_proveedor", methods=["GET"]) -def generar_pdf_proveedor(): - pedido_proveedor = session.get('pedido_proveedor') - if not pedido_proveedor: - flash("No hay ningún pedido para generar el PDF.") - return redirect(url_for("pedir_proveedor")) - - pdf = FPDF() - pdf.add_page() - pdf.set_font("Arial", size=12) - - pdf.cell(0, 5, f"Pedido al Proveedor - Touken Café ({datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})", ln=True, align="C") - pdf.ln(5) - - for item in pedido_proveedor: - if item['cantidad'] > 0: - pdf.cell(0, 5, f"Código: {item['codigo']}", ln=True) - pdf.cell(0, 5, f"Producto: {item['nombre']}", ln=True) - pdf.cell(0, 5, f"Cantidad: {item['cantidad']}, Precio: ${item['precio_unitario']:.2f}", ln=True) - pdf.cell(0, 5, f"Entrega: {item.get('tiempo_entrega', 'N/A')} días, Stock: {item.get('stock_actual', 'N/A')}", ln=True) - pdf.ln(3) - - pdf_file = f"pedido_proveedor_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf" - filepath = os.path.join(app.config["UPLOAD_FOLDER"], pdf_file) - pdf.output(filepath) - flash(f"PDF de pedido al proveedor generado exitosamente. Guardado como {pdf_file} en la carpeta uploads.") - session.pop('pedido_proveedor', None) - - # Considera eliminar el PDF aquí si solo lo necesitas para la sesión activa - # os.remove(filepath) - # print(f"Archivo PDF eliminado: {filepath}") - - return redirect(url_for("upload_csv")) - -# Ruta para exportar inventario como JSON -@app.route("/export_json") -def export_json(): - productos = session.get('productos') - if not productos: - flash("No hay datos de inventario para exportar. Por favor, sube un archivo CSV.") - return redirect(url_for("upload_csv")) - return jsonify(productos) - -# Ruta para exportar inventario como CSV -@app.route("/export_csv") -def export_csv(): - csv_filepath = session.get('csv_filepath') - if not csv_filepath: - flash("No hay datos de inventario para exportar. Por favor, sube un archivo CSV.") - return redirect(url_for("upload_csv")) - return send_file(csv_filepath, as_attachment=True, download_name="inventario_exportado.csv") - -# Ejecución -if __name__ == "__main__": - app.run(debug=True) diff --git a/Unidad 3/Flask/requisistos.txt b/Unidad 3/Flask/requisistos.txt deleted file mode 100644 index 44d39b7..0000000 --- a/Unidad 3/Flask/requisistos.txt +++ /dev/null @@ -1,5 +0,0 @@ -Flask -pandas -fpdf -os -datetime \ No newline at end of file diff --git a/Unidad 3/Flask/static/css/styles.css b/Unidad 3/Flask/static/css/styles.css deleted file mode 100644 index 3918577..0000000 --- a/Unidad 3/Flask/static/css/styles.css +++ /dev/null @@ -1,51 +0,0 @@ -/* Estilo básico... */ -body { - font-family: Arial, sans-serif; - background-color: #f9f9f9; - margin: 0; - padding: 0; -} - -h1 { - color: #333; - text-align: center; - margin-top: 20px; -} - -table { - width: 80%; - margin: 20px auto; - border-collapse: collapse; -} - -table, th, td { - border: 1px solid #ddd; - padding: 10px; -} - -th { - background-color: #4CAF50; - color: white; - text-align: center; -} - -td { - text-align: center; -} - -button { - background-color: #4CAF50; - color: white; - border: none; - padding: 10px 20px; - cursor: pointer; -} - -button:hover { - background-color: #45a049; -} - -input[type="number"] { - width: 60px; - padding: 5px; -} \ No newline at end of file diff --git a/Unidad 3/Flask/static/css/syle.css b/Unidad 3/Flask/static/css/syle.css deleted file mode 100644 index 7cac865..0000000 --- a/Unidad 3/Flask/static/css/syle.css +++ /dev/null @@ -1,51 +0,0 @@ -/* Estilo básico... */ -body { - font-family: Arial, sans-serif; - background-color: #f9f9f9; - margin: 0; - padding: 0; -} - -h1 { - color: #333; - text-align: center; - margin-top: 20px; -} - -table { - width: 80%; - margin: 20px auto; - border-collapse: collapse; -} - -table, th, td { - border: 1px solid #ddd; - padding: 10px; -} - -th { - background-color: #4CAF50; - color: white; - text-align: center; -} - -td { - text-align: center; -} - -button { - background-color: #4CAF50; - color: white; - border: none; - padding: 10px 20px; - cursor: pointer; -} - -button:hover { - background-color: #45a049; -} - -input[type="number"] { - width: 60px; - padding: 5px; -} diff --git a/Unidad 3/Flask/static/image/ReadME.md b/Unidad 3/Flask/static/image/ReadME.md deleted file mode 100644 index a300743..0000000 --- a/Unidad 3/Flask/static/image/ReadME.md +++ /dev/null @@ -1 +0,0 @@ -# In building diff --git a/Unidad 3/Flask/static/js/Script.js b/Unidad 3/Flask/static/js/Script.js deleted file mode 100644 index 62f2555..0000000 --- a/Unidad 3/Flask/static/js/Script.js +++ /dev/null @@ -1,24 +0,0 @@ -// Muestra una alerta subir el archivo -document.addEventListener("DOMContentLoaded", function () { - const form = document.querySelector("form"); - form.addEventListener("submit", function () { - alert("¡Archivo subido con éxito!"); - }); -}); - -// Validar campos -function validarPedido() { - const inputs = document.querySelectorAll('input[type="number"]'); - let total = 0; - - inputs.forEach(input => { - const cantidad = parseInt(input.value) || 0; - total += cantidad; - }); - - if (total === 0) { - alert("No has seleccionado ningún producto. Por favor, selecciona al menos uno."); - return false; - } - return true; - } diff --git a/Unidad 3/Flask/static/js/script.js b/Unidad 3/Flask/static/js/script.js deleted file mode 100644 index eb22eff..0000000 --- a/Unidad 3/Flask/static/js/script.js +++ /dev/null @@ -1,163 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - setupCrearInventarioUI(); - setupPedirStockUI(); - setupPedirProveedorUI(); - setupFileUploadUI(); // Lógica UI para la subida de archivos -}); - -// ------------------------------------------------------------ -// Lógica de Interfaz de Usuario para la página de subida de CSV (subir_csv.html) -// ------------------------------------------------------------ -function setupFileUploadUI() { - const formSubirCsv = document.querySelector('form'); - if (formSubirCsv) { - formSubirCsv.addEventListener('submit', () => { - alert('Subiendo archivo...'); // Feedback inmediato al usuario - }); - } -} - -// ------------------------------------------------------------ -// Lógica de Interfaz de Usuario y Gestión de Formulario para la página de creación de inventario (crear_inventario.html) -// ------------------------------------------------------------ -let inventarioCreadoUI = []; // Mantener temporalmente en el frontend para la UI - -function setupCrearInventarioUI() { - const formAgregarProducto = document.querySelector('#crear-inventario-form'); - const btnAgregarProducto = document.querySelector('#agregar-producto-btn'); - const btnGuardarCsv = document.querySelector('#guardar-inventario-btn'); - const tablaInventario = document.querySelector('#inventario-creado-table tbody'); - - if (btnAgregarProducto) { - btnAgregarProducto.addEventListener('click', (event) => { - event.preventDefault(); - const nuevoProducto = obtenerDatosFormulario('#crear-inventario-form'); - if (validarProducto(nuevoProducto)) { - inventarioCreadoUI.push(nuevoProducto); - renderTablaInventarioUI(tablaInventario); - formAgregarProducto.reset(); - } - }); - } - - if (btnGuardarCsv) { - btnGuardarCsv.addEventListener('click', () => { - // Cuando se guarda, simplemente enviamos los datos al backend - // El backend se encargará de crear el CSV y actualizar la sesión. - const inventarioParaGuardar = { inventario: inventarioCreadoUI }; - // Aquí podrías usar fetch para enviar los datos al backend de forma asíncrona - // y manejar la respuesta (éxito/error) sin recargar la página. - console.log('Inventario para guardar:', inventarioParaGuardar); - alert('Enviando inventario para guardar (la lógica real está en el backend).'); - // Para un envío tradicional, podrías crear un formulario dinámicamente y enviarlo. - }); - } - - // Cargar el inventario temporal de sessionStorage (opcional para persistencia en la sesión) - const inventarioGuardadoUI = sessionStorage.getItem('inventarioCreadoUI'); - if (inventarioGuardadoUI) { - inventarioCreadoUI = JSON.parse(inventarioGuardadoUI); - renderTablaInventarioUI(tablaInventario); - } -} - -function obtenerDatosFormulario(selectorForm) { - const form = document.querySelector(selectorForm); - if (!form) return {}; - const formData = new FormData(form); - const data = {}; - for (let [key, value] of formData.entries()) { - data[key] = value; - } - return data; -} - -function validarProducto(producto) { - if (!producto.nombre || isNaN(parseInt(producto.demanda)) || parseInt(producto.demanda) < 1 || parseInt(producto.demanda) > 10 || isNaN(parseInt(producto.tiempo_entrega)) || !producto.fecha_limite || isNaN(parseInt(producto.cantidad))) { - alert('Por favor, complete todos los campos correctamente.'); - return false; - } - return true; -} - -function renderTablaInventarioUI(tbody) { - tbody.innerHTML = ''; - inventarioCreadoUI.forEach(producto => { - const row = tbody.insertRow(); - row.insertCell().textContent = producto.codigo || generarCodigoAleatorioUI(); - row.insertCell().textContent = producto.nombre; - row.insertCell().textContent = producto.demanda; - row.insertCell().textContent = producto.tiempo_entrega; - row.insertCell().textContent = producto.fecha_limite; - row.insertCell().textContent = producto.cantidad; - }); -} - -function generarCodigoAleatorioUI(longitud = 6) { - const caracteres = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - let resultado = ''; - for (let i = 0; i < longitud; i++) { - resultado += caracteres.charAt(Math.floor(Math.random() * caracteres.length)); - } - return resultado; -} - -// ------------------------------------------------------------ -// Lógica de Interfaz de Usuario y Gestión de Formulario para la página de pedido de stock (pedir_stock.html) -// ------------------------------------------------------------ -function setupPedirStockUI() { - const formPedirStock = document.querySelector('#pedir-stock-form'); - if (formPedirStock) { - formPedirStock.addEventListener('submit', (event) => { - if (!validarPedidoUI()) { - event.preventDefault(); // Evitar envío si no se selecciona nada - } else { - alert('Realizando pedido de stock...'); - } - }); - } -} - -function validarPedidoUI() { - const inputs = document.querySelectorAll('#pedir-stock-form input[type="number"]'); - let total = 0; - inputs.forEach(input => { - total += parseInt(input.value) || 0; - }); - if (total === 0) { - alert("No has seleccionado ningún producto para el pedido de stock."); - return false; - } - return true; -} - -// ------------------------------------------------------------ -// Lógica de Interfaz de Usuario y Gestión de Formulario para la página de pedido al proveedor (pedir_proveedor.html) -// ------------------------------------------------------------ -function setupPedirProveedorUI() { - const formPedirProveedor = document.querySelector('#pedir-proveedor-form'); - if (formPedirProveedor) { - formPedirProveedor.addEventListener('submit', (event) => { - if (!validarPedidoProveedorUI()) { - event.preventDefault(); - } else { - alert('Realizando pedido al proveedor...'); - } - }); - } -} - -function validarPedidoProveedorUI() { - const inputsCantidad = document.querySelectorAll('#pedir-proveedor-form input[name^="pedido_proveedor"]'); - let algunPedido = false; - inputsCantidad.forEach(input => { - if (parseInt(input.value) > 0) { - algunPedido = true; - } - }); - if (!algunPedido) { - alert("No has especificado ninguna cantidad para el pedido al proveedor."); - return false; - } - return true; -} \ No newline at end of file diff --git a/Unidad 3/Flask/static/js/scripts.js b/Unidad 3/Flask/static/js/scripts.js deleted file mode 100644 index 717cb14..0000000 --- a/Unidad 3/Flask/static/js/scripts.js +++ /dev/null @@ -1,24 +0,0 @@ -// Muestra una alerta subir el archivo -document.addEventListener("DOMContentLoaded", function () { - const form = document.querySelector("form"); - form.addEventListener("submit", function () { - alert("¡Archivo subido con éxito!"); - }); -}); - -// Validar campos -function validarPedido() { - const inputs = document.querySelectorAll('input[type="number"]'); - let total = 0; - - inputs.forEach(input => { - const cantidad = parseInt(input.value) || 0; - total += cantidad; - }); - - if (total === 0) { - alert("No has seleccionado ningún producto. Por favor, selecciona al menos uno."); - return false; - } - return true; -} \ No newline at end of file diff --git a/Unidad 3/Flask/templates/base.html b/Unidad 3/Flask/templates/base.html deleted file mode 100644 index 173320d..0000000 --- a/Unidad 3/Flask/templates/base.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - ToukenCafe - - - - {% with messages = get_flashed_messages() %} - {% if messages %} -
    - {% for message in messages %} -
  • {{ message }}
  • - {% endfor %} -
- {% endif %} - {% endwith %} - {% block content %}{% endblock %} - - - \ No newline at end of file diff --git a/Unidad 3/Flask/templates/crear_inventario.html b/Unidad 3/Flask/templates/crear_inventario.html deleted file mode 100644 index 6c1d833..0000000 --- a/Unidad 3/Flask/templates/crear_inventario.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -

Touken Café-Crear Nuevo Inventario

-

Ingrese los datos de sus productos:

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
- -{% if inventario_creado %} -

Inventario Creado:

- - - - - - - - - - - - - {% for producto in inventario_creado %} - - - - - - - - - {% endfor %} - -
CódigoNombreDemandaTiempo EntregaFecha LímiteCantidad
{{ producto.codigo }}{{ producto.nombre }}{{ producto.demanda }}{{ producto.tiempo_entrega }}{{ producto.fecha_limite }}{{ producto.cantidad }}
-
- -
-{% endif %} - -
-

Volver a la página principal

-{% endblock %} \ No newline at end of file diff --git a/Unidad 3/Flask/templates/pedir_proveedor.html b/Unidad 3/Flask/templates/pedir_proveedor.html deleted file mode 100644 index 1389689..0000000 --- a/Unidad 3/Flask/templates/pedir_proveedor.html +++ /dev/null @@ -1,40 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -

Pedido al Proveedor

-{% if productos_proveedor %} -
- - - - - - - - - - {% for producto in productos_proveedor %} - - - - - - - - - {% endfor %} -
CódigoProductoCantidad en StockTiempo Entrega (Días)Cantidad a PedirPrecio Unitario
{{ producto['Código'] }}{{ producto['Nombre'] }}{{ producto['Cantidad'] }}{{ producto['Tiempo entrega(Días)'] }} - - - -
- -
-{% else %} -

No hay productos en el inventario. Por favor, sube el archivo CSV.

-

Volver a la página de subida

-{% endif %} -
-

Volver a la página principal / Subir otro archivo

-

Ir a Pedido de Stock Interno

-{% endblock %} \ No newline at end of file diff --git a/Unidad 3/Flask/templates/pedir_stock.html b/Unidad 3/Flask/templates/pedir_stock.html deleted file mode 100644 index c47245d..0000000 --- a/Unidad 3/Flask/templates/pedir_stock.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -

Pedido de Stock Interno

- -{% with messages = get_flashed_messages() %} - {% if messages %} -
    - {% for message in messages %} -
  • {{ message }}
  • - {% endfor %} -
- {% endif %} -{% endwith %} - -{% if productos_stock %} -
- - - - - - - - - {% for producto in productos_stock %} - - - - - - - - {% endfor %} -
CódigoProductoCantidad en StockDemanda ActualCantidad a Pedir
{{ producto['Código'] }}{{ producto['Nombre'] }}{{ producto['Cantidad'] }}{{ producto['Demanda'] }} - -
- -
-{% else %} -

No hay productos en el inventario. Por favor, sube el archivo CSV.

-

Volver a la página de subida

-{% endif %} - -
-

Volver a la página principal / Subir otro archivo

-

Exportar Inventario a JSON

-

Exportar Inventario a CSV

-

Ir a Pedido al Proveedor

- -{% endblock %} \ No newline at end of file diff --git a/Unidad 3/Flask/templates/subir_csv.html b/Unidad 3/Flask/templates/subir_csv.html deleted file mode 100644 index 5ca2d7e..0000000 --- a/Unidad 3/Flask/templates/subir_csv.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -

Bienvenido a Touken Café - Gestión de Pedidos

-

Por favor, sube el archivo CSV de inventario generado previamente.

-
- - -
- -
-

¿No tienes un archivo CSV? ¡Crealo ahora mismo!

-

Crear inventario manualmente

- -{% if session['productos'] %} -

Selecciona el tipo de pedido que deseas realizar:

- -{% endif %} -{% endblock %} \ No newline at end of file diff --git a/Unidad 3/Flask/uploads/registro_inventario_prueba.csv b/Unidad 3/Flask/uploads/registro_inventario_prueba.csv deleted file mode 100644 index 029cfab..0000000 --- a/Unidad 3/Flask/uploads/registro_inventario_prueba.csv +++ /dev/null @@ -1,5 +0,0 @@ -Fecha Consulta,Código,Nombre,Demanda,Tiempo entrega(Días),Fecha límite,Cantidad -2025-04-24 17:53:58,3,Lechuga,6,2,2025-04-22,7 -2025-04-24 17:53:58,2,Banana,7,5,2025-04-25,2 -2025-04-24 17:53:58,1,Manzana,8,3,2025-04-28,15 -2025-04-24 17:53:58,4,Tomate,9,4,2025-05-01,3 diff --git a/Unidad-7/Items-Management.py b/Unidad-7/Items-Management.py deleted file mode 100644 index c08bd38..0000000 --- a/Unidad-7/Items-Management.py +++ /dev/null @@ -1,246 +0,0 @@ -import sys -# sys se importa solo como ejemplo para manejo de errores generales, -# aunque no es estrictamente necesario para la lógica central del programa. - -# ==================================================================== -# CLASE PRODUCTO (Modelos) -# ==================================================================== - -class Producto: - def __init__(self, nombre, precio, cantidad, codigo): - # Al asignar a self.atributo, se llama automáticamente al setter - self.nombre = nombre - self.precio = precio - self.cantidad = cantidad - self.codigo = codigo - - def __del__(self): - # Método para demostrar la eliminación de la referencia (no garantizado que se ejecute al instante) - print(f"Objeto {self.codigo} - {self.nombre} eliminado") - - def __str__(self): - # Representación legible del objeto - return f"Código: {self.codigo}\n Producto: {self.nombre}\n Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}" - - # Métodos dunder de comparación - def __lt__(self, otro): - if not isinstance(otro, Producto): - return NotImplemented - return self.precio < otro.precio # Compara por precio - - def __eq__(self, otro): - if not isinstance(otro, Producto): - return NotImplemented - return self.codigo == otro.codigo # Compara por unicidad del código - - # --- Propiedades (Getters y Setters con validación) --- - # Usamos __ para los atributos internos para indicar que son privados - - @property - def nombre(self): - return self.__nombre - - @nombre.setter - def nombre(self, nombre): - if not isinstance(nombre, str) or not nombre.strip(): - raise ValueError("El nombre del producto debe ser una cadena no vacía.") - self.__nombre = nombre - - @property - def precio(self): - return self.__precio - - @precio.setter - def precio(self, precio): - if not isinstance(precio, (int, float)) or precio <= 0: - raise ValueError("El precio debe ser un número positivo mayor que cero.") - self.__precio = precio - - @property - def cantidad(self): - return self.__cantidad - - @cantidad.setter - def cantidad(self, cantidad): - if not isinstance(cantidad, int) or cantidad < 0: - raise ValueError("La cantidad debe ser un número entero no negativo.") - self.__cantidad = cantidad - - @property - def codigo(self): - return self.__codigo - - @codigo.setter - def codigo(self, codigo): - if not isinstance(codigo, str) or not codigo.strip(): - raise ValueError("El código del producto debe ser una cadena no vacía.") - self.__codigo = codigo - - -# ==================================================================== -# CLASE INVENTARIO (Servicios/Manager) -# ==================================================================== - -class Inventario: - def __init__(self): - self.productos = {} # Diccionario: clave=código (str), valor=objeto Producto - - def agg_prod(self, producto): - if not isinstance(producto, Producto): - print("Error: Solo se pueden agregar objetos de tipo 'Producto' al inventario.") - return - - if producto.codigo in self.productos: - print(f"Error: El producto con código '{producto.codigo}' ya existe en el inventario.") - return - - self.productos[producto.codigo] = producto - print(f"✅ Éxito: Producto '{producto.nombre}' (Código: {producto.codigo}) agregado.") - - def imprimir(self): - if not self.productos: - print("El inventario está vacío. No hay productos para mostrar.") - return - - print("\n--- Listado de Productos en Inventario ---") - # Itera sobre los valores (objetos Producto) del diccionario - for producto_obj in self.productos.values(): - print(producto_obj) - print("-" * 30) - print("--- Fin del Listado ---") - - def buscar_producto(self): - id_search = input("Ingrese el código del producto a buscar: ").strip() - - if id_search in self.productos: - # Acceso directo al objeto Producto por su código - resultado_producto = self.productos[id_search] - print("\n✅ ¡Producto Encontrado!") - print(resultado_producto) - print("--------------------------\n") - else: - print(f"❌ Error: El producto con el código {id_search} no se encuentra en el inventario.") - - def actualizar_cantidad(self): - id_search = input("Ingrese el código del producto a actualizar: ").strip() - - if id_search not in self.productos: - print(f"❌ Error: Producto con código '{id_search}' no encontrado.") - return - - producto_a_actualizar = self.productos[id_search] - - # Lógica de validación de entrada - while True: - try: - # Muestra el nombre del producto que se está actualizando - new_cantidad_str = input(f"Ingrese la nueva cantidad para '{producto_a_actualizar.nombre}' (actual: {producto_a_actualizar.cantidad}): ").strip() - new_cantidad = int(new_cantidad_str) - - # La validación de new_cantidad < 0 ya la tienes en el setter, pero es buena práctica - # validar antes de la asignación para mejorar el feedback al usuario. - if new_cantidad < 0: - print("❌ Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo.") - continue - - break - - except ValueError: - print("❌ Error de Valor: Se espera una cantidad numérica entera.") - except Exception as e: - print(f"Ocurrió un error inesperado al leer la nueva cantidad: {e}") - - # Asigna la nueva cantidad (llamando al setter de Producto) - producto_a_actualizar.cantidad = new_cantidad - print(f"✅ Éxito: Cantidad del producto '{producto_a_actualizar.nombre}' actualizada a {new_cantidad}.") - - def eliminar_producto(self): - id_search = input("Ingrese el código del producto a eliminar: ").strip() - - if id_search not in self.productos: - print(f"❌ Error: Producto con código '{id_search}' no encontrado.") - return - - # Opcional: Mostrar nombre antes de eliminar - nombre_producto = self.productos[id_search].nombre - - del self.productos[id_search] # Elimina la referencia del diccionario - print(f"✅ Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario.") - - -# ==================================================================== -# MENÚ PRINCIPAL Y LÓGICA DE LA APLICACIÓN -# ==================================================================== - -def obtener_datos_producto(): - """Solicita los datos del producto al usuario con validación.""" - try: - nombre = input("Nombre del producto: ").strip() - # Intentamos obtener y convertir precio y cantidad - precio = float(input("Precio del producto: ")) - cantidad = int(input("Cantidad del producto: ")) - codigo = input("Código del producto: ").strip() - - # Creamos la instancia de Producto. Las validaciones de los setters se ejecutan aquí. - return Producto(nombre, precio, cantidad, codigo) - - except ValueError as e: - # Captura errores si float() o int() fallan, o si un setter lanza ValueError - print(f"❌ Error de entrada de datos: {e}. Asegúrate de ingresar tipos y valores correctos.") - return None - except Exception as e: - print(f"❌ Ocurrió un error inesperado al obtener los datos: {e}") - return None - -def main_menu(): - """Ejecuta el menú interactivo.""" - mi_inventario = Inventario() - - while True: - print("\n--- Sistema de Gestión de Inventario (Monolítico) ---") - print("1. Agregar producto") - print("2. Buscar producto") - print("3. Actualizar cantidad") - print("4. Eliminar producto") - print("5. Listar todos los productos") - print("6. Salir") - print("-----------------------------------------------------") - - try: - opcion = input("Selecciona una opción (1-6): ").strip() - - if opcion == '1': - print("\n>>> Agregar Producto <<<") - nuevo_producto = obtener_datos_producto() - if nuevo_producto: - mi_inventario.agg_prod(nuevo_producto) - - elif opcion == '2': - print("\n>>> Buscar Producto <<<") - mi_inventario.buscar_producto() - - elif opcion == '3': - print("\n>>> Actualizar Cantidad <<<") - mi_inventario.actualizar_cantidad() - - elif opcion == '4': - print("\n>>> Eliminar Producto <<<") - mi_inventario.eliminar_producto() - - elif opcion == '5': - print("\n>>> Listar Productos <<<") - mi_inventario.imprimir() - - elif opcion == '6': - print("\nSaliendo del sistema de inventario. ¡Hasta pronto! 👋") - break - - else: - print("⚠️ Opción no válida. Por favor, selecciona un número del 1 al 6.") - - except Exception as e: - print(f"❌ Ocurrió un error inesperado en el menú: {e}") - -# Punto de entrada de la aplicación -if __name__ == "__main__": - main_menu() \ No newline at end of file diff --git a/Unidad-7/PystockManagement/main.py b/Unidad-7/PystockManagement/main.py deleted file mode 100644 index 613e659..0000000 --- a/Unidad-7/PystockManagement/main.py +++ /dev/null @@ -1,83 +0,0 @@ -from src.services.inventario_manager import Inventario -from src.models.productos import Producto - -def obtener_datos_producto(): - """Solicita los datos del producto al usuario con validación.""" - try: - nombre = input("Nombre del producto: ").strip() - # Intentamos obtener y convertir precio y cantidad - precio = float(input("Precio del producto: ")) - cantidad = int(input("Cantidad del producto: ")) - codigo = input("Código del producto: ").strip() - - # Creamos la instancia de Producto. - # Las validaciones de los setters se ejecutan aquí. - return Producto(nombre, precio, cantidad, codigo) - - except ValueError as e: - # Captura errores si float() o int() fallan - # O si un setter lanza ValueError - print( - f"Error de entrada de datos: {e}") - print("Asegúrate de ingresar tipos y valores correctos.") - return None - except Exception as e: - print(f"Ocurrió un error inesperado al obtener los datos: {e}") - return None - - -def main_menu(): - """Ejecuta el menú interactivo.""" - mi_inventario = Inventario() - - while True: - print("\n--- Sistema de Gestión de Inventario (Monolítico) ---") - print("1. Agregar producto") - print("2. Buscar producto") - print("3. Actualizar cantidad") - print("4. Eliminar producto") - print("5. Listar todos los productos") - print("6. Salir") - print("-----------------------------------------------------") - - try: - opcion = input("Selecciona una opción (1-6): ").strip() - - if opcion == '1': - print("\n>>> Agregar Producto <<<") - nuevo_producto = obtener_datos_producto() - if nuevo_producto: - mi_inventario.agg_prod(nuevo_producto) - - elif opcion == '2': - print("\n>>> Buscar Producto <<<") - mi_inventario.buscar_producto() - - elif opcion == '3': - print("\n>>> Actualizar Cantidad <<<") - mi_inventario.actualizar_cantidad() - - elif opcion == '4': - print("\n>>> Eliminar Producto <<<") - mi_inventario.eliminar_producto() - - elif opcion == '5': - print("\n>>> Listar Productos <<<") - mi_inventario.imprimir() - - elif opcion == '6': - mi_inventario.save_stock() - print("\nSaliendo del sistema de inventario. ¡Hasta pronto!") - break - - else: - print("Opción no válida.") - print("Por favor, selecciona un número del 1 al 6.") - - except Exception as e: - print(f" Ocurrió un error inesperado en el menú: {e}") - - -# Punto de entrada de la aplicación -if __name__ == "__main__": - main_menu() diff --git a/Unidad-7/PystockManagement/src/models/productos.py b/Unidad-7/PystockManagement/src/models/productos.py deleted file mode 100644 index f49a173..0000000 --- a/Unidad-7/PystockManagement/src/models/productos.py +++ /dev/null @@ -1,74 +0,0 @@ -class Producto: - def __init__(self, nombre, precio, cantidad, codigo): - # Al asignar a self.atributo, se llama automáticamente al setter - self.nombre = nombre - self.precio = precio - self.cantidad = cantidad - self.codigo = codigo - - def __del__(self): - # Método para demostrar la eliminación de la referencia - # (no garantizado que se ejecute al instante) - print(f"Objeto {self.codigo} - {self.nombre} eliminado") - - def __str__(self): - # Representación legible del objeto - return f"Código: {self.codigo}\n Producto: {self.nombre}\n Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}" - - # Métodos dunder de comparación - def __lt__(self, otro): - if not isinstance(otro, Producto): - return NotImplemented - return self.precio < otro.precio # Compara por precio - - def __eq__(self, otro): - if not isinstance(otro, Producto): - return NotImplemented - return self.codigo == otro.codigo # Compara por unicidad del código - - # --- Propiedades (Getters y Setters con validación) --- - # Usamos __ para los atributos internos para indicar que son privados - - @property - def nombre(self): - return self.__nombre - - @nombre.setter - def nombre(self, nombre): - if not isinstance(nombre, str) or not nombre.strip(): - raise ValueError( - "El nombre del producto debe ser una cadena no vacía.") - self.__nombre = nombre - - @property - def precio(self): - return self.__precio - - @precio.setter - def precio(self, precio): - if not isinstance(precio, (int, float)) or precio <= 0: - raise ValueError( - "El precio debe ser un número positivo mayor que cero.") - self.__precio = precio - - @property - def cantidad(self): - return self.__cantidad - - @cantidad.setter - def cantidad(self, cantidad): - if not isinstance(cantidad, int) or cantidad < 0: - raise ValueError( - "La cantidad debe ser un número entero no negativo.") - self.__cantidad = cantidad - - @property - def codigo(self): - return self.__codigo - - @codigo.setter - def codigo(self, codigo): - if not isinstance(codigo, str) or not codigo.strip(): - raise ValueError( - "El código del producto debe ser una cadena no vacía.") - self.__codigo = codigo diff --git a/Unidad-7/PystockManagement/src/services/inventario_manager.py b/Unidad-7/PystockManagement/src/services/inventario_manager.py deleted file mode 100644 index 3cf8afc..0000000 --- a/Unidad-7/PystockManagement/src/services/inventario_manager.py +++ /dev/null @@ -1,154 +0,0 @@ -from ..models.productos import Producto -import os -import json - - -class Inventario: - def __init__(self, file_name='data_stock.json'): - # Crear referencia de ruta - current_dir = os.path.dirname(__file__) - base_dir = os.path.join(current_dir, "..", "..", "models") - # Crear archio en la ruta indicada y nombre declarado - self.route_file = os.path.join(base_dir, file_name) - # Asegurar que exista la ruta - if not os.path.exists(base_dir): - os.makedirs(base_dir) # Si la ruta NO existe ENTONCES la crea - # Diccionario: clave=código (str), valor=objeto Producto - self.productos = self.load_stock() or {} - - def load_stock(self): - """Cargar datos del JSON en data""" - if os.path.exists(self.route_file): - try: - with open(self.route_file, 'r') as dicc: - data_loaded = json.load(dicc) - stock_loaded = {} - for codigo, data in data_loaded.items(): - # Aquí debes asegurarte de que los datos tengan en - # los campos necesarios - stock_loaded[codigo] = Producto( - data['nombre'], - data['precio'], - data['cantidad'], - data['codigo'] - ) - return stock_loaded - except (json.JSONDecodeError, FileNotFoundError): - print( - f"Error al cargar el inventario desde '{self.route_file}'") - print("Se iniciara un inventario Vacío") - return None - - def save_stock(self): - """Guardar inventario actual en sesión""" - # Convertir objetos en Producto a un diccionario básico - save_data = {} - for codigo, producto_obj in self.productos.items(): - save_data[codigo] = { - 'codigo': producto_obj.codigo, - 'nombre': producto_obj.nombre, - 'precio': producto_obj.precio, - 'cantidad': producto_obj.cantidad - } - try: - with open(self.route_file, 'w') as f: - json.dump(save_data, f, indent=4) - print(f"Exito: Inventario guardado en '{self.route_file}'") - except Exception as e: - print( - f"Error: No fue posible guardar el inventario en '{self.route_file}'") - print("/n") - print(f"Error '{e}'") - - def agg_prod(self, producto): - if not isinstance(producto, Producto): - print( - "Error: Solo se pueden agregar objetos de tipo 'Producto' al inventario.") - return - - if producto.codigo in self.productos: - print( - f"Error: El producto con código '{producto.codigo}' ya existe en el inventario.") - return - - self.productos[producto.codigo] = producto - print( - f" Éxito: Producto '{producto.nombre}' (Código: {producto.codigo}) agregado.") - - def imprimir(self): - if not self.productos: - print("El inventario está vacío. No hay productos para mostrar.") - return - - print("\n--- Listado de Productos en Inventario ---") - # Itera sobre los valores (objetos Producto) del diccionario - for producto_obj in self.productos.values(): - print(producto_obj) - print("-" * 30) - print("--- Fin del Listado ---") - - def buscar_producto(self): - id_search = input("Ingrese el código del producto a buscar: ").strip() - - if id_search in self.productos: - # Acceso directo al objeto Producto por su código - resultado_producto = self.productos[id_search] - print("\n ¡Producto Encontrado!") - print(resultado_producto) - print("--------------------------\n") - else: - print( - f" Error: El producto con el código {id_search} no se encuentra en el inventario.") - - def actualizar_cantidad(self): - id_search = input( - "Ingrese el código del producto a actualizar: ").strip() - - if id_search not in self.productos: - print(f" Error: Producto con código '{id_search}' no encontrado.") - return - - producto_a_actualizar = self.productos[id_search] - - # Lógica de validación de entrada - while True: - try: - # Muestra el nombre del producto que se está actualizando - new_cantidad_str = input( - f"Ingrese la nueva cantidad para '{producto_a_actualizar.nombre}' (actual: {producto_a_actualizar.cantidad}): ").strip() - new_cantidad = int(new_cantidad_str) - - # La validación de new_cantidad < 0 ya la tienes en el setter, pero es buena práctica - # validar antes de la asignación para mejorar el feedback al usuario. - if new_cantidad < 0: - print( - " Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo.") - continue - - break - - except ValueError: - print(" Error de Valor: Se espera una cantidad numérica entera.") - except Exception as e: - print( - f"Ocurrió un error inesperado al leer la nueva cantidad: {e}") - - # Asigna la nueva cantidad (llamando al setter de Producto) - producto_a_actualizar.cantidad = new_cantidad - print( - f" Éxito: Cantidad del producto '{producto_a_actualizar.nombre}' actualizada a {new_cantidad}.") - - def eliminar_producto(self): - id_search = input( - "Ingrese el código del producto a eliminar: ").strip() - - if id_search not in self.productos: - print(f" Error: Producto con código '{id_search}' no encontrado.") - return - - # Opcional: Mostrar nombre antes de eliminar - nombre_producto = self.productos[id_search].nombre - - del self.productos[id_search] # Elimina la referencia del diccionario - print( - f" Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario.") diff --git a/Wolves-Page/package.json b/Wolves-Page/package.json index 15da0f2..34eb37d 100644 --- a/Wolves-Page/package.json +++ b/Wolves-Page/package.json @@ -4,49 +4,35 @@ "private": true, "homepage": ".", "dependencies": { - "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^16.3.0", - "@testing-library/user-event": "^13.5.0", - "framer-motion": "^12.23.24", - "node": "^25.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-icons": "^5.5.0", "react-router-dom": "^7.9.5", - "react-scripts": "5.0.1", + "react-icons": "^5.5.0", + "framer-motion": "^12.23.24", "web-vitals": "^2.1.4" }, "engines": { "node": "22.x" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] + "start": "webpack serve --config webpack.config.js --mode development", + "build": "webpack --config webpack.config.js --mode production", + "test": "jest" }, "devDependencies": { - "autoprefixer": "^10.4.21", + "@babel/core": "^7.28.5", + "@babel/preset-env": "^7.28.5", + "@babel/preset-react": "^7.28.5", + "babel-loader": "^10.0.0", + "css-loader": "^7.1.2", + "style-loader": "^4.0.0", "postcss": "^8.5.6", - "tailwindcss": "^3.4.18" + "postcss-loader": "^8.2.0", + "tailwindcss": "^3.4.18", + "autoprefixer": "^10.4.21", + "html-webpack-plugin": "^5.6.4", + "webpack": "^5.102.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.2.2" } -} +} \ No newline at end of file diff --git a/Wolves-Page/public/index.html b/Wolves-Page/public/index.html index aa069f2..0b5daef 100644 --- a/Wolves-Page/public/index.html +++ b/Wolves-Page/public/index.html @@ -2,42 +2,16 @@ - + - - - - - - - React App + + + + + Wolves Page - +
- - + \ No newline at end of file diff --git a/Wolves-Page/webpack.config.js b/Wolves-Page/webpack.config.js new file mode 100644 index 0000000..14a183e --- /dev/null +++ b/Wolves-Page/webpack.config.js @@ -0,0 +1,51 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +module.exports = { + mode: process.env.NODE_ENV || 'development', + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'bundle.[contenthash].js', + clean: true, + publicPath: '/', + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, // React y JS moderno + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env', '@babel/preset-react'], + }, + }, + }, + { + test: /\.css$/i, // Tailwind + PostCSS + use: ['style-loader', 'css-loader', 'postcss-loader'], + }, + { + test: /\.(png|jpe?g|gif|svg)$/i, // imágenes + type: 'asset/resource', + }, + ], + }, + resolve: { + extensions: ['.js', '.jsx'], + }, + plugins: [ + new HtmlWebpackPlugin({ + template: './public/index.html', + favicon: './public/favicon.ico', + }), + ], + devServer: { + static: path.resolve(__dirname, 'public'), + hot: true, + port: 3000, + historyApiFallback: true, // soporte para react-router-dom + allowedHosts: 'all' + }, +}; \ No newline at end of file diff --git a/scripts/concat_readmes.py b/scripts/concat_readmes.py new file mode 100644 index 0000000..73d837e --- /dev/null +++ b/scripts/concat_readmes.py @@ -0,0 +1,56 @@ +""" +Script para concatenar todos los archivos README.md encontrados en los +subdirectorios del repositorio en un único archivo GLOBAL.md. + +Esto facilita la visualización o el procesamiento del contenido completo. +""" + +import os + +# C0103: Las variables que actúan como constantes (no cambian durante la ejecución) +# deben nombrarse en MAYÚSCULAS_CON_GUION_BAJO (UPPER_CASE). + +# Ruta base del repositorio (punto actual) +REPO_PATH = "." + +# Archivo de salida +OUTPUT_FILE = "GLOBAL.md" + +try: + with open(OUTPUT_FILE, "w", encoding="utf-8") as global_file: + # os.walk busca archivos recursivamente + for root, dirs, files in os.walk(REPO_PATH): + # Ignorar carpetas ocultas y la carpeta .git (mejora de la condición original) + if any(part.startswith(".") for part in root.split(os.sep)): + continue + + # Si existe un README.md en la carpeta, lo añadimos + if "README.md" in files: + # El nombre del proyecto es el nombre del directorio actual + PROJECT_NAME = os.path.basename(root) + + # Manejar el caso de la raíz (donde project_name puede ser vacío o ".") + if PROJECT_NAME in ("", "."): + PROJECT_NAME = "REPOSITORIO PRINCIPAL" + + # Escribir título del proyecto + global_file.write(f"# {PROJECT_NAME}\n\n") + + # Escribir contenido del README + readme_path = os.path.join(root, "README.md") + + try: + with open(readme_path, encoding="utf-8") as readme_file: + global_file.write(readme_file.read()) + global_file.write("\n\n") + except IOError as e: + # Manejo específico de error si un README no se puede abrir + global_file.write(f"ERROR: No se pudo leer {readme_path}. {e}\n\n") + + # Separador entre proyectos + global_file.write("---\n\n") + + print(f"Archivo {OUTPUT_FILE} generado con todos los README.md") + +except IOError as e: + print(f"Error al intentar escribir el archivo global {OUTPUT_FILE}: {e}") diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py new file mode 100644 index 0000000..6a3ed8e --- /dev/null +++ b/scripts/scan_projects.py @@ -0,0 +1,61 @@ +""" +Script para escanear el repositorio en busca de proyectos. + +Un proyecto se define como un directorio que contiene un archivo README.md. +El script genera un archivo `projects.json` con metadatos de cada proyecto, +incluyendo el nombre, la ruta del README y la ruta de un posible icono. +""" + +import os +import json + +# C0103: Las variables que actúan como constantes (no cambian durante la ejecución) +# deben nombrarse en MAYÚSCULAS_CON_GUION_BAJO (UPPER_CASE). + +# Ruta base del repositorio (punto actual) +REPO_PATH = "." + +PROJECTS = [] # C0103: Renombrado a constante (aunque se llena, su inicialización es constante) + +for root, dirs, files in os.walk(REPO_PATH): + # Ignorar carpetas ocultas y la carpeta .git + if any(part.startswith(".") for part in root.split(os.sep)): + continue + + # Detectar proyectos: carpeta que contiene un README.md + if "README.md" in files: + PROJECT_NAME = os.path.basename(root) + + # Ignorar la raíz del repositorio si no tiene un nombre significativo + if PROJECT_NAME in ("", "."): + continue + + # Buscar icono si existe + ICON_FILE = None + for ext in [".png", ".jpg", ".jpeg", ".svg"]: + CANDIDATE = f"icon{ext}" + if CANDIDATE in files: + ICON_FILE = os.path.join(root, CANDIDATE) + break + + # Construir objeto del proyecto + PROJECT = { + "id": PROJECT_NAME.lower(), + "title": PROJECT_NAME, + "readme": os.path.join(root, "README.md"), + "icon": ICON_FILE, + # NOTA: Asegúrate de reemplazar USUARIO/REPO con los valores correctos + "url": f"https://github.com/WolveJC/ACADE-Prog/tree/main/{root}", + } + + PROJECTS.append(PROJECT) + +# Guardar en projects.json +OUTPUT_FILE = "projects.json" # C0103: Definido como constante +try: + with open(OUTPUT_FILE, "w", encoding="utf-8") as file: + json.dump(PROJECTS, file, indent=2, ensure_ascii=False) + + print(f" Generados {len(PROJECTS)} proyectos en {OUTPUT_FILE}") +except IOError as e: + print(f"Error al escribir el archivo {OUTPUT_FILE}: {e}") diff --git a/tests/unidad_1/test_.py b/tests/unidad_1/test_.py new file mode 100644 index 0000000..2465643 --- /dev/null +++ b/tests/unidad_1/test_.py @@ -0,0 +1,45 @@ +""" +Módulo de pruebas unitarias para la función list_arch del módulo +unidad_1/listar_archivos_drive.py. + +Utiliza unittest.mock para simular las llamadas al sistema de archivos +(os.listdir, os.path.isfile, os.path.getmtime) y garantizar que la función +list_arch ordena correctamente los archivos por tiempo de modificación. +""" +import unittest +from unittest.mock import patch +# Se asume que la estructura de tu proyecto es correcta para esta importación +from unidad_1.listar_archivos_drive import list_arch + + +class TestListArch(unittest.TestCase): + """ + Contiene las pruebas unitarias para la función list_arch. + """ + @patch("os.listdir") + @patch("os.path.isfile") + @patch("os.path.getmtime") + def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir): + """ + Verifica que list_arch ordena correctamente los archivos por tiempo de + modificación (mtime) de forma descendente (el más reciente primero). + + Mocks: + - mock_listdir: Simula la lista de archivos en el directorio. + - mock_isfile: Simula que todos los elementos son archivos. + - mock_getmtime: Simula los tiempos de modificación (timestamps). + """ + # Configuramos los valores simulados + mock_listdir.return_value = ["a.txt", "b.txt", "c.txt"] + mock_isfile.return_value = True + # Asignamos mtimes a [a.txt, b.txt, c.txt] -> [100, 300, 200] + mock_getmtime.side_effect = [100, 300, 200] + + # Ejecución de la función bajo prueba + result = list_arch("fake_dir") + + # El orden esperado debe ser el más reciente primero (300, 200, 100) + expected = [("b.txt", 300), ("c.txt", 200), ("a.txt", 100)] + + # Aserción + self.assertEqual(result, expected) \ No newline at end of file diff --git a/Unidad-7/PystockManagement/__init__.py b/unidad_1/__init__.py similarity index 100% rename from Unidad-7/PystockManagement/__init__.py rename to unidad_1/__init__.py diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py new file mode 100644 index 0000000..5134114 --- /dev/null +++ b/unidad_1/listar_archivos_drive.py @@ -0,0 +1,114 @@ +""" +Módulo para listar y ordenar archivos por tiempo de modificación +(mtime) dentro de un directorio, con especial enfoque en entornos +como Google Drive (vía DIRUTH). +""" +import os +import time +import unittest +from unittest.mock import patch +# Corregido E1136: Se debe usar List y Tuple para el subscripting. +from typing import Union, List, Tuple + +# Definir el directorio +DIRUTH = "/content/drive/My Drive/" + + +def list_arch(directory: str) -> Union[List[Tuple[str, float]], None]: + """ + Lista los archivos en un directorio dado, excluye directorios y los + ordena de forma descendente por la fecha de última modificación (mtime). + + Args: + directory (str): La ruta del directorio a escanear. + + Returns: + Union[List[Tuple[str, float]], None]: Una lista de tuplas + (nombre_archivo, mtime_timestamp) ordenada, o None si ocurre un error. + """ + file_dates: List[Tuple[str, float]] = [] # Añadida anotación de tipo + + try: + files = os.listdir(directory) + + for file in files: + file_path = os.path.join(directory, file) + + if os.path.isfile(file_path): + modi_time = os.path.getmtime(file_path) + file_dates.append((file, modi_time)) + + # Ordenar por timestamp (índice 1 de la tupla) de forma descendente (más reciente primero) + file_dates.sort(key=lambda x: x[1], reverse=True) + + print("\nArchivos en el directorio ordenados por fecha de subida:") + for file, modi_time in file_dates: + print(f"{file} - {time.ctime(modi_time)}") + + return file_dates + + except FileNotFoundError: + print("Directorio no encontrado.") + return None # R1710: Asegurar que se devuelve None en caso de error + + except PermissionError: + print("No tienes permiso para acceder a este directorio.") + return None # R1710: Asegurar que se devuelve None en caso de error + + # Se mantiene la captura general ya que el propósito del script + # es manejar errores de I/O impredecibles en el sistema de archivos. + except OSError as e: + print(f"Ocurrió un error de E/S inesperado: {e}") + return None + + +# ============================ +# Pruebas unitarias +# ============================ + + +class TestListArch(unittest.TestCase): + """ + Contiene las pruebas unitarias para la función list_arch. + """ + + @patch("os.listdir") + @patch("os.path.isfile") + @patch("os.path.getmtime") + def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir): + """ + Verifica que list_arch ordena correctamente los archivos por tiempo de + modificación (mtime) de forma descendente. + """ + mock_listdir.return_value = ["a.txt", "b.txt", "c.txt"] + mock_isfile.return_value = True + # mtimes: a=100, b=300, c=200. Orden esperado: b, c, a. + mock_getmtime.side_effect = [100, 300, 200] + + result = list_arch("fake_dir") + expected = [("b.txt", 300), ("c.txt", 200), ("a.txt", 100)] + self.assertEqual(result, expected) + + @patch("os.listdir", side_effect=FileNotFoundError) + def test_directory_not_found(self, _): # <--- W0613 Corregido: Argumento renombrado a '_' + """ + Verifica que list_arch maneja correctamente la excepción FileNotFoundError + y devuelve None. + + Se usa '_' para indicar que el mock está presente solo para el side_effect + del decorador, resolviendo W0613. + """ + result = list_arch("missing_dir") + self.assertIsNone(result) + + +# ============================ +# Ejecutar pruebas si es main +# ============================ + +if __name__ == "__main__": + # Si quisieras ejecutar la función real primero: + # list_arch(DIRUTH) + + # Ejecuta las pruebas unitarias + unittest.main() \ No newline at end of file diff --git a/Unidad 2/ReadMe.md b/unidad_2/ReadMe.md similarity index 100% rename from Unidad 2/ReadMe.md rename to unidad_2/ReadMe.md diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py new file mode 100644 index 0000000..6e20412 --- /dev/null +++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py @@ -0,0 +1,378 @@ +""" +Sistema de Gestión de Inventario Interactivo (v1.2 - Múltiples Tablas). + +Permite el ingreso interactivo de productos, aplica cuatro algoritmos de +ordenamiento a criterios clave (cantidad, tiempo, fecha, demanda) y genera +una visualización tabular (Matplotlib) y un archivo de registro CSV para +cada uno de los criterios. +""" + +# Standard library +import csv +import datetime +import random +import string +import sys +from typing import Union, List, Any + +# Third-party libraries +import matplotlib.pyplot as plt + +# ------------------------------------------ +# 1. Funciones Auxiliares +# ------------------------------------------ + + +def generar_codigo_aleatorio(longitud: int = 6) -> str: + """ + Genera un código alfanumérico aleatorio. + ... + """ + caracteres = string.ascii_uppercase + string.digits + return "".join(random.choices(caracteres, k=longitud)) + + +def _obtener_entero_valido(prompt: str) -> Union[int, None]: + """Función auxiliar para obtener una entrada entera válida.""" + try: + return int(input(prompt).strip()) + except ValueError: + print("Entrada inválida. Se espera un número entero.") + return None + + +def _obtener_fecha_valida(prompt: str, formato: str = "%Y-%m-%d") -> Union[str, None]: + """Función auxiliar para obtener una fecha válida.""" + fecha_str = input(prompt).strip() + try: + datetime.datetime.strptime(fecha_str, formato) + return fecha_str + except ValueError: + print(f"Fecha inválida. Asegúrate de ingresar en el formato {formato}.") + return None + + +# ------------------------------------------ +# Ingreso interactivo de productos +# ------------------------------------------ +def ingresar_productos(lista_inventario: list): + """Permite al usuario ingresar productos de manera interactiva.""" + while True: + print("\n--- Ingrese los datos del nuevo producto ---") + + # 1. Código + codigo_input = input("Código (dejar vacío para auto-generar): ").strip() + if not codigo_input: + codigo = generar_codigo_aleatorio() + existentes = {p["codigo"] for p in lista_inventario} + while codigo in existentes: + codigo = generar_codigo_aleatorio() + print(f"Se generó el código automático: {codigo}") + else: + codigo = codigo_input + if codigo in {p["codigo"] for p in lista_inventario}: + print(f"Error: El código '{codigo}' ya existe.") + continue + + nombre = input("Nombre del producto: ").strip() + if not nombre: + print("El nombre no puede estar vacío.") + continue + + # 2. Demanda + demanda = _obtener_entero_valido("Demanda (valor entre 1-10): ") + if ( + demanda is None + or demanda < 1 + or demanda > 10): + print("La demanda debe ser un número entre 1 y 10.") + continue + + # 3. Tiempo de entrega + tiempo_entrega = _obtener_entero_valido("Tiempo de entrega (en días): ") + if tiempo_entrega is None or tiempo_entrega < 0: + print("El tiempo de entrega no puede ser negativo.") + continue + + # 4. Fecha límite + fecha_limite = _obtener_fecha_valida("Fecha límite para consumo (YYYY-MM-DD): ") + if fecha_limite is None: + continue + + # 5. Cantidad + cantidad = _obtener_entero_valido("Cantidad en inventario: ") + if cantidad is None or cantidad < 0: + print("La cantidad en inventario no puede ser negativa.") + continue + + producto = { + "codigo": codigo, + "nombre": nombre, + "demanda": demanda, + "tiempo_entrega": tiempo_entrega, + "fecha_limite": fecha_limite, + "cantidad": cantidad, + } + lista_inventario.append(producto) + + continuar = input("¿Desea ingresar otro producto? (s/n): ").strip().lower() + if continuar != "s": + break + + +# ------------------------------------------ +# 2. Funciones de ordenamiento +# ------------------------------------------ +# ... (Funciones de ordenamiento sin cambios) ... + +def insertion_sort(lista: list, key: callable, descending: bool = False) -> list: + """ + Implementa el Insertion Sort, adaptable para orden ascendente o descendente. + ... + """ + for i in range(1, len(lista)): + actual = lista[i] + j = i - 1 + + while j >= 0: + # Lógica de comparación + comparacion = ( + (key(lista[j]) < key(actual)) if descending else (key(lista[j]) > key(actual)) + ) + + if comparacion: + lista[j + 1] = lista[j] + j -= 1 + else: + break + lista[j + 1] = actual + return lista + + +def bubble_sort(lista: list, key: callable, descending: bool = False) -> list: + """ + Implementa el algoritmo Bubble Sort. + ... + """ + n = len(lista) + swapped = True + while swapped: + swapped = False + for i in range(1, n): + comparacion = ( + (key(lista[i - 1]) < key(lista[i])) + if descending + else (key(lista[i - 1]) > key(lista[i])) + ) + + if comparacion: + lista[i - 1], lista[i] = lista[i], lista[i - 1] + swapped = True + n -= 1 + return lista + + +def quick_sort(lista: list, key: callable) -> list: + """ + Implementa el Quick Sort recursivo (orden ascendente). + ... + """ + if len(lista) <= 1: + return lista + + pivot = lista[0] + menos = [x for x in lista[1:] if key(x) < key(pivot)] + iguales = [x for x in lista if key(x) == key(pivot)] + mayor = [x for x in lista[1:] if key(x) >= key(pivot)] + + return quick_sort(menos, key) + iguales + quick_sort(mayor, key) + + +def selection_sort(lista: list, key: callable, reverse: bool = False) -> list: + """ + Implementa el Selection Sort (utilizado aquí de forma estable con lista temporal). + ... + """ + nueva_lista = [] + temp = lista.copy() + while temp: + candidato = temp[0] + indice_candidato = 0 + for i, item in enumerate(temp): + comparacion = (key(item) > key(candidato)) if reverse else (key(item) < key(candidato)) + + if comparacion: + candidato = item + indice_candidato = i + + nueva_lista.append(candidato) + temp.pop(indice_candidato) + return nueva_lista + + +# ------------------------------------------ +# 3. Generar listas ordenadas utilizando distintos algoritmos +# ------------------------------------------ + + +def generar_inventarios_ordenados(datos_originales: list) -> dict: + """Aplica los cuatro algoritmos de ordenamiento al inventario.""" + return { + "cantidad": insertion_sort( + datos_originales.copy(), key=lambda x: x["cantidad"], descending=False + ), + "tiempo": bubble_sort( + datos_originales.copy(), key=lambda x: x["tiempo_entrega"], descending=True + ), + "fecha": quick_sort( + datos_originales.copy(), + key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d"), + ), + "demanda": selection_sort( + datos_originales.copy(), + key=lambda x: x["demanda"], + reverse=True + ), + } + + +# ------------------------------------------ +# 4. Función para visualizar y guardar cada tabla (Refactorizado) +# ------------------------------------------ + + +def _generate_visual_table(data_matrix: List[List[Any]], title: str, headers: list, query_date: str, algorithm_key: str): + """Genera la tabla visual con Matplotlib y la guarda.""" + fig, ax = plt.subplots(figsize=(10, len(data_matrix) * 0.6 + 2)) + ax.axis("off") + tbl = ax.table(cellText=data_matrix, colLabels=headers, loc="center", cellLoc="center") + tbl.auto_set_font_size(False) + tbl.set_fontsize(10) + tbl.scale(1.2, 1.2) + + plt.title(title, fontsize=14) + plt.figtext(0.5, 0.01, f"Consulta generada: {query_date}", ha="center", fontsize=8) + + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + image_filename = f"inventario_{algorithm_key}_{timestamp}.png" + + try: + plt.savefig(image_filename, bbox_inches="tight") + plt.show() # Muestra la tabla + except IOError as e: + print(f" Error al guardar la imagen {image_filename}: {e}") + + plt.close(fig) + return image_filename + + +def _generate_csv_log(data_matrix: List[List[Any]], _: str, headers: list, query_date: str, algorithm_key: str): + """Guarda el registro de datos en un archivo CSV.""" + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + csv_filename = f"registro_{algorithm_key}_{timestamp}.csv" + + try: + with open(csv_filename, mode="w", newline="", encoding="utf-8") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(["Fecha Consulta"] + headers) + for row in data_matrix: + writer.writerow([query_date] + row) + except IOError as e: + print(f"Error al escribir el archivo CSV {csv_filename}: {e}") + return None # Devuelve None en caso de fallo + + return csv_filename + + +def generate_table_and_csv( + sorted_data: list, title: str, headers: list, query_date: str, algorithm_key: str # <--- R0913 Corregido (5/5 argumentos) +): + """ + Genera una tabla visual con Matplotlib, guarda la imagen PNG y un CSV de registro. + + Args: + sorted_data: La lista de diccionarios de inventario ya ordenada. + title: Título del gráfico. + headers: Encabezados para la tabla visual y el archivo CSV. + query_date: Fecha y hora de la consulta (timestamp). + algorithm_key: Clave del algoritmo (usada para nombrar archivos). + """ + # R0914 Corregido: La complejidad de variables se movió a las funciones auxiliares. + + # Preparar la matriz de datos para la tabla + data_matrix = [] + for item in sorted_data: + row = [ + item["codigo"], + item["nombre"], + item["demanda"], + item["tiempo_entrega"], + item["fecha_limite"], + item["cantidad"], + ] + data_matrix.append(row) + + # Llama a las funciones auxiliares + image_filename = _generate_visual_table(data_matrix, title, headers, query_date, algorithm_key) + csv_filename = _generate_csv_log(data_matrix, title, headers, query_date, algorithm_key) + + + print(f"\n {title} - Archivos generados:") + # Solo imprime si la generación fue exitosa + if image_filename: + print(" Imagen:", image_filename) + if csv_filename: + print(" Registro CSV:", csv_filename) + print("-----------------------------------------") + + +# ------------------------------------------ +# 5. Ejecución Principal +# ------------------------------------------ + +if __name__ == "__main__": + inventario = [] + ingresar_productos(inventario) + + if not inventario: + print("\n No se ingresaron productos. El programa se cerrará.") + sys.exit() + + inventarios_ordenados = generar_inventarios_ordenados(inventario) + + # Definición de variables locales para los encabezados + headers_all = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"] + + fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + ordenamientos_config = [ + { + "title": "Ordenamiento por Cantidad (Insertion Sort) - Mas agotados", + "data": inventarios_ordenados["cantidad"], + "algorithm_key": "insertion_cantidad", + }, + { + "title": "Ordenamiento por Tiempo de entrega (Bubble Sort) - Mas tardados", + "data": inventarios_ordenados["tiempo"], + "algorithm_key": "bubble_tiempo", + }, + { + "title": "Ordenamiento por Fecha límite (Quick Sort) - Mas próximas", + "data": inventarios_ordenados["fecha"], + "algorithm_key": "quick_fecha", + }, + { + "title": "Ordenamiento por Demanda (Selection Sort) - Mas populares", + "data": inventarios_ordenados["demanda"], + "algorithm_key": "selection_demanda", + }, + ] + + for orden in ordenamientos_config: + generate_table_and_csv( + sorted_data=orden["data"], + title=orden["title"], + headers=headers_all, # <--- headers_visual y headers_csv agrupados + query_date=fecha_consulta, + algorithm_key=orden["algorithm_key"], + ) \ No newline at end of file diff --git a/unidad_2/gestion_restorant_v0_2_alpha.py b/unidad_2/gestion_restorant_v0_2_alpha.py new file mode 100644 index 0000000..65829e2 --- /dev/null +++ b/unidad_2/gestion_restorant_v0_2_alpha.py @@ -0,0 +1,211 @@ +""" +Sistema de Gestión de Inventario para Restaurantes (v0.2 alpha). + +Implementa y demuestra cuatro algoritmos de ordenamiento (Insertion, Bubble, +Quick, Selection) aplicados a diferentes criterios de gestión de inventario: +cantidad, tiempo de entrega, fecha límite y demanda. +""" + +from datetime import datetime + +# ========================================================================== +# Inventario de Ejemplo +# ========================================================================== +inventario = [ + { + "codigo": 1, + "nombre": "Manzana", + "demanda": 8, + "tiempo_entrega": 3, + "fecha_limite": "2025-04-28", + "cantidad": 15, + }, + { + "codigo": 2, + "nombre": "Banana", + "demanda": 7, + "tiempo_entrega": 5, + "fecha_limite": "2025-04-25", + "cantidad": 2, + }, + { + "codigo": 3, + "nombre": "Lechuga", + "demanda": 6, + "tiempo_entrega": 2, + "fecha_limite": "2025-04-22", + "cantidad": 7, + }, + { + "codigo": 4, + "nombre": "Tomate", + "demanda": 9, + "tiempo_entrega": 4, + "fecha_limite": "2025-05-01", + "cantidad": 3, + }, +] + + +# ========================================================================== +# Algoritmos de Ordenamiento +# ========================================================================== + + +def insertion_sort(lista: list, key: callable) -> list: + """ + Implementa el algoritmo Insertion Sort. + + Ordena la lista en orden ascendente basándose en la clave proporcionada. + Utilizado para ordenar por cantidad (mas agotados primero). + + Args: + lista: La lista de diccionarios a ordenar. + key: Una función lambda para especificar la clave de ordenamiento. + + Returns: + La lista ordenada. + """ + for i in range(1, len(lista)): + actual = lista[i] + j = i - 1 + while j >= 0 and key(lista[j]) > key(actual): + lista[j + 1] = lista[j] + j -= 1 + lista[j + 1] = actual + return lista + + +def bubble_sort(lista: list, key: callable, descending: bool = False) -> list: + """ + Implementa el algoritmo Bubble Sort. + + Ordena la lista en orden ascendente (o descendente si `descending=True`) + basándose en la clave. Utilizado para ordenar por tiempo de entrega. + + Args: + lista: La lista de diccionarios a ordenar. + key: Una función lambda para especificar la clave de ordenamiento. + descending: Si es True, ordena de mayor a menor. + + Returns: + La lista ordenada. + """ + n = len(lista) + swapped = True + while swapped: + swapped = False + for i in range(1, n): + # Lógica central del Bubble Sort con manejo de orden + comparacion = ( + (key(lista[i - 1]) < key(lista[i])) + if descending + else (key(lista[i - 1]) > key(lista[i])) + ) + + if comparacion: + lista[i - 1], lista[i] = lista[i], lista[i - 1] + swapped = True + n -= 1 + return lista + + +def quick_sort(lista: list, key: callable) -> list: + """ + Implementa el algoritmo Quick Sort (recursivo). + + Ordena la lista en orden ascendente basándose en la clave. + Utilizado para ordenar por fecha (más próximas primero). + + Args: + lista: La lista de diccionarios a ordenar. + key: Una función lambda para especificar la clave de ordenamiento. + + Returns: + La lista ordenada. + """ + if len(lista) <= 1: + return lista + + pivot = lista[0] + # Se usa list comprehensions para crear las particiones + menos = [x for x in lista[1:] if key(x) < key(pivot)] + iguales = [x for x in lista if key(x) == key(pivot)] + mayor = [x for x in lista[1:] if key(x) >= key(pivot)] + + return quick_sort(menos, key) + iguales + quick_sort(mayor, key) + + +def selection_sort(lista: list, key: callable, reverse: bool = False) -> list: + """ + Implementa el algoritmo Selection Sort. + + Ordena la lista en orden ascendente (o descendente si `reverse=True`) + basándose en la clave. Utilizado para ordenar por demanda. + + Args: + lista: La lista de diccionarios a ordenar. + key: Una función lambda para especificar la clave de ordenamiento. + reverse: Si es True, ordena de mayor a menor. + + Returns: + La lista ordenada. + """ + n = len(lista) + # Se itera sobre la lista + for i in range(n): + index_extremo = i + # Se busca el índice del elemento más extremo (mínimo o máximo) + for j in range(i + 1, n): + comparacion = ( + (key(lista[j]) > key(lista[index_extremo])) + if reverse + else (key(lista[j]) < key(lista[index_extremo])) + ) + + if comparacion: + index_extremo = j + # Intercambio + lista[i], lista[index_extremo] = lista[index_extremo], lista[i] + return lista + + +# ========================================================================== +# Aplicación de Ordenamientos +# ========================================================================== + +# 1. Ordenar por 'cantidad' (Mas agotados primero) +inventario_por_cantidad = insertion_sort(inventario.copy(), key=lambda x: x["cantidad"]) +print("--- Inventario Ordenado por Cantidad (Insertion Sort) ---") +print("Ordenados por cantidad (Mas agotados primero):") +for item in inventario_por_cantidad: + print(f"{item['nombre']} - Cantidad: {item['cantidad']}") +print("---------------------------------------------------------") + +# 2. Ordenar por 'tiempo_entrega' (Entregas más tardías primero) +inventario_por_tiempo = bubble_sort( + inventario.copy(), key=lambda x: x["tiempo_entrega"], descending=True +) +print("\n--- Inventario Ordenado por Tiempo de Entrega (Bubble Sort) ---") +print("Ordenados por tiempo de entrega(en Días):") +for item in inventario_por_tiempo: + print(f"{item['nombre']} - Tiempo de entrega: {item['tiempo_entrega']}") +print("-------------------------------------------------------------") + +# 3. Ordenar por 'fecha_limite' (Próximas a vencer primero) +inventario_por_fecha = quick_sort( + inventario.copy(), key=lambda x: datetime.strptime(x["fecha_limite"], "%Y-%m-%d") +) +print("\n--- Inventario Ordenado por Fecha Límite (Quick Sort) ---") +print("Ordenados por fecha límite (próximas primero):") +for item in inventario_por_fecha: + print(f"{item['nombre']} - Fecha límite para consumir: {item['fecha_limite']}") +print("-------------------------------------------------------") + +# 4. Ordenar por 'demanda' (Mas populares primero) +inventario_por_demanda = selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True) +print("\n--- Inventario Ordenado por Demanda (Selection Sort) ---") +print("Ordenados por demanda (Mas populares primero):") +for item in inventario_por_demanda: + print(f"{item['nombre']} - Demanda: {item['demanda']}") +print("----------------------------------------------------") diff --git a/unidad_2/gestion_restorant_v0_5_beta.py b/unidad_2/gestion_restorant_v0_5_beta.py new file mode 100644 index 0000000..bf8bad8 --- /dev/null +++ b/unidad_2/gestion_restorant_v0_5_beta.py @@ -0,0 +1,177 @@ +""" +Sistema de Gestión de Inventario para Restaurantes (v0.5 beta). + +Combina el algoritmo Selection Sort para ordenar el inventario por demanda +y utiliza Matplotlib para generar una visualización tabular del inventario +ordenado, además de generar un registro en formato CSV. +""" + +# Standard library +import csv +import datetime + +# Third-party libraries +import matplotlib.pyplot as plt + +# Nota: La importación de 'table' dentro del módulo 'matplotlib' es correcta + +# ========================================================================== +# Inventario de Ejemplo +# ========================================================================== +inventario = [ + { + "codigo": 1, + "nombre": "Manzana", + "demanda": 8, + "tiempo_entrega": 3, + "fecha_limite": "2025-04-28", + "cantidad": 15, + }, + { + "codigo": 2, + "nombre": "Banana", + "demanda": 7, + "tiempo_entrega": 5, + "fecha_limite": "2025-04-25", + "cantidad": 2, + }, + { + "codigo": 3, + "nombre": "Lechuga", + "demanda": 6, + "tiempo_entrega": 2, + "fecha_limite": "2025-04-22", + "cantidad": 7, + }, + { + "codigo": 4, + "nombre": "Tomate", + "demanda": 9, + "tiempo_entrega": 4, + "fecha_limite": "2025-05-01", + "cantidad": 3, + }, +] + + +# ========================================================================== +# 1. Selection Sort para ordenar por demanda (de mayor a menor) +# ========================================================================== + + +def selection_sort(lista: list, key: callable, reverse: bool = False) -> list: + """ + Implementa el algoritmo Selection Sort. + + Ordena la lista en orden ascendente (o descendente si `reverse=True`) + basándose en la clave proporcionada. + + Args: + lista: La lista de diccionarios a ordenar. + key: Una función lambda para especificar la clave de ordenamiento. + reverse: Si es True, ordena de mayor a menor. + + Returns: + La lista ordenada. + """ + n = len(lista) + for i in range(n): + index_extremo = i + # Buscar el elemento más extremo (máximo si reverse=True, mínimo si reverse=False) + for j in range(i + 1, n): + # Lógica de comparación + comparacion = ( + (key(lista[j]) > key(lista[index_extremo])) + if reverse + else (key(lista[j]) < key(lista[index_extremo])) + ) + + if comparacion: + index_extremo = j + + # Intercambio + lista[i], lista[index_extremo] = lista[index_extremo], lista[i] + + return lista + + +# Ejecutar el ordenamiento +inventario_ordenado = selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True) + + +# ========================================================================== +# 2. Preparar la estructura de la tabla para visualizar +# ========================================================================== + +HEADERS = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"] + +# Crear la matriz de datos +data_matrix = [] +for item in inventario_ordenado: + row = [ + item["codigo"], + item["nombre"], + item["demanda"], + item["tiempo_entrega"], + item["fecha_limite"], + item["cantidad"], + ] + data_matrix.append(row) + +# Obtener la fecha y hora de la consulta +FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + +# ========================================================================== +# 3. Visualización con Matplotlib +# ========================================================================== + +# Ajustar el tamaño para que la tabla quepa mejor +fig, ax = plt.subplots(figsize=(9, 2.5)) +ax.xaxis.set_visible(False) +ax.yaxis.set_visible(False) +ax.set_frame_on(False) + +# Crear la tabla en el eje (ax) +tabla = ax.table(cellText=data_matrix, collabels=HEADERS, loc="center", cellLoc="center") + + +tabla.auto_set_font_size(False) +tabla.set_fontsize(10) +tabla.scale(1.0, 1.2) # Ajuste la escala para un mejor aspecto + +plt.title("Inventario Ordenado por Demanda", fontsize=14, pad=20) + +# Agregar la fecha de la consulta en la parte inferior +plt.figtext(0.5, 0.05, f"Consulta generada: {FECHA_CONSULTA}", ha="center", fontsize=8) + + +# ========================================================================== +# 4. Guardar la figura generada con una fecha en el nombre del archivo +# ========================================================================== + +IMAGEN_FILENAME = f"inventario_demanda_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.png" +# Usar bbox_inches="tight" para que el texto adicional no se corte +plt.savefig(IMAGEN_FILENAME, bbox_inches="tight") +plt.show() + +# ========================================================================== +# 5. Crear un archivo CSV de registro con los datos y la fecha de consulta +# ========================================================================== + +CSV_FILENAME = f"registro_inventario_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" +try: + with open(CSV_FILENAME, mode="w", newline="", encoding="utf-8") as file: + writer = csv.writer(file) + # Escribir encabezados + writer.writerow(["Fecha Consulta"] + HEADERS) + # Escribir datos + for row in data_matrix: + writer.writerow([FECHA_CONSULTA] + row) +except IOError as e: + print(f"Error al escribir el archivo CSV: {e}") + + +print("\n--- Generación de Archivos Finalizada ---") +print("Imagen generada:", IMAGEN_FILENAME) +print("Registro CSV generado:", CSV_FILENAME) +print("------------------------------------------") diff --git a/unidad_2/gestion_restorant_varias_tablas_v1_0.py b/unidad_2/gestion_restorant_varias_tablas_v1_0.py new file mode 100644 index 0000000..a141db4 --- /dev/null +++ b/unidad_2/gestion_restorant_varias_tablas_v1_0.py @@ -0,0 +1,292 @@ +""" +Sistema de Gestión de Inventario para Restaurantes (v1.0 - Múltiples Tablas). + +Demuestra cuatro algoritmos de ordenamiento (Insertion, Bubble, Quick, Selection) +aplicados a diferentes criterios de gestión de inventario. Por cada criterio, +genera una visualización tabular con Matplotlib y un archivo de registro CSV. +""" + +# Standard library +import csv +import datetime +from typing import List, Any # Añadida importación para anotación de tipo + +# Third-party libraries +import matplotlib.pyplot as plt + +# ======================================= +# Inventario de Ejemplo +# ======================================= +INVENTARIO = [ + { + "codigo": 1, + "nombre": "Manzana", + "demanda": 8, + "tiempo_entrega": 3, + "fecha_limite": "2025-04-28", + "cantidad": 15, + }, + { + "codigo": 2, + "nombre": "Banana", + "demanda": 7, + "tiempo_entrega": 5, + "fecha_limite": "2025-04-25", + "cantidad": 2, + }, + { + "codigo": 3, + "nombre": "Lechuga", + "demanda": 6, + "tiempo_entrega": 2, + "fecha_limite": "2025-04-22", + "cantidad": 7, + }, + { + "codigo": 4, + "nombre": "Tomate", + "demanda": 9, + "tiempo_entrega": 4, + "fecha_limite": "2025-05-01", + "cantidad": 3, + }, +] + +# ======================================= +# Funciones de ordenamiento +# ======================================= + + +def insertion_sort(lista: list, key: callable) -> list: + """Implementa el algoritmo Insertion Sort (orden ascendente).""" + for i in range(1, len(lista)): + actual = lista[i] + j = i - 1 + while j >= 0 and key(lista[j]) > key(actual): + lista[j + 1] = lista[j] + j -= 1 + lista[j + 1] = actual + return lista + + +def bubble_sort(lista: list, key: callable, descending: bool = False) -> list: + """Implementa el algoritmo Bubble Sort.""" + n = len(lista) + swapped = True + while swapped: + swapped = False + for i in range(1, n): + comparacion = ( + (key(lista[i - 1]) < key(lista[i])) + if descending + else (key(lista[i - 1]) > key(lista[i])) + ) + + if comparacion: + lista[i - 1], lista[i] = lista[i], lista[i - 1] + swapped = True + n -= 1 + return lista + + +def quick_sort(lista: list, key: callable) -> list: + """Implementa el algoritmo Quick Sort recursivo (orden ascendente).""" + if len(lista) <= 1: + return lista + + pivot = lista[0] + menos = [x for x in lista[1:] if key(x) < key(pivot)] + iguales = [x for x in lista if key(x) == key(pivot)] + mayor = [x for x in lista[1:] if key(x) >= key(pivot)] + + return quick_sort(menos, key) + iguales + quick_sort(mayor, key) + + +def selection_sort(lista: list, key: callable, reverse: bool = False) -> list: + """Implementa el algoritmo Selection Sort.""" + n = len(lista) + for i in range(n): + index_extremo = i + for j in range(i + 1, n): + comparacion = ( + (key(lista[j]) > key(lista[index_extremo])) + if reverse + else (key(lista[j]) < key(lista[index_extremo])) + ) + + if comparacion: + index_extremo = j + + lista[i], lista[index_extremo] = lista[index_extremo], lista[i] + return lista + + +# ======================================= +# Criterios de Ordenamiento +# ======================================= + +# Ordenar por Cantidad en inventario (usando Insertion Sort: de menor a mayor) +INVENTARIO_POR_CANTIDAD = insertion_sort( + INVENTARIO.copy(), key=lambda x: x["cantidad"] +) + +# Ordenar por Tiempo de entrega (usando Bubble Sort: de mayor a menor) +INVENTARIO_POR_TIEMPO = bubble_sort( + INVENTARIO.copy(), key=lambda x: x["tiempo_entrega"], descending=True +) + +# Ordenar por Fecha límite (usando Quick Sort: las fechas más próximas primero) +INVENTARIO_POR_FECHA = quick_sort( + INVENTARIO.copy(), + key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d"), +) + +# Ordenar por Demanda del cliente (usando Selection Sort: de mayor a menor) +INVENTARIO_POR_DEMANDA = selection_sort( + INVENTARIO.copy(), key=lambda x: x["demanda"], reverse=True +) + + +# ======================================= +# Funciones auxiliares (para eliminar R0914) +# ======================================= + +def _generate_visual_table( + data_rows: List[List[Any]], title: str, headers: list, query_date: str, algorithm_key: str +) -> str: + """Genera la tabla visual con Matplotlib y la guarda.""" + fig, ax = plt.subplots(figsize=(10, len(data_rows) * 0.6 + 2)) + ax.axis("off") + + table_obj = ax.table( + cellText=data_rows, colLabels=headers, loc="center", cellLoc="center" + ) + table_obj.auto_set_font_size(False) + table_obj.set_fontsize(10) + table_obj.scale(1.2, 1.2) + + plt.title(title, fontsize=14) + plt.figtext(0.5, 0.01, f"Consulta generada: {query_date}", ha="center", fontsize=8) + + time_stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + image_filename = f"inventario_{algorithm_key}_{time_stamp}.png" + + try: + plt.savefig(image_filename, bbox_inches="tight") + plt.show() + except IOError as e: + print(f"Error al guardar la imagen {image_filename}: {e}") + image_filename = None + + plt.close(fig) + return image_filename + + +def _generate_csv_log( + data_rows: List[List[Any]], headers: list, query_date: str, algorithm_key: str +) -> str: + """Genera y guarda el registro de datos en un archivo CSV.""" + time_stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + csv_filename = f"registro_{algorithm_key}_{time_stamp}.csv" + try: + with open(csv_filename, mode="w", newline="", encoding="utf-8") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(["Fecha Consulta"] + headers) + for row in data_rows: + writer.writerow([query_date] + row) + except IOError as e: + print(f"Error al escribir el archivo CSV {csv_filename}: {e}") + csv_filename = None + + return csv_filename + + +# ======================================= +# Función para generar tabla y CSV (Refactorizada) +# ======================================= + +def generate_table_and_csv( + sorted_data: list, title: str, table_headers: list, query_date: str, algorithm_key: str +): + """ + Genera una tabla visual de los datos ordenados usando Matplotlib, guarda + una imagen PNG y un archivo CSV de registro. + + R0914 Corregido: La complejidad de variables locales se ha movido + a las funciones auxiliares. + """ + # 1. Crear una matriz con los datos ordenados (Variables: data_rows, item, row) + data_rows = [] + for item in sorted_data: + row = [ + item["codigo"], + item["nombre"], + item["demanda"], + item["tiempo_entrega"], + item["fecha_limite"], + item["cantidad"], + ] + data_rows.append(row) + + # 2. Generar y guardar la tabla visual + image_filename = _generate_visual_table( + data_rows, title, table_headers, query_date, algorithm_key + ) + + # 3. Generar y guardar el registro CSV + csv_filename = _generate_csv_log( + data_rows, table_headers, query_date, algorithm_key + ) + + print(f"{title} - Archivos generados:") + if image_filename: + print(" Imagen:", image_filename) + if csv_filename: + print(" Registro CSV:", csv_filename) + print("-----------------------------------------") + + +# ======================================= +# Ejecución principal +# ======================================= + +# Cabeceras para la tabla +HEADERS = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"] + +# Fecha y hora de la consulta +FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + +# Diccionarios con cada ordenamiento +ORDENAMIENTOS = [ + { + "title": "Ordenamiento por Cantidad (Insertion Sort)", + "data": INVENTARIO_POR_CANTIDAD, + "algorithm_key": "insertion", + }, + { + "title": "Ordenamiento por Tiempo de entrega (Bubble Sort)", + "data": INVENTARIO_POR_TIEMPO, + "algorithm_key": "bubble", + }, + { + "title": "Ordenamiento por Fecha límite (Quick Sort)", + "data": INVENTARIO_POR_FECHA, + "algorithm_key": "quick", + }, + { + "title": "Ordenamiento por Demanda (Selection Sort)", + "data": INVENTARIO_POR_DEMANDA, + "algorithm_key": "selection", + }, +] + +# Iterar sobre cada criterio de ordenamiento, generar y mostrar la tabla +# Finalmente, guardar el CSV correspondiente. +for orden in ORDENAMIENTOS: + generate_table_and_csv( + sorted_data=orden["data"], + title=orden["title"], + table_headers=HEADERS, + query_date=FECHA_CONSULTA, + algorithm_key=orden["algorithm_key"], + ) \ No newline at end of file diff --git a/unidad_2/requisitos.txt b/unidad_2/requisitos.txt new file mode 100644 index 0000000..4b43f7e --- /dev/null +++ b/unidad_2/requisitos.txt @@ -0,0 +1 @@ +matplotlib \ No newline at end of file diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py new file mode 100644 index 0000000..199978d --- /dev/null +++ b/unidad_3/Flask/app.py @@ -0,0 +1,481 @@ +import csv +import datetime +import io +import os +import random +import string +import time +from typing import List, Tuple, Dict + +from flask import ( + Flask, render_template, request, + redirect, url_for, flash, + session, jsonify, send_file +) +from fpdf import FPDF +import pandas as pd +from pandas.errors import EmptyDataError, ParserError + +# ============================== +# Configuración básica +# ============================== +app = Flask(__name__) +app.secret_key = os.environ.get("FLASK_SECRET_KEY", "secret-key-default-insecure") +UPLOAD_FOLDER = "uploads" + +if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER) +app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER + + +# ============================== +# Utilidades +# ============================== +def generar_codigo_aleatorio(longitud: int = 6) -> str: + """Genera un código alfanumérico aleatorio.""" + caracteres = string.ascii_uppercase + string.digits + return "".join(random.choices(caracteres, k=longitud)) + + +def limpiar_archivos_antiguos(dias: int = 1): + """Elimina archivos antiguos de la carpeta uploads.""" + ahora = time.time() + segundos_limite = dias * 24 * 3600 + archivos_eliminados = 0 + folder_path = app.config["UPLOAD_FOLDER"] + + if os.path.exists(folder_path): + for nombre_archivo in os.listdir(folder_path): + ruta_archivo = os.path.join(folder_path, nombre_archivo) + try: + if os.path.isfile(ruta_archivo): + timestamp_modificacion = os.path.getmtime(ruta_archivo) + if (ahora - timestamp_modificacion) > segundos_limite: + os.remove(ruta_archivo) + archivos_eliminados += 1 + print(f"Archivo eliminado: {ruta_archivo}") + except (OSError, PermissionError) as e: + print(f"Error al eliminar {ruta_archivo}: {e}") + + print(f"Se eliminaron {archivos_eliminados} archivos antiguos.") + + +with app.app_context(): + limpiar_archivos_antiguos(dias=1) + + +def _guardar_inventario_csv(inventario_data: list, filename: str) -> str: + """Guarda inventario en CSV.""" + ruta_archivo = os.path.join(app.config["UPLOAD_FOLDER"], filename) + + if not inventario_data or not all(isinstance(item, dict) for item in inventario_data): + raise ValueError("Formato de inventario inválido o vacío.") + + fieldnames = inventario_data[0].keys() + try: + with open(ruta_archivo, "w", newline="", encoding="utf-8") as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(inventario_data) + return ruta_archivo + except Exception as e: + raise OSError(f"Error al guardar CSV: {e}") from e + + +# ============================== +# Subir CSV +# ============================== +@app.route("/", methods=["GET", "POST"]) +def upload_csv(): + """Subir CSV de inventario.""" + if request.method == "POST": + if "file" not in request.files or request.files["file"].filename == "": + flash("No se seleccionó ningún archivo.", "error") + return redirect(request.url) + + file = request.files["file"] + + if not file.filename.lower().endswith(".csv"): + flash("Por favor, sube un archivo CSV válido.", "error") + return redirect(request.url) + + if file.content_type not in ("text/csv", "application/vnd.ms-excel", "application/octet-stream"): + # Algunos navegadores usan tipos genéricos al subir .csv + print(f"Advertencia: content_type no estándar: {file.content_type}") + + filepath = os.path.join(app.config["UPLOAD_FOLDER"], file.filename) + try: + file.save(filepath) + data = pd.read_csv(filepath) + data.fillna("", inplace=True) + session["productos"] = data.to_dict(orient="records") + session["csv_filepath"] = filepath + flash("Archivo CSV cargado con éxito.") + except EmptyDataError: + flash("El archivo CSV está vacío.", "error") + except ParserError: + flash("Error al procesar el archivo CSV. Verifique el formato.", "error") + except Exception as e: + flash(f"Ocurrió un error inesperado al procesar el archivo: {e}", "error") + # limpiar archivo fallido + if os.path.exists(filepath): + try: + os.remove(filepath) + print(f"Archivo fallido eliminado: {filepath}") + except OSError: + pass + return redirect(url_for("upload_csv")) + + return render_template("subir_csv.html") + + +# ============================== +# Crear inventario manual (JSON → CSV) +# ============================== +@app.route("/crear_inventario", methods=["GET", "POST"]) +def crear_inventario(): + """ + Mostrar interfaz o procesar la creación manual del inventario. + POST espera JSON con {"guardar_csv": true, "inventario": [ ...dicts... ]} + """ + if request.method == "POST": + if not request.is_json: + flash("Método no soportado. Use la interfaz o envíe JSON.", "error") + return redirect(url_for("crear_inventario")) + + data = request.get_json() + inventario_para_guardar = data.get("inventario") + if not data.get("guardar_csv") or not inventario_para_guardar: + return jsonify({"message": "Datos inválidos o acción no reconocida."}), 400 + + nombre_archivo_creado = ( + f"inventario_creado_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" + ) + + try: + ruta_archivo_creado = _guardar_inventario_csv(inventario_para_guardar, nombre_archivo_creado) + + # Cargar datos en la sesión + session["csv_filepath"] = ruta_archivo_creado + df = pd.read_csv(ruta_archivo_creado) + df.fillna("", inplace=True) + session["productos"] = df.to_dict(orient="records") + session.pop("inventario_creado", None) + return jsonify({"message": f"Inventario creado y guardado como {nombre_archivo_creado}"}), 200 + + except (KeyError, ValueError, OSError) as e: + return jsonify({"message": f"Error en los datos del inventario: {e}"}), 400 + + inventario_temporal = session.get("inventario_creado", []) + return render_template("crear_inventario.html", inventario_creado=inventario_temporal) + + +# ============================== +# Pedir stock interno +# ============================== +def _procesar_formulario_stock(form_data: dict) -> Dict[str, int]: + """ + Extrae y valida el pedido del formulario. + Returns: Diccionario con {codigo: cantidad}. + """ + pedido_stock = {} + for key, value in form_data.items(): + if key.startswith("pedido_stock[") and key.endswith("]"): + codigo_limpio = key[len("pedido_stock["):-1] + try: + cantidad_pedida = int(value) + if cantidad_pedida > 0: + pedido_stock[codigo_limpio] = cantidad_pedida + except ValueError: + # Ignorar valores no numéricos + pass + return pedido_stock + + +def _actualizar_stock_inventario( + productos: List[Dict], pedido_stock: Dict[str, int] +) -> Tuple[List[Dict], List[str], bool]: + """ + Aplica la lógica de negocio para actualizar el stock. + """ + resultados_msg = [] + hubo_cambios = False + factor_aprendizaje = 0.3 + productos_actualizados = [p.copy() for p in productos] + + for producto in productos_actualizados: + codigo_str = str(producto.get("Código")) + if codigo_str in pedido_stock: + cantidad_pedida = pedido_stock[codigo_str] + stock_actual = int(producto.get("Cantidad") or 0) + demanda_actual = int(producto.get("Demanda") or 0) + + if cantidad_pedida <= stock_actual: + producto["Cantidad"] = stock_actual - cantidad_pedida + + # Ajuste de demanda + nueva_demanda = demanda_actual + (cantidad_pedida - demanda_actual) * factor_aprendizaje + producto["Demanda"] = min(round(nueva_demanda), 10) + + resultados_msg.append( + f"Pedido de {cantidad_pedida} {producto.get('Nombre','')} OK. " + f"Nuevo stock: {producto['Cantidad']}" + ) + hubo_cambios = True + else: + resultados_msg.append( + f"Stock insuficiente de {producto.get('Nombre','')} " + f"(disp: {stock_actual}) para pedir {cantidad_pedida}." + ) + + return productos_actualizados, resultados_msg, hubo_cambios + + +@app.route("/pedir_stock", methods=["GET", "POST"]) +def pedir_stock(): + """ + Ruta para procesar el pedido de stock interno y actualizar el inventario. + """ + productos = session.get("productos") + if not productos: + flash("No hay inventario cargado. Sube o crea un archivo CSV.", "warning") + return redirect(url_for("upload_csv")) + + if request.method == "POST": + pedido_stock = _procesar_formulario_stock(request.form) + + if not pedido_stock: + return jsonify({"message": "No se especificó ninguna cantidad válida."}), 400 + + productos_nuevos, mensajes, cambios = _actualizar_stock_inventario(productos, pedido_stock) + + if cambios: + session["productos"] = productos_nuevos + csv_path = session.get("csv_filepath") + if csv_path and os.path.exists(csv_path): + try: + df = pd.DataFrame(productos_nuevos) + df.to_csv(csv_path, index=False, encoding="utf-8") + except (IOError, OSError) as e: + print(f"Error al actualizar CSV de inventario: {e}") + + return jsonify({ + "message": "Proceso de pedido de stock completado.", + "detalles": mensajes, + "hubo_cambios": cambios, + }), 200 + + return render_template("pedir_stock.html", productos_stock=productos) + + +# ============================== +# Pedir a proveedor y generar PDF +# ============================== +def _obtener_y_validar_pedido(productos: list, form_data: dict) -> Tuple[List, List]: + """ + Extrae y valida los datos del pedido al proveedor. + """ + pedido_proveedor = [] + errores_validacion = [] + productos_dict = {str(p.get("Código")): p for p in productos} + + codigos_pedidos = set() + for key in form_data.keys(): + if key.startswith("pedido_proveedor[") and key.endswith("]"): + codigos_pedidos.add(key[len("pedido_proveedor["):-1]) + + for codigo in codigos_pedidos: + cantidad_str = form_data.get(f"pedido_proveedor[{codigo}]") + precio_str = form_data.get(f"precio_proveedor[{codigo}]") + producto_info = productos_dict.get(codigo) + + if not producto_info: + errores_validacion.append(f"Producto {codigo} no encontrado.") + continue + + # Validar cantidad + try: + cantidad = int(cantidad_str) if cantidad_str else 0 + if cantidad < 0: + errores_validacion.append(f"Producto {codigo}: Cantidad negativa.") + continue + except ValueError: + errores_validacion.append(f"Producto {codigo}: Cantidad inválida.") + continue + + # Validar precio + if cantidad > 0: + try: + precio = float(precio_str) if precio_str else 0.0 + if precio <= 0: + errores_validacion.append(f"Producto {codigo}: Precio debe ser > 0.") + continue + except ValueError: + errores_validacion.append(f"Producto {codigo}: Precio inválido.") + continue + + pedido_proveedor.append({ + "codigo": producto_info.get("Código"), + "nombre": producto_info.get("Nombre"), + "cantidad": cantidad, + "precio_unitario": precio, + "tiempo_entrega": producto_info.get("Tiempo entrega(Días)", "N/A"), + "stock_actual": producto_info.get("Cantidad", "N/A"), + }) + + return pedido_proveedor, errores_validacion + + +@app.route("/pedir_proveedor", methods=["GET", "POST"]) +def pedir_proveedor(): + """Recopila y procesa los datos del pedido al proveedor.""" + productos = session.get("productos") + if not productos: + flash("No hay inventario cargado.", "warning") + return redirect(url_for("upload_csv")) + + if request.method == "POST": + try: + pedido_prov, errores = _obtener_y_validar_pedido(productos, request.form) + + if errores: + return jsonify({"message": "Errores en el formulario.", "errors": errores}), 400 + + if not pedido_prov: + return jsonify({"message": "No se seleccionaron productos válidos."}), 400 + + session["pedido_proveedor"] = pedido_prov + return jsonify({ + "message": "Pedido listo para generar PDF.", + "redirect_url": url_for("generar_pdf_proveedor"), + }), 200 + + except (KeyError, ValueError) as e: + print(f"Error de datos en /pedir_proveedor: {e}") + return jsonify({"message": f"Error al procesar datos: {e}"}), 400 + except Exception as e: + print(f"Error inesperado en /pedir_proveedor: {e}") + return jsonify({"message": "Error interno del servidor."}), 500 + + return render_template("pedir_proveedor.html", productos_proveedor=productos) + + +def _crear_pdf_pedido(pedido_proveedor: list) -> Tuple[bytes, str]: + """Genera el contenido binario del PDF del pedido al proveedor.""" + pdf = FPDF() + pdf.add_page() + pdf.set_font("Arial", size=10) + + fecha_hora = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + pdf.set_font("Arial", "B", size=12) + pdf.cell(0, 10, "Pedido al Proveedor - Touken Café", ln=True, align="C") + pdf.set_font("Arial", size=9) + pdf.cell(0, 5, f"Fecha de Generación: {fecha_hora}", ln=True, align="C") + pdf.ln(8) + + # Encabezados + pdf.set_font("Arial", "B", size=10) + col_widths = [25, 75, 25, 30, 35] + headers = ["Código", "Producto", "Cantidad", "Precio Unit.", "Precio Total"] + for i, header in enumerate(headers): + pdf.cell(col_widths[i], 10, header, border=1, align="C") + pdf.ln() + + # Contenido + pdf.set_font("Arial", size=9) + total_pedido = 0.0 + for item in pedido_proveedor: + precio_total_item = item["cantidad"] * item["precio_unitario"] + total_pedido += precio_total_item + + pdf.cell(col_widths[0], 8, str(item["codigo"]), border=1) + x_before = pdf.get_x() + y_before = pdf.get_y() + pdf.multi_cell(col_widths[1], 8, item["nombre"], border=1, align="L") + + pdf.set_xy(x_before + col_widths[1], y_before) + pdf.cell(col_widths[2], 8, str(item["cantidad"]), border=1, align="C") + pdf.cell(col_widths[3], 8, f"${item['precio_unitario']:.2f}", border=1, align="R") + pdf.cell(col_widths[4], 8, f"${precio_total_item:.2f}", border=1, align="R") + pdf.ln() + + pdf.ln(5) + pdf.set_font("Arial", "B", size=10) + pdf.cell(sum(col_widths) - col_widths[-1], 10, "Total del Pedido:", border=0, align="R") + pdf.cell(col_widths[-1], 10, f"${total_pedido:.2f}", border=1, align="R") + pdf.ln() + + pdf_filename = f"pedido_proveedor_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf" + buffer = io.BytesIO(pdf.output(dest="S").encode("latin-1")) + return buffer, pdf_filename + + +@app.route("/generar_pdf_proveedor", methods=["GET"]) +def generar_pdf_proveedor(): + """Genera el PDF del pedido almacenado.""" + pedido_proveedor = session.pop("pedido_proveedor", None) + + if not pedido_proveedor: + flash("No hay ningún pedido pendiente para generar el PDF.", "warning") + return redirect(url_for("pedir_proveedor")) + + try: + buffer, pdf_filename = _crear_pdf_pedido(pedido_proveedor) + return send_file( + buffer, + mimetype="application/pdf", + as_attachment=True, + download_name=pdf_filename, + ) + except (RuntimeError, OSError, ValueError) as e: + print(f"Error al generar PDF: {e}") + flash("Error al generar el archivo PDF.", "error") + return redirect(url_for("pedir_proveedor")) + + +# ============================== +# Exportar inventario +# ============================== +@app.route("/export_json") +def export_json(): + """Exporta el inventario actual como JSON.""" + productos = session.get("productos") + if not productos: + return jsonify({"message": "No hay datos de inventario para exportar."}), 404 + return jsonify(productos) + + +@app.route("/export_csv") +def export_csv(): + """Exporta el inventario actual como CSV.""" + productos = session.get("productos") + if not productos: + flash("No hay datos de inventario para exportar.", "warning") + return redirect(url_for("upload_csv")) + + try: + output = io.StringIO() + if productos: + df = pd.DataFrame(productos) + df.to_csv(output, index=False, encoding="utf-8") + else: + output.write("Código,Nombre,Cantidad,Demanda,Tiempo entrega(Días),Fecha límite\n") + + output.seek(0) + return send_file( + io.BytesIO(output.getvalue().encode("utf-8")), + mimetype="text/csv", + as_attachment=True, + download_name="inventario_exportado.csv", + ) + except (IOError, OSError) as e: + print(f"Error al exportar CSV: {e}") + flash("Error al generar el archivo CSV de exportación.", "error") + return redirect(url_for("upload_csv")) + + +# ============================== +# Ejecución principal +# ============================== +if __name__ == "__main__": + app.run(debug=True, host="0.0.0.0") \ No newline at end of file diff --git a/unidad_3/Flask/requisitos.txt b/unidad_3/Flask/requisitos.txt new file mode 100644 index 0000000..93e38f9 --- /dev/null +++ b/unidad_3/Flask/requisitos.txt @@ -0,0 +1,3 @@ +flask +fpdf +pandas diff --git a/Flask/static/css/styles.css b/unidad_3/Flask/static/css/styles.css similarity index 100% rename from Flask/static/css/styles.css rename to unidad_3/Flask/static/css/styles.css diff --git a/Flask/static/js/script.js b/unidad_3/Flask/static/js/script.js similarity index 100% rename from Flask/static/js/script.js rename to unidad_3/Flask/static/js/script.js diff --git a/Flask/templates/base.html b/unidad_3/Flask/templates/base.html similarity index 100% rename from Flask/templates/base.html rename to unidad_3/Flask/templates/base.html diff --git a/Flask/templates/crear_inventario.html b/unidad_3/Flask/templates/crear_inventario.html similarity index 100% rename from Flask/templates/crear_inventario.html rename to unidad_3/Flask/templates/crear_inventario.html diff --git a/Flask/templates/pedir_proveedor.html b/unidad_3/Flask/templates/pedir_proveedor.html similarity index 100% rename from Flask/templates/pedir_proveedor.html rename to unidad_3/Flask/templates/pedir_proveedor.html diff --git a/Flask/templates/pedir_stock.html b/unidad_3/Flask/templates/pedir_stock.html similarity index 100% rename from Flask/templates/pedir_stock.html rename to unidad_3/Flask/templates/pedir_stock.html diff --git a/Flask/templates/subir_csv.html b/unidad_3/Flask/templates/subir_csv.html similarity index 100% rename from Flask/templates/subir_csv.html rename to unidad_3/Flask/templates/subir_csv.html diff --git a/Flask/uploads/registro_inventario_jerarquico_20250424_175403.csv b/unidad_3/Flask/uploads/registro_inventario_jerarquico_20250424_175403.csv similarity index 100% rename from Flask/uploads/registro_inventario_jerarquico_20250424_175403.csv rename to unidad_3/Flask/uploads/registro_inventario_jerarquico_20250424_175403.csv diff --git "a/Unidad 3/Gesti\303\263n de inventario y pedidos/ReadME.md" "b/unidad_3/Gesti\303\263n de inventario y pedidos/ReadME.md" similarity index 100% rename from "Unidad 3/Gesti\303\263n de inventario y pedidos/ReadME.md" rename to "unidad_3/Gesti\303\263n de inventario y pedidos/ReadME.md" diff --git a/unidad_7/items_management.py b/unidad_7/items_management.py new file mode 100644 index 0000000..584e8f2 --- /dev/null +++ b/unidad_7/items_management.py @@ -0,0 +1,316 @@ +""" +Módulo de Gestión de Inventario (Monolítico - v1.0). + +Implementa las clases Producto (Modelo) e Inventario (Servicio/Manager) +para la gestión básica de productos con validación de datos mediante +propiedades (getters y setters). +""" + +import sys +# from typing import Union + +# ==================================================================== +# CLASE PRODUCTO (Modelo) +# ==================================================================== + + +class Producto: + """ + Representa un producto con sus atributos de nombre, precio, cantidad y código. + """ + + def __init__(self, nombre: str, precio: float, cantidad: int, codigo: str): + """ + Inicializa una nueva instancia de Producto. + + Args: + nombre: Nombre del producto. + precio: Precio del producto (debe ser positivo). + cantidad: Cantidad en inventario (debe ser no negativo). + codigo: Identificador único del producto. + """ + # Atributos internos (con un solo guion bajo para Pylint R0902) + self._nombre = nombre + self._precio = precio + self._cantidad = cantidad + self._codigo = codigo + + def __del__(self): + """Método de finalización que se llama cuando el objeto es destruido.""" + print(f"Objeto {self.codigo} - {self.nombre} eliminado") + + def __str__(self): + """Representación legible del objeto para impresión.""" + return ( + f"Código: {self.codigo}\n Producto: {self.nombre}\n " + f"Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}" + ) + + # Métodos dunder de comparación + def __lt__(self, otro): + """Define la comparación 'menor que' (<) basada en el precio.""" + if not isinstance(otro, Producto): + return NotImplemented + return self.precio < otro.precio + + def __eq__(self, otro): + """Define la comparación 'igual a' (==) basada en el código.""" + if not isinstance(otro, Producto): + return NotImplemented + return self.codigo == otro.codigo + + # --- Propiedades (Getters y Setters con validación) --- + # C0116 Corregido: Añadidos Docstrings a todos los getters y setters + + @property + def nombre(self): + """Obtiene el nombre del producto.""" + return self._nombre + + @nombre.setter + def nombre(self, nombre): + """Establece el nombre del producto, validando que no esté vacío.""" + if not isinstance(nombre, str) or not nombre.strip(): + raise ValueError("El nombre del producto debe ser una cadena no vacía.") + self._nombre = nombre + + @property + def precio(self): + """Obtiene el precio del producto.""" + return self._precio + + @precio.setter + def precio(self, precio): + """Establece el precio del producto, validando que sea positivo.""" + if not isinstance(precio, (int, float)) or precio <= 0: + raise ValueError("El precio debe ser un número positivo mayor que cero.") + self._precio = precio + + @property + def cantidad(self): + """Obtiene la cantidad en inventario.""" + return self._cantidad + + @cantidad.setter + def cantidad(self, cantidad): + """Establece la cantidad, validando que sea un entero no negativo.""" + if not isinstance(cantidad, int) or cantidad < 0: + raise ValueError("La cantidad debe ser un número entero no negativo.") + self._cantidad = cantidad + + @property + def codigo(self): + """Obtiene el código del producto.""" + return self._codigo + + @codigo.setter + def codigo(self, codigo): + """Establece el código, validando que sea una cadena no vacía.""" + if not isinstance(codigo, str) or not codigo.strip(): + raise ValueError("El código del producto debe ser una cadena no vacía.") + self._codigo = codigo + + +# -------------------------------------------------------------------- +# CLASE INVENTARIO (Servicios/Manager) +# -------------------------------------------------------------------- + + +class Inventario: + """ + Gestiona una colección de objetos Producto utilizando un diccionario. + + Las operaciones incluyen agregar, listar, buscar, actualizar y eliminar productos. + """ + + def __init__(self): + """Inicializa el inventario con un diccionario vacío.""" + self.productos = {} + + def agg_prod(self, producto: Producto): + """ + Agrega un producto al inventario. + + Args: + producto: Objeto Producto a agregar. + """ + if not isinstance(producto, Producto): + print("Error: Solo se pueden agregar objetos de tipo 'Producto' al inventario.") + return + + if producto.codigo in self.productos: + print(f"Error: El producto con código '{producto.codigo}' ya existe en el inventario.") + return + + self.productos[producto.codigo] = producto + print(f"Éxito: Producto '{producto.nombre}' (Código: {producto.codigo}) agregado.") + + def imprimir(self): + """Muestra una lista detallada de todos los productos en el inventario.""" + if not self.productos: + print("El inventario está vacío. No hay productos para mostrar.") + return + + print("\n--- Listado de Productos en Inventario ---") + for producto_obj in self.productos.values(): + print(producto_obj) + print("-" * 30) + print("--- Fin del Listado ---") + + def buscar_producto(self): + """Solicita un código y muestra la información del producto si existe.""" + id_search = input("Ingrese el código del producto a buscar: ").strip() + + if id_search in self.productos: + resultado_producto = self.productos[id_search] + print("\n ¡Producto Encontrado!") + print(resultado_producto) + print("--------------------------\n") + else: + print( + f"Error: El producto con el código {id_search} no se encuentra en el inventario." + ) + + def actualizar_cantidad(self): + """Solicita un código y actualiza la cantidad de ese producto.""" + id_search = input("Ingrese el código del producto a actualizar: ").strip() + + if id_search not in self.productos: + print(f"Error: Producto con código '{id_search}' no encontrado.") + return + + producto_a_actualizar = self.productos[id_search] + + while True: + try: + new_cantidad_str = input( + f"Ingrese la nueva cantidad para '{producto_a_actualizar.nombre}' " + f"(actual: {producto_a_actualizar.cantidad}): " + ).strip() + new_cantidad = int(new_cantidad_str) + + if new_cantidad < 0: + print( + "Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo." + ) + continue + + break + + except ValueError: + print("Error de Valor: Se espera una cantidad numérica entera.") + except TypeError: + print("Error de Tipo: Ocurrió un problema con el tipo de dato.") + + # Asigna la nueva cantidad (llamando al setter de Producto) + try: + producto_a_actualizar.cantidad = new_cantidad + print( + f"Éxito: Cantidad del producto '{producto_a_actualizar.nombre}'" + f" actualizada a '{new_cantidad}'." + ) + except ValueError as e: + print(f"Error de validación: {e}") + + def eliminar_producto(self): + """Solicita un código y elimina el producto del inventario.""" + id_search = input("Ingrese el código del producto a eliminar: ").strip() + + if id_search not in self.productos: + print(f"Error: Producto con código '{id_search}' no encontrado.") + return + + nombre_producto = self.productos[id_search].nombre + + del self.productos[id_search] + print( + f"Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario." + ) + + +# -------------------------------------------------------------------- +# MENÚ PRINCIPAL Y LÓGICA DE LA APLICACIÓN +# -------------------------------------------------------------------- + + +def obtener_datos_producto(): + """ + Solicita los datos del producto al usuario con validación. + + Returns: + Producto: Objeto Producto si los datos son válidos, None en caso contrario. + """ + try: + nombre = input("Nombre del producto: ").strip() + precio = float(input("Precio del producto: ")) + cantidad = int(input("Cantidad del producto: ")) + codigo = input("Código del producto: ").strip() + + return Producto(nombre, precio, cantidad, codigo) + + except ValueError as e: + print( + f"Error de entrada de datos: {e}. Asegúrate de ingresar tipos y valores correctos." + ) + return None + except TypeError as e: + print(f"Error de tipo de dato: {e}") + return None + + +def main_menu(): + """Ejecuta el menú interactivo de la aplicación.""" + mi_inventario = Inventario() + + while True: + print("\n--- Sistema de Gestión de Inventario (Monolítico) ---") + print("1. Agregar producto") + print("2. Buscar producto") + print("3. Actualizar cantidad") + print("4. Eliminar producto") + print("5. Listar todos los productos") + print("6. Salir") + print("-----------------------------------------------------") + + try: + opcion = input("Selecciona una opción (1-6): ").strip() + + if opcion == "1": + print("\n>>> Agregar Producto <<<") + nuevo_producto = obtener_datos_producto() + if nuevo_producto: + mi_inventario.agg_prod(nuevo_producto) + + elif opcion == "2": + print("\n>>> Buscar Producto <<<") + mi_inventario.buscar_producto() + + elif opcion == "3": + print("\n>>> Actualizar Cantidad <<<") + mi_inventario.actualizar_cantidad() + + elif opcion == "4": + print("\n>>> Eliminar Producto <<<") + mi_inventario.eliminar_producto() + + elif opcion == "5": + print("\n>>> Listar Productos <<<") + mi_inventario.imprimir() + + elif opcion == "6": + print("\nSaliendo del sistema de inventario. ¡Hasta pronto!") + sys.exit(0) + + else: + print("Opción no válida. Por favor, selecciona un número del 1 al 6.") + + # W0718 Corregido: Capturamos errores específicos de interrupción del flujo. + except (KeyboardInterrupt, SystemExit): + # Captura Ctrl+C y sys.exit(0) + print("\nInterrupción detectada. Terminando el programa.") + sys.exit(0) + + +# Punto de entrada de la aplicación +if __name__ == "__main__": + main_menu() \ No newline at end of file diff --git a/Unidad-7/PystockManagement/.gitignore b/unidad_7/pystock_management/.gitignore similarity index 100% rename from Unidad-7/PystockManagement/.gitignore rename to unidad_7/pystock_management/.gitignore diff --git a/Unidad-7/PystockManagement/src/__init__.py b/unidad_7/pystock_management/__init__.py similarity index 100% rename from Unidad-7/PystockManagement/src/__init__.py rename to unidad_7/pystock_management/__init__.py diff --git a/Unidad-7/PystockManagement/__pycache__/main.cpython-310.pyc b/unidad_7/pystock_management/__pycache__/main.cpython-310.pyc similarity index 100% rename from Unidad-7/PystockManagement/__pycache__/main.cpython-310.pyc rename to unidad_7/pystock_management/__pycache__/main.cpython-310.pyc diff --git a/Unidad-7/PystockManagement/__pycache__/stock_app.cpython-310.pyc b/unidad_7/pystock_management/__pycache__/stock_app.cpython-310.pyc similarity index 100% rename from Unidad-7/PystockManagement/__pycache__/stock_app.cpython-310.pyc rename to unidad_7/pystock_management/__pycache__/stock_app.cpython-310.pyc diff --git a/unidad_7/pystock_management/main.py b/unidad_7/pystock_management/main.py new file mode 100644 index 0000000..eb9bd46 --- /dev/null +++ b/unidad_7/pystock_management/main.py @@ -0,0 +1,116 @@ +""" +Módulo principal para la ejecución de la aplicación de Gestión de Inventario. + +Este módulo contiene la lógica del menú interactivo y la función para +obtener datos de productos del usuario, actuando como la capa de Presentación. +""" +import sys +# Añadido para salida limpia +from typing import Union + +from src.services.inventario_manager import Inventario +from src.models.productos import Producto + + +def obtener_datos_producto() -> Union[Producto, None]: + """ + Solicita los datos del producto al usuario con validación. + + Maneja errores de conversión (ValueError) y errores de validación + lanzados por los setters de la clase Producto. + + Returns: + Producto: Una instancia de Producto si los datos son válidos, + o None en caso de error de entrada. + """ + try: + nombre = input("Nombre del producto: ").strip() + # Intentamos obtener y convertir precio y cantidad + precio = float(input("Precio del producto: ")) + cantidad = int(input("Cantidad del producto: ")) + codigo = input("Código del producto: ").strip() + + # Creamos la instancia de Producto. Las validaciones se ejecutan. + return Producto(nombre, precio, cantidad, codigo) + + except ValueError as e: + # Captura errores si float() o int() fallan, o si un setter lanza ValueError + print(f"Error de entrada de datos: {e}") + print("Asegúrate de ingresar tipos y valores correctos (números para precio/cantidad).") + return None + except TypeError as e: + print(f"Error de tipo: {e}") + return None + + +def main_menu(): + """ + Ejecuta el menú interactivo principal de la aplicación. + + Instancia el Inventario y gestiona el flujo de opciones del usuario. + """ + # Se asume que la clase Inventario cargará el stock al inicializarse + mi_inventario = Inventario() + + while True: + print("\n--- Sistema de Gestión de Inventario (Monolítico) ---") + print("1. Agregar producto") + print("2. Buscar producto") + print("3. Actualizar cantidad") + print("4. Eliminar producto") + print("5. Listar todos los productos") + print("6. Salir") + print("-----------------------------------------------------") + + try: + opcion = input("Selecciona una opción (1-6): ").strip() + + if opcion == "1": + print("\n>>> Agregar Producto <<<") + nuevo_producto = obtener_datos_producto() + if nuevo_producto: + mi_inventario.agg_prod(nuevo_producto) + + elif opcion == "2": + print("\n>>> Buscar Producto <<<") + mi_inventario.buscar_producto() + + elif opcion == "3": + print("\n>>> Actualizar Cantidad <<<") + mi_inventario.actualizar_cantidad() + + elif opcion == "4": + print("\n>>> Eliminar Producto <<<") + mi_inventario.eliminar_producto() + + elif opcion == "5": + print("\n>>> Listar Productos <<<") + mi_inventario.imprimir() + + elif opcion == "6": + # Se asume que save_stock() existe y guarda los datos + mi_inventario.save_stock() + print("\nSaliendo del sistema de inventario. ¡Hasta pronto! ") + # Se usa sys.exit para una salida limpia, aunque 'break' es suficiente + sys.exit(0) + + else: + print("Opción no válida.") + print("Por favor, selecciona un número del 1 al 6.") + + except (IOError, OSError, ValueError, TypeError) as e: + # Captura errores comunes de I/O, disco o validación interna + print(f"Error crítico en la operación: {e}") + except (KeyboardInterrupt, SystemExit) as e: + # Captura Ctrl+C y sys.exit. + if isinstance(e, SystemExit): + raise + + print("\nInterrupción detectada (Ctrl+C).") + print("Terminando el programa.") + sys.exit(1) + + +# Punto de entrada de la aplicación +if __name__ == "__main__": + main_menu() diff --git a/Unidad-7/PystockManagement/models/data_stock.json b/unidad_7/pystock_management/models/data_stock.json similarity index 100% rename from Unidad-7/PystockManagement/models/data_stock.json rename to unidad_7/pystock_management/models/data_stock.json diff --git a/Unidad-7/PystockManagement/src/models/__init__.py b/unidad_7/pystock_management/src/__init__.py similarity index 100% rename from Unidad-7/PystockManagement/src/models/__init__.py rename to unidad_7/pystock_management/src/__init__.py diff --git a/Unidad-7/PystockManagement/src/__pycache__/__init__.cpython-310.pyc b/unidad_7/pystock_management/src/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from Unidad-7/PystockManagement/src/__pycache__/__init__.cpython-310.pyc rename to unidad_7/pystock_management/src/__pycache__/__init__.cpython-310.pyc diff --git a/Unidad-7/PystockManagement/src/services/__init__.py b/unidad_7/pystock_management/src/models/__init__.py similarity index 100% rename from Unidad-7/PystockManagement/src/services/__init__.py rename to unidad_7/pystock_management/src/models/__init__.py diff --git a/Unidad-7/PystockManagement/src/models/__pycache__/__init__.cpython-310.pyc b/unidad_7/pystock_management/src/models/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from Unidad-7/PystockManagement/src/models/__pycache__/__init__.cpython-310.pyc rename to unidad_7/pystock_management/src/models/__pycache__/__init__.cpython-310.pyc diff --git a/Unidad-7/PystockManagement/src/models/__pycache__/productos.cpython-310.pyc b/unidad_7/pystock_management/src/models/__pycache__/productos.cpython-310.pyc similarity index 100% rename from Unidad-7/PystockManagement/src/models/__pycache__/productos.cpython-310.pyc rename to unidad_7/pystock_management/src/models/__pycache__/productos.cpython-310.pyc diff --git a/unidad_7/pystock_management/src/models/productos.py b/unidad_7/pystock_management/src/models/productos.py new file mode 100644 index 0000000..08217e6 --- /dev/null +++ b/unidad_7/pystock_management/src/models/productos.py @@ -0,0 +1,117 @@ +""" +Módulo Producto. + +Define el modelo de datos fundamental 'Producto', que encapsula los +atributos de un artículo de inventario y aplica validaciones estrictas +mediante el uso de propiedades (getters y setters). +""" + +# pylint: disable=R0902 +class Producto: + """ + Representa un producto con sus atributos de nombre, precio, cantidad y código. + + Utiliza propiedades (getters/setters) para encapsular y validar los datos. + + Attributes: + nombre (str): Nombre descriptivo del producto. + precio (float): Precio unitario (debe ser > 0). + cantidad (int): Cantidad en inventario (debe ser >= 0). + codigo (str): Identificador único del producto. + """ + + def __init__(self, nombre, precio, cantidad, codigo): + """ + Inicializa una nueva instancia de Producto. + + Al asignar a self.atributo, se llama automáticamente al setter para la validación inicial. + + Args: + nombre: Nombre del producto. + precio: Precio del producto. + cantidad: Cantidad en inventario. + codigo: Identificador único del producto. + """ + # Al asignar a self.atributo, se llama automáticamente al setter + self.nombre = nombre + self.precio = precio + self.cantidad = cantidad + self.codigo = codigo + + def __del__(self): + """Método de finalización que se llama + cuando el objeto es destruido + (garbage collection).""" + print(f"Objeto {self.codigo} - {self.nombre} eliminado") + + def __str__(self): + """Representación legible del objeto para impresión (dunder method).""" + return ( + f"Código: {self.codigo}\n Producto: {self.nombre}\n " + f"Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}" + ) + + # Métodos dunder de comparación + def __lt__(self, otro): + """Define la comparación 'menor que' (<) basada en el precio.""" + if not isinstance(otro, Producto): + return NotImplemented + return self.precio < otro.precio + + def __eq__(self, otro): + """Define la comparación 'igual a' (==) basada en la unicidad del código.""" + if not isinstance(otro, Producto): + return NotImplemented + return self.codigo == otro.codigo + + # --- Propiedades (Getters y Setters con validación) --- + # R0902 Corregido: Usamos _atributo en lugar de __atributo para + # evitar que Pylint cuente atributos duplicados. + + @property + def nombre(self): + """Obtiene el nombre del producto.""" + return self._nombre + + @nombre.setter + def nombre(self, nombre): + """Establece el nombre del producto, validando que sea una cadena no vacía.""" + if not isinstance(nombre, str) or not nombre.strip(): + raise ValueError("El nombre del producto debe ser una cadena no vacía.") + self._nombre = nombre + + @property + def precio(self): + """Obtiene el precio del producto.""" + return self._precio + + @precio.setter + def precio(self, precio): + """Establece el precio del producto, validando que sea un número positivo.""" + if not isinstance(precio, (int, float)) or precio <= 0: + raise ValueError("El precio debe ser un número positivo mayor que cero.") + self._precio = precio + + @property + def cantidad(self): + """Obtiene la cantidad en inventario.""" + return self._cantidad + + @cantidad.setter + def cantidad(self, cantidad): + """Establece la cantidad, validando que sea un número entero no negativo.""" + if not isinstance(cantidad, int) or cantidad < 0: + raise ValueError("La cantidad debe ser un número entero no negativo.") + self._cantidad = cantidad + + @property + def codigo(self): + """Obtiene el código del producto.""" + return self._codigo + + @codigo.setter + def codigo(self, codigo): + """Establece el código, validando que sea una cadena no vacía.""" + if not isinstance(codigo, str) or not codigo.strip(): + raise ValueError("El código del producto debe ser una cadena no vacía.") + self._codigo = codigo \ No newline at end of file diff --git a/unidad_7/pystock_management/src/services/__init__.py b/unidad_7/pystock_management/src/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Unidad-7/PystockManagement/src/services/__pycache__/__init__.cpython-310.pyc b/unidad_7/pystock_management/src/services/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from Unidad-7/PystockManagement/src/services/__pycache__/__init__.cpython-310.pyc rename to unidad_7/pystock_management/src/services/__pycache__/__init__.cpython-310.pyc diff --git a/Unidad-7/PystockManagement/src/services/__pycache__/inventario_manager.cpython-310.pyc b/unidad_7/pystock_management/src/services/__pycache__/inventario_manager.cpython-310.pyc similarity index 100% rename from Unidad-7/PystockManagement/src/services/__pycache__/inventario_manager.cpython-310.pyc rename to unidad_7/pystock_management/src/services/__pycache__/inventario_manager.cpython-310.pyc diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py new file mode 100644 index 0000000..bf7c099 --- /dev/null +++ b/unidad_7/pystock_management/src/services/inventario_manager.py @@ -0,0 +1,200 @@ +# pylint: disable=R0801 + + +import os +import json +from typing import Union + +from ..models.productos import Producto + +""" +Servicio de Gestión de Inventario. +Define la clase Inventario, que maneja la colección de objetos Producto +y las operaciones de persistencia de datos (JSON) para mantener el stock +""" + +class Inventario: + """ + Gestiona la colección de objetos Producto y sus operaciones de persistencia. + + Attributes: + route_file (str): Ruta completa al archivo JSON de stock. + productos (dict): Diccionario de productos, clave=código, valor=Objeto Producto. + """ + + def __init__(self, file_name: str = "data_stock.json"): + """ + Inicializa el Inventario, configura la ruta del archivo y carga el stock inicial. + + Args: + file_name: Nombre del archivo de persistencia JSON. + """ + # Crear referencia de ruta (asumiendo estructura src/services/inventario_manager.py) + current_dir = os.path.dirname(__file__) + # Construye la ruta para que apunte a la carpeta 'models' un nivel superior. + # Esto es un patrón común en proyectos modulados. + base_dir = os.path.join(current_dir, "..", "models_data") + + # Crear archio en la ruta indicada y nombre declarado + self.route_file = os.path.join(base_dir, file_name) + + # Asegurar que exista la ruta + if not os.path.exists(base_dir): + os.makedirs(base_dir) # Si la ruta NO existe ENTONCES la crea + + # Diccionario: clave=código (str), valor=objeto Producto + # El operador 'or {}' garantiza que self.productos sea un diccionario. + self.productos = self.load_stock() or {} + + def load_stock(self) -> Union[dict, None]: + """ + Carga los datos del inventario desde el archivo JSON de persistencia. + + Transforma los datos JSON cargados de vuelta a objetos Producto. + + Returns: + dict | None: Un diccionario con objetos Producto si la carga es exitosa, + o None si hay un error o el archivo no existe. + """ + if os.path.exists(self.route_file): + try: + # W1514: Especificar 'encoding="utf-8"' es buena práctica. + with open(self.route_file, "r", encoding="utf-8") as dicc: + data_loaded = json.load(dicc) + stock_loaded = {} + for codigo, data in data_loaded.items(): + # Aquí se reconstruye el objeto Producto + stock_loaded[codigo] = Producto( + data["nombre"], data["precio"], data["cantidad"], data["codigo"] + ) + return stock_loaded + except (json.JSONDecodeError, FileNotFoundError, TypeError) as e: + # Captura errores de JSON mal formado o problemas en la creación de Producto + print(f"Error al cargar/procesar el inventario desde '{self.route_file}': {e}") + print("Se iniciará un inventario Vacío.") + return None + + def save_stock(self): + """ + Guarda el inventario actual en el archivo JSON de persistencia. + + Convierte los objetos Producto a un diccionario básico para la serialización. + """ + # Convertir objetos en Producto a un diccionario básico + save_data = {} + for codigo, producto_obj in self.productos.items(): + save_data[codigo] = { + "codigo": producto_obj.codigo, + "nombre": producto_obj.nombre, + "precio": producto_obj.precio, + "cantidad": producto_obj.cantidad, + } + try: + # W1514: Especificar 'encoding="utf-8"' es buena práctica. + with open(self.route_file, "w", encoding="utf-8") as file: + json.dump(save_data, file, indent=4) + print(f"Éxito: Inventario guardado en '{self.route_file}'") + except IOError as e: + # W0718: Cambiado de 'Exception' a 'IOError' para manejo específico de disco/ruta. + print(f"Error: No fue posible guardar el inventario en '{self.route_file}'") + print(f"Detalle del error: {e}") + + def agg_prod(self, producto: Producto): + """ + Agrega un nuevo objeto Producto al inventario. + + Args: + producto: El objeto Producto a añadir. + """ + if not isinstance(producto, Producto): + print("Error: Solo se pueden agregar objetos de tipo 'Producto' al inventario.") + return + + if producto.codigo in self.productos: + print(f"Error: El producto con código '{producto.codigo}' ya existe en el inventario.") + return + + self.productos[producto.codigo] = producto + print(f"Éxito: Producto '{producto.nombre}' (Código: {producto.codigo}) agregado.") + + def imprimir(self): + """Muestra una lista detallada de todos los productos en el inventario.""" + if not self.productos: + print("El inventario está vacío. No hay productos para mostrar.") + return + + print("\n--- Listado de Productos en Inventario ---") + # Itera sobre los valores (objetos Producto) del diccionario + for producto_obj in self.productos.values(): + print(producto_obj) + print("-" * 30) + print("--- Fin del Listado ---") + + def buscar_producto(self): + """Solicita un código y muestra la información del producto si existe.""" + id_search = input("Ingrese el código del producto a buscar: ").strip() + + if id_search in self.productos: + # Acceso directo al objeto Producto por su código + resultado_producto = self.productos[id_search] + print("\n ¡Producto Encontrado!") + print(resultado_producto) + print("--------------------------\n") + else: + print(f"Error: El producto con el código {id_search} no se encuentra en el inventario.") + + def actualizar_cantidad(self): + """Solicita un código y actualiza la cantidad de ese producto.""" + id_search = input("Ingrese el código del producto a actualizar: ").strip() + + if id_search not in self.productos: + print(f"Error: Producto con código '{id_search}' no encontrado.") + return + + producto_a_actualizar = self.productos[id_search] + + # Lógica de validación de entrada + while True: + try: + # Muestra el nombre del producto que se está actualizando + new_cantidad_str = input( + f"Ingrese la nueva cantidad para '{producto_a_actualizar.nombre}' " + f"(actual: {producto_a_actualizar.cantidad}): " + ).strip() + new_cantidad = int(new_cantidad_str) + + if new_cantidad < 0: + print("Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo.") + continue + + break + + except ValueError: + print("Error de Valor: Se espera una cantidad numérica entera.") + + # Asigna la nueva cantidad (llamando al setter de Producto) + try: + producto_a_actualizar.cantidad = new_cantidad + print( + f"Éxito: Cantidad del producto '{producto_a_actualizar.nombre}'" + f"actualizada a {new_cantidad}." + ) + except ValueError as e: + # Captura si el setter de Producto rechaza el valor + print(f"Error de validación: {e}") + + def eliminar_producto(self): + """Solicita un código y elimina el producto del inventario.""" + id_search = input("Ingrese el código del producto a eliminar: ").strip() + + if id_search not in self.productos: + print(f"Error: Producto con código '{id_search}' no encontrado.") + return + + # Opcional: Mostrar nombre antes de eliminar + nombre_producto = self.productos[id_search].nombre + + del self.productos[id_search] # Elimina la referencia del diccionario + print( + f"Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario." + )