Ejercicios Python

Reto de python para crear un gestor de tareas en la consola

Capítulo 2: Gestor de tareas en consola con Python

    Dificultad:

Requisitos para superar el reto

Crees que este reto es muy complicado o no lo sabes resolver? No te preocupes, haz este curso básico de Python y vuelve aquí:

Curso básico de Python.

A continuación, tienes un listado de todo lo que necesitas saber para afrontar el reto y superarlo.

  • Variables
  • Tipos de datos básicos
  • Manejo básico de cadenas de caracteres
  • Funciones print()
  • Manejo básico de bucles
  • Condicionales
  • Operadores aritméticos
  • Operadores lógicos
  • Manejo de listas
  • Funciones
Todo esto y mucho más, lo aprenderás en ese curso básico de Python.

Además, estos temas que no se han dado en el curso básico: (Te daré las indicaciones necesarias para que no sea un problema.)

  • Manejo básico de módulos e importaciones
  • Uso de la función predefinida enumerate()
  • Bucles con dos iteradores

Objetivo principal del ejercicio

El objetivo del ejercicio, es crear un programa de consola para gestionar tareas, con varias funcionalidades.

Para conseguir esto, no solo tendrás que saber la teórica de mi curso básico de Python, si no que también, deberás seguir ciertas instrucciones para programar con temas que no se han dado en ese curso.
La idea de hacer esto, es que vayas saliendo de tu zona de confort, y empieces a aprender sobre la marcha, a medida que practicas.

Primero te voy a indicar aquí los componentes que requiere esta aplicación, y a partir de ahí, ves consultando la solución hasta donde vayas necesitando. Ponte a prueba todo lo que quieras, o ves simplemente aprendiendo a medida que ves como se resuelve cada parte.

Componentes de la aplicación

El programa contará con dos módulos:

  • app.py
  • funciones.py

El primer módulo (app.py) contendrá la parte esencial de presentación para el usuario.

La interfaz de usuario la formará un bucle indeterminado (while True), con un menú.

El segundo módulo contendrá una lista para gestionar las tareas y cuatro funciones, que operarán toda la lógica de nuestra aplicación.

Estas son las funcionalidades que hay que crear:

  • Añadir tarea
  • Ver tareas
  • Marcar tarea como completada
  • Eliminar tarea
  • Salir del programa

Crear los archivos para los módulos

Todavía no he explicado el tema de módulos en el curso básico, así que no te preocupes si no sabes sobre este tema, ya que te voy a explicar lo que haga falta.

Para crear un módulo de Python, tienes que crear un archivo con extensión .py.

Entonces, eso es lo que quiero que hagas. Crea estos dos archivos:

  • app.py
  • funciones.py

Ambos deberán estar en la raíz del proyecto. Sin carpetas.

app.py

Una vez creados los dos módulos, abre el módulo app.py. En él, escribe en la primera línea el siguiente fragmento:

from funciones import *

Esto es una importación. Con ella, tendrás acceso automático a todos los componentes del módulo importado, en este caso el de funciones.py.

Si ya manejas importaciones con Python, haz la importación de la forma que quieras. Por ejemplo así:

import funciones

Esta segunda forma es la que utilizaré en la resolución del proyecto.


Crear un menú para la interfaz de usuario

El siguiente paso es el de crear un menú para los usuarios. Este menú tendrá que tener las siguientes opciones:

  • Título de tu aplicación
  • 1. Añadir tarea
  • 2. Ver tareas
  • 3. Marcar tarea como completada
  • 4. Eliminar tarea
  • 5. Salir del programa

En un menú de consola es importante que marques de alguna forma las opciones, por ejemplo, con números, como puedes ver en este listado.

# Importación de la lógica
import funciones

# Bucle principal
while True:
    print("\n***** --- Gestor de tareas de ConsoleTec --- ******\n ")
    print("1. Añadir tarea")
    print("2. Ver tareas")
    print("3. Marcar tarea como completada")
    print("4. Eliminar tarea")
    print("5. Salir")

    # Entrada para el usuario
    opcion = input("Introduzca una opción: ")

    print("\n")

    # Menú de opciones
    match opcion:
        case "1":
            pass
    
        case "2":
			pass
    
        case "3":
			pass
    
        case "4":
			pass
    
        case "5":
			pass
    
        case _:
            pass

El menú es simplemente un bucle indeterminado, que permite mediante una entrada, que el usuario elija opciones, hasta que decida salir.

Añadir la funcionalidad de salida y opción inválida

Lo siguiente que hay que hacer, es añadir la funcionalidad de salida del programa (opción 5). Para ello, avisa al usuario de que se está saliendo del programa, y utiliza uno de los elementos de Python para romper la ejecución del bucle.

También añade un mensaje que avise de que se introdujo una opción inválida, si no está contemplada en el condicional.

Esta sería una posible solución:

# Importación de la lógica
import funciones

# Bucle principal
while True:
	print("\n***** --- Gestor de tareas de ConsoleTec --- ******\n ")
	print("1. Añadir tarea")
	print("2. Ver tareas")
	print("3. Marcar tarea como completada")
	print("4. Eliminar tarea")
	print("5. Salir")

	# Entrada para el usuario
	opcion = input("Introduzca una opción: ")

	print("\n")

	# Menú de opciones
	match opcion:
		case "1":
			pass
	
		case "2":
			pass
	
		case "3":
			pass
	
		case "4":
			pass
	
		case "5":
			print("Gracias por usar el software de ConsoleTec. Desconectando...")
			break
	
		case _:
			print("Opción inválida.")

funciones.py

Vamos a crear la lógica de la aplicación. De momento, el esqueleto.

Primero añade una lista sobre la que ir guardando las tareas.

Después, añade las funciones.

Las cuatro funcionalidades que forman la lógica son estas:

  • Añadir tarea
  • Ver tareas
  • Marcar tarea como completada
  • Eliminar tarea
# Lista de tareas
tareas = []

# Funciones del programa
def agregar_tarea(lista):
	pass

def ver_tareas(lista):
	pass

def tarea_completada(lista):
	pass

def eliminar_tarea(lista):
	pass

Estas funciones tienen un único parámetro, que servirá para pasar la lista.

Enlazar la interfaz con la lógica

Ahora, volvemos al archivo app.py, a enlazar la interfaz con la parte lógica.
Para ello, solo tendrás que hacer llamadas a las funciones en los case correspondientes.

Para llamar a los elementos del módulo, tendrás que poner de prefijo el nombre del módulo importado. Por ejemplo, para llamar a la función agregar_tarea() del módulo funciones, lo hacemos con la sintaxis de modulo.función. Aunque esto depende de como hayas hecho la importación. Si la has hecho como yo, esta es la forma.

# Importación de la lógica
import funciones

# Bucle principal
while True:
    print("\n***** --- Gestor de tareas de ConsoleTec --- ******\n ")
    print("1. Añadir tarea")
    print("2. Ver tareas")
    print("3. Marcar tarea como completada")
    print("4. Eliminar tarea")
    print("5. Salir")

    # Entrada para el usuario
    opcion = input("Introduzca una opción: ")

    print("\n")

    # Menú de opciones
    match opcion:
        case "1":
            funciones.agregar_tarea(funciones.tareas)

        case "2":
            funciones.ver_tareas(funciones.tareas)

        case "3":
            funciones.tarea_completada(funciones.tareas)

        case "4":
            funciones.eliminar_tarea(funciones.tareas)

        case "5":
            print("Gracias por usar el software de ConsoleTec. Desconectando...")
            break

        case _:
            print("Opción inválida.")

Completar la lógica

Una vez está enlazada la interfaz con la lógica, es el momento de ir completando cada una de las funciones.

Función agregar_tarea()

Esta función servirá para añadir nuevas tareas. Debes completar todos los pasos que puedas. Hazlos de la forma que creas conveniente. No tiene porqué ser igual que mi solución.

# Funciones del programa
def agregar_tarea(lista):
    # Entrada para la tarea
	
    # Añadir la tarea al final de la lista
	
    # Informe de tarea añadida
	
    # Imprime la tarea añadida
	
    # Informa del número de tarea
def agregar_tarea(lista):
    # Entrada para la tarea
    tarea = input("Introduzca la descripción de la tarea: ")
	
    # Añadir la tarea al final de la lista
    lista.append(tarea)
	
    # Informe de tarea añadida
    print("\nLa tarea se añadió a la lista de tareas pendientes.\n")
	
    # Imprime la tarea añadida
    print("La tarea añadadida es esta:")
	
    print(f"{tarea}")
	
    # Informa del número de tarea
    print(f"La tarea se almacenó en la posición {len(lista)}\n")
  • Línea 1: def agregar_tarea(lista):
    • Se define la función agregar_tarea(), con un parámetro.
  • Línea 3: tarea = input("Introduzca la descripción de la tarea: ")
    • Entrada de datos para el usuario. En ella, podrá introducir un str con la tarea.
  • Línea 6: lista.append(tarea)
    • Esta línea añade la tarea que el usuario acaba de introducir al final de la lista. append() es un método de las listas de Python que añade un elemento al final de la lista.
      La tarea que se almacena es el str de la variable tarea, pasado como argumento para append().
  • Líneas 9, 12, 14 y 17:
    • Se informa al usuario de que la tarea se ha añadido, cuál es la tarea añadida, e informa de la posición del elemento en la lista, gracias a la función len(), que permite contar el total de elementos que tiene la lista.
      Puesto que los elementos se añaden siempre al final de la lista, siempre coincide el número de tarea añadida, con el número total de elementos de la lista.

Función ver_tareas()


Esta función servirá para añadir nuevas tareas:

def ver_tareas(lista):
    # Condicional que evalúe si algo está en la lista
    
    # Si hay algo en la lista se presenta

    # Si la lista está vacía avisa de ello

    # Mensaje de fin de listado
Para resolver esto, puedes utilizar un condicional, un bucle con doble iterador y una llamada a la función predefinida enumerate().

La función enumerate de Python

Por si no conoces la función enumerate() de Python, te la explico en un momento.

Esta función devuelve un objeto iterador. Este objeto se puede iterar con un bucle, pero para saber como, debes conocer con que datos trabaja.

Si utilizamos un bucle simple, con un iterador, veremos lo que maneja enumerate() en cada iteración:

tareas = ["Tarea A", "Tarea B", "Tarea C"]

for tarea in enumerate(tareas):
    print(tarea)
(0, 'Tarea A')
(1, ' Tarea B')
(2, 'Tarea C')

La función hace una enumeración desde el 0, hasta la última posición de la lista. Esta enumeración, la almacena en tuplas de pares de posición/valor.

Si quieres hacer un listado con los elementos de la lista, solo tienes que iterar primero la posición de número y luego, la de la tarea.

Para que quede un listado como este:

  1. Tarea A
  2. Tarea B
  3. Tarea C

Esto lo puedes hacer con un bucle con doble iterador:

tareas = ["Tarea A", "Tarea B", "Tarea C"]

for indice, tarea in enumerate(tareas):
	print(indice, tarea)
0 Tarea A
1 Tarea B
2 Tarea C

El primer iterador (indice) itera la primera posición de cada tupla producida por el enumerate(). El segundo, el valor.
Con estos dos iteradores podemos presentar el listado como queramos.

Así no necesitamos utilizar un bucle anidado, y obtenemos dos datos de una iteración del bucle.

Deberás ingeniártelas para que el contador de la lista empiece en 1, y no en 0.

def ver_tareas(lista):
    # Condicional que evalúe si algo está en la lista
    # Si hay algo en la lista se presenta
    if lista:
        for indice, tarea in enumerate(lista):
            print(f"{indice + 1}. {tarea}")
    # Si la lista está vacía avisa de ello
    else:
        print("No hay tareas pendientes.")

    # Mensaje de fin de listado
    print("--- FIN DEL LISTADO DE TAREAS ---")
  • Línea 1: def ver_tareas(lista):
    • Esta línea define una función llamada ver_tareas que toma un argumento llamado lista.
  • Línea 4: if lista:
    • Esta línea es una declaración condicional que verifica si la lista contiene algún elemento. Si la lista no está vacía, el código dentro de este bloque if se ejecutará.
  • Línea 5: for indice, tarea in enumerate(lista):
    • Esta línea es un bucle for que recorre cada elemento en la lista. La función enumerate() devuelve tanto el índice (posición) como el valor de cada elemento en la lista. Los valores de índice y elemento se almacenan en las variables indice y tarea, respectivamente.
  • Línea 6: print(f"{indice + 1}. {tarea}")
    • Esta línea imprime el número de la tarea (que es el índice más uno, para hacer que la numeración comience en 1 en lugar de 0) y la tarea misma.
  • Línea 8: else:
    • Esta línea corresponde al bloque else del condicional if. Este bloque se ejecutará si la lista está vacía.
  • Línea 9: print("No hay tareas pendientes.")
    • Esta línea imprime un mensaje que indica que no hay tareas pendientes.
  • Línea 12: print("--- FIN DEL LISTADO DE TAREAS ---")
    • Esta línea imprime un mensaje que indica el fin del listado de tareas. Este mensaje se imprime independientemente de si la lista estaba vacía o no.

Función tarea_completada()


Esta función servirá para marcar las tareas existentes en la lista, por parte del usuario.

Lo primero que hará, será desplegar el listado de tareas. Para ello. Utiliza una función ya existente en el programa. Así, el usuario no irá escogiendo una opción a ciegas.

Quedará algo así:

1. Tarea A
2. Tarea B
3. Tarea C
--- FIN DEL LISTADO DE TAREAS ---

Una vez que haya obtenido el listado de tareas, podrá introducir con una entrada, un número de opción.

Introduzca el número de la tarea a marcar como completada:

Un condicional evaluará si la opción existe en el listado, o no.

Si la opción no existe, se le avisa de ello.

Introduzca el número de la tarea a marcar como completada: 5
Opción inválida.

En cambio, si existe, utilizaremos un segundo condicional anidado.

El condicional anidado evaluará si la opción ya está marcada como completada, o no. Si ya lo está, avisa al usuario de ello. En cambio, si no lo está, deberá marcarla.

Te dejo una serie de comentarios para que tengas una guía de los pasos a completar. No obstante, lo puedes hacer como quieras. Esto es solo para que te orientes.

def tarea_completada(lista):
    # Llamamos a la función ver_tareas()

    # Entrada para que el usuario introduzca una tarea

    # Condicional para marcar tareas como completadas

        # Condicional para evaluar si la tarea ya estaba completada
        # Si la tarea ya está en la lista...

        # En cambio, si no está...

    # Avisar si la opción elegida es inválida
def tarea_completada(lista):
    # Llamamos a la función ver_tareas()
    ver_tareas(lista)

    # Entrada para que el usuario introduzca una tarea
    completada = int(input("Introduzca el número de la tarea a marcar como completada: "))

    # Condicional para marcar tareas como completadas
    if completada > 0 and completada <= len(lista):
        # Condicional para evaluar si la tarea ya estaba completada
        # Si la tarea ya está en la lista...
        if "(Completada)" in lista[completada - 1]:
            print("La tarea ya estaba marcada como completada.")
        # En cambio, si no está... 
        else:
            lista[completada - 1] = "(Completada) " + lista[completada - 1]
            print("Se marcó la tarea como completada.")
    # Avisar si la opción elegida es inválida
    else:
        print("Opción inválida.")
  • Línea 1: def tarea_completada(lista):
    • Esta línea define una función llamada tarea_completada que toma un parámetro lista.
  • Línea 3: ver_tareas(lista)
    • Esta línea llama a la función ver_tareas con lista como argumento.
  • Línea 6: completada = int(input("Introduzca el número de la tarea a marcar como completada: "))
    • Esta línea solicita al usuario que introduzca el número de la tarea a marcar como completada y lo almacena en la variable completada.
  • Línea 9: if completada > 0 and completada <= len(lista):
    • Esta línea verifica si el número de tarea introducido es válido. Es decir, si está dentro del rango de la lista de tareas.
  • Línea 12: if "(Completada)" in lista[completada - 1]:
    • Esta línea verifica si la tarea seleccionada ya está marcada como completada.
  • Línea 13: print("La tarea ya estaba marcada como completada.")
    • Si la tarea ya estaba completada, esta línea imprime un mensaje informando al usuario de ello.
  • Línea 16: lista[completada - 1] = "(Completada) " + lista[completada - 1]
    • Si la tarea no estaba completada, esta línea marca la tarea como completada añadiendo '(Completada)' al principio de la tarea en la lista.
  • Línea 17: print("Se marcó la tarea como completada.")
    • Esta línea imprime un mensaje informando al usuario de que la tarea ha sido marcada como completada.
  • Línea 20: print("Opción inválida.")
    • Si el número de tarea ingresado no es válido, esta línea imprime un mensaje informando al usuario de que la opción es inválida.

Función eliminar_tarea()


Esta función servirá para eliminar tareas pendientes. Aquí tienes las partes que tienes que completar:

def eliminar_tarea(lista):
        #Llamamos a la función ver_tareas()

        # Entrada para que el usuario introduzca una tarea

        # Opción inválida si la tarea no está en el rango de la lista

        # Si la opción es válida se elimina la tarea
    
    # Si la lista está vacía se avisa de ello

Estos comentarios te dan una perspectiva de como completar esta funcionalidad, pero hazla de la forma que quieras. No tiene porqué seguir esta estructura.

def eliminar_tarea(lista):

    if lista:
        #Llamamos a la función ver_tareas()
        ver_tareas(lista)

        # Entrada para que el usuario introduzca una tarea
        tarea = int(input("Introduzca el número de la tarea a eliminar: "))

        # Opción inválida si la tarea no está en el rango de la lista
        if tarea <= 0 or tarea > len(lista):
            print("Opción inválida.")

        # Si la opción es válida se elimina la tarea
        else:
            del lista[tarea - 1]
            print("Se eliminó la tarea.")
    
    # Si la lista está vacía se avisa de ello
    else:
        print("No hay tareas.")
  • Línea 1: def eliminar_tarea(lista):
    • Esta línea define una función llamada eliminar_tarea que toma un argumento llamado lista.
  • Línea 3: if lista:
    • Esta línea evalúa si lista no está vacía. Si lista contiene elementos, la condición es verdadera y se ejecuta el bloque de código dentro del if.
  • Línea 5: ver_tareas(tareas)
    • Esta línea llama a la función llamada ver_tareas() y le pasa lista como argumento.
  • Línea 8: tarea = int(input("Introduzca el número de la tarea a eliminar: "))
    • Esta línea solicita al usuario que introduzca un número y convierte la entrada en un entero utilizando int(). Este número se almacena en la variable tarea.
  • Línea 11: if tarea <= 0 or tarea > len(lista):
    • Esta línea verifica si el número introducido por el usuario es menor o igual a 0, o mayor que la longitud de lista. Si cualquiera de estas condiciones es verdadera, la opción es inválida.
  • Línea 12: print("Opción inválida.")
    • Si la condición en la línea anterior es verdadera, se imprime el mensaje "Opción inválida.".
  • Línea 15: else:
    • Este else se ejecuta si la condición en la línea 11 es falsa, es decir, si el número introducido es válido.
  • Línea 16: del lista[tarea - 1]
    • Esta línea elimina el elemento en la posición tarea - 1 de lista. Se usa tarea - 1 porque las listas en Python son indexadas desde cero.
  • Línea 17: print("Se eliminó la tarea.")
    • Después de eliminar la tarea, se imprime el mensaje "Se eliminó la tarea.".
  • Línea 20: else:
    • Este else se empareja con el if en la línea 3 y se ejecuta si lista está vacía.
  • Línea 21: print("No hay tareas.")
    • Si lista está vacía, se imprime el mensaje "No hay tareas.".

Comentarios

Si te quedan dudas sobre el temario, sobre Programación, Python o cualquier otra cosa relacionada o simplemente quieres agradecer, aquí tienes tu sitio para dejar tu granito de arena. Gracias por tus comentarios y por darle vida a este sitio web.

Programación Fácil YouTube

Suscríbete

Si te ha gustado este curso y crees que el trabajo merece la pena, te agradeceré eternamente que te suscribas a mi canal de YouTube para apoyarme y que pueda seguir haciendo cursos gratuitos.

Además, si te encanta la programación, tienes un montón más de cursos gratuitos para ver.

No solo eso, podrás participar enviándome comentarios con tus sugerencias para temas específicos o cursos completos o incluso las dudas que tengas y las intentaré ir resolviendo en los cursos que estén todavía abiertos.