We'll build a todo app that persists tasks to a JSON file. It will support adding, listing, completing, and deleting tasks. This uses file I/O, JSON, functions, and all the other concepts from previous lessons.
import json
import os
import sys
from datetime import datetime
TODO_FILE = "todos.json"
def load_todos():
if not os.path.exists(TODO_FILE):
return []
with open(TODO_FILE, "r") as f:
return json.load(f)
def save_todos(todos):
with open(TODO_FILE, "w") as f:
json.dump(todos, f, indent=2)
def add_todo(text):
todos = load_todos()
todo = {
"id": len(todos) + 1,
"text": text,
"done": False,
"created": datetime.now().isoformat()
}
todos.append(todo)
save_todos(todos)
print(f"Added: {text}")
def list_todos():
todos = load_todos()
if not todos:
print("No todos yet. Add some!")
return
for todo in todos:
status = "✓" if todo["done"] else "○"
print(f"[{todo['id']}] {status} {todo['text']}")
def complete_todo(todo_id):
todos = load_todos()
for todo in todos:
if todo["id"] == todo_id:
todo["done"] = True
save_todos(todos)
print(f"Completed: {todo['text']}")
return
print(f"No todo with id {todo_id}")
def delete_todo(todo_id):
todos = load_todos()
todos = [t for t in todos if t["id"] != todo_id]
save_todos(todos)
print(f"Deleted todo {todo_id}")
def main():
if len(sys.argv) < 2:
print("Usage: python todo.py [add|list|done|delete] [args]")
return
command = sys.argv[1]
if command == "add" and len(sys.argv) > 2:
add_todo(" ".join(sys.argv[2:]))
elif command == "list":
list_todos()
elif command == "done" and len(sys.argv) > 2:
complete_todo(int(sys.argv[2]))
elif command == "delete" and len(sys.argv) > 2:
delete_todo(int(sys.argv[2]))
else:
print("Unknown command or missing arguments")
if __name__ == "__main__":
main()
Save this as todo.py and try it out. The if __name__ == "__main__": pattern means the main() function only runs when you execute the file directly, not when it's imported as a module — a crucial Python convention.
python todo.py add "Buy groceries"
python todo.py add "Write code"
python todo.py list
python todo.py done 1
python todo.py list
python todo.py delete 2
◆ Note
Next steps: try adding a "due date" field, or rewrite the file storage to use SQLite instead of JSON (hint: look at the sqlite3 module in the standard library).