Programa de gestión de tareas en Python

Oct 08 2020

Hice un programa simple en Python

  • almacena tareas en el archivo
  • permite al usuario gestionarlos.

Me interesa si mi implementación es eficiente y si puedo hacer algo mejor. No estoy seguro de si cargar todos los datos del archivo en la memoria y guardarlos nuevamente en el archivo es una buena manera de implementar tareas de guardado de listas.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os


CHOICE_LIST_TASKS = 1
CHOICE_ADD_TASK = 2
CHOICE_REMOVE_TASK = 3
CHOICE_QUIT = 4


def read_int(prompt='> ', errmsg='Invalid number!'):
    num = None
    while num is None:
        try:
            num = int(input(prompt))
        except ValueError:
            print(errmsg)
    return num


def display_menu():
    print('What do you want to do?')
    print('[1] List all tasks')
    print('[2] Add a new task')
    print('[3] Delete task')
    print('[4] Quit')


choice = None
file = None
if not os.path.exists('tasks'):
    file = open('tasks', 'w+')
else:
    file = open('tasks', 'r+')
tasks = []

for line in file.readlines():
    tasks.append(line.strip())

while choice != CHOICE_QUIT:
    display_menu()
    choice = read_int()
    if choice == CHOICE_LIST_TASKS:
        if len(tasks) == 0:
            print('Task list is empty!')
        else:
            print('Tasks:')
            for x in tasks:
                print(x)
    elif choice == CHOICE_ADD_TASK:
        desc = input('Which task do you want to do? ')
        index = tasks.append(desc)
        print('Successfully added a new task!')
    elif choice == CHOICE_REMOVE_TASK:
        if len(tasks) == 0:
            print('Task list is empty!')
        else:
            for x in tasks:
                print(tasks.index(x), x)
            index = read_int('Which task do you want to delete? ')
            try:
                tasks.pop(index)
                print('Successfully deleted task!')
            except IndexError:
                print('Please enter proper task number')
    elif choice == CHOICE_QUIT:
        print('Good bye!')
    else:
        print('Invalid choice!')

file = open('tasks', 'w+')
for x in tasks:
    file.write(x + '\n')

Respuestas

3 AryanParekh Oct 08 2020 at 20:53

Empecemos desde arriba.

CHOICE_LIST_TASKS = 1
CHOICE_ADD_TASK = 2
CHOICE_REMOVE_TASK = 3
CHOICE_QUIT = 4

Para mí está claro que desea crear variables y asignarles ciertos números enteros para no tener que usar números mágicos en ningún lado, felicitaciones por esto. Hay una manera mucho mejor de implementar esta idea en Python

Enumeraciones: Enum

Enumeraciones en Python

Una enumeración es un conjunto de nombres simbólicos
(miembros) ligados a valores constantes únicos

creando un Enum

class Choices(Enum):
    list_task = 1
    add_task = 2 
    remove_task = 3
    quit = 4

Esto ya parece mucho más claro que las variables globales aleatorias. También puede recorrer la enumeración y mostrar sus valores con nombres .

print(Choices.add_task.name)

agregar tarea

print(Choices.add_task.value) 

1

Usa más funciones

file = None
if not os.path.exists('tasks'):
    file = open('tasks', 'w+')
else:
    file = open('tasks', 'r+')
tasks = []

for line in file.readlines():
    tasks.append(line.strip())

¿Por qué no podemos mover esto a una función llamada read_task_file()?
De esta manera, es extremadamente claro para mí, usted, usted después de un año leyendo proyectos antiguos, y cualquier otra persona que alguna vez lea este código, que esas pocas líneas de código leerán el archivo de tareas . Sin eso, uno debe leer el código a fondo y comprender su propósito.

def read_task_file():
    file = None
    if not os.path.exists('tasks'):
        file = open('tasks', 'w+')
    else:
        file = open('tasks', 'r+')
    tasks = []
    for line in file.readlines():
        tasks.append(line.strip())
    return tasks

ahora conseguir las tareas es

tasks = read_task_file()

Esto mismo se aplica al siguiente conjunto de código, que permitirá al usuario ingresar su elección para que podamos realizarla.

Factorizar la parte en la que leemos una elección del usuario y la realizamos en una función se vería como

def read_task_choice():
    display_menu()
    choice = read_int()
    if choice not in Choices._value2member_map_:
        print("Invalid input! Please enter a correct choice\n")
        read_task_choice()

Nota: Choices._value2member_map_serán todos los valores en una enumeración.

¡Esto ya se ve mucho más limpio! Eliminamos la necesidad de tenerlo en un bucle donde verificará todos los casos, la función solo regresa choicecuando hay una entrada válida.

Como dije, factorice el trabajo en funciones y todas ellas cuando sea necesario. He hecho lo mismo para los 4 trabajos principales (las 4 opciones).


Lista de tareas

def list_tasks(tasks):
    if len(tasks) == 0:
        print("Task list is empty!")
        return None


    for i in range(len(tasks)):
        print(f"{i} {tasks[i]}")

Agregar nueva tarea

def add_new_task(tasks):
    return input("What task would you like to finish ?: ")
    tasks.append(task)

Quitar tarea

def remove_task(tasks):
    if len(tasks) == 0:
        print("Task list is already empty!")
        return None

    list_tasks(tasks)
    task = int(input(("Which task would you like to remove: ?")))

    if task < 0 or task >= len(tasks):
        print("Invalid input! Please select an appropriate task")
        remove_task(tasks)
    tasks.pop(task)

Y finalmente

Actualizar archivo de tareas

def update_task_file(tasks):
    file = open('tasks', 'w+')
    for x in tasks:
        file.write(x + '\n')

¿De qué sirve tener millones de funciones cuando puedo poner todo como ya lo he hecho? Ahora cuando tienes que jugar el juego

tasks = read_task_file()

while True:
    choice = read_task_choice()
    if choice == Choices.list_task.value:
        list_tasks(tasks)

    elif choice == Choices.add_task.value:
        add_new_task(tasks)
        

    elif choice == Choices.remove_task.value:
        remove_task(tasks)

    else:
        break

    update_task_file(tasks)

Esta es la principal ventaja de tener funciones fuertemente tipadas.

Limpiar la pantalla y esperar la entrada

Debido a la ausencia de gráficos, podemos ver que después de algunas elecciones el terminal se ve un poco extraño

Podemos mejorar esto simplemente borrando la pantalla después de cada elección y luego esperar a que el usuario presione una tecla

Limpiar la terminal

Hay algunas maneras. Una forma popular es usarla os.system("cls")si está en Windows, pero llamar a esto es bastante costoso y también hace que su programa dependa más de la plataforma.

Tu puedes hacer

print(chr(27) + "[2J")

Y esto también funcionará.

Esperando entrada

Agregar esta línea input("Press any key to continue...")esperará la respuesta del usuario antes de mostrar todas las opciones nuevamente, mejorando la experiencia.

Final

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
from enum import Enum

class Choices(Enum):
    list_task = 1
    add_task = 2
    remove_task = 3
    quit = 4

def read_int(prompt='> ', errmsg='Invalid number!'):
    num = None
    while num is None:
        try:
            num = int(input(prompt))
        except ValueError:
            print(errmsg)
    return num

def display_menu():
    print('What do you want to do?')
    print('[1] List all tasks')
    print('[2] Add a new task')
    print('[3] Delete task')
    print('[4] Quit')

def read_task_file():
    file = None
    if not os.path.exists('tasks'):
        file = open('tasks', 'w+')
    else:
        file = open('tasks', 'r+')
    tasks = []
    for line in file.readlines():
        tasks.append(line.strip())
    return tasks

def update_task_file(tasks):
    file = open('tasks', 'w+')
    for x in tasks:
        file.write(x + '\n')

def read_task_choice():
    display_menu()
    choice = read_int()
    if choice not in Choices._value2member_map_:
        print("Invalid input! Please enter a correct choice\n")
        read_task_choice()
    return choice

def add_new_task(tasks):
    task = input("What task would you like to do: ")
    tasks.append(task)

def list_tasks(tasks):
    if len(tasks) == 0:
        print("Task list is empty!")
        return None


    for i in range(len(tasks)):
        print(f"{i} {tasks[i]}")

def remove_task(tasks):
    if len(tasks) == 0:
        print("Task list is already empty!")
        return None

    list_tasks(tasks)
    task = int(input(("Which task would you like to remove: ?")))

    if task < 0 or task >= len(tasks):
        print("Invalid input! Please select an appropriate task")
        remove_task(tasks)
    tasks.pop(task)

def clear_screen():
    print(chr(27) + "[2J")

def pause():
    input("Press any key to continue...")

tasks = read_task_file()

while True:
    clear_screen()
    choice = read_task_choice()
    if choice == Choices.list_task.value:
        list_tasks(tasks)

    elif choice == Choices.add_task.value:
        add_new_task(tasks)


    elif choice == Choices.remove_task.value:
        remove_task(tasks)

    else:
        break

    update_task_file(tasks)
    pause()