This project combines file handling (saving tasks), user input, data structures (lists/dictionaries), functions, and basic error handling. We'll create a console-based application that lets users add, view, complete, and delete tasks, with data persistence to a JSON file.
# TODO LIST APPLICATION
import json
import os
from datetime import datetime
print("TODO LIST APPLICATION")
print("=" * 50)
class TodoApp:
"""A simple Todo List Application"""
def __init__(self, filename="todos.json"):
self.filename = filename
self.todos = self.load_todos()
def load_todos(self):
"""Load todos from JSON file"""
if os.path.exists(self.filename):
try:
with open(self.filename, 'r') as file:
return json.load(file)
except (json.JSONDecodeError, IOError):
print("Warning: Could not load todos. Starting fresh.")
return []
return []
def save_todos(self):
"""Save todos to JSON file"""
try:
with open(self.filename, 'w') as file:
json.dump(self.todos, file, indent=2)
except IOError:
print("Error: Could not save todos to file.")
def display_menu(self):
"""Display main menu"""
print("\n" + "=" * 40)
print("TODO LIST MENU")
print("=" * 40)
print("1. View All Tasks")
print("2. View Pending Tasks")
print("3. View Completed Tasks")
print("4. Add New Task")
print("5. Mark Task as Complete")
print("6. Delete Task")
print("7. Search Tasks")
print("8. Statistics")
print("9. Exit")
print("=" * 40)
def add_task(self):
"""Add a new task"""
print("\n" + "-" * 30)
print("ADD NEW TASK")
print("-" * 30)
title = input("Task title: ").strip()
if not title:
print("Task title cannot be empty!")
return
description = input("Task description (optional): ").strip()
priority = self.get_priority()
due_date = self.get_due_date()
category = input("Category (work/personal/health/etc): ").strip().lower() or "general"
new_task = {
"id": len(self.todos) + 1,
"title": title,
"description": description,
"priority": priority,
"due_date": due_date,
"category": category,
"completed": False,
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"completed_at": None
}
self.todos.append(new_task)
self.save_todos()
print(f"\nā
Task '{title}' added successfully!")
def get_priority(self):
"""Get task priority from user"""
while True:
try:
priority = input("Priority (1=Low, 2=Medium, 3=High): ").strip()
if not priority:
return 2 # Default medium
priority = int(priority)
if 1 <= priority <= 3:
return priority
else:
print("Please enter 1, 2, or 3")
except ValueError:
print("Please enter a valid number")
def get_due_date(self):
"""Get due date from user"""
while True:
due_date = input("Due date (YYYY-MM-DD or press Enter for none): ").strip()
if not due_date:
return None
try:
# Validate date format
datetime.strptime(due_date, "%Y-%m-%d")
return due_date
except ValueError:
print("Invalid date format. Please use YYYY-MM-DD")
def view_tasks(self, show_completed=None):
"""View tasks based on completion status"""
if show_completed is None:
tasks = self.todos
title = "ALL TASKS"
elif show_completed:
tasks = [t for t in self.todos if t["completed"]]
title = "COMPLETED TASKS"
else:
tasks = [t for t in self.todos if not t["completed"]]
title = "PENDING TASKS"
if not tasks:
print(f"\nNo {title.lower()} found.")
return
print(f"\n{title}")
print("-" * 60)
for task in tasks:
self.display_task(task)
print(f"\nTotal: {len(tasks)} task(s)")
def display_task(self, task):
"""Display a single task"""
# Priority indicators
priority_symbols = {1: "āŖ", 2: "š”", 3: "š“"}
priority_symbol = priority_symbols.get(task["priority"], "āŖ")
# Completion status
status = "ā
" if task["completed"] else "ā³"
# Format display
print(f"\n{status} {priority_symbol} Task #{task['id']}: {task['title']}")
if task["description"]:
print(f" Description: {task['description']}")
print(f" Category: {task['category'].upper()}")
if task["due_date"]:
due_text = f"Due: {task['due_date']}"
# Check if overdue (only for pending tasks)
if not task["completed"] and task["due_date"]:
due_date = datetime.strptime(task["due_date"], "%Y-%m-%d")
today = datetime.now().date()
if due_date.date() < today:
due_text += " ā OVERDUE!"
print(f" {due_text}")
if task["completed"] and task["completed_at"]:
print(f" Completed: {task['completed_at']}")
def mark_complete(self):
"""Mark a task as complete"""
pending_tasks = [t for t in self.todos if not t["completed"]]
if not pending_tasks:
print("\nNo pending tasks to complete!")
return
print("\nPENDING TASKS:")
for task in pending_tasks:
print(f"{task['id']}. {task['title']}")
try:
task_id = int(input("\nEnter task ID to mark as complete: "))
for task in self.todos:
if task["id"] == task_id and not task["completed"]:
task["completed"] = True
task["completed_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.save_todos()
print(f"\nā
Task '{task['title']}' marked as complete!")
return
print("\nā Task not found or already completed.")
except ValueError:
print("\nā Please enter a valid number.")
def delete_task(self):
"""Delete a task"""
if not self.todos:
print("\nNo tasks to delete!")
return
print("\nALL TASKS:")
for task in self.todos:
status = "ā
" if task["completed"] else "ā³"
print(f"{task['id']}. {status} {task['title']}")
try:
task_id = int(input("\nEnter task ID to delete: "))
for i, task in enumerate(self.todos):
if task["id"] == task_id:
confirm = input(f"Are you sure you want to delete '{task['title']}'? (y/n): ")
if confirm.lower() == 'y':
deleted_task = self.todos.pop(i)
self.save_todos()
print(f"\nš Task '{deleted_task['title']}' deleted successfully!")
# Reassign IDs
for j, t in enumerate(self.todos, 1):
t["id"] = j
self.save_todos()
return
print("\nā Task not found.")
except ValueError:
print("\nā Please enter a valid number.")
def search_tasks(self):
"""Search tasks by keyword"""
if not self.todos:
print("\nNo tasks to search!")
return
keyword = input("\nEnter search keyword: ").lower().strip()
if not keyword:
print("Please enter a keyword to search.")
return
results = []
for task in self.todos:
if (keyword in task["title"].lower() or
keyword in task["description"].lower() or
keyword in task["category"].lower()):
results.append(task)
if results:
print(f"\nš SEARCH RESULTS FOR '{keyword}':")
print("-" * 50)
for task in results:
self.display_task(task)
print(f"\nFound {len(results)} task(s)")
else:
print(f"\nNo tasks found matching '{keyword}'.")
def show_statistics(self):
"""Show task statistics"""
if not self.todos:
print("\nNo tasks to show statistics for!")
return
total = len(self.todos)
completed = sum(1 for t in self.todos if t["completed"])
pending = total - completed
# Calculate completion percentage
completion_rate = (completed / total * 100) if total > 0 else 0
# Tasks by priority
high_priority = sum(1 for t in self.todos if t["priority"] == 3)
medium_priority = sum(1 for t in self.todos if t["priority"] == 2)
low_priority = sum(1 for t in self.todos if t["priority"] == 1)
# Tasks by category
categories = {}
for task in self.todos:
cat = task["category"]
categories[cat] = categories.get(cat, 0) + 1
print("\n" + "š TASK STATISTICS")
print("=" * 40)
print(f"Total Tasks: {total}")
print(f"Completed: {completed}")
print(f"Pending: {pending}")
print(f"Completion Rate: {completion_rate:.1f}%")
print("\nBy Priority:")
print(f" š“ High: {high_priority}")
print(f" š” Medium: {medium_priority}")
print(f" āŖ Low: {low_priority}")
print("\nBy Category:")
for category, count in categories.items():
print(f" {category.upper()}: {count}")
# Overdue tasks
overdue = 0
today = datetime.now().date()
for task in self.todos:
if not task["completed"] and task["due_date"]:
due_date = datetime.strptime(task["due_date"], "%Y-%m-%d").date()
if due_date < today:
overdue += 1
if overdue > 0:
print(f"\nā Overdue Tasks: {overdue}")
def run(self):
"""Run the todo application"""
print("\n⨠Welcome to the Todo List App!")
print("Manage your tasks efficiently.")
while True:
self.display_menu()
try:
choice = input("\nEnter your choice (1-9): ").strip()
if choice == "1":
self.view_tasks()
elif choice == "2":
self.view_tasks(show_completed=False)
elif choice == "3":
self.view_tasks(show_completed=True)
elif choice == "4":
self.add_task()
elif choice == "5":
self.mark_complete()
elif choice == "6":
self.delete_task()
elif choice == "7":
self.search_tasks()
elif choice == "8":
self.show_statistics()
elif choice == "9":
print("\nš Thank you for using Todo List App!")
print("Your tasks have been saved.")
break
else:
print("\nā Invalid choice. Please enter 1-9.")
except KeyboardInterrupt:
print("\n\nš Goodbye!")
break
except Exception as e:
print(f"\nā An error occurred: {e}")
# Run the application
if __name__ == "__main__":
app = TodoApp()
app.run()