Python  

Build a Simple Command-Line To-Do List App in Python

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.