Gestor de Tareas CLI en Python | Capítulo 14

En este capítulo, nos sumergimos en un proyecto práctico de Python: la creación de un gestor de tareas CLI (Command-Line Interface) en su versión inicial. Este proyecto integra conceptos fundamentales como listastuplas para datos inmutables, diccionarios para modelar estructuras clave-valor, sets para manejar etiquetas únicas, y operaciones de entrada/salida con archivos de texto (.txt) procesados línea por línea. Usaremos condicionalesbucles y comprehensions para construir lógica eficiente, todo sin recurrir a funciones avanzadas, programación orientada a objetos, decoradores o módulos externos. Imagina esto como construir un organizador personal desde cero, como un bloc de notas digital que vive en tu terminal, ayudándote a aplicar lo aprendido de manera tangible y a sentirte confiado en el manejo de datos básicos en Python.

Por Qué Este Proyecto Es Tu Próximo Paso en Python

Como mentor, te digo con exigencia: los proyectos no son solo ejercicios, son el puente entre teoría y maestría. Este gestor de tareas CLI te obliga a manipular datos reales de forma estructurada. Piensa en él como un asistente personal minimalista: agregarás tareas, las listarás, las marcarás como completadas y las guardarás en un archivo para persistencia. Usaremos listas para colecciones ordenadas de tareas, tuplas para representar cada tarea de manera inmutable (por ejemplo, (id, descripción, estado)), diccionarios para un modelo eficiente donde la clave es el ID y el valor son los detalles, y sets para etiquetas únicas sin duplicados. La E/S con archivos .txt se hará línea por línea, simulando una base de datos simple. No hay atajos aquí; cada paso se explica con profundidad para que lo domines, no solo lo copies.

Antes de codificar, asegúrate de tener Python instalado. Guardaremos el código en un archivo como gestor_tareas.py. Para ejecutarlo, abre tu terminal y escribe python gestor_tareas.py. Esto iniciará el programa en modo CLI, donde interactuarás mediante comandos de texto.

Diseñando la Estructura de Datos para Tareas

Comencemos por el núcleo: cómo representamos las tareas. Una tarea típica tiene un ID único, una descripción, un estado (pendiente o completada) y etiquetas. Usaremos un diccionario principal donde la clave es el ID (un entero) y el valor es una tupla inmutable con los detalles básicos: (descripción, estado). ¿Por qué tuplas? Son como contenedores sellados; una vez creadas, no se modifican accidentalmente, lo que es perfecto para datos fijos como una tarea inicial.

Para etiquetas, emplearemos sets porque garantizan unicidad – no quieres etiquetas duplicadas como “urgente” apareciendo dos veces. Imagina un set como una bolsa de etiquetas donde cada una es única, sin orden ni repeticiones.

Aquí va un ejemplo inicial de cómo inicializar estas estructuras. Copia esto en tu archivo gestor_tareas.py y ejecútalo con python gestor_tareas.py para ver la salida.

# Diccionario principal para tareas: {id: (descripcion, estado)}
tareas = {
    1: ("Comprar leche", "pendiente"),
    2: ("Llamar al doctor", "completada")
}

# Set para etiquetas de una tarea específica (por ejemplo, para ID 1)
etiquetas_tarea1 = {"urgente", "compras"}

# Imprimimos para verificar
print("Tareas:", tareas)
print("Etiquetas para tarea 1:", etiquetas_tarea1)
Python

Ejecuta esto y verás cómo Python maneja estos datos de forma limpia. Nota los comentarios: cada línea explica su propósito. Ahora, expande tu comprensión: un diccionario es como un directorio telefónico (clave: nombre, valor: número), rápido para búsquedas. Un set es como una lista de invitados únicos a una fiesta – agrega uno y si ya existe, se ignora.

Implementando la Lógica Principal con Bucles y Condicionales

Ahora, construyamos el bucle principal del CLI. Usaremos un bucle while infinito que se rompe solo con un comando de salida. Dentro, leeremos inputs del usuario con input() y usaremos condicionales (if-elif-else) para manejar comandos como “agregar”, “listar”, “completar” o “salir”.

Para eficiencia, incorporaremos list comprehensions para generar listas derivadas, como filtrar tareas pendientes. Recuerda: una comprehension es como un atajo elegante para bucles – transforma datos en una sola línea, pero la explico paso a paso para que la domines.

Primero, explica el flujo: el programa carga tareas de un archivo .txt al inicio (lo cubriremos pronto), luego entra en el bucle CLI. Cada comando procesa los datos usando bucles for para iterar sobre diccionarios o lists.

Aquí un snippet del bucle principal. Agrega esto a tu archivo y ejecútalo.

# Diccionario de tareas (inicial vacío)
tareas = {}

# Bucle principal del CLI
while True:
    comando = input("Ingrese comando (agregar, listar, completar, salir): ").strip().lower()
    
    if comando == "salir":
        break
    elif comando == "listar":
        if not tareas:
            print("No hay tareas.")
        else:
            # Usamos list comprehension para formatear la salida
            lista_tareas = [f"ID {id}: {desc} ({estado})" for id, (desc, estado) in tareas.items()]
            for tarea in lista_tareas:
                print(tarea)
    elif comando == "agregar":
        desc = input("Descripción: ")
        id_nuevo = len(tareas) + 1  # ID secuencial
        tareas[id_nuevo] = (desc, "pendiente")  # Tupla inmutable
        print(f"Tarea {id_nuevo} agregada.")
    elif comando == "completar":
        id_completar = int(input("ID a completar: "))
        if id_completar in tareas:
            desc, _ = tareas[id_completar]  # Desempaquetamos tupla
            tareas[id_completar] = (desc, "completada")  # Nueva tupla
            print(f"Tarea {id_completar} completada.")
        else:
            print("ID no encontrado.")
    else:
        print("Comando inválido.")
Python

Observa cómo usamos condicionales para ramificar la lógica. El bucle while mantiene el programa vivo hasta “salir”. La comprehension [f"ID {id}: {desc} ({estado})" for id, (desc, estado) in tareas.items()] es como decir: “Para cada ítem en el diccionario, crea una cadena formateada en una lista”. Es eficiente y legible – pruébalo agregando y listando tareas.

Manejo de Etiquetas con Sets

Las etiquetas añaden flexibilidad. Para cada tarea, asociamos un set de etiquetas en otro diccionario: {id: set_etiquetas}. Sets son ideales porque add() ignora duplicados automáticamente.

Imagina etiquetas como pegatinas en una nota: únicas y fáciles de agregar/quitar. Extiende el código anterior con esto.

# Diccionario para etiquetas: {id: set()}
etiquetas = {}

# En el comando "agregar", después de crear la tarea:
etiquetas[id_nuevo] = set()  # Set vacío inicial

# Nuevo comando para agregar etiqueta
elif comando == "etiquetar":
    id_tarea = int(input("ID de tarea: "))
    if id_tarea in tareas:
        etiqueta = input("Etiqueta: ")
        if id_tarea not in etiquetas:
            etiquetas[id_tarea] = set()
        etiquetas[id_tarea].add(etiqueta)  # Agrega al set, ignora duplicados
        print(f"Etiqueta '{etiqueta}' agregada a tarea {id_tarea}.")
    else:
        print("ID no encontrado.")

# En "listar", modifica la comprehension para incluir etiquetas
lista_tareas = [
    f"ID {id}: {desc} ({estado}) [Etiquetas: {', '.join(etiquetas.get(id, set()))}]"
    for id, (desc, estado) in tareas.items()
]
Python

Aquí, set() asegura unicidad. El método add() es como invitar a alguien a la fiesta solo si no está ya. Usa join() en una comprehension para mostrarlas – profundo pero accesible.

Persistencia con E/S en Archivos TXT Línea por Línea

Sin persistencia, las tareas se pierden al salir. Usaremos E/S con open() para leer/escribir en tareas.txt línea por línea. Cada línea será como “id|descripcion|estado|etiqueta1,etiqueta2”.

Al inicio, carga con un bucle for sobre el archivo. Al salir, guarda con otro bucle. Usa condicionales para manejar archivos vacíos.

Agrega esto al principio y al final.

# Cargar tareas desde archivo
tareas = {}
etiquetas = {}
try:
    with open("tareas.txt", "r") as archivo:
        for linea in archivo:  # Línea por línea
            partes = linea.strip().split("|")
            if len(partes) >= 3:
                id_tarea = int(partes[0])
                desc = partes[1]
                estado = partes[2]
                tareas[id_tarea] = (desc, estado)
                etiquetas[id_tarea] = set(partes[3].split(",")) if len(partes) > 3 else set()
except FileNotFoundError:
    pass  # Archivo no existe, empezar vacío

# Al final del bucle while, antes de break en "salir":
with open("tareas.txt", "w") as archivo:
    for id_tarea, (desc, estado) in tareas.items():
        etiq_str = ",".join(etiquetas.get(id_tarea, set()))
        archivo.write(f"{id_tarea}|{desc}|{estado}|{etiq_str}\n")  # Escribe línea por línea
Python

Esto usa bucles para procesar líneas, condicionales para errores, y comprehensions implícitas en splits. Como una libreta: lees página por página, escribes igual.

Optimizaciones con Comprehensions y Pruebas

Para pulir, usa comprehensions para filtrar, como tareas pendientes: pendientes = {id: data for id, data in tareas.items() if data[1] == "pendiente"}. Es como filtrar correos no leídos en una bandeja.

Prueba todo: agrega tareas, etiqueta, completa, lista y sal. Reinicia y verifica la persistencia. Si dominas esto, has internalizado estas estructuras – no solo las has visto.

Resumen del Capítulo

  • Estructuras de datos integradas: Usamos listas para colecciones ordenadas, tuplas para tareas inmutables, diccionarios para modelado {id: datos}, y sets para etiquetas únicas sin duplicados.
  • Lógica de control: Implementamos condicionales (if-elif-else) para comandos CLI, bucles (while y for) para iteraciones, y comprehensions para transformaciones eficientes de datos.
  • Entrada/Salida con archivos: Cargamos y guardamos datos en .txt línea por línea usando open(), manejando errores con try-except para robustez.
  • Funcionalidades del proyecto: Comandos para agregar tareas, listarlas con etiquetas, completarlas, etiquetarlas y salir, todo en un CLI interactivo.
  • Mejores prácticas: Código comentado, legible y optimizado; explicaciones paso a paso con analogías para una comprensión profunda.
  • Ejecución y pruebas: Guarda en gestor_tareas.py, ejecuta con python gestor_tareas.py, y experimenta para dominar el flujo.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio