Project · after Level 3

Todo List with Save ✅

A todo list is the 'hello world' of persistent apps. Add tasks, list them, mark them done, and, crucially, have them still be there tomorrow. This is where your programs stop forgetting everything the moment they end.

Difficulty 🐍🐍🐍🐍🐍

📋 Build this

  • Show a menu: add, list, mark done, quit.
  • Store tasks as a list of dictionaries, each with a title and a done flag.
  • Save to a JSON file on every change, load it on start.
  • Survive a missing or empty file gracefully.

Hints, if you want them

Try the spec cold first. Open a hint only when you are properly stuck; the struggle is where the learning is.

Hint 1: The data shape
A task is {"title": "...", "done": False}. The whole list is a list of those, which is exactly what JSON stores (Lesson 23).
Hint 2: Load and save
On start, read the JSON file if it exists, else start with an empty list (Lesson 21). After every add or change, write the whole list back with json.dumps(tasks, indent=2).
Hint 3: The menu loop
A while True: loop that reads a choice and dispatches with if/elif or a match (Lesson 7). Break on quit, saving first.

The reference solution

Yours does not need to match this. There are many good ways to build any of these. Compare only after you have your own working.

Reveal the reference solution
import json
from pathlib import Path

FILE = Path("tasks.json")


def load():
    if FILE.exists():
        return json.loads(FILE.read_text(encoding="utf-8"))
    return []


def save(tasks):
    FILE.write_text(json.dumps(tasks, indent=2), encoding="utf-8")


def show(tasks):
    if not tasks:
        print("  (no tasks yet)")
    for i, task in enumerate(tasks, start=1):
        mark = "x" if task["done"] else " "
        print(f"  {i}. [{mark}] {task['title']}")


def main():
    tasks = load()
    while True:
        choice = input("add / list / done / quit: ").strip().lower()
        if choice == "add":
            title = input("  Title: ")
            tasks.append({"title": title, "done": False})
            save(tasks)
        elif choice == "list":
            show(tasks)
        elif choice == "done":
            show(tasks)
            n = int(input("  Which number: "))
            tasks[n - 1]["done"] = True
            save(tasks)
        elif choice == "quit":
            save(tasks)
            print("Saved. Bye.")
            break


if __name__ == "__main__":
    main()
add / list / done / quit: add
  Title: Write the todo app
add / list / done / quit: add
  Title: Eat lunch
add / list / done / quit: list
  1. [ ] Write the todo app
  2. [ ] Eat lunch
add / list / done / quit: done
  1. [ ] Write the todo app
  2. [ ] Eat lunch
  Which number: 1
add / list / done / quit: list
  1. [x] Write the todo app
  2. [ ] Eat lunch
add / list / done / quit: quit
Saved. Bye.

Stretch goals

+250 XP