Introduction
Keeping track of tasks is a universal need. In this tutorial, you will build a simple To-Do List application that runs in the terminal. It stores tasks persistently in a file, allows adding, viewing, marking complete, and deleting tasks. This project reinforces basic Python programming concepts while delivering a small real-world utility.
Prerequisites
- Python 3.x installed
- Basic knowledge of Python syntax (variables, loops, functions)
Project Features
- Add new tasks
- View pending and completed tasks
- Mark tasks as done
- Delete tasks
- Persistent storage in a simple text/CSV file
Code: todo.py
import os
import json
DATA_FILE = "tasks.json"
def load_tasks():
if not os.path.exists(DATA_FILE):
return {"pending": [], "done": []}
with open(DATA_FILE, "r") as f:
return json.load(f)
def save_tasks(tasks):
with open(DATA_FILE, "w") as f:
json.dump(tasks, f, indent=2)
def list_tasks(tasks):
if tasks["pending"]:
print("\nPending Tasks:")
for idx, task in enumerate(tasks["pending"], 1):
print(f"{idx}. {task}")
else:
print("\nNo pending tasks.")
if tasks["done"]:
print("\nCompleted Tasks:")
for idx, task in enumerate(tasks["done"], 1):
print(f"{idx}. {task}")
else:
print("\nNo completed tasks.")
def add_task(tasks):
task = input("Enter new task: ").strip()
if task:
tasks["pending"].append(task)
print(f"Added task: {task}")
else:
print("Task cannot be empty.")
def mark_done(tasks):
if not tasks["pending"]:
print("No pending tasks to mark.")
return
list_tasks({"pending": tasks["pending"], "done": []})
try:
choice = int(input("Enter number of task to mark as done: "))
if 1 <= choice <= len(tasks["pending"]):
task = tasks["pending"].pop(choice - 1)
tasks["done"].append(task)
print(f"Marked done: {task}")
else:
print("Invalid selection.")
except ValueError:
print("Please enter a valid number.")
def delete_task(tasks):
print("\n1. Delete from pending")
print("2. Delete from completed")
choice = input("Choice: ").strip()
if choice == "1":
if not tasks["pending"]:
print("No pending tasks to delete.")
return
list_tasks({"pending": tasks["pending"], "done": []})
try:
idx = int(input("Enter number to delete: "))
if 1 <= idx <= len(tasks["pending"]):
removed = tasks["pending"].pop(idx - 1)
print(f"Deleted pending task: {removed}")
else:
print("Invalid number.")
except ValueError:
print("Enter a number.")
elif choice == "2":
if not tasks["done"]:
print("No completed tasks to delete.")
return
list_tasks({"pending": [], "done": tasks["done"]})
try:
idx = int(input("Enter number to delete: "))
if 1 <= idx <= len(tasks["done"]):
removed = tasks["done"].pop(idx - 1)
print(f"Deleted completed task: {removed}")
else:
print("Invalid number.")
except ValueError:
print("Enter a number.")
else:
print("Invalid choice.")
def main():
tasks = load_tasks()
while True:
print("\n--- Simple To-Do List ---")
print("1. List tasks")
print("2. Add task")
print("3. Mark task as done")
print("4. Delete task")
print("5. Exit")
choice = input("Enter choice: ").strip()
if choice == "1":
list_tasks(tasks)
elif choice == "2":
add_task(tasks)
elif choice == "3":
mark_done(tasks)
elif choice == "4":
delete_task(tasks)
elif choice == "5":
save_tasks(tasks)
print("Goodbye!")
break
else:
print("Invalid option, try again.")
save_tasks(tasks)
if __name__ == "__main__":
main()
Explanation
The script stores tasks in a JSON file (tasks.json) with two categories: pending and done. It provides a simple text menu for the user to interact. All changes are saved after each operation to ensure persistence across runs.
Example Run
--- Simple To-Do List ---
1. List tasks
2. Add task
3. Mark task as done
4. Delete task
5. Exit
Enter choice: 2
Enter new task: Buy groceries
Added task: Buy groceries
--- Simple To-Do List ---
1. List tasks
2. Add task
3. Mark task as done
4. Delete task
5. Exit
Enter choice: 1
Pending Tasks:
1. Buy groceries
No completed tasks.
--- Simple To-Do List ---
Enter choice: 3
Pending Tasks:
1. Buy groceries
Enter number of task to mark as done: 1
Marked done: Buy groceries
--- Simple To-Do List ---
Enter choice: 1
No pending tasks.
Completed Tasks:
1. Buy groceries
--- Simple To-Do List ---
Enter choice: 5
Goodbye!
Enhancements & Next Steps
- Add due dates and sorting by priority.
- Implement search/filter of tasks.
- Add tagging or categories.
- Build a GUI version with Tkinter or a web version with Flask.
- Sync with cloud or export to CSV.
Best Practices
- Validate user input to avoid errors.
- Backup the JSON file or version tasks.
- Avoid data corruption by atomic writes (e.g., write to temp then rename).
Conclusion
This To-Do List app is a practical starting project for learning Python. It demonstrates state persistence, user interaction, and basic data structures. Readers can extend it in many directions to increase complexity and usefulness.
Join the conversation! Your thoughts help the community grow.