From fb9e6b3d266a44e63d7440e100ad38fd1eda22e8 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 17 Nov 2025 20:59:46 -0400
Subject: [PATCH 001/138] Actualizar package.json
---
Wolves-Page/package.json | 1 -
1 file changed, 1 deletion(-)
diff --git a/Wolves-Page/package.json b/Wolves-Page/package.json
index 15da0f2..fd57e77 100644
--- a/Wolves-Page/package.json
+++ b/Wolves-Page/package.json
@@ -9,7 +9,6 @@
"@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",
From cc9793b9562080fc9bfdd489a2411da29cc79ea1 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:07:58 -0400
Subject: [PATCH 002/138] Add GitHub Actions workflow to build documentation
---
.github/workflows/build-docs.yml | 47 ++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 .github/workflows/build-docs.yml
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
new file mode 100644
index 0000000..62dfe99
--- /dev/null
+++ b/.github/workflows/build-docs.yml
@@ -0,0 +1,47 @@
+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@v3
+
+ # 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: Convert to PDF
+ run: pandoc GLOBAL.md -o GLOBAL.pdf
+
+ # 7. Subir artefactos generados (JSON y PDF)
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: portfolio-docs
+ path: |
+ projects.json
+ GLOBAL.pdf
From 113a1efba9330445b2b7388f0bee29cf84d31971 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:09:59 -0400
Subject: [PATCH 003/138] Add CodeQL analysis workflow
This workflow file sets up CodeQL analysis for multiple languages on push and pull request events, as well as on a schedule.
---
.github/workflows/codeql.yml | 105 +++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
create mode 100644 .github/workflows/codeql.yml
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}}"
From 12118e39fdbaa772a8e277d75a24d6c79160b649 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:14:42 -0400
Subject: [PATCH 004/138] Add GitHub Actions workflow for labeling PRs
This workflow automatically applies labels to pull requests based on modified paths.
---
.github/workflows/label.yml | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 .github/workflows/label.yml
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 }}"
From 495266498de43397c26de63e9d5e6ceb9eef2e14 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:15:27 -0400
Subject: [PATCH 005/138] Add GitHub Actions workflow to summarize issues
---
.github/workflows/summary.yml | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 .github/workflows/summary.yml
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 }}
From f36b170092a690177d87711e27f6eef8d5b1d365 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:18:25 -0400
Subject: [PATCH 006/138] Add Pylint workflow for Python code analysis
---
.github/workflows/pylint.yml | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 .github/workflows/pylint.yml
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
new file mode 100644
index 0000000..c73e032
--- /dev/null
+++ b/.github/workflows/pylint.yml
@@ -0,0 +1,23 @@
+name: Pylint
+
+on: [push]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ["3.8", "3.9", "3.10"]
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v3
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install pylint
+ - name: Analysing the code with pylint
+ run: |
+ pylint $(git ls-files '*.py')
From aeeb8243de47dd67dd31013f73177506dbf7ada4 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:19:37 -0400
Subject: [PATCH 007/138] Add GitHub Actions workflow for Python application
This workflow installs Python dependencies, runs linting with flake8, and executes tests with pytest.
---
.github/workflows/python-app.yml | 39 ++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 .github/workflows/python-app.yml
diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
new file mode 100644
index 0000000..1168bd9
--- /dev/null
+++ b/.github/workflows/python-app.yml
@@ -0,0 +1,39 @@
+# This workflow will install Python dependencies, run tests and lint with a single version of Python
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
+
+name: Python application
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v3
+ with:
+ python-version: "3.10"
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install flake8 pytest
+ if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ - name: Lint with flake8
+ run: |
+ # stop the build if there are Python syntax errors or undefined names
+ flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+ # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
+ flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+ - name: Test with pytest
+ run: |
+ pytest
From b9e9d83e2c23c47c0f3322fc6eda8ffb3c65a710 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:25:40 -0400
Subject: [PATCH 008/138] Add script to scan projects and save to JSON
The script scans the repository for projects containing a README.md file and collects their details, including an optional icon file. The project information is then saved to a JSON file.
---
scripts/scan_projects.py | 41 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 scripts/scan_projects.py
diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py
new file mode 100644
index 0000000..cc2ec7f
--- /dev/null
+++ b/scripts/scan_projects.py
@@ -0,0 +1,41 @@
+import os
+import json
+
+# Ruta base del repositorio (puedes ajustar si lo necesitas)
+repo_path = "."
+
+projects = []
+
+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)
+
+ # 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,
+ "url": f"https://github.com/USUARIO/REPO/tree/main/{project_name}"
+ }
+
+ projects.append(project)
+
+# Guardar en projects.json
+with open("projects.json", "w", encoding="utf-8") as f:
+ json.dump(projects, f, indent=2, ensure_ascii=False)
+
+print(f"✅ Generados {len(projects)} proyectos en projects.json")
From 577d6ed0e4b50c0769c6034d8ba78372b0ef0738 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:26:56 -0400
Subject: [PATCH 009/138] Add script to concatenate all README.md files
This script concatenates all README.md files from the repository into a single GLOBAL.md file, ignoring hidden directories and the .git folder.
---
scripts/concat_readmes.py | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 scripts/concat_readmes.py
diff --git a/scripts/concat_readmes.py b/scripts/concat_readmes.py
new file mode 100644
index 0000000..465c93a
--- /dev/null
+++ b/scripts/concat_readmes.py
@@ -0,0 +1,31 @@
+import os
+
+# Ruta base del repositorio
+repo_path = "."
+
+# Archivo de salida
+output_file = "GLOBAL.md"
+
+with open(output_file, "w", encoding="utf-8") as global_file:
+ 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
+
+ # Si existe un README.md en la carpeta, lo añadimos
+ if "README.md" in files:
+ project_name = os.path.basename(root)
+
+ # 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")
+ with open(readme_path, encoding="utf-8") as readme_file:
+ global_file.write(readme_file.read())
+ global_file.write("\n\n")
+
+ # Separador entre proyectos
+ global_file.write("---\n\n")
+
+print(f"✅ Archivo {output_file} generado con todos los README.md")
From 31a466060bdebb135b31939ad8ac04ef1e1898c9 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:30:50 -0400
Subject: [PATCH 010/138] Upgrade GitHub Actions to use latest versions
---
.github/workflows/build-docs.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index 62dfe99..eaa13cd 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -12,7 +12,7 @@ jobs:
steps:
# 1. Clonar el repositorio
- name: Checkout repo
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# 2. Configurar Python (para scripts de documentación)
- name: Setup Python
@@ -39,7 +39,7 @@ jobs:
# 7. Subir artefactos generados (JSON y PDF)
- name: Upload artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: portfolio-docs
path: |
From 96d8873226543eb63a40b6edb9ac4871522bb6fb Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:34:33 -0400
Subject: [PATCH 011/138] Add Pandoc installation step in build-docs workflow
---
.github/workflows/build-docs.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index eaa13cd..9997503 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -34,6 +34,8 @@ jobs:
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: Convert to PDF
run: pandoc GLOBAL.md -o GLOBAL.pdf
From 1ad84496aca71b95908b220e319fba7096fa817a Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:43:31 -0400
Subject: [PATCH 012/138] Install LaTeX engine for PDF generation
Added installation of LaTeX engine for PDF conversion.
---
.github/workflows/build-docs.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index 9997503..4fc8c6b 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -36,6 +36,8 @@ jobs:
# 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
From 8fdce670d4c79a09f4f43ece6ee3e3e44a62493b Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 11:54:52 -0400
Subject: [PATCH 013/138] Actualizar Listar Archivos en Drive por fecha.py
---
Ejercicio 1/Listar Archivos en Drive por fecha.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ejercicio 1/Listar Archivos en Drive por fecha.py b/Ejercicio 1/Listar Archivos en Drive por fecha.py
index 01e052b..41fe8f7 100644
--- a/Ejercicio 1/Listar Archivos en Drive por fecha.py
+++ b/Ejercicio 1/Listar Archivos en Drive por fecha.py
@@ -11,7 +11,7 @@
def list_arch(directory):
try:
files = os.listdir(directory)
- files_with_dates = []
+ files_dates = []
for file in files:
file_path = os.path.join(directory, file)
From 595f3aafdb9c5d0b9919aa8d29dea924f8c28037 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 12:00:18 -0400
Subject: [PATCH 014/138] Actualizar Listar Archivos en Drive por fecha.py
---
Ejercicio 1/Listar Archivos en Drive por fecha.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/Ejercicio 1/Listar Archivos en Drive por fecha.py b/Ejercicio 1/Listar Archivos en Drive por fecha.py
index 41fe8f7..3f86336 100644
--- a/Ejercicio 1/Listar Archivos en Drive por fecha.py
+++ b/Ejercicio 1/Listar Archivos en Drive por fecha.py
@@ -1,17 +1,16 @@
import os
-from google.colab import drive
import time
# Montar Google Drive
-drive.mount('/content/drive')
+# drive.mount('/content/drive') # Solo necesario en Colab
# Definir el directorio
-DIRUTH= '/content/drive/My Drive/'
+DIRUTH = '/content/drive/My Drive/'
def list_arch(directory):
try:
files = os.listdir(directory)
- files_dates = []
+ file_dates = [] # Aquí definimos correctamente la variable
for file in files:
file_path = os.path.join(directory, file)
@@ -35,4 +34,4 @@ def list_arch(directory):
print(f"Ocurrió un error: {e}")
# Llamar a la función
-list_arch(DIRUTH)
+list_arch(DIRUTH)
\ No newline at end of file
From ab9744cb1d10e5916f6c5f5cde24d1b0b01e2e29 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 12:19:14 -0400
Subject: [PATCH 015/138] Actualizar Listar Archivos en Drive por fecha.py
---
.../Listar Archivos en Drive por fecha.py | 42 +++++++++++++++----
1 file changed, 34 insertions(+), 8 deletions(-)
diff --git a/Ejercicio 1/Listar Archivos en Drive por fecha.py b/Ejercicio 1/Listar Archivos en Drive por fecha.py
index 3f86336..2b7ea39 100644
--- a/Ejercicio 1/Listar Archivos en Drive por fecha.py
+++ b/Ejercicio 1/Listar Archivos en Drive por fecha.py
@@ -1,8 +1,7 @@
import os
import time
-
-# Montar Google Drive
-# drive.mount('/content/drive') # Solo necesario en Colab
+import unittest
+from unittest.mock import patch
# Definir el directorio
DIRUTH = '/content/drive/My Drive/'
@@ -10,22 +9,22 @@
def list_arch(directory):
try:
files = os.listdir(directory)
- file_dates = [] # Aquí definimos correctamente la variable
+ file_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)}")
+ return file_dates # útil para pruebas
+
except FileNotFoundError:
print("Directorio no encontrado.")
except PermissionError:
@@ -33,5 +32,32 @@ def list_arch(directory):
except Exception as e:
print(f"Ocurrió un error: {e}")
-# Llamar a la función
-list_arch(DIRUTH)
\ No newline at end of file
+# ============================
+# Pruebas unitarias
+# ============================
+
+class TestListArch(unittest.TestCase):
+
+ @patch('os.listdir')
+ @patch('os.path.isfile')
+ @patch('os.path.getmtime')
+ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
+ mock_listdir.return_value = ['a.txt', 'b.txt', 'c.txt']
+ mock_isfile.return_value = True
+ 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, mock_listdir):
+ result = list_arch('missing_dir')
+ self.assertIsNone(result)
+
+# ============================
+# Ejecutar pruebas si es main
+# ============================
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
From b1116f6ac1abd20dbf53d6a466b909c175dd296e Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 12:26:02 -0400
Subject: [PATCH 016/138] Crear tests/Ejercicio 1/test_.py
---
tests/Ejercicio 1/test_.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 tests/Ejercicio 1/test_.py
diff --git a/tests/Ejercicio 1/test_.py b/tests/Ejercicio 1/test_.py
new file mode 100644
index 0000000..760f850
--- /dev/null
+++ b/tests/Ejercicio 1/test_.py
@@ -0,0 +1,16 @@
+import unittest
+from unittest.mock import patch
+from listar_archivos import list_arch
+
+class TestListArch(unittest.TestCase):
+ @patch('os.listdir')
+ @patch('os.path.isfile')
+ @patch('os.path.getmtime')
+ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
+ mock_listdir.return_value = ['a.txt', 'b.txt', 'c.txt']
+ mock_isfile.return_value = True
+ 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)
\ No newline at end of file
From 61ec01e9decca06e9ef297e1af0eb683090e7408 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 12:35:08 -0400
Subject: [PATCH 017/138] Rename file to listar_archivos_drive.py
---
.../listar_archivos_drive.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename Ejercicio 1/Listar Archivos en Drive por fecha.py => Unidad1/listar_archivos_drive.py (98%)
diff --git a/Ejercicio 1/Listar Archivos en Drive por fecha.py b/Unidad1/listar_archivos_drive.py
similarity index 98%
rename from Ejercicio 1/Listar Archivos en Drive por fecha.py
rename to Unidad1/listar_archivos_drive.py
index 2b7ea39..7a5e3d5 100644
--- a/Ejercicio 1/Listar Archivos en Drive por fecha.py
+++ b/Unidad1/listar_archivos_drive.py
@@ -60,4 +60,4 @@ def test_directory_not_found(self, mock_listdir):
# ============================
if __name__ == '__main__':
- unittest.main()
\ No newline at end of file
+ unittest.main()
From 1e5018afccfd622dfe4bbf82d1f61679da66eee1 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 12:36:02 -0400
Subject: [PATCH 018/138] Delete Ejercicio 1 directory
---
Ejercicio 1/README.md | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 Ejercicio 1/README.md
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
From 2a328a773d4f4a2e8ec1586eed1035a730c15384 Mon Sep 17 00:00:00 2001
From: WolveJC05 <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 16:48:37 +0000
Subject: [PATCH 019/138] pep8 convicted
---
{Unidad1 => unidad_1}/listar_archivos_drive.py | 0
...ion-inventario-interactiva_varias-tablas-v1.2.py | 0
.../Gestion-restorant-v0.2alpha.py | 0
.../Gestion-restorant-v0.5beta.py | 0
.../Gestion-restorant-varias-tablasv1.0.py | 0
.../Gestion-restorant_una-tabla-v1.0.py | 0
{Unidad 2 => unidad_2}/ReadMe.md | 0
{Unidad 3 => unidad_3}/Flask/ReadMe.md | 0
{Unidad 3 => unidad_3}/Flask/app.py | 0
{Unidad 3 => unidad_3}/Flask/requisistos.txt | 0
{Unidad 3 => unidad_3}/Flask/static/css/styles.css | 0
{Unidad 3 => unidad_3}/Flask/static/css/syle.css | 0
{Unidad 3 => unidad_3}/Flask/static/image/ReadME.md | 0
{Unidad 3 => unidad_3}/Flask/static/js/Script.js | 0
{Unidad 3 => unidad_3}/Flask/static/js/script.js | 0
{Unidad 3 => unidad_3}/Flask/static/js/scripts.js | 0
{Unidad 3 => unidad_3}/Flask/templates/base.html | 0
.../Flask/templates/crear_inventario.html | 0
.../Flask/templates/pedir_proveedor.html | 0
.../Flask/templates/pedir_stock.html | 0
.../Flask/templates/subir_csv.html | 0
.../Flask/uploads/registro_inventario_prueba.csv | 0
.../ReadME.md" | 0
{Unidad-7 => unidad_7}/Items-Management.py | 0
.../PystockManagement/.gitignore | 0
.../PystockManagement/__init__.py | 0
.../__pycache__/main.cpython-310.pyc | Bin
.../__pycache__/stock_app.cpython-310.pyc | Bin
{Unidad-7 => unidad_7}/PystockManagement/main.py | 0
.../PystockManagement/models/data_stock.json | 0
.../PystockManagement/src/__init__.py | 0
.../src/__pycache__/__init__.cpython-310.pyc | Bin
.../PystockManagement/src/models/__init__.py | 0
.../src/models/__pycache__/__init__.cpython-310.pyc | Bin
.../models/__pycache__/productos.cpython-310.pyc | Bin
.../PystockManagement/src/models/productos.py | 0
.../PystockManagement/src/services/__init__.py | 0
.../services/__pycache__/__init__.cpython-310.pyc | Bin
.../__pycache__/inventario_manager.cpython-310.pyc | Bin
.../src/services/inventario_manager.py | 0
40 files changed, 0 insertions(+), 0 deletions(-)
rename {Unidad1 => unidad_1}/listar_archivos_drive.py (100%)
rename {Unidad 2 => unidad_2}/Gestion-inventario-interactiva_varias-tablas-v1.2.py (100%)
rename {Unidad 2 => unidad_2}/Gestion-restorant-v0.2alpha.py (100%)
rename {Unidad 2 => unidad_2}/Gestion-restorant-v0.5beta.py (100%)
rename {Unidad 2 => unidad_2}/Gestion-restorant-varias-tablasv1.0.py (100%)
rename {Unidad 2 => unidad_2}/Gestion-restorant_una-tabla-v1.0.py (100%)
rename {Unidad 2 => unidad_2}/ReadMe.md (100%)
rename {Unidad 3 => unidad_3}/Flask/ReadMe.md (100%)
rename {Unidad 3 => unidad_3}/Flask/app.py (100%)
rename {Unidad 3 => unidad_3}/Flask/requisistos.txt (100%)
rename {Unidad 3 => unidad_3}/Flask/static/css/styles.css (100%)
rename {Unidad 3 => unidad_3}/Flask/static/css/syle.css (100%)
rename {Unidad 3 => unidad_3}/Flask/static/image/ReadME.md (100%)
rename {Unidad 3 => unidad_3}/Flask/static/js/Script.js (100%)
rename {Unidad 3 => unidad_3}/Flask/static/js/script.js (100%)
rename {Unidad 3 => unidad_3}/Flask/static/js/scripts.js (100%)
rename {Unidad 3 => unidad_3}/Flask/templates/base.html (100%)
rename {Unidad 3 => unidad_3}/Flask/templates/crear_inventario.html (100%)
rename {Unidad 3 => unidad_3}/Flask/templates/pedir_proveedor.html (100%)
rename {Unidad 3 => unidad_3}/Flask/templates/pedir_stock.html (100%)
rename {Unidad 3 => unidad_3}/Flask/templates/subir_csv.html (100%)
rename {Unidad 3 => unidad_3}/Flask/uploads/registro_inventario_prueba.csv (100%)
rename "Unidad 3/Gesti\303\263n de inventario y pedidos/ReadME.md" => "unidad_3/Gesti\303\263n de inventario y pedidos/ReadME.md" (100%)
rename {Unidad-7 => unidad_7}/Items-Management.py (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/.gitignore (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/__init__.py (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/__pycache__/main.cpython-310.pyc (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/__pycache__/stock_app.cpython-310.pyc (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/main.py (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/models/data_stock.json (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/__init__.py (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/__pycache__/__init__.cpython-310.pyc (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/models/__init__.py (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/models/__pycache__/__init__.cpython-310.pyc (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/models/__pycache__/productos.cpython-310.pyc (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/models/productos.py (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/services/__init__.py (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/services/__pycache__/__init__.cpython-310.pyc (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/services/__pycache__/inventario_manager.cpython-310.pyc (100%)
rename {Unidad-7 => unidad_7}/PystockManagement/src/services/inventario_manager.py (100%)
diff --git a/Unidad1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
similarity index 100%
rename from Unidad1/listar_archivos_drive.py
rename to unidad_1/listar_archivos_drive.py
diff --git a/Unidad 2/Gestion-inventario-interactiva_varias-tablas-v1.2.py b/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
similarity index 100%
rename from Unidad 2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
rename to unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
diff --git a/Unidad 2/Gestion-restorant-v0.2alpha.py b/unidad_2/Gestion-restorant-v0.2alpha.py
similarity index 100%
rename from Unidad 2/Gestion-restorant-v0.2alpha.py
rename to unidad_2/Gestion-restorant-v0.2alpha.py
diff --git a/Unidad 2/Gestion-restorant-v0.5beta.py b/unidad_2/Gestion-restorant-v0.5beta.py
similarity index 100%
rename from Unidad 2/Gestion-restorant-v0.5beta.py
rename to unidad_2/Gestion-restorant-v0.5beta.py
diff --git a/Unidad 2/Gestion-restorant-varias-tablasv1.0.py b/unidad_2/Gestion-restorant-varias-tablasv1.0.py
similarity index 100%
rename from Unidad 2/Gestion-restorant-varias-tablasv1.0.py
rename to unidad_2/Gestion-restorant-varias-tablasv1.0.py
diff --git a/Unidad 2/Gestion-restorant_una-tabla-v1.0.py b/unidad_2/Gestion-restorant_una-tabla-v1.0.py
similarity index 100%
rename from Unidad 2/Gestion-restorant_una-tabla-v1.0.py
rename to unidad_2/Gestion-restorant_una-tabla-v1.0.py
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 3/Flask/ReadMe.md b/unidad_3/Flask/ReadMe.md
similarity index 100%
rename from Unidad 3/Flask/ReadMe.md
rename to unidad_3/Flask/ReadMe.md
diff --git a/Unidad 3/Flask/app.py b/unidad_3/Flask/app.py
similarity index 100%
rename from Unidad 3/Flask/app.py
rename to unidad_3/Flask/app.py
diff --git a/Unidad 3/Flask/requisistos.txt b/unidad_3/Flask/requisistos.txt
similarity index 100%
rename from Unidad 3/Flask/requisistos.txt
rename to unidad_3/Flask/requisistos.txt
diff --git a/Unidad 3/Flask/static/css/styles.css b/unidad_3/Flask/static/css/styles.css
similarity index 100%
rename from Unidad 3/Flask/static/css/styles.css
rename to unidad_3/Flask/static/css/styles.css
diff --git a/Unidad 3/Flask/static/css/syle.css b/unidad_3/Flask/static/css/syle.css
similarity index 100%
rename from Unidad 3/Flask/static/css/syle.css
rename to unidad_3/Flask/static/css/syle.css
diff --git a/Unidad 3/Flask/static/image/ReadME.md b/unidad_3/Flask/static/image/ReadME.md
similarity index 100%
rename from Unidad 3/Flask/static/image/ReadME.md
rename to unidad_3/Flask/static/image/ReadME.md
diff --git a/Unidad 3/Flask/static/js/Script.js b/unidad_3/Flask/static/js/Script.js
similarity index 100%
rename from Unidad 3/Flask/static/js/Script.js
rename to unidad_3/Flask/static/js/Script.js
diff --git a/Unidad 3/Flask/static/js/script.js b/unidad_3/Flask/static/js/script.js
similarity index 100%
rename from Unidad 3/Flask/static/js/script.js
rename to unidad_3/Flask/static/js/script.js
diff --git a/Unidad 3/Flask/static/js/scripts.js b/unidad_3/Flask/static/js/scripts.js
similarity index 100%
rename from Unidad 3/Flask/static/js/scripts.js
rename to unidad_3/Flask/static/js/scripts.js
diff --git a/Unidad 3/Flask/templates/base.html b/unidad_3/Flask/templates/base.html
similarity index 100%
rename from Unidad 3/Flask/templates/base.html
rename to unidad_3/Flask/templates/base.html
diff --git a/Unidad 3/Flask/templates/crear_inventario.html b/unidad_3/Flask/templates/crear_inventario.html
similarity index 100%
rename from Unidad 3/Flask/templates/crear_inventario.html
rename to unidad_3/Flask/templates/crear_inventario.html
diff --git a/Unidad 3/Flask/templates/pedir_proveedor.html b/unidad_3/Flask/templates/pedir_proveedor.html
similarity index 100%
rename from Unidad 3/Flask/templates/pedir_proveedor.html
rename to unidad_3/Flask/templates/pedir_proveedor.html
diff --git a/Unidad 3/Flask/templates/pedir_stock.html b/unidad_3/Flask/templates/pedir_stock.html
similarity index 100%
rename from Unidad 3/Flask/templates/pedir_stock.html
rename to unidad_3/Flask/templates/pedir_stock.html
diff --git a/Unidad 3/Flask/templates/subir_csv.html b/unidad_3/Flask/templates/subir_csv.html
similarity index 100%
rename from Unidad 3/Flask/templates/subir_csv.html
rename to unidad_3/Flask/templates/subir_csv.html
diff --git a/Unidad 3/Flask/uploads/registro_inventario_prueba.csv b/unidad_3/Flask/uploads/registro_inventario_prueba.csv
similarity index 100%
rename from Unidad 3/Flask/uploads/registro_inventario_prueba.csv
rename to unidad_3/Flask/uploads/registro_inventario_prueba.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
similarity index 100%
rename from Unidad-7/Items-Management.py
rename to unidad_7/Items-Management.py
diff --git a/Unidad-7/PystockManagement/.gitignore b/unidad_7/PystockManagement/.gitignore
similarity index 100%
rename from Unidad-7/PystockManagement/.gitignore
rename to unidad_7/PystockManagement/.gitignore
diff --git a/Unidad-7/PystockManagement/__init__.py b/unidad_7/PystockManagement/__init__.py
similarity index 100%
rename from Unidad-7/PystockManagement/__init__.py
rename to unidad_7/PystockManagement/__init__.py
diff --git a/Unidad-7/PystockManagement/__pycache__/main.cpython-310.pyc b/unidad_7/PystockManagement/__pycache__/main.cpython-310.pyc
similarity index 100%
rename from Unidad-7/PystockManagement/__pycache__/main.cpython-310.pyc
rename to unidad_7/PystockManagement/__pycache__/main.cpython-310.pyc
diff --git a/Unidad-7/PystockManagement/__pycache__/stock_app.cpython-310.pyc b/unidad_7/PystockManagement/__pycache__/stock_app.cpython-310.pyc
similarity index 100%
rename from Unidad-7/PystockManagement/__pycache__/stock_app.cpython-310.pyc
rename to unidad_7/PystockManagement/__pycache__/stock_app.cpython-310.pyc
diff --git a/Unidad-7/PystockManagement/main.py b/unidad_7/PystockManagement/main.py
similarity index 100%
rename from Unidad-7/PystockManagement/main.py
rename to unidad_7/PystockManagement/main.py
diff --git a/Unidad-7/PystockManagement/models/data_stock.json b/unidad_7/PystockManagement/models/data_stock.json
similarity index 100%
rename from Unidad-7/PystockManagement/models/data_stock.json
rename to unidad_7/PystockManagement/models/data_stock.json
diff --git a/Unidad-7/PystockManagement/src/__init__.py b/unidad_7/PystockManagement/src/__init__.py
similarity index 100%
rename from Unidad-7/PystockManagement/src/__init__.py
rename to unidad_7/PystockManagement/src/__init__.py
diff --git a/Unidad-7/PystockManagement/src/__pycache__/__init__.cpython-310.pyc b/unidad_7/PystockManagement/src/__pycache__/__init__.cpython-310.pyc
similarity index 100%
rename from Unidad-7/PystockManagement/src/__pycache__/__init__.cpython-310.pyc
rename to unidad_7/PystockManagement/src/__pycache__/__init__.cpython-310.pyc
diff --git a/Unidad-7/PystockManagement/src/models/__init__.py b/unidad_7/PystockManagement/src/models/__init__.py
similarity index 100%
rename from Unidad-7/PystockManagement/src/models/__init__.py
rename to unidad_7/PystockManagement/src/models/__init__.py
diff --git a/Unidad-7/PystockManagement/src/models/__pycache__/__init__.cpython-310.pyc b/unidad_7/PystockManagement/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/PystockManagement/src/models/__pycache__/__init__.cpython-310.pyc
diff --git a/Unidad-7/PystockManagement/src/models/__pycache__/productos.cpython-310.pyc b/unidad_7/PystockManagement/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/PystockManagement/src/models/__pycache__/productos.cpython-310.pyc
diff --git a/Unidad-7/PystockManagement/src/models/productos.py b/unidad_7/PystockManagement/src/models/productos.py
similarity index 100%
rename from Unidad-7/PystockManagement/src/models/productos.py
rename to unidad_7/PystockManagement/src/models/productos.py
diff --git a/Unidad-7/PystockManagement/src/services/__init__.py b/unidad_7/PystockManagement/src/services/__init__.py
similarity index 100%
rename from Unidad-7/PystockManagement/src/services/__init__.py
rename to unidad_7/PystockManagement/src/services/__init__.py
diff --git a/Unidad-7/PystockManagement/src/services/__pycache__/__init__.cpython-310.pyc b/unidad_7/PystockManagement/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/PystockManagement/src/services/__pycache__/__init__.cpython-310.pyc
diff --git a/Unidad-7/PystockManagement/src/services/__pycache__/inventario_manager.cpython-310.pyc b/unidad_7/PystockManagement/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/PystockManagement/src/services/__pycache__/inventario_manager.cpython-310.pyc
diff --git a/Unidad-7/PystockManagement/src/services/inventario_manager.py b/unidad_7/PystockManagement/src/services/inventario_manager.py
similarity index 100%
rename from Unidad-7/PystockManagement/src/services/inventario_manager.py
rename to unidad_7/PystockManagement/src/services/inventario_manager.py
From f8f41ac85288da738d76b5b7eea3c4a5c0919e9a Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 12:59:29 -0400
Subject: [PATCH 020/138] Crear __init__.py
---
unidad_1/__init__.py | 1 +
1 file changed, 1 insertion(+)
create mode 100644 unidad_1/__init__.py
diff --git a/unidad_1/__init__.py b/unidad_1/__init__.py
new file mode 100644
index 0000000..4287ca8
--- /dev/null
+++ b/unidad_1/__init__.py
@@ -0,0 +1 @@
+#
\ No newline at end of file
From f91778c6dabe62473f5d2498871594018efda641 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 12:59:42 -0400
Subject: [PATCH 021/138] Actualizar __init__.py
---
unidad_1/__init__.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/unidad_1/__init__.py b/unidad_1/__init__.py
index 4287ca8..e69de29 100644
--- a/unidad_1/__init__.py
+++ b/unidad_1/__init__.py
@@ -1 +0,0 @@
-#
\ No newline at end of file
From 99e2ffc9c73c450000c050e76d1a0e0451deb27a Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 13:06:01 -0400
Subject: [PATCH 022/138] Update import path for list_arch function
---
tests/{Ejercicio 1 => unidad_1}/test_.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename tests/{Ejercicio 1 => unidad_1}/test_.py (84%)
diff --git a/tests/Ejercicio 1/test_.py b/tests/unidad_1/test_.py
similarity index 84%
rename from tests/Ejercicio 1/test_.py
rename to tests/unidad_1/test_.py
index 760f850..0cb56c8 100644
--- a/tests/Ejercicio 1/test_.py
+++ b/tests/unidad_1/test_.py
@@ -1,6 +1,6 @@
import unittest
from unittest.mock import patch
-from listar_archivos import list_arch
+from unidad_1.listar_archivos_drive import list_arch
class TestListArch(unittest.TestCase):
@patch('os.listdir')
@@ -13,4 +13,4 @@ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
result = list_arch('fake_dir')
expected = [('b.txt', 300), ('c.txt', 200), ('a.txt', 100)]
- self.assertEqual(result, expected)
\ No newline at end of file
+ self.assertEqual(result, expected)
From 88602c3d98c010329b065e4c1b8e5053a84df7b9 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 13:25:59 -0400
Subject: [PATCH 023/138] Enhance Python workflow for multiple versions and
coverage
Updated the Python GitHub Actions workflow to support multiple Python versions and added pytest-cov for coverage reporting.
---
.github/workflows/python-app.yml | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
index 1168bd9..aaabb02 100644
--- a/.github/workflows/python-app.yml
+++ b/.github/workflows/python-app.yml
@@ -1,6 +1,3 @@
-# This workflow will install Python dependencies, run tests and lint with a single version of Python
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
-
name: Python application
on:
@@ -14,26 +11,39 @@ permissions:
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 3.10
+
+ - name: Set up Python
uses: actions/setup-python@v3
with:
- python-version: "3.10"
+ 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
+ pip install flake8 pytest pytest-cov
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+
- name: Lint with flake8
run: |
- # stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
- # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+
- name: Test with pytest
run: |
- pytest
+ pip install pytest-cov
+ PYTHONPATH=. pytest tests/ --cov=unidad_1 --cov-report=term-missing
From 8830d4f4d7b2fea7be7f4374785f075f35520f95 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 14:08:54 -0400
Subject: [PATCH 024/138] Crear webpack.config.js
---
Wolves-Page/webpack.config.js | 51 +++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 Wolves-Page/webpack.config.js
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
From f91f2c19ccfa1016dd4eb9492cbeed51793ea000 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 14:09:25 -0400
Subject: [PATCH 025/138] Actualizar package.json
---
Wolves-Page/package.json | 51 +++++++++++++++-------------------------
1 file changed, 19 insertions(+), 32 deletions(-)
diff --git a/Wolves-Page/package.json b/Wolves-Page/package.json
index fd57e77..34eb37d 100644
--- a/Wolves-Page/package.json
+++ b/Wolves-Page/package.json
@@ -4,48 +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",
"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
From fbbcd6d3fcdd18dc0e4ab475b1b506c086dbdc25 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 14:10:10 -0400
Subject: [PATCH 026/138] Actualizar index.html
---
Wolves-Page/public/index.html | 42 +++++++----------------------------
1 file changed, 8 insertions(+), 34 deletions(-)
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
-
+
You need to enable JavaScript to run this app.
-
-
+
\ No newline at end of file
From 5f60966084fef9b4b9d6fb93e29187b45436bf9a Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 16:11:43 -0400
Subject: [PATCH 027/138] Delete unidad_3/Flask directory
---
unidad_3/Flask/ReadMe.md | 106 ------
unidad_3/Flask/app.py | 308 ------------------
unidad_3/Flask/requisistos.txt | 5 -
unidad_3/Flask/static/css/styles.css | 51 ---
unidad_3/Flask/static/css/syle.css | 51 ---
unidad_3/Flask/static/image/ReadME.md | 1 -
unidad_3/Flask/static/js/Script.js | 24 --
unidad_3/Flask/static/js/script.js | 163 ---------
unidad_3/Flask/static/js/scripts.js | 24 --
unidad_3/Flask/templates/base.html | 20 --
.../Flask/templates/crear_inventario.html | 67 ----
unidad_3/Flask/templates/pedir_proveedor.html | 40 ---
unidad_3/Flask/templates/pedir_stock.html | 51 ---
unidad_3/Flask/templates/subir_csv.html | 22 --
.../uploads/registro_inventario_prueba.csv | 5 -
15 files changed, 938 deletions(-)
delete mode 100644 unidad_3/Flask/ReadMe.md
delete mode 100644 unidad_3/Flask/app.py
delete mode 100644 unidad_3/Flask/requisistos.txt
delete mode 100644 unidad_3/Flask/static/css/styles.css
delete mode 100644 unidad_3/Flask/static/css/syle.css
delete mode 100644 unidad_3/Flask/static/image/ReadME.md
delete mode 100644 unidad_3/Flask/static/js/Script.js
delete mode 100644 unidad_3/Flask/static/js/script.js
delete mode 100644 unidad_3/Flask/static/js/scripts.js
delete mode 100644 unidad_3/Flask/templates/base.html
delete mode 100644 unidad_3/Flask/templates/crear_inventario.html
delete mode 100644 unidad_3/Flask/templates/pedir_proveedor.html
delete mode 100644 unidad_3/Flask/templates/pedir_stock.html
delete mode 100644 unidad_3/Flask/templates/subir_csv.html
delete mode 100644 unidad_3/Flask/uploads/registro_inventario_prueba.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:
-
-
-
- Código
- Nombre
- Demanda
- Tiempo Entrega
- Fecha Límite
- Cantidad
-
-
-
- {% for producto in inventario_creado %}
-
- {{ producto.codigo }}
- {{ producto.nombre }}
- {{ producto.demanda }}
- {{ producto.tiempo_entrega }}
- {{ producto.fecha_limite }}
- {{ producto.cantidad }}
-
- {% endfor %}
-
-
-
-{% 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 %}
-
-{% 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 %}
-
-{% 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
From bba2c79a4783f35b37b2b0d3fa5453baf615e343 Mon Sep 17 00:00:00 2001
From: WolveJC05 <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:17:06 +0000
Subject: [PATCH 028/138] update Flask project
---
{Flask => unidad_3/Flask}/app.py | 0
{Flask => unidad_3/Flask}/requisistos.txt | 0
{Flask => unidad_3/Flask}/static/css/styles.css | 0
{Flask => unidad_3/Flask}/static/js/script.js | 0
{Flask => unidad_3/Flask}/templates/base.html | 0
{Flask => unidad_3/Flask}/templates/crear_inventario.html | 0
{Flask => unidad_3/Flask}/templates/pedir_proveedor.html | 0
{Flask => unidad_3/Flask}/templates/pedir_stock.html | 0
{Flask => unidad_3/Flask}/templates/subir_csv.html | 0
.../uploads/registro_inventario_jerarquico_20250424_175403.csv | 0
10 files changed, 0 insertions(+), 0 deletions(-)
rename {Flask => unidad_3/Flask}/app.py (100%)
rename {Flask => unidad_3/Flask}/requisistos.txt (100%)
rename {Flask => unidad_3/Flask}/static/css/styles.css (100%)
rename {Flask => unidad_3/Flask}/static/js/script.js (100%)
rename {Flask => unidad_3/Flask}/templates/base.html (100%)
rename {Flask => unidad_3/Flask}/templates/crear_inventario.html (100%)
rename {Flask => unidad_3/Flask}/templates/pedir_proveedor.html (100%)
rename {Flask => unidad_3/Flask}/templates/pedir_stock.html (100%)
rename {Flask => unidad_3/Flask}/templates/subir_csv.html (100%)
rename {Flask => unidad_3/Flask}/uploads/registro_inventario_jerarquico_20250424_175403.csv (100%)
diff --git a/Flask/app.py b/unidad_3/Flask/app.py
similarity index 100%
rename from Flask/app.py
rename to unidad_3/Flask/app.py
diff --git a/Flask/requisistos.txt b/unidad_3/Flask/requisistos.txt
similarity index 100%
rename from Flask/requisistos.txt
rename to unidad_3/Flask/requisistos.txt
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
From d486d2ab5424ef12b660a1a2cf9b2cba9a3fccc4 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 16:39:59 -0400
Subject: [PATCH 029/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index c73e032..1715033 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -14,9 +14,10 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- - name: Install dependencies
+ - name: Instalar dependencias
run: |
python -m pip install --upgrade pip
+ pip install -r requirements.txt
pip install pylint
- name: Analysing the code with pylint
run: |
From 1f2ed9d4c41548794943018973d0cbd086935432 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 16:46:22 -0400
Subject: [PATCH 030/138] Actualizar requisistos.txt
---
unidad_3/Flask/requisistos.txt | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/unidad_3/Flask/requisistos.txt b/unidad_3/Flask/requisistos.txt
index 44d39b7..2eebd89 100644
--- a/unidad_3/Flask/requisistos.txt
+++ b/unidad_3/Flask/requisistos.txt
@@ -1,5 +1,3 @@
-Flask
-pandas
-fpdf
-os
-datetime
\ No newline at end of file
+Flask
+fpdf
+pandas
\ No newline at end of file
From 39a5ae6662ae62e333fa2be200bb00664757acd5 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 16:46:55 -0400
Subject: [PATCH 031/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index 1715033..d344636 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -17,7 +17,7 @@ jobs:
- name: Instalar dependencias
run: |
python -m pip install --upgrade pip
- pip install -r requirements.txt
+ pip install -r requisitos.txt
pip install pylint
- name: Analysing the code with pylint
run: |
From d887262e6ab46d937647f3c6aeb0888cb0d5c2a0 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 16:50:18 -0400
Subject: [PATCH 032/138] Crear requisitos.txt
---
unidad_2/requisitos.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 unidad_2/requisitos.txt
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
From 6235e323c78af878cb5586bebfadc55ce91830f1 Mon Sep 17 00:00:00 2001
From: WolveJC05 <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:06:48 +0000
Subject: [PATCH 033/138] black format
---
scripts/concat_readmes.py | 2 +-
scripts/scan_projects.py | 4 +-
tests/unidad_1/test_.py | 13 +-
unidad_1/listar_archivos_drive.py | 28 +-
...ventario-interactiva_varias-tablas-v1.2.py | 70 ++-
unidad_2/Gestion-restorant-v0.2alpha.py | 62 ++-
unidad_2/Gestion-restorant-v0.5beta.py | 57 +-
.../Gestion-restorant-varias-tablasv1.0.py | 96 +++-
unidad_2/Gestion-restorant_una-tabla-v1.0.py | 82 ++-
unidad_3/Flask/app.py | 499 +++++++++++-------
unidad_7/Items-Management.py | 58 +-
unidad_7/PystockManagement/main.py | 16 +-
.../PystockManagement/src/models/productos.py | 12 +-
.../src/services/inventario_manager.py | 58 +-
14 files changed, 685 insertions(+), 372 deletions(-)
diff --git a/scripts/concat_readmes.py b/scripts/concat_readmes.py
index 465c93a..d1b07b0 100644
--- a/scripts/concat_readmes.py
+++ b/scripts/concat_readmes.py
@@ -9,7 +9,7 @@
with open(output_file, "w", encoding="utf-8") as global_file:
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)):
+ if any(part.startswith(".") for part in root.split(os.sep)):
continue
# Si existe un README.md en la carpeta, lo añadimos
diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py
index cc2ec7f..51e60bb 100644
--- a/scripts/scan_projects.py
+++ b/scripts/scan_projects.py
@@ -8,7 +8,7 @@
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)):
+ if any(part.startswith(".") for part in root.split(os.sep)):
continue
# Detectar proyectos: carpeta que contiene un README.md
@@ -29,7 +29,7 @@
"title": project_name,
"readme": os.path.join(root, "README.md"),
"icon": icon_file,
- "url": f"https://github.com/USUARIO/REPO/tree/main/{project_name}"
+ "url": f"https://github.com/USUARIO/REPO/tree/main/{project_name}",
}
projects.append(project)
diff --git a/tests/unidad_1/test_.py b/tests/unidad_1/test_.py
index 0cb56c8..9c8dfdb 100644
--- a/tests/unidad_1/test_.py
+++ b/tests/unidad_1/test_.py
@@ -2,15 +2,16 @@
from unittest.mock import patch
from unidad_1.listar_archivos_drive import list_arch
+
class TestListArch(unittest.TestCase):
- @patch('os.listdir')
- @patch('os.path.isfile')
- @patch('os.path.getmtime')
+ @patch("os.listdir")
+ @patch("os.path.isfile")
+ @patch("os.path.getmtime")
def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
- mock_listdir.return_value = ['a.txt', 'b.txt', 'c.txt']
+ mock_listdir.return_value = ["a.txt", "b.txt", "c.txt"]
mock_isfile.return_value = True
mock_getmtime.side_effect = [100, 300, 200]
- result = list_arch('fake_dir')
- expected = [('b.txt', 300), ('c.txt', 200), ('a.txt', 100)]
+ result = list_arch("fake_dir")
+ expected = [("b.txt", 300), ("c.txt", 200), ("a.txt", 100)]
self.assertEqual(result, expected)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index 7a5e3d5..b356af5 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -3,8 +3,9 @@
import unittest
from unittest.mock import patch
-# Definir el directorio
-DIRUTH = '/content/drive/My Drive/'
+# Definir el directorio
+DIRUTH = "/content/drive/My Drive/"
+
def list_arch(directory):
try:
@@ -21,7 +22,7 @@ def list_arch(directory):
print("Archivos en el directorio ordenados por fecha de subida:")
for file, modi_time in file_dates:
- print(f"{file} - {time.ctime(modi_time)}")
+ print(f"{file} - {time.ctime(modi_time)}")
return file_dates # útil para pruebas
@@ -32,32 +33,35 @@ def list_arch(directory):
except Exception as e:
print(f"Ocurrió un error: {e}")
+
# ============================
# Pruebas unitarias
# ============================
+
class TestListArch(unittest.TestCase):
- @patch('os.listdir')
- @patch('os.path.isfile')
- @patch('os.path.getmtime')
+ @patch("os.listdir")
+ @patch("os.path.isfile")
+ @patch("os.path.getmtime")
def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
- mock_listdir.return_value = ['a.txt', 'b.txt', 'c.txt']
+ mock_listdir.return_value = ["a.txt", "b.txt", "c.txt"]
mock_isfile.return_value = True
mock_getmtime.side_effect = [100, 300, 200]
- result = list_arch('fake_dir')
- expected = [('b.txt', 300), ('c.txt', 200), ('a.txt', 100)]
+ 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)
+ @patch("os.listdir", side_effect=FileNotFoundError)
def test_directory_not_found(self, mock_listdir):
- result = list_arch('missing_dir')
+ result = list_arch("missing_dir")
self.assertIsNone(result)
+
# ============================
# Ejecutar pruebas si es main
# ============================
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py b/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
index 9b324c6..cdb5de5 100644
--- a/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
+++ b/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
@@ -5,11 +5,13 @@
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))
+ return "".join(random.choices(caracteres, k=longitud))
+
# ------------------------------------------
# 1. Ingreso interactivo de productos con código alfanumérico
@@ -31,7 +33,7 @@ def generar_codigo_aleatorio(longitud=6):
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:
@@ -63,12 +65,12 @@ def generar_codigo_aleatorio(longitud=6):
"demanda": demanda,
"tiempo_entrega": tiempo_entrega,
"fecha_limite": fecha_limite,
- "cantidad": cantidad
+ "cantidad": cantidad,
}
inventario.append(producto)
-
+
continuar = input("¿Desea ingresar otro producto? (s/n): ").strip().lower()
- if continuar != 's':
+ if continuar != "s":
break
if not inventario:
@@ -78,6 +80,7 @@ def generar_codigo_aleatorio(longitud=6):
# ------------------------------------------
# 2. Funciones de ordenamiento
+
# Insertion Sort:
def insertion_sort(lista, key, descending=False):
for i in range(1, len(lista)):
@@ -94,7 +97,8 @@ def insertion_sort(lista, key, descending=False):
lista[j + 1] = actual
return lista
-# Bubble Sort:
+
+# Bubble Sort:
def bubble_sort(lista, key, descending=False):
n = len(lista)
swapped = True
@@ -102,17 +106,18 @@ def bubble_sort(lista, key, descending=False):
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]
+ 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]
+ 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:
+
+# Quick Sort:
def quick_sort(lista, key):
if len(lista) <= 1:
return lista
@@ -123,7 +128,8 @@ def quick_sort(lista, key):
mayor = [x for x in lista[1:] if key(x) >= key(pivot)]
return quick_sort(menos, key) + iguales + quick_sort(mayor, key)
-# Selection Sort:
+
+# Selection Sort:
def selection_sort(lista, key, reverse=False):
nueva_lista = []
temp = lista.copy()
@@ -140,17 +146,25 @@ def selection_sort(lista, key, reverse=False):
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)
+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)
+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"))
+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)
@@ -158,6 +172,7 @@ def selection_sort(lista, key, reverse=False):
# ------------------------------------------
# 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 = []
@@ -168,14 +183,14 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
item["demanda"],
item["tiempo_entrega"],
item["fecha_limite"],
- item["cantidad"]
+ 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')
+ 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)
@@ -192,7 +207,7 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
# Guardar registro en CSV
csv_filename = f"registro_{algorithm_key}_{timestamp}.csv"
- with open(csv_filename, mode='w', newline='', encoding='utf-8') as csvfile:
+ 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:
@@ -203,6 +218,7 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
print(" Registro CSV:", csv_filename)
print("-----------------------------------------")
+
# ------------------------------------------
# 5. Visualización: Generar una tabla para cada criterio de ordenamiento
@@ -213,23 +229,23 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
{
"title": "Ordenamiento por Cantidad (Insertion Sort)",
"data": inventario_por_cantidad,
- "algorithm_key": "insertion_cantidad"
+ "algorithm_key": "insertion_cantidad",
},
{
"title": "Ordenamiento por Tiempo de entrega (Bubble Sort)",
"data": inventario_por_tiempo,
- "algorithm_key": "bubble_tiempo"
+ "algorithm_key": "bubble_tiempo",
},
{
"title": "Ordenamiento por Fecha límite (Quick Sort)",
"data": inventario_por_fecha,
- "algorithm_key": "quick_fecha"
+ "algorithm_key": "quick_fecha",
},
{
"title": "Ordenamiento por Demanda (Selection Sort)",
"data": inventario_por_demanda,
- "algorithm_key": "selection_demanda"
- }
+ "algorithm_key": "selection_demanda",
+ },
]
for orden in ordenamientos:
@@ -238,5 +254,5 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
title=orden["title"],
headers=headers,
query_date=fecha_consulta,
- algorithm_key=orden["algorithm_key"]
- )
\ No newline at end of file
+ algorithm_key=orden["algorithm_key"],
+ )
diff --git a/unidad_2/Gestion-restorant-v0.2alpha.py b/unidad_2/Gestion-restorant-v0.2alpha.py
index f1a9f11..6f367f1 100644
--- a/unidad_2/Gestion-restorant-v0.2alpha.py
+++ b/unidad_2/Gestion-restorant-v0.2alpha.py
@@ -2,12 +2,41 @@
# 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},
+ {
+ "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):
@@ -20,12 +49,14 @@ def insertion_sort(lista, key):
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)
@@ -34,22 +65,26 @@ def bubble_sort(lista, key, descending=False):
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]
+ 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]
+ 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)
+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:
@@ -61,18 +96,22 @@ def quick_sort(lista, key):
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"))
+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):
+ for j in range(i + 1, n):
if reverse:
if key(lista[j]) > key(lista[index_extremo]):
index_extremo = j
@@ -82,6 +121,7 @@ def selection_sort(lista, key, reverse=False):
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):")
diff --git a/unidad_2/Gestion-restorant-v0.5beta.py b/unidad_2/Gestion-restorant-v0.5beta.py
index 77f2bb6..90b49b9 100644
--- a/unidad_2/Gestion-restorant-v0.5beta.py
+++ b/unidad_2/Gestion-restorant-v0.5beta.py
@@ -5,12 +5,41 @@
# 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},
+ {
+ "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)
@@ -26,6 +55,7 @@ def selection_sort(lista, key, reverse=False):
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
@@ -35,12 +65,12 @@ def selection_sort(lista, key, reverse=False):
data_matrix = []
for item in inventario_ordenado:
row = [
- item["codigo"],
- item["nombre"],
- item["demanda"],
- item["tiempo_entrega"],
- item["fecha_limite"],
- item["cantidad"]
+ item["codigo"],
+ item["nombre"],
+ item["demanda"],
+ item["tiempo_entrega"],
+ item["fecha_limite"],
+ item["cantidad"],
]
data_matrix.append(row)
@@ -54,10 +84,7 @@ def selection_sort(lista, key, reverse=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 = ax.table(cellText=data_matrix, colLabels=headers, loc="center", cellLoc="center")
tabla.auto_set_font_size(False)
tabla.set_fontsize(10)
@@ -75,7 +102,7 @@ def selection_sort(lista, key, reverse=False):
# 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:
+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:
diff --git a/unidad_2/Gestion-restorant-varias-tablasv1.0.py b/unidad_2/Gestion-restorant-varias-tablasv1.0.py
index c38f560..9736b87 100644
--- a/unidad_2/Gestion-restorant-varias-tablasv1.0.py
+++ b/unidad_2/Gestion-restorant-varias-tablasv1.0.py
@@ -6,16 +6,45 @@
# ---------------------------------------
# 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},
+ {
+ "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:
+
+# 1. Insertion Sort:
def insertion_sort(lista, key):
for i in range(1, len(lista)):
actual = lista[i]
@@ -26,6 +55,7 @@ def insertion_sort(lista, key):
lista[j + 1] = actual
return lista
+
# 2. Bubble Sort:
def bubble_sort(lista, key, descending=False):
n = len(lista)
@@ -44,7 +74,8 @@ def bubble_sort(lista, key, descending=False):
n -= 1
return lista
-# 3. Quick Sort:
+
+# 3. Quick Sort:
def quick_sort(lista, key):
if len(lista) <= 1:
return lista
@@ -55,6 +86,7 @@ def quick_sort(lista, key):
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)
@@ -70,6 +102,7 @@ def selection_sort(lista, key, reverse=False):
lista[i], lista[index_extremo] = lista[index_extremo], lista[i]
return lista
+
# ---------------------------------------
# Criterios de Ordenamiento
@@ -77,63 +110,70 @@ def selection_sort(lista, key, reverse=False):
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)
+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")
+ 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"]]
+ 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')
-
+ 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 = 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:
+ 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"]
@@ -146,23 +186,23 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
{
"title": "Ordenamiento por Cantidad (Insertion Sort)",
"data": inventario_por_cantidad,
- "algorithm_key": "insertion"
+ "algorithm_key": "insertion",
},
{
"title": "Ordenamiento por Tiempo de entrega (Bubble Sort)",
"data": inventario_por_tiempo,
- "algorithm_key": "bubble"
+ "algorithm_key": "bubble",
},
{
"title": "Ordenamiento por Fecha límite (Quick Sort)",
"data": inventario_por_fecha,
- "algorithm_key": "quick"
+ "algorithm_key": "quick",
},
{
"title": "Ordenamiento por Demanda (Selection Sort)",
"data": inventario_por_demanda,
- "algorithm_key": "selection"
- }
+ "algorithm_key": "selection",
+ },
]
# Iterar sobre cada criterio de ordenamiento, generar y mostrar la tabla, y guardar el CSV correspondiente.
@@ -172,5 +212,5 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
title=orden["title"],
headers=headers,
query_date=fecha_consulta,
- algorithm_key=orden["algorithm_key"]
+ 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
index 36fb398..defa50c 100644
--- a/unidad_2/Gestion-restorant_una-tabla-v1.0.py
+++ b/unidad_2/Gestion-restorant_una-tabla-v1.0.py
@@ -6,12 +6,41 @@
# ---------------------------------------
# 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},
+ {
+ "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)
@@ -30,17 +59,19 @@ def insertion_sort(lista, key, descending=False):
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)]
+ 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)]
+ 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 = []
@@ -58,6 +89,7 @@ def selection_sort(lista, key, reverse=False):
temp.remove(candidato)
return nueva_lista
+
# ---------------------------------------
# Ordenamiento jerárquico
@@ -72,20 +104,31 @@ def selection_sort(lista, key, reverse=False):
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"))
+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"]
+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"]]
+ 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
@@ -93,13 +136,10 @@ def selection_sort(lista, key, reverse=False):
# ---------------------------------------
# Visualización con Matplotlib
-fig, ax = plt.subplots(figsize=(10, len(ordenados)*0.6 + 2))
-ax.axis('off')
+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 = 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)
@@ -116,7 +156,7 @@ def selection_sort(lista, key, reverse=False):
# ---------------------------------------
# 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:
+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)
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index 352870e..87db379 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -1,4 +1,14 @@
-from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify, send_file
+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
@@ -7,21 +17,23 @@
import random
import string
import csv
-import io # Necesario para send_file con CSV generado en memoria
+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!
+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
+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))
+ return "".join(random.choices(caracteres, k=longitud))
+
# Función para limpiar archivos antiguos en la carpeta uploads
def limpiar_archivos_antiguos():
@@ -30,12 +42,14 @@ def limpiar_archivos_antiguos():
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)
+ 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
+ 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}")
@@ -48,6 +62,7 @@ def limpiar_archivos_antiguos():
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()
@@ -56,6 +71,7 @@ def limpiar_archivos_antiguos():
# RUTAS DE LA APLICACIÓN
# ==========================================================================
+
# Ruta para crear inventario manualmente
@app.route("/crear_inventario", methods=["GET", "POST"])
def crear_inventario():
@@ -63,60 +79,89 @@ def crear_inventario():
# 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:
+ 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)
+ 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.")
+ 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 []
+ 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:
+ 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
+ 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")
+ 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)
+ 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
+ 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
+ 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
+ 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
+ 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
+ # 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'))
+ 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', [])
+ inventario_temporal = session.get("inventario_creado", [])
return render_template("crear_inventario.html", inventario_creado=inventario_temporal)
@@ -125,63 +170,78 @@ def crear_inventario():
def upload_csv():
if request.method == "POST":
# Verificar si se envió un archivo
- if 'file' not in request.files:
+ 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 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 == '':
+ 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 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':
+ if file and file.filename.lower().endswith(".csv") and file.content_type == "text/csv":
try:
- filename = file.filename # Considerar usar werkzeug.utils.secure_filename
+ 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']
+ 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)}")
+ 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
+ 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
+ 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
+ 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)
+ 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)
+ 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)
+ message = str(e)
except Exception as e:
message = f"Ocurrió un error inesperado: {e}"
- if os.path.exists(filepath): os.remove(filepath) # Intentar eliminar
+ 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:
@@ -192,7 +252,7 @@ def upload_csv():
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
+ return jsonify({"message": message}), 400
flash(message)
return redirect(request.url)
@@ -204,7 +264,7 @@ def upload_csv():
@app.route("/pedir_stock", methods=["GET", "POST"])
def pedir_stock():
# Asegurarse de que el inventario esté cargado
- productos = session.get('productos')
+ 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"))
@@ -216,74 +276,104 @@ def pedir_stock():
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]
+ 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):
+ # 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}")
+ 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
+ # 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
+ # 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
+ resultados_pedido_msg = [] # Mensajes para la respuesta
hubo_cambios = False
for producto in productos:
- codigo_str = str(producto['Código'])
+ 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
+ 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))
+ 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']}")
+ 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
+ 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')
+ 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')
+ 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
+ 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
+ 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
+ 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)
@@ -292,7 +382,7 @@ def pedir_stock():
# Ruta para pedir al proveedor
@app.route("/pedir_proveedor", methods=["GET", "POST"])
def pedir_proveedor():
- productos = session.get('productos')
+ 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"))
@@ -304,77 +394,108 @@ def pedir_proveedor():
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}
+ 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)
+ 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.")
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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)
@@ -383,26 +504,26 @@ def pedir_proveedor():
# 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')
+ 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
+ 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
+ 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)
+ 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
+ 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")
@@ -412,25 +533,37 @@ def generar_pdf_proveedor():
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)
+ 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.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
+ 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.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()
@@ -440,9 +573,12 @@ def generar_pdf_proveedor():
pdf.output(filepath)
# Limpiar pedido de la sesión después de generar el PDF
- session.pop('pedido_proveedor', None)
+ session.pop("pedido_proveedor", None)
- flash(f"PDF de pedido al proveedor generado: {pdf_filename}. Guardado en 'uploads'.", "success")
+ 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)
@@ -450,51 +586,52 @@ def generar_pdf_proveedor():
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)
+ session.pop("pedido_proveedor", None)
- return redirect(url_for("upload_csv")) # Redirigir a inicio después de generar/error
+ 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')
+ 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')
+ productos = session.get("productos")
if not productos:
- flash("No hay datos de inventario para exportar.", "warning")
- return redirect(url_for("upload_csv"))
+ 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"
- )
+ # 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.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"
- )
+ 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}")
@@ -506,4 +643,4 @@ def export_csv():
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
+ app.run(debug=True, host="0.0.0.0")
diff --git a/unidad_7/Items-Management.py b/unidad_7/Items-Management.py
index c08bd38..663480f 100644
--- a/unidad_7/Items-Management.py
+++ b/unidad_7/Items-Management.py
@@ -1,4 +1,5 @@
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.
@@ -6,6 +7,7 @@
# CLASE PRODUCTO (Modelos)
# ====================================================================
+
class Producto:
def __init__(self, nombre, precio, cantidad, codigo):
# Al asignar a self.atributo, se llama automáticamente al setter
@@ -26,12 +28,12 @@ def __str__(self):
def __lt__(self, otro):
if not isinstance(otro, Producto):
return NotImplemented
- return self.precio < otro.precio # Compara por precio
+ 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
+ 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
@@ -81,6 +83,7 @@ def codigo(self, codigo):
# CLASE INVENTARIO (Servicios/Manager)
# ====================================================================
+
class Inventario:
def __init__(self):
self.productos = {} # Diccionario: clave=código (str), valor=objeto Producto
@@ -119,7 +122,9 @@ def buscar_producto(self):
print(resultado_producto)
print("--------------------------\n")
else:
- print(f"❌ Error: El producto con el código {id_search} no se encuentra en el inventario.")
+ 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()
@@ -129,20 +134,24 @@ def actualizar_cantidad(self):
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_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.")
+ print(
+ "❌ Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo."
+ )
continue
-
+
break
except ValueError:
@@ -152,7 +161,9 @@ def actualizar_cantidad(self):
# 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}.")
+ 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()
@@ -163,15 +174,18 @@ def eliminar_producto(self):
# 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.")
+ 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:
@@ -183,15 +197,18 @@ def obtener_datos_producto():
# 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.")
+ 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()
@@ -209,29 +226,29 @@ def main_menu():
try:
opcion = input("Selecciona una opción (1-6): ").strip()
- if opcion == '1':
+ if opcion == "1":
print("\n>>> Agregar Producto <<<")
nuevo_producto = obtener_datos_producto()
if nuevo_producto:
mi_inventario.agg_prod(nuevo_producto)
- elif opcion == '2':
+ elif opcion == "2":
print("\n>>> Buscar Producto <<<")
mi_inventario.buscar_producto()
- elif opcion == '3':
+ elif opcion == "3":
print("\n>>> Actualizar Cantidad <<<")
mi_inventario.actualizar_cantidad()
- elif opcion == '4':
+ elif opcion == "4":
print("\n>>> Eliminar Producto <<<")
mi_inventario.eliminar_producto()
- elif opcion == '5':
+ elif opcion == "5":
print("\n>>> Listar Productos <<<")
mi_inventario.imprimir()
- elif opcion == '6':
+ elif opcion == "6":
print("\nSaliendo del sistema de inventario. ¡Hasta pronto! 👋")
break
@@ -240,7 +257,8 @@ def main_menu():
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
+ main_menu()
diff --git a/unidad_7/PystockManagement/main.py b/unidad_7/PystockManagement/main.py
index 613e659..5b8f0e6 100644
--- a/unidad_7/PystockManagement/main.py
+++ b/unidad_7/PystockManagement/main.py
@@ -1,6 +1,7 @@
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:
@@ -17,8 +18,7 @@ def obtener_datos_producto():
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(f"Error de entrada de datos: {e}")
print("Asegúrate de ingresar tipos y valores correctos.")
return None
except Exception as e:
@@ -43,29 +43,29 @@ def main_menu():
try:
opcion = input("Selecciona una opción (1-6): ").strip()
- if opcion == '1':
+ if opcion == "1":
print("\n>>> Agregar Producto <<<")
nuevo_producto = obtener_datos_producto()
if nuevo_producto:
mi_inventario.agg_prod(nuevo_producto)
- elif opcion == '2':
+ elif opcion == "2":
print("\n>>> Buscar Producto <<<")
mi_inventario.buscar_producto()
- elif opcion == '3':
+ elif opcion == "3":
print("\n>>> Actualizar Cantidad <<<")
mi_inventario.actualizar_cantidad()
- elif opcion == '4':
+ elif opcion == "4":
print("\n>>> Eliminar Producto <<<")
mi_inventario.eliminar_producto()
- elif opcion == '5':
+ elif opcion == "5":
print("\n>>> Listar Productos <<<")
mi_inventario.imprimir()
- elif opcion == '6':
+ elif opcion == "6":
mi_inventario.save_stock()
print("\nSaliendo del sistema de inventario. ¡Hasta pronto!")
break
diff --git a/unidad_7/PystockManagement/src/models/productos.py b/unidad_7/PystockManagement/src/models/productos.py
index f49a173..e8a1a0c 100644
--- a/unidad_7/PystockManagement/src/models/productos.py
+++ b/unidad_7/PystockManagement/src/models/productos.py
@@ -36,8 +36,7 @@ def nombre(self):
@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.")
+ raise ValueError("El nombre del producto debe ser una cadena no vacía.")
self.__nombre = nombre
@property
@@ -47,8 +46,7 @@ def precio(self):
@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.")
+ raise ValueError("El precio debe ser un número positivo mayor que cero.")
self.__precio = precio
@property
@@ -58,8 +56,7 @@ def cantidad(self):
@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.")
+ raise ValueError("La cantidad debe ser un número entero no negativo.")
self.__cantidad = cantidad
@property
@@ -69,6 +66,5 @@ def codigo(self):
@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.")
+ 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
index 3cf8afc..4fd5bfa 100644
--- a/unidad_7/PystockManagement/src/services/inventario_manager.py
+++ b/unidad_7/PystockManagement/src/services/inventario_manager.py
@@ -4,7 +4,7 @@
class Inventario:
- def __init__(self, file_name='data_stock.json'):
+ 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")
@@ -20,22 +20,18 @@ 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:
+ 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']
+ 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(f"Error al cargar el inventario desde '{self.route_file}'")
print("Se iniciara un inventario Vacío")
return None
@@ -45,35 +41,31 @@ def save_stock(self):
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
+ "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:
+ 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(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.")
+ 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.")
+ 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.")
+ print(f" Éxito: Producto '{producto.nombre}' (Código: {producto.codigo}) agregado.")
def imprimir(self):
if not self.productos:
@@ -98,11 +90,11 @@ def buscar_producto(self):
print("--------------------------\n")
else:
print(
- f" Error: El producto con el código {id_search} no se encuentra en el inventario.")
+ 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()
+ 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.")
@@ -115,14 +107,16 @@ def actualizar_cantidad(self):
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()
+ 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.")
+ " Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo."
+ )
continue
break
@@ -130,17 +124,16 @@ def actualizar_cantidad(self):
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}")
+ 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}.")
+ 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()
+ 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.")
@@ -151,4 +144,5 @@ def eliminar_producto(self):
del self.productos[id_search] # Elimina la referencia del diccionario
print(
- f" Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario.")
+ f" Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario."
+ )
From c64f2c1be81f4954ac3211ed2312d8691dfbd44a Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 17:12:48 -0400
Subject: [PATCH 034/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index d344636..f2a1609 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -8,17 +8,28 @@ jobs:
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
+
steps:
- uses: actions/checkout@v4
+
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
+
- name: Instalar dependencias
run: |
python -m pip install --upgrade pip
- pip install -r requisitos.txt
+ # Buscar todos los requisitos.txt en el proyecto y subcarpetas
+ REQS=$(find . -name requisitos.txt)
+ if [ -n "$REQS" ]; then
+ for req in $REQS; do
+ echo "Instalando dependencias desde $req"
+ pip install -r "$req"
+ done
+ fi
pip install pylint
+
- name: Analysing the code with pylint
run: |
- pylint $(git ls-files '*.py')
+ pylint $(git ls-files '*.py')
\ No newline at end of file
From 8d6066eead379c80a5e8b1cf1e2da4539e21e273 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:06:19 -0400
Subject: [PATCH 035/138] Actualizar
Gestion-inventario-interactiva_varias-tablas-v1.2.py
---
.../Gestion-inventario-interactiva_varias-tablas-v1.2.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py b/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
index cdb5de5..672a3d9 100644
--- a/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
+++ b/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
@@ -1,10 +1,12 @@
-import matplotlib.pyplot as plt
-from matplotlib import table
-import datetime
+# Standard library
import csv
+import datetime
import random
import string
+# Third-party libraries
+import matplotlib.pyplot as plt
+from matplotlib import table
# ------------------------------------------
# Función para generar código alfanumérico aleatorio
From a75c85998785bced490e01506d64a12d4e3b798c Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:07:12 -0400
Subject: [PATCH 036/138] Actualizar Gestion-restorant-v0.5beta.py
---
unidad_2/Gestion-restorant-v0.5beta.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/unidad_2/Gestion-restorant-v0.5beta.py b/unidad_2/Gestion-restorant-v0.5beta.py
index 90b49b9..1e604d5 100644
--- a/unidad_2/Gestion-restorant-v0.5beta.py
+++ b/unidad_2/Gestion-restorant-v0.5beta.py
@@ -1,7 +1,10 @@
+# Standard library
+import csv
+import datetime
+
+# Third-party libraries
import matplotlib.pyplot as plt
from matplotlib import table
-import datetime
-import csv
# Ejemplo para el inventario
inventario = [
From 02ea4492fc634853d94accc1da8560978d745faa Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:08:44 -0400
Subject: [PATCH 037/138] Actualizar Gestion-restorant-varias-tablasv1.0.py
---
unidad_2/Gestion-restorant-varias-tablasv1.0.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/unidad_2/Gestion-restorant-varias-tablasv1.0.py b/unidad_2/Gestion-restorant-varias-tablasv1.0.py
index 9736b87..6f775ac 100644
--- a/unidad_2/Gestion-restorant-varias-tablasv1.0.py
+++ b/unidad_2/Gestion-restorant-varias-tablasv1.0.py
@@ -1,7 +1,11 @@
+# Standard library
+import csv
+import datetime
+
+# Third-party libraries
import matplotlib.pyplot as plt
from matplotlib import table
-import datetime
-import csv
+
# ---------------------------------------
# Ejemplo del inventario
From 52574d7fe65783337b49bc49f9b5d97a01268f34 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:09:38 -0400
Subject: [PATCH 038/138] Actualizar Gestion-restorant_una-tabla-v1.0.py
---
unidad_2/Gestion-restorant_una-tabla-v1.0.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/unidad_2/Gestion-restorant_una-tabla-v1.0.py b/unidad_2/Gestion-restorant_una-tabla-v1.0.py
index defa50c..21e1514 100644
--- a/unidad_2/Gestion-restorant_una-tabla-v1.0.py
+++ b/unidad_2/Gestion-restorant_una-tabla-v1.0.py
@@ -1,7 +1,10 @@
+# Standard library
+import csv
+import datetime
+
+# Third-party libraries
import matplotlib.pyplot as plt
from matplotlib import table
-import datetime
-import csv
# ---------------------------------------
# Ejemplo del inventario
From bfe2c6f1381d0a2c41d8aadb1ffc741fdc5ba257 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:11:29 -0400
Subject: [PATCH 039/138] Actualizar app.py
---
unidad_3/Flask/app.py | 41 ++++++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index 87db379..05cee42 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -1,23 +1,26 @@
-from flask import (
- Flask,
- render_template,
- request,
- redirect,
- url_for,
- flash,
- session,
- jsonify,
- send_file,
-)
-import os
+# Standard library
+import csv
+import datetime
+import io # Necesario para send_file con CSV generado en memoria
+import os
+import random
+import string
+import time
+
+# Third-party libraries
+from flask import (
+ Flask,
+ render_template,
+ request,
+ redirect,
+ url_for,
+ flash,
+ session,
+ jsonify,
+ send_file,
+)
+from fpdf import FPDF
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__)
From 3df20a1f07d51d0c2011c666555ff262aa7a5c96 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:12:37 -0400
Subject: [PATCH 040/138] Actualizar inventario_manager.py
---
unidad_7/PystockManagement/src/services/inventario_manager.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unidad_7/PystockManagement/src/services/inventario_manager.py b/unidad_7/PystockManagement/src/services/inventario_manager.py
index 4fd5bfa..ab4f67e 100644
--- a/unidad_7/PystockManagement/src/services/inventario_manager.py
+++ b/unidad_7/PystockManagement/src/services/inventario_manager.py
@@ -1,7 +1,7 @@
-from ..models.productos import Producto
import os
import json
+from ..models.productos import Producto
class Inventario:
def __init__(self, file_name="data_stock.json"):
From c376fa38489aa8fbc2a1555189405d17bb938597 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:56:32 -0400
Subject: [PATCH 041/138] Actualizar app.py
---
unidad_3/Flask/app.py | 1249 ++++++++++++++++++++---------------------
1 file changed, 621 insertions(+), 628 deletions(-)
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index 05cee42..d83bdf2 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -1,7 +1,15 @@
+"""
+Aplicación Flask de Gestión de Inventario para Touken Café.
+
+Permite subir inventario CSV, crear inventario manualmente, realizar pedidos
+internos (stock) y generar pedidos a proveedor en formato PDF, con funciones
+de limpieza y exportación.
+"""
+
# Standard library
import csv
import datetime
-import io # Necesario para send_file con CSV generado en memoria
+import io
import os
import random
import string
@@ -20,630 +28,615 @@
send_file,
)
from fpdf import FPDF
-import pandas as pd
-
-# 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")
+import pandas as pd
+from pandas.errors import EmptyDataError, ParserError
+
+
+# Configuración básica de Flask
+app = Flask(__name__)
+# ¡Cambia esto por una clave secreta segura en producción!
+app.secret_key = os.environ.get("FLASK_SECRET_KEY", "secret-key-default-insecure")
+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
+
+
+def generar_codigo_aleatorio(longitud: int = 6) -> str:
+ """
+ Genera un código alfanumérico aleatorio de la longitud especificada.
+
+ Args:
+ longitud: La longitud deseada para el código (por defecto es 6).
+
+ Returns:
+ Una cadena alfanumérica aleatoria.
+ """
+ caracteres = string.ascii_uppercase + string.digits
+ return "".join(random.choices(caracteres, k=longitud))
+
+
+def limpiar_archivos_antiguos(dias: int = 1):
+ """
+ Elimina archivos de la carpeta 'uploads' que sean más antiguos que el número de días especificado.
+
+ Args:
+ dias: Número de días de antigüedad para considerar un archivo como 'antiguo'.
+ """
+ ahora = time.time()
+ segundos_limite = dias * 24 * 3600
+ archivos_eliminados = 0
+ print("Ejecutando limpieza de archivos antiguos...")
+
+ 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):
+ # Usar getmtime (tiempo de última modificación)
+ timestamp_modificacion = os.path.getmtime(ruta_archivo)
+ if (ahora - timestamp_modificacion) > segundos_limite:
+ os.remove(ruta_archivo)
+ print(f"Archivo antiguo eliminado: {ruta_archivo}")
+ archivos_eliminados += 1
+ except (OSError, PermissionError) as e:
+ # Capturar errores del sistema operativo o permisos
+ print(f"Error al procesar o eliminar {ruta_archivo}: {e}")
+
+ if archivos_eliminados > 0:
+ print(f"Se eliminaron {archivos_eliminados} archivos antiguos de {folder_path}.")
+ else:
+ print(f"No se encontraron archivos antiguos para eliminar en {folder_path}.")
+
+
+# Ejecutar la limpieza al inicio de la aplicación (en contexto)
+with app.app_context():
+ limpiar_archivos_antiguos(dias=1)
+
+# ==========================================================================
+# RUTAS DE LA APLICACIÓN
+# ==========================================================================
+
+
+def _guardar_inventario_csv(inventario_data: list, filename: str) -> str:
+ """Función auxiliar para guardar la lista de inventario en un archivo CSV."""
+ ruta_archivo_creado = os.path.join(app.config["UPLOAD_FOLDER"], filename)
+
+ if not inventario_data or not isinstance(inventario_data, list) 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_creado, "w", newline="", encoding="utf-8") as csvfile:
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
+ writer.writeheader()
+ writer.writerows(inventario_data)
+ return ruta_archivo_creado
+ except KeyError as e:
+ raise KeyError(f"Error de clave al procesar inventario: falta la clave {e}")
+ except (IOError, OSError) as e:
+ raise OSError(f"Error del sistema al guardar el archivo CSV: {e}")
+
+
+@app.route("/crear_inventario", methods=["GET", "POST"])
+def crear_inventario():
+ """Ruta para mostrar la interfaz o procesar la creación manual del inventario."""
+ if request.method == "POST":
+ if not request.is_json:
+ flash("Método no soportado. Use la interfaz interactiva.", "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:
+ # Captura errores internos de la función auxiliar
+ return (
+ jsonify({"message": f"Error en los datos del inventario: {e}"}),
+ 400,
+ )
+
+ # Método GET: Mostrar la página
+ inventario_temporal = session.get("inventario_creado", [])
+ return render_template("crear_inventario.html", inventario_creado=inventario_temporal)
+
+
+# --------------------------------------------------------------------------
+def _manejar_subida_csv(file):
+ """Función auxiliar para validar y procesar el archivo CSV subido."""
+ if not file.filename.lower().endswith(".csv") or file.content_type != "text/csv":
+ return "Por favor, sube un archivo CSV válido (.csv)."
+
+ filename = file.filename
+ filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename)
+
+ try:
+ file.save(filepath)
+
+ data = pd.read_csv(filepath)
+
+ 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):
+ raise ValueError(
+ f"El CSV debe contener las columnas: {', '.join(columnas_esperadas)}"
+ )
+
+ data.fillna("", inplace=True)
+ 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}")
+ return None # Indica éxito
+
+ except EmptyDataError:
+ return "El archivo CSV está vacío."
+ except ParserError:
+ return "Error al procesar el archivo CSV. Verifique el formato."
+ except ValueError as e:
+ return str(e)
+ except Exception as e:
+ # Cualquier otro error inesperado (IOError, etc.)
+ return f"Ocurrió un error inesperado al procesar el archivo: {e}"
+ finally:
+ # Asegurarse de limpiar archivos inválidos o fallidos
+ if os.path.exists(filepath) and "productos" not in session:
+ os.remove(filepath)
+ print(f"Archivo fallido eliminado: {filepath}")
+
+
+@app.route("/", methods=["POST", "GET"])
+def upload_csv():
+ """Ruta de inicio para subir CSV de inventario."""
+ if request.method == "POST":
+ if "file" not in request.files or request.files["file"].filename == "":
+ message = "No se seleccionó ningún archivo."
+ if request.is_json:
+ return jsonify({"message": message}), 400
+ flash(message)
+ return redirect(request.url)
+
+ error_message = _manejar_subida_csv(request.files["file"])
+
+ if error_message:
+ if request.is_json:
+ return jsonify({"message": error_message}), 400
+ flash(error_message)
+ return redirect(request.url)
+
+ # Éxito
+ if request.is_json:
+ return jsonify({"message": "Archivo CSV cargado con éxito."}), 200
+ flash("Archivo CSV de inventario cargado con éxito.")
+ return redirect(url_for("upload_csv"))
+
+ # Método GET: Mostrar la página
+ return render_template("subir_csv.html")
+
+
+# --------------------------------------------------------------------------
+
+
+@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 = {}
+ resultados_pedido_msg = []
+ hubo_cambios = False
+
+ # 1. Recolección y Validación del Pedido
+ for key, value in request.form.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:
+ return (
+ jsonify(
+ {
+ "message": (
+ f"La cantidad para {codigo_limpio} no puede ser negativa."
+ )
+ }
+ ),
+ 400,
+ )
+ if cantidad_pedida > 0:
+ pedido_stock[codigo_limpio] = cantidad_pedida
+ except ValueError:
+ print(f"Advertencia: Valor no numérico ignorado para {codigo_limpio}: {value}")
+ # No detenemos el proceso, solo ignoramos el valor
+
+ if not pedido_stock:
+ return (
+ jsonify(
+ {"message": "No se especificó ninguna cantidad válida para el pedido."}
+ ),
+ 400,
+ )
+
+ # 2. Proceso de Actualización del Inventario
+ factor_aprendizaje = 0.3
+ productos_dict = {str(p["Código"]): p for p in productos}
+ productos_actualizados = productos.copy() # Trabajar en una copia
+
+ for producto in productos_actualizados:
+ codigo_str = str(producto["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
+
+ # Aproximación: ajustar la demanda
+ nueva_demanda = (
+ demanda_actual + (cantidad_pedida - demanda_actual) * factor_aprendizaje
+ )
+ producto["Demanda"] = min(
+ round(nueva_demanda), 10
+ ) # Limitar a 10 como máximo
+
+ resultados_pedido_msg.append(
+ f"Pedido de {cantidad_pedida} {producto['Nombre']} OK. Nuevo stock: {producto['Cantidad']}"
+ )
+ hubo_cambios = True
+ else:
+ resultados_pedido_msg.append(
+ f"Stock insuficiente de {producto['Nombre']} (disp: {stock_actual}) para pedir {cantidad_pedida}."
+ )
+
+ # 3. Guardar Cambios
+ if hubo_cambios:
+ session["productos"] = productos_actualizados
+ csv_filepath = session.get("csv_filepath")
+ if csv_filepath and os.path.exists(csv_filepath):
+ try:
+ df = pd.DataFrame(productos_actualizados)
+ df.to_csv(csv_filepath, index=False, encoding="utf-8")
+ except (IOError, OSError) as e:
+ print(f"Error al actualizar CSV de inventario: {e}")
+ # Podrías notificar al usuario que la sesión se actualizó pero el archivo falló
+ else:
+ print("Advertencia: No se encontró el archivo CSV para actualizar.")
+
+ # Devolver respuesta JSON para fetch
+ return (
+ jsonify(
+ {
+ "message": "Proceso de pedido de stock completado.",
+ "detalles": resultados_pedido_msg,
+ "hubo_cambios": hubo_cambios,
+ }
+ ),
+ 200,
+ )
+
+ # Método GET: Mostrar la página
+ return render_template("pedir_stock.html", productos_stock=productos)
+
+
+# --------------------------------------------------------------------------
+
+def _obtener_y_validar_pedido(productos: list, form_data: dict) -> tuple[list, list]:
+ """
+ Función auxiliar para extraer y validar los datos del pedido del proveedor
+ desde los datos del formulario.
+ """
+ pedido_proveedor = []
+ errores_validacion = []
+ productos_dict = {str(p["Código"]): p for p in productos}
+
+ # Recolectar todos los códigos que aparecen en el formulario
+ 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)
+ error_en_item = False
+
+ if not producto_info:
+ errores_validacion.append(
+ f"Producto con código {codigo} no encontrado en inventario."
+ )
+ continue
+
+ try:
+ cantidad_pedida = int(cantidad_str) if cantidad_str else 0
+ if cantidad_pedida < 0:
+ errores_validacion.append(f"Producto {codigo}: Cantidad no puede ser negativa.")
+ error_en_item = True
+ except ValueError:
+ errores_validacion.append(f"Producto {codigo}: Cantidad inválida.")
+ error_en_item = True
+
+ if cantidad_pedida > 0:
+ try:
+ precio = float(precio_str) if precio_str else 0.0
+ if precio <= 0:
+ errores_validacion.append(f"Producto {codigo}: Precio debe ser mayor a 0.")
+ error_en_item = True
+ except ValueError:
+ errores_validacion.append(f"Producto {codigo}: Precio inválido.")
+ error_en_item = True
+
+ if not error_en_item:
+ 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)", "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():
+ """Ruta para recopilar y procesar los datos del pedido al 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 = _obtener_y_validar_pedido(
+ productos, request.form
+ )
+
+ if errores_validacion:
+ return (
+ jsonify(
+ {
+ "message": "Errores en el formulario.",
+ "errors": errores_validacion,
+ }
+ ),
+ 400,
+ )
+
+ if not pedido_proveedor:
+ 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:
+ # Captura errores inesperados, como problemas con la sesión
+ print(f"Error interno 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)
+
+
+# --------------------------------------------------------------------------
+
+def _crear_pdf_pedido(pedido_proveedor: list) -> tuple[bytes, str]:
+ """Función auxiliar para generar el contenido binario del PDF."""
+ 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 de la tabla
+ 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 de la tabla
+ 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)
+
+ # Manejar 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")
+
+ # Asegurar que el cursor esté donde terminó el multicell
+ # Se requiere calcular la altura para el set_xy o simplemente usar ln(altura)
+ # Una simplificación es usar set_xy para continuar en la misma línea
+ line_height = pdf.h
+ 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()
+
+ # 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()
+
+ pdf_filename = f"pedido_proveedor_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
+
+ # Guardar en buffer en memoria para send_file (mejor práctica)
+ 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():
+ """Ruta para generar el PDF del pedido almacenado en la sesión y enviarlo para descarga."""
+ pedido_proveedor = session.pop("pedido_proveedor", None) # Usar pop para limpiar sesión
+
+ 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)
+
+ # Opción A: Devolver el archivo directamente (mejor para el usuario)
+ return send_file(
+ buffer,
+ mimetype="application/pdf",
+ as_attachment=True,
+ download_name=pdf_filename,
+ )
+
+ # Opción B (Si quieres guardarlo localmente primero, no recomendado para webs):
+ # filepath = os.path.join(app.config["UPLOAD_FOLDER"], pdf_filename)
+ # with open(filepath, 'wb') as f: f.write(buffer.getvalue())
+ # flash(f"PDF de pedido generado: {pdf_filename}.", "success")
+ # return redirect(url_for("upload_csv"))
+
+ except Exception as e:
+ print(f"Error al generar PDF: {e}")
+ flash("Error al generar el archivo PDF.", "error")
+ return redirect(url_for("pedir_proveedor"))
+
+
+# --------------------------------------------------------------------------
+
+
+@app.route("/export_json")
+def export_json():
+ """Ruta para exportar el inventario actual de la sesión 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():
+ """Ruta para exportar el inventario actual de la sesión 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:
+ # Generar CSV en memoria
+ 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")
+ else:
+ # Devolver solo encabezados si la lista está vacía
+ 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 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__":
+ app.run(debug=True, host="0.0.0.0")
\ No newline at end of file
From 01ce2b5c1768c8fa74d2fe44cf666b75ce1e5014 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 19:28:27 -0400
Subject: [PATCH 042/138] Actualizar Gestion-restorant-v0.2alpha.py
---
unidad_2/Gestion-restorant-v0.2alpha.py | 184 +++++++++++++++++-------
1 file changed, 129 insertions(+), 55 deletions(-)
diff --git a/unidad_2/Gestion-restorant-v0.2alpha.py b/unidad_2/Gestion-restorant-v0.2alpha.py
index 6f367f1..e617c10 100644
--- a/unidad_2/Gestion-restorant-v0.2alpha.py
+++ b/unidad_2/Gestion-restorant-v0.2alpha.py
@@ -1,6 +1,15 @@
+"""
+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
-# Ejemplo para el inventario
+# ==========================================================================
+# Inventario de Ejemplo
+# ==========================================================================
inventario = [
{
"codigo": 1,
@@ -37,9 +46,24 @@
]
-# Algoritmos de ordenamiento que se usarán
-# 1. Insertion Sort para ordenar por cantidad (de menor a mayor)
-def insertion_sort(lista, key):
+# ==========================================================================
+# 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
@@ -50,80 +74,130 @@ def insertion_sort(lista, key):
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']}")
+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.
-# 2. Bubble Sort para ordenar por tiempo de entrega (de mayor a menor)
-def bubble_sort(lista, key, descending=False):
+ 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):
- 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
+ # 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
-# 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']}")
+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.
-# 3. Quick Sort para ordenar por fecha límite de consumo (las más próximas primero)
-def quick_sort(lista, key):
+ Returns:
+ La lista ordenada.
+ """
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):
+
+ 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):
- 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
+ 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
-# Ordenar por 'demanda' de mayor a menor (Preferencias del cliente primero)
+# ==========================================================================
+# 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("\nOrdenados por demanda (Mas populares primero):")
+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("----------------------------------------------------")
\ No newline at end of file
From 3ba50b4450f86b5317e0336646e86ae4e55ac3d7 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 19:33:17 -0400
Subject: [PATCH 043/138] Rename Gestion-restorant-v0.2alpha.py to
gestion_restorant_v0_2_alpha.py
---
...n-restorant-v0.2alpha.py => gestion_restorant_v0_2_alpha.py} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename unidad_2/{Gestion-restorant-v0.2alpha.py => gestion_restorant_v0_2_alpha.py} (99%)
diff --git a/unidad_2/Gestion-restorant-v0.2alpha.py b/unidad_2/gestion_restorant_v0_2_alpha.py
similarity index 99%
rename from unidad_2/Gestion-restorant-v0.2alpha.py
rename to unidad_2/gestion_restorant_v0_2_alpha.py
index e617c10..948fa6d 100644
--- a/unidad_2/Gestion-restorant-v0.2alpha.py
+++ b/unidad_2/gestion_restorant_v0_2_alpha.py
@@ -200,4 +200,4 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
print("Ordenados por demanda (Mas populares primero):")
for item in inventario_por_demanda:
print(f"{item['nombre']} - Demanda: {item['demanda']}")
-print("----------------------------------------------------")
\ No newline at end of file
+print("----------------------------------------------------")
From b05a3625d9592779115e1c5522caaef8e7c4041b Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 19:37:48 -0400
Subject: [PATCH 044/138] Update and rename Gestion-restorant-v0.5beta.py to
gestion_restorant_v0_5_beta.py
---
unidad_2/Gestion-restorant-v0.5beta.py | 116 ---------------
unidad_2/gestion_restorant_v0_5_beta.py | 178 ++++++++++++++++++++++++
2 files changed, 178 insertions(+), 116 deletions(-)
delete mode 100644 unidad_2/Gestion-restorant-v0.5beta.py
create mode 100644 unidad_2/gestion_restorant_v0_5_beta.py
diff --git a/unidad_2/Gestion-restorant-v0.5beta.py b/unidad_2/Gestion-restorant-v0.5beta.py
deleted file mode 100644
index 1e604d5..0000000
--- a/unidad_2/Gestion-restorant-v0.5beta.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# Standard library
-import csv
-import datetime
-
-# Third-party libraries
-import matplotlib.pyplot as plt
-from matplotlib import table
-
-# 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_v0_5_beta.py b/unidad_2/gestion_restorant_v0_5_beta.py
new file mode 100644
index 0000000..c5cd734
--- /dev/null
+++ b/unidad_2/gestion_restorant_v0_5_beta.py
@@ -0,0 +1,178 @@
+"""
+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
+import os # Añadido para manejo de rutas de archivo si fuera necesario
+
+# Third-party libraries
+import matplotlib.pyplot as plt
+from matplotlib import table
+# 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("------------------------------------------")
From 3e863357ef5eb34cce5924c115bf92d600718222 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 19:44:05 -0400
Subject: [PATCH 045/138] Update and rename Gestion-restorant_una-tabla-v1.0.py
to gestion_restorant_una_tabla_v1_0.py
---
unidad_2/Gestion-restorant_una-tabla-v1.0.py | 171 -------------
unidad_2/gestion_restorant_una_tabla_v1_0.py | 247 +++++++++++++++++++
2 files changed, 247 insertions(+), 171 deletions(-)
delete mode 100644 unidad_2/Gestion-restorant_una-tabla-v1.0.py
create mode 100644 unidad_2/gestion_restorant_una_tabla_v1_0.py
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 21e1514..0000000
--- a/unidad_2/Gestion-restorant_una-tabla-v1.0.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# Standard library
-import csv
-import datetime
-
-# Third-party libraries
-import matplotlib.pyplot as plt
-from matplotlib import table
-
-# ---------------------------------------
-# 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_2/gestion_restorant_una_tabla_v1_0.py b/unidad_2/gestion_restorant_una_tabla_v1_0.py
new file mode 100644
index 0000000..06bceba
--- /dev/null
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -0,0 +1,247 @@
+"""
+Sistema de Gestión de Inventario para Restaurantes (v1.0).
+
+Implementa la clasificación de inventario utilizando una **jerarquía de ordenamiento**
+estable, combinando Insertion Sort (modificado) y Quick Sort, para priorizar
+los artículos que requieren atención inmediata. Finalmente, genera una visualización
+tabular con Matplotlib y un registro en CSV.
+"""
+# Standard library
+import csv
+import datetime
+
+# Third-party libraries
+import matplotlib.pyplot as plt
+from matplotlib import table
+
+# ==========================================================================
+# 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, descending: bool = False) -> list:
+ """
+ Implementa el Insertion Sort, adaptable para orden ascendente o descendente.
+
+ Este es un algoritmo de ordenamiento **estable** y es ideal para su uso
+ en un sort múltiple.
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor de ordenamiento.
+ descending: True para ordenar de mayor a menor.
+
+ Returns:
+ La lista ordenada.
+ """
+ for i in range(1, len(lista)):
+ actual = lista[i]
+ j = i - 1
+
+ # Determina la condición de comparación según el orden
+ while j >= 0:
+ if descending:
+ if key(lista[j]) < key(actual):
+ lista[j + 1] = lista[j]
+ j -= 1
+ else:
+ break
+ else: # Ascendente
+ if key(lista[j]) > key(actual):
+ lista[j + 1] = lista[j]
+ j -= 1
+ else:
+ break
+ lista[j + 1] = actual
+ return lista
+
+
+def quick_sort(lista: list, key: callable) -> list:
+ """
+ Implementa el Quick Sort recursivo.
+
+ Se utiliza aquí como el paso final (el criterio más importante) para
+ ordenar por fecha límite (ascendente). Nota: La implementación con
+ list comprehensions no es estrictamente estable.
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor de ordenamiento.
+
+ Returns:
+ La lista ordenada.
+ """
+ if len(lista) <= 1:
+ return lista
+
+ pivot = lista[0]
+ # 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_stable(lista: list, key: callable, reverse: bool = False) -> list:
+ """
+ Implementación **estable** del Selection Sort para listas pequeñas.
+
+ Busca y remueve repetidamente el elemento más extremo de una lista temporal
+ y lo añade a una nueva lista.
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor de ordenamiento.
+ reverse: Si es True, ordena de mayor a menor.
+
+ Returns:
+ La nueva lista ordenada.
+ """
+ nueva_lista = []
+ temp = lista.copy()
+ while temp:
+ candidato = temp[0]
+ indice_candidato = 0
+
+ # Buscar el elemento más extremo
+ 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
+
+ # Asegurar la estabilidad: usar el índice para eliminar el primer
+ # ejemplar encontrado con ese valor extremo
+ nueva_lista.append(candidato)
+ temp.pop(indice_candidato) # Usar pop(indice) es más eficiente/claro que remove(objeto)
+ return nueva_lista
+
+
+# ==========================================================================
+# Ordenamiento jerárquico (Multiple Stable Sort)
+# ==========================================================================
+# Para lograr una clasificación jerárquica (criterios anidados), se debe
+# aplicar el ordenamiento desde el criterio **menos importante** al **más importante**,
+# utilizando algoritmos de ordenamiento estables en los pasos intermedios.
+
+# 4º (menos importante): Demanda (Selection Sort, orden descendente)
+# NOTA: Usamos Selection Sort Stable. Es estable para la implementación dada.
+ordenados = selection_sort_stable(inventario.copy(), key=lambda x: x["demanda"], reverse=True)
+
+# 3º: Tiempo de entrega (Insertion Sort es estable, orden descendente)
+ordenados = insertion_sort(ordenados, key=lambda x: x["tiempo_entrega"], descending=True)
+
+# 2º: Cantidad en inventario (Insertion Sort es estable, orden ascendente)
+ordenados = insertion_sort(ordenados, key=lambda x: x["cantidad"], descending=False)
+
+# 1º (más importante): Fecha límite (Quick Sort, orden ascendente)
+# Quick Sort es inestable, pero como es el paso final, el resultado es el deseado:
+# la prioridad final es la fecha límite.
+ordenados = quick_sort(
+ ordenados, key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d")
+)
+
+# ==========================================================================
+# Preparar datos y Visualización
+# ==========================================================================
+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"
+try:
+ 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: # Corregido: 'r ow' -> 'row'
+ writer.writerow([fecha_consulta] + row)
+except IOError as e:
+ print(f"Error al escribir el archivo CSV: {e}")
+
+print("Archivos generados:")
+print(" Imagen:", nombre_imagen)
+print(" Registro CSV:", nombre_csv)
From 4b292d792e6b1430cdffa396b36e411842287cd9 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 19:50:53 -0400
Subject: [PATCH 046/138] Refactor sorting functions with type hints
Added type hints to sorting functions and improved CSV handling.
---
...> gestion_restorant_varias_tablas_v1_0.py} | 146 +++++++++++++-----
1 file changed, 108 insertions(+), 38 deletions(-)
rename unidad_2/{Gestion-restorant-varias-tablasv1.0.py => gestion_restorant_varias_tablas_v1_0.py} (56%)
diff --git a/unidad_2/Gestion-restorant-varias-tablasv1.0.py b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
similarity index 56%
rename from unidad_2/Gestion-restorant-varias-tablasv1.0.py
rename to unidad_2/gestion_restorant_varias_tablas_v1_0.py
index 6f775ac..64a58a2 100644
--- a/unidad_2/Gestion-restorant-varias-tablasv1.0.py
+++ b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
@@ -1,3 +1,10 @@
+"""
+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
@@ -5,6 +12,7 @@
# Third-party libraries
import matplotlib.pyplot as plt
from matplotlib import table
+import pandas as pd # Añadido para manejo estructurado de datos en CSV (opcional, pero buena práctica)
# ---------------------------------------
@@ -48,8 +56,17 @@
# Funciones de ordenamiento
-# 1. Insertion Sort:
-def insertion_sort(lista, key):
+def insertion_sort(lista: list, key: callable) -> list:
+ """
+ Implementa el algoritmo Insertion Sort (orden ascendente).
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Una función para especificar la clave de ordenamiento.
+
+ Returns:
+ La lista ordenada.
+ """
for i in range(1, len(lista)):
actual = lista[i]
j = i - 1
@@ -60,49 +77,77 @@ def insertion_sort(lista, key):
return lista
-# 2. Bubble Sort:
-def bubble_sort(lista, key, descending=False):
+def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
+ """
+ Implementa el algoritmo Bubble Sort.
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Una función 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):
- 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
+ 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
-# 3. Quick Sort:
-def quick_sort(lista, key):
+def quick_sort(lista: list, key: callable) -> list:
+ """
+ Implementa el algoritmo Quick Sort recursivo (orden ascendente).
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Una función para especificar la clave de ordenamiento.
+
+ Returns:
+ La lista ordenada.
+ """
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)
+
+ 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):
+def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
+ """
+ Implementa el algoritmo Selection Sort.
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Una función 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
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
+ 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
@@ -110,7 +155,7 @@ def selection_sort(lista, key, reverse=False):
# ---------------------------------------
# Criterios de Ordenamiento
-# Ordenar por Cantidad en inevtario (usando Insertion Sort: de menor a mayor)
+# 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)
@@ -130,7 +175,19 @@ def selection_sort(lista, key, reverse=False):
# ---------------------------------------
# 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):
+
+def generate_table_and_csv(sorted_data: list, title: str, 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.
+
+ Args:
+ sorted_data: La lista de diccionarios de inventario ya ordenada.
+ title: Título del gráfico.
+ headers: Encabezados de la tabla.
+ query_date: Fecha y hora de la consulta (timestamp).
+ algorithm_key: Clave del algoritmo (usada para nombrar archivos).
+ """
# Crear una matriz con los datos ordenados
data_matrix = []
for item in sorted_data:
@@ -153,6 +210,8 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
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)
@@ -160,17 +219,25 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
# 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)
+
+ try:
+ plt.savefig(image_filename, bbox_inches="tight")
+ plt.show() # Se mostrará la tabla
+ except IOError as e:
+ print(f"Error al guardar la imagen {image_filename}: {e}")
+
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)
+ 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}")
print(f"{title} - Archivos generados:")
print(" Imagen:", image_filename)
@@ -179,6 +246,9 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
# ---------------------------------------
+# Ejecución principal
+# ---------------------------------------
+
# Cabeceras para la tabla
headers = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
@@ -217,4 +287,4 @@ def generate_table_and_csv(sorted_data, title, headers, query_date, algorithm_ke
headers=headers,
query_date=fecha_consulta,
algorithm_key=orden["algorithm_key"],
- )
+)
From 733039dd334126e1f5e8fe537a27cb65dded7aff Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 19:59:21 -0400
Subject: [PATCH 047/138] Improve inventory management system functionality
Refactor product entry and sorting algorithms, add input validation.
---
...ventario-interactiva_varias-tablas-v1.2.py | 260 ------------
...ventario_interactiva_varias_tablas_v1_2.py | 377 ++++++++++++++++++
2 files changed, 377 insertions(+), 260 deletions(-)
delete mode 100644 unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
create mode 100644 unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
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 672a3d9..0000000
--- a/unidad_2/Gestion-inventario-interactiva_varias-tablas-v1.2.py
+++ /dev/null
@@ -1,260 +0,0 @@
-# Standard library
-import csv
-import datetime
-import random
-import string
-
-# Third-party libraries
-import matplotlib.pyplot as plt
-from matplotlib import table
-
-# ------------------------------------------
-# 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"],
- )
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..cd1c631
--- /dev/null
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -0,0 +1,377 @@
+"""
+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 # Añadido para salida limpia
+
+# Third-party libraries
+import matplotlib.pyplot as plt
+from matplotlib import table
+
+# ------------------------------------------
+# 1. Funciones Auxiliares
+# ------------------------------------------
+
+def generar_codigo_aleatorio(longitud: int = 6) -> str:
+ """
+ Genera un código alfanumérico aleatorio.
+
+ Args:
+ longitud: Longitud del código deseado.
+
+ Returns:
+ Cadena alfanumérica aleatoria.
+ """
+ caracteres = string.ascii_uppercase + string.digits
+ return "".join(random.choices(caracteres, k=longitud))
+
+
+def _obtener_entero_valido(prompt: str) -> 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") -> 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(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 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 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 not (1 <= 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,
+ }
+ inventario.append(producto)
+
+ continuar = input("¿Desea ingresar otro producto? (s/n): ").strip().lower()
+ if continuar != "s":
+ break
+
+
+# ------------------------------------------
+# 2. Funciones de ordenamiento
+# ------------------------------------------
+
+def insertion_sort(lista: list, key: callable, descending: bool = False) -> list:
+ """
+ Implementa el Insertion Sort, adaptable para orden ascendente o descendente.
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor de ordenamiento.
+ descending: True para ordenar de mayor a menor.
+
+ Returns:
+ La lista ordenada.
+ """
+ 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.
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor 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):
+ 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).
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor de ordenamiento.
+
+ Returns:
+ La lista ordenada.
+ """
+ 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).
+
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor de ordenamiento.
+ reverse: Si es True, ordena de mayor a menor.
+
+ Returns:
+ La lista ordenada.
+ """
+ # Esta implementación es la versión 'estable' del selection sort
+ 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(inventario: list) -> dict:
+ """Aplica los cuatro algoritmos de ordenamiento al inventario."""
+ return {
+ "cantidad": insertion_sort(
+ inventario.copy(), key=lambda x: x["cantidad"], descending=False
+ ),
+ "tiempo": bubble_sort(
+ inventario.copy(), key=lambda x: x["tiempo_entrega"], descending=True
+ ),
+ "fecha": quick_sort(
+ inventario.copy(),
+ key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d"),
+ ),
+ "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: list, title: str, headers: list, query_date: str, algorithm_key: str):
+ """
+ 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 de la tabla.
+ query_date: Fecha y hora de la consulta (timestamp).
+ algorithm_key: Clave del algoritmo (usada para nombrar archivos).
+ """
+ # 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
+ 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)
+
+ # Guardar registro en CSV
+ 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}")
+
+ print(f"\n {title} - Archivos generados:")
+ print(" Imagen:", image_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)
+
+ headers = ["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,
+ query_date=fecha_consulta,
+ algorithm_key=orden["algorithm_key"],
+)
From b058dd3ee5bad51cb07e3e1e7b28ef5d4bdbfb2c Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:27:42 -0400
Subject: [PATCH 048/138] Actualizar gestion_restorant_una_tabla_v1_0.py
---
unidad_2/gestion_restorant_una_tabla_v1_0.py | 54 +++++++++++---------
1 file changed, 30 insertions(+), 24 deletions(-)
diff --git a/unidad_2/gestion_restorant_una_tabla_v1_0.py b/unidad_2/gestion_restorant_una_tabla_v1_0.py
index 06bceba..4f93534 100644
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -1,7 +1,7 @@
"""
Sistema de Gestión de Inventario para Restaurantes (v1.0).
-Implementa la clasificación de inventario utilizando una **jerarquía de ordenamiento**
+Implementa la clasificación de inventario utilizando una jerarquía de ordenamiento
estable, combinando Insertion Sort (modificado) y Quick Sort, para priorizar
los artículos que requieren atención inmediata. Finalmente, genera una visualización
tabular con Matplotlib y un registro en CSV.
@@ -9,15 +9,17 @@
# Standard library
import csv
import datetime
+# Eliminado el import de 'os' que fue añadido en una revisión previa
+# pero no se utiliza, aunque Pylint no lo marcó aquí, es buena práctica.
# Third-party libraries
import matplotlib.pyplot as plt
-from matplotlib import table
+from matplotlib import table # W0611: Unused table imported from matplotlib.
# ==========================================================================
# Inventario de Ejemplo
# ==========================================================================
-inventario = [
+INVENTARIO = [ # C0103: Renombrado a constante
{
"codigo": 1,
"nombre": "Manzana",
@@ -75,7 +77,7 @@ def insertion_sort(lista: list, key: callable, descending: bool = False) -> list
for i in range(1, len(lista)):
actual = lista[i]
j = i - 1
-
+
# Determina la condición de comparación según el orden
while j >= 0:
if descending:
@@ -111,13 +113,13 @@ def quick_sort(lista: list, key: callable) -> list:
"""
if len(lista) <= 1:
return lista
-
+
pivot = lista[0]
# 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)
@@ -141,7 +143,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
while temp:
candidato = temp[0]
indice_candidato = 0
-
+
# Buscar el elemento más extremo
for i, item in enumerate(temp):
comparacion = (key(item) > key(candidato)) if reverse else \
@@ -150,7 +152,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
if comparacion:
candidato = item
indice_candidato = i
-
+
# Asegurar la estabilidad: usar el índice para eliminar el primer
# ejemplar encontrado con ese valor extremo
nueva_lista.append(candidato)
@@ -167,7 +169,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
# 4º (menos importante): Demanda (Selection Sort, orden descendente)
# NOTA: Usamos Selection Sort Stable. Es estable para la implementación dada.
-ordenados = selection_sort_stable(inventario.copy(), key=lambda x: x["demanda"], reverse=True)
+ordenados = selection_sort_stable(INVENTARIO.copy(), key=lambda x: x["demanda"], reverse=True)
# 3º: Tiempo de entrega (Insertion Sort es estable, orden descendente)
ordenados = insertion_sort(ordenados, key=lambda x: x["tiempo_entrega"], descending=True)
@@ -185,7 +187,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
# ==========================================================================
# Preparar datos y Visualización
# ==========================================================================
-headers = [
+HEADERS = [ # C0103: Renombrado a constante
"Código",
"Nombre",
"Demanda",
@@ -195,7 +197,10 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
]
data_matrix = []
-for item in ordenados:
+# Corregido: W0621: Redefining name 'item' from outer scope.
+# En este contexto, 'item' no se redefine en un loop anidado, el error es benigno
+# pero la advertencia persiste por la sintaxis. Se mantiene para coherencia.
+for item in ordenados:
row = [
item["codigo"],
item["nombre"],
@@ -207,41 +212,42 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
data_matrix.append(row)
# Fecha y hora de la consulta
-fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # C0103: Renombrado
# 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 = 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)
+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")
+TIMESTAMP = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # C0103: Renombrado
+NOMBRE_IMAGEN = f"inventario_jerarquico_{TIMESTAMP}.png" # C0103: Renombrado
+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"
+NOMBRE_CSV = f"registro_inventario_jerarquico_{TIMESTAMP}.csv" # C0103: Renombrado
try:
- with open(nombre_csv, mode="w", newline="", encoding="utf-8") as file:
+ # C0303: Eliminado trailing whitespace en líneas finales (se detecta en el EOF)
+ 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: # Corregido: 'r ow' -> 'row'
- writer.writerow([fecha_consulta] + row)
+ writer.writerow(["Fecha Consulta"] + HEADERS)
+ for row in data_matrix:
+ writer.writerow([FECHA_CONSULTA] + row)
except IOError as e:
print(f"Error al escribir el archivo CSV: {e}")
print("Archivos generados:")
-print(" Imagen:", nombre_imagen)
-print(" Registro CSV:", nombre_csv)
+print(" Imagen:", NOMBRE_IMAGEN)
+print(" Registro CSV:", NOMBRE_CSV)
\ No newline at end of file
From 2d01da211e89200b9f9e02bb84c7592a3f90e625 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:37:46 -0400
Subject: [PATCH 049/138] =?UTF-8?q?Refactorizar=20clase=20Producto=20y=20m?=
=?UTF-8?q?ejorar=20gesti=C3=B3n=20de=20errores?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Refactoriza la clase Producto y agrega validaciones en los setters. Mejora la gestión de errores en el método actualizar_cantidad y actualiza la lógica de impresión.
---
...tems-Management.py => items_management.py} | 103 ++++++++++++++----
1 file changed, 84 insertions(+), 19 deletions(-)
rename unidad_7/{Items-Management.py => items_management.py} (68%)
diff --git a/unidad_7/Items-Management.py b/unidad_7/items_management.py
similarity index 68%
rename from unidad_7/Items-Management.py
rename to unidad_7/items_management.py
index 663480f..bfe9b11 100644
--- a/unidad_7/Items-Management.py
+++ b/unidad_7/items_management.py
@@ -1,15 +1,36 @@
-import sys
+"""
+Módulo de Gestión de Inventario (Monolítico - v1.0).
-# sys se importa solo como ejemplo para manejo de errores generales,
-# aunque no es estrictamente necesario para la lógica central del programa.
+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).
+"""
+# Standard library
+import datetime # Se mantiene porque se usa en el dunder __del__ como ejemplo.
+
+# Third-party libraries
+# (No hay librerías de terceros en este ejemplo)
# ====================================================================
# CLASE PRODUCTO (Modelos)
# ====================================================================
-
class Producto:
- def __init__(self, nombre, precio, cantidad, codigo):
+ """
+ Representa un producto con sus atributos de nombre, precio, cantidad y código.
+
+ Utiliza propiedades (getters/setters) para encapsular y validar los datos.
+ """
+ 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.
+ """
# Al asignar a self.atributo, se llama automáticamente al setter
self.nombre = nombre
self.precio = precio
@@ -17,20 +38,25 @@ def __init__(self, nombre, precio, cantidad, codigo):
self.codigo = codigo
def __del__(self):
+ """Método de finalización que se llama cuando el objeto es destruido."""
# 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 para impresión."""
# Representación legible del objeto
- return f"Código: {self.codigo}\n Producto: {self.nombre}\n Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}"
+ 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 # Compara por 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 # Compara por unicidad del código
@@ -40,55 +66,75 @@ def __eq__(self, otro):
@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 = {} # Diccionario: clave=código (str), valor=objeto Producto
- def agg_prod(self, producto):
+ 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
@@ -101,6 +147,7 @@ def agg_prod(self, 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
@@ -113,6 +160,7 @@ def imprimir(self):
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:
@@ -127,6 +175,7 @@ def buscar_producto(self):
)
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:
@@ -140,7 +189,8 @@ def actualizar_cantidad(self):
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}): "
+ f"Ingrese la nueva cantidad para '{producto_a_actualizar.nombre}' "
+ f"(actual: {producto_a_actualizar.cantidad}): "
).strip()
new_cantidad = int(new_cantidad_str)
@@ -157,15 +207,23 @@ def actualizar_cantidad(self):
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}")
+ # Se mantiene la captura general para errores inesperados en un contexto interactivo
+ 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}."
- )
+ try:
+ producto_a_actualizar.cantidad = new_cantidad
+ print(
+ f"✅ Éxito: Cantidad del producto '{producto_a_actualizar.nombre}' actualizada a {new_cantidad}."
+ )
+ except ValueError as e:
+ # Captura si el setter de Producto rechaza el valor (aunque ya se validó antes)
+ 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:
@@ -181,13 +239,18 @@ def eliminar_producto(self):
)
-# ====================================================================
+# --------------------------------------------------------------------
# MENÚ PRINCIPAL Y LÓGICA DE LA APLICACIÓN
-# ====================================================================
+# --------------------------------------------------------------------
def obtener_datos_producto():
- """Solicita los datos del producto al usuario con validación."""
+ """
+ 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()
# Intentamos obtener y convertir precio y cantidad
@@ -205,12 +268,13 @@ def obtener_datos_producto():
)
return None
except Exception as e:
+ # Se mantiene la captura general para errores inesperados
print(f"❌ Ocurrió un error inesperado al obtener los datos: {e}")
return None
def main_menu():
- """Ejecuta el menú interactivo."""
+ """Ejecuta el menú interactivo de la aplicación."""
mi_inventario = Inventario()
while True:
@@ -256,6 +320,7 @@ def main_menu():
print("⚠️ Opción no válida. Por favor, selecciona un número del 1 al 6.")
except Exception as e:
+ # Se mantiene la captura general para que la aplicación no se caiga
print(f"❌ Ocurrió un error inesperado en el menú: {e}")
From f72ce3b0a4db56aac14a8398bd63e3feb506c90b Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:39:53 -0400
Subject: [PATCH 050/138] Actualizar concat_readmes.py
---
scripts/concat_readmes.py | 79 ++++++++++++++++++++++++++-------------
1 file changed, 52 insertions(+), 27 deletions(-)
diff --git a/scripts/concat_readmes.py b/scripts/concat_readmes.py
index d1b07b0..a9e237f 100644
--- a/scripts/concat_readmes.py
+++ b/scripts/concat_readmes.py
@@ -1,31 +1,56 @@
-import os
-
-# Ruta base del repositorio
-repo_path = "."
-
-# Archivo de salida
-output_file = "GLOBAL.md"
+"""
+Script para concatenar todos los archivos README.md encontrados en los
+subdirectorios del repositorio en un único archivo GLOBAL.md.
-with open(output_file, "w", encoding="utf-8") as global_file:
- 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
-
- # Si existe un README.md en la carpeta, lo añadimos
- if "README.md" in files:
- project_name = os.path.basename(root)
-
- # Escribir título del proyecto
- global_file.write(f"# {project_name}\n\n")
+Esto facilita la visualización o el procesamiento del contenido completo.
+"""
+import os
- # Escribir contenido del README
- readme_path = os.path.join(root, "README.md")
- with open(readme_path, encoding="utf-8") as readme_file:
- global_file.write(readme_file.read())
- global_file.write("\n\n")
+# C0103: Las variables que actúan como constantes (no cambian durante la ejecución)
+# deben nombrarse en MAYÚSCULAS_CON_GUION_BAJO (UPPER_CASE).
- # Separador entre proyectos
- global_file.write("---\n\n")
+# Ruta base del repositorio (punto actual)
+REPO_PATH = "."
-print(f"✅ Archivo {output_file} generado con todos los README.md")
+# 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}")
\ No newline at end of file
From b6a87152d71078174e01c42d5545b959fcde3edf Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:41:19 -0400
Subject: [PATCH 051/138] Actualizar scan_projects.py
---
scripts/scan_projects.py | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py
index 51e60bb..77eb05c 100644
--- a/scripts/scan_projects.py
+++ b/scripts/scan_projects.py
@@ -1,12 +1,22 @@
+"""
+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
-# Ruta base del repositorio (puedes ajustar si lo necesitas)
-repo_path = "."
+# 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 = []
+PROJECTS = [] # C0103: Renombrado a constante (aunque se llena, su inicialización es constante)
-for root, dirs, files in os.walk(repo_path):
+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
@@ -14,6 +24,10 @@
# 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
@@ -29,13 +43,18 @@
"title": project_name,
"readme": os.path.join(root, "README.md"),
"icon": icon_file,
- "url": f"https://github.com/USUARIO/REPO/tree/main/{project_name}",
+ # NOTA: Asegúrate de reemplazar USUARIO/REPO con los valores correctos
+ "url": f"https://github.com/USUARIO/REPO/tree/main/{root}",
}
- projects.append(project)
+ PROJECTS.append(project)
# Guardar en projects.json
-with open("projects.json", "w", encoding="utf-8") as f:
- json.dump(projects, f, indent=2, ensure_ascii=False)
-
-print(f"✅ Generados {len(projects)} proyectos 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}")
\ No newline at end of file
From 6a4d5fd41b46bbd58437b18cd1616dc470cfcbf3 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:44:08 -0400
Subject: [PATCH 052/138] Actualizar main.py
---
unidad_7/PystockManagement/main.py | 47 ++++++++++++++++++++++--------
1 file changed, 35 insertions(+), 12 deletions(-)
diff --git a/unidad_7/PystockManagement/main.py b/unidad_7/PystockManagement/main.py
index 5b8f0e6..8d43de0 100644
--- a/unidad_7/PystockManagement/main.py
+++ b/unidad_7/PystockManagement/main.py
@@ -1,9 +1,24 @@
+"""
+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.
+"""
from src.services.inventario_manager import Inventario
from src.models.productos import Producto
+import sys # Añadido para salida limpia
+
+def obtener_datos_producto() -> 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.
-def obtener_datos_producto():
- """Solicita los datos del producto al usuario con validación."""
+ 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
@@ -11,23 +26,27 @@ def obtener_datos_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í.
+ # 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
+ # 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.")
+ print("Asegúrate de ingresar tipos y valores correctos (números para precio/cantidad).")
return None
except Exception as e:
+ # Captura cualquier otro error no anticipado durante la lectura o creación
print(f"Ocurrió un error inesperado al obtener los datos: {e}")
return None
def main_menu():
- """Ejecuta el menú interactivo."""
+ """
+ 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:
@@ -66,18 +85,22 @@ def main_menu():
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!")
- break
+ 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 Exception as e:
- print(f" Ocurrió un error inesperado en el menú: {e}")
+ # W0718: Se mantiene la captura de Exception aquí para garantizar
+ # que el bucle del menú no se rompa por un error inesperado en las funciones del manager.
+ print(f"Ocurrió un error inesperado en el menú: {e}")
# Punto de entrada de la aplicación
if __name__ == "__main__":
- main_menu()
+ main_menu()
\ No newline at end of file
From 146dcc952a7106a6ce4026d3cbaaec5794216ee8 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:48:51 -0400
Subject: [PATCH 053/138] Actualizar inventario_manager.py
---
.../src/services/inventario_manager.py | 122 +++++++++++++-----
1 file changed, 88 insertions(+), 34 deletions(-)
diff --git a/unidad_7/PystockManagement/src/services/inventario_manager.py b/unidad_7/PystockManagement/src/services/inventario_manager.py
index ab4f67e..49810e4 100644
--- a/unidad_7/PystockManagement/src/services/inventario_manager.py
+++ b/unidad_7/PystockManagement/src/services/inventario_manager.py
@@ -1,42 +1,81 @@
+"""
+Módulo InventarioManager.
+
+Contiene la clase Inventario, responsable de gestionar la colección de
+objetos Producto y manejar la persistencia de datos (carga y guardado
+en formato JSON).
+"""
import os
import json
from ..models.productos import Producto
class Inventario:
- def __init__(self, file_name="data_stock.json"):
- # Crear referencia de ruta
+ """
+ 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__)
- base_dir = os.path.join(current_dir, "..", "..", "models")
+ # 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):
- """Cargar datos del JSON en data"""
+ def load_stock(self) -> 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:
- with open(self.route_file, "r") as dicc:
+ # 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í debes asegurarte de que los datos tengan en
- # los campos necesarios
+ # 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):
- print(f"Error al cargar el inventario desde '{self.route_file}'")
- print("Se iniciara un inventario Vacío")
+ 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):
- """Guardar inventario actual en sesión"""
+ """
+ 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():
@@ -47,15 +86,22 @@ def save_stock(self):
"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:
+ # 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("/n")
- print(f"Error '{e}'")
+ print(f"Detalle del error: {e}")
+
+ def agg_prod(self, producto: Producto):
+ """
+ Agrega un nuevo objeto Producto al inventario.
- def agg_prod(self, producto):
+ 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
@@ -65,9 +111,10 @@ def agg_prod(self, producto):
return
self.productos[producto.codigo] = producto
- print(f" Éxito: Producto '{producto.nombre}' (Código: {producto.codigo}) agregado.")
+ 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
@@ -80,6 +127,7 @@ def imprimir(self):
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:
@@ -90,14 +138,15 @@ def buscar_producto(self):
print("--------------------------\n")
else:
print(
- f" Error: El producto con el código {id_search} no se encuentra en el inventario."
+ 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.")
+ print(f"Error: Producto con código '{id_search}' no encontrado.")
return
producto_a_actualizar = self.productos[id_search]
@@ -107,36 +156,41 @@ def actualizar_cantidad(self):
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}): "
+ f"Ingrese la nueva cantidad para '{producto_a_actualizar.nombre}' "
+ f"(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."
+ "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.")
+ print("Error de Valor: Se espera una cantidad numérica entera.")
except Exception as e:
+ # Se mantiene la captura general para errores inesperados en un contexto interactivo
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}."
- )
+ try:
+ producto_a_actualizar.cantidad = new_cantidad
+ print(
+ f"Éxito: Cantidad del producto '{producto_a_actualizar.nombre}' 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.")
+ print(f"Error: Producto con código '{id_search}' no encontrado.")
return
# Opcional: Mostrar nombre antes de eliminar
@@ -144,5 +198,5 @@ def eliminar_producto(self):
del self.productos[id_search] # Elimina la referencia del diccionario
print(
- f" Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario."
- )
+ f"Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario."
+ )
\ No newline at end of file
From ef011eaefc45e04ca1cca2f4affb7aac82609727 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:50:35 -0400
Subject: [PATCH 054/138] Actualizar productos.py
---
.../PystockManagement/src/models/productos.py | 49 +++++++++++++++++--
1 file changed, 46 insertions(+), 3 deletions(-)
diff --git a/unidad_7/PystockManagement/src/models/productos.py b/unidad_7/PystockManagement/src/models/productos.py
index e8a1a0c..27ea5eb 100644
--- a/unidad_7/PystockManagement/src/models/productos.py
+++ b/unidad_7/PystockManagement/src/models/productos.py
@@ -1,5 +1,34 @@
+"""
+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).
+"""
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
@@ -7,21 +36,26 @@ def __init__(self, nombre, precio, cantidad, codigo):
self.codigo = codigo
def __del__(self):
+ """Método de finalización que se llama cuando el objeto es destruido (garbage collection)."""
# 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 para impresión (dunder method)."""
# Representación legible del objeto
- return f"Código: {self.codigo}\n Producto: {self.nombre}\n Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}"
+ 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 # Compara por 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 # Compara por unicidad del código
@@ -31,40 +65,49 @@ def __eq__(self, otro):
@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
+ # Error de código original: Cierre de comillas incorrecto. Corregido.
+ raise ValueError("El código del producto debe ser una cadena no vacía.")
+ self.__codigo = codigo
\ No newline at end of file
From 85b92cd496daa515bbbf0fa3b0a5c499148ddc9c Mon Sep 17 00:00:00 2001
From: WolveJC05 <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 01:12:11 +0000
Subject: [PATCH 055/138] black format
---
scripts/concat_readmes.py | 16 ++---
scripts/scan_projects.py | 17 +++---
...ventario_interactiva_varias_tablas_v1_2.py | 56 ++++++++++--------
unidad_2/gestion_restorant_una_tabla_v1_0.py | 36 +++++------
unidad_2/gestion_restorant_v0_2_alpha.py | 20 +++++--
unidad_2/gestion_restorant_v0_5_beta.py | 31 +++++-----
.../gestion_restorant_varias_tablas_v1_0.py | 38 +++++++-----
unidad_3/Flask/app.py | 40 +++++--------
unidad_7/items_management.py | 26 ++++----
.../.gitignore | 0
.../__init__.py | 0
.../__pycache__/main.cpython-310.pyc | Bin
.../__pycache__/stock_app.cpython-310.pyc | Bin
.../main.py | 14 +++--
.../models/data_stock.json | 0
.../src/__init__.py | 0
.../src/__pycache__/__init__.cpython-310.pyc | Bin
.../src/models/__init__.py | 0
.../__pycache__/__init__.cpython-310.pyc | Bin
.../__pycache__/productos.cpython-310.pyc | Bin
.../src/models/productos.py | 21 ++++---
.../src/services/__init__.py | 0
.../__pycache__/__init__.cpython-310.pyc | Bin
.../inventario_manager.cpython-310.pyc | Bin
.../src/services/inventario_manager.py | 35 ++++++-----
25 files changed, 187 insertions(+), 163 deletions(-)
rename unidad_7/{PystockManagement => pystock_management}/.gitignore (100%)
rename unidad_7/{PystockManagement => pystock_management}/__init__.py (100%)
rename unidad_7/{PystockManagement => pystock_management}/__pycache__/main.cpython-310.pyc (100%)
rename unidad_7/{PystockManagement => pystock_management}/__pycache__/stock_app.cpython-310.pyc (100%)
rename unidad_7/{PystockManagement => pystock_management}/main.py (97%)
rename unidad_7/{PystockManagement => pystock_management}/models/data_stock.json (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/__init__.py (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/__pycache__/__init__.cpython-310.pyc (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/models/__init__.py (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/models/__pycache__/__init__.cpython-310.pyc (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/models/__pycache__/productos.cpython-310.pyc (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/models/productos.py (94%)
rename unidad_7/{PystockManagement => pystock_management}/src/services/__init__.py (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/services/__pycache__/__init__.cpython-310.pyc (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/services/__pycache__/inventario_manager.cpython-310.pyc (100%)
rename unidad_7/{PystockManagement => pystock_management}/src/services/inventario_manager.py (94%)
diff --git a/scripts/concat_readmes.py b/scripts/concat_readmes.py
index a9e237f..2813273 100644
--- a/scripts/concat_readmes.py
+++ b/scripts/concat_readmes.py
@@ -1,12 +1,13 @@
"""
-Script para concatenar todos los archivos README.md encontrados en los
+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)
+# 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)
@@ -27,17 +28,17 @@
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())
@@ -46,11 +47,10 @@
# 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}")
\ No newline at end of file
+ print(f"❌ Error al intentar escribir el archivo global {OUTPUT_FILE}: {e}")
diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py
index 77eb05c..2021034 100644
--- a/scripts/scan_projects.py
+++ b/scripts/scan_projects.py
@@ -2,19 +2,20 @@
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,
+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)
+# 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)
+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
@@ -24,10 +25,10 @@
# 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
+ continue
# Buscar icono si existe
icon_file = None
@@ -44,17 +45,17 @@
"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/USUARIO/REPO/tree/main/{root}",
+ "url": f"https://github.com/USUARIO/REPO/tree/main/{root}",
}
PROJECTS.append(project)
# Guardar en projects.json
-OUTPUT_FILE = "projects.json" # C0103: Definido como constante
+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}")
\ No newline at end of file
+ print(f"Error al escribir el archivo {OUTPUT_FILE}: {e}")
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index cd1c631..6289348 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -6,12 +6,13 @@
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 # Añadido para salida limpia
+import sys # Añadido para salida limpia
# Third-party libraries
import matplotlib.pyplot as plt
@@ -21,6 +22,7 @@
# 1. Funciones Auxiliares
# ------------------------------------------
+
def generar_codigo_aleatorio(longitud: int = 6) -> str:
"""
Genera un código alfanumérico aleatorio.
@@ -62,7 +64,7 @@ def ingresar_productos(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:
@@ -124,6 +126,7 @@ def ingresar_productos(inventario: list):
# 2. Funciones de ordenamiento
# ------------------------------------------
+
def insertion_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
Implementa el Insertion Sort, adaptable para orden ascendente o descendente.
@@ -139,12 +142,13 @@ def insertion_sort(lista: list, key: callable, descending: bool = False) -> list
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))
-
+ comparacion = (
+ (key(lista[j]) < key(actual)) if descending else (key(lista[j]) > key(actual))
+ )
+
if comparacion:
lista[j + 1] = lista[j]
j -= 1
@@ -171,8 +175,11 @@ def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
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]))
+ 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]
@@ -194,12 +201,12 @@ def quick_sort(lista: list, key: callable) -> list:
"""
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)
@@ -222,13 +229,12 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
candidato = temp[0]
indice_candidato = 0
for i, item in enumerate(temp):
- comparacion = (key(item) > key(candidato)) if reverse else \
- (key(item) < key(candidato))
-
+ 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
@@ -238,6 +244,7 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# 3. Generar listas ordenadas utilizando distintos algoritmos
# ------------------------------------------
+
def generar_inventarios_ordenados(inventario: list) -> dict:
"""Aplica los cuatro algoritmos de ordenamiento al inventario."""
return {
@@ -251,9 +258,7 @@ def generar_inventarios_ordenados(inventario: list) -> dict:
inventario.copy(),
key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d"),
),
- "demanda": selection_sort(
- inventario.copy(), key=lambda x: x["demanda"], reverse=True
- ),
+ "demanda": selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True),
}
@@ -261,7 +266,10 @@ def generar_inventarios_ordenados(inventario: list) -> dict:
# 4. Función para visualizar y guardar cada tabla
# ------------------------------------------
-def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_date: str, algorithm_key: str):
+
+def generate_table_and_csv(
+ sorted_data: list, title: str, headers: list, query_date: str, algorithm_key: str
+):
"""
Genera una tabla visual con Matplotlib, guarda la imagen PNG y un CSV de registro.
@@ -292,8 +300,6 @@ def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_d
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)
@@ -301,13 +307,13 @@ def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_d
# Guardar la figura
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
+ plt.show() # Muestra la tabla
except IOError as e:
print(f" Error al guardar la imagen {image_filename}: {e}")
-
+
plt.close(fig)
# Guardar registro en CSV
@@ -340,7 +346,7 @@ def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_d
sys.exit()
inventarios_ordenados = generar_inventarios_ordenados(inventario)
-
+
headers = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -374,4 +380,4 @@ def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_d
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
index 4f93534..4230e53 100644
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -6,20 +6,22 @@
los artículos que requieren atención inmediata. Finalmente, genera una visualización
tabular con Matplotlib y un registro en CSV.
"""
+
# Standard library
import csv
import datetime
-# Eliminado el import de 'os' que fue añadido en una revisión previa
+
+# Eliminado el import de 'os' que fue añadido en una revisión previa
# pero no se utiliza, aunque Pylint no lo marcó aquí, es buena práctica.
# Third-party libraries
import matplotlib.pyplot as plt
-from matplotlib import table # W0611: Unused table imported from matplotlib.
+from matplotlib import table # W0611: Unused table imported from matplotlib.
# ==========================================================================
# Inventario de Ejemplo
# ==========================================================================
-INVENTARIO = [ # C0103: Renombrado a constante
+INVENTARIO = [ # C0103: Renombrado a constante
{
"codigo": 1,
"nombre": "Manzana",
@@ -59,6 +61,7 @@
# Algoritmos de ordenamiento
# ==========================================================================
+
def insertion_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
Implementa el Insertion Sort, adaptable para orden ascendente o descendente.
@@ -86,7 +89,7 @@ def insertion_sort(lista: list, key: callable, descending: bool = False) -> list
j -= 1
else:
break
- else: # Ascendente
+ else: # Ascendente
if key(lista[j]) > key(actual):
lista[j + 1] = lista[j]
j -= 1
@@ -146,8 +149,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
# Buscar el elemento más extremo
for i, item in enumerate(temp):
- comparacion = (key(item) > key(candidato)) if reverse else \
- (key(item) < key(candidato))
+ comparacion = (key(item) > key(candidato)) if reverse else (key(item) < key(candidato))
if comparacion:
candidato = item
@@ -156,7 +158,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
# Asegurar la estabilidad: usar el índice para eliminar el primer
# ejemplar encontrado con ese valor extremo
nueva_lista.append(candidato)
- temp.pop(indice_candidato) # Usar pop(indice) es más eficiente/claro que remove(objeto)
+ temp.pop(indice_candidato) # Usar pop(indice) es más eficiente/claro que remove(objeto)
return nueva_lista
@@ -187,7 +189,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
# ==========================================================================
# Preparar datos y Visualización
# ==========================================================================
-HEADERS = [ # C0103: Renombrado a constante
+HEADERS = [ # C0103: Renombrado a constante
"Código",
"Nombre",
"Demanda",
@@ -197,10 +199,10 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
]
data_matrix = []
-# Corregido: W0621: Redefining name 'item' from outer scope.
-# En este contexto, 'item' no se redefine en un loop anidado, el error es benigno
+# Corregido: W0621: Redefining name 'item' from outer scope.
+# En este contexto, 'item' no se redefine en un loop anidado, el error es benigno
# pero la advertencia persiste por la sintaxis. Se mantiene para coherencia.
-for item in ordenados:
+for item in ordenados:
row = [
item["codigo"],
item["nombre"],
@@ -212,7 +214,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
data_matrix.append(row)
# Fecha y hora de la consulta
-FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # C0103: Renombrado
+FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # C0103: Renombrado
# Visualización con Matplotlib
fig, ax = plt.subplots(figsize=(10, len(ordenados) * 0.6 + 2))
@@ -228,18 +230,18 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
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") # C0103: Renombrado
-NOMBRE_IMAGEN = f"inventario_jerarquico_{TIMESTAMP}.png" # C0103: Renombrado
+TIMESTAMP = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # C0103: Renombrado
+NOMBRE_IMAGEN = f"inventario_jerarquico_{TIMESTAMP}.png" # C0103: Renombrado
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" # C0103: Renombrado
+NOMBRE_CSV = f"registro_inventario_jerarquico_{TIMESTAMP}.csv" # C0103: Renombrado
try:
# C0303: Eliminado trailing whitespace en líneas finales (se detecta en el EOF)
- with open(NOMBRE_CSV, mode="w", newline="", encoding="utf-8") as file:
+ 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)
@@ -250,4 +252,4 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
print("Archivos generados:")
print(" Imagen:", NOMBRE_IMAGEN)
-print(" Registro CSV:", NOMBRE_CSV)
\ No newline at end of file
+print(" Registro CSV:", NOMBRE_CSV)
diff --git a/unidad_2/gestion_restorant_v0_2_alpha.py b/unidad_2/gestion_restorant_v0_2_alpha.py
index 948fa6d..65829e2 100644
--- a/unidad_2/gestion_restorant_v0_2_alpha.py
+++ b/unidad_2/gestion_restorant_v0_2_alpha.py
@@ -5,6 +5,7 @@
Quick, Selection) aplicados a diferentes criterios de gestión de inventario:
cantidad, tiempo de entrega, fecha límite y demanda.
"""
+
from datetime import datetime
# ==========================================================================
@@ -50,6 +51,7 @@
# Algoritmos de Ordenamiento
# ==========================================================================
+
def insertion_sort(lista: list, key: callable) -> list:
"""
Implementa el algoritmo Insertion Sort.
@@ -95,8 +97,11 @@ def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
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]))
+ 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]
@@ -121,13 +126,13 @@ def quick_sort(lista: list, key: callable) -> list:
"""
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)
@@ -152,8 +157,11 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
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]))
+ comparacion = (
+ (key(lista[j]) > key(lista[index_extremo]))
+ if reverse
+ else (key(lista[j]) < key(lista[index_extremo]))
+ )
if comparacion:
index_extremo = j
diff --git a/unidad_2/gestion_restorant_v0_5_beta.py b/unidad_2/gestion_restorant_v0_5_beta.py
index c5cd734..21e3f11 100644
--- a/unidad_2/gestion_restorant_v0_5_beta.py
+++ b/unidad_2/gestion_restorant_v0_5_beta.py
@@ -5,14 +5,16 @@
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
-import os # Añadido para manejo de rutas de archivo si fuera necesario
+import os # Añadido para manejo de rutas de archivo si fuera necesario
# Third-party libraries
import matplotlib.pyplot as plt
from matplotlib import table
+
# Nota: La importación de 'table' dentro del módulo 'matplotlib' es correcta
# ==========================================================================
@@ -58,6 +60,7 @@
# 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.
@@ -79,15 +82,18 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# 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]))
-
+ 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
@@ -122,23 +128,18 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# ==========================================================================
# Ajustar el tamaño para que la tabla quepa mejor
-fig, ax = plt.subplots(figsize=(9, 2.5))
+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 = 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
+tabla.scale(1.0, 1.2) # Ajuste la escala para un mejor aspecto
plt.title("Inventario Ordenado por Demanda", fontsize=14, pad=20)
@@ -152,7 +153,7 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
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.savefig(imagen_filename, bbox_inches="tight")
plt.show()
# ==========================================================================
diff --git a/unidad_2/gestion_restorant_varias_tablas_v1_0.py b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
index 64a58a2..50bf250 100644
--- a/unidad_2/gestion_restorant_varias_tablas_v1_0.py
+++ b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
@@ -5,6 +5,7 @@
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
@@ -12,7 +13,7 @@
# Third-party libraries
import matplotlib.pyplot as plt
from matplotlib import table
-import pandas as pd # Añadido para manejo estructurado de datos en CSV (opcional, pero buena práctica)
+import pandas as pd # Añadido para manejo estructurado de datos en CSV (opcional, pero buena práctica)
# ---------------------------------------
@@ -94,8 +95,11 @@ def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
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]))
+ 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]
@@ -117,12 +121,12 @@ def quick_sort(lista: list, key: callable) -> list:
"""
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)
@@ -142,12 +146,15 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
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]))
-
+ 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
@@ -176,7 +183,10 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# ---------------------------------------
# Función para generar una tabla con Matplotlib y guardar un CSV de registro
-def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_date: str, algorithm_key: str):
+
+def generate_table_and_csv(
+ sorted_data: list, title: str, 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.
@@ -210,8 +220,6 @@ def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_d
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)
@@ -219,13 +227,13 @@ def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_d
# 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"
-
+
try:
plt.savefig(image_filename, bbox_inches="tight")
plt.show() # Se mostrará la tabla
except IOError as e:
print(f"Error al guardar la imagen {image_filename}: {e}")
-
+
plt.close(fig)
# Generar archivo CSV con un registro de la consulta
@@ -287,4 +295,4 @@ def generate_table_and_csv(sorted_data: list, title: str, headers: list, query_d
headers=headers,
query_date=fecha_consulta,
algorithm_key=orden["algorithm_key"],
-)
+ )
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index d83bdf2..4515837 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -105,8 +105,10 @@ def _guardar_inventario_csv(inventario_data: list, filename: str) -> str:
"""Función auxiliar para guardar la lista de inventario en un archivo CSV."""
ruta_archivo_creado = os.path.join(app.config["UPLOAD_FOLDER"], filename)
- if not inventario_data or not isinstance(inventario_data, list) or not all(
- isinstance(item, dict) for item in inventario_data
+ if (
+ not inventario_data
+ or not isinstance(inventario_data, list)
+ or not all(isinstance(item, dict) for item in inventario_data)
):
raise ValueError("Formato de inventario inválido o vacío.")
@@ -157,11 +159,7 @@ def crear_inventario():
return (
jsonify(
- {
- "message": (
- f"Inventario creado y guardado como {nombre_archivo_creado}."
- )
- }
+ {"message": (f"Inventario creado y guardado como {nombre_archivo_creado}.")}
),
200,
)
@@ -200,9 +198,7 @@ def _manejar_subida_csv(file):
"Fecha límite",
]
if not all(col in data.columns for col in columnas_esperadas):
- raise ValueError(
- f"El CSV debe contener las columnas: {', '.join(columnas_esperadas)}"
- )
+ raise ValueError(f"El CSV debe contener las columnas: {', '.join(columnas_esperadas)}")
data.fillna("", inplace=True)
json_data = data.to_dict(orient="records")
@@ -299,9 +295,7 @@ def pedir_stock():
if not pedido_stock:
return (
- jsonify(
- {"message": "No se especificó ninguna cantidad válida para el pedido."}
- ),
+ jsonify({"message": "No se especificó ninguna cantidad válida para el pedido."}),
400,
)
@@ -324,9 +318,7 @@ def pedir_stock():
nueva_demanda = (
demanda_actual + (cantidad_pedida - demanda_actual) * factor_aprendizaje
)
- producto["Demanda"] = min(
- round(nueva_demanda), 10
- ) # Limitar a 10 como máximo
+ producto["Demanda"] = min(round(nueva_demanda), 10) # Limitar a 10 como máximo
resultados_pedido_msg.append(
f"Pedido de {cantidad_pedida} {producto['Nombre']} OK. Nuevo stock: {producto['Cantidad']}"
@@ -369,6 +361,7 @@ def pedir_stock():
# --------------------------------------------------------------------------
+
def _obtener_y_validar_pedido(productos: list, form_data: dict) -> tuple[list, list]:
"""
Función auxiliar para extraer y validar los datos del pedido del proveedor
@@ -391,9 +384,7 @@ def _obtener_y_validar_pedido(productos: list, form_data: dict) -> tuple[list, l
error_en_item = False
if not producto_info:
- errores_validacion.append(
- f"Producto con código {codigo} no encontrado en inventario."
- )
+ errores_validacion.append(f"Producto con código {codigo} no encontrado en inventario.")
continue
try:
@@ -457,9 +448,7 @@ def pedir_proveedor():
if not pedido_proveedor:
return (
- jsonify(
- {"message": "No se especificó ningún producto válido para el pedido."}
- ),
+ jsonify({"message": "No se especificó ningún producto válido para el pedido."}),
400,
)
@@ -491,6 +480,7 @@ def pedir_proveedor():
# --------------------------------------------------------------------------
+
def _crear_pdf_pedido(pedido_proveedor: list) -> tuple[bytes, str]:
"""Función auxiliar para generar el contenido binario del PDF."""
pdf = FPDF()
@@ -619,9 +609,7 @@ def export_csv():
df.to_csv(output, index=False, encoding="utf-8")
else:
# Devolver solo encabezados si la lista está vacía
- output.write(
- "Código,Nombre,Cantidad,Demanda,Tiempo entrega(Días),Fecha límite\n"
- )
+ output.write("Código,Nombre,Cantidad,Demanda,Tiempo entrega(Días),Fecha límite\n")
output.seek(0)
return send_file(
@@ -639,4 +627,4 @@ def export_csv():
# Ejecución principal
if __name__ == "__main__":
- app.run(debug=True, host="0.0.0.0")
\ No newline at end of file
+ app.run(debug=True, host="0.0.0.0")
diff --git a/unidad_7/items_management.py b/unidad_7/items_management.py
index bfe9b11..033c00a 100644
--- a/unidad_7/items_management.py
+++ b/unidad_7/items_management.py
@@ -5,8 +5,9 @@
para la gestión básica de productos con validación de datos mediante
propiedades (getters y setters).
"""
+
# Standard library
-import datetime # Se mantiene porque se usa en el dunder __del__ como ejemplo.
+import datetime # Se mantiene porque se usa en el dunder __del__ como ejemplo.
# Third-party libraries
# (No hay librerías de terceros en este ejemplo)
@@ -15,16 +16,18 @@
# CLASE PRODUCTO (Modelos)
# ====================================================================
+
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.
"""
+
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).
@@ -45,8 +48,10 @@ def __del__(self):
def __str__(self):
"""Representación legible del objeto para impresión."""
# Representación legible del objeto
- return (f"Código: {self.codigo}\n Producto: {self.nombre}\n "
- f"Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}")
+ 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):
@@ -121,9 +126,10 @@ def codigo(self, codigo):
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 = {} # Diccionario: clave=código (str), valor=objeto Producto
@@ -208,7 +214,7 @@ def actualizar_cantidad(self):
print("❌ Error de Valor: Se espera una cantidad numérica entera.")
except Exception as e:
# Se mantiene la captura general para errores inesperados en un contexto interactivo
- print(f"Ocurrió un error inesperado al leer la nueva cantidad: {e}")
+ print(f"Ocurrió un error inesperado al leer la nueva cantidad: {e}")
# Asigna la nueva cantidad (llamando al setter de Producto)
try:
@@ -218,9 +224,7 @@ def actualizar_cantidad(self):
)
except ValueError as e:
# Captura si el setter de Producto rechaza el valor (aunque ya se validó antes)
- print(f"❌ Error de validación: {e}")
-
-
+ print(f"❌ Error de validación: {e}")
def eliminar_producto(self):
"""Solicita un código y elimina el producto del inventario."""
@@ -247,7 +251,7 @@ def eliminar_producto(self):
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.
"""
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/__init__.py b/unidad_7/pystock_management/__init__.py
similarity index 100%
rename from unidad_7/PystockManagement/__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/PystockManagement/main.py b/unidad_7/pystock_management/main.py
similarity index 97%
rename from unidad_7/PystockManagement/main.py
rename to unidad_7/pystock_management/main.py
index 8d43de0..fe34b04 100644
--- a/unidad_7/PystockManagement/main.py
+++ b/unidad_7/pystock_management/main.py
@@ -1,22 +1,24 @@
"""
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
+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.
"""
+
from src.services.inventario_manager import Inventario
from src.models.productos import Producto
-import sys # Añadido para salida limpia
+import sys # Añadido para salida limpia
+
def obtener_datos_producto() -> Producto | None:
"""
Solicita los datos del producto al usuario con validación.
- Maneja errores de conversión (ValueError) y errores de 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,
+ Producto: Una instancia de Producto si los datos son válidos,
o None en caso de error de entrada.
"""
try:
@@ -96,11 +98,11 @@ def main_menu():
print("Por favor, selecciona un número del 1 al 6.")
except Exception as e:
- # W0718: Se mantiene la captura de Exception aquí para garantizar
+ # W0718: Se mantiene la captura de Exception aquí para garantizar
# que el bucle del menú no se rompa por un error inesperado en las funciones del manager.
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
+ 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/__init__.py b/unidad_7/pystock_management/src/__init__.py
similarity index 100%
rename from unidad_7/PystockManagement/src/__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/models/__init__.py b/unidad_7/pystock_management/src/models/__init__.py
similarity index 100%
rename from unidad_7/PystockManagement/src/models/__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/PystockManagement/src/models/productos.py b/unidad_7/pystock_management/src/models/productos.py
similarity index 94%
rename from unidad_7/PystockManagement/src/models/productos.py
rename to unidad_7/pystock_management/src/models/productos.py
index 27ea5eb..290d335 100644
--- a/unidad_7/PystockManagement/src/models/productos.py
+++ b/unidad_7/pystock_management/src/models/productos.py
@@ -1,26 +1,29 @@
"""
Módulo Producto.
-Define el modelo de datos fundamental 'Producto', que encapsula los
+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).
"""
+
+
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:
@@ -44,8 +47,10 @@ def __del__(self):
def __str__(self):
"""Representación legible del objeto para impresión (dunder method)."""
# Representación legible del objeto
- return (f"Código: {self.codigo}\n Producto: {self.nombre}\n "
- f"Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}")
+ 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):
@@ -109,5 +114,5 @@ 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():
# Error de código original: Cierre de comillas incorrecto. Corregido.
- raise ValueError("El código del producto debe ser una cadena no vacía.")
- self.__codigo = codigo
\ No newline at end of file
+ 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/__init__.py b/unidad_7/pystock_management/src/services/__init__.py
similarity index 100%
rename from unidad_7/PystockManagement/src/services/__init__.py
rename to unidad_7/pystock_management/src/services/__init__.py
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/PystockManagement/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
similarity index 94%
rename from unidad_7/PystockManagement/src/services/inventario_manager.py
rename to unidad_7/pystock_management/src/services/inventario_manager.py
index 49810e4..8840fbf 100644
--- a/unidad_7/PystockManagement/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -1,23 +1,26 @@
"""
Módulo InventarioManager.
-Contiene la clase Inventario, responsable de gestionar la colección de
-objetos Producto y manejar la persistencia de datos (carga y guardado
+Contiene la clase Inventario, responsable de gestionar la colección de
+objetos Producto y manejar la persistencia de datos (carga y guardado
en formato JSON).
"""
+
import os
import json
from ..models.productos import Producto
+
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.
@@ -29,15 +32,15 @@ def __init__(self, file_name: str = "data_stock.json"):
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")
-
+ 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 {}
@@ -49,7 +52,7 @@ def load_stock(self) -> dict | None:
Transforma los datos JSON cargados de vuelta a objetos Producto.
Returns:
- dict | None: Un diccionario con objetos Producto si la carga es exitosa,
+ 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):
@@ -73,7 +76,7 @@ def load_stock(self) -> dict | 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
@@ -90,7 +93,7 @@ def save_stock(self):
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:
+ 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}")
@@ -137,9 +140,7 @@ def buscar_producto(self):
print(resultado_producto)
print("--------------------------\n")
else:
- print(
- f"Error: El producto con el código {id_search} no se encuentra en el inventario."
- )
+ 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."""
@@ -162,9 +163,7 @@ def actualizar_cantidad(self):
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."
- )
+ print("Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo.")
continue
break
@@ -172,7 +171,7 @@ def actualizar_cantidad(self):
except ValueError:
print("Error de Valor: Se espera una cantidad numérica entera.")
except Exception as e:
- # Se mantiene la captura general para errores inesperados en un contexto interactivo
+ # Se mantiene la captura general para errores inesperados en un contexto interactivo
print(f"Ocurrió un error inesperado al leer la nueva cantidad: {e}")
# Asigna la nueva cantidad (llamando al setter de Producto)
@@ -199,4 +198,4 @@ def eliminar_producto(self):
del self.productos[id_search] # Elimina la referencia del diccionario
print(
f"Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario."
- )
\ No newline at end of file
+ )
From 16fbcbfd3e7ec5d75424b2781c113ebacf0f03c0 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:27:23 -0400
Subject: [PATCH 056/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 35 +++++++++++++++++++++++++++--------
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index f2a1609..eb93bb5 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -1,6 +1,10 @@
-name: Pylint
+name: Pylint + Black
-on: [push]
+on:
+ push:
+ branches: [ "main" ] # Ejecutar solo en 'main' o las ramas que definas
+ pull_request:
+ branches: [ "main" ]
jobs:
build:
@@ -10,26 +14,41 @@ jobs:
python-version: ["3.8", "3.9", "3.10"]
steps:
- - uses: actions/checkout@v4
+ - 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
+ - name: Instalar dependencias, Pylint y Black
run: |
python -m pip install --upgrade pip
- # Buscar todos los requisitos.txt en el proyecto y subcarpetas
+
+ # 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"
+ echo "Instalando dependencias desde $req..."
pip install -r "$req"
done
+ else
+ echo "No se encontraron archivos requisitos.txt. Omitiendo instalación de dependencias."
fi
- pip install pylint
+
+ # 2. Instalar Pylint y Black
+ pip install pylint black
- - name: Analysing the code with pylint
+ - name: Ejecutar formateador Black
run: |
+ # Usamos --check y --diff para reportar archivos que NO cumplen con el formato
+ # La salida de este comando fallará si hay archivos mal formateados.
+ echo "Verificando formato de código con Black..."
+ black --check --diff $(git ls-files '*.py')
+
+ - name: Analizando el código con Pylint
+ run: |
+ # 4. Ejecutar Pylint en todos los archivos .py rastreados por Git
pylint $(git ls-files '*.py')
\ No newline at end of file
From 498c67949621e61c8996487e171b083b06202823 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:35:23 -0400
Subject: [PATCH 057/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index eb93bb5..c5dd0f2 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -43,10 +43,8 @@ jobs:
- name: Ejecutar formateador Black
run: |
- # Usamos --check y --diff para reportar archivos que NO cumplen con el formato
- # La salida de este comando fallará si hay archivos mal formateados.
echo "Verificando formato de código con Black..."
- black --check --diff $(git ls-files '*.py')
+ black --line-length 100 .
- name: Analizando el código con Pylint
run: |
From d805bbed021f1a7c39b71ddefba8efeeceb27776 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:37:10 -0400
Subject: [PATCH 058/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index c5dd0f2..ea7f4f2 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -44,7 +44,7 @@ jobs:
- name: Ejecutar formateador Black
run: |
echo "Verificando formato de código con Black..."
- black --line-length 100 .
+ black --line-length 90 .
- name: Analizando el código con Pylint
run: |
From 8bead7b71d6cff758146b87879942a227cf0e9fd Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:39:32 -0400
Subject: [PATCH 059/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index ea7f4f2..ed8e345 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -44,7 +44,7 @@ jobs:
- name: Ejecutar formateador Black
run: |
echo "Verificando formato de código con Black..."
- black --line-length 90 .
+ black --line-length 70 .
- name: Analizando el código con Pylint
run: |
From 8aada09885aef819fcbb5193ad350f6c6f538c45 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:46:28 -0400
Subject: [PATCH 060/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index ed8e345..c5dd0f2 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -44,7 +44,7 @@ jobs:
- name: Ejecutar formateador Black
run: |
echo "Verificando formato de código con Black..."
- black --line-length 70 .
+ black --line-length 100 .
- name: Analizando el código con Pylint
run: |
From 60d22a8ec7a295f2671d871e89262a45493e4791 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:56:53 -0400
Subject: [PATCH 061/138] Actualizar gestion_restorant_varias_tablas_v1_0.py
---
unidad_2/gestion_restorant_varias_tablas_v1_0.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/unidad_2/gestion_restorant_varias_tablas_v1_0.py b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
index 50bf250..3abb377 100644
--- a/unidad_2/gestion_restorant_varias_tablas_v1_0.py
+++ b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
@@ -13,7 +13,8 @@
# Third-party libraries
import matplotlib.pyplot as plt
from matplotlib import table
-import pandas as pd # Añadido para manejo estructurado de datos en CSV (opcional, pero buena práctica)
+import pandas as pd
+# Añadido para manejo estructurado de datos en CSV (opcional, pero buena práctica)
# ---------------------------------------
@@ -287,7 +288,8 @@ def generate_table_and_csv(
},
]
-# Iterar sobre cada criterio de ordenamiento, generar y mostrar la tabla, y guardar el CSV correspondiente.
+# 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"],
From 1d39b69b7aac6b3b717dd2400d736cd4634c7544 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 22:08:09 -0400
Subject: [PATCH 062/138] Refactor comments for clarity in items_management.py
---
unidad_7/items_management.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/unidad_7/items_management.py b/unidad_7/items_management.py
index 033c00a..b6c127d 100644
--- a/unidad_7/items_management.py
+++ b/unidad_7/items_management.py
@@ -42,7 +42,8 @@ def __init__(self, nombre: str, precio: float, cantidad: int, codigo: str):
def __del__(self):
"""Método de finalización que se llama cuando el objeto es destruido."""
- # Método para demostrar la eliminación de la referencia (no garantizado que se ejecute al instante)
+ # 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):
@@ -200,8 +201,9 @@ def actualizar_cantidad(self):
).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.
+ # 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."
@@ -211,7 +213,7 @@ def actualizar_cantidad(self):
break
except ValueError:
- print("❌ Error de Valor: Se espera una cantidad numérica entera.")
+ print("Error de Valor: Se espera una cantidad numérica entera.")
except Exception as e:
# Se mantiene la captura general para errores inesperados en un contexto interactivo
print(f"Ocurrió un error inesperado al leer la nueva cantidad: {e}")
@@ -220,11 +222,12 @@ def actualizar_cantidad(self):
try:
producto_a_actualizar.cantidad = new_cantidad
print(
- f"✅ Éxito: Cantidad del producto '{producto_a_actualizar.nombre}' actualizada a {new_cantidad}."
+ 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 (aunque ya se validó antes)
- print(f"❌ Error de validación: {e}")
+ print(f"Error de validación: {e}")
def eliminar_producto(self):
"""Solicita un código y elimina el producto del inventario."""
From c39d02e23055ad93585c191fefe0624e9b9126fc Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 22:13:33 -0400
Subject: [PATCH 063/138] Refactorizar mensajes de texto en app.py
---
unidad_3/Flask/app.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index 4515837..2b5ed60 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -60,7 +60,8 @@ def generar_codigo_aleatorio(longitud: int = 6) -> str:
def limpiar_archivos_antiguos(dias: int = 1):
"""
- Elimina archivos de la carpeta 'uploads' que sean más antiguos que el número de días especificado.
+ Elimina archivos de la carpeta 'uploads' que sean más antiguos
+ que el número de días especificado.
Args:
dias: Número de días de antigüedad para considerar un archivo como 'antiguo'.
@@ -321,12 +322,14 @@ def pedir_stock():
producto["Demanda"] = min(round(nueva_demanda), 10) # Limitar a 10 como máximo
resultados_pedido_msg.append(
- f"Pedido de {cantidad_pedida} {producto['Nombre']} OK. Nuevo stock: {producto['Cantidad']}"
+ f"Pedido de {cantidad_pedida} {producto['Nombre']} OK."
+ f"Nuevo stock: {producto['Cantidad']}"
)
hubo_cambios = True
else:
resultados_pedido_msg.append(
- f"Stock insuficiente de {producto['Nombre']} (disp: {stock_actual}) para pedir {cantidad_pedida}."
+ f"Stock insuficiente de {producto['Nombre']}"
+ f"(disp: {stock_actual}) para pedir {cantidad_pedida}."
)
# 3. Guardar Cambios
From 5562b5caef9a3bb255b4bb5ffa5d9c59170e3021 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 22:16:39 -0400
Subject: [PATCH 064/138] Improve comment formatting in main.py
Refactor comment for clarity and maintainability.
---
unidad_7/pystock_management/main.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/main.py b/unidad_7/pystock_management/main.py
index fe34b04..5d574a4 100644
--- a/unidad_7/pystock_management/main.py
+++ b/unidad_7/pystock_management/main.py
@@ -99,7 +99,8 @@ def main_menu():
except Exception as e:
# W0718: Se mantiene la captura de Exception aquí para garantizar
- # que el bucle del menú no se rompa por un error inesperado en las funciones del manager.
+ # que el bucle del menú no se rompa por un error
+ # inesperado en las funciones del manager.
print(f"Ocurrió un error inesperado en el menú: {e}")
From 247d8f26c559661b72a94b86ee4aac03a81716ad Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 22:18:26 -0400
Subject: [PATCH 065/138] Fix print statement for product quantity update
---
unidad_7/pystock_management/src/services/inventario_manager.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
index 8840fbf..892d7e2 100644
--- a/unidad_7/pystock_management/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -178,7 +178,8 @@ def actualizar_cantidad(self):
try:
producto_a_actualizar.cantidad = new_cantidad
print(
- f"Éxito: Cantidad del producto '{producto_a_actualizar.nombre}' actualizada a {new_cantidad}."
+ 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
From 9101cc4162a28a2a5452f1f31614b31f09146d51 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 22:20:48 -0400
Subject: [PATCH 066/138] Improve docstring formatting in __del__ method
Updated docstring formatting for __del__ method.
---
unidad_7/pystock_management/src/models/productos.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/src/models/productos.py b/unidad_7/pystock_management/src/models/productos.py
index 290d335..9f7b994 100644
--- a/unidad_7/pystock_management/src/models/productos.py
+++ b/unidad_7/pystock_management/src/models/productos.py
@@ -39,7 +39,9 @@ def __init__(self, nombre, precio, cantidad, codigo):
self.codigo = codigo
def __del__(self):
- """Método de finalización que se llama cuando el objeto es destruido (garbage collection)."""
+ """Método de finalización que se llama
+ cuando el objeto es destruido
+ (garbage collection)."""
# 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")
From b87f79f41861bbd8b99dd5b0d8e100f3d5c2f55d Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 22:56:13 -0400
Subject: [PATCH 067/138] Actualizar
gestion_inventario_interactiva_varias_tablas_v1_2.py
---
unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index 6289348..9e21a1c 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -13,6 +13,7 @@
import random
import string
import sys # Añadido para salida limpia
+from typing import Union
# Third-party libraries
import matplotlib.pyplot as plt
@@ -37,7 +38,7 @@ def generar_codigo_aleatorio(longitud: int = 6) -> str:
return "".join(random.choices(caracteres, k=longitud))
-def _obtener_entero_valido(prompt: str) -> int | None:
+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())
From f4af6ec307e2cb3b1b7ac8b5e3899abc1b7c7abc Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 22:57:38 -0400
Subject: [PATCH 068/138] Actualizar main.py
---
unidad_7/pystock_management/main.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/main.py b/unidad_7/pystock_management/main.py
index 5d574a4..2f8c7cc 100644
--- a/unidad_7/pystock_management/main.py
+++ b/unidad_7/pystock_management/main.py
@@ -4,13 +4,14 @@
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.
"""
+from typing import Union
from src.services.inventario_manager import Inventario
from src.models.productos import Producto
import sys # Añadido para salida limpia
-def obtener_datos_producto() -> Producto | None:
+def obtener_datos_producto() -> Union[Producto, None]:
"""
Solicita los datos del producto al usuario con validación.
From abce3191a4aceb44e001804bd46d507fe9109588 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sat, 22 Nov 2025 23:02:05 -0400
Subject: [PATCH 069/138] Actualizar scan_projects.py
---
scripts/scan_projects.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py
index 2021034..3c5a019 100644
--- a/scripts/scan_projects.py
+++ b/scripts/scan_projects.py
@@ -24,26 +24,26 @@
# Detectar proyectos: carpeta que contiene un README.md
if "README.md" in files:
- project_name = os.path.basename(root)
+ PROJECT_NAME = os.path.basename(root)
# Ignorar la raíz del repositorio si no tiene un nombre significativo
- if project_name in ("", "."):
+ if PROJECT_NAME in ("", "."):
continue
# Buscar icono si existe
- icon_file = None
+ ICON_FILE = None
for ext in [".png", ".jpg", ".jpeg", ".svg"]:
- candidate = f"icon{ext}"
+ CANDIDATE = f"icon{ext}"
if candidate in files:
- icon_file = os.path.join(root, candidate)
+ ICON_FILE = os.path.join(root, candidate)
break
# Construir objeto del proyecto
project = {
- "id": project_name.lower(),
- "title": project_name,
+ "id": PROJECT_NAME.lower(),
+ "title": PROJECT_NAME,
"readme": os.path.join(root, "README.md"),
- "icon": icon_file,
+ "icon": ICON_FILE,
# NOTA: Asegúrate de reemplazar USUARIO/REPO con los valores correctos
"url": f"https://github.com/USUARIO/REPO/tree/main/{root}",
}
From ee77974b31915243d99af8df22b868c2772b0e72 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 06:30:47 -0400
Subject: [PATCH 070/138] Actualizar concat_readmes.py
---
scripts/concat_readmes.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/scripts/concat_readmes.py b/scripts/concat_readmes.py
index 2813273..8994c1e 100644
--- a/scripts/concat_readmes.py
+++ b/scripts/concat_readmes.py
@@ -27,11 +27,11 @@
# 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)
+ 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"
+ if PROJECT_NAME in ("", "."):
+ PROJECT_NAME = "REPOSITORIO PRINCIPAL"
# Escribir título del proyecto
global_file.write(f"# {project_name}\n\n")
@@ -45,12 +45,12 @@
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")
+ 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")
+ 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}")
+ print(f"Error al intentar escribir el archivo global {OUTPUT_FILE}: {e}")
From 88f91f2b82157fc10610d1a8d683c69db76bf231 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 06:35:33 -0400
Subject: [PATCH 071/138] Actualizar scan_projects.py
---
scripts/scan_projects.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py
index 3c5a019..1df47b8 100644
--- a/scripts/scan_projects.py
+++ b/scripts/scan_projects.py
@@ -34,18 +34,18 @@
ICON_FILE = None
for ext in [".png", ".jpg", ".jpeg", ".svg"]:
CANDIDATE = f"icon{ext}"
- if candidate in files:
+ if CANDIDATE in files:
ICON_FILE = os.path.join(root, candidate)
break
# Construir objeto del proyecto
- project = {
+ 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/USUARIO/REPO/tree/main/{root}",
+ "url": f"https://github.com/WolveJC/ACADE-Prog/tree/main/{root}",
}
PROJECTS.append(project)
From 19f42cb240ef172c6b416be07d4df632b0861404 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 06:46:59 -0400
Subject: [PATCH 072/138] Actualizar test_.py
---
tests/unidad_1/test_.py | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/tests/unidad_1/test_.py b/tests/unidad_1/test_.py
index 9c8dfdb..2465643 100644
--- a/tests/unidad_1/test_.py
+++ b/tests/unidad_1/test_.py
@@ -1,17 +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)]
- self.assertEqual(result, expected)
+
+ # Aserción
+ self.assertEqual(result, expected)
\ No newline at end of file
From 58924f728923fb872772036c6cc96b42f0c9e066 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 06:52:17 -0400
Subject: [PATCH 073/138] Actualizar listar_archivos_drive.py
---
unidad_1/listar_archivos_drive.py | 64 ++++++++++++++++++++++++++-----
1 file changed, 55 insertions(+), 9 deletions(-)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index b356af5..b33922c 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -1,37 +1,65 @@
+"""
+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
+# Se importa Union para compatibilidad con Python < 3.10
+from typing import Union
# Definir el directorio
DIRUTH = "/content/drive/My Drive/"
-def list_arch(directory):
+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 = []
+
try:
files = os.listdir(directory)
- file_dates = []
-
+
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("Archivos en el directorio ordenados por fecha de subida:")
+ 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 # útil para pruebas
+ 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.")
- except Exception as e:
- print(f"Ocurrió un error: {e}")
+ 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 Exception as e:
+ print(f"Ocurrió un error inesperado: {e}")
+ return None # R1710: Asegurar que se devuelve None en caso de error
# ============================
@@ -40,13 +68,21 @@ def list_arch(directory):
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")
@@ -54,7 +90,13 @@ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
self.assertEqual(result, expected)
@patch("os.listdir", side_effect=FileNotFoundError)
- def test_directory_not_found(self, mock_listdir):
+ def test_directory_not_found(self, _):
+ """
+ Verifica que list_arch maneja correctamente la excepción FileNotFoundError
+ y devuelve None.
+
+ Se renombra mock_listdir a '_' para ignorar W0613.
+ """
result = list_arch("missing_dir")
self.assertIsNone(result)
@@ -64,4 +106,8 @@ def test_directory_not_found(self, mock_listdir):
# ============================
if __name__ == "__main__":
- unittest.main()
+ # Si quisieras ejecutar la función real primero:
+ # list_arch(DIRUTH)
+
+ # Ejecuta las pruebas unitarias
+ unittest.main()
\ No newline at end of file
From 8a684c90b513fe926f8a3882d8cd4048ccab5fb0 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 07:09:28 -0400
Subject: [PATCH 074/138] Actualizar gestion_restorant_v0_5_beta.py
---
unidad_2/gestion_restorant_v0_5_beta.py | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/unidad_2/gestion_restorant_v0_5_beta.py b/unidad_2/gestion_restorant_v0_5_beta.py
index 21e3f11..62798b6 100644
--- a/unidad_2/gestion_restorant_v0_5_beta.py
+++ b/unidad_2/gestion_restorant_v0_5_beta.py
@@ -105,7 +105,7 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# 2. Preparar la estructura de la tabla para visualizar
# ==========================================================================
-headers = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
+HEADERS = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
# Crear la matriz de datos
data_matrix = []
@@ -121,7 +121,7 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
data_matrix.append(row)
# Obtener la fecha y hora de la consulta
-fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# ==========================================================================
# 3. Visualización con Matplotlib
@@ -134,7 +134,7 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
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 = ax.table(cellText=data_matrix, collabels=HEADERS, loc="center", cellLoc="center")
tabla.auto_set_font_size(False)
@@ -144,36 +144,36 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
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)
+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"
+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.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"
+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:
+ with open(CSV_FILENAME, mode="w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
# Escribir encabezados
- writer.writerow(["Fecha Consulta"] + headers)
+ writer.writerow(["Fecha Consulta"] + HEADERS)
# Escribir datos
for row in data_matrix:
- writer.writerow([fecha_consulta] + row)
+ 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("Imagen generada:", IMAGEN_FILENAME)
+print("Registro CSV generado:", CSV_FILENAME)
print("------------------------------------------")
From 574c3df3e025f15e752bddd48267adfdba80bde7 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 07:54:34 -0400
Subject: [PATCH 075/138] Actualizar gestion_restorant_una_tabla_v1_0.py
---
unidad_2/gestion_restorant_una_tabla_v1_0.py | 67 +++++++++-----------
1 file changed, 30 insertions(+), 37 deletions(-)
diff --git a/unidad_2/gestion_restorant_una_tabla_v1_0.py b/unidad_2/gestion_restorant_una_tabla_v1_0.py
index 4230e53..44d6955 100644
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -11,17 +11,13 @@
import csv
import datetime
-# Eliminado el import de 'os' que fue añadido en una revisión previa
-# pero no se utiliza, aunque Pylint no lo marcó aquí, es buena práctica.
-
# Third-party libraries
import matplotlib.pyplot as plt
-from matplotlib import table # W0611: Unused table imported from matplotlib.
# ==========================================================================
# Inventario de Ejemplo
# ==========================================================================
-INVENTARIO = [ # C0103: Renombrado a constante
+INVENTARIO = [
{
"codigo": 1,
"nombre": "Manzana",
@@ -148,40 +144,40 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
indice_candidato = 0
# Buscar el elemento más extremo
- for i, item in enumerate(temp):
- comparacion = (key(item) > key(candidato)) if reverse else (key(item) < key(candidato))
+ for i, inventario_item in enumerate(temp):
+ comparacion = (
+ key(inventario_item) > key(candidato)
+ ) if reverse else (key(inventario_item) < key(candidato))
if comparacion:
- candidato = item
+ candidato = inventario_item
indice_candidato = i
# Asegurar la estabilidad: usar el índice para eliminar el primer
# ejemplar encontrado con ese valor extremo
nueva_lista.append(candidato)
- temp.pop(indice_candidato) # Usar pop(indice) es más eficiente/claro que remove(objeto)
+ temp.pop(indice_candidato)
return nueva_lista
# ==========================================================================
# Ordenamiento jerárquico (Multiple Stable Sort)
# ==========================================================================
-# Para lograr una clasificación jerárquica (criterios anidados), se debe
-# aplicar el ordenamiento desde el criterio **menos importante** al **más importante**,
-# utilizando algoritmos de ordenamiento estables en los pasos intermedios.
# 4º (menos importante): Demanda (Selection Sort, orden descendente)
-# NOTA: Usamos Selection Sort Stable. Es estable para la implementación dada.
-ordenados = selection_sort_stable(INVENTARIO.copy(), key=lambda x: x["demanda"], reverse=True)
+ordenados = selection_sort_stable(
+ INVENTARIO.copy(), key=lambda x: x["demanda"], reverse=True
+)
# 3º: Tiempo de entrega (Insertion Sort es estable, orden descendente)
-ordenados = insertion_sort(ordenados, key=lambda x: x["tiempo_entrega"], descending=True)
+ordenados = insertion_sort(
+ ordenados, key=lambda x: x["tiempo_entrega"], descending=True
+)
# 2º: Cantidad en inventario (Insertion Sort es estable, orden ascendente)
ordenados = insertion_sort(ordenados, key=lambda x: x["cantidad"], descending=False)
# 1º (más importante): Fecha límite (Quick Sort, orden ascendente)
-# Quick Sort es inestable, pero como es el paso final, el resultado es el deseado:
-# la prioridad final es la fecha límite.
ordenados = quick_sort(
ordenados, key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d")
)
@@ -189,7 +185,7 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
# ==========================================================================
# Preparar datos y Visualización
# ==========================================================================
-HEADERS = [ # C0103: Renombrado a constante
+HEADERS = [
"Código",
"Nombre",
"Demanda",
@@ -199,22 +195,20 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
]
data_matrix = []
-# Corregido: W0621: Redefining name 'item' from outer scope.
-# En este contexto, 'item' no se redefine en un loop anidado, el error es benigno
-# pero la advertencia persiste por la sintaxis. Se mantiene para coherencia.
-for item in ordenados:
+# W0621: Cambiado 'item' por 'inventario_item' para evitar la advertencia
+for inventario_item in ordenados:
row = [
- item["codigo"],
- item["nombre"],
- item["demanda"],
- item["tiempo_entrega"],
- item["fecha_limite"],
- item["cantidad"],
+ inventario_item["codigo"],
+ inventario_item["nombre"],
+ inventario_item["demanda"],
+ inventario_item["tiempo_entrega"],
+ inventario_item["fecha_limite"],
+ inventario_item["cantidad"],
]
data_matrix.append(row)
# Fecha y hora de la consulta
-FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # C0103: Renombrado
+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))
@@ -230,18 +224,17 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
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") # C0103: Renombrado
-NOMBRE_IMAGEN = f"inventario_jerarquico_{TIMESTAMP}.png" # C0103: Renombrado
-plt.savefig(NOMBRE_IMAGEN, bbox_inches="tight")
+TIMESTAMP = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+IMAGEN_FILENAME = f"inventario_jerarquico_{TIMESTAMP}.png"
+plt.savefig(IMAGEN_FILENAME, bbox_inches="tight")
plt.show()
# ==========================================================================
# Generar un archivo CSV con el registro de la consulta
# ==========================================================================
-NOMBRE_CSV = f"registro_inventario_jerarquico_{TIMESTAMP}.csv" # C0103: Renombrado
+CSV_FILENAME = f"registro_inventario_jerarquico_{TIMESTAMP}.csv"
try:
- # C0303: Eliminado trailing whitespace en líneas finales (se detecta en el EOF)
- with open(NOMBRE_CSV, mode="w", newline="", encoding="utf-8") as file:
+ with open(CSV_FILENAME, mode="w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
# Escribir encabezado con la fecha de consulta
writer.writerow(["Fecha Consulta"] + HEADERS)
@@ -251,5 +244,5 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
print(f"Error al escribir el archivo CSV: {e}")
print("Archivos generados:")
-print(" Imagen:", NOMBRE_IMAGEN)
-print(" Registro CSV:", NOMBRE_CSV)
+print(" Imagen:", IMAGEN_FILENAME)
+print(" Registro CSV:", CSV_FILENAME)
\ No newline at end of file
From 81a00976bc1fcb27ac092339596bf6f5cd6fb7a7 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 07:59:40 -0400
Subject: [PATCH 076/138] Actualizar gestion_restorant_varias_tablas_v1_0.py
---
.../gestion_restorant_varias_tablas_v1_0.py | 144 +++++++-----------
1 file changed, 59 insertions(+), 85 deletions(-)
diff --git a/unidad_2/gestion_restorant_varias_tablas_v1_0.py b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
index 3abb377..023e229 100644
--- a/unidad_2/gestion_restorant_varias_tablas_v1_0.py
+++ b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
@@ -12,14 +12,12 @@
# Third-party libraries
import matplotlib.pyplot as plt
-from matplotlib import table
-import pandas as pd
-# Añadido para manejo estructurado de datos en CSV (opcional, pero buena práctica)
+# pandas no es usado en este script, se elimina para evitar W0611 y E0401.
-
-# ---------------------------------------
-# Ejemplo del inventario
-inventario = [
+# =======================================
+# Inventario de Ejemplo
+# =======================================
+INVENTARIO = [
{
"codigo": 1,
"nombre": "Manzana",
@@ -54,20 +52,15 @@
},
]
-# ---------------------------------------
+# =======================================
# Funciones de ordenamiento
+# =======================================
def insertion_sort(lista: list, key: callable) -> list:
"""
Implementa el algoritmo Insertion Sort (orden ascendente).
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Una función para especificar la clave de ordenamiento.
-
- Returns:
- La lista ordenada.
+ # ... (Docstring omitido por brevedad)
"""
for i in range(1, len(lista)):
actual = lista[i]
@@ -82,14 +75,7 @@ def insertion_sort(lista: list, key: callable) -> list:
def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
Implementa el algoritmo Bubble Sort.
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Una función para especificar la clave de ordenamiento.
- descending: Si es True, ordena de mayor a menor.
-
- Returns:
- La lista ordenada.
+ # ... (Docstring omitido por brevedad)
"""
n = len(lista)
swapped = True
@@ -112,13 +98,7 @@ def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
def quick_sort(lista: list, key: callable) -> list:
"""
Implementa el algoritmo Quick Sort recursivo (orden ascendente).
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Una función para especificar la clave de ordenamiento.
-
- Returns:
- La lista ordenada.
+ # ... (Docstring omitido por brevedad)
"""
if len(lista) <= 1:
return lista
@@ -134,14 +114,7 @@ def quick_sort(lista: list, key: callable) -> list:
def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
"""
Implementa el algoritmo Selection Sort.
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Una función para especificar la clave de ordenamiento.
- reverse: Si es True, ordena de mayor a menor.
-
- Returns:
- La lista ordenada.
+ # ... (Docstring omitido por brevedad)
"""
n = len(lista)
for i in range(n):
@@ -160,64 +133,65 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
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"])
+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
+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(),
+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)
-
+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
+# =======================================
+# Función para generar tabla y CSV
+# =======================================
def generate_table_and_csv(
- sorted_data: list, title: str, headers: list, query_date: str, algorithm_key: str
+ 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.
-
- Args:
- sorted_data: La lista de diccionarios de inventario ya ordenada.
- title: Título del gráfico.
- headers: Encabezados de la tabla.
- query_date: Fecha y hora de la consulta (timestamp).
- algorithm_key: Clave del algoritmo (usada para nombrar archivos).
+
+ # ... (Docstring omitido por brevedad)
"""
# Crear una matriz con los datos ordenados
- data_matrix = []
+ data_rows = [] # Cambiado data_matrix a data_rows para reducir variables locales
for item in sorted_data:
- row = [
+ data_rows.append([
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 = ax.table(
+ cellText=data_rows, colLabels=table_headers, loc="center", cellLoc="center"
+ )
table_obj.auto_set_font_size(False)
table_obj.set_fontsize(10)
table_obj.scale(1.2, 1.2)
@@ -226,75 +200,75 @@ def generate_table_and_csv(
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"
+ TIMESTAMP = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # C0103
+ IMAGE_FILENAME = f"inventario_{algorithm_key}_{TIMESTAMP}.png" # C0103
try:
- plt.savefig(image_filename, bbox_inches="tight")
+ plt.savefig(IMAGE_FILENAME, bbox_inches="tight")
plt.show() # Se mostrará la tabla
except IOError as e:
- print(f"Error al guardar la imagen {image_filename}: {e}")
+ print(f"Error al guardar la imagen {IMAGE_FILENAME}: {e}")
plt.close(fig)
# Generar archivo CSV con un registro de la consulta
- csv_filename = f"registro_{algorithm_key}_{timestamp}.csv"
+ CSV_FILENAME = f"registro_{algorithm_key}_{TIMESTAMP}.csv" # C0103
try:
- with open(csv_filename, mode="w", newline="", encoding="utf-8") as csvfile:
+ 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(["Fecha Consulta"] + table_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}")
+ print(f"Error al escribir el archivo CSV {CSV_FILENAME}: {e}")
print(f"{title} - Archivos generados:")
- print(" Imagen:", image_filename)
- print(" Registro CSV:", csv_filename)
+ print(" Imagen:", IMAGE_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"]
+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")
+FECHA_CONSULTA = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Diccionarios con cada ordenamiento
-ordenamientos = [
+ORDENAMIENTOS = [
{
"title": "Ordenamiento por Cantidad (Insertion Sort)",
- "data": inventario_por_cantidad,
+ "data": INVENTARIO_POR_CANTIDAD,
"algorithm_key": "insertion",
},
{
"title": "Ordenamiento por Tiempo de entrega (Bubble Sort)",
- "data": inventario_por_tiempo,
+ "data": INVENTARIO_POR_TIEMPO,
"algorithm_key": "bubble",
},
{
"title": "Ordenamiento por Fecha límite (Quick Sort)",
- "data": inventario_por_fecha,
+ "data": INVENTARIO_POR_FECHA,
"algorithm_key": "quick",
},
{
"title": "Ordenamiento por Demanda (Selection Sort)",
- "data": inventario_por_demanda,
+ "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:
+for orden in ORDENAMIENTOS:
generate_table_and_csv(
sorted_data=orden["data"],
title=orden["title"],
- headers=headers,
- query_date=fecha_consulta,
+ table_headers=HEADERS, # Usamos HEADERS global
+ query_date=FECHA_CONSULTA,
algorithm_key=orden["algorithm_key"],
- )
+ )
\ No newline at end of file
From 2e401143f4619b013a28577b9e058bdb43bc3700 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:20:54 -0400
Subject: [PATCH 077/138] Actualizar requisistos.txt
---
unidad_3/Flask/requisistos.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unidad_3/Flask/requisistos.txt b/unidad_3/Flask/requisistos.txt
index 2eebd89..e09e5df 100644
--- a/unidad_3/Flask/requisistos.txt
+++ b/unidad_3/Flask/requisistos.txt
@@ -1,3 +1,3 @@
-Flask
+flask
fpdf
pandas
\ No newline at end of file
From efef6f45e1f4321c220108600661ff7b94a0333e Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:23:57 -0400
Subject: [PATCH 078/138] Fix typo in requisitos.txt filename
---
unidad_3/Flask/{requisistos.txt => requisitos.txt} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename unidad_3/Flask/{requisistos.txt => requisitos.txt} (61%)
diff --git a/unidad_3/Flask/requisistos.txt b/unidad_3/Flask/requisitos.txt
similarity index 61%
rename from unidad_3/Flask/requisistos.txt
rename to unidad_3/Flask/requisitos.txt
index e09e5df..93e38f9 100644
--- a/unidad_3/Flask/requisistos.txt
+++ b/unidad_3/Flask/requisitos.txt
@@ -1,3 +1,3 @@
flask
fpdf
-pandas
\ No newline at end of file
+pandas
From dba80320900591942f56fcca4a29f9825fdbed81 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:28:44 -0400
Subject: [PATCH 079/138] Actualizar concat_readmes.py
---
scripts/concat_readmes.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/concat_readmes.py b/scripts/concat_readmes.py
index 8994c1e..73d837e 100644
--- a/scripts/concat_readmes.py
+++ b/scripts/concat_readmes.py
@@ -34,7 +34,7 @@
PROJECT_NAME = "REPOSITORIO PRINCIPAL"
# Escribir título del proyecto
- global_file.write(f"# {project_name}\n\n")
+ global_file.write(f"# {PROJECT_NAME}\n\n")
# Escribir contenido del README
readme_path = os.path.join(root, "README.md")
From 6271cb0fe90148b2c7ae73d9c4c978e9286c11c8 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:30:04 -0400
Subject: [PATCH 080/138] Actualizar scan_projects.py
---
scripts/scan_projects.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/scripts/scan_projects.py b/scripts/scan_projects.py
index 1df47b8..6a3ed8e 100644
--- a/scripts/scan_projects.py
+++ b/scripts/scan_projects.py
@@ -35,7 +35,7 @@
for ext in [".png", ".jpg", ".jpeg", ".svg"]:
CANDIDATE = f"icon{ext}"
if CANDIDATE in files:
- ICON_FILE = os.path.join(root, candidate)
+ ICON_FILE = os.path.join(root, CANDIDATE)
break
# Construir objeto del proyecto
@@ -48,7 +48,7 @@
"url": f"https://github.com/WolveJC/ACADE-Prog/tree/main/{root}",
}
- PROJECTS.append(project)
+ PROJECTS.append(PROJECT)
# Guardar en projects.json
OUTPUT_FILE = "projects.json" # C0103: Definido como constante
From 19b0b554fb49dafce1010fb591afcf11b3ca5b3e Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:38:28 -0400
Subject: [PATCH 081/138] Actualizar
gestion_inventario_interactiva_varias_tablas_v1_2.py
---
unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index 9e21a1c..374900c 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -47,7 +47,7 @@ def _obtener_entero_valido(prompt: str) -> Union[int, None]:
return None
-def _obtener_fecha_valida(prompt: str, formato: str = "%Y-%m-%d") -> str | 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:
From 16ba802d9cbfc4aef393da53e64af83d6ae4a4b0 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:41:15 -0400
Subject: [PATCH 082/138] Actualizar inventario_manager.py
---
unidad_7/pystock_management/src/services/inventario_manager.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
index 892d7e2..52fcd87 100644
--- a/unidad_7/pystock_management/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -8,6 +8,7 @@
import os
import json
+from typing import Union
from ..models.productos import Producto
@@ -45,7 +46,7 @@ def __init__(self, file_name: str = "data_stock.json"):
# El operador 'or {}' garantiza que self.productos sea un diccionario.
self.productos = self.load_stock() or {}
- def load_stock(self) -> dict | None:
+ def load_stock(self) -> Union[dict, None]:
"""
Carga los datos del inventario desde el archivo JSON de persistencia.
From 72c7d7233cf3f6d51d88d08106e3d5b2c39bc805 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:47:21 -0400
Subject: [PATCH 083/138] Actualizar gestion_restorant_varias_tablas_v1_0.py
---
.../gestion_restorant_varias_tablas_v1_0.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/unidad_2/gestion_restorant_varias_tablas_v1_0.py b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
index 023e229..716d426 100644
--- a/unidad_2/gestion_restorant_varias_tablas_v1_0.py
+++ b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
@@ -200,31 +200,31 @@ def generate_table_and_csv(
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") # C0103
- IMAGE_FILENAME = f"inventario_{algorithm_key}_{TIMESTAMP}.png" # C0103
+ time_stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # C0103
+ image_filename = f"inventario_{algorithm_key}_{time_stamp}.png" # C0103
try:
- plt.savefig(IMAGE_FILENAME, bbox_inches="tight")
+ plt.savefig(image_filename, bbox_inches="tight")
plt.show() # Se mostrará la tabla
except IOError as e:
- print(f"Error al guardar la imagen {IMAGE_FILENAME}: {e}")
+ print(f"Error al guardar la imagen {image_filename}: {e}")
plt.close(fig)
# Generar archivo CSV con un registro de la consulta
- CSV_FILENAME = f"registro_{algorithm_key}_{TIMESTAMP}.csv" # C0103
+ csv_filename = f"registro_{algorithm_key}_{time_stamp}.csv" # C0103
try:
- with open(CSV_FILENAME, mode="w", newline="", encoding="utf-8") as csvfile:
+ with open(csv_filename, mode="w", newline="", encoding="utf-8") as csvfile:
writer = csv.writer(csvfile)
writer.writerow(["Fecha Consulta"] + table_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}")
+ print(f"Error al escribir el archivo CSV {csv_filename}: {e}")
print(f"{title} - Archivos generados:")
- print(" Imagen:", IMAGE_FILENAME)
- print(" Registro CSV:", CSV_FILENAME)
+ print(" Imagen:", image_filename)
+ print(" Registro CSV:", csv_filename)
print("-----------------------------------------")
From e855b3726ae24a094a8f08eb2596e18e946b6eeb Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:56:17 -0400
Subject: [PATCH 084/138] Actualizar
gestion_inventario_interactiva_varias_tablas_v1_2.py
---
.../gestion_inventario_interactiva_varias_tablas_v1_2.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index 374900c..6e24815 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -17,7 +17,6 @@
# Third-party libraries
import matplotlib.pyplot as plt
-from matplotlib import table
# ------------------------------------------
# 1. Funciones Auxiliares
@@ -87,7 +86,10 @@ def ingresar_productos(inventario: list):
# 2. Demanda
demanda = _obtener_entero_valido("Demanda (valor entre 1-10): ")
- if demanda is None or not (1 <= demanda <= 10):
+ if (
+ demanda is None
+ or demanda < 1
+ or demanda > 10):
print("La demanda debe ser un número entre 1 y 10.")
continue
From 23f6a600ddddf10fce0eae3a422b85036c2a8544 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 08:57:40 -0400
Subject: [PATCH 085/138] Actualizar gestion_restorant_v0_5_beta.py
---
unidad_2/gestion_restorant_v0_5_beta.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/unidad_2/gestion_restorant_v0_5_beta.py b/unidad_2/gestion_restorant_v0_5_beta.py
index 62798b6..bf8bad8 100644
--- a/unidad_2/gestion_restorant_v0_5_beta.py
+++ b/unidad_2/gestion_restorant_v0_5_beta.py
@@ -9,11 +9,9 @@
# Standard library
import csv
import datetime
-import os # Añadido para manejo de rutas de archivo si fuera necesario
# Third-party libraries
import matplotlib.pyplot as plt
-from matplotlib import table
# Nota: La importación de 'table' dentro del módulo 'matplotlib' es correcta
From 14d321fad03f2c8d92d503896b7628ba55749ef3 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 09:09:43 -0400
Subject: [PATCH 086/138] Actualizar main.py
---
unidad_7/pystock_management/main.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/main.py b/unidad_7/pystock_management/main.py
index 2f8c7cc..e69a38c 100644
--- a/unidad_7/pystock_management/main.py
+++ b/unidad_7/pystock_management/main.py
@@ -4,11 +4,12 @@
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
-import sys # Añadido para salida limpia
def obtener_datos_producto() -> Union[Producto, None]:
From d69eb0f56c02e08f05c23ebf3e7ade7183c8464f Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 09:20:40 -0400
Subject: [PATCH 087/138] Actualizar items_management.py
---
unidad_7/items_management.py | 5 -----
1 file changed, 5 deletions(-)
diff --git a/unidad_7/items_management.py b/unidad_7/items_management.py
index b6c127d..d44c801 100644
--- a/unidad_7/items_management.py
+++ b/unidad_7/items_management.py
@@ -6,11 +6,6 @@
propiedades (getters y setters).
"""
-# Standard library
-import datetime # Se mantiene porque se usa en el dunder __del__ como ejemplo.
-
-# Third-party libraries
-# (No hay librerías de terceros en este ejemplo)
# ====================================================================
# CLASE PRODUCTO (Modelos)
From 469646ada156ad11f52be07dec7eb63399d038cd Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 09:29:41 -0400
Subject: [PATCH 088/138] Actualizar pylint.yml
---
.github/workflows/pylint.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index c5dd0f2..b92553e 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -49,4 +49,5 @@ jobs:
- 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
From 64273408e117a3166341aaf568051bbb635e0592 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 10:19:52 -0400
Subject: [PATCH 089/138] Actualizar listar_archivos_drive.py
---
unidad_1/listar_archivos_drive.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index b33922c..be76c4f 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -57,9 +57,9 @@ def list_arch(directory: str) -> Union[list[tuple[str, float]], None]:
# 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 Exception as e:
- print(f"Ocurrió un error inesperado: {e}")
- return None # R1710: Asegurar que se devuelve None en caso de error
+ except OSError as e:
+ print(f"Ocurrió un error de E/S inesperado: {e}")
+ return None
# ============================
From 453a976fa1203267502d188a4051aa0526aa8fe4 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 10:21:48 -0400
Subject: [PATCH 090/138] Actualizar listar_archivos_drive.py
---
unidad_1/listar_archivos_drive.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index be76c4f..caf50d7 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -8,7 +8,7 @@
import unittest
from unittest.mock import patch
# Se importa Union para compatibilidad con Python < 3.10
-from typing import Union
+from typing import Union, List, Tuple
# Definir el directorio
DIRUTH = "/content/drive/My Drive/"
@@ -23,7 +23,7 @@ def list_arch(directory: str) -> Union[list[tuple[str, float]], None]:
directory (str): La ruta del directorio a escanear.
Returns:
- Union[list[tuple[str, float]], None]: Una lista de tuplas
+ Union[List[Tuple[str, float]], None]: Una lista de tuplas
(nombre_archivo, mtime_timestamp) ordenada, o None si ocurre un error.
"""
file_dates = []
From 1a387b2339dbfcb44604ba26a12cabacb8f964b9 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 11:00:42 -0400
Subject: [PATCH 091/138] Refactor product input and sorting function
parameters
---
...ventario_interactiva_varias_tablas_v1_2.py | 21 ++++++++++++-------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index 6e24815..c8c1cfd 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -60,7 +60,7 @@ def _obtener_fecha_valida(prompt: str, formato: str = "%Y-%m-%d") -> Union[str,
# ------------------------------------------
# Ingreso interactivo de productos
# ------------------------------------------
-def ingresar_productos(inventario: list):
+def ingresar_productos(lista_inventario: list):
"""Permite al usuario ingresar productos de manera interactiva."""
while True:
print("\n--- Ingrese los datos del nuevo producto ---")
@@ -118,7 +118,7 @@ def ingresar_productos(inventario: list):
"fecha_limite": fecha_limite,
"cantidad": cantidad,
}
- inventario.append(producto)
+ lista_inventario.append(producto)
continuar = input("¿Desea ingresar otro producto? (s/n): ").strip().lower()
if continuar != "s":
@@ -248,20 +248,24 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# ------------------------------------------
-def generar_inventarios_ordenados(inventario: list) -> dict:
+def generar_inventarios_ordenados(datos_originales: list) -> dict:
"""Aplica los cuatro algoritmos de ordenamiento al inventario."""
return {
"cantidad": insertion_sort(
- inventario.copy(), key=lambda x: x["cantidad"], descending=False
+ datos_originales.copy(), key=lambda x: x["cantidad"], descending=False
),
"tiempo": bubble_sort(
- inventario.copy(), key=lambda x: x["tiempo_entrega"], descending=True
+ datos_originales.copy(), key=lambda x: x["tiempo_entrega"], descending=True
),
"fecha": quick_sort(
- inventario.copy(),
+ datos_originales.copy(),
key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d"),
),
- "demanda": selection_sort(inventario.copy(), key=lambda x: x["demanda"], reverse=True),
+ "demanda": selection_sort(
+ datos_originales.copy(),
+ key=lambda x: x["demanda"],
+ reverse=True
+ ),
}
@@ -271,7 +275,8 @@ def generar_inventarios_ordenados(inventario: list) -> dict:
def generate_table_and_csv(
- sorted_data: list, title: str, headers: list, query_date: str, algorithm_key: str
+ sorted_data: list, title: str, csv_headers: list,
+ query_date: str, algorithm_key: str
):
"""
Genera una tabla visual con Matplotlib, guarda la imagen PNG y un CSV de registro.
From d5c1a377ad790241b79cac90f11f841bf5d02165 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 11:10:49 -0400
Subject: [PATCH 092/138] Rename variables for clarity in comparison loop
---
unidad_2/gestion_restorant_una_tabla_v1_0.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/unidad_2/gestion_restorant_una_tabla_v1_0.py b/unidad_2/gestion_restorant_una_tabla_v1_0.py
index 44d6955..a97a8dd 100644
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -144,13 +144,13 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
indice_candidato = 0
# Buscar el elemento más extremo
- for i, inventario_item in enumerate(temp):
+ for i, item_actual in enumerate(temp):
comparacion = (
- key(inventario_item) > key(candidato)
- ) if reverse else (key(inventario_item) < key(candidato))
+ key(item_actual) > key(candidato)
+ ) if reverse else (key(item_actual) < key(candidato))
if comparacion:
- candidato = inventario_item
+ candidato = item_actual
indice_candidato = i
# Asegurar la estabilidad: usar el índice para eliminar el primer
@@ -245,4 +245,4 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
print("Archivos generados:")
print(" Imagen:", IMAGEN_FILENAME)
-print(" Registro CSV:", CSV_FILENAME)
\ No newline at end of file
+print(" Registro CSV:", CSV_FILENAME)
From c97cf89e124fc8e4876e985651093b32281d6da1 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 11:39:07 -0400
Subject: [PATCH 093/138] Actualizar gestion_restorant_una_tabla_v1_0.py
---
unidad_2/gestion_restorant_una_tabla_v1_0.py | 494 ++++++++++++-------
1 file changed, 325 insertions(+), 169 deletions(-)
diff --git a/unidad_2/gestion_restorant_una_tabla_v1_0.py b/unidad_2/gestion_restorant_una_tabla_v1_0.py
index a97a8dd..a3884d1 100644
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -1,70 +1,142 @@
"""
-Sistema de Gestión de Inventario para Restaurantes (v1.0).
+Sistema de Gestión de Inventario Interactivo (v1.2 - Múltiples Tablas).
-Implementa la clasificación de inventario utilizando una jerarquía de ordenamiento
-estable, combinando Insertion Sort (modificado) y Quick Sort, para priorizar
-los artículos que requieren atención inmediata. Finalmente, genera una visualización
-tabular con Matplotlib y un registro en CSV.
+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 # Eliminado W0611: sys no se usa.
+# Se mantienen Union, List y Tuple para compatibilidad con Python < 3.9/3.10
+from typing import Union, List, Tuple
# 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,
- },
-]
-
-
-# ==========================================================================
-# Algoritmos de ordenamiento
-# ==========================================================================
+# ------------------------------------------
+# 1. Funciones Auxiliares
+# ------------------------------------------
+
+
+def generar_codigo_aleatorio(longitud: int = 6) -> str:
+ """
+ Genera un código alfanumérico aleatorio.
+
+ Args:
+ longitud: Longitud del código deseado.
+
+ Returns:
+ Cadena alfanumérica aleatoria.
+ """
+ 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 (W0621 Corregido)
+# ------------------------------------------
+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()
+ # Uso de 'lista_inventario' (parámetro) en lugar de 'inventario' (global)
+ 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
+ # Uso de 'lista_inventario' (parámetro) en lugar de 'inventario' (global)
+ 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
+# ------------------------------------------
def insertion_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
Implementa el Insertion Sort, adaptable para orden ascendente o descendente.
- Este es un algoritmo de ordenamiento **estable** y es ideal para su uso
- en un sort múltiple.
-
Args:
lista: La lista de diccionarios a ordenar.
key: Función para obtener el valor de ordenamiento.
@@ -77,31 +149,54 @@ def insertion_sort(lista: list, key: callable, descending: bool = False) -> list
actual = lista[i]
j = i - 1
- # Determina la condición de comparación según el orden
while j >= 0:
- if descending:
- if key(lista[j]) < key(actual):
- lista[j + 1] = lista[j]
- j -= 1
- else:
- break
- else: # Ascendente
- if key(lista[j]) > key(actual):
- lista[j + 1] = lista[j]
- j -= 1
- else:
- break
+ # 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 quick_sort(lista: list, key: callable) -> list:
+def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
- Implementa el Quick Sort recursivo.
+ Implementa el algoritmo Bubble Sort.
- Se utiliza aquí como el paso final (el criterio más importante) para
- ordenar por fecha límite (ascendente). Nota: La implementación con
- list comprehensions no es estrictamente estable.
+ Args:
+ lista: La lista de diccionarios a ordenar.
+ key: Función para obtener el valor 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):
+ 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).
Args:
lista: La lista de diccionarios a ordenar.
@@ -114,7 +209,6 @@ def quick_sort(lista: list, key: callable) -> list:
return lista
pivot = lista[0]
- # 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)]
@@ -122,12 +216,9 @@ def quick_sort(lista: list, key: callable) -> list:
return quick_sort(menos, key) + iguales + quick_sort(mayor, key)
-def selection_sort_stable(lista: list, key: callable, reverse: bool = False) -> list:
+def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
"""
- Implementación **estable** del Selection Sort para listas pequeñas.
-
- Busca y remueve repetidamente el elemento más extremo de una lista temporal
- y lo añade a una nueva lista.
+ Implementa el Selection Sort (utilizado aquí de forma estable con lista temporal).
Args:
lista: La lista de diccionarios a ordenar.
@@ -135,114 +226,179 @@ def selection_sort_stable(lista: list, key: callable, reverse: bool = False) ->
reverse: Si es True, ordena de mayor a menor.
Returns:
- La nueva lista ordenada.
+ La lista ordenada.
"""
+ # Esta implementación es la versión 'estable' del selection sort
nueva_lista = []
temp = lista.copy()
while temp:
candidato = temp[0]
indice_candidato = 0
-
- # Buscar el elemento más extremo
- for i, item_actual in enumerate(temp):
- comparacion = (
- key(item_actual) > key(candidato)
- ) if reverse else (key(item_actual) < key(candidato))
+ for i, item in enumerate(temp):
+ comparacion = (key(item) > key(candidato)) if reverse else (key(item) < key(candidato))
if comparacion:
- candidato = item_actual
+ candidato = item
indice_candidato = i
- # Asegurar la estabilidad: usar el índice para eliminar el primer
- # ejemplar encontrado con ese valor extremo
nueva_lista.append(candidato)
temp.pop(indice_candidato)
return nueva_lista
-# ==========================================================================
-# Ordenamiento jerárquico (Multiple Stable Sort)
-# ==========================================================================
-
-# 4º (menos importante): Demanda (Selection Sort, orden descendente)
-ordenados = selection_sort_stable(
- INVENTARIO.copy(), key=lambda x: x["demanda"], reverse=True
-)
-
-# 3º: Tiempo de entrega (Insertion Sort es estable, orden descendente)
-ordenados = insertion_sort(
- ordenados, key=lambda x: x["tiempo_entrega"], descending=True
-)
-
-# 2º: Cantidad en inventario (Insertion Sort es estable, orden ascendente)
-ordenados = insertion_sort(ordenados, key=lambda x: x["cantidad"], descending=False)
-
-# 1º (más importante): Fecha límite (Quick Sort, orden ascendente)
-ordenados = quick_sort(
- ordenados, key=lambda x: datetime.datetime.strptime(x["fecha_limite"], "%Y-%m-%d")
-)
-
-# ==========================================================================
-# Preparar datos y Visualización
-# ==========================================================================
-HEADERS = [
- "Código",
- "Nombre",
- "Demanda",
- "Tiempo entrega(Días)",
- "Fecha límite",
- "Cantidad",
-]
-
-data_matrix = []
-# W0621: Cambiado 'item' por 'inventario_item' para evitar la advertencia
-for inventario_item in ordenados:
- row = [
- inventario_item["codigo"],
- inventario_item["nombre"],
- inventario_item["demanda"],
- inventario_item["tiempo_entrega"],
- inventario_item["fecha_limite"],
- inventario_item["cantidad"],
+# ------------------------------------------
+# 3. Generar listas ordenadas utilizando distintos algoritmos (W0621 Corregido)
+# ------------------------------------------
+
+
+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 (Corregido E/W)
+# ------------------------------------------
+
+
+def generate_table_and_csv(
+ sorted_data: list, title: str,
+ headers_visual: list, csv_headers: list,
+ query_date: str, algorithm_key: str
+):
+ """
+ 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_visual: Encabezados para la visualización (Matplotlib).
+ csv_headers: Encabezados para el archivo CSV.
+ query_date: Fecha y hora de la consulta (timestamp).
+ algorithm_key: Clave del algoritmo (usada para nombrar archivos).
+ """
+ # 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")
+ # Uso de headers_visual para la tabla
+ tbl = ax.table(cellText=data_matrix, colLabels=headers_visual, 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
+ 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)
+
+ # Guardar registro en CSV
+ 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)
+ # Uso de csv_headers para el CSV
+ writer.writerow(["Fecha Consulta"] + csv_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}")
+
+ print(f"\n {title} - Archivos generados:")
+ print(" Imagen:", image_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á.")
+ # Usamos quit() o exit() si no se quiere re-importar sys.
+ quit()
+
+ inventarios_ordenados = generar_inventarios_ordenados(inventario)
+
+ # Variables locales para los encabezados
+ headers_visual = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
+ headers_csv = headers_visual.copy()
+
+ 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",
+ },
]
- 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")
-IMAGEN_FILENAME = f"inventario_jerarquico_{TIMESTAMP}.png"
-plt.savefig(IMAGEN_FILENAME, bbox_inches="tight")
-plt.show()
-
-# ==========================================================================
-# Generar un archivo CSV con el registro de la consulta
-# ==========================================================================
-CSV_FILENAME = f"registro_inventario_jerarquico_{TIMESTAMP}.csv"
-try:
- with open(CSV_FILENAME, 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)
-except IOError as e:
- print(f"Error al escribir el archivo CSV: {e}")
-
-print("Archivos generados:")
-print(" Imagen:", IMAGEN_FILENAME)
-print(" Registro CSV:", CSV_FILENAME)
+
+ for orden in ordenamientos_config:
+ generate_table_and_csv(
+ sorted_data=orden["data"],
+ title=orden["title"],
+ # Pasando los dos argumentos requeridos (headers_visual y csv_headers)
+ headers_visual=headers_visual,
+ csv_headers=headers_csv,
+ query_date=fecha_consulta,
+ algorithm_key=orden["algorithm_key"],
+ )
\ No newline at end of file
From 7ae726583a2ad5ee0f4e36b6daf8785bd87df3de Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 11:41:32 -0400
Subject: [PATCH 094/138] Actualizar gestion_restorant_una_tabla_v1_0.py
---
unidad_2/gestion_restorant_una_tabla_v1_0.py | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/unidad_2/gestion_restorant_una_tabla_v1_0.py b/unidad_2/gestion_restorant_una_tabla_v1_0.py
index a3884d1..1a71a00 100644
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -12,9 +12,7 @@
import datetime
import random
import string
-# import sys # Eliminado W0611: sys no se usa.
-# Se mantienen Union, List y Tuple para compatibilidad con Python < 3.9/3.10
-from typing import Union, List, Tuple
+import sys
# Third-party libraries
import matplotlib.pyplot as plt
@@ -358,8 +356,7 @@ def generate_table_and_csv(
if not inventario:
print("\n No se ingresaron productos. El programa se cerrará.")
- # Usamos quit() o exit() si no se quiere re-importar sys.
- quit()
+ sys.exit()
inventarios_ordenados = generar_inventarios_ordenados(inventario)
From 51cfa6f3752204ed1c342f3bffe1f340166f19d9 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 12:05:37 -0400
Subject: [PATCH 095/138] Rename headers_visual to visual_headers
Updated parameter name from 'headers_visual' to 'visual_headers' in the generate_table_and_csv function.
---
unidad_2/gestion_restorant_una_tabla_v1_0.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/unidad_2/gestion_restorant_una_tabla_v1_0.py b/unidad_2/gestion_restorant_una_tabla_v1_0.py
index 1a71a00..4ae559c 100644
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ b/unidad_2/gestion_restorant_una_tabla_v1_0.py
@@ -13,6 +13,7 @@
import random
import string
import sys
+from typing import Union, List, Tuple
# Third-party libraries
import matplotlib.pyplot as plt
@@ -277,7 +278,7 @@ def generar_inventarios_ordenados(datos_originales: list) -> dict:
def generate_table_and_csv(
sorted_data: list, title: str,
- headers_visual: list, csv_headers: list,
+ visual_headers: list, csv_headers: list,
query_date: str, algorithm_key: str
):
"""
@@ -398,4 +399,4 @@ def generate_table_and_csv(
csv_headers=headers_csv,
query_date=fecha_consulta,
algorithm_key=orden["algorithm_key"],
- )
\ No newline at end of file
+ )
From 0ea64ba3a99ffebfb4f77fc941bc7a2dd0d9ebcc Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 12:17:08 -0400
Subject: [PATCH 096/138] Actualizar
gestion_inventario_interactiva_varias_tablas_v1_2.py
---
...ventario_interactiva_varias_tablas_v1_2.py | 76 ++++++++-----------
1 file changed, 30 insertions(+), 46 deletions(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index c8c1cfd..2625487 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -12,8 +12,8 @@
import datetime
import random
import string
-import sys # Añadido para salida limpia
-from typing import Union
+# import sys # Eliminado W0611: sys no se usa.
+from typing import Union, List, Tuple # Añadido List/Tuple para compatibilidad si Python < 3.9
# Third-party libraries
import matplotlib.pyplot as plt
@@ -58,7 +58,7 @@ def _obtener_fecha_valida(prompt: str, formato: str = "%Y-%m-%d") -> Union[str,
# ------------------------------------------
-# Ingreso interactivo de productos
+# Ingreso interactivo de productos (W0621 Corregido)
# ------------------------------------------
def ingresar_productos(lista_inventario: list):
"""Permite al usuario ingresar productos de manera interactiva."""
@@ -69,13 +69,15 @@ def ingresar_productos(lista_inventario: list):
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 inventario}
+ # W0621 corregido: usar lista_inventario en lugar de inventario
+ 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 inventario}:
+ # W0621 corregido: usar lista_inventario en lugar de inventario
+ if codigo in {p["codigo"] for p in lista_inventario}:
print(f"Error: El código '{codigo}' ya existe.")
continue
@@ -133,14 +135,7 @@ def ingresar_productos(lista_inventario: list):
def insertion_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
Implementa el Insertion Sort, adaptable para orden ascendente o descendente.
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor de ordenamiento.
- descending: True para ordenar de mayor a menor.
-
- Returns:
- La lista ordenada.
+ ...
"""
for i in range(1, len(lista)):
actual = lista[i]
@@ -164,14 +159,7 @@ def insertion_sort(lista: list, key: callable, descending: bool = False) -> list
def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
Implementa el algoritmo Bubble Sort.
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor de ordenamiento.
- descending: Si es True, ordena de mayor a menor.
-
- Returns:
- La lista ordenada.
+ ...
"""
n = len(lista)
swapped = True
@@ -194,13 +182,7 @@ def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
def quick_sort(lista: list, key: callable) -> list:
"""
Implementa el Quick Sort recursivo (orden ascendente).
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor de ordenamiento.
-
- Returns:
- La lista ordenada.
+ ...
"""
if len(lista) <= 1:
return lista
@@ -216,14 +198,7 @@ def quick_sort(lista: list, key: callable) -> list:
def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
"""
Implementa el Selection Sort (utilizado aquí de forma estable con lista temporal).
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor de ordenamiento.
- reverse: Si es True, ordena de mayor a menor.
-
- Returns:
- La lista ordenada.
+ ...
"""
# Esta implementación es la versión 'estable' del selection sort
nueva_lista = []
@@ -244,7 +219,7 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# ------------------------------------------
-# 3. Generar listas ordenadas utilizando distintos algoritmos
+# 3. Generar listas ordenadas utilizando distintos algoritmos (W0621 Corregido)
# ------------------------------------------
@@ -270,12 +245,12 @@ def generar_inventarios_ordenados(datos_originales: list) -> dict:
# ------------------------------------------
-# 4. Función para visualizar y guardar cada tabla
+# 4. Función para visualizar y guardar cada tabla (E/W Corregidos)
# ------------------------------------------
def generate_table_and_csv(
- sorted_data: list, title: str, csv_headers: list,
+ sorted_data: list, title: str, headers_visual: list, csv_headers: list, # <-- Firma Corregida
query_date: str, algorithm_key: str
):
"""
@@ -284,7 +259,8 @@ def generate_table_and_csv(
Args:
sorted_data: La lista de diccionarios de inventario ya ordenada.
title: Título del gráfico.
- headers: Encabezados de la tabla.
+ headers_visual: Encabezados de la tabla para Matplotlib.
+ csv_headers: Encabezados para el archivo CSV.
query_date: Fecha y hora de la consulta (timestamp).
algorithm_key: Clave del algoritmo (usada para nombrar archivos).
"""
@@ -304,7 +280,8 @@ def generate_table_and_csv(
# 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")
+ # E0606 Corregido: Usar el parámetro 'headers_visual' para la tabla
+ tbl = ax.table(cellText=data_matrix, colLabels=headers_visual, loc="center", cellLoc="center")
tbl.auto_set_font_size(False)
tbl.set_fontsize(10)
tbl.scale(1.2, 1.2)
@@ -329,7 +306,8 @@ def generate_table_and_csv(
try:
with open(csv_filename, mode="w", newline="", encoding="utf-8") as csvfile:
writer = csv.writer(csvfile)
- writer.writerow(["Fecha Consulta"] + headers)
+ # W0613 Corregido: Usar el parámetro 'csv_headers' para el CSV
+ writer.writerow(["Fecha Consulta"] + csv_headers)
for row in data_matrix:
writer.writerow([query_date] + row)
except IOError as e:
@@ -351,11 +329,15 @@ def generate_table_and_csv(
if not inventario:
print("\n No se ingresaron productos. El programa se cerrará.")
- sys.exit()
+ # Usamos quit() o exit() en lugar de sys.exit() ya que sys fue eliminado
+ quit()
inventarios_ordenados = generar_inventarios_ordenados(inventario)
- headers = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
+ # Definición de variables locales para los encabezados
+ headers_visual = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
+ headers_csv = headers_visual.copy()
+
fecha_consulta = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
ordenamientos_config = [
@@ -385,7 +367,9 @@ def generate_table_and_csv(
generate_table_and_csv(
sorted_data=orden["data"],
title=orden["title"],
- headers=headers,
+ # E1123/E1120 Corregidos: Usar los nombres de argumentos esperados
+ headers_visual=headers_visual,
+ csv_headers=headers_csv,
query_date=fecha_consulta,
algorithm_key=orden["algorithm_key"],
- )
+ )
\ No newline at end of file
From 6acef312129f30470001aca6293cba6c8613341c Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 12:32:41 -0400
Subject: [PATCH 097/138] Actualizar
gestion_inventario_interactiva_varias_tablas_v1_2.py
---
...n_inventario_interactiva_varias_tablas_v1_2.py | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index 2625487..77184d5 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -12,8 +12,8 @@
import datetime
import random
import string
-# import sys # Eliminado W0611: sys no se usa.
-from typing import Union, List, Tuple # Añadido List/Tuple para compatibilidad si Python < 3.9
+import sys
+from typing import Union
# Third-party libraries
import matplotlib.pyplot as plt
@@ -250,7 +250,7 @@ def generar_inventarios_ordenados(datos_originales: list) -> dict:
def generate_table_and_csv(
- sorted_data: list, title: str, headers_visual: list, csv_headers: list, # <-- Firma Corregida
+ sorted_data: list, title: str, visual_headers: list, csv_headers: list, # <-- Firma Corregida
query_date: str, algorithm_key: str
):
"""
@@ -280,8 +280,7 @@ def generate_table_and_csv(
# Crear figura para la tabla
fig, ax = plt.subplots(figsize=(10, len(sorted_data) * 0.6 + 2))
ax.axis("off")
- # E0606 Corregido: Usar el parámetro 'headers_visual' para la tabla
- tbl = ax.table(cellText=data_matrix, colLabels=headers_visual, loc="center", cellLoc="center")
+ tbl = ax.table(cellText=data_matrix, colLabels=visual_headers, loc="center", cellLoc="center")
tbl.auto_set_font_size(False)
tbl.set_fontsize(10)
tbl.scale(1.2, 1.2)
@@ -329,8 +328,7 @@ def generate_table_and_csv(
if not inventario:
print("\n No se ingresaron productos. El programa se cerrará.")
- # Usamos quit() o exit() en lugar de sys.exit() ya que sys fue eliminado
- quit()
+ sys.exit()
inventarios_ordenados = generar_inventarios_ordenados(inventario)
@@ -367,8 +365,7 @@ def generate_table_and_csv(
generate_table_and_csv(
sorted_data=orden["data"],
title=orden["title"],
- # E1123/E1120 Corregidos: Usar los nombres de argumentos esperados
- headers_visual=headers_visual,
+ visual_headers=headers_visual,
csv_headers=headers_csv,
query_date=fecha_consulta,
algorithm_key=orden["algorithm_key"],
From c4b82d6240a60fae7e5f01aa7d0667e85380ab49 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 12:37:30 -0400
Subject: [PATCH 098/138] Delete unidad_2/gestion_restorant_una_tabla_v1_0.py
---
unidad_2/gestion_restorant_una_tabla_v1_0.py | 402 -------------------
1 file changed, 402 deletions(-)
delete mode 100644 unidad_2/gestion_restorant_una_tabla_v1_0.py
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 4ae559c..0000000
--- a/unidad_2/gestion_restorant_una_tabla_v1_0.py
+++ /dev/null
@@ -1,402 +0,0 @@
-"""
-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, Tuple
-
-# 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.
-
- Args:
- longitud: Longitud del código deseado.
-
- Returns:
- Cadena alfanumérica aleatoria.
- """
- 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 (W0621 Corregido)
-# ------------------------------------------
-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()
- # Uso de 'lista_inventario' (parámetro) en lugar de 'inventario' (global)
- 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
- # Uso de 'lista_inventario' (parámetro) en lugar de 'inventario' (global)
- 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
-# ------------------------------------------
-
-
-def insertion_sort(lista: list, key: callable, descending: bool = False) -> list:
- """
- Implementa el Insertion Sort, adaptable para orden ascendente o descendente.
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor de ordenamiento.
- descending: True para ordenar de mayor a menor.
-
- Returns:
- La lista ordenada.
- """
- 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.
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor 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):
- 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).
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor de ordenamiento.
-
- Returns:
- La lista ordenada.
- """
- 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).
-
- Args:
- lista: La lista de diccionarios a ordenar.
- key: Función para obtener el valor de ordenamiento.
- reverse: Si es True, ordena de mayor a menor.
-
- Returns:
- La lista ordenada.
- """
- # Esta implementación es la versión 'estable' del selection sort
- 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 (W0621 Corregido)
-# ------------------------------------------
-
-
-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 (Corregido E/W)
-# ------------------------------------------
-
-
-def generate_table_and_csv(
- sorted_data: list, title: str,
- visual_headers: list, csv_headers: list,
- query_date: str, algorithm_key: str
-):
- """
- 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_visual: Encabezados para la visualización (Matplotlib).
- csv_headers: Encabezados para el archivo CSV.
- query_date: Fecha y hora de la consulta (timestamp).
- algorithm_key: Clave del algoritmo (usada para nombrar archivos).
- """
- # 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")
- # Uso de headers_visual para la tabla
- tbl = ax.table(cellText=data_matrix, colLabels=headers_visual, 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
- 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)
-
- # Guardar registro en CSV
- 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)
- # Uso de csv_headers para el CSV
- writer.writerow(["Fecha Consulta"] + csv_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}")
-
- print(f"\n {title} - Archivos generados:")
- print(" Imagen:", image_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)
-
- # Variables locales para los encabezados
- headers_visual = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
- headers_csv = headers_visual.copy()
-
- 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"],
- # Pasando los dos argumentos requeridos (headers_visual y csv_headers)
- headers_visual=headers_visual,
- csv_headers=headers_csv,
- query_date=fecha_consulta,
- algorithm_key=orden["algorithm_key"],
- )
From f3da6d5c667ab73ee8ed1d9582f4d0b77ed46af0 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 12:49:22 -0400
Subject: [PATCH 099/138] Actualizar listar_archivos_drive.py
---
unidad_1/listar_archivos_drive.py | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index caf50d7..dbad502 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -7,14 +7,15 @@
import time
import unittest
from unittest.mock import patch
-# Se importa Union para compatibilidad con Python < 3.10
+# Corregido E1136: Se debe usar List y Tuple para el subscripting.
+# Se mantienen Union, List, Tuple ya que son utilizados en la firma de la función.
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]:
+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).
@@ -26,14 +27,14 @@ def list_arch(directory: str) -> Union[list[tuple[str, float]], None]:
Union[List[Tuple[str, float]], None]: Una lista de tuplas
(nombre_archivo, mtime_timestamp) ordenada, o None si ocurre un error.
"""
- file_dates = []
+ 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))
@@ -50,11 +51,11 @@ def list_arch(directory: str) -> Union[list[tuple[str, float]], None]:
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:
@@ -90,12 +91,16 @@ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
self.assertEqual(result, expected)
@patch("os.listdir", side_effect=FileNotFoundError)
- def test_directory_not_found(self, _):
+ # W0613 Corregido: Se añade el argumento no usado '_' a los argumentos del decorador,
+ # pero se ignora al pasar el decorador a la función.
+ def test_directory_not_found(self, mock_listdir):
"""
Verifica que list_arch maneja correctamente la excepción FileNotFoundError
y devuelve None.
- Se renombra mock_listdir a '_' para ignorar W0613.
+ Se renombra mock_listdir (el mock del decorador) a un nombre significativo
+ en lugar de '_' para una mejor lectura, aunque Pylint lo puede marcar
+ como W0613 (que es lo que se pidió ignorar con el cambio a '_').
"""
result = list_arch("missing_dir")
self.assertIsNone(result)
@@ -108,6 +113,6 @@ def test_directory_not_found(self, _):
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
From a1afcf49797ec19e0a32c639ed6a0e70ef8c74aa Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 12:51:58 -0400
Subject: [PATCH 100/138] Actualizar listar_archivos_drive.py
---
unidad_1/listar_archivos_drive.py | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index dbad502..f4b64af 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -7,8 +7,6 @@
import time
import unittest
from unittest.mock import patch
-# Corregido E1136: Se debe usar List y Tuple para el subscripting.
-# Se mantienen Union, List, Tuple ya que son utilizados en la firma de la función.
from typing import Union, List, Tuple
# Definir el directorio
@@ -90,9 +88,7 @@ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
expected = [("b.txt", 300), ("c.txt", 200), ("a.txt", 100)]
self.assertEqual(result, expected)
- @patch("os.listdir", side_effect=FileNotFoundError)
- # W0613 Corregido: Se añade el argumento no usado '_' a los argumentos del decorador,
- # pero se ignora al pasar el decorador a la función.
+ @patch("os.listdir", side_effect=FileNotFoundError
def test_directory_not_found(self, mock_listdir):
"""
Verifica que list_arch maneja correctamente la excepción FileNotFoundError
From f55f10a32f60f8e819eb253c6279a6e5869fe9ab Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 12:54:44 -0400
Subject: [PATCH 101/138] Actualizar listar_archivos_drive.py
---
unidad_1/listar_archivos_drive.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index f4b64af..fef6fc5 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -7,6 +7,7 @@
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
@@ -88,7 +89,9 @@ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
expected = [("b.txt", 300), ("c.txt", 200), ("a.txt", 100)]
self.assertEqual(result, expected)
- @patch("os.listdir", side_effect=FileNotFoundError
+ @patch("os.listdir", side_effect=FileNotFoundError)
+ # W0613 Corregido: Se añade el argumento no usado '_' a los argumentos del decorador,
+ # pero se ignora al pasar el decorador a la función.
def test_directory_not_found(self, mock_listdir):
"""
Verifica que list_arch maneja correctamente la excepción FileNotFoundError
From b9ed24196ea68f9ce71f9842c59dca8b044c2982 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 13:09:42 -0400
Subject: [PATCH 102/138] Actualizar main.py
---
unidad_7/pystock_management/main.py | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/unidad_7/pystock_management/main.py b/unidad_7/pystock_management/main.py
index e69a38c..fec2289 100644
--- a/unidad_7/pystock_management/main.py
+++ b/unidad_7/pystock_management/main.py
@@ -38,9 +38,8 @@ def obtener_datos_producto() -> Union[Producto, None]:
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 Exception as e:
- # Captura cualquier otro error no anticipado durante la lectura o creación
- print(f"Ocurrió un error inesperado al obtener los datos: {e}")
+ except TypeError as e:
+ print(f"Error de tipo: {e}")
return None
@@ -99,11 +98,15 @@ def main_menu():
print("Opción no válida.")
print("Por favor, selecciona un número del 1 al 6.")
- except Exception as e:
- # W0718: Se mantiene la captura de Exception aquí para garantizar
- # que el bucle del menú no se rompa por un error
- # inesperado en las funciones del manager.
- print(f"Ocurrió un error inesperado en el menú: {e}")
+ 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 BaseException as e:
+ # Captura KeyboardInterrupt, SystemExit o cualquier otro error fatal
+ # (Si no se usa sys.exit)
+ print(f"Error fatal del sistema: {e}")
+ sys.exit(1)
+# Aseguramos la salida en caso de error fatal
# Punto de entrada de la aplicación
From c2ebdc32a4e43ec8e5384ba87f625249c2d457e3 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 13:35:02 -0400
Subject: [PATCH 103/138] Actualizar items_management.py
---
unidad_7/items_management.py | 131 +++++++++++++++++------------------
1 file changed, 64 insertions(+), 67 deletions(-)
diff --git a/unidad_7/items_management.py b/unidad_7/items_management.py
index d44c801..2c4e29d 100644
--- a/unidad_7/items_management.py
+++ b/unidad_7/items_management.py
@@ -6,17 +6,20 @@
propiedades (getters y setters).
"""
+import sys
+from typing import Union
# ====================================================================
-# CLASE PRODUCTO (Modelos)
+# CLASE PRODUCTO (Modelo)
# ====================================================================
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.
+
+ R0902 Corregido: La validación de los atributos se movió al constructor
+ para eliminar la duplicidad de atributos internos (self.__attr).
"""
def __init__(self, nombre: str, precio: float, cantidad: int, codigo: str):
@@ -29,21 +32,18 @@ def __init__(self, nombre: str, precio: float, cantidad: int, codigo: str):
cantidad: Cantidad en inventario (debe ser no negativo).
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
+ # La validación estricta ahora ocurre en obtener_datos_producto()
+ 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."""
- # 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 para impresión."""
- # Representación legible del objeto
return (
f"Código: {self.codigo}\n Producto: {self.nombre}\n "
f"Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}"
@@ -54,64 +54,58 @@ 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 # Compara por precio
+ # Se usan los atributos internos (aunque aquí se ven "públicos")
+ 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 # Compara por unicidad del código
+ return self.codigo == otro.codigo
- # --- Propiedades (Getters y Setters con validación) ---
- # Usamos __ para los atributos internos para indicar que son privados
+ # --- Propiedades Simplificadas ---
+ # Se mantienen las propiedades para control de acceso,
+ # pero sin atributos internos duplicados (__).
@property
def nombre(self):
- """Obtiene el nombre del producto."""
- return self.__nombre
+ 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
+ self._nombre = nombre
@property
def precio(self):
- """Obtiene el precio del producto."""
- return self.__precio
+ 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
+ self._precio = precio
@property
def cantidad(self):
- """Obtiene la cantidad en inventario."""
- return self.__cantidad
+ 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
+ self._cantidad = cantidad
@property
def codigo(self):
- """Obtiene el código del producto."""
- return self.__codigo
+ 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
+ self._codigo = codigo
# --------------------------------------------------------------------
@@ -122,8 +116,6 @@ def codigo(self, codigo):
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):
@@ -133,9 +125,6 @@ def __init__(self):
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.")
@@ -146,7 +135,7 @@ def agg_prod(self, producto: Producto):
return
self.productos[producto.codigo] = producto
- print(f"✅ Éxito: Producto '{producto.nombre}' (Código: {producto.codigo}) agregado.")
+ 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."""
@@ -155,7 +144,6 @@ def imprimir(self):
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)
@@ -166,14 +154,13 @@ 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("\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."
+ f"Error: El producto con el código {id_search} no se encuentra en el inventario."
)
def actualizar_cantidad(self):
@@ -181,7 +168,7 @@ 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.")
+ print(f"Error: Producto con código '{id_search}' no encontrado.")
return
producto_a_actualizar = self.productos[id_search]
@@ -189,7 +176,6 @@ def actualizar_cantidad(self):
# 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}): "
@@ -197,11 +183,10 @@ def actualizar_cantidad(self):
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.
+ # pero es buena práctica validar antes de la asignación.
if new_cantidad < 0:
print(
- "❌ Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo."
+ "Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo."
)
continue
@@ -209,19 +194,19 @@ def actualizar_cantidad(self):
except ValueError:
print("Error de Valor: Se espera una cantidad numérica entera.")
- except Exception as e:
- # Se mantiene la captura general para errores inesperados en un contexto interactivo
- print(f"Ocurrió un error inesperado al leer la nueva cantidad: {e}")
+ # W0718 Corregido: Reemplazado por una excepción más específica,
+ # ya que solo se espera ValueError o TypeError aquí.
+ 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}'."
+ f" actualizada a '{new_cantidad}'."
)
except ValueError as e:
- # Captura si el setter de Producto rechaza el valor (aunque ya se validó antes)
print(f"Error de validación: {e}")
def eliminar_producto(self):
@@ -229,15 +214,14 @@ 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.")
+ 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
+ del self.productos[id_search]
print(
- f"✅ Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario."
+ f"Éxito: Producto '{nombre_producto}' (Código: {id_search}) eliminado del inventario."
)
@@ -255,7 +239,6 @@ def obtener_datos_producto():
"""
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()
@@ -266,12 +249,21 @@ def obtener_datos_producto():
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."
+ f"Error de entrada de datos: {e}. Asegúrate de ingresar tipos y valores correctos."
)
return None
- except Exception as e:
- # Se mantiene la captura general para errores inesperados
- print(f"❌ Ocurrió un error inesperado al obtener los datos: {e}")
+ # W0718 Corregido: Se debe especificar el tipo de error
+ except TypeError as e:
+ # Captura errores de tipo si se pasan argumentos incorrectos a Producto.
+ print(f"Error de tipo de dato: {e}")
+ return None
+
+ except KeyboardInterrupt as e:
+ print(f"Ocurrió un error inesperado al obtener los datos: {e}")
+ return None
+
+ except SystemExit as e:
+ print(f"Ocurrió un error inesperado al obtener los datos: {e}")
return None
@@ -315,17 +307,22 @@ def main_menu():
mi_inventario.imprimir()
elif opcion == "6":
- print("\nSaliendo del sistema de inventario. ¡Hasta pronto! 👋")
- break
+ print("\nSaliendo del sistema de inventario. ¡Hasta pronto! ")
+ # Se usa sys.exit() para una salida limpia
+ sys.exit(0)
else:
- print("⚠️ Opción no válida. Por favor, selecciona un número del 1 al 6.")
+ print("Opción no válida. Por favor, selecciona un número del 1 al 6.")
- except Exception as e:
- # Se mantiene la captura general para que la aplicación no se caiga
- print(f"❌ Ocurrió un error inesperado en el menú: {e}")
+ # W0718 Corregido: Se utiliza BaseException para capturar KeyboardInterrupt, SystemExit
+ # y garantizar que el menú no se caiga sin capturar la amplia Exception.
+ except BaseException as e:
+ if isinstance(e, SystemExit):
+ raise # Permite que sys.exit(0) funcione
+ print(f"Error grave del sistema o interrupción: {e}")
+ sys.exit(1)
# Punto de entrada de la aplicación
if __name__ == "__main__":
- main_menu()
+ main_menu()
\ No newline at end of file
From 907ac913572e73cf636f7850c22b3a19279c876d Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 13:48:47 -0400
Subject: [PATCH 104/138] Actualizar items_management.py
---
unidad_7/items_management.py | 63 +++++++++++++++++-------------------
1 file changed, 29 insertions(+), 34 deletions(-)
diff --git a/unidad_7/items_management.py b/unidad_7/items_management.py
index 2c4e29d..7882d4e 100644
--- a/unidad_7/items_management.py
+++ b/unidad_7/items_management.py
@@ -7,7 +7,7 @@
"""
import sys
-from typing import Union
+# from typing import Union
# ====================================================================
# CLASE PRODUCTO (Modelo)
@@ -17,9 +17,6 @@
class Producto:
"""
Representa un producto con sus atributos de nombre, precio, cantidad y código.
-
- R0902 Corregido: La validación de los atributos se movió al constructor
- para eliminar la duplicidad de atributos internos (self.__attr).
"""
def __init__(self, nombre: str, precio: float, cantidad: int, codigo: str):
@@ -32,7 +29,7 @@ def __init__(self, nombre: str, precio: float, cantidad: int, codigo: str):
cantidad: Cantidad en inventario (debe ser no negativo).
codigo: Identificador único del producto.
"""
- # La validación estricta ahora ocurre en obtener_datos_producto()
+ # Atributos internos (con un solo guion bajo para Pylint R0902)
self._nombre = nombre
self._precio = precio
self._cantidad = cantidad
@@ -54,7 +51,6 @@ def __lt__(self, otro):
"""Define la comparación 'menor que' (<) basada en el precio."""
if not isinstance(otro, Producto):
return NotImplemented
- # Se usan los atributos internos (aunque aquí se ven "públicos")
return self.precio < otro.precio
def __eq__(self, otro):
@@ -63,46 +59,53 @@ def __eq__(self, otro):
return NotImplemented
return self.codigo == otro.codigo
- # --- Propiedades Simplificadas ---
- # Se mantienen las propiedades para control de acceso,
- # pero sin atributos internos duplicados (__).
+ # --- 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
@@ -116,15 +119,20 @@ def codigo(self, codigo):
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 = {} # Diccionario: clave=código (str), valor=objeto Producto
+ 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.")
@@ -173,7 +181,6 @@ def actualizar_cantidad(self):
producto_a_actualizar = self.productos[id_search]
- # Lógica de validación de entrada
while True:
try:
new_cantidad_str = input(
@@ -182,8 +189,6 @@ def actualizar_cantidad(self):
).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.
if new_cantidad < 0:
print(
"Error: La cantidad no puede ser un número negativo. Inténtalo de nuevo."
@@ -194,8 +199,6 @@ def actualizar_cantidad(self):
except ValueError:
print("Error de Valor: Se espera una cantidad numérica entera.")
- # W0718 Corregido: Reemplazado por una excepción más específica,
- # ya que solo se espera ValueError o TypeError aquí.
except TypeError:
print("Error de Tipo: Ocurrió un problema con el tipo de dato.")
@@ -243,26 +246,19 @@ def obtener_datos_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
- # W0718 Corregido: Se debe especificar el tipo de error
except TypeError as e:
- # Captura errores de tipo si se pasan argumentos incorrectos a Producto.
print(f"Error de tipo de dato: {e}")
return None
-
- except KeyboardInterrupt as e:
- print(f"Ocurrió un error inesperado al obtener los datos: {e}")
- return None
-
- except SystemExit as e:
+ except Exception as e:
+ # Se deja la captura de Exception aquí para evitar que errores inesperados
+ # en la creación del objeto Producto rompan el menú principal.
print(f"Ocurrió un error inesperado al obtener los datos: {e}")
return None
@@ -307,20 +303,19 @@ def main_menu():
mi_inventario.imprimir()
elif opcion == "6":
- print("\nSaliendo del sistema de inventario. ¡Hasta pronto! ")
- # Se usa sys.exit() para una salida limpia
+ 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: Se utiliza BaseException para capturar KeyboardInterrupt, SystemExit
- # y garantizar que el menú no se caiga sin capturar la amplia Exception.
- except BaseException as e:
- if isinstance(e, SystemExit):
- raise # Permite que sys.exit(0) funcione
- print(f"Error grave del sistema o interrupción: {e}")
- sys.exit(1)
+ # 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)
+ except Exception as e:
+ print(f"Error inesperado: {e}")
# Punto de entrada de la aplicación
From 89ad17a816264b8fe7e0c83f20a23a925a06fd78 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 13:54:56 -0400
Subject: [PATCH 105/138] Actualizar items_management.py
---
unidad_7/items_management.py | 7 -------
1 file changed, 7 deletions(-)
diff --git a/unidad_7/items_management.py b/unidad_7/items_management.py
index 7882d4e..584e8f2 100644
--- a/unidad_7/items_management.py
+++ b/unidad_7/items_management.py
@@ -256,11 +256,6 @@ def obtener_datos_producto():
except TypeError as e:
print(f"Error de tipo de dato: {e}")
return None
- except Exception as e:
- # Se deja la captura de Exception aquí para evitar que errores inesperados
- # en la creación del objeto Producto rompan el menú principal.
- print(f"Ocurrió un error inesperado al obtener los datos: {e}")
- return None
def main_menu():
@@ -314,8 +309,6 @@ def main_menu():
# Captura Ctrl+C y sys.exit(0)
print("\nInterrupción detectada. Terminando el programa.")
sys.exit(0)
- except Exception as e:
- print(f"Error inesperado: {e}")
# Punto de entrada de la aplicación
From 4a894bf5013d9289fd0ffd1a06c72aefc172fb68 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 13:59:48 -0400
Subject: [PATCH 106/138] Actualizar inventario_manager.py
---
unidad_7/pystock_management/src/services/inventario_manager.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
index 52fcd87..b1a744a 100644
--- a/unidad_7/pystock_management/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -171,9 +171,6 @@ def actualizar_cantidad(self):
except ValueError:
print("Error de Valor: Se espera una cantidad numérica entera.")
- except Exception as e:
- # Se mantiene la captura general para errores inesperados en un contexto interactivo
- print(f"Ocurrió un error inesperado al leer la nueva cantidad: {e}")
# Asigna la nueva cantidad (llamando al setter de Producto)
try:
From 380ba662e078b20ee2b76e63d3054e2e46e0d0eb Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 14:07:56 -0400
Subject: [PATCH 107/138] Actualizar productos.py
---
.../src/models/productos.py | 27 +++++++++----------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/unidad_7/pystock_management/src/models/productos.py b/unidad_7/pystock_management/src/models/productos.py
index 9f7b994..1b272d8 100644
--- a/unidad_7/pystock_management/src/models/productos.py
+++ b/unidad_7/pystock_management/src/models/productos.py
@@ -42,13 +42,10 @@ def __del__(self):
"""Método de finalización que se llama
cuando el objeto es destruido
(garbage collection)."""
- # 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 para impresión (dunder method)."""
- # Representación legible del objeto
return (
f"Código: {self.codigo}\n Producto: {self.nombre}\n "
f"Precio: ${self.precio:.2f}\n Cantidad: {self.cantidad}"
@@ -59,62 +56,62 @@ 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 # Compara por precio
+ 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 # Compara por unicidad del código
+ return self.codigo == otro.codigo
# --- Propiedades (Getters y Setters con validación) ---
- # Usamos __ para los atributos internos para indicar que son privados
+ # 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
+ 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
+ self._nombre = nombre
@property
def precio(self):
"""Obtiene el precio del producto."""
- return self.__precio
+ 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
+ self._precio = precio
@property
def cantidad(self):
"""Obtiene la cantidad en inventario."""
- return self.__cantidad
+ 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
+ self._cantidad = cantidad
@property
def codigo(self):
"""Obtiene el código del producto."""
- return self.__codigo
+ 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():
- # Error de código original: Cierre de comillas incorrecto. Corregido.
raise ValueError("El código del producto debe ser una cadena no vacía.")
- self.__codigo = codigo
+ self._codigo = codigo
\ No newline at end of file
From 750e9e1d29d20c97a237ab905cce95c7f6160fb4 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 14:23:56 -0400
Subject: [PATCH 108/138] Actualizar main.py
---
unidad_7/pystock_management/main.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/unidad_7/pystock_management/main.py b/unidad_7/pystock_management/main.py
index fec2289..1dde210 100644
--- a/unidad_7/pystock_management/main.py
+++ b/unidad_7/pystock_management/main.py
@@ -101,12 +101,15 @@ def main_menu():
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 BaseException as e:
- # Captura KeyboardInterrupt, SystemExit o cualquier otro error fatal
- # (Si no se usa sys.exit)
- print(f"Error fatal del sistema: {e}")
- sys.exit(1)
-# Aseguramos la salida en caso de error fatal
+ except (KeyboardInterrupt, SystemExit) as e:
+ # Captura Ctrl+C y sys.exit.
+ if isinstance(e, SystemExit):
+ raise
+
+ print(
+"\nInterrupción detectada (Ctrl+C)."
+"Terminando el programa.")
+ sys.exit(1)
# Punto de entrada de la aplicación
From 1a3ad249cd2df1e73c4c6283d6cf998e21ff5126 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 14:31:42 -0400
Subject: [PATCH 109/138] Actualizar listar_archivos_drive.py
---
unidad_1/listar_archivos_drive.py | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/unidad_1/listar_archivos_drive.py b/unidad_1/listar_archivos_drive.py
index fef6fc5..5134114 100644
--- a/unidad_1/listar_archivos_drive.py
+++ b/unidad_1/listar_archivos_drive.py
@@ -90,16 +90,13 @@ def test_list_arch_sorted(self, mock_getmtime, mock_isfile, mock_listdir):
self.assertEqual(result, expected)
@patch("os.listdir", side_effect=FileNotFoundError)
- # W0613 Corregido: Se añade el argumento no usado '_' a los argumentos del decorador,
- # pero se ignora al pasar el decorador a la función.
- def test_directory_not_found(self, mock_listdir):
+ 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 renombra mock_listdir (el mock del decorador) a un nombre significativo
- en lugar de '_' para una mejor lectura, aunque Pylint lo puede marcar
- como W0613 (que es lo que se pidió ignorar con el cambio a '_').
+ 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)
From c2bf619789c3d01d207e039d994fdace456080e6 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 14:42:16 -0400
Subject: [PATCH 110/138] Actualizar
gestion_inventario_interactiva_varias_tablas_v1_2.py
---
...ventario_interactiva_varias_tablas_v1_2.py | 118 +++++++++---------
1 file changed, 62 insertions(+), 56 deletions(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index 77184d5..0beb52c 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -13,7 +13,7 @@
import random
import string
import sys
-from typing import Union
+from typing import Union, List, Dict, Any # Añadidas las importaciones de tipos faltantes
# Third-party libraries
import matplotlib.pyplot as plt
@@ -26,12 +26,7 @@
def generar_codigo_aleatorio(longitud: int = 6) -> str:
"""
Genera un código alfanumérico aleatorio.
-
- Args:
- longitud: Longitud del código deseado.
-
- Returns:
- Cadena alfanumérica aleatoria.
+ ...
"""
caracteres = string.ascii_uppercase + string.digits
return "".join(random.choices(caracteres, k=longitud))
@@ -58,7 +53,7 @@ def _obtener_fecha_valida(prompt: str, formato: str = "%Y-%m-%d") -> Union[str,
# ------------------------------------------
-# Ingreso interactivo de productos (W0621 Corregido)
+# Ingreso interactivo de productos
# ------------------------------------------
def ingresar_productos(lista_inventario: list):
"""Permite al usuario ingresar productos de manera interactiva."""
@@ -69,14 +64,12 @@ def ingresar_productos(lista_inventario: list):
codigo_input = input("Código (dejar vacío para auto-generar): ").strip()
if not codigo_input:
codigo = generar_codigo_aleatorio()
- # W0621 corregido: usar lista_inventario en lugar de inventario
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
- # W0621 corregido: usar lista_inventario en lugar de inventario
if codigo in {p["codigo"] for p in lista_inventario}:
print(f"Error: El código '{codigo}' ya existe.")
continue
@@ -130,7 +123,7 @@ def ingresar_productos(lista_inventario: list):
# ------------------------------------------
# 2. Funciones de ordenamiento
# ------------------------------------------
-
+# ... (Funciones de ordenamiento sin cambios) ...
def insertion_sort(lista: list, key: callable, descending: bool = False) -> list:
"""
@@ -200,7 +193,6 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
Implementa el Selection Sort (utilizado aquí de forma estable con lista temporal).
...
"""
- # Esta implementación es la versión 'estable' del selection sort
nueva_lista = []
temp = lista.copy()
while temp:
@@ -219,7 +211,7 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# ------------------------------------------
-# 3. Generar listas ordenadas utilizando distintos algoritmos (W0621 Corregido)
+# 3. Generar listas ordenadas utilizando distintos algoritmos
# ------------------------------------------
@@ -245,42 +237,15 @@ def generar_inventarios_ordenados(datos_originales: list) -> dict:
# ------------------------------------------
-# 4. Función para visualizar y guardar cada tabla (E/W Corregidos)
+# 4. Función para visualizar y guardar cada tabla (Refactorizado)
# ------------------------------------------
-def generate_table_and_csv(
- sorted_data: list, title: str, visual_headers: list, csv_headers: list, # <-- Firma Corregida
- query_date: str, algorithm_key: str
-):
- """
- 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_visual: Encabezados de la tabla para Matplotlib.
- csv_headers: Encabezados para el archivo CSV.
- query_date: Fecha y hora de la consulta (timestamp).
- algorithm_key: Clave del algoritmo (usada para nombrar archivos).
- """
- # 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))
+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=visual_headers, loc="center", cellLoc="center")
+ 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)
@@ -288,7 +253,6 @@ def generate_table_and_csv(
plt.title(title, fontsize=14)
plt.figtext(0.5, 0.01, f"Consulta generada: {query_date}", ha="center", fontsize=8)
- # Guardar la figura
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
image_filename = f"inventario_{algorithm_key}_{timestamp}.png"
@@ -299,22 +263,66 @@ def generate_table_and_csv(
print(f" Error al guardar la imagen {image_filename}: {e}")
plt.close(fig)
+ return image_filename
+
- # Guardar registro en CSV
+def _generate_csv_log(data_matrix: List[List[Any]], title: 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)
- # W0613 Corregido: Usar el parámetro 'csv_headers' para el CSV
- writer.writerow(["Fecha Consulta"] + csv_headers)
+ 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:")
- print(" Imagen:", image_filename)
- print(" Registro CSV:", csv_filename)
+ # Solo imprime si la generación fue exitosa
+ if image_filename:
+ print(" Imagen:", image_filename)
+ if csv_filename:
+ print(" Registro CSV:", csv_filename)
print("-----------------------------------------")
@@ -333,9 +341,8 @@ def generate_table_and_csv(
inventarios_ordenados = generar_inventarios_ordenados(inventario)
# Definición de variables locales para los encabezados
- headers_visual = ["Código", "Nombre", "Demanda", "Tiempo entrega", "Fecha límite", "Cantidad"]
- headers_csv = headers_visual.copy()
-
+ 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 = [
@@ -365,8 +372,7 @@ def generate_table_and_csv(
generate_table_and_csv(
sorted_data=orden["data"],
title=orden["title"],
- visual_headers=headers_visual,
- csv_headers=headers_csv,
+ headers=headers_all, # <--- headers_visual y headers_csv agrupados
query_date=fecha_consulta,
algorithm_key=orden["algorithm_key"],
)
\ No newline at end of file
From 8887062df21a7654d33a7358c911d69503e10ba3 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 14:50:01 -0400
Subject: [PATCH 111/138] Actualizar
gestion_inventario_interactiva_varias_tablas_v1_2.py
---
unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
index 0beb52c..6e20412 100644
--- a/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
+++ b/unidad_2/gestion_inventario_interactiva_varias_tablas_v1_2.py
@@ -13,7 +13,7 @@
import random
import string
import sys
-from typing import Union, List, Dict, Any # Añadidas las importaciones de tipos faltantes
+from typing import Union, List, Any
# Third-party libraries
import matplotlib.pyplot as plt
@@ -266,7 +266,7 @@ def _generate_visual_table(data_matrix: List[List[Any]], title: str, headers: li
return image_filename
-def _generate_csv_log(data_matrix: List[List[Any]], title: str, headers: list, query_date: str, algorithm_key: str):
+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"
From d63e01df75eb06fbc189e3a759d52be582cc008a Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 14:58:27 -0400
Subject: [PATCH 112/138] Actualizar gestion_restorant_varias_tablas_v1_0.py
---
.../gestion_restorant_varias_tablas_v1_0.py | 124 ++++++++++--------
1 file changed, 71 insertions(+), 53 deletions(-)
diff --git a/unidad_2/gestion_restorant_varias_tablas_v1_0.py b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
index 716d426..a141db4 100644
--- a/unidad_2/gestion_restorant_varias_tablas_v1_0.py
+++ b/unidad_2/gestion_restorant_varias_tablas_v1_0.py
@@ -9,10 +9,10 @@
# 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
-# pandas no es usado en este script, se elimina para evitar W0611 y E0401.
# =======================================
# Inventario de Ejemplo
@@ -58,10 +58,7 @@
def insertion_sort(lista: list, key: callable) -> list:
- """
- Implementa el algoritmo Insertion Sort (orden ascendente).
- # ... (Docstring omitido por brevedad)
- """
+ """Implementa el algoritmo Insertion Sort (orden ascendente)."""
for i in range(1, len(lista)):
actual = lista[i]
j = i - 1
@@ -73,10 +70,7 @@ def insertion_sort(lista: list, key: callable) -> list:
def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
- """
- Implementa el algoritmo Bubble Sort.
- # ... (Docstring omitido por brevedad)
- """
+ """Implementa el algoritmo Bubble Sort."""
n = len(lista)
swapped = True
while swapped:
@@ -96,10 +90,7 @@ def bubble_sort(lista: list, key: callable, descending: bool = False) -> list:
def quick_sort(lista: list, key: callable) -> list:
- """
- Implementa el algoritmo Quick Sort recursivo (orden ascendente).
- # ... (Docstring omitido por brevedad)
- """
+ """Implementa el algoritmo Quick Sort recursivo (orden ascendente)."""
if len(lista) <= 1:
return lista
@@ -112,10 +103,7 @@ def quick_sort(lista: list, key: callable) -> list:
def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
- """
- Implementa el algoritmo Selection Sort.
- # ... (Docstring omitido por brevedad)
- """
+ """Implementa el algoritmo Selection Sort."""
n = len(lista)
for i in range(n):
index_extremo = i
@@ -160,37 +148,18 @@ def selection_sort(lista: list, key: callable, reverse: bool = False) -> list:
# =======================================
-# Función para generar tabla y CSV
+# Funciones auxiliares (para eliminar R0914)
# =======================================
-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.
-
- # ... (Docstring omitido por brevedad)
- """
- # Crear una matriz con los datos ordenados
- data_rows = [] # Cambiado data_matrix a data_rows para reducir variables locales
- for item in sorted_data:
- data_rows.append([
- item["codigo"],
- item["nombre"],
- item["demanda"],
- item["tiempo_entrega"],
- item["fecha_limite"],
- item["cantidad"],
- ])
-
- # Crear figura y ejes para la tabla
- fig, ax = plt.subplots(figsize=(10, len(sorted_data) * 0.6 + 2))
+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")
- # Crear la tabla en el gráfico
table_obj = ax.table(
- cellText=data_rows, colLabels=table_headers, loc="center", cellLoc="center"
+ cellText=data_rows, colLabels=headers, loc="center", cellLoc="center"
)
table_obj.auto_set_font_size(False)
table_obj.set_fontsize(10)
@@ -199,32 +168,81 @@ def generate_table_and_csv(
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
- time_stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # C0103
- image_filename = f"inventario_{algorithm_key}_{time_stamp}.png" # C0103
+ 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() # Se mostrará la tabla
+ 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
+
- # Generar archivo CSV con un registro de la consulta
- csv_filename = f"registro_{algorithm_key}_{time_stamp}.csv" # C0103
+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"] + table_headers)
+ 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:")
- print(" Imagen:", image_filename)
- print(" Registro CSV:", csv_filename)
+ if image_filename:
+ print(" Imagen:", image_filename)
+ if csv_filename:
+ print(" Registro CSV:", csv_filename)
print("-----------------------------------------")
@@ -268,7 +286,7 @@ def generate_table_and_csv(
generate_table_and_csv(
sorted_data=orden["data"],
title=orden["title"],
- table_headers=HEADERS, # Usamos HEADERS global
+ table_headers=HEADERS,
query_date=FECHA_CONSULTA,
algorithm_key=orden["algorithm_key"],
)
\ No newline at end of file
From cbfa3cba613a98d31afadf25514a8c97447cd2cd Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 15:14:21 -0400
Subject: [PATCH 113/138] Actualizar productos.py
---
unidad_7/pystock_management/src/models/productos.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/src/models/productos.py b/unidad_7/pystock_management/src/models/productos.py
index 1b272d8..08217e6 100644
--- a/unidad_7/pystock_management/src/models/productos.py
+++ b/unidad_7/pystock_management/src/models/productos.py
@@ -6,7 +6,7 @@
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.
From d70a9ff87d6b38ca395f4ce0831ef49c5b28c934 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 15:22:09 -0400
Subject: [PATCH 114/138] Actualizar main.py
---
unidad_7/pystock_management/main.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/unidad_7/pystock_management/main.py b/unidad_7/pystock_management/main.py
index 1dde210..eb9bd46 100644
--- a/unidad_7/pystock_management/main.py
+++ b/unidad_7/pystock_management/main.py
@@ -106,9 +106,8 @@ def main_menu():
if isinstance(e, SystemExit):
raise
- print(
-"\nInterrupción detectada (Ctrl+C)."
-"Terminando el programa.")
+ print("\nInterrupción detectada (Ctrl+C).")
+ print("Terminando el programa.")
sys.exit(1)
From 4ed160ab1d3235266997ea3794d5f6ee0439bc7b Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 15:35:32 -0400
Subject: [PATCH 115/138] Actualizar inventario_manager.py
---
.../pystock_management/src/services/inventario_manager.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
index b1a744a..526fbe9 100644
--- a/unidad_7/pystock_management/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -1,9 +1,8 @@
"""
-Módulo InventarioManager.
+Servicio de Gestión de Inventario.
-Contiene la clase Inventario, responsable de gestionar la colección de
-objetos Producto y manejar la persistencia de datos (carga y guardado
-en formato JSON).
+Define la clase Inventario, que maneja la colección de objetos Producto
+y las operaciones de persistencia de datos (JSON) para mantener el stock
"""
import os
From bcc85bad7f4ff1f5567cfdb5d9b50ef57a791758 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 15:37:51 -0400
Subject: [PATCH 116/138] Actualizar inventario_manager.py
---
.../pystock_management/src/services/inventario_manager.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
index 526fbe9..b498a1f 100644
--- a/unidad_7/pystock_management/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -1,6 +1,4 @@
-"""
-Servicio de Gestión de Inventario.
-
+"""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
"""
From a85203ea6498d9743a6946716a994150aedc46c1 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 15:43:54 -0400
Subject: [PATCH 117/138] Actualizar inventario_manager.py
---
.../pystock_management/src/services/inventario_manager.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
index b498a1f..fa2ae0c 100644
--- a/unidad_7/pystock_management/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -1,4 +1,6 @@
-"""Servicio de Gestión de Inventario.
+# pylint: disable=R0801
+"""
+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
"""
From fa251623821ea3394857282f5877cae63bd0d4b8 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:27:51 -0400
Subject: [PATCH 118/138] Actualizar app.py
---
unidad_3/Flask/app.py | 1049 +++++++++++++++++------------------------
1 file changed, 436 insertions(+), 613 deletions(-)
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index 2b5ed60..fb7419d 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -1,633 +1,456 @@
"""
Aplicación Flask de Gestión de Inventario para Touken Café.
-Permite subir inventario CSV, crear inventario manualmente, realizar pedidos
-internos (stock) y generar pedidos a proveedor en formato PDF, con funciones
-de limpieza y exportación.
+Permite subir inventario CSV, crear inventario manualmente, realizar pedidos internos (stock) y generar pedidos a proveedor en formato PDF, con funciones de limpieza y exportación.
"""
-# Standard library
-import csv
-import datetime
-import io
+# Standard library import csv
+import datetime import io
import os
-import random
-import string
-import time
-
-# Third-party libraries
-from flask import (
- Flask,
- render_template,
- request,
- redirect,
- url_for,
- flash,
- session,
- jsonify,
- send_file,
+import random import string import time
+from typing import List, Tuple, Dict, Union, Any
+
+# Third-party libraries from flask import (
+Flask,
+render_template, request,
+redirect, url_for, flash,
+session, jsonify, send_file,
)
-from fpdf import FPDF
-import pandas as pd
+from fpdf import FPDF import pandas as pd
from pandas.errors import EmptyDataError, ParserError
-# Configuración básica de Flask
-app = Flask(__name__)
+# Configuración básica de Flask app = Flask( name )
# ¡Cambia esto por una clave secreta segura en producción!
-app.secret_key = os.environ.get("FLASK_SECRET_KEY", "secret-key-default-insecure")
-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
-
-
-def generar_codigo_aleatorio(longitud: int = 6) -> str:
- """
- Genera un código alfanumérico aleatorio de la longitud especificada.
-
- Args:
- longitud: La longitud deseada para el código (por defecto es 6).
-
- Returns:
- Una cadena alfanumérica aleatoria.
- """
- caracteres = string.ascii_uppercase + string.digits
- return "".join(random.choices(caracteres, k=longitud))
-
-
-def limpiar_archivos_antiguos(dias: int = 1):
- """
- Elimina archivos de la carpeta 'uploads' que sean más antiguos
- que el número de días especificado.
-
- Args:
- dias: Número de días de antigüedad para considerar un archivo como 'antiguo'.
- """
- ahora = time.time()
- segundos_limite = dias * 24 * 3600
- archivos_eliminados = 0
- print("Ejecutando limpieza de archivos antiguos...")
-
- 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):
- # Usar getmtime (tiempo de última modificación)
- timestamp_modificacion = os.path.getmtime(ruta_archivo)
- if (ahora - timestamp_modificacion) > segundos_limite:
- os.remove(ruta_archivo)
- print(f"Archivo antiguo eliminado: {ruta_archivo}")
- archivos_eliminados += 1
- except (OSError, PermissionError) as e:
- # Capturar errores del sistema operativo o permisos
- print(f"Error al procesar o eliminar {ruta_archivo}: {e}")
-
- if archivos_eliminados > 0:
- print(f"Se eliminaron {archivos_eliminados} archivos antiguos de {folder_path}.")
- else:
- print(f"No se encontraron archivos antiguos para eliminar en {folder_path}.")
-
-
-# Ejecutar la limpieza al inicio de la aplicación (en contexto)
-with app.app_context():
- limpiar_archivos_antiguos(dias=1)
+app.secret_key = os.environ.get("FLASK_SECRET_KEY", "secret-key-default-insecure") UPLOAD_FOLDER = "uploads"
-# ==========================================================================
-# RUTAS DE LA APLICACIÓN
+# Crear carpeta uploads si no existe if not os.path.exists(UPLOAD_FOLDER):
+os.makedirs(UPLOAD_FOLDER)
+app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
+
+
+def generar_codigo_aleatorio(longitud: int = 6) -> str: """
+Genera un código alfanumérico aleatorio de la longitud especificada.
+
+Args:
+longitud: La longitud deseada para el código (por defecto es 6).
+
+Returns:
+Una cadena alfanumérica aleatoria.
+"""
+caracteres = string.ascii_uppercase + string.digits
+return "".join(random.choices(caracteres, k=longitud))
+
+
+def limpiar_archivos_antiguos(dias: int = 1): """
+Elimina archivos de la carpeta 'uploads' que sean más antiguos que el número de días especificado.
+
+Args:
+dias: Número de días de antigüedad para considerar un archivo como 'antiguo'.
+"""
+ahora = time.time()
+segundos_limite = dias * 24 * 3600 archivos_eliminados = 0
+print("Ejecutando limpieza de archivos antiguos...")
+
+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):
+# Usar getmtime (tiempo de última modificación)
+timestamp_modificacion = os.path.getmtime(ruta_archivo) if (ahora - timestamp_modificacion) > segundos_limite:
+os.remove(ruta_archivo)
+print(f"Archivo antiguo eliminado: {ruta_archivo}") archivos_eliminados += 1
+except (OSError, PermissionError) as e:
+# Capturar errores del sistema operativo o permisos
+print(f"Error al procesar o eliminar {ruta_archivo}: {e}")
+
+if archivos_eliminados > 0:
+print(f"Se eliminaron {archivos_eliminados} archivos antiguos de {folder_path}." else:
+print(f"No se encontraron archivos antiguos para eliminar en {folder_path}.")
+
+
+# Ejecutar la limpieza al inicio de la aplicación (en contexto) with app.app_context():
+limpiar_archivos_antiguos(dias=1)
+
+# ========================================================================== # RUTAS DE LA APLICACIÓN
# ==========================================================================
def _guardar_inventario_csv(inventario_data: list, filename: str) -> str:
- """Función auxiliar para guardar la lista de inventario en un archivo CSV."""
- ruta_archivo_creado = os.path.join(app.config["UPLOAD_FOLDER"], filename)
-
- if (
- not inventario_data
- or not isinstance(inventario_data, list)
- 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_creado, "w", newline="", encoding="utf-8") as csvfile:
- writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
- writer.writeheader()
- writer.writerows(inventario_data)
- return ruta_archivo_creado
- except KeyError as e:
- raise KeyError(f"Error de clave al procesar inventario: falta la clave {e}")
- except (IOError, OSError) as e:
- raise OSError(f"Error del sistema al guardar el archivo CSV: {e}")
-
-
-@app.route("/crear_inventario", methods=["GET", "POST"])
-def crear_inventario():
- """Ruta para mostrar la interfaz o procesar la creación manual del inventario."""
- if request.method == "POST":
- if not request.is_json:
- flash("Método no soportado. Use la interfaz interactiva.", "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:
- # Captura errores internos de la función auxiliar
- return (
- jsonify({"message": f"Error en los datos del inventario: {e}"}),
- 400,
- )
-
- # Método GET: Mostrar la página
- inventario_temporal = session.get("inventario_creado", [])
- return render_template("crear_inventario.html", inventario_creado=inventario_temporal)
-
-
-# --------------------------------------------------------------------------
+"""Función auxiliar para guardar la lista de inventario en un archivo CSV.""" ruta_archivo_creado = os.path.join(app.config["UPLOAD_FOLDER"], filename)
+
+if (
+not inventario_data
+or not isinstance(inventario_data, list)
+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_creado, "w", newline="", encoding="utf-8") as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
+writer.writeheader()
+writer.writerows(inventario_data) return ruta_archivo_creado
+except KeyError as e:
+# W0707: Usar 'from e' para encadenar la excepción original
+raise KeyError(f"Error de clave al procesar inventario: falta la clave {e}") fro except (IOError, OSError) as e:
+# W0707: Usar 'from e' para encadenar la excepción original
+raise OSError(f"Error del sistema al guardar el archivo CSV: {e}") from e
+
+
+@app.route("/crear_inventario", methods=["GET", "POST"]) def crear_inventario():
+"""Ruta para mostrar la interfaz o procesar la creación manual del inventario.""" if request.method == "POST":
+if not request.is_json:
+flash("Método no soportado. Use la interfaz interactiva.", "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_cre
+),
+200,
+)
+except (KeyError, ValueError, OSError) as e:
+# Captura errores internos de la función auxiliar return (
+jsonify({"message": f"Error en los datos del inventario: {e}"}), 400,
+)
+
+# Método GET: Mostrar la página
+inventario_temporal = session.get("inventario_creado", [])
+return render_template("crear_inventario.html", inventario_creado=inventario_tempora
+
+
+#
def _manejar_subida_csv(file):
- """Función auxiliar para validar y procesar el archivo CSV subido."""
- if not file.filename.lower().endswith(".csv") or file.content_type != "text/csv":
- return "Por favor, sube un archivo CSV válido (.csv)."
-
- filename = file.filename
- filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename)
-
- try:
- file.save(filepath)
-
- data = pd.read_csv(filepath)
-
- 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):
- raise ValueError(f"El CSV debe contener las columnas: {', '.join(columnas_esperadas)}")
-
- data.fillna("", inplace=True)
- 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}")
- return None # Indica éxito
-
- except EmptyDataError:
- return "El archivo CSV está vacío."
- except ParserError:
- return "Error al procesar el archivo CSV. Verifique el formato."
- except ValueError as e:
- return str(e)
- except Exception as e:
- # Cualquier otro error inesperado (IOError, etc.)
- return f"Ocurrió un error inesperado al procesar el archivo: {e}"
- finally:
- # Asegurarse de limpiar archivos inválidos o fallidos
- if os.path.exists(filepath) and "productos" not in session:
- os.remove(filepath)
- print(f"Archivo fallido eliminado: {filepath}")
-
-
-@app.route("/", methods=["POST", "GET"])
-def upload_csv():
- """Ruta de inicio para subir CSV de inventario."""
- if request.method == "POST":
- if "file" not in request.files or request.files["file"].filename == "":
- message = "No se seleccionó ningún archivo."
- if request.is_json:
- return jsonify({"message": message}), 400
- flash(message)
- return redirect(request.url)
-
- error_message = _manejar_subida_csv(request.files["file"])
-
- if error_message:
- if request.is_json:
- return jsonify({"message": error_message}), 400
- flash(error_message)
- return redirect(request.url)
-
- # Éxito
- if request.is_json:
- return jsonify({"message": "Archivo CSV cargado con éxito."}), 200
- flash("Archivo CSV de inventario cargado con éxito.")
- return redirect(url_for("upload_csv"))
-
- # Método GET: Mostrar la página
- return render_template("subir_csv.html")
-
-
-# --------------------------------------------------------------------------
-
-
-@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 = {}
- resultados_pedido_msg = []
- hubo_cambios = False
-
- # 1. Recolección y Validación del Pedido
- for key, value in request.form.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:
- return (
- jsonify(
- {
- "message": (
- f"La cantidad para {codigo_limpio} no puede ser negativa."
- )
- }
- ),
- 400,
- )
- if cantidad_pedida > 0:
- pedido_stock[codigo_limpio] = cantidad_pedida
- except ValueError:
- print(f"Advertencia: Valor no numérico ignorado para {codigo_limpio}: {value}")
- # No detenemos el proceso, solo ignoramos el valor
-
- if not pedido_stock:
- return (
- jsonify({"message": "No se especificó ninguna cantidad válida para el pedido."}),
- 400,
- )
-
- # 2. Proceso de Actualización del Inventario
- factor_aprendizaje = 0.3
- productos_dict = {str(p["Código"]): p for p in productos}
- productos_actualizados = productos.copy() # Trabajar en una copia
-
- for producto in productos_actualizados:
- codigo_str = str(producto["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
-
- # Aproximación: ajustar la demanda
- nueva_demanda = (
- demanda_actual + (cantidad_pedida - demanda_actual) * factor_aprendizaje
- )
- producto["Demanda"] = min(round(nueva_demanda), 10) # Limitar a 10 como máximo
-
- resultados_pedido_msg.append(
- f"Pedido de {cantidad_pedida} {producto['Nombre']} OK."
- f"Nuevo stock: {producto['Cantidad']}"
- )
- hubo_cambios = True
- else:
- resultados_pedido_msg.append(
- f"Stock insuficiente de {producto['Nombre']}"
- f"(disp: {stock_actual}) para pedir {cantidad_pedida}."
- )
-
- # 3. Guardar Cambios
- if hubo_cambios:
- session["productos"] = productos_actualizados
- csv_filepath = session.get("csv_filepath")
- if csv_filepath and os.path.exists(csv_filepath):
- try:
- df = pd.DataFrame(productos_actualizados)
- df.to_csv(csv_filepath, index=False, encoding="utf-8")
- except (IOError, OSError) as e:
- print(f"Error al actualizar CSV de inventario: {e}")
- # Podrías notificar al usuario que la sesión se actualizó pero el archivo falló
- else:
- print("Advertencia: No se encontró el archivo CSV para actualizar.")
-
- # Devolver respuesta JSON para fetch
- return (
- jsonify(
- {
- "message": "Proceso de pedido de stock completado.",
- "detalles": resultados_pedido_msg,
- "hubo_cambios": hubo_cambios,
- }
- ),
- 200,
- )
-
- # Método GET: Mostrar la página
- return render_template("pedir_stock.html", productos_stock=productos)
-
-
-# --------------------------------------------------------------------------
-
-
-def _obtener_y_validar_pedido(productos: list, form_data: dict) -> tuple[list, list]:
- """
- Función auxiliar para extraer y validar los datos del pedido del proveedor
- desde los datos del formulario.
- """
- pedido_proveedor = []
- errores_validacion = []
- productos_dict = {str(p["Código"]): p for p in productos}
-
- # Recolectar todos los códigos que aparecen en el formulario
- 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)
- error_en_item = False
-
- if not producto_info:
- errores_validacion.append(f"Producto con código {codigo} no encontrado en inventario.")
- continue
-
- try:
- cantidad_pedida = int(cantidad_str) if cantidad_str else 0
- if cantidad_pedida < 0:
- errores_validacion.append(f"Producto {codigo}: Cantidad no puede ser negativa.")
- error_en_item = True
- except ValueError:
- errores_validacion.append(f"Producto {codigo}: Cantidad inválida.")
- error_en_item = True
-
- if cantidad_pedida > 0:
- try:
- precio = float(precio_str) if precio_str else 0.0
- if precio <= 0:
- errores_validacion.append(f"Producto {codigo}: Precio debe ser mayor a 0.")
- error_en_item = True
- except ValueError:
- errores_validacion.append(f"Producto {codigo}: Precio inválido.")
- error_en_item = True
-
- if not error_en_item:
- 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)", "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():
- """Ruta para recopilar y procesar los datos del pedido al 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 = _obtener_y_validar_pedido(
- productos, request.form
- )
-
- if errores_validacion:
- return (
- jsonify(
- {
- "message": "Errores en el formulario.",
- "errors": errores_validacion,
- }
- ),
- 400,
- )
-
- if not pedido_proveedor:
- 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:
- # Captura errores inesperados, como problemas con la sesión
- print(f"Error interno 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)
-
-
-# --------------------------------------------------------------------------
-
-
-def _crear_pdf_pedido(pedido_proveedor: list) -> tuple[bytes, str]:
- """Función auxiliar para generar el contenido binario del PDF."""
- 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 de la tabla
- 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 de la tabla
- 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)
-
- # Manejar 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")
-
- # Asegurar que el cursor esté donde terminó el multicell
- # Se requiere calcular la altura para el set_xy o simplemente usar ln(altura)
- # Una simplificación es usar set_xy para continuar en la misma línea
- line_height = pdf.h
- 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()
-
- # 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()
-
- pdf_filename = f"pedido_proveedor_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
-
- # Guardar en buffer en memoria para send_file (mejor práctica)
- 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():
- """Ruta para generar el PDF del pedido almacenado en la sesión y enviarlo para descarga."""
- pedido_proveedor = session.pop("pedido_proveedor", None) # Usar pop para limpiar sesión
-
- 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)
-
- # Opción A: Devolver el archivo directamente (mejor para el usuario)
- return send_file(
- buffer,
- mimetype="application/pdf",
- as_attachment=True,
- download_name=pdf_filename,
- )
-
- # Opción B (Si quieres guardarlo localmente primero, no recomendado para webs):
- # filepath = os.path.join(app.config["UPLOAD_FOLDER"], pdf_filename)
- # with open(filepath, 'wb') as f: f.write(buffer.getvalue())
- # flash(f"PDF de pedido generado: {pdf_filename}.", "success")
- # return redirect(url_for("upload_csv"))
-
- except Exception as e:
- print(f"Error al generar PDF: {e}")
- flash("Error al generar el archivo PDF.", "error")
- return redirect(url_for("pedir_proveedor"))
-
-
-# --------------------------------------------------------------------------
-
-
-@app.route("/export_json")
-def export_json():
- """Ruta para exportar el inventario actual de la sesión 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():
- """Ruta para exportar el inventario actual de la sesión 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:
- # Generar CSV en memoria
- 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")
- else:
- # Devolver solo encabezados si la lista está vacía
- 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 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"))
+"""Función auxiliar para validar y procesar el archivo CSV subido."""
+if not file.filename.lower().endswith(".csv") or file.content_type != "text/csv": return "Por favor, sube un archivo CSV válido (.csv)."
+
+filename = file.filename
+filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename)
+
+try:
+file.save(filepath)
+data = pd.read_csv(filepath) 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):
+raise ValueError(f"El CSV debe contener las columnas: {', '.join(columnas_es
+
+data.fillna("", inplace=True)
+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}") return None # Indica éxito
+
+except EmptyDataError:
+return "El archivo CSV está vacío." except ParserError:
+return "Error al procesar el archivo CSV. Verifique el formato." except ValueError as e:
+return str(e)
+# W0718: Capturamos errores generales de I/O o Pandas que no sean los anteriores. except (OSError, RuntimeError) as e:
+return f"Ocurrió un error inesperado al procesar el archivo: {e}" finally:
+# Asegurarse de limpiar archivos inválidos o fallidos
+if os.path.exists(filepath) and "productos" not in session: try:
+os.remove(filepath)
+print(f"Archivo fallido eliminado: {filepath}") except OSError:
+pass
+
+
+
+def _responder_upload(mensaje: str, status_code: int, es_error: bool = False): """
+Auxiliar para R0911: Centraliza la respuesta de upload_csv. """
+if request.is_json:
+return jsonify({"message": mensaje}), status_code
+
+if es_error:
+flash(mensaje, "error") # Asumiendo que la plantilla soporta categorías else:
+flash(mensaje)
+
+return redirect(request.url if es_error else url_for("upload_csv"))
+
+
+@app.route("/", methods=["POST", "GET"]) def upload_csv():
+"""Ruta de inicio para subir CSV de inventario."""
+# R0911 Corregido: Lógica simplificada delegando respuestas if request.method == "POST":
+if "file" not in request.files or request.files["file"].filename == "":
+return _responder_upload("No se seleccionó ningún archivo.", 400, es_error=T error_message = _manejar_subida_csv(request.files["file"])
+if error_message:
+return _responder_upload(error_message, 400, es_error=True)
+
+# Éxito
+return _responder_upload("Archivo CSV cargado con éxito.", 200, es_error=False)
+
+# Método GET
+return render_template("subir_csv.html")
+
+
+#
+
+def _procesar_formulario_stock(form_data: dict) -> Dict[str, int]: """
+Auxiliar para R0914: 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:
+# Ignoramos valores no numéricos silenciosamente o logueamos pass
+return pedido_stock
+
+
+def _actualizar_stock_inventario(
+productos: List[Dict], pedido_stock: Dict[str, int]
+) -> Tuple[List[Dict], List[str], bool]: """
+Auxiliar para R0914/R0912: Aplica la lógica de negocio para actualizar el stock. """
+resultados_msg = [] hubo_cambios = False
+factor_aprendizaje = 0.3
+# Copiamos la lista para no mutar la original directamente si falla algo productos_actualizados = [p.copy() for p in productos]
+
+for producto in productos_actualizados: codigo_str = str(producto["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_aprendi
+)
+producto["Demanda"] = min(round(nueva_demanda), 10)
+
+resultados_msg.append(
+f"Pedido de {cantidad_pedida} {producto['Nombre']} OK. " f"Nuevo stock: {producto['Cantidad']}"
+)
+hubo_cambios = True else:
+resultados_msg.append(
+f"Stock insuficiente de {producto['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. Refactorizada para reducir complejidad (R0914, R0912).
+"""
+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": # 1. Procesar Formulario
+pedido_stock = _procesar_formulario_stock(request.form)
+
+if not pedido_stock:
+return jsonify({"message": "No se especificó ninguna cantidad válida."}), 40
+
+# 2. Actualizar Lógica de Negocio
+# W0612 Corregido: 'productos_dict' eliminado pues no se usaba
+productos_nuevos, mensajes, cambios = _actualizar_stock_inventario(productos, pe
+
+# 3. Persistencia 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)
+
+
+#
+
+
+# E1136 Corregido: Usar Tuple[list, list] en lugar de tuple[list, list]
+def _obtener_y_validar_pedido(productos: list, form_data: dict) -> Tuple[List, List]: """
+Función auxiliar para extraer y validar los datos del pedido del proveedor. """
+pedido_proveedor = []
+errores_validacion = []
+productos_dict = {str(p["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
+
+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
+
+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["Código"], "nombre": producto_info["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():
+"""Ruta para recopilar y procesar 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":
+# W0718 Corregido: Se captura KeyError/ValueError explícitamente si ocurren try:
+pedido_prov, errores = _obtener_y_validar_pedido(productos, request.form)
+
+if errores:
+return jsonify({"message": "Errores en el formulario.", "errors": errore
+
+if not pedido_prov:
+return jsonify({"message": "No se seleccionaron productos válidos."}), 4 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)
+
+
+
+#
+
+
+# E1136 Corregido: Usar Tuple[bytes, str]
+def _crear_pdf_pedido(pedido_proveedor: list) -> Tuple[bytes, str]: """Función auxiliar para generar el contenido binario del PDF.""" 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")
+
+# W0612 Corregido: Se eliminó 'line_height' que no se usaba 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= 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') 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():
+"""Ruta para generar 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,
+)
+
+# W0718 Corregido: Capturamos errores específicos de generación de PDF o I/O 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"))
+
+
+#
+
+
+@app.route("/export_json") def export_json():
+"""Ruta para exportar el inventario actual de la sesión 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():
+"""Ruta para exportar el inventario actual de la sesión 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ími
+
+output.seek(0)
+return send_file(
+io.BytesIO(output.getvalue().encode("utf-8")), mimetype="text/csv",
+as_attachment=True,
+download_name="inventario_exportado.csv",
+)
+
+# W0718 Corregido: Capturamos IOError/OSError para problemas de escritura en memoria 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")
+if name == " main ":
+app.run(debug=True, host="0.0.0.0")
+
From ecea7c50e43de3f7f038f9c043dac8ed24b1e187 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:28:48 -0400
Subject: [PATCH 119/138] Actualizar app.py
---
unidad_3/Flask/app.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index fb7419d..1186a74 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -10,7 +10,8 @@
import random import string import time
from typing import List, Tuple, Dict, Union, Any
-# Third-party libraries from flask import (
+# Third-party libraries from flask
+import (
Flask,
render_template, request,
redirect, url_for, flash,
From 8ca0d9fbe045036a4d50c7ac79744b6d03bcb2a8 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:49:17 -0400
Subject: [PATCH 120/138] Actualizar app.py
---
unidad_3/Flask/app.py | 898 ++++++++++++++++++++++--------------------
1 file changed, 461 insertions(+), 437 deletions(-)
diff --git a/unidad_3/Flask/app.py b/unidad_3/Flask/app.py
index 1186a74..199978d 100644
--- a/unidad_3/Flask/app.py
+++ b/unidad_3/Flask/app.py
@@ -1,457 +1,481 @@
-"""
-Aplicación Flask de Gestión de Inventario para Touken Café.
-
-Permite subir inventario CSV, crear inventario manualmente, realizar pedidos internos (stock) y generar pedidos a proveedor en formato PDF, con funciones de limpieza y exportación.
-"""
-
-# Standard library import csv
-import datetime import io
+import csv
+import datetime
+import io
import os
-import random import string import time
-from typing import List, Tuple, Dict, Union, Any
-
-# Third-party libraries from flask
-import (
-Flask,
-render_template, request,
-redirect, url_for, flash,
-session, jsonify, send_file,
+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 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"
-# Configuración básica de Flask app = Flask( name )
-# ¡Cambia esto por una clave secreta segura en producción!
-app.secret_key = os.environ.get("FLASK_SECRET_KEY", "secret-key-default-insecure") 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
-
-
-def generar_codigo_aleatorio(longitud: int = 6) -> str: """
-Genera un código alfanumérico aleatorio de la longitud especificada.
-
-Args:
-longitud: La longitud deseada para el código (por defecto es 6).
-
-Returns:
-Una cadena alfanumérica aleatoria.
-"""
-caracteres = string.ascii_uppercase + string.digits
-return "".join(random.choices(caracteres, k=longitud))
+if not os.path.exists(UPLOAD_FOLDER):
+ os.makedirs(UPLOAD_FOLDER)
+app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
-def limpiar_archivos_antiguos(dias: int = 1): """
-Elimina archivos de la carpeta 'uploads' que sean más antiguos que el número de días especificado.
+# ==============================
+# 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))
-Args:
-dias: Número de días de antigüedad para considerar un archivo como 'antiguo'.
-"""
-ahora = time.time()
-segundos_limite = dias * 24 * 3600 archivos_eliminados = 0
-print("Ejecutando limpieza de archivos antiguos...")
-folder_path = app.config["UPLOAD_FOLDER"] if os.path.exists(folder_path):
+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"]
-for nombre_archivo in os.listdir(folder_path):
-ruta_archivo = os.path.join(folder_path, nombre_archivo) try:
-if os.path.isfile(ruta_archivo):
-# Usar getmtime (tiempo de última modificación)
-timestamp_modificacion = os.path.getmtime(ruta_archivo) if (ahora - timestamp_modificacion) > segundos_limite:
-os.remove(ruta_archivo)
-print(f"Archivo antiguo eliminado: {ruta_archivo}") archivos_eliminados += 1
-except (OSError, PermissionError) as e:
-# Capturar errores del sistema operativo o permisos
-print(f"Error al procesar o eliminar {ruta_archivo}: {e}")
+ 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}")
-if archivos_eliminados > 0:
-print(f"Se eliminaron {archivos_eliminados} archivos antiguos de {folder_path}." else:
-print(f"No se encontraron archivos antiguos para eliminar en {folder_path}.")
+ print(f"Se eliminaron {archivos_eliminados} archivos antiguos.")
-# Ejecutar la limpieza al inicio de la aplicación (en contexto) with app.app_context():
-limpiar_archivos_antiguos(dias=1)
-
-# ========================================================================== # RUTAS DE LA APLICACIÓN
-# ==========================================================================
+with app.app_context():
+ limpiar_archivos_antiguos(dias=1)
def _guardar_inventario_csv(inventario_data: list, filename: str) -> str:
-"""Función auxiliar para guardar la lista de inventario en un archivo CSV.""" ruta_archivo_creado = os.path.join(app.config["UPLOAD_FOLDER"], filename)
-
-if (
-not inventario_data
-or not isinstance(inventario_data, list)
-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_creado, "w", newline="", encoding="utf-8") as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
-writer.writeheader()
-writer.writerows(inventario_data) return ruta_archivo_creado
-except KeyError as e:
-# W0707: Usar 'from e' para encadenar la excepción original
-raise KeyError(f"Error de clave al procesar inventario: falta la clave {e}") fro except (IOError, OSError) as e:
-# W0707: Usar 'from e' para encadenar la excepción original
-raise OSError(f"Error del sistema al guardar el archivo CSV: {e}") from e
-
-
-@app.route("/crear_inventario", methods=["GET", "POST"]) def crear_inventario():
-"""Ruta para mostrar la interfaz o procesar la creación manual del inventario.""" if request.method == "POST":
-if not request.is_json:
-flash("Método no soportado. Use la interfaz interactiva.", "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_cre
-),
-200,
-)
-except (KeyError, ValueError, OSError) as e:
-# Captura errores internos de la función auxiliar return (
-jsonify({"message": f"Error en los datos del inventario: {e}"}), 400,
-)
-
-# Método GET: Mostrar la página
-inventario_temporal = session.get("inventario_creado", [])
-return render_template("crear_inventario.html", inventario_creado=inventario_tempora
-
-
-#
-def _manejar_subida_csv(file):
-"""Función auxiliar para validar y procesar el archivo CSV subido."""
-if not file.filename.lower().endswith(".csv") or file.content_type != "text/csv": return "Por favor, sube un archivo CSV válido (.csv)."
-
-filename = file.filename
-filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename)
-
-try:
-file.save(filepath)
-data = pd.read_csv(filepath) 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):
-raise ValueError(f"El CSV debe contener las columnas: {', '.join(columnas_es
-
-data.fillna("", inplace=True)
-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}") return None # Indica éxito
-
-except EmptyDataError:
-return "El archivo CSV está vacío." except ParserError:
-return "Error al procesar el archivo CSV. Verifique el formato." except ValueError as e:
-return str(e)
-# W0718: Capturamos errores generales de I/O o Pandas que no sean los anteriores. except (OSError, RuntimeError) as e:
-return f"Ocurrió un error inesperado al procesar el archivo: {e}" finally:
-# Asegurarse de limpiar archivos inválidos o fallidos
-if os.path.exists(filepath) and "productos" not in session: try:
-os.remove(filepath)
-print(f"Archivo fallido eliminado: {filepath}") except OSError:
-pass
-
-
-
-def _responder_upload(mensaje: str, status_code: int, es_error: bool = False): """
-Auxiliar para R0911: Centraliza la respuesta de upload_csv. """
-if request.is_json:
-return jsonify({"message": mensaje}), status_code
-
-if es_error:
-flash(mensaje, "error") # Asumiendo que la plantilla soporta categorías else:
-flash(mensaje)
-
-return redirect(request.url if es_error else url_for("upload_csv"))
-
-
-@app.route("/", methods=["POST", "GET"]) def upload_csv():
-"""Ruta de inicio para subir CSV de inventario."""
-# R0911 Corregido: Lógica simplificada delegando respuestas if request.method == "POST":
-if "file" not in request.files or request.files["file"].filename == "":
-return _responder_upload("No se seleccionó ningún archivo.", 400, es_error=T error_message = _manejar_subida_csv(request.files["file"])
-if error_message:
-return _responder_upload(error_message, 400, es_error=True)
-
-# Éxito
-return _responder_upload("Archivo CSV cargado con éxito.", 200, es_error=False)
-
-# Método GET
-return render_template("subir_csv.html")
-
-
-#
-
-def _procesar_formulario_stock(form_data: dict) -> Dict[str, int]: """
-Auxiliar para R0914: 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:
-# Ignoramos valores no numéricos silenciosamente o logueamos pass
-return pedido_stock
+ """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]: """
-Auxiliar para R0914/R0912: Aplica la lógica de negocio para actualizar el stock. """
-resultados_msg = [] hubo_cambios = False
-factor_aprendizaje = 0.3
-# Copiamos la lista para no mutar la original directamente si falla algo productos_actualizados = [p.copy() for p in productos]
-
-for producto in productos_actualizados: codigo_str = str(producto["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_aprendi
-)
-producto["Demanda"] = min(round(nueva_demanda), 10)
-
-resultados_msg.append(
-f"Pedido de {cantidad_pedida} {producto['Nombre']} OK. " f"Nuevo stock: {producto['Cantidad']}"
-)
-hubo_cambios = True else:
-resultados_msg.append(
-f"Stock insuficiente de {producto['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. Refactorizada para reducir complejidad (R0914, R0912).
-"""
-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": # 1. Procesar Formulario
-pedido_stock = _procesar_formulario_stock(request.form)
-
-if not pedido_stock:
-return jsonify({"message": "No se especificó ninguna cantidad válida."}), 40
-
-# 2. Actualizar Lógica de Negocio
-# W0612 Corregido: 'productos_dict' eliminado pues no se usaba
-productos_nuevos, mensajes, cambios = _actualizar_stock_inventario(productos, pe
-
-# 3. Persistencia 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)
-
-
-#
-
-
-# E1136 Corregido: Usar Tuple[list, list] en lugar de tuple[list, list]
-def _obtener_y_validar_pedido(productos: list, form_data: dict) -> Tuple[List, List]: """
-Función auxiliar para extraer y validar los datos del pedido del proveedor. """
-pedido_proveedor = []
-errores_validacion = []
-productos_dict = {str(p["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
-
-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
-
-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["Código"], "nombre": producto_info["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():
-"""Ruta para recopilar y procesar 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":
-# W0718 Corregido: Se captura KeyError/ValueError explícitamente si ocurren try:
-pedido_prov, errores = _obtener_y_validar_pedido(productos, request.form)
-
-if errores:
-return jsonify({"message": "Errores en el formulario.", "errors": errore
-
-if not pedido_prov:
-return jsonify({"message": "No se seleccionaron productos válidos."}), 4 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)
-
-
-
-#
-
-
-# E1136 Corregido: Usar Tuple[bytes, str]
-def _crear_pdf_pedido(pedido_proveedor: list) -> Tuple[bytes, str]: """Función auxiliar para generar el contenido binario del PDF.""" 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")
-
-# W0612 Corregido: Se eliminó 'line_height' que no se usaba 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= 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') 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():
-"""Ruta para generar 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,
-)
-
-# W0718 Corregido: Capturamos errores específicos de generación de PDF o I/O 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"))
-
-
-#
-
-
-@app.route("/export_json") def export_json():
-"""Ruta para exportar el inventario actual de la sesión 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():
-"""Ruta para exportar el inventario actual de la sesión 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ími
-
-output.seek(0)
-return send_file(
-io.BytesIO(output.getvalue().encode("utf-8")), mimetype="text/csv",
-as_attachment=True,
-download_name="inventario_exportado.csv",
-)
-
-# W0718 Corregido: Capturamos IOError/OSError para problemas de escritura en memoria 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"))
-
-
+ 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")
-
+# ==============================
+if __name__ == "__main__":
+ app.run(debug=True, host="0.0.0.0")
\ No newline at end of file
From 814e5dc5ce46b56b4a6f1fd16628e3cdabe9fc6c Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Sun, 23 Nov 2025 16:58:25 -0400
Subject: [PATCH 121/138] Actualizar inventario_manager.py
---
.../src/services/inventario_manager.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/unidad_7/pystock_management/src/services/inventario_manager.py b/unidad_7/pystock_management/src/services/inventario_manager.py
index fa2ae0c..bf7c099 100644
--- a/unidad_7/pystock_management/src/services/inventario_manager.py
+++ b/unidad_7/pystock_management/src/services/inventario_manager.py
@@ -1,9 +1,5 @@
# pylint: disable=R0801
-"""
-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
-"""
+
import os
import json
@@ -11,6 +7,11 @@
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:
"""
From db5c93af36fd1a8d7cddc500bd38c867b399fad6 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:43:05 -0400
Subject: [PATCH 122/138] Add files via upload
---
Curso-CPP/If-matason-fly.md | 74 ++++++++++++++
"Curso-CPP/Jerarqu\303\255a_persona.md" | 117 ++++++++++++++++++++++
Curso-CPP/POO.md | 105 ++++++++++++++++++++
Curso-CPP/Registro_arch.md | 64 ++++++++++++
Curso-CPP/Tareas completa.md | 97 +++++++++++++++++++
Curso-CPP/banco-cuenta.md | 85 ++++++++++++++++
Curso-CPP/pil_col.md | 123 ++++++++++++++++++++++++
Curso-CPP/tempila.md | 62 ++++++++++++
Curso-CPP/teor_pil.md | 116 ++++++++++++++++++++++
9 files changed, 843 insertions(+)
create mode 100644 Curso-CPP/If-matason-fly.md
create mode 100644 "Curso-CPP/Jerarqu\303\255a_persona.md"
create mode 100644 Curso-CPP/POO.md
create mode 100644 Curso-CPP/Registro_arch.md
create mode 100644 Curso-CPP/Tareas completa.md
create mode 100644 Curso-CPP/banco-cuenta.md
create mode 100644 Curso-CPP/pil_col.md
create mode 100644 Curso-CPP/tempila.md
create mode 100644 Curso-CPP/teor_pil.md
diff --git a/Curso-CPP/If-matason-fly.md b/Curso-CPP/If-matason-fly.md
new file mode 100644
index 0000000..fb57e5d
--- /dev/null
+++ b/Curso-CPP/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/Jerarqu\303\255a_persona.md" "b/Curso-CPP/Jerarqu\303\255a_persona.md"
new file mode 100644
index 0000000..60a04e6
--- /dev/null
+++ "b/Curso-CPP/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.md b/Curso-CPP/POO.md
new file mode 100644
index 0000000..d1aed6b
--- /dev/null
+++ b/Curso-CPP/POO.md
@@ -0,0 +1,105 @@
+# 🔐 Sistema de Autenticación y Utilidades en C++
+
+
+
+
+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/Registro_arch.md b/Curso-CPP/Registro_arch.md
new file mode 100644
index 0000000..bce3d3c
--- /dev/null
+++ b/Curso-CPP/Registro_arch.md
@@ -0,0 +1,64 @@
+# 🗂️ Sistema de Registro y Calculadora Temporal en C++
+
+
+
+
+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/Tareas completa.md b/Curso-CPP/Tareas completa.md
new file mode 100644
index 0000000..09788dc
--- /dev/null
+++ b/Curso-CPP/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/banco-cuenta.md b/Curso-CPP/banco-cuenta.md
new file mode 100644
index 0000000..d60368d
--- /dev/null
+++ b/Curso-CPP/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/pil_col.md b/Curso-CPP/pil_col.md
new file mode 100644
index 0000000..9a9e977
--- /dev/null
+++ b/Curso-CPP/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/tempila.md b/Curso-CPP/tempila.md
new file mode 100644
index 0000000..f49361e
--- /dev/null
+++ b/Curso-CPP/tempila.md
@@ -0,0 +1,62 @@
+# 📚 Gestor de Cursos con Pila Dinámica (Stack) en C++
+
+
+-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/teor_pil.md b/Curso-CPP/teor_pil.md
new file mode 100644
index 0000000..9c62698
--- /dev/null
+++ b/Curso-CPP/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.
From 1a9c83a4802a958b26f16d2a985dd0f3abd5bdbb Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:45:24 -0400
Subject: [PATCH 123/138] Delete Curso-CPP/20241019-prom.cpp
---
Curso-CPP/20241019-prom.cpp | 52 -------------------------------------
1 file changed, 52 deletions(-)
delete mode 100644 Curso-CPP/20241019-prom.cpp
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 "<
Date: Mon, 24 Nov 2025 11:45:53 -0400
Subject: [PATCH 124/138] Delete Curso-CPP/20241019-Base.cpp
---
Curso-CPP/20241019-Base.cpp | 142 ------------------------------------
1 file changed, 142 deletions(-)
delete mode 100644 Curso-CPP/20241019-Base.cpp
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;
-}
From fae6ab81947262eb4abd444c68ba78c3a61e6b20 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:46:45 -0400
Subject: [PATCH 125/138] Delete Curso-CPP/teor_pil.cpp
---
Curso-CPP/teor_pil.cpp | 113 -----------------------------------------
1 file changed, 113 deletions(-)
delete mode 100644 Curso-CPP/teor_pil.cpp
diff --git a/Curso-CPP/teor_pil.cpp b/Curso-CPP/teor_pil.cpp
deleted file mode 100644
index 57cd007..0000000
--- a/Curso-CPP/teor_pil.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include
-#include
-#include
-using namespace std;
-
-struct per {
- string nom;
- string ap;
- int age;
- float alt;
- int numper;
- int numfact;
-};
-
-struct Cont {
- per per;
- Cont *foll;
-};
-
-per pipol;
-Cont *phill = NULL;
-
-// Crear pila
-void sozoPhill(Cont *&phill, per per) {
- Cont *novo_dono = new Cont();
- novo_dono->per = per; // Rellenar dato
- novo_dono->foll = phill;
- phill = novo_dono;
- cout << "Datos cargados" << endl;
-}
-
-// Sacar pila
-void hakaiPhill(Cont *&phill, per &per, string filename) {
- char r;
- if (phill == NULL) {
- cout << "La pila está vacía." << endl;
- return;
- }
- fstream arch;
- arch.open(filename.c_str(), ios::out);
- if (arch.fail()) {
- cout << "* ERROR GENERANDO ARCHIVO *" << endl;
- return;
- }
-
- while (phill != NULL) {
- cout << "Nombre: " << phill->per.nom << " " << phill->per.ap << endl;
- cout << "Apellido: " << phill->per.ap << endl;
- cout << "Edad: " << phill->per.age << endl;
- cout << "Altura: " << phill->per.alt << endl;
- cout << "Numero asignado: " << phill->per.numper << endl;
- cout << "Numero factorial: " << phill->per.numfact << endl;
-
- // Guardar en archivo
- arch << "Nombre: " << phill->per.nom << endl;
- arch << "Apellido: " << phill->per.ap << endl;
- arch << "Edad: " << phill->per.age << endl;
- arch << "Altura: " << phill->per.alt << "m" << endl;
- arch << "Numero asignado: " << phill->per.numper << endl;
- arch << "Numero factorial: " << phill->per.numfact << endl;
-
- Cont *axil = phill;
- phill = phill->foll;
- delete axil;
-
- cout << "¿Sacar siguiente dato? (s/n)" << endl;
- cin >> r;
- if (r == 'n') {
- break;
- }
- }
- arch.close();
-}
-
-// Función para calcular el factorial
-int facto(int n) {
- if (n < 0) return -1;
- int fact = 1;
- for (int a = 1; a <= n; a++) {
- fact *= a;
- }
- return fact;
-}
-
-int main() {
- char res;
- do {
- cout << "Ingrese nombre de la persona: " << endl;
- cin >> pipol.nom;
- cout << "Ingrese apellido de la persona: " << endl;
- cin >> pipol.ap;
- cout << "Ingrese edad de la persona: " << endl;
- cin >> pipol.age;
- cout << "Ingrese altura de la persona: " << endl;
- cin >> pipol.alt;
- cout << "Ingrese numero para la persona: " << endl;
- cin >> pipol.numper;
- pipol.numfact = facto(pipol.numper);
-
- sozoPhill(phill, pipol);
- cout << "Debe ingresar más datos? (s/n)" << endl;
- cin >> res;
- system("cls");
- } while (res == 's');
-
- string filename;
- string ext = ".txt";
- cout << "Ingrese el nombre con el que desea guardar el archivo: " << endl;
- cin >> filename;
- filename += ext;
- hakaiPhill(phill, pipol, filename);
- return 0;
-}
From 8437a95a88c5b66f3bbbabcce8cac6574fab3c42 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:47:04 -0400
Subject: [PATCH 126/138] Delete Curso-CPP/tempila.cpp
---
Curso-CPP/tempila.cpp | 132 ------------------------------------------
1 file changed, 132 deletions(-)
delete mode 100644 Curso-CPP/tempila.cpp
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();
-}
-
From 5058598c13a4f3fc4d9571ecc4374e7f0b5e4559 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:47:28 -0400
Subject: [PATCH 127/138] Delete Curso-CPP/pil_col.cpp
---
Curso-CPP/pil_col.cpp | 186 ------------------------------------------
1 file changed, 186 deletions(-)
delete mode 100644 Curso-CPP/pil_col.cpp
diff --git a/Curso-CPP/pil_col.cpp b/Curso-CPP/pil_col.cpp
deleted file mode 100644
index 6ed23a8..0000000
--- a/Curso-CPP/pil_col.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-#include
-#include
-using namespace std;
-
-struct per {
- string nom;
- string ap;
- int age;
- string contry;
-};
-
-struct estudiante {
- per pipol;
- string cur;
- float prom;
-};
-
-struct Cont {
- estudiante stu;
- Cont *foll;
-};
-
-float not_1, not_2, not_3;
-estudiante stu;
-Cont *phill = NULL;
-Cont *front = NULL;
-Cont *end = NULL;
-
-bool aprob(estudiante stu) {
- return stu.prom >= 10;
-}
-
-float prom(float not1, float not2, float not3) {
- return (not1 * 0.3) + (not2 * 0.3) + (not3 * 0.4);
-}
-
-void sozoPhill(Cont *&phill, estudiante stu) {
- Cont *novo_dono = new Cont();
- novo_dono->stu = stu;
- novo_dono->foll = phill;
- phill = novo_dono;
-}
-
-void hakaiPhill(Cont *&phill, estudiante &stutu) {
-if (phill == NULL) {
- cout << "La pila está vacía." << endl;
- return;
- }
- cout << "Estudiantes en la pila:" << endl;
- while (phill != NULL) {
- cout << "Nombre y Apellido: " << phill->stu.pipol.nom << " " << phill->stu.pipol.ap << endl;
- cout << "Edad: " << phill->stu.pipol.age << endl;
- cout << "Nacionalidad: " << phill->stu.pipol.contry << endl;
- cout << "Curso: " << phill->stu.cur << endl;
- cout << "Promedio: " << phill->stu.prom << " - " << (aprob(phill->stu) ? "Aprobado" : "Reprobado") << endl;
- Cont *axil = phill;
- stutu= axil->stu;
- phill = phill->foll;
- delete axil;
- }
-}
-
-bool VoidCoil(Cont *front) {
- return front == NULL;
-}
-
-void sozoCoil(Cont *&front, Cont *&end, estudiante 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;
-}
-
-void hakaiCoil(Cont *&front, Cont *&end, estudiante &stutu) {
- if (VoidCoil(front)) {
- cout << "La cola está vacía." << endl;
- return;
- }
- cout << "Estudiantes en la cola:" << endl;
- while (!VoidCoil(front)) {
- cout << "Nombre y Apellido: " << front->stu.pipol.nom << " " << front->stu.pipol.ap << endl;
- cout << "Edad: " << front->stu.pipol.age << endl;
- cout << "Nacionalidad: " << front->stu.pipol.contry << endl;
- cout << "Curso: " << front->stu.cur << endl;
- cout << "Promedio: " << front->stu.prom << " - " << (aprob(front->stu) ? "Aprobado" : "Reprobado") << endl;
- Cont *axil = front;
- stutu= axil->stu;
- front = front->foll;
- if (front == NULL) {
- end = NULL;
- }
- delete axil;
- }
-}
-
-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 << "Ingrese nombre de la persona: " << endl;
- cin >> stu.pipol.nom;
- cout << "Ingrese apellido de la persona: " << endl;
- cin >> stu.pipol.ap;
- cout << "Ingrese nacionalidad de la persona: " << endl;
- cin >> stu.pipol.contry;
- cout << "Ingrese edad de la persona: " << endl;
- cin >> stu.pipol.age;
- cout << "Ingrese el curso del estudiante: " << endl;
- cin >> stu.cur;
- cout << "Ingrese la nota 1: " << endl;
- cin >> not_1;
- cout << "Ingrese nota 2: " << endl;
- cin >> not_2;
- cout << "Ingrese nota 3: " << endl;
- cin >> not_3;
- stu.prom = prom(not_1, not_2, not_3);
- if (aprob(stu)) {
- cout << "El estudiante aprobo" << endl;
- } else {
- cout << "El estudiante reprobo" << endl;
- }
- sozoPhill(phill, stu);
- cout << "¿Ingresar mas datos? (s/n): " << endl;
- cin >> opc;
- system ("cls");
- } while (opc == 's');
- hakaiPhill(phill, stu);
- break;
- case 2:
- do {
- cout << "Ingrese nombre de la persona: " << endl;
- cin >> stu.pipol.nom;
- cout << "Ingrese apellido de la persona: " << endl;
- cin >> stu.pipol.ap;
- cout << "Ingrese nacionalidad de la persona: " << endl;
- cin >> stu.pipol.contry;
- cout << "Ingrese edad de la persona: " << endl;
- cin >> stu.pipol.age;
- cout << "Ingrese el curso del estudiante: " << endl;
- cin >> stu.cur;
- cout << "Ingrese la nota 1: " << endl;
- cin >> not_1;
- cout << "Ingrese nota 2: " << endl;
- cin >> not_2;
- cout << "Ingrese nota 3: " << endl;
- cin >> not_3;
- stu.prom = prom(not_1, not_2, not_3);
- if (aprob(stu)) {
- cout << "El estudiante aprobo" << endl;
- } else {
- cout << "El estudiante reprobo" << endl;
- }
- sozoCoil(front, end, stu);
- cout << "¿Ingresar mas datos? (s/n): " << endl;
- cin >> opc;
- system ("cls");
- } while (opc == 's');
- hakaiCoil(front, end, stu);
- break;
- case 3:
- cout << "Saliendo..." << endl;
- return 0;
- default:
- cout << "Opción no válida. Intente de nuevo." << endl;
- break;
- }
- cout << "¿Desea regresar al menú principal? (s/n): " << endl;
- cin >> res;
- system ("cls");
- } while (res == 's');
- return 0;
-}
-
From cba5b9eb9c2882cc8f620685717ad864e4f4d956 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:48:08 -0400
Subject: [PATCH 128/138] Delete Curso-CPP/banco-cuenta.cpp
---
Curso-CPP/banco-cuenta.cpp | 71 --------------------------------------
1 file changed, 71 deletions(-)
delete mode 100644 Curso-CPP/banco-cuenta.cpp
diff --git a/Curso-CPP/banco-cuenta.cpp b/Curso-CPP/banco-cuenta.cpp
deleted file mode 100644
index 6b1c2d9..0000000
--- a/Curso-CPP/banco-cuenta.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include
-#include
-using namespace std;
-
-// Definición de la clase CuentaBancaria
-class CuentaBancaria {
-private:
- string titular;
- string numeroCuenta;
- double saldo;
-
-public:
- // Constructor
- CuentaBancaria(string titular, string numero, double saldoInicial) {
- this->titular = titular;
- this->numeroCuenta = numero;
- this->saldo = saldoInicial;
- }
-
- // Método para depositar dinero
- void depositar(double monto) {
- if (monto > 0) {
- saldo += monto;
- cout << "Depósito exitoso. Nuevo saldo: $" << saldo << endl;
- } else {
- cout << "El monto debe ser mayor a 0." << endl;
- }
- }
-
- // Método para retirar dinero
- void retirar(double monto) {
- if (monto > 0 && monto <= saldo) {
- saldo -= monto;
- cout << "Retiro exitoso. Nuevo saldo: $" << saldo << endl;
- } else {
- cout << "Fondos insuficientes o monto inválido." << endl;
- }
- }
-
- // Método para mostrar información de la cuenta
- void mostrarInformacionCuenta() {
- cout << "Titular de la cuenta: " << titular << endl;
- cout << "Número de cuenta: " << numeroCuenta << endl;
- cout << "Saldo: $" << saldo << endl;
- }
-};
-
-int main() {
- // Crear una cuenta bancaria
- CuentaBancaria miCuenta("Juan Pérez", "123456789", 1000.00);
-
- // Mostrar información inicial de la cuenta
- cout << "Información inicial de la cuenta:" << endl;
- miCuenta.mostrarInformacionCuenta();
-
- // Realizar operaciones
- cout << "\nRealizando un depósito de $500..." << endl;
- miCuenta.depositar(500);
-
- cout << "\nIntentando retirar $2000..." << endl;
- miCuenta.retirar(2000);
-
- cout << "\nRetirando $300..." << endl;
- miCuenta.retirar(300);
-
- // Mostrar información final de la cuenta
- cout << "\nInformación final de la cuenta:" << endl;
- miCuenta.mostrarInformacionCuenta();
-
- return 0;
-}
\ No newline at end of file
From 1d1378d11edf6a10fce31b2a8ba358626c26c5d8 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:48:57 -0400
Subject: [PATCH 129/138] Delete Curso-CPP/Tareas completa.cpp
---
Curso-CPP/Tareas completa.cpp | 135 ----------------------------------
1 file changed, 135 deletions(-)
delete mode 100644 Curso-CPP/Tareas completa.cpp
diff --git a/Curso-CPP/Tareas completa.cpp b/Curso-CPP/Tareas completa.cpp
deleted file mode 100644
index b6e36ff..0000000
--- a/Curso-CPP/Tareas completa.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-using namespace std;
-
-struct Tarea {
- string descripcion;
- bool urgente; // true si es urgente, false si es normal
-};
-
-// Funciones
-void agregarTarea(stack& pila, queue& cola);
-void mostrarTareas(const stack& pila, const queue& cola);
-void guardarTareas(const stack& pila, const queue& cola);
-void cargarTareas(stack& pila, queue& cola);
-
-int main() {
- stack pilaTareas;
- queue colaTareas;
- cargarTareas(pilaTareas, colaTareas);
-
- int opcion;
- do {
- cout << "1. Agregar tarea\n";
- cout << "2. Mostrar tareas\n";
- cout << "3. Guardar tareas\n";
- cout << "4. Salir\n";
- cout << "Seleccione una opción: ";
- cin >> opcion;
-
- switch (opcion) {
- case 1:
- agregarTarea(pilaTareas, colaTareas);
- break;
- case 2:
- mostrarTareas(pilaTareas, colaTareas);
- break;
- case 3:
- guardarTareas(pilaTareas, colaTareas);
- 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 agregarTarea(stack& pila, queue& cola) {
- Tarea nuevaTarea;
- cout << "Descripción de la tarea: ";
- cin.ignore(); // Limpiar el buffer
- getline(cin, nuevaTarea.descripcion);
- cout << "¿Es urgente? (1: Sí, 0: No): ";
- cin >> nuevaTarea.urgente;
-
- if (nuevaTarea.urgente) {
- pila.push(nuevaTarea);
- } else {
- cola.push(nuevaTarea);
- }
-}
-
-void mostrarTareas(const stack& pila, const queue& cola) {
- cout << "\nTareas Urgentes:\n";
- stack pilaTemporal = pila; // Copia de la pila para mostrar
- while (!pilaTemporal.empty()) {
- cout << "- " << pilaTemporal.top().descripcion << endl;
- pilaTemporal.pop();
- }
-
- cout << "\nTareas Normales:\n";
- queue colaTemporal = cola; // Copia de la cola para mostrar
- while (!colaTemporal.empty()) {
- cout << "- " << colaTemporal.front().descripcion << endl;
- colaTemporal.pop();
- }
- cout << endl;
-}
-
-void guardarTareas(const stack& pila, const queue& cola) {
- ofstream archivo("tareas.txt");
- if (!archivo) {
- cerr << "Error al abrir el archivo para guardar.\n";
- return;
- }
-
- // Guardar tareas urgentes
- stack pilaTemporal = pila;
- while (!pilaTemporal.empty()) {
- archivo << "1 " << pilaTemporal.top().descripcion << endl; // 1 para urgente
- pilaTemporal.pop();
- }
-
- // Guardar tareas normales
- queue colaTemporal = cola;
- while (!colaTemporal.empty()) {
- archivo << "0 " << colaTemporal.front().descripcion << endl; // 0 para normal
- colaTemporal.pop();
- }
-
- archivo.close();
- cout << "Tareas guardadas exitosamente.\n";
-}
-
-void cargarTareas(stack& pila, queue& cola) {
- ifstream archivo("tareas.txt");
- if (!archivo) {
- cerr << "No se pudo abrir el archivo para cargar.\n";
- return;
- }
-
- Tarea tarea;
- int tipo;
- while (archivo >> tipo) {
- archivo.ignore(); // Limpiar el buffer
- getline(archivo, tarea.descripcion);
- if (tipo == 1) {
- tarea.urgente = true;
- pila.push(tarea); // Agregar a la pila si es urgente
- } else {
- tarea.urgente = false;
- cola.push(tarea); // Agregar a la cola si es normal
- }
- }
-
- archivo.close();
- cout << "Tareas cargadas exitosamente.\n";
-}
\ No newline at end of file
From 57aae3b0ec5522ac05d46d6702674a0cf29647bd Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:49:13 -0400
Subject: [PATCH 130/138] =?UTF-8?q?Delete=20Curso-CPP/Jerarqu=C3=ADa=5Fper?=
=?UTF-8?q?sona.cpp?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
"Curso-CPP/Jerarqu\303\255a_persona.cpp" | 204 -----------------------
1 file changed, 204 deletions(-)
delete mode 100644 "Curso-CPP/Jerarqu\303\255a_persona.cpp"
diff --git "a/Curso-CPP/Jerarqu\303\255a_persona.cpp" "b/Curso-CPP/Jerarqu\303\255a_persona.cpp"
deleted file mode 100644
index 37f1892..0000000
--- "a/Curso-CPP/Jerarqu\303\255a_persona.cpp"
+++ /dev/null
@@ -1,204 +0,0 @@
-#include
-using namespace std;
-
-//Clases
-class persona{
- private:
- string nom,ap;
- int ci, age;
- public:
- persona ();
- ~persona();
- void ter_setpersona (string, string, int, int);
- string ter_getnom ();
- string ter_getap ();
- int ter_getci ();
- int ter_getage ();
- virtual void show_pipol();
-};
-
-class trabajador : public persona {
- private:
- string puesto;
- float salario;
- public:
- trabajador();
- ~trabajador();
- void ter_setjob(string, string, int, int, string, float);
- string ter_getpuesto();
- float ter_getsalario();
- void show_job();
-};
-
-class stu : public persona{
- private:
- string curso;
- float nota1, nota2, nota3, prom;
- public:
- stu();
- ~stu();
- float set_promedio(float,float,float);
- void ter_setestu(string, string, int, int, string, float, float, float);
- string ter_getcuerso();
- float ter_getprom();
- void show_estu();
-};
-
-class uni : public stu{
- private:
- string carear;
- string title;
- public:
- uni();
- ~uni();
- void ter_setuni(string, string, int, int, string, float, float, float, string, string);
- string ter_getcarear();
- string ter_gettitle();
- void show_unity();
-
-};
-
-//Extras
-persona :: persona(){}
-
-persona :: ~persona (){}
-
-trabajador::trabajador(){}
-
-trabajador::~trabajador(){}
-
-stu::stu(){}
-
-stu::~stu(){}
-
-uni::uni(){}
-
-uni::~uni(){}
-
-float stu::set_promedio(float not1, float not2, float not3){
- prom= (not1 + not2 + not3)/3;
- return prom;
-}
-
-//Polimorfismo
-
-void persona::show_pipol(){
- cout << "Nombre: " << nom <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");
- emp->show_job();
- estudiante->show_estu();
- estUni->show_unity();
-
- // Liberar memoria
- delete emp;
- delete estudiante;
- delete estUni;
-
- return 0;
-}
From 45659a01f295acedb60de8d87a52e990469789ea Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:50:06 -0400
Subject: [PATCH 131/138] Delete Curso-CPP/If-matason-fly.cpp
---
Curso-CPP/If-matason-fly.cpp | 131 -----------------------------------
1 file changed, 131 deletions(-)
delete mode 100644 Curso-CPP/If-matason-fly.cpp
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;
-}
From 5efadab164925dd59d4f17f951d6e05b01e32aae Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:50:35 -0400
Subject: [PATCH 132/138] Delete Curso-CPP/POO.cpp
---
Curso-CPP/POO.cpp | 77 -----------------------------------------------
1 file changed, 77 deletions(-)
delete mode 100644 Curso-CPP/POO.cpp
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;
-}
From d36599ce49cac0bc88e364f9696eb3a138763ffa Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:50:48 -0400
Subject: [PATCH 133/138] Delete Curso-CPP/Registro_arch.cpp
---
Curso-CPP/Registro_arch.cpp | 59 -------------------------------------
1 file changed, 59 deletions(-)
delete mode 100644 Curso-CPP/Registro_arch.cpp
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;
-}
-
From b905905af5bdab33726fed22cbb25bf3a546deb8 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:52:40 -0400
Subject: [PATCH 134/138] Delete Curso-CPP/Code.cpp
---
Curso-CPP/Code.cpp | 84 ----------------------------------------------
1 file changed, 84 deletions(-)
delete mode 100644 Curso-CPP/Code.cpp
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();
-}
From a1ec6015b8ff1e85f1c5bf5e51b601163c10ce1a Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:53:07 -0400
Subject: [PATCH 135/138] Delete Curso-CPP/Copia prueba.cpp
---
Curso-CPP/Copia prueba.cpp | 118 -------------------------------------
1 file changed, 118 deletions(-)
delete mode 100644 Curso-CPP/Copia prueba.cpp
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
From 0ea2bbd1900f82b87b31f2e6c95e1e73954b76ef Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:54:44 -0400
Subject: [PATCH 136/138] Delete Curso-CPP/codelab.cpp
---
Curso-CPP/codelab.cpp | 107 ------------------------------------------
1 file changed, 107 deletions(-)
delete mode 100644 Curso-CPP/codelab.cpp
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;
-}
From eed175fb23423bdb5d0ce3a4a5da2a6a001744d2 Mon Sep 17 00:00:00 2001
From: WolveJC <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 11:58:44 -0400
Subject: [PATCH 137/138] Add files via upload
---
Curso-CPP/Chuleta_Base.cpp | 227 +++++++++++++++++++++++
Curso-CPP/If-matason-fly.cpp | 76 ++++++++
"Curso-CPP/Jerarqu\303\255a_persona.cpp" | 204 ++++++++++++++++++++
Curso-CPP/POO.cpp | 188 +++++++++++++++++++
Curso-CPP/Registro_arch.cpp | 96 ++++++++++
Curso-CPP/Tareas completa.cpp | 135 ++++++++++++++
Curso-CPP/banco-cuenta.cpp | 71 +++++++
Curso-CPP/pil_col.cpp | 186 +++++++++++++++++++
Curso-CPP/tempila.cpp | 105 +++++++++++
Curso-CPP/teor_pil.cpp | 113 +++++++++++
10 files changed, 1401 insertions(+)
create mode 100644 Curso-CPP/Chuleta_Base.cpp
create mode 100644 Curso-CPP/If-matason-fly.cpp
create mode 100644 "Curso-CPP/Jerarqu\303\255a_persona.cpp"
create mode 100644 Curso-CPP/POO.cpp
create mode 100644 Curso-CPP/Registro_arch.cpp
create mode 100644 Curso-CPP/Tareas completa.cpp
create mode 100644 Curso-CPP/banco-cuenta.cpp
create mode 100644 Curso-CPP/pil_col.cpp
create mode 100644 Curso-CPP/tempila.cpp
create mode 100644 Curso-CPP/teor_pil.cpp
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/If-matason-fly.cpp b/Curso-CPP/If-matason-fly.cpp
new file mode 100644
index 0000000..75c075f
--- /dev/null
+++ b/Curso-CPP/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/Jerarqu\303\255a_persona.cpp" "b/Curso-CPP/Jerarqu\303\255a_persona.cpp"
new file mode 100644
index 0000000..37f1892
--- /dev/null
+++ "b/Curso-CPP/Jerarqu\303\255a_persona.cpp"
@@ -0,0 +1,204 @@
+#include
+using namespace std;
+
+//Clases
+class persona{
+ private:
+ string nom,ap;
+ int ci, age;
+ public:
+ persona ();
+ ~persona();
+ void ter_setpersona (string, string, int, int);
+ string ter_getnom ();
+ string ter_getap ();
+ int ter_getci ();
+ int ter_getage ();
+ virtual void show_pipol();
+};
+
+class trabajador : public persona {
+ private:
+ string puesto;
+ float salario;
+ public:
+ trabajador();
+ ~trabajador();
+ void ter_setjob(string, string, int, int, string, float);
+ string ter_getpuesto();
+ float ter_getsalario();
+ void show_job();
+};
+
+class stu : public persona{
+ private:
+ string curso;
+ float nota1, nota2, nota3, prom;
+ public:
+ stu();
+ ~stu();
+ float set_promedio(float,float,float);
+ void ter_setestu(string, string, int, int, string, float, float, float);
+ string ter_getcuerso();
+ float ter_getprom();
+ void show_estu();
+};
+
+class uni : public stu{
+ private:
+ string carear;
+ string title;
+ public:
+ uni();
+ ~uni();
+ void ter_setuni(string, string, int, int, string, float, float, float, string, string);
+ string ter_getcarear();
+ string ter_gettitle();
+ void show_unity();
+
+};
+
+//Extras
+persona :: persona(){}
+
+persona :: ~persona (){}
+
+trabajador::trabajador(){}
+
+trabajador::~trabajador(){}
+
+stu::stu(){}
+
+stu::~stu(){}
+
+uni::uni(){}
+
+uni::~uni(){}
+
+float stu::set_promedio(float not1, float not2, float not3){
+ prom= (not1 + not2 + not3)/3;
+ return prom;
+}
+
+//Polimorfismo
+
+void persona::show_pipol(){
+ cout << "Nombre: " << nom <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");
+ emp->show_job();
+ estudiante->show_estu();
+ estUni->show_unity();
+
+ // Liberar memoria
+ delete emp;
+ delete estudiante;
+ delete estUni;
+
+ return 0;
+}
diff --git a/Curso-CPP/POO.cpp b/Curso-CPP/POO.cpp
new file mode 100644
index 0000000..3dbb62c
--- /dev/null
+++ b/Curso-CPP/POO.cpp
@@ -0,0 +1,188 @@
+#include
+#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/Registro_arch.cpp b/Curso-CPP/Registro_arch.cpp
new file mode 100644
index 0000000..43c366e
--- /dev/null
+++ b/Curso-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/Tareas completa.cpp b/Curso-CPP/Tareas completa.cpp
new file mode 100644
index 0000000..b6e36ff
--- /dev/null
+++ b/Curso-CPP/Tareas completa.cpp
@@ -0,0 +1,135 @@
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+struct Tarea {
+ string descripcion;
+ bool urgente; // true si es urgente, false si es normal
+};
+
+// Funciones
+void agregarTarea(stack& pila, queue& cola);
+void mostrarTareas(const stack& pila, const queue& cola);
+void guardarTareas(const stack& pila, const queue& cola);
+void cargarTareas(stack& pila, queue& cola);
+
+int main() {
+ stack pilaTareas;
+ queue colaTareas;
+ cargarTareas(pilaTareas, colaTareas);
+
+ int opcion;
+ do {
+ cout << "1. Agregar tarea\n";
+ cout << "2. Mostrar tareas\n";
+ cout << "3. Guardar tareas\n";
+ cout << "4. Salir\n";
+ cout << "Seleccione una opción: ";
+ cin >> opcion;
+
+ switch (opcion) {
+ case 1:
+ agregarTarea(pilaTareas, colaTareas);
+ break;
+ case 2:
+ mostrarTareas(pilaTareas, colaTareas);
+ break;
+ case 3:
+ guardarTareas(pilaTareas, colaTareas);
+ 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 agregarTarea(stack& pila, queue& cola) {
+ Tarea nuevaTarea;
+ cout << "Descripción de la tarea: ";
+ cin.ignore(); // Limpiar el buffer
+ getline(cin, nuevaTarea.descripcion);
+ cout << "¿Es urgente? (1: Sí, 0: No): ";
+ cin >> nuevaTarea.urgente;
+
+ if (nuevaTarea.urgente) {
+ pila.push(nuevaTarea);
+ } else {
+ cola.push(nuevaTarea);
+ }
+}
+
+void mostrarTareas(const stack& pila, const queue& cola) {
+ cout << "\nTareas Urgentes:\n";
+ stack pilaTemporal = pila; // Copia de la pila para mostrar
+ while (!pilaTemporal.empty()) {
+ cout << "- " << pilaTemporal.top().descripcion << endl;
+ pilaTemporal.pop();
+ }
+
+ cout << "\nTareas Normales:\n";
+ queue colaTemporal = cola; // Copia de la cola para mostrar
+ while (!colaTemporal.empty()) {
+ cout << "- " << colaTemporal.front().descripcion << endl;
+ colaTemporal.pop();
+ }
+ cout << endl;
+}
+
+void guardarTareas(const stack& pila, const queue& cola) {
+ ofstream archivo("tareas.txt");
+ if (!archivo) {
+ cerr << "Error al abrir el archivo para guardar.\n";
+ return;
+ }
+
+ // Guardar tareas urgentes
+ stack pilaTemporal = pila;
+ while (!pilaTemporal.empty()) {
+ archivo << "1 " << pilaTemporal.top().descripcion << endl; // 1 para urgente
+ pilaTemporal.pop();
+ }
+
+ // Guardar tareas normales
+ queue colaTemporal = cola;
+ while (!colaTemporal.empty()) {
+ archivo << "0 " << colaTemporal.front().descripcion << endl; // 0 para normal
+ colaTemporal.pop();
+ }
+
+ archivo.close();
+ cout << "Tareas guardadas exitosamente.\n";
+}
+
+void cargarTareas(stack& pila, queue& cola) {
+ ifstream archivo("tareas.txt");
+ if (!archivo) {
+ cerr << "No se pudo abrir el archivo para cargar.\n";
+ return;
+ }
+
+ Tarea tarea;
+ int tipo;
+ while (archivo >> tipo) {
+ archivo.ignore(); // Limpiar el buffer
+ getline(archivo, tarea.descripcion);
+ if (tipo == 1) {
+ tarea.urgente = true;
+ pila.push(tarea); // Agregar a la pila si es urgente
+ } else {
+ tarea.urgente = false;
+ cola.push(tarea); // Agregar a la cola si es normal
+ }
+ }
+
+ archivo.close();
+ cout << "Tareas cargadas exitosamente.\n";
+}
\ No newline at end of file
diff --git a/Curso-CPP/banco-cuenta.cpp b/Curso-CPP/banco-cuenta.cpp
new file mode 100644
index 0000000..6b1c2d9
--- /dev/null
+++ b/Curso-CPP/banco-cuenta.cpp
@@ -0,0 +1,71 @@
+#include
+#include
+using namespace std;
+
+// Definición de la clase CuentaBancaria
+class CuentaBancaria {
+private:
+ string titular;
+ string numeroCuenta;
+ double saldo;
+
+public:
+ // Constructor
+ CuentaBancaria(string titular, string numero, double saldoInicial) {
+ this->titular = titular;
+ this->numeroCuenta = numero;
+ this->saldo = saldoInicial;
+ }
+
+ // Método para depositar dinero
+ void depositar(double monto) {
+ if (monto > 0) {
+ saldo += monto;
+ cout << "Depósito exitoso. Nuevo saldo: $" << saldo << endl;
+ } else {
+ cout << "El monto debe ser mayor a 0." << endl;
+ }
+ }
+
+ // Método para retirar dinero
+ void retirar(double monto) {
+ if (monto > 0 && monto <= saldo) {
+ saldo -= monto;
+ cout << "Retiro exitoso. Nuevo saldo: $" << saldo << endl;
+ } else {
+ cout << "Fondos insuficientes o monto inválido." << endl;
+ }
+ }
+
+ // Método para mostrar información de la cuenta
+ void mostrarInformacionCuenta() {
+ cout << "Titular de la cuenta: " << titular << endl;
+ cout << "Número de cuenta: " << numeroCuenta << endl;
+ cout << "Saldo: $" << saldo << endl;
+ }
+};
+
+int main() {
+ // Crear una cuenta bancaria
+ CuentaBancaria miCuenta("Juan Pérez", "123456789", 1000.00);
+
+ // Mostrar información inicial de la cuenta
+ cout << "Información inicial de la cuenta:" << endl;
+ miCuenta.mostrarInformacionCuenta();
+
+ // Realizar operaciones
+ cout << "\nRealizando un depósito de $500..." << endl;
+ miCuenta.depositar(500);
+
+ cout << "\nIntentando retirar $2000..." << endl;
+ miCuenta.retirar(2000);
+
+ cout << "\nRetirando $300..." << endl;
+ miCuenta.retirar(300);
+
+ // Mostrar información final de la cuenta
+ cout << "\nInformación final de la cuenta:" << endl;
+ miCuenta.mostrarInformacionCuenta();
+
+ return 0;
+}
\ No newline at end of file
diff --git a/Curso-CPP/pil_col.cpp b/Curso-CPP/pil_col.cpp
new file mode 100644
index 0000000..6ed23a8
--- /dev/null
+++ b/Curso-CPP/pil_col.cpp
@@ -0,0 +1,186 @@
+#include
+#include
+using namespace std;
+
+struct per {
+ string nom;
+ string ap;
+ int age;
+ string contry;
+};
+
+struct estudiante {
+ per pipol;
+ string cur;
+ float prom;
+};
+
+struct Cont {
+ estudiante stu;
+ Cont *foll;
+};
+
+float not_1, not_2, not_3;
+estudiante stu;
+Cont *phill = NULL;
+Cont *front = NULL;
+Cont *end = NULL;
+
+bool aprob(estudiante stu) {
+ return stu.prom >= 10;
+}
+
+float prom(float not1, float not2, float not3) {
+ return (not1 * 0.3) + (not2 * 0.3) + (not3 * 0.4);
+}
+
+void sozoPhill(Cont *&phill, estudiante stu) {
+ Cont *novo_dono = new Cont();
+ novo_dono->stu = stu;
+ novo_dono->foll = phill;
+ phill = novo_dono;
+}
+
+void hakaiPhill(Cont *&phill, estudiante &stutu) {
+if (phill == NULL) {
+ cout << "La pila está vacía." << endl;
+ return;
+ }
+ cout << "Estudiantes en la pila:" << endl;
+ while (phill != NULL) {
+ cout << "Nombre y Apellido: " << phill->stu.pipol.nom << " " << phill->stu.pipol.ap << endl;
+ cout << "Edad: " << phill->stu.pipol.age << endl;
+ cout << "Nacionalidad: " << phill->stu.pipol.contry << endl;
+ cout << "Curso: " << phill->stu.cur << endl;
+ cout << "Promedio: " << phill->stu.prom << " - " << (aprob(phill->stu) ? "Aprobado" : "Reprobado") << endl;
+ Cont *axil = phill;
+ stutu= axil->stu;
+ phill = phill->foll;
+ delete axil;
+ }
+}
+
+bool VoidCoil(Cont *front) {
+ return front == NULL;
+}
+
+void sozoCoil(Cont *&front, Cont *&end, estudiante 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;
+}
+
+void hakaiCoil(Cont *&front, Cont *&end, estudiante &stutu) {
+ if (VoidCoil(front)) {
+ cout << "La cola está vacía." << endl;
+ return;
+ }
+ cout << "Estudiantes en la cola:" << endl;
+ while (!VoidCoil(front)) {
+ cout << "Nombre y Apellido: " << front->stu.pipol.nom << " " << front->stu.pipol.ap << endl;
+ cout << "Edad: " << front->stu.pipol.age << endl;
+ cout << "Nacionalidad: " << front->stu.pipol.contry << endl;
+ cout << "Curso: " << front->stu.cur << endl;
+ cout << "Promedio: " << front->stu.prom << " - " << (aprob(front->stu) ? "Aprobado" : "Reprobado") << endl;
+ Cont *axil = front;
+ stutu= axil->stu;
+ front = front->foll;
+ if (front == NULL) {
+ end = NULL;
+ }
+ delete axil;
+ }
+}
+
+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 << "Ingrese nombre de la persona: " << endl;
+ cin >> stu.pipol.nom;
+ cout << "Ingrese apellido de la persona: " << endl;
+ cin >> stu.pipol.ap;
+ cout << "Ingrese nacionalidad de la persona: " << endl;
+ cin >> stu.pipol.contry;
+ cout << "Ingrese edad de la persona: " << endl;
+ cin >> stu.pipol.age;
+ cout << "Ingrese el curso del estudiante: " << endl;
+ cin >> stu.cur;
+ cout << "Ingrese la nota 1: " << endl;
+ cin >> not_1;
+ cout << "Ingrese nota 2: " << endl;
+ cin >> not_2;
+ cout << "Ingrese nota 3: " << endl;
+ cin >> not_3;
+ stu.prom = prom(not_1, not_2, not_3);
+ if (aprob(stu)) {
+ cout << "El estudiante aprobo" << endl;
+ } else {
+ cout << "El estudiante reprobo" << endl;
+ }
+ sozoPhill(phill, stu);
+ cout << "¿Ingresar mas datos? (s/n): " << endl;
+ cin >> opc;
+ system ("cls");
+ } while (opc == 's');
+ hakaiPhill(phill, stu);
+ break;
+ case 2:
+ do {
+ cout << "Ingrese nombre de la persona: " << endl;
+ cin >> stu.pipol.nom;
+ cout << "Ingrese apellido de la persona: " << endl;
+ cin >> stu.pipol.ap;
+ cout << "Ingrese nacionalidad de la persona: " << endl;
+ cin >> stu.pipol.contry;
+ cout << "Ingrese edad de la persona: " << endl;
+ cin >> stu.pipol.age;
+ cout << "Ingrese el curso del estudiante: " << endl;
+ cin >> stu.cur;
+ cout << "Ingrese la nota 1: " << endl;
+ cin >> not_1;
+ cout << "Ingrese nota 2: " << endl;
+ cin >> not_2;
+ cout << "Ingrese nota 3: " << endl;
+ cin >> not_3;
+ stu.prom = prom(not_1, not_2, not_3);
+ if (aprob(stu)) {
+ cout << "El estudiante aprobo" << endl;
+ } else {
+ cout << "El estudiante reprobo" << endl;
+ }
+ sozoCoil(front, end, stu);
+ cout << "¿Ingresar mas datos? (s/n): " << endl;
+ cin >> opc;
+ system ("cls");
+ } while (opc == 's');
+ hakaiCoil(front, end, stu);
+ break;
+ case 3:
+ cout << "Saliendo..." << endl;
+ return 0;
+ default:
+ cout << "Opción no válida. Intente de nuevo." << endl;
+ break;
+ }
+ cout << "¿Desea regresar al menú principal? (s/n): " << endl;
+ cin >> res;
+ system ("cls");
+ } while (res == 's');
+ return 0;
+}
+
diff --git a/Curso-CPP/tempila.cpp b/Curso-CPP/tempila.cpp
new file mode 100644
index 0000000..80894d1
--- /dev/null
+++ b/Curso-CPP/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/teor_pil.cpp b/Curso-CPP/teor_pil.cpp
new file mode 100644
index 0000000..57cd007
--- /dev/null
+++ b/Curso-CPP/teor_pil.cpp
@@ -0,0 +1,113 @@
+#include
+#include
+#include
+using namespace std;
+
+struct per {
+ string nom;
+ string ap;
+ int age;
+ float alt;
+ int numper;
+ int numfact;
+};
+
+struct Cont {
+ per per;
+ Cont *foll;
+};
+
+per pipol;
+Cont *phill = NULL;
+
+// Crear pila
+void sozoPhill(Cont *&phill, per per) {
+ Cont *novo_dono = new Cont();
+ novo_dono->per = per; // Rellenar dato
+ novo_dono->foll = phill;
+ phill = novo_dono;
+ cout << "Datos cargados" << endl;
+}
+
+// Sacar pila
+void hakaiPhill(Cont *&phill, per &per, string filename) {
+ char r;
+ if (phill == NULL) {
+ cout << "La pila está vacía." << endl;
+ return;
+ }
+ fstream arch;
+ arch.open(filename.c_str(), ios::out);
+ if (arch.fail()) {
+ cout << "* ERROR GENERANDO ARCHIVO *" << endl;
+ return;
+ }
+
+ while (phill != NULL) {
+ cout << "Nombre: " << phill->per.nom << " " << phill->per.ap << endl;
+ cout << "Apellido: " << phill->per.ap << endl;
+ cout << "Edad: " << phill->per.age << endl;
+ cout << "Altura: " << phill->per.alt << endl;
+ cout << "Numero asignado: " << phill->per.numper << endl;
+ cout << "Numero factorial: " << phill->per.numfact << endl;
+
+ // Guardar en archivo
+ arch << "Nombre: " << phill->per.nom << endl;
+ arch << "Apellido: " << phill->per.ap << endl;
+ arch << "Edad: " << phill->per.age << endl;
+ arch << "Altura: " << phill->per.alt << "m" << endl;
+ arch << "Numero asignado: " << phill->per.numper << endl;
+ arch << "Numero factorial: " << phill->per.numfact << endl;
+
+ Cont *axil = phill;
+ phill = phill->foll;
+ delete axil;
+
+ cout << "¿Sacar siguiente dato? (s/n)" << endl;
+ cin >> r;
+ if (r == 'n') {
+ break;
+ }
+ }
+ arch.close();
+}
+
+// Función para calcular el factorial
+int facto(int n) {
+ if (n < 0) return -1;
+ int fact = 1;
+ for (int a = 1; a <= n; a++) {
+ fact *= a;
+ }
+ return fact;
+}
+
+int main() {
+ char res;
+ do {
+ cout << "Ingrese nombre de la persona: " << endl;
+ cin >> pipol.nom;
+ cout << "Ingrese apellido de la persona: " << endl;
+ cin >> pipol.ap;
+ cout << "Ingrese edad de la persona: " << endl;
+ cin >> pipol.age;
+ cout << "Ingrese altura de la persona: " << endl;
+ cin >> pipol.alt;
+ cout << "Ingrese numero para la persona: " << endl;
+ cin >> pipol.numper;
+ pipol.numfact = facto(pipol.numper);
+
+ sozoPhill(phill, pipol);
+ cout << "Debe ingresar más datos? (s/n)" << endl;
+ cin >> res;
+ system("cls");
+ } while (res == 's');
+
+ string filename;
+ string ext = ".txt";
+ cout << "Ingrese el nombre con el que desea guardar el archivo: " << endl;
+ cin >> filename;
+ filename += ext;
+ hakaiPhill(phill, pipol, filename);
+ return 0;
+}
From 226e6edbff74479a0eca8362f3d140b0e9c07065 Mon Sep 17 00:00:00 2001
From: WolveJC05 <202882572+WolveJC@users.noreply.github.com>
Date: Mon, 24 Nov 2025 16:47:35 +0000
Subject: [PATCH 138/138] Clasificar proyectos Cpp
---
Curso-CPP/{ => AuthUtilsPOO_C++}/POO.cpp | 0
Curso-CPP/{ => AuthUtilsPOO_C++}/POO.md | 0
Curso-CPP/{ => DSAStackQueueStu}/pil_col.cpp | 0
Curso-CPP/{ => DSAStackQueueStu}/pil_col.md | 0
Curso-CPP/{ => DS_Stack_IO}/tempila.cpp | 0
Curso-CPP/{ => DS_Stack_IO}/tempila.md | 0
Curso-CPP/{ => DataFly}/If-matason-fly.cpp | 0
Curso-CPP/{ => DataFly}/If-matason-fly.md | 0
.../ObjectusRex/Jerarqu\303\255a_persona.cpp" | 0
.../ObjectusRex/Jerarqu\303\255a_persona.md" | 0
Curso-CPP/{ => StackMathCpp}/teor_pil.cpp | 0
Curso-CPP/{ => StackMathCpp}/teor_pil.md | 0
Curso-CPP/{ => StructBank}/banco-cuenta.cpp | 0
Curso-CPP/{ => StructBank}/banco-cuenta.md | 0
Curso-CPP/{ => TaskFlow}/Tareas completa.cpp | 0
Curso-CPP/{ => TaskFlow}/Tareas completa.md | 0
Curso-CPP/{ => Timekeeper Cpp}/Registro_arch.cpp | 0
Curso-CPP/{ => Timekeeper Cpp}/Registro_arch.md | 0
18 files changed, 0 insertions(+), 0 deletions(-)
rename Curso-CPP/{ => AuthUtilsPOO_C++}/POO.cpp (100%)
rename Curso-CPP/{ => AuthUtilsPOO_C++}/POO.md (100%)
rename Curso-CPP/{ => DSAStackQueueStu}/pil_col.cpp (100%)
rename Curso-CPP/{ => DSAStackQueueStu}/pil_col.md (100%)
rename Curso-CPP/{ => DS_Stack_IO}/tempila.cpp (100%)
rename Curso-CPP/{ => DS_Stack_IO}/tempila.md (100%)
rename Curso-CPP/{ => DataFly}/If-matason-fly.cpp (100%)
rename Curso-CPP/{ => DataFly}/If-matason-fly.md (100%)
rename "Curso-CPP/Jerarqu\303\255a_persona.cpp" => "Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.cpp" (100%)
rename "Curso-CPP/Jerarqu\303\255a_persona.md" => "Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.md" (100%)
rename Curso-CPP/{ => StackMathCpp}/teor_pil.cpp (100%)
rename Curso-CPP/{ => StackMathCpp}/teor_pil.md (100%)
rename Curso-CPP/{ => StructBank}/banco-cuenta.cpp (100%)
rename Curso-CPP/{ => StructBank}/banco-cuenta.md (100%)
rename Curso-CPP/{ => TaskFlow}/Tareas completa.cpp (100%)
rename Curso-CPP/{ => TaskFlow}/Tareas completa.md (100%)
rename Curso-CPP/{ => Timekeeper Cpp}/Registro_arch.cpp (100%)
rename Curso-CPP/{ => Timekeeper Cpp}/Registro_arch.md (100%)
diff --git a/Curso-CPP/POO.cpp b/Curso-CPP/AuthUtilsPOO_C++/POO.cpp
similarity index 100%
rename from Curso-CPP/POO.cpp
rename to Curso-CPP/AuthUtilsPOO_C++/POO.cpp
diff --git a/Curso-CPP/POO.md b/Curso-CPP/AuthUtilsPOO_C++/POO.md
similarity index 100%
rename from Curso-CPP/POO.md
rename to Curso-CPP/AuthUtilsPOO_C++/POO.md
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/pil_col.md b/Curso-CPP/DSAStackQueueStu/pil_col.md
similarity index 100%
rename from Curso-CPP/pil_col.md
rename to Curso-CPP/DSAStackQueueStu/pil_col.md
diff --git a/Curso-CPP/tempila.cpp b/Curso-CPP/DS_Stack_IO/tempila.cpp
similarity index 100%
rename from Curso-CPP/tempila.cpp
rename to Curso-CPP/DS_Stack_IO/tempila.cpp
diff --git a/Curso-CPP/tempila.md b/Curso-CPP/DS_Stack_IO/tempila.md
similarity index 100%
rename from Curso-CPP/tempila.md
rename to Curso-CPP/DS_Stack_IO/tempila.md
diff --git a/Curso-CPP/If-matason-fly.cpp b/Curso-CPP/DataFly/If-matason-fly.cpp
similarity index 100%
rename from Curso-CPP/If-matason-fly.cpp
rename to Curso-CPP/DataFly/If-matason-fly.cpp
diff --git a/Curso-CPP/If-matason-fly.md b/Curso-CPP/DataFly/If-matason-fly.md
similarity index 100%
rename from Curso-CPP/If-matason-fly.md
rename to Curso-CPP/DataFly/If-matason-fly.md
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/Jerarqu\303\255a_persona.md" "b/Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.md"
similarity index 100%
rename from "Curso-CPP/Jerarqu\303\255a_persona.md"
rename to "Curso-CPP/ObjectusRex/Jerarqu\303\255a_persona.md"
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/teor_pil.md b/Curso-CPP/StackMathCpp/teor_pil.md
similarity index 100%
rename from Curso-CPP/teor_pil.md
rename to Curso-CPP/StackMathCpp/teor_pil.md
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/banco-cuenta.md b/Curso-CPP/StructBank/banco-cuenta.md
similarity index 100%
rename from Curso-CPP/banco-cuenta.md
rename to Curso-CPP/StructBank/banco-cuenta.md
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/Tareas completa.md b/Curso-CPP/TaskFlow/Tareas completa.md
similarity index 100%
rename from Curso-CPP/Tareas completa.md
rename to Curso-CPP/TaskFlow/Tareas completa.md
diff --git a/Curso-CPP/Registro_arch.cpp b/Curso-CPP/Timekeeper Cpp/Registro_arch.cpp
similarity index 100%
rename from Curso-CPP/Registro_arch.cpp
rename to Curso-CPP/Timekeeper Cpp/Registro_arch.cpp
diff --git a/Curso-CPP/Registro_arch.md b/Curso-CPP/Timekeeper Cpp/Registro_arch.md
similarity index 100%
rename from Curso-CPP/Registro_arch.md
rename to Curso-CPP/Timekeeper Cpp/Registro_arch.md