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.