Cristhian Villegas
Backend14 min read1 views

Curso Python #10: Proyecto Final - Gestor de Tareas

Curso Python #10: Proyecto Final - Gestor de Tareas

Proyecto Final: Gestor de Tareas en la Terminal - Parte 10 de 10

Logo de Python

¡Llegamos al ultimo articulo del Curso de Python desde Cero! Este es el articulo 10 de 10, y es el momento de poner en practica todo lo que has aprendido a lo largo del curso.

Vamos a construir un gestor de tareas (to-do list) que funciona en la terminal. Este proyecto usa variables, funciones, listas, diccionarios, programacion orientada a objetos (POO), manejo de archivos y manejo de errores — basicamente todo lo que hemos cubierto.

Al final tendras un programa completo y funcional que puedes usar en tu dia a dia y que demuestra que ya sabes programar en Python.

📌 Nota: Este proyecto combina conceptos de los 9 articulos anteriores. Si algo no te queda claro, puedes volver al articulo correspondiente para repasar.

Descripcion del proyecto

Nuestro gestor de tareas tendra las siguientes funcionalidades:

  • Agregar tareas con titulo y descripcion
  • Listar todas las tareas mostrando su estado (pendiente o completada)
  • Marcar tareas como completadas
  • Eliminar tareas
  • Buscar tareas por palabra clave
  • Guardar las tareas en un archivo JSON para que no se pierdan al cerrar el programa
  • Menu interactivo con opciones numeradas

El proyecto tendra 3 archivos:

Archivo Que contiene
models.py La clase Task (modelo de datos)
task_manager.py La clase TaskManager (logica de negocio)
main.py El menu interactivo (interfaz de usuario)

Paso 1: Estructura del proyecto

Primero, crea una carpeta para el proyecto y dentro de ella los tres archivos:

python
1# Estructura de carpetas
2gestor_tareas/
3    models.py
4    task_manager.py
5    main.py

Puedes crear la carpeta y los archivos desde la terminal:

python
1# En la terminal (no es codigo Python, son comandos)
2# Windows:
3mkdir gestor_tareas
4cd gestor_tareas
5type nul > models.py
6type nul > task_manager.py
7type nul > main.py
8
9# macOS / Linux:
10mkdir gestor_tareas
11cd gestor_tareas
12touch models.py task_manager.py main.py
💡 Tip: Separar el codigo en varios archivos es una buena practica. Cada archivo tiene una responsabilidad clara: uno para los datos, otro para la logica y otro para la interfaz. Esto hace que el codigo sea mas facil de entender y mantener.

Paso 2: Modelo de datos — La clase Task

Empecemos por el modelo de datos. La clase Task representa una tarea individual con sus propiedades.

Abre el archivo models.py y escribe:

python
1from datetime import datetime
2
3
4class Task:
5    """Representa una tarea individual en el gestor."""
6
7    def __init__(self, task_id, title, description=""):
8        self.task_id = task_id
9        self.title = title
10        self.description = description
11        self.completed = False
12        self.created_at = datetime.now().strftime("%Y-%m-%d %H:%M")
13
14    def mark_completed(self):
15        """Marca la tarea como completada."""
16        self.completed = True
17
18    def to_dict(self):
19        """Convierte la tarea a diccionario (para guardar en JSON)."""
20        return {
21            "task_id": self.task_id,
22            "title": self.title,
23            "description": self.description,
24            "completed": self.completed,
25            "created_at": self.created_at
26        }
27
28    @classmethod
29    def from_dict(cls, data):
30        """Crea una tarea desde un diccionario (para cargar desde JSON)."""
31        task = cls(data["task_id"], data["title"], data.get("description", ""))
32        task.completed = data.get("completed", False)
33        task.created_at = data.get("created_at", "")
34        return task
35
36    def __str__(self):
37        """Representacion en texto de la tarea."""
38        status = "✓" if self.completed else "○"
39        text = f"[{status}] #{self.task_id} - {self.title}"
40        if self.description:
41            text += f"
42      {self.description}"
43        text += f"
44      Creada: {self.created_at}"
45        return text

Veamos que hace cada parte:

  • __init__: Inicializa la tarea con un ID, titulo, descripcion (opcional), estado (pendiente por defecto) y fecha de creacion
  • mark_completed: Cambia el estado a completada
  • to_dict: Convierte la tarea a un diccionario para poder guardarla en un archivo JSON
  • from_dict: Metodo de clase que crea una tarea desde un diccionario (para cargar datos desde JSON)
  • __str__: Define como se muestra la tarea cuando la imprimimos con print()

Paso 3: Logica de negocio — La clase TaskManager

Ahora vamos a crear la clase que maneja todas las operaciones: agregar, listar, completar, eliminar y buscar tareas. Tambien se encarga de guardar y cargar las tareas desde un archivo JSON.

Abre el archivo task_manager.py y escribe:

python
1import json
2import os
3from models import Task
4
5
6class TaskManager:
7    """Gestiona la lista de tareas con persistencia en archivo JSON."""
8
9    def __init__(self, filename="tasks.json"):
10        self.filename = filename
11        self.tasks = []
12        self.next_id = 1
13        self.load_tasks()
14
15    def load_tasks(self):
16        """Carga las tareas desde el archivo JSON."""
17        if not os.path.exists(self.filename):
18            return
19
20        try:
21            with open(self.filename, "r", encoding="utf-8") as file:
22                data = json.load(file)
23                self.tasks = [Task.from_dict(item) for item in data]
24                if self.tasks:
25                    self.next_id = max(t.task_id for t in self.tasks) + 1
26                print(f"Se cargaron {len(self.tasks)} tareas desde {self.filename}")
27        except (json.JSONDecodeError, KeyError) as e:
28            print(f"Error al leer el archivo de tareas: {e}")
29            print("Se iniciara con una lista vacia.")
30            self.tasks = []
31
32    def save_tasks(self):
33        """Guarda las tareas en el archivo JSON."""
34        try:
35            data = [task.to_dict() for task in self.tasks]
36            with open(self.filename, "w", encoding="utf-8") as file:
37                json.dump(data, file, indent=2, ensure_ascii=False)
38        except IOError as e:
39            print(f"Error al guardar las tareas: {e}")
40
41    def add_task(self, title, description=""):
42        """Agrega una nueva tarea a la lista."""
43        if not title or not title.strip():
44            raise ValueError("El titulo de la tarea no puede estar vacio")
45
46        task = Task(self.next_id, title.strip(), description.strip())
47        self.tasks.append(task)
48        self.next_id += 1
49        self.save_tasks()
50        return task
51
52    def list_tasks(self, show_all=True):
53        """Retorna las tareas (todas o solo pendientes)."""
54        if show_all:
55            return self.tasks
56        return [t for t in self.tasks if not t.completed]
57
58    def complete_task(self, task_id):
59        """Marca una tarea como completada."""
60        task = self._find_task(task_id)
61        if task is None:
62            raise ValueError(f"No existe una tarea con ID #{task_id}")
63        if task.completed:
64            raise ValueError(f"La tarea #{task_id} ya esta completada")
65
66        task.mark_completed()
67        self.save_tasks()
68        return task
69
70    def delete_task(self, task_id):
71        """Elimina una tarea de la lista."""
72        task = self._find_task(task_id)
73        if task is None:
74            raise ValueError(f"No existe una tarea con ID #{task_id}")
75
76        self.tasks.remove(task)
77        self.save_tasks()
78        return task
79
80    def search_tasks(self, keyword):
81        """Busca tareas que contengan la palabra clave en titulo o descripcion."""
82        if not keyword or not keyword.strip():
83            raise ValueError("La palabra de busqueda no puede estar vacia")
84
85        keyword = keyword.lower().strip()
86        results = [
87            t for t in self.tasks
88            if keyword in t.title.lower() or keyword in t.description.lower()
89        ]
90        return results
91
92    def get_stats(self):
93        """Retorna estadisticas de las tareas."""
94        total = len(self.tasks)
95        completed = sum(1 for t in self.tasks if t.completed)
96        pending = total - completed
97        return {
98            "total": total,
99            "completed": completed,
100            "pending": pending
101        }
102
103    def _find_task(self, task_id):
104        """Busca una tarea por su ID (metodo privado)."""
105        for task in self.tasks:
106            if task.task_id == task_id:
107                return task
108        return None

Esta clase tiene toda la logica del gestor de tareas:

  • load_tasks y save_tasks: Manejan la persistencia en JSON (Parte 8 del curso)
  • add_task: Valida y agrega una tarea nueva
  • list_tasks: Puede mostrar todas o solo las pendientes (listas por comprension, Parte 4)
  • complete_task y delete_task: Buscan la tarea y lanzan errores si no existe (Parte 9)
  • search_tasks: Busca por palabra clave en titulo y descripcion
  • get_stats: Retorna estadisticas usando diccionarios (Parte 5)
  • _find_task: Metodo privado auxiliar para buscar por ID
📌 Nota: El metodo _find_task empieza con guion bajo (_). En Python, esto indica que es un metodo "privado" — solo debe usarse dentro de la clase, no desde fuera.

Paso 4: Guardar y cargar tareas con JSON

La persistencia es lo que hace que las tareas no se pierdan cuando cierras el programa. Cada vez que agregas, completas o eliminas una tarea, el programa guarda automaticamente todo en un archivo tasks.json.

Asi se ve el archivo JSON que genera el programa:

python
1# Contenido de tasks.json (generado automaticamente)
2[
3  {
4    "task_id": 1,
5    "title": "Comprar leche",
6    "description": "De la tienda de la esquina",
7    "completed": false,
8    "created_at": "2025-01-15 10:30"
9  },
10  {
11    "task_id": 2,
12    "title": "Estudiar Python",
13    "description": "Terminar el proyecto final del curso",
14    "completed": true,
15    "created_at": "2025-01-15 10:35"
16  }
17]

El flujo funciona asi:

  1. Cuando el programa inicia, load_tasks() lee el archivo JSON y crea objetos Task
  2. Cuando haces cualquier cambio, save_tasks() convierte todas las tareas a diccionarios y las guarda en JSON
  3. Si el archivo no existe (primera vez), el programa empieza con una lista vacia
  4. Si el archivo esta corrupto, el programa muestra un error y empieza con lista vacia en vez de romperse
💡 Tip: Usamos ensure_ascii=False en json.dump() para que los caracteres especiales (como acentos) se guarden correctamente en el archivo.

Paso 5: Menu interactivo — El archivo main.py

Finalmente, vamos a crear la interfaz de usuario: un menu interactivo que le permite al usuario elegir que quiere hacer.

Abre el archivo main.py y escribe:

python
1from task_manager import TaskManager
2
3
4def show_menu():
5    """Muestra el menu principal."""
6    print()
7    print("=" * 45)
8    print("   GESTOR DE TAREAS - Menu Principal")
9    print("=" * 45)
10    print("  1. Agregar tarea")
11    print("  2. Listar todas las tareas")
12    print("  3. Listar tareas pendientes")
13    print("  4. Completar una tarea")
14    print("  5. Eliminar una tarea")
15    print("  6. Buscar tareas")
16    print("  7. Ver estadisticas")
17    print("  0. Salir")
18    print("-" * 45)
19
20
21def add_task_flow(manager):
22    """Flujo para agregar una tarea."""
23    print("
24--- Agregar Nueva Tarea ---")
25    title = input("Titulo: ").strip()
26    if not title:
27        print("Error: el titulo no puede estar vacio.")
28        return
29
30    description = input("Descripcion (opcional, presiona Enter para omitir): ").strip()
31
32    try:
33        task = manager.add_task(title, description)
34        print(f"
35Tarea agregada exitosamente:")
36        print(task)
37    except ValueError as e:
38        print(f"Error: {e}")
39
40
41def list_tasks_flow(manager, show_all=True):
42    """Flujo para listar tareas."""
43    label = "Todas las Tareas" if show_all else "Tareas Pendientes"
44    print(f"
45--- {label} ---")
46
47    tasks = manager.list_tasks(show_all=show_all)
48    if not tasks:
49        print("No hay tareas para mostrar.")
50        return
51
52    for task in tasks:
53        print()
54        print(task)
55    print(f"
56Total: {len(tasks)} tarea(s)")
57
58
59def complete_task_flow(manager):
60    """Flujo para completar una tarea."""
61    print("
62--- Completar Tarea ---")
63    list_tasks_flow(manager, show_all=False)
64
65    try:
66        task_id = int(input("
67Ingresa el ID de la tarea a completar: "))
68        task = manager.complete_task(task_id)
69        print(f"
70Tarea completada:")
71        print(task)
72    except ValueError as e:
73        print(f"Error: {e}")
74
75
76def delete_task_flow(manager):
77    """Flujo para eliminar una tarea."""
78    print("
79--- Eliminar Tarea ---")
80    list_tasks_flow(manager)
81
82    try:
83        task_id = int(input("
84Ingresa el ID de la tarea a eliminar: "))
85        confirm = input(f"¿Seguro que quieres eliminar la tarea #{task_id}? (s/n): ")
86        if confirm.lower() == "s":
87            task = manager.delete_task(task_id)
88            print(f"
89Tarea eliminada: {task.title}")
90        else:
91            print("Operacion cancelada.")
92    except ValueError as e:
93        print(f"Error: {e}")
94
95
96def search_tasks_flow(manager):
97    """Flujo para buscar tareas."""
98    print("
99--- Buscar Tareas ---")
100    keyword = input("Palabra a buscar: ").strip()
101
102    try:
103        results = manager.search_tasks(keyword)
104        if not results:
105            print(f"No se encontraron tareas con '{keyword}'.")
106            return
107
108        print(f"
109Resultados para '{keyword}': {len(results)} tarea(s)")
110        for task in results:
111            print()
112            print(task)
113    except ValueError as e:
114        print(f"Error: {e}")
115
116
117def stats_flow(manager):
118    """Flujo para mostrar estadisticas."""
119    print("
120--- Estadisticas ---")
121    stats = manager.get_stats()
122
123    print(f"  Total de tareas:  {stats['total']}")
124    print(f"  Completadas:      {stats['completed']}")
125    print(f"  Pendientes:       {stats['pending']}")
126
127    if stats["total"] > 0:
128        percentage = (stats["completed"] / stats["total"]) * 100
129        bar_length = 20
130        filled = int(bar_length * stats["completed"] / stats["total"])
131        bar = "█" * filled + "░" * (bar_length - filled)
132        print(f"  Progreso:         [{bar}] {percentage:.0f}%")
133
134
135def main():
136    """Funcion principal del programa."""
137    print("
138Bienvenido al Gestor de Tareas")
139    manager = TaskManager()
140
141    while True:
142        show_menu()
143        option = input("Elige una opcion: ").strip()
144
145        if option == "1":
146            add_task_flow(manager)
147        elif option == "2":
148            list_tasks_flow(manager, show_all=True)
149        elif option == "3":
150            list_tasks_flow(manager, show_all=False)
151        elif option == "4":
152            complete_task_flow(manager)
153        elif option == "5":
154            delete_task_flow(manager)
155        elif option == "6":
156            search_tasks_flow(manager)
157        elif option == "7":
158            stats_flow(manager)
159        elif option == "0":
160            print("
161¡Hasta luego! Tus tareas estan guardadas.")
162            break
163        else:
164            print("Opcion no valida. Elige un numero del 0 al 7.")
165
166
167if __name__ == "__main__":
168    main()

El archivo main.py es la interfaz de usuario. Cada opcion del menu tiene su propia funcion (add_task_flow, complete_task_flow, etc.) que maneja la interaccion con el usuario y llama a los metodos de TaskManager.

Paso 6: Manejo de errores en todo el proyecto

Observa como el manejo de errores esta integrado en todo el proyecto:

En el modelo (models.py)

  • El metodo from_dict usa .get() con valores por defecto para evitar KeyError

En la logica (task_manager.py)

  • load_tasks: Atrapa json.JSONDecodeError si el archivo esta corrupto
  • save_tasks: Atrapa IOError si no se puede escribir el archivo
  • add_task: Valida que el titulo no este vacio
  • complete_task: Verifica que la tarea exista y no este ya completada
  • delete_task: Verifica que la tarea exista antes de eliminar
  • search_tasks: Valida que la palabra clave no este vacia

En la interfaz (main.py)

  • Cada flujo usa try/except para atrapar errores de validacion y mostrar mensajes amigables
  • La funcion delete_task_flow pide confirmacion antes de eliminar
  • El menu principal maneja opciones invalidas con un mensaje claro
python
1# Ejemplo de como se ven los errores para el usuario:
2
3# Si intenta completar una tarea que no existe:
4# "Error: No existe una tarea con ID #99"
5
6# Si intenta agregar una tarea sin titulo:
7# "Error: el titulo no puede estar vacio."
8
9# Si escribe letras donde se espera un numero:
10# "Error: invalid literal for int() with base 10: 'abc'"
⚠️ Importante: Siempre valida los datos del usuario antes de procesarlos. Nunca confies en que el usuario ingresara datos correctos — siempre puede haber errores, sean accidentales o intencionales.

Codigo completo del proyecto

Aqui tienes el codigo completo de cada archivo para que puedas copiarlo y ejecutarlo directamente:

Archivo: models.py

python
1from datetime import datetime
2
3
4class Task:
5    """Representa una tarea individual en el gestor."""
6
7    def __init__(self, task_id, title, description=""):
8        self.task_id = task_id
9        self.title = title
10        self.description = description
11        self.completed = False
12        self.created_at = datetime.now().strftime("%Y-%m-%d %H:%M")
13
14    def mark_completed(self):
15        """Marca la tarea como completada."""
16        self.completed = True
17
18    def to_dict(self):
19        """Convierte la tarea a diccionario (para guardar en JSON)."""
20        return {
21            "task_id": self.task_id,
22            "title": self.title,
23            "description": self.description,
24            "completed": self.completed,
25            "created_at": self.created_at
26        }
27
28    @classmethod
29    def from_dict(cls, data):
30        """Crea una tarea desde un diccionario (para cargar desde JSON)."""
31        task = cls(data["task_id"], data["title"], data.get("description", ""))
32        task.completed = data.get("completed", False)
33        task.created_at = data.get("created_at", "")
34        return task
35
36    def __str__(self):
37        """Representacion en texto de la tarea."""
38        status = "✓" if self.completed else "○"
39        text = f"[{status}] #{self.task_id} - {self.title}"
40        if self.description:
41            text += f"\n      {self.description}"
42        text += f"\n      Creada: {self.created_at}"
43        return text

Archivo: task_manager.py

python
1import json
2import os
3from models import Task
4
5
6class TaskManager:
7    """Gestiona la lista de tareas con persistencia en archivo JSON."""
8
9    def __init__(self, filename="tasks.json"):
10        self.filename = filename
11        self.tasks = []
12        self.next_id = 1
13        self.load_tasks()
14
15    def load_tasks(self):
16        """Carga las tareas desde el archivo JSON."""
17        if not os.path.exists(self.filename):
18            return
19
20        try:
21            with open(self.filename, "r", encoding="utf-8") as file:
22                data = json.load(file)
23                self.tasks = [Task.from_dict(item) for item in data]
24                if self.tasks:
25                    self.next_id = max(t.task_id for t in self.tasks) + 1
26                print(f"Se cargaron {len(self.tasks)} tareas desde {self.filename}")
27        except (json.JSONDecodeError, KeyError) as e:
28            print(f"Error al leer el archivo de tareas: {e}")
29            print("Se iniciara con una lista vacia.")
30            self.tasks = []
31
32    def save_tasks(self):
33        """Guarda las tareas en el archivo JSON."""
34        try:
35            data = [task.to_dict() for task in self.tasks]
36            with open(self.filename, "w", encoding="utf-8") as file:
37                json.dump(data, file, indent=2, ensure_ascii=False)
38        except IOError as e:
39            print(f"Error al guardar las tareas: {e}")
40
41    def add_task(self, title, description=""):
42        """Agrega una nueva tarea a la lista."""
43        if not title or not title.strip():
44            raise ValueError("El titulo de la tarea no puede estar vacio")
45
46        task = Task(self.next_id, title.strip(), description.strip())
47        self.tasks.append(task)
48        self.next_id += 1
49        self.save_tasks()
50        return task
51
52    def list_tasks(self, show_all=True):
53        """Retorna las tareas (todas o solo pendientes)."""
54        if show_all:
55            return self.tasks
56        return [t for t in self.tasks if not t.completed]
57
58    def complete_task(self, task_id):
59        """Marca una tarea como completada."""
60        task = self._find_task(task_id)
61        if task is None:
62            raise ValueError(f"No existe una tarea con ID #{task_id}")
63        if task.completed:
64            raise ValueError(f"La tarea #{task_id} ya esta completada")
65
66        task.mark_completed()
67        self.save_tasks()
68        return task
69
70    def delete_task(self, task_id):
71        """Elimina una tarea de la lista."""
72        task = self._find_task(task_id)
73        if task is None:
74            raise ValueError(f"No existe una tarea con ID #{task_id}")
75
76        self.tasks.remove(task)
77        self.save_tasks()
78        return task
79
80    def search_tasks(self, keyword):
81        """Busca tareas que contengan la palabra clave."""
82        if not keyword or not keyword.strip():
83            raise ValueError("La palabra de busqueda no puede estar vacia")
84
85        keyword = keyword.lower().strip()
86        results = [
87            t for t in self.tasks
88            if keyword in t.title.lower() or keyword in t.description.lower()
89        ]
90        return results
91
92    def get_stats(self):
93        """Retorna estadisticas de las tareas."""
94        total = len(self.tasks)
95        completed = sum(1 for t in self.tasks if t.completed)
96        pending = total - completed
97        return {"total": total, "completed": completed, "pending": pending}
98
99    def _find_task(self, task_id):
100        """Busca una tarea por su ID (metodo privado)."""
101        for task in self.tasks:
102            if task.task_id == task_id:
103                return task
104        return None

Archivo: main.py

python
1from task_manager import TaskManager
2
3
4def show_menu():
5    """Muestra el menu principal."""
6    print()
7    print("=" * 45)
8    print("   GESTOR DE TAREAS - Menu Principal")
9    print("=" * 45)
10    print("  1. Agregar tarea")
11    print("  2. Listar todas las tareas")
12    print("  3. Listar tareas pendientes")
13    print("  4. Completar una tarea")
14    print("  5. Eliminar una tarea")
15    print("  6. Buscar tareas")
16    print("  7. Ver estadisticas")
17    print("  0. Salir")
18    print("-" * 45)
19
20
21def add_task_flow(manager):
22    """Flujo para agregar una tarea."""
23    print("\n--- Agregar Nueva Tarea ---")
24    title = input("Titulo: ").strip()
25    if not title:
26        print("Error: el titulo no puede estar vacio.")
27        return
28
29    description = input("Descripcion (opcional, presiona Enter para omitir): ").strip()
30
31    try:
32        task = manager.add_task(title, description)
33        print(f"\nTarea agregada exitosamente:")
34        print(task)
35    except ValueError as e:
36        print(f"Error: {e}")
37
38
39def list_tasks_flow(manager, show_all=True):
40    """Flujo para listar tareas."""
41    label = "Todas las Tareas" if show_all else "Tareas Pendientes"
42    print(f"\n--- {label} ---")
43
44    tasks = manager.list_tasks(show_all=show_all)
45    if not tasks:
46        print("No hay tareas para mostrar.")
47        return
48
49    for task in tasks:
50        print()
51        print(task)
52    print(f"\nTotal: {len(tasks)} tarea(s)")
53
54
55def complete_task_flow(manager):
56    """Flujo para completar una tarea."""
57    print("\n--- Completar Tarea ---")
58    list_tasks_flow(manager, show_all=False)
59
60    try:
61        task_id = int(input("\nIngresa el ID de la tarea a completar: "))
62        task = manager.complete_task(task_id)
63        print(f"\nTarea completada:")
64        print(task)
65    except ValueError as e:
66        print(f"Error: {e}")
67
68
69def delete_task_flow(manager):
70    """Flujo para eliminar una tarea."""
71    print("\n--- Eliminar Tarea ---")
72    list_tasks_flow(manager)
73
74    try:
75        task_id = int(input("\nIngresa el ID de la tarea a eliminar: "))
76        confirm = input(f"Seguro que quieres eliminar la tarea #{task_id}? (s/n): ")
77        if confirm.lower() == "s":
78            task = manager.delete_task(task_id)
79            print(f"\nTarea eliminada: {task.title}")
80        else:
81            print("Operacion cancelada.")
82    except ValueError as e:
83        print(f"Error: {e}")
84
85
86def search_tasks_flow(manager):
87    """Flujo para buscar tareas."""
88    print("\n--- Buscar Tareas ---")
89    keyword = input("Palabra a buscar: ").strip()
90
91    try:
92        results = manager.search_tasks(keyword)
93        if not results:
94            print(f"No se encontraron tareas con '{keyword}'.")
95            return
96
97        print(f"\nResultados para '{keyword}': {len(results)} tarea(s)")
98        for task in results:
99            print()
100            print(task)
101    except ValueError as e:
102        print(f"Error: {e}")
103
104
105def stats_flow(manager):
106    """Flujo para mostrar estadisticas."""
107    print("\n--- Estadisticas ---")
108    stats = manager.get_stats()
109
110    print(f"  Total de tareas:  {stats['total']}")
111    print(f"  Completadas:      {stats['completed']}")
112    print(f"  Pendientes:       {stats['pending']}")
113
114    if stats["total"] > 0:
115        percentage = (stats["completed"] / stats["total"]) * 100
116        bar_length = 20
117        filled = int(bar_length * stats["completed"] / stats["total"])
118        bar = "█" * filled + "░" * (bar_length - filled)
119        print(f"  Progreso:         [{bar}] {percentage:.0f}%")
120
121
122def main():
123    """Funcion principal del programa."""
124    print("\nBienvenido al Gestor de Tareas")
125    manager = TaskManager()
126
127    while True:
128        show_menu()
129        option = input("Elige una opcion: ").strip()
130
131        if option == "1":
132            add_task_flow(manager)
133        elif option == "2":
134            list_tasks_flow(manager, show_all=True)
135        elif option == "3":
136            list_tasks_flow(manager, show_all=False)
137        elif option == "4":
138            complete_task_flow(manager)
139        elif option == "5":
140            delete_task_flow(manager)
141        elif option == "6":
142            search_tasks_flow(manager)
143        elif option == "7":
144            stats_flow(manager)
145        elif option == "0":
146            print("\nHasta luego! Tus tareas estan guardadas.")
147            break
148        else:
149            print("Opcion no valida. Elige un numero del 0 al 7.")
150
151
152if __name__ == "__main__":
153    main()

Para ejecutar el programa, abre una terminal en la carpeta del proyecto y escribe:

python
1# Ejecutar el gestor de tareas
2python main.py

Ideas para mejorar el proyecto

Ya tienes un gestor de tareas funcional. Aqui tienes algunas ideas para seguir practicando y mejorar el proyecto:

Nivel facil

  • Colores en la terminal: Instala la libreria colorama (pip install colorama) y agrega colores al menu y los mensajes. Por ejemplo, tareas completadas en verde y pendientes en amarillo.
  • Ordenar tareas: Agrega una opcion para ordenar las tareas por fecha de creacion, por titulo (alfabeticamente) o por estado.
  • Editar tareas: Agrega la posibilidad de cambiar el titulo o la descripcion de una tarea existente.

Nivel intermedio

  • Prioridades: Agrega un campo de prioridad (alta, media, baja) a las tareas y permite filtrar por prioridad.
  • Fechas limite: Agrega una fecha limite a las tareas y muestra alertas cuando una tarea esta por vencer.
  • Categorias: Permite asignar categorias a las tareas (trabajo, personal, estudio) y filtrar por categoria.

Nivel avanzado

  • Base de datos SQLite: En lugar de JSON, usa SQLite para almacenar las tareas. Python incluye el modulo sqlite3 sin necesidad de instalar nada.
  • Interfaz grafica: Crea una version con interfaz grafica usando tkinter (viene incluido con Python).
  • API REST: Convierte el gestor en una API web usando Flask o FastAPI — asi podras acceder a tus tareas desde un navegador.
💡 Tip: La mejor forma de aprender a programar es construir proyectos. No te limites a seguir tutoriales — modifica este proyecto, rompe cosas, arreglalas. Cada error que resuelves te hace un mejor programador.

Conclusion del Curso de Python

¡Felicidades! Has completado el Curso de Python desde Cero. A lo largo de 10 articulos, has pasado de no saber nada de programacion a construir un proyecto completo con multiples archivos, clases, persistencia de datos y manejo de errores.

Repasemos todo lo que aprendiste:

Articulo Tema
Parte 1 Instalacion de Python y tu primer programa
Parte 2 Variables y tipos de datos
Parte 3 Condicionales (if, elif, else)
Parte 4 Bucles (for, while) y listas
Parte 5 Funciones y diccionarios
Parte 6 Strings y metodos de texto
Parte 7 Programacion orientada a objetos (POO)
Parte 8 Archivos y modulos
Parte 9 Manejo de errores y excepciones
Parte 10 Proyecto final: gestor de tareas

¿Que sigue despues de este curso?

Ahora que dominas los fundamentos de Python, tienes muchas opciones para seguir creciendo:

  • Desarrollo Web: Aprende Flask o Django para crear aplicaciones web con Python. Flask es mas simple para empezar, Django es mas completo para proyectos grandes.
  • Ciencia de Datos: Aprende pandas, NumPy y Matplotlib para analizar y visualizar datos. Es una de las areas mejor pagadas en tecnologia.
  • Automatizacion: Usa Python para automatizar tareas repetitivas: renombrar archivos, extraer datos de paginas web (web scraping con BeautifulSoup), enviar emails automaticos, etc.
  • APIs REST: Aprende FastAPI para crear APIs modernas y rapidas. Es el framework Python mas popular para APIs.
  • Machine Learning: Aprende scikit-learn y TensorFlow para crear modelos de inteligencia artificial.
  • Bases de datos: Aprende SQL y como conectar Python con bases de datos como PostgreSQL o MySQL.
💡 Consejo final: La programacion se aprende programando. No intentes aprender todo de una vez. Elige un area que te interese, construye un proyecto, y ve aprendiendo sobre la marcha. Cada proyecto que completes te acercara mas a convertirte en un desarrollador profesional. ¡Sigue programando y nunca dejes de aprender!
Share:
CV

Cristhian Villegas

Software Engineer specializing in Java, Spring Boot, Angular & AWS. Building scalable distributed systems with clean architecture.

Comments

Sign in to leave a comment

No comments yet. Be the first!

Related Articles