Day 051 - Project 51 : ToDo list with saving

If you have any questions, comments or issues with this project please post them here!

For anyone who gets an unexpected EOF error with the Day 51 example code and are struggling…

The file you are trying to write to needs to be populated with an empty list [] beforehand.

Thought I’d post the solution here to hopefully help someone out.

3 Likes

For file auto-saves: why don’t we use “a+” instead of “w”? It seems like we’re trying to append items to the list, not rewrite the whole list. I know it works, just curious as to why it does.

Autosave is designed to use as little code as possible, if we used a+ it would append after the [] of the list. Originally I’d used this as a quick way for students to memorise code snippets to use in exams but it’s a neat way of dumping the contents of a list and being able to use things like .remove and have the changes propogate.

2 Likes

Hi,
I have a problem with the auto-save trick.

The file doesn’t get updated in the first loop but gets updated with the first task after the second loop. I printed the list to test that everything works. But I still don’t understand why the file doesn’t get updated on the first go ?

I will keep scratching my mind to understand what’s going on, but I would be open to some help from the community. Thanks !

import os, time

todo = []

#f = open("todo.txt", 'r')
#todo = eval(f.read())
#f.close()


def prettyprint(sublist):
  print(f"""
  {'Task':<10}{'->':^10}{sublist[0]:>10} 
  {'Due time':<10}{'->':^10}{sublist[1]:>10}
  {'Priority':<10}{'->':^10}{sublist[2]:>10}""")


while True:
  #os.system('clear')
  title = "YOUR TODO LIST"
  print(f"{title:^6}")
  print()
  menu = input('add, view, edit or remove an item ? > ').strip().lower()
  if menu == "add":
    item = input("what to do ? > ").strip().lower()
    due = input("when is it due ? > ").strip().lower()
    priority = input(
      "is the priority high, medium or low ? > ").strip().lower()
    todo.append([item, due, priority])
    print('the task has been added to the list, select view to check it out')
    time.sleep(3)
  elif menu == "view":
    type_of_view = input(
      "Do you want to view high, medium or low priority, or all items ? > "
    ).strip().lower()
    print()
    if type_of_view[0] == 'a':
      print("ALL YOUR TASKS")
      for task_number in range(len(todo)):
        prettyprint(todo[task_number])
    elif type_of_view[0] == 'h':
      print("HIGH PRIORITY TASKS")
      for task_number in range(len(todo)):
        if todo[task_number][2] == 'high':
          prettyprint(todo[task_number])
    elif type_of_view[0] == 'm':
      print("MEDIUM PRIORITY TASKS")
      for task_number in range(len(todo)):
        if todo[task_number][2] == 'medium':
          prettyprint(todo[task_number])
    elif type_of_view[0] == 'l':
      print("LOW PRIORITY TASKS")
      for task_number in range(len(todo)):
        if todo[task_number][2] == 'low':
          prettyprint(todo[task_number])
    print()
    input("press any key to return to the menu >")

  elif menu == "edit":
    item_to_edit = input("which task do you want to edit ? > ").strip().lower()
    for sub_todo in range(len(todo)):
      if item_to_edit == todo[sub_todo][0]:
        print(
          f"the task you selected is to {todo[sub_todo][0]} before {todo[sub_todo][1]} with a {todo[sub_todo][2]} priority."
        )
        info_to_edit = input(
          "do you want to change the task, the time or priority ? > ").strip(
          ).lower()
        if info_to_edit == 'task':
          new_task = input("what's the new task ? > ").strip().lower()
          todo[sub_todo][0] = new_task
        elif info_to_edit == 'time':
          new_time = input("what's the new due time ? > ").strip().lower()
          todo[sub_todo][1] = new_time
        elif info_to_edit == 'priority':
          new_priority = input("what's the new priority ? > ").strip().lower()
          todo[sub_todo][2] = new_priority
      print("the information has been changed, returning to the menu")
      time.sleep(2.5)
  elif menu == "remove":
    item_to_remove = input("which task did you finish ? > ").strip().lower()
    for sub_todo in range(len(todo)):
      if todo[sub_todo][0] == item_to_remove:
        todo.pop(sub_todo)
        print("Well done ! The list is updated, select view to check it out.")
        time.sleep(3)
        break
  else:
    print("instruction unclear, please input add, view, edit or remove")
    time.sleep(3)

  print(todo)  # test: the todo list is up to date
  f = open("to.do", "w")
  f.write(str(todo))
  f.close

Well, I still can’t figure out what’s wrong.
To be clear let me give an example.
1/ I select add from the menu and enter the task > run, tomorrow, high.
2/ The program runs and my empty list todo is now printed [[run, tomorrow, high]]
3/ Here comes the issue, the f.write(str(todo)) does not write anything to the file ?!
4/ The loop is done and I add another task > read, today, medium.
5/ Now the todo is printed [[run, tomorrow, high], [read, today, medium]]
6/ And in the file appear the first task only [run, tomorrow, high]

I think it has to do with how I constructed my add block but still don’t get what’s wrong.

If you could give it a go and tell me if you encounter the same issue. Thanks !

The problem might be that you can not saving the list to the file and also the flie name is to.do but it is meant to be a .txt file.

This is my code that saves the tasks.

import os, time

todo = []

#f = open("todo.txt", 'r')
#todo = eval(f.read())
#f.close()


def prettyprint(sublist):
  print(f"""
  {'Task':<10}{'->':^10}{sublist[0]:>10} 
  {'Due time':<10}{'->':^10}{sublist[1]:>10}
  {'Priority':<10}{'->':^10}{sublist[2]:>10}""")


while True:
  #os.system('clear')
  title = "YOUR TODO LIST"
  print(f"{title:^6}")
  print()
  menu = input('add, view, edit or remove an item ? > ').strip().lower()
  if menu == "add":
    item = input("what to do ? > ").strip().lower()
    due = input("when is it due ? > ").strip().lower()
    priority = input(
      "is the priority high, medium or low ? > ").strip().lower()
    todo.append([item, due, priority])
    print('the task has been added to the list, select view to check it out')
    f = open("todo.txt", "w")
    f.write(str(todo))
    f.close
    time.sleep(3)
  elif menu == "view":
    type_of_view = input(
      "Do you want to view high, medium or low priority, or all items ? > "
    ).strip().lower()
    print()
    if type_of_view[0] == 'a':
      print("ALL YOUR TASKS")
      for task_number in range(len(todo)):
        prettyprint(todo[task_number])
    elif type_of_view[0] == 'h':
      print("HIGH PRIORITY TASKS")
      for task_number in range(len(todo)):
        if todo[task_number][2] == 'high':
          prettyprint(todo[task_number])
    elif type_of_view[0] == 'm':
      print("MEDIUM PRIORITY TASKS")
      for task_number in range(len(todo)):
        if todo[task_number][2] == 'medium':
          prettyprint(todo[task_number])
    elif type_of_view[0] == 'l':
      print("LOW PRIORITY TASKS")
      for task_number in range(len(todo)):
        if todo[task_number][2] == 'low':
          prettyprint(todo[task_number])
    print()
    input("press any key to return to the menu >")

  elif menu == "edit":
    item_to_edit = input("which task do you want to edit ? > ").strip().lower()
    for sub_todo in range(len(todo)):
      if item_to_edit == todo[sub_todo][0]:
        print(
          f"the task you selected is to {todo[sub_todo][0]} before {todo[sub_todo][1]} with a {todo[sub_todo][2]} priority."
        )
        info_to_edit = input(
          "do you want to change the task, the time or priority ? > ").strip(
          ).lower()
        if info_to_edit == 'task':
          new_task = input("what's the new task ? > ").strip().lower()
          todo[sub_todo][0] = new_task
        elif info_to_edit == 'time':
          new_time = input("what's the new due time ? > ").strip().lower()
          todo[sub_todo][1] = new_time
        elif info_to_edit == 'priority':
          new_priority = input("what's the new priority ? > ").strip().lower()
          todo[sub_todo][2] = new_priority
      print("the information has been changed, returning to the menu")
      f = open("todo.txt", "w")
      f.write(str(todo))
      f.close
    time.sleep(2.5)
  elif menu == "remove":
    item_to_remove = input("which task did you finish ? > ").strip().lower()
    for sub_todo in range(len(todo)):
      if todo[sub_todo][0] == item_to_remove:
        todo.pop(sub_todo)
        print("Well done ! The list is updated, select view to check it out.")
        time.sleep(3)
        break
  else:
    print("instruction unclear, please input add, view, edit or remove")
    time.sleep(3)

  print(todo)  # test: the todo list is up to date
  f = open("todo.txt", "w")
  f.write(str(todo))
  f.close

2 Likes

Thanks a lot !
My understanding was that the extension of the file doesn’t matter, but I apparently does !

Cheers

I don’t know, but it’s possible it’s because Python doesn’t know that that file extension should be read as text (since different files are read differently depending on their extension).

because f.close() is missing brackets. so the file never gets properly closed and the change remains in limbo until the 2nd loop.

Hello All,

Please help me figure out print statement in the “delete” function. I want to add if input is not in list element - print something. I am not sure how to structure the if-else. I tried multiple ways.

import random, os, time

myEvents = []

f = open("calendar.txt", "r")
myEvents = eval(f.read())
f.close()

def inputIdea():
  os.system("clear")
  idea = input("Input your idea: ")
  row = [idea]
  myEvents.append(row)
  print(idea, "added!")
  print()
  time.sleep(2)
  os.system("clear")
  

def generateIdea():
  os.system("clear")
  idea = random.choice(myEvents)
  print()
  print("Your random idea is: ", *idea)
  time.sleep(2)
  os.system("clear")


def deleteIdea():
  os.system("clear")
  idea_to_delete = input("What idea you would like to delete?: ")

#I want to add an "else" statement if idea_to_delete is not in the list and print something, but if I add it, it will not find my idea_to_delete if it is not the first item in list as it starts to loop one by one - please help where to put else statemnt
  for row in myEvents:
    if idea_to_delete in row:
      myEvents.remove(row)
      print()
      print(idea_to_delete, "is removed form your idea list.")

  time.sleep(2)
  os.system("clear")

while True:
  print("Idea Storage")
  print()
  usr_choice = input("'1' Enter your idea. \n'2' Display a random idea. \n'3' Delete idea. \n > ")
  
  if usr_choice == "1":
    inputIdea()
  elif usr_choice == "2":
    generateIdea()
  elif usr_choice == "3":
    deleteIdea()

  f = open("calendar.txt", "w")
  f.write(str(myEvents))
  f.close()

I’m not very sure about it myself - but i suppose you could try checking the ideea user is inputting against the list you are trying to remove it from. But here … i’ve checked your code and i’ve made a few modifications:

for the first at the beginning we check if the file is empty or not … so you won’t get errors. If it exists or not you should add try, except - but i don’t know what operators have you worked with until now.

then i have removed the row = [ideea] line - that was unnecessary!
then the for loop was really making a mess in your list :)) so i’ve removed it!
there are a few commented lines - about os.system(“clear”) - but those are not working properly on my spyder IDE and i usually don’t use it.

I’m a beginner too - so don’t know a lot yet … but started to realize how little i actually know :)))

Here is your code sort of updated:

# -*- coding: utf-8 -*-
"""
Created on Wed Feb 15 09:44:53 2023

@author: soreen
"""

import random, os, time

myEvents = []

f = open("calendar.txt", "r")
contents = f.read()
if len(contents) > 0:
    myEvents = eval(contents)
f.close()

def inputIdea():
#  os.system("clear")
  idea = input("Input your idea: ")
#  row = [idea]
  myEvents.append(idea)
  print(idea, "added!")
  print()
  time.sleep(2)
#  os.system("clear")
  

def generateIdea():
#  os.system("clear")
  idea = random.choice(myEvents)
  print()
  print("Your random idea is: ", *idea)
  time.sleep(2)
#  os.system("clear")


def deleteIdea():
#  os.system("clear")
  idea_to_delete = input("What idea you would like to delete?: ")

#I want to add an "else" statement if idea_to_delete is not in the list and print something, but if I add it, it will not find my idea_to_delete if it is not the first item in list as it starts to loop one by one - please help where to put else statemnt
  if idea_to_delete not in myEvents:
     print("No way Jose, you need to first have that ideea!")
  elif idea_to_delete in myEvents:
     myEvents.remove(idea_to_delete)
     print()
     print(idea_to_delete, "is removed form your idea list.")

  time.sleep(2)
#  os.system("clear")

while True:
  print("Idea Storage")
  print()
  usr_choice = input("'1' Enter your idea. \n'2' Display a random idea. \n'3' Delete idea. \n'4' Exit > ")
  
  if usr_choice == "1":
    inputIdea()
  elif usr_choice == "2":
    generateIdea()
  elif usr_choice == "3":
    deleteIdea()
  elif usr_choice == "4":
      f = open("calendar.txt", "w")
      f.write(str(myEvents))
      f.close()
      break

Well I hope you manage :slight_smile: I’m struggling myself!

3 Likes

Thank you very much :slight_smile:

In the solution suddenly David uses the eval() function (“todo = eval(f.read())”).
It is unclear to me what exactly that does? I’ve tried searching, but didn’t found an explanation I’ve comprehend… Perhaps someone here can explain this?
Thx in advance!

To be honest I would advise you not to do that and use Eval on files. It is a lazy to load files into lists and alike, and it is a serious security risk.

Why is it a security risk?

Because when you use eval on a file, the command will evaluate the file. This can actually be a program itself …

1 Like

Hello, everyone!

I’m almost finished with this project, but I don’t understand why my if-statement under prettyprintremove(): isn’t working. If the “else” isn’t there it works fine and deletes the task, but if the else is there it skips the “if” even when the tasks is in the list.

import os, time

todolist = []

f = open("todo.txt", "r")
todolist = eval(f.read())
f.close

def prettyprintadd():
    os.system('clear')
    title = "\033[1;34m \033[4m🤯 Adding To The List! 🤯\033[0m"
    print()
    print(f"{title:^20}")
    print()
    task = input("\033[1;37mWhat is the activity -\033[1;34m ").capitalize()
    print()
    date = input("\033[1;37mDue Date -\033[1;34m ") 
    print()
    critical = input("\033[1;37mPriority - [High(H), Medium(M) or Low(L)] -\033[1;34m ").upper()
    print()
    row = [task, date, critical]
    todolist.append(row)
    print("Task has been added!")
    os.system('clear')
    return

def prettyprintview():
    os.system('clear')
    title = "\033[1;32m \033[4mPresent To-Do list\033[0m"
    print(f"{title:^20}")
    print()
    for row in todolist:
        for item in row:
            print(item, end=" | ")
        print()
        print()
    last_sen = "\033[1;32mEnd of the list\033[0m"
    print(f"{last_sen:>47}")
    time.sleep(15)
    os.system("clear")
    return


#problem area
def prettyprintremove():
    os.system('clear')
    print("\033[1;31m \033[4mRemoving Some Tasks!\033[0m")
    print()
    for row in todolist:
        for item in row:
            print(item, end=" | ")
        print()
        print()
    task = input("\033[1;31mWhat do you want to remove?\033[0m ").capitalize()
    print()
    for row in todolist:
        if task in row:
            todolist.remove(row)
            print("\033[1;31mTask removed!")
            time.sleep(2)
            os.system('clear')
            return
        else:
            print("\033[1;31mTask not in list")
            time.sleep(2)
            os.system('clear')
            return
#end of problem area


def prettyprintedit():
    os.system("clear")
    print("\033[1;35m \033[4mEditing the list!\033[0m")
    print()
    for row in todolist:
        for item in row:
            print(item, end=" | ")
        print()
    print()
    find = input("\033[1;35mWhat do you want to change?\033[0m ").capitalize()
    print()
    found = False
    for row in todolist:
        if find in row:
            found = True
    if not found:
        print("\033[1;35mThat task is not in the list")
        time.sleep(4)
        print()
        os.system('clear')
        return
    for row in todolist:
        if find in row:
            todolist.remove(row)
    task = input("\033[1;35mThe new activity -\033[0m ").capitalize()
    print()
    date = input("\033[1;35mDue Date -\033[0m ") 
    print()
    critical = input("\033[1;35mPriority - [High(H), Medium(M) or Low(L)] - \033[0 ").upper()
    print()
    new_row = [task, date, critical]
    todolist.append(new_row)
    print("\033[1;35mTask has been changed!\033[0m ")
    print()
    os.system("clear")
    for row in todolist:
        for item in row:
            print(item, end=" | ")
        print()
    time.sleep(20)
    os.system("clear")
    return  


while True:
    title = "\033[1;35m \033[4m📝 To Do List 📝\033[0m"
    print(f"{title:^20}")
    print()
    options = int(input("\033[1;34m Add -> 1\n \033[1;32mView -> 2\n \033[1;31mRemove -> 3\n \033[0;37mEdit -> 4\n\n \033[1;35mWhat Would You Like To Do - "))
    print()
    if options == 1:
        prettyprintadd()
    elif options == 2:
        prettyprintview()
    elif options == 3:
        prettyprintremove()
    elif options == 4:
        prettyprintedit()
    else:
        print("Goodbye !☺️")
        exit()

    f = open("todo.txt", "w")
    f.write(str(todolist))
    f.close()

Why do you print something and then return it all in one function?

1 Like

I thought the return had to be on the bottom. Even when I remove them the if- statement still doesn’t work.